为什么在 System V AMD64 ABI 中不使用 RAX 来传递参数?

Ily*_*hin 6 x86-64 calling-convention

我不明白不在 RAX 中传递参数有什么好处,因为返回值在 RAX 中,无论如何它都会被被调用者破坏。

有人可以解释一下吗?

Pet*_*des 7

x86-64 System V确实将 AL 用于可变参数函数:调用者在 XMM 寄存器中传递 FP 参数的数量。

\n\n

(这只是一种优化,允许被调用者不将所有向量寄存器转储到数组中;AL 中的数量允许高于 FP 参数的数量。在实践中,gcc 的可变参数函数的代码生成只是检查它是否非零,并转储 xmm0..7 的任何 8 个或全部 8 个。我认为 ABI 保证al=8即使实际上没有任何 FP 参数,它始终可以安全地通过,并且您无法通过设置在堆栈上传递传递 FP args al=0

\n\n
\n\n

但为什么不使用r9b它,而使用 RAX 作为第 6 个参数呢?或者 RAX 对于一些早期的参数?

\n\n

因为 RAX 在 x86 中有很多隐式用途,并且在设计调用约定时进行了实验(http://web.archive.org/web/20140414124645/http://www.x86-64.org/pipermail/discuss/2000- November/001257.html)发现使用 RAX 往往需要调用者或被调用者提供额外的指令。例如,因为在调用者中计算其他参数时通常需要 RAX,或者在代码开始使用 RAX 中传递的参数之前对其他参数之一执行某些操作时需要 RAX。

\n\n

RAX 用于rep stos(gcc 过去更积极地使用内联 memset),它用于div和加宽(单操作数)mul/ imul,gcc 用于除以编译时常量。(为什么GCC在实现整数除法时使用乘以奇怪的数字?)。

\n\n

大多数其他 RAX 特殊用途只是您也可以使用其他寄存器执行的操作的较短编码,例如cdqevs movsxd rax, eax(或任何其他寄存器之间)。或者add eax,imm32(无 ModRM)与add r/m32, imm32(或大多数其他 ALU 指令)。请参阅我在 \n Tips for Golfing in x86/x64 machine code上的答案之一。最初的 8086 缺乏许多更长的非 AX 替代品,但在 8086 和 386 之间,添加了像imul r32,r32and movsx/这样的东西。movzx其他仅 RAX 的指令在优化速度时不值得使用(例如xlatblodsd),或者被 P6 / AMD64 扩展废弃(lahf作为 FP 的一部分,被fucomiSSE/SSE2 废弃并用于ucomisdFP 数学),或者是专用指令likecmpxchgcpuid太罕见,不会对调用约定设计产生影响。无论如何,编译器并没有使用 BCD 指令aaa,AMD64 删除了它们。

\n\n
\n\n

x86-64 System V 调用约定的设计者(主要是用于整数参数传递寄存器设计的 Jan Hubi\xc4\x8dka)通常旨在避免具有许多/常见隐式用途的寄存器。 在参数传递顺序中rdx位于前面,因为需要可变移位计数(没有 BMI2)。这些可能比和更常见,因为 2 操作数允许正常的非加宽乘法,而不会破坏 RDX:RAX。rcxclmuldivimul reg,reg

\n\n

选择rdiandrsi作为前 2 个参数显然是由内联memsetmemcpyas驱动的rep movs(gcc 在 2000 年就这样做了,尽管在 gcc 这样做的许多情况下实际上并不是一个好的选择)。尽管rep-string 指令使用 RCX 作为计数器,但它们仍然发现平均保存的指令是在 RDX 而不是 RCX 中传递第三个参数,因此调用约定不太memcpy适合rep stosb/ ret

\n\n

Jan Hubi\xc4\x8dka 通过使用当时版本的 x86-64 gcc 编译 SpecInt 来评估 arg 传递寄存器的多个变体。请参阅我的回答Why does Windows64 use a different call conventions from all other OSes on x86-64? 了解更多详细信息和链接。

\n\n

他评估的 arg-register 命令之一是RAX, RDX, RCX, RBX, RSI, RDI,但他发现它不如其他选项好。(请参阅上面链接的邮件列表消息)。

\n\n
\n\n

RISC 调用约定在第一个返回值寄存器中传递第一个参数是相当常见的。ARM 做到了这一点(r0),我认为 PowerPC 也是如此。其他的(如 MIPS)则不然。但所有这些架构都没有隐式使用大多数整数寄存器,通常只是链接寄存器,也可能是堆栈指针。

\n\n

x86-64 SysV 和 Windows 对 FP 参数执行此操作:xmm0 用于传递和返回。

\n

  • @IlyaLesokhin:是的。在寄存器中传递太多参数是不好的,因为有时被调用者所做的第一件事是将其中一个参数传递给非内联函数调用,因此其余所有参数都必须溢出。或者将它们复制到调用保留的寄存器中。如果您想要做的第一件事是除以 RAX 中未传递的参数之一,则情况类似。 (2认同)