LLVM이 중복 변수를 할당하는 이유는 무엇입니까?


9

다음은 열거 형 정의와 main함수 가있는 간단한 C 파일입니다 .

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    return 0;
}

다음 LLVM IR로 변환합니다.

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 2, i32* %2, align 4
  ret i32 0
}

%2분명히 d변수이며 2가 할당됩니다. %10이 직접 반환되면 무엇에 해당합니까?


1
이 IR을 생성하기 위해 어떤 플래그를 사용 했습니까?
화살표로 표시

@arrowd, 나는 안정적인 최신 LLVM 제품군을 설치하고clang-9 -S -emit-llvm simple.c
macleginn을

1
이전에는 초기화와 관련이 있다고 생각합니다 main( godbolt.org/z/kEtS-s ). 링크는 어셈블리가 소스에 매핑되는 방법을 보여줍니다
Pradeep Kumar

2
@PradeepKumar : 실제로 함수 이름을 다른 이름으로 변경 main하면 신비한 추가 변수가 사라집니다. 흥미롭게도, return문장을 완전히 생략하면 ( mainC에서 합법적이며 와 동일 return 0;) 사라집니다 .
Nate Eldredge

1
@ macleginn : 잘 모르겠습니다. 당신은 선언하는 경우 main로서 int main(int argc, char **argv)당신이 볼 argcargv스택에 복사하지만, 신비 제로 변수가 그들에게 추가로 남아 있습니다.
Nate Eldredge

답변:


3

%1레지스터는 clang에 의해 함수에서 여러 개의 return 문처리 하기 위해 생성되었습니다 . 정수의 계승을 계산하는 함수가 있다고 상상해보십시오. 이렇게 쓰는 대신

int factorial(int n){
    int result;
    if(n < 2)
      result = 1;
    else{
      result = n * factorial(n-1);
    }
    return result;
}

당신은 아마 이것을 할 것입니다

int factorial(int n){
    if(n < 2)
      return 1;
    return n * factorial(n-1);
}

왜? Clang은 result리턴 값을 보유한 변수를 삽입하기 때문 입니다. 예 그것의 정확한 목적입니다 %1. 코드의 약간 수정 된 버전은 ir를보십시오.

수정 된 코드

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    if(d) return 1;
    return 0;
}

IR,

define dso_local i32 @main() #0 !dbg !15 {
    %1 = alloca i32, align 4
    %2 = alloca i32, align 4
    store i32 0, i32* %1, align 4
    store i32 2, i32* %2, align 4, !dbg !22
    %3 = load i32, i32* %2, align 4, !dbg !23
    %4 = icmp ne i32 %3, 0, !dbg !23
    br i1 %4, label %5, label %6, !dbg !25

 5:                                                ; preds = %0
   store i32 1, i32* %1, align 4, !dbg !26
   br label %7, !dbg !26

 6:                                                ; preds = %0
  store i32 0, i32* %1, align 4, !dbg !27
  br label %7, !dbg !27

 7:                                                ; preds = %6, %5
  %8 = load i32, i32* %1, align 4, !dbg !28
  ret i32 %8, !dbg !28
}

이제 당신은 %1자신을 유용하게 만드는 것을 보았 습니까? 다른 사람들이 지적했듯이 하나의 return 문이있는 함수의 경우이 변수는 아마도 llvm의 optim pass 중 하나에 의해 제거 될 것입니다.


1

왜 이것이 문제가됩니까? 실제 문제는 무엇입니까?

LLVM의 아키텍처는 상당히 간단한 프론트 엔드와 많은 패스를 기반으로합니다. 프론트 엔드는 올바른 코드를 생성해야하지만 좋은 코드 일 필요는 없습니다. 그들은 가장 간단한 일을 할 수 있습니다.

이 경우 Clang은 어떤 용도로도 사용되지 않는 몇 가지 명령어를 생성합니다. LLVM의 일부가 불필요한 명령을 제거하기 때문에 일반적으로 문제가되지 않습니다. Clang은 이러한 일이 발생한다고 신뢰합니다. Clang은 데드 코드 방출을 피할 필요가 없습니다. 구현은 정확성, 단순성, 테스트 가능성 등에 중점을 둘 수 있습니다.


1

Clang은 구문 분석으로 수행되지만 LLVM은 최적화로 시작되지 않았습니다.

Clang 프론트 엔드는 기계 코드가 아닌 IR (Intermediate Representation)을 생성했습니다. 이러한 변수는 SSA (Single Static Assignments)입니다. 그들은 아직 레지스터에 바인딩되지 않았으며 실제로 최적화 후에는 결코 중복되지 않기 때문입니다.

이 코드는 소스를 다소 문자 그대로 표현한 것입니다. 최적화를 위해 clang이 LLVM을 사용합니다. 기본적으로 LLVM은 그로부터 시작하여 거기서부터 최적화됩니다. 실제로 버전 10 및 x86_64의 경우 llc -O2 는 결국 다음을 생성합니다.

main: # @main
  xor eax, eax
  ret

이 수준의 프로세스를 이해합니다. 왜이 IR이 처음부터 생성되었는지 알고 싶었습니다.
macleginn

컴파일러를 단일 패스로 생각할 수 있습니다. IR을 생성하는 Clang 프런트 엔드로 시작하는 패스 파이프 라인이 있습니다. clang -emit-llvm -S file.cpp 대신 누군가 요청한이 텍스트 IR을 생성하지도 않았습니다. cpp Clang은 실제로 이진 직렬화 가능 비트 코드 버전의 IR을 생성했습니다. LLVM은 여러 단계로 구성되며, 각각 IR을 수행하고 최적화합니다. 첫 번째 LLVM 패스는 Clang에서 IR을 가져옵니다. 동일한 옵티 마이저 + 코드 생성기로 다른 언어를 지원하기 위해 Clang을 Fortran FE로 대체 할 수 있기 때문에 IR이 필요합니다.
Olsonist
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.