为什么 rust 使用 rsi 将参数传递给 fn 指针
Why rust use rsi to pass parameter to fn pointer
对于普通函数,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
它使用 rsi,而普通函数 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 中传递,然后分配堆栈 space 来存储合成的零大小结构。该包装器然后在 rdi 中传递合成结构,并在下一个参数槽 rsi 中传递参数。在优化的代码中,预期将固有方法内联到包装器中,并且将消除与寄存器合成无用参数的怪异舞蹈。
简而言之,这是正常的 system v 调用约定,但有一个永远不会被读取的额外隐式参数,因此所有实际参数都从正常的列表中进一步移动了一个寄存器。
对于普通函数,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
它使用 rsi,而普通函数 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 中传递,然后分配堆栈 space 来存储合成的零大小结构。该包装器然后在 rdi 中传递合成结构,并在下一个参数槽 rsi 中传递参数。在优化的代码中,预期将固有方法内联到包装器中,并且将消除与寄存器合成无用参数的怪异舞蹈。
简而言之,这是正常的 system v 调用约定,但有一个永远不会被读取的额外隐式参数,因此所有实际参数都从正常的列表中进一步移动了一个寄存器。