对于普通功能,rust的行为与x86-64 abi相同,后者通过参数使用以rdi开头的寄存器进行传递,但是对于fn指针rust使用rsi,那么为什么rustc选择这样做?
以这个为例
fn foo(f: fn(u64)) {
f(10);
}
fn main() {
foo(|i| {
i + 1;
});
}
闭包被编译为
playground::main::{{closure}}:
sub rsp, 24
mov qword ptr [rsp + 8], rdi
mov qword ptr [rsp + 16], rsi
add rsi, 1
setb al
test al, 1
jne .LBB11_2
add rsp, 24
ret
在正常功能foo
编译为
playground::foo:
sub rsp, 24
mov qword ptr [rsp + 16], rdi
mov eax, 10
mov qword ptr [rsp + 8], rdi
mov rdi, rax
mov rax, qword ptr [rsp + 8]
call rax
add rsp, 24
ret
使用rdi并调用它main
是
lea rdi, [rip + core::ops::function::FnOnce::call_once]
call playground::foo
所以这通过调用core::ops::function::FnOnce::call_once
来完成
core::ops::function::FnOnce::call_once:
sub rsp, 40
mov qword ptr [rsp + 16], rdi
mov rsi, qword ptr [rsp + 16]
lea rdi, [rsp + 8]
call playground::main::{{closure}}
jmp .LBB4_1
.LBB4_1:
jmp .LBB4_2
.LBB4_2:
add rsp, 40
ret
纯粹是推测,但闭包可能总是使用带有&self的固有方法编译为结构,然后生成包装程序以将其转换为函数指针。这将允许固有方法也称为传递数据指针的特征对象。包装器的参数在rdi中传递,然后rdi分配堆栈空间以存储合成的零大小结构。然后,该包装程序在rdi中传递合成的结构,并在下一个参数槽rsi中传递参数。在优化的代码中,可以期望将固有方法内联到包装器中,并且可以消除与寄存器合成奇怪参数的怪异舞步。
简而言之,这是常规系统v的调用约定,但是还有一个永不读取的隐式参数,因此所有实际参数都将与常规参数一起沿列表进一步移位一个寄存器。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句