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

时间:2018-10-09 06:13:28

标签: x86-64 calling-convention

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

有人可以解释吗?

1 个答案:

答案 0 :(得分:3)

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

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


但是为什么不使用r9b并使用RAX作为第六个参数?还是使用RAX来获取早期版本的arg?

因为RAX在x86中有很多隐式用法,并且在设计调用约定(http://web.archive.org/web/20140414124645/http://www.x86-64.org/pipermail/discuss/2000-November/001257.html)时进行的实验发现,使用RAX往往需要在调用者或被调用者中使用额外的指令。例如因为RAX通常是在调用方中计算其他arg的一部分,或者在代码使用RAX中传递的arg之前与其他arg中的一个做某事时需要。

RAX用于rep stos(gcc曾经更积极地用于内联memset),它用于div和扩展(单操作数)mul / {{1 }},gcc用于将其除以编译时常数。 (Why does GCC use multiplication by a strange number in implementing integer division?)。

大多数其他RAX特殊用途只是对其他寄存器(例如imulcdqe(或任何其他寄存器之间)也可以进行的较短编码。或movsxd rax, eax(无ModRM)对比add eax,imm32(或大多数其他ALU指令)。看到我的答案之一  Tips for golfing in x86/x64 machine code。原始的8086缺少许多更长的非AX替代方案,但在8086和386之间,添加了诸如add r/m32, imm32imul r32,r32 / movsx之类的东西。其他仅用于RAX的指令在优化速度时不值得使用(例如movzxxlatb),或者被P6 / AMD64扩展(lodsd废弃,而FP的一部分则被废弃) lahf,并使用SSE / SSE2 fucomi进行FP数学运算),或者像cmpxchgucomisd这样的专用指令,它们很少会对调用约定设计产生影响。编译器始终没有使用cpuid之类的BCD指令,而AMD64删除了它们。


x86-64 System V调用约定(主要是Janargička用于整数arg-pass寄存器设计)的设计人员通常旨在避免使用具有许多/常见隐式用法的寄存器。 aaa按arg传递顺序排在rdx之前,因为变量移位计数(无BMI2)需要rcx。这些可能比clmul更常见,因为2操作数div允许正常的非扩幅乘法而不会破坏RDX:RAX。

选择imul reg,regrdi作为前两个参数显然是由于将rsimemset内联为memcpy(gcc在2000,即使在gcc这么做的许多情况下,实际上并不是一个不错的选择)。即使rep movs字符串指令使用RCX作为计数器,他们仍然发现平均保存的指令可以通过RDX而不是RCX传递RDX中的第3个arg,因此{{1} }设为rep / memcpy

JanHubička通过使用当时最新版本的x86-64 gcc编译SpecInt,对arg传递寄存器进行了多种评估。有关更多详细信息和链接,请参见我在Why does Windows64 use a different calling convention from all other OSes on x86-64?上的答复。

他评估的arg寄存器指令之一是rep stosb,但他发现该指令不如其他选项好。 (请参阅上面链接的邮件列表消息。)


RISC调用约定在第一个返回值寄存器中传递第一个arg是相当普遍的。 ARM这样做(ret,而PowerPC也是如此。其他(如MIPS)则不然。但是所有这些体系结构都没有隐式使用大多数整数寄存器,通常只是链接寄存器,也许还有堆栈指针。

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