【问题标题】:Different assembly when initializing an array with a string literal使用字符串文字初始化数组时的不同程序集
【发布时间】:2018-12-21 08:52:02
【问题描述】:

根据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,但在 ARM gcc 上我收到了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

【问题讨论】:

  • 就 C 标准而言,它们是等价的。尽管允许编译器跳过在空终止符之后写入零,但如果它可以推断出\0 之后的内存没有被使用(由于外部链接,它不能在这里使用)。因此,这似乎是某种错误。如果我将test2 更改为char buff[8] = "test\0\0\0";(4 个尾随零),我也会在 ARM 上获得相同的机器代码。
  • 顺便说一句:如果你也使用 {'t', 'e', 's', 't', 0 },buff 也会被 0 填满。我希望这会产生与字符串文字版本相同的汇编器输出。
  • @StephanLechner: it does,除非使用 arm gcc 进行编译。
  • x86-64 是一个 64 位架构(并且免费将 32 位立即数零扩展为 64 位),因此两个版本都使用单个 qword 存储区的多种原因也就不足为奇了。如果您将数组设为 9 字节长,并添加第 5 个非零字节 (godbolt.org/z/Xk-vLP),您将获得与 gcc 截然不同的代码(带有一些愚蠢的 test1 代码),但 clang 仍然为两者制作相同的好代码。跨度>

标签: c gcc assembly arm array-initialization


【解决方案1】:

首先,代码在构成buf 对象的内存内容相同的意义上是等价的。

也就是说,编译器显然会为第二个函数生成更糟糕的代码。因此,由于有一种方法可以生成具有等效语义的更优化的代码,因此可以合理地将其视为编译器中的优化失败并为其提交错误。

如果编译器想要发出相同的代码,它必须认识到内存中的字符串文字表示可以在不改变程序语义的情况下补零(尽管字符串文字本身不能被填充,因为@987654322 @ 不能等于 sizeof "test\0\0\0")。

然而,因为这只有在使用字符串字面量来初始化一个比字面量更长的显式长度数组(通常是一个坏主意)并且正常的字符串语义不适用的情况下是有利的足够(超过空终止符的字节是相关的),这种情况下更好优化的价值似乎有限。

附录:如果您将 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\000"
.space  3

有趣的是,编译器不会去重,它会将字符串文字后面的填充留给汇编器 (.space 3),而不是显式地将其归零。

【讨论】:

    猜你喜欢
    • 2017-09-01
    • 2015-06-25
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 2021-05-10
    • 2017-06-14
    相关资源
    最近更新 更多