使用字符串文字初始化数组时的不同程序集
Different assembly when initializing an array with a string literal
根据 this thread,用较短的字符串文字初始化数组用零填充数组。
那么,为什么这两个函数(test1
和 test2
)在为 ARM Cortex M4 编译时会产生不同的结果?
extern void write(char * buff);
void test1(void)
{
char buff[8] = {'t', 'e', 's', 't', 0, 0, 0, 0 };
write(buff);
}
void test2(void)
{
char buff[8] = "test";
write(buff);
}
我得到 equivalent assemblies for x86-64, but on ARM gcc I get different output:
test1:
str lr, [sp, #-4]!
sub sp, sp, #12
mov r3, sp
ldr r2, .L4
ldm r2, {r0, r1}
stm r3, {r0, r1}
mov r0, r3
bl write
add sp, sp, #12
ldr pc, [sp], #4
.L4:
.word .LANCHOR0
test2:
mov r3, #0
str lr, [sp, #-4]!
ldr r2, .L8
sub sp, sp, #12
ldm r2, {r0, r1}
str r0, [sp]
mov r0, sp
strb r1, [sp, #4]
strb r3, [sp, #5]
strb r3, [sp, #6]
strb r3, [sp, #7]
bl write
add sp, sp, #12
ldr pc, [sp], #4
.L8:
.word .LANCHOR0+8
首先,代码是等效的,因为构成名为 buf
的对象的内存内容是相同的。
也就是说,编译器显然会为第二个函数生成更糟糕的代码。因此,由于有一种方法可以生成具有等效语义的更优化的代码,因此将此视为编译器中的优化失败并为其提交错误是合理的。
如果编译器想要发出相同的代码,它必须认识到可以在不改变程序语义的情况下对内存中的字符串文字表示进行零填充(尽管字符串文字本身不能被填充,因为sizeof "test"
不能等于 sizeof "test[=13=][=13=][=13=]"
).
但是,如果字符串文字用于初始化比文字更长的显式长度数组(通常是个坏主意),那么这 仅 是有利的,并且在哪里正常的字符串语义是不够的(空终止符之后的字节是相关的),这种情况下更好优化的价值似乎有限。
附录:如果您将 godbolt 设置为不删除汇编程序指令,您可以看到文字是如何创建的:
.section .rodata
.align 2
.set .LANCHOR0,. + 0
.byte 116
.byte 101
.byte 115
.byte 116
.byte 0
.byte 0
.byte 0
.byte 0
.ascii "test[=10=]0"
.space 3
有趣的是,编译器不进行重复数据删除,而是将字符串文字后的填充留给汇编程序 (.space 3
),而不是显式将其置零。
根据 this thread,用较短的字符串文字初始化数组用零填充数组。
那么,为什么这两个函数(test1
和 test2
)在为 ARM Cortex M4 编译时会产生不同的结果?
extern void write(char * buff);
void test1(void)
{
char buff[8] = {'t', 'e', 's', 't', 0, 0, 0, 0 };
write(buff);
}
void test2(void)
{
char buff[8] = "test";
write(buff);
}
我得到 equivalent assemblies for x86-64, but on ARM gcc I get different output:
test1:
str lr, [sp, #-4]!
sub sp, sp, #12
mov r3, sp
ldr r2, .L4
ldm r2, {r0, r1}
stm r3, {r0, r1}
mov r0, r3
bl write
add sp, sp, #12
ldr pc, [sp], #4
.L4:
.word .LANCHOR0
test2:
mov r3, #0
str lr, [sp, #-4]!
ldr r2, .L8
sub sp, sp, #12
ldm r2, {r0, r1}
str r0, [sp]
mov r0, sp
strb r1, [sp, #4]
strb r3, [sp, #5]
strb r3, [sp, #6]
strb r3, [sp, #7]
bl write
add sp, sp, #12
ldr pc, [sp], #4
.L8:
.word .LANCHOR0+8
首先,代码是等效的,因为构成名为 buf
的对象的内存内容是相同的。
也就是说,编译器显然会为第二个函数生成更糟糕的代码。因此,由于有一种方法可以生成具有等效语义的更优化的代码,因此将此视为编译器中的优化失败并为其提交错误是合理的。
如果编译器想要发出相同的代码,它必须认识到可以在不改变程序语义的情况下对内存中的字符串文字表示进行零填充(尽管字符串文字本身不能被填充,因为sizeof "test"
不能等于 sizeof "test[=13=][=13=][=13=]"
).
但是,如果字符串文字用于初始化比文字更长的显式长度数组(通常是个坏主意),那么这 仅 是有利的,并且在哪里正常的字符串语义是不够的(空终止符之后的字节是相关的),这种情况下更好优化的价值似乎有限。
附录:如果您将 godbolt 设置为不删除汇编程序指令,您可以看到文字是如何创建的:
.section .rodata
.align 2
.set .LANCHOR0,. + 0
.byte 116
.byte 101
.byte 115
.byte 116
.byte 0
.byte 0
.byte 0
.byte 0
.ascii "test[=10=]0"
.space 3
有趣的是,编译器不进行重复数据删除,而是将字符串文字后的填充留给汇编程序 (.space 3
),而不是显式将其置零。