使用指定的初始化程序时,不同的gcc程序集

Lou*_*Lou 5 c assembly gcc arm

我正在为ARM检查一些gcc生成的程序集,并注意到如果我使用指定的初始化程序,我会得到奇怪的结果:

例如,如果我有这个代码:

struct test 
{
    int x;
    int y;
};

__attribute__((noinline))
struct test get_struct_1(void)
{
    struct test x;
    x.x = 123456780;
    x.y = 123456781;
    return x;
}

__attribute__((noinline))
struct test get_struct_2(void)
{
    return (struct test){ .x = 123456780, .y = 123456781 };
}
Run Code Online (Sandbox Code Playgroud)

对于ARM(ARM GCC 6.3.0),我使用gcc -O2 -std = C11 得到以下输出:

get_struct_1:
    ldr r1, .L2
    ldr r2, .L2+4
    stm r0, {r1, r2}
    bx lr
.L2:
    .word 123456780
    .word 123456781


get_struct_2:     // <--- what is happening here
    mov r3, r0
    ldr r2, .L5
    ldm r2, {r0, r1}
    stm r3, {r0, r1}
    mov r0, r3
    bx lr
.L5:
    .word .LANCHOR0
Run Code Online (Sandbox Code Playgroud)

我可以看到第一个函数的常量,但我不明白它是如何get_struct_2工作的.

如果我为x86编译,两个函数只在一条指令中加载相同的单个64位值.

get_struct_1:
    movabs rax, 530242836987890956
    ret

get_struct_2:
    movabs rax, 530242836987890956
    ret
Run Code Online (Sandbox Code Playgroud)

我是否引发了一些未定义的行为,或者这是否.LANCHOR0与这些常量有关?

Pet*_*des 2

看起来 gcc 在将常量的负载合并到 ldm 后,用额外的间接级别搬起石头砸了自己的脚。

不知道为什么,但很明显是错过了优化错误。

x86-64 很容易优化;整个 8 字节常量可以放入一个立即数中。但 ARM 经常使用与 PC 相关的负载来加载对于立即数来说太大的常量。