두 개의 가변 참조가 별칭을 지정할 수 없다고 가정 할 때 Rust 컴파일러가 코드를 최적화하지 않는 이유는 무엇입니까?


301

내가 아는 한, 참조 / 포인터 앨리어싱은 컴파일러가 최적화 된 코드를 생성하는 능력을 방해 할 수 있는데, 두 참조 / 포인터가 실제로 앨리어스 인 경우 생성 된 바이너리가 올바르게 동작해야하기 때문입니다. 예를 들어 다음 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 *aint *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

이 보증 그 이용하지 않습니다 ab수없는 별명입니다.

현재 Rust 컴파일러가 아직 개발 중이며 최적화를 위해 별칭 분석을 아직 통합하지 않았기 때문입니까?

이 기회는 여전히 존재하기 때문에 그게 ab수도 안전 녹 별명?



76
참고 사항 : " Rust는 안전하지 않은 코드를 제외하고 두 개의 가변 참조가 앨리어싱 할 수 없음을 확인 하므로" unsafe코드에서도 앨리어싱 가능한 변경 참조는 허용되지 않으며 정의되지 않은 동작이 발생합니다. 앨리어싱 원시 포인터를 가질 수 있지만 unsafe코드에서는 실제로 Rust 표준 규칙을 무시할 수 없습니다. 그것은 일반적인 오해 일 뿐이므로 지적 할 가치가 있습니다.
Lukas Kalbertodt

6
asm을 읽는 데 익숙하지 않기 때문에 예제가 무엇을 얻는 지 알아내는 데 시간이 걸렸습니다. 따라서 다른 사람에게 도움이되는 경우 : +=본문의 두 작업을 adds로 해석 할 수 있는지 여부로 귀결 됩니다 *a = *a + *b + *b. 포인터가 별칭이 아닌 경우 b* + *b두 번째 asm 목록에서 어떤 내용을 볼 수 있는지 알 수 있습니다 2: 01 c0 add %eax,%eax. 그러나 별칭 *b을 사용하면 두 번째로 추가 할 때 첫 번째 값 ( 4:첫 번째 asm 목록의 행 에 저장 한 값)과 다른 값을 포함 하므로 별칭을 사용할 수 없습니다 .
dlukes '08 -08-08

답변:


364

녹는 원래 LLVM의 수 noalias속성 만이 발생 잘못 컴파일 코드를 . 지원되는 모든 LLVM 버전이 더 이상 코드를 잘못 컴파일하지 않으면 다시 활성화 됩니다.

-Zmutable-noalias=yes컴파일러 옵션에 추가 하면 예상 어셈블리가 나타납니다.

adds:
        mov     eax, dword ptr [rsi]
        add     eax, eax
        add     dword ptr [rdi], eax
        ret

간단히 말해서, Rust는 C의 restrict키워드 와 동등한 것을 어디에나 넣었으며 , 일반적인 C 프로그램보다 훨씬 널리 퍼졌습니다. 이로 인해 LLVM의 코너 케이스가 올바르게 처리 할 수있는 것보다 더 많이 실행되었습니다. C 및 C ++ 프로그래머 는 Rust에서 사용하는 것만 restrict큼 자주 사용하지 않는 것으로 나타 &mut났습니다.

이것은 여러 번 일어났다 .

  • Rust 1.0 ~ 1.7 — noalias사용
  • 녹 1.8 ~ 1.27 — noalias비활성화
  • 녹 1.28 ~ 1.29 — noalias활성화
  • 녹 1.30부터 ??? —noalias 비활성화

녹 관련 문제


12
이것은 놀라운 일이 아닙니다. 다국어 친화성에 대한 광범위한 주장에도 불구하고 LLVM은 특별히 C ++ 백엔드로 설계되었으며 C ++처럼 보이지 않는 것들에 질식하는 경향이 있습니다.
메이슨 휠러

47
@MasonWheeler 몇 가지 문제를 클릭하면 restrictClang과 GCC 모두에서 잘못 컴파일 되는 C 코드 예제를 찾을 수 있습니다 . 해당 그룹에서 C ++ 자체를 세지 않는 한 "C ++"이 아닌 언어로 제한되지 않습니다 .
Shepmaster

6
@MasonWheeler : LLVM이 실제로 C 또는 C ++의 규칙을 중심으로 설계된 것이 아니라 LLVM의 규칙을 중심으로 설계되었다고 생각합니다. 그것은 일반적으로 C 또는 C ++ 코드에 적용되는 가정 이지만, 내가 알 수있는 것은 설계가 까다로운 코너 사례를 처리 할 수없는 정적 데이터 의존성 모델을 전제로합니다. 증명할 수없는 데이터 의존성을 비판적으로 가정한다면, 그 대신에 보유한 것과 동일한 비트 패턴으로 스토리지를 작성하고 잠재적이지만 입증 가능한 데이터 의존성을 갖는 비 작동 동작으로 취급합니다. 읽고 쓰기.
supercat

8
@supercat 나는 당신의 의견을 몇 번 읽었지만, 나는 그 문제에 시달렸다는 것을 인정한다. 나는 그들이이 질문이나 답변과 어떤 관계가 있는지 전혀 모른다. 정의되지 않은 동작은 여기서 작동하지 않습니다. 이는 여러 최적화 패스가 서로 제대로 상호 작용하지 않는 경우에 해당합니다.
Shepmaster

2
@avl_sweden을 반복 하면 버그 일뿐 입니다. 루프 언 롤링 최적화 단계는 noalias실행시 포인터를 완전히 고려하지 않았습니다 . 입력 포인터를 기반으로 새 포인터를 작성 noalias하여 새 포인터가 별명을 지정하더라도 속성을 부적절하게 복사했습니다 .
Shepmaster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.