据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实别名的情况下生成的二进制行为正确。例如,在下面的C代码中,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
当clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)编译时,它会发出-O3标志
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax # load a into EAX
2: 03 06 add (%rsi),%eax # load-and-add b
4: 89 07 mov %eax,(%rdi) # store into a
6: 03 06 add (%rsi),%eax # load-and-add b again
8: 89 07 mov %eax,(%rdi) # store into a again
a: c3 retq
这里代码存储回(%rdi)两次,以防int *a和int *b别名。
当我们显式地告诉编译器这两个指针不能与restrict关键字别名:
void adds(int *restrict a, int *restrict b) {
*a += *b;
*a += *b;
}
然后Clang将发出一个更优化的版本,有效地执行*a += 2 * (*b),如果(正如restrict所承诺的那样)*b没有通过赋值给*a而被修改,则等价:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax # load b once
2: 01 c0 add %eax,%eax # double it
4: 01 07 add %eax,(%rdi) # *a += 2 * (*b)
6: c3 retq
由于Rust确保(除了不安全的代码)两个可变引用不能别名,我认为编译器应该能够发出更优化的代码版本。
当我测试下面的代码并使用-C opt-level=3——emit obj用rustc 1.35.0编译时,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
它生成:
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi)
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi)
a: c3 retq
这没有利用a和b不能别名的保证。
这是因为当前Rust编译器仍在开发中,还没有结合别名分析来进行优化吗?
这是因为即使在安全的Rust中,a和b仍然有可能别名吗?