为什么这个函数将 RAX 压入堆栈作为第一个操作? [英] Why does this function push RAX to the stack as the first operation?

查看:27
本文介绍了为什么这个函数将 RAX 压入堆栈作为第一个操作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的 C++ 源代码的汇编中.为什么 RAX 被压入堆栈?

In the assembly of the C++ source below. Why is RAX pushed to the stack?

RAX,正如我从 ABI 理解的那样,它可以包含来自调用函数的任何内容.但是我们将它保存在这里,然后将堆栈向后移动 8 个字节.所以堆栈上的 RAX,我认为只与 std::__throw_bad_function_call() 操作相关......?

RAX, as I understand it from the ABI could contain anything from the calling function. But we save it here, and then later move the stack back by 8 bytes. So the RAX on the stack is, I think only relevant for the std::__throw_bad_function_call() operation ... ?

代码:-

#include <functional> 

void f(std::function<void()> a) 
{
  a(); 
}

输出,来自 gcc.godbolt.org,使用 Clang 3.7.1 -O3:

Output, from gcc.godbolt.org, using Clang 3.7.1 -O3:

f(std::function<void ()>):                  # @f(std::function<void ()>)
        push    rax
        cmp     qword ptr [rdi + 16], 0
        je      .LBB0_1
        add     rsp, 8
        jmp     qword ptr [rdi + 24]    # TAILCALL
.LBB0_1:
        call    std::__throw_bad_function_call()

我确定原因很明显,但我正在努力弄清楚.

I'm sure the reason is obvious, but I'm struggling to figure it out.

这是一个没有 std::function 包装器的尾调用,用于比较:

Here's a tailcall without the std::function<void()> wrapper for comparison:

void g(void(*a)())
{
  a(); 
}

琐碎:

g(void (*)()):             # @g(void (*)())
        jmp     rdi        # TAILCALL

推荐答案

64 位 ABI 要求堆栈在 call 指令之前对齐到 16 个字节.

The 64-bit ABI requires that the stack is aligned to 16 bytes before a call instruction.

call 将一个 8 字节的返回地址压入栈中,这打破了对齐方式,因此编译器需要在下一个 之前做一些事情将栈再次对齐到 16 的倍数调用.

call pushes an 8-byte return address on the stack, which breaks the alignment, so the compiler needs to do something to align the stack again to a multiple of 16 before the next call.

(要求在 call 之前而不是之后对齐的 ABI 设计选择有一个次要的优势,即如果在堆栈上传递了任何 args,则此选择会使第一个 arg 与 16B 对齐.)

(The ABI design choice of requiring alignment before a call instead of after has the minor advantage that if any args were passed on the stack, this choice makes the first arg 16B-aligned.)

推送一个不关心的值效果很好,并且可以比 sub rsp, 8具有堆栈引擎的 CPU.(见评论).

Pushing a don't-care value works well, and can be more efficient than sub rsp, 8 on CPUs with a stack engine. (See the comments).

这篇关于为什么这个函数将 RAX 压入堆栈作为第一个操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆