我正在检查一些gcc为ARM生成的程序集,并发现如果使用指定的初始化程序,则会得到奇怪的结果:
例如如果我有此代码:
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 };
}
对于ARM(ARM GCC 6.3.0),我得到了following output,带有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
我可以看到第一个函数的常量,但是我不明白get_struct_2
是如何工作的。
如果我针对x86进行编译,则这两个函数只需在一条指令中加载相同的单个64位值即可。
get_struct_1:
movabs rax, 530242836987890956
ret
get_struct_2:
movabs rax, 530242836987890956
ret
我是在引起一些未定义的行为,还是.LANCHOR0
与这些常量有某种联系?
答案 0 :(得分:2)
看起来像gcc在将常量的负载合并到ldm中之后,它会以一种额外的间接方式在脚上射击。
不知道为什么,但是很明显错过了优化错误。
x86-64易于优化;整个8字节常数可以立即进入。但是ARM经常使用相对于PC的负载来加载常量,而这些常量对于一个立即数而言太大。