assembly GCC和Clang奇怪的不必要的堆栈使用[重复]

qij5mzcb  于 5个月前  发布在  其他
关注(0)|答案(1)|浏览(47)

此问题在此处已有答案

Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment?(1个答案)
Why does this function push RAX to the stack as the first operation?(3个答案)
20天前关闭。
我使用的是Apache Explorer,注意到GCC和Clang在编译这个简单函数(Compiler Explorer)时会发出与堆栈相关的看似不必要的指令。

void bar(void);

int foo(void) {
    bar();
    return 42;
}

字符串
下面是编译的结果(也可以通过上面的链接在编译器资源管理器中看到)。-mabi=sysv对输出程序集没有影响,但是我想排除ABI是导致奇怪程序集的原因。

// Expected output:
foo:
        call    bar
        mov     eax, 42
        ret

// gcc -O3 -mabi=sysv
// Why is it reserving unused space in the stack frame?
foo:
        sub     rsp, 8
        call    bar
        mov     eax, 42
        add     rsp, 8
        ret

// clang -O3 -mabi=sysv
// Why is it preserving a scratch register then moving it to another unused scratch register?
foo:
        push    rax
        call    bar@PLT
        mov     eax, 42
        pop     rcx
        ret

为什么函数不使用栈,栈帧却被修改?

我觉得这特别奇怪,因为这似乎是一个特别容易的优化,主要编译器,如GCC和Clang执行时,与一个已知的ABI。
我有几个理论,但我希望得到一些澄清。

  • 也许这样做是为了防止在bar递归调用foo的情况下出现无限循环?通过在每次调用时消耗少量的堆栈空间,我们确保程序最终在耗尽堆栈空间时发生segfaults。也许clang也在做同样的事情,但它使用pushpop来允许在某些情况下更好的流水线?如果是这样的话,我可以使用任何CLI参数来禁用此行为吗?然而,这似乎不是问题,因为call在x86-64上无论如何都会将rip推送到堆栈。
  • 也许有一些怪癖的C或AMD 64系统V ABI,我不知道?
  • 也许我想多了,奇怪的汇编只是寄存器/堆栈优化不好的结果。也许在编译过程中的某个时候堆栈被使用了,但是在使用被优化之后,它无法删除堆栈上的值。
uurity8g

uurity8g1#

对中。
call指令将8个字节压入堆栈(返回地址),因此优化后的函数再调整8个字节,以确保堆栈指针与16字节对齐。
我相信这是ABI的一项要求,以确保128位SSE寄存器值可以溢出到自然对齐的地址,这对于避免性能命中或故障非常重要,具体取决于CPU配置。并且/或者SSE指令可以用于从适当地址进行优化的块移动。
clang和gcc的情况实际上是相同的--您并不真正关心向堆栈槽中写入了什么,或者更新了哪个volatile寄存器,只关心调整了堆栈指针。

相关问题