使用字符串文字初始化数组时的不同程序集

Different assembly when initializing an array with a string literal

根据 this thread,用较短的字符串文字初始化数组用零填充数组。

那么,为什么这两个函数(test1test2)在为 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),而不是显式将其置零。