두 개의 가변 참조가 별칭을 지정할 수 없다고 가정 할 때 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 *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수도 안전 녹 별명?



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.