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

Posted

技术标签:

【中文标题】使用指定初始化程序时的不同 gcc 程序集【英文标题】:Different gcc assembly when using designated initializers 【发布时间】:2018-11-26 12:22:34 【问题描述】:

我正在检查一些 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 ;

我得到了following output 与 gcc -O2 -std=C11 for ARM (ARM GCC 6.3.0):

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 是否与这些常量有关?

【问题讨论】:

您没有显示.LANCHOR0,但我认为其中包含您的两个常量。 @Jester:没错,如果我关闭“未使用标签过滤”,我可以find those values.LANCHOR0。这是有道理的,即使我不明白为什么程序集不同。 【参考方案1】:

在将常量的负载合并到一个 ldm 之后,看起来 gcc 以额外的间接级别在脚上射击自己。

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

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

【讨论】:

更不用说将 r0 复制到 r3 并返回。我想知道是否真的为该代码和第一个代码启用了优化。编辑:是的,它正在......奇怪。 另外,如果我只是从main() 调用这些函数并添加它们的结果,gcc 将计算compile time constant and return it,因此在这种情况下它会正确优化它。也许我可以在某个地方报告这个问题,以便在未来的版本中修复。谢谢! @Lou:是的,在gcc.gnu.org/bugzilla 报告并使用“missed-optimization”关键字。恒定传播仍然有效也就不足为奇了,但是很高兴知道这个错误的范围是有限的。

以上是关于使用指定初始化程序时的不同 gcc 程序集的主要内容,如果未能解决你的问题,请参考以下文章

segueing 时的核心数据错误:无法在 NSManagedObject 类上调用指定的初始化程序

按引用和按值传递时的 gcc 程序集

构造函数干扰成员变量指定的初始化程序?

gcc 的扩展初始化程序列出警告

为啥 const 限定变量被接受为 gcc 上的初始化程序?

在 gcc 中将二维数组初始化为 0 时的值不正确