【发布时间】:2016-04-12 01:52:25
【问题描述】:
一些语言,比如 Haskell,不区分按值传递和按引用传递。然后,编译器可以通过启发式近似选择最有效的调用约定。 Linux x64 ABI 是一个启发式示例:如果参数的大小大于 16 字节,则将指针传递给堆栈,否则将值传递到寄存器中。
在 Rust 中同时保留按值传递和按引用传递(当然是不可变的)的概念并强制用户选择有什么好处?
如果看到值被修改,传值是否是传引用 + 复制的语法糖?
【问题讨论】:
一些语言,比如 Haskell,不区分按值传递和按引用传递。然后,编译器可以通过启发式近似选择最有效的调用约定。 Linux x64 ABI 是一个启发式示例:如果参数的大小大于 16 字节,则将指针传递给堆栈,否则将值传递到寄存器中。
在 Rust 中同时保留按值传递和按引用传递(当然是不可变的)的概念并强制用户选择有什么好处?
如果看到值被修改,传值是否是传引用 + 复制的语法糖?
【问题讨论】:
两件事:
换句话说,在 Rust 中,这两种形式具有不同的语义。不过,这并不排除也进行优化。
【讨论】:
[已编辑:将示例更改为在发布模式下工作]
它不是语法糖,通过查看生成的代码可以看出。
鉴于这些功能:
fn by_value(v: (u64, u64)) -> u64 {
v.0 + v.1
}
fn by_ref(v: &(u64, u64)) -> u64 {
v.0 + v.1
}
如果一个是另一个的语法糖,我们希望它们生成相同的汇编代码,或至少相同的调用约定。但实际上,我们发现by_ref 在rdi 和rsi 寄存器中传递了v,而by_value 在rdi 寄存器中传递了一个指向v 的指针,并且必须遵循该指针才能获得值:(see details,使用释放模式):
by_value:
movq 8(%rdi), %rax
addq (%rdi), %rax
retq
by_ref:
leaq (%rdi,%rsi), %rax
retq
【讨论】:
leaq 1(%rdi), %rax; retq。请注意,所有关于推送和弹出寄存器的垃圾都已被优化掉。
u64 的元组是 16 个字节,引用是 8 个字节。优化器完全有可能认为 8 字节优于 16 字节。