我不明白在RAX中不传递参数有什么好处, 由于返回值在RAX中,因此无论如何,被调用方都会破坏它。
有人可以解释吗?
答案 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特殊用途只是对其他寄存器(例如imul
和cdqe
(或任何其他寄存器之间)也可以进行的较短编码。或movsxd rax, eax
(无ModRM)对比add eax,imm32
(或大多数其他ALU指令)。看到我的答案之一
Tips for golfing in x86/x64 machine code。原始的8086缺少许多更长的非AX替代方案,但在8086和386之间,添加了诸如add r/m32, imm32
和imul r32,r32
/ movsx
之类的东西。其他仅用于RAX的指令在优化速度时不值得使用(例如movzx
,xlatb
),或者被P6 / AMD64扩展(lodsd
废弃,而FP的一部分则被废弃) lahf
,并使用SSE / SSE2 fucomi
进行FP数学运算),或者像cmpxchg
或ucomisd
这样的专用指令,它们很少会对调用约定设计产生影响。编译器始终没有使用cpuid
之类的BCD指令,而AMD64删除了它们。
x86-64 System V调用约定(主要是Janargička用于整数arg-pass寄存器设计)的设计人员通常旨在避免使用具有许多/常见隐式用法的寄存器。 aaa
按arg传递顺序排在rdx
之前,因为变量移位计数(无BMI2)需要rcx
。这些可能比cl
和mul
更常见,因为2操作数div
允许正常的非扩幅乘法而不会破坏RDX:RAX。
选择imul reg,reg
和rdi
作为前两个参数显然是由于将rsi
或memset
内联为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用于传递和返回。