내가 아는 한, 참조 / 포인터 앨리어싱은 컴파일러가 최적화 된 코드를 생성하는 능력을 방해 할 수 있는데, 두 참조 / 포인터가 실제로 앨리어스 인 경우 생성 된 바이너리가 올바르게 동작해야하기 때문입니다. 예를 들어 다음 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
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi) # The first time
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi) # The second time
a: c3 retq
여기서 코드는 (%rdi)
대소 문자 int *a
와 int *b
별칭 을 위해 두 번 다시 저장됩니다 .
컴파일러에게이 두 포인터가 restrict
키워드 와 별명을 지정할 수 없다고 명시 적으로 말할 때 :
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
그러면 Clang은보다 최적화 된 이진 코드 버전을 내 보냅니다.
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Rust는 (안전하지 않은 코드를 제외하고) 두 개의 변경 가능한 참조가 별칭을 사용할 수 없음을 확인하므로 컴파일러는 더 최적화 된 코드 버전을 생성 할 수 있어야한다고 생각합니다.
나는 아래의 코드와 테스트로 컴파일 할 때 rustc 1.35.0
와 -C opt-level=3 --emit obj
,
#![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 컴파일러가 아직 개발 중이며 최적화를 위해 별칭 분석을 아직 통합하지 않았기 때문입니까?
이 기회는 여전히 존재하기 때문에 그게 a
및 b
수도 안전 녹 별명?
unsafe
코드에서도 앨리어싱 가능한 변경 참조는 허용되지 않으며 정의되지 않은 동작이 발생합니다. 앨리어싱 원시 포인터를 가질 수 있지만 unsafe
코드에서는 실제로 Rust 표준 규칙을 무시할 수 없습니다. 그것은 일반적인 오해 일 뿐이므로 지적 할 가치가 있습니다.
+=
본문의 두 작업을 adds
로 해석 할 수 있는지 여부로 귀결 됩니다 *a = *a + *b + *b
. 포인터가 별칭이 아닌 경우 b* + *b
두 번째 asm 목록에서 어떤 내용을 볼 수 있는지 알 수 있습니다 2: 01 c0 add %eax,%eax
. 그러나 별칭 *b
을 사용하면 두 번째로 추가 할 때 첫 번째 값 ( 4:
첫 번째 asm 목록의 행 에 저장 한 값)과 다른 값을 포함 하므로 별칭을 사용할 수 없습니다 .