根据维基百科:“微软或 GCC __fastcall 约定(又名 __msfastcall)传递适合 ECX 和 EDX 的前两个参数(从左到右评估)。剩余的参数从右到左推入堆栈。”
为什么他们决定不对 arg0、arg1、arg2 使用 EAX、ECX、EDX?如果他们要将参数推入寄存器,为什么要停在 2 处?我知道Borland 的fast all 可以做到这一点,那么微软选择不使用EAX 只是为了与众不同吗?
根据维基百科:“微软或 GCC __fastcall 约定(又名 __msfastcall)传递适合 ECX 和 EDX 的前两个参数(从左到右评估)。剩余的参数从右到左推入堆栈。”
为什么他们决定不对 arg0、arg1、arg2 使用 EAX、ECX、EDX?如果他们要将参数推入寄存器,为什么要停在 2 处?我知道Borland 的fast all 可以做到这一点,那么微软选择不使用EAX 只是为了与众不同吗?
由于x86命令集的限制,没有
CALL immediate
命令,有
CALL IP.offset,
是相对的。当然,编译器编写者希望保留具有绝对偏移量的东西,并且处理器制造商可能会要求这样做,因此我们有以下“妥协”:
MOV eax, absolute_address(label)
CALL eax
等于
CALL absolute_address(label)
这种调用方法需要1个临时寄存器,仅用于调用,可以很容易地重用,EAX是这种目的的最佳选择。
这些考虑的结果很有用,您可以在您的 asm 代码中使用它。基准测试表明,分支预测器至少可以部分地与此类代码一起正常工作。另一种可能性,但很少见,是当调用已经在顶级缓存中的过程时,您必须重置标志以避免依赖性损失或部分寄存器停顿。例如,如果您在子程序的开头使用类似 mov ah, 1 的东西,就会发生这种情况。为避免这种情况,请使用 EAX 作为临时寄存器并将
XOR eax, eax放在
CALL 之前。在极少数情况下,它实际上可能会节省几个时钟周期。但是这样做与使用它进行参数传递的实际好处是值得怀疑的,其原因可能如上所述。