【问题标题】:Why parameters stored in registers and not on the stack in x86-64 Assembly?为什么参数存储在寄存器中而不是 x86-64 程序集中的堆栈中?
【发布时间】:2016-08-15 12:26:38
【问题描述】:

在 x86-32 汇编中,参数存储在堆栈中,但在 x86-64 中,参数存储在寄存器中。这是什么原因?

【问题讨论】:

  • IA64 不是 x64,由于历史原因。
  • 在 x86 下,参数总是在堆栈上而不是在寄存器中是不好的。这完全取决于调用约定、传递的数据类型和参数数量。
  • 1) 因为它可以 2) 更多的寄存器 3) 速度

标签: assembly x86-64 function-parameter


【解决方案1】:

访问 CPU 寄存器比访问 RAM 快(很多)。

由于 64 位 CPU 有更多的通用寄存器(与 64 位无关,只是因为它们更新/更大),因此使用它们是有意义的。

【讨论】:

  • Microsoft 的 32 位 __fastcall__vectorcall 调用约定即使在 32 位模式下也使用两个调用破坏寄存器(ecx 和 edx)进行参数传递。这不仅仅是利用更多寄存器的问题,而是更复杂的高性能 ABI 问题。
【解决方案2】:

存储/重新加载往返需要指令并花费大约 6 个周期的存储转发延迟,因此现代调用约定使用更高效的设计。在某些情况下,这也节省了指令,因为调用者可以只在寄存器中生成 arg 而不是推送它。 (并且返回后不必出栈)。

由于 x86-64 是一种新模式,它对向后兼容没有任何要求,因此可以设计一个没有遗留包袱的全新 ABI。有关如何设计 x86-64 SysV 调用约定以及为什么它比 Windows x86-64 调用约定更有效的一些历史,请参阅this answer。 (红色区域,更多 arg 传递寄存器。)它比 windows 约定更复杂,尤其是对于 varargs 函数。


在寄存器中传递前几个参数在 32 位代码中也更有效,但引入新的调用约定会破坏与库的向后兼容性。

即便如此,MS 还是使用 __fastcall / __vectorcall 做到了这一点,即使在 32 位模式下,它们也使用两个调用破坏寄存器(ecx 和 edx)进行 arg 传递。这些调用约定的 64 位版本使用更多的参数传递寄存器,因为 x86-64 有更多的 GP 寄存器。

Unix/Linux 没有尝试引入 32 位新的调用约定,基本上只是放弃了 32 位作为过时的遗留代码,这些代码一直很慢。 (虽然 32 位 SysV ABI 扩展了在向量 regs 中传递/返回 16B SSE 和 32B AVX 向量的规则,而不是在堆栈上)。

有关调用约定文档的链接,请参阅 标签 wiki,有关存储转发延迟的更多详细信息,请参阅性能链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 2019-02-12
    • 1970-01-01
    • 2012-01-18
    • 2013-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多