在x64上选择四个参数寄存器-UN * X / Win64通用
关于x86,要记住的一件事是“ reg number”编码的寄存器名称并不明显。就指令编码(MOD R / M字节,请参见http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm)而言,寄存器编号0 ... 7是-按此顺序- ?AX
,?CX
,?DX
,?BX
,?SP
,?BP
,?SI
,?DI
。
因此,为返回值选择A / C / D(第0..2条)和前两个参数(这是“经典的” 32位__fastcall
约定)是一种逻辑选择。就使用64位而言,必须订购“更高”的法规,并且Microsoft和UN * X / Linux都以R8
/ R9
为首位。
牢记这一点,微软的选择RAX
(返回值)和RCX
,RDX
,R8
,R9
(ARG [0..3])是,如果你选择一个可以理解的选择4级为参数的寄存器。
我不知道为什么AMD64 UN * X ABI RDX
之前选择了RCX
。
在x64上选择六个参数寄存器-特定于UN * X
在RISC架构上,UN * X传统上是在寄存器中传递参数-特别是对于前六个参数(至少在PPC,SPARC和MIPS中是如此)。这可能是AMD64(UN * X)ABI设计人员选择在该体系结构上也使用六个寄存器的主要原因之一。
所以,如果你想6个寄存器来传递参数,这是合乎逻辑的选择RCX
,RDX
,R8
并R9
为他们四个,你应该选择哪两个其他?
“较高”的reg需要一个额外的指令前缀字节来选择它们,因此具有较大的指令大小占用空间,因此,如果您有选择的话,您就不想选择其中的任何一个。经典的寄存器,由于中隐含的意义RBP
和RSP
这些都没有用,而且RBX
传统上对联合国* X(全局偏移表),这看似AMD64 ABI设计师不想无谓地成为了不兼容的特殊用途。
因此,唯一的选择是RSI
/ RDI
。
因此,如果您必须将RSI
/ RDI
用作参数寄存器,它们应该是哪个参数?
使他们arg[0]
和arg[1]
有一定的优势。请参阅cHao的评论。
?SI
并且?DI
是字符串指令的源/目标操作数,并且如cHao所述,它们用作参数寄存器意味着通过AMD64 UN * X调用约定,strcpy()
例如,最简单的功能仅由两个CPU指令组成,repz movsb; ret
因为源/目标地址已由调用方放入正确的寄存器中。特别是在低级的和编译器生成的“胶水”代码中(例如,请考虑一些C ++堆分配器在构造时将对象填充零,或在内核中将堆零页面填充至)。sbrk()
,或写时复制页面错误)的大量块复制/填充,因此对于经常用于保存两个或三个CPU指令的代码很有用,否则该代码会将此类源/目标地址参数加载到“正确的”寄存器。
因此,在某种程度上,联合国* X和Win64中是只有在UN * X“预规划”两个额外的参数,在特意挑选不同RSI
/ RDI
寄存器,为四个参数中的自然选择RCX
,RDX
,R8
和R9
。
除此之外 ...
UN * X和Windows x64 ABI之间的区别不只是参数到特定寄存器的映射。有关Win64的概述,请检查:
http://msdn.microsoft.com/zh-CN/library/7kcdt6fy.aspx
Win64和AMD64 UN * X在使用堆栈空间的方式上也有显着不同。例如,在Win64上,即使args 0 ... 3在寄存器中传递,调用方也必须为函数参数分配堆栈空间。另一方面,在UN * X上,如果叶函数(不调用其他函数)所需要的空间不超过128字节,则根本不需要分配叶空间(是的,您拥有并可以使用一定数量的堆栈而没有分配它...好吧,除非您是内核代码,否则就是一些令人讨厌的错误的来源)。所有这些都是特殊的优化选择,原始海报的Wikipedia参考所指向的完整ABI参考中对这些选择的大多数理由进行了解释。