没有“Abi64”这样的东西。由于您标记了问题 MASM,我们可以猜测您正在使用 Windows 平台,并且显然“64”意味着这是 64 位代码,因此这确实极大地缩小了可能性。但是,对于 Windows 上的 64 位代码,仍有两种常见的调用约定。其中一个是__vectorcall,另一个是 Microsoft x64 调用约定(最初是为了使所有其他调用约定过时而发明的,但……没有)。
由于 Microsoft x64 调用约定是最常见的,并且在这种特殊情况下,使用 __vectorcall 不会改变任何内容,我假设它就是您正在使用的那个。然后所需的代码变得非常简单。您需要做的就是从f1 跳转到f2,因为堆栈的设置方式相同。 f1的前两个参数是应该传递给f2的两个参数,f2的返回值就是f1的返回值。因此:
f1:
rex_jmp r8 ; third parameter (pointer to f2) is passed in r8
这不仅写起来很简单,而且在大小和速度上都是最优化的实现。
您甚至可以根据需要预先修改v1 或v2 参数,例如:
f1:
inc ecx ; increment v1 (passed in ecx)
; multiply v2 (xmm1) by v1 (ecx)
movd xmm0, ecx
cvtdq2ps xmm0, xmm0
mulss xmm1, xmm0
rex_jmp r8 ; third parameter (pointer to f2) is passed in r8
如果你想做一些更复杂的事情,它的工作原理如下:
f1:
sub rsp, 40 ; allocate the required space on the stack
call r8 ; call f2 through the pointer, passed in r8
add rsp, 40 ; clean up the stack
ret
请注意,您不需要问题中显示的序言/尾声代码,但如果您选择包含它不会有任何影响。
但是,您在问题中显示的示例代码中所做的参数改组是错误!在 Microsoft x64 调用约定中,前最多四个整数参数在寄存器中传递,从左到右,在 RCX、RDX、R8 和 R9 中。所有其他整数参数都在堆栈上传递。前最多四个浮点值也在寄存器中传递,从左到右,在 XMM0、XMM1、XMM2 和 XMM3 中。其余的都在堆栈上传递,以及对于寄存器来说太大的结构。
不过,奇怪的是插槽是“固定的”,所以总共只能使用 4 个寄存器参数,即使你有整数和 fp 参数的混合。因此:
╔═══════════╦══════════════════════════╗
║ ║ TYPE ║
║ PARAMETER ╠═════════╦════════════════╣
║ ║ Integer ║ Floating-Point ║
╠═══════════╬═════════╬════════════════╣
║ First ║ RCX ║ XMM0 ║
╠═══════════╬═════════╬════════════════╣
║ Second ║ RDX ║ XMM1 ║
╠═══════════╬═════════╬════════════════╣
║ Third ║ R8 ║ XMM2 ║
╠═══════════╬═════════╬════════════════╣
║ Fourth ║ R9 ║ XMM3 ║
╠═══════════╬═════════╩════════════════╣
║ (rest) ║ on stack ║
╚═══════════╩══════════════════════════╝
第二个参数是第一个被传递的浮点值并不重要。它不进入 XMM0 因为它是第一个浮点值,它进入 XMM1 因为它是第二个参数,因此在第二个“插槽”中。 (这与 the x86-64 System V ABI 不同,其中前 6 个整数 args 进入寄存器,无论是否有 FP args)。
here 提供有关 Windows 参数传递的更详细文档,包括示例。