정확히 PHI 명령어가하는 일과 LLVM에서 사용하는 방법


87

LLVM에는 매우 이상한 설명 이 포함 된 파이 명령어가 있습니다.

'phi'명령어는 함수를 나타내는 SSA 그래프에서 φ 노드를 구현하는 데 사용됩니다.

일반적으로 분기를 구현하는 데 사용됩니다. 올바르게 이해했다면 종속성 분석을 가능하게해야하며 경우에 따라 불필요한로드를 피하는 데 도움이 될 수 있습니다. 그러나 정확히 무엇을하는지 이해하기는 여전히 어렵습니다.

Kaleidoscope 예제사례에 대해 꽤 잘 설명합니다 if. 그러나 같은 논리 연산을 구현하는 방법이 명확하지 않다 &&||. 온라인 llvm 컴파일러에 다음을 입력하면 :

void main1(bool r, bool y) {
    bool l = y || r;
}

마지막 몇 줄은 완전히 혼란 스럽습니다.

; <label>:10                                      ; preds = %7, %0
%11 = phi i1 [ true, %0 ], [ %9, %7 ]
%12 = zext i1 %11 to i8

파이 노드가 사용할 수있는 결과를 생성하는 것처럼 보입니다. 그리고 나는 파이 노드가 값이 오는 경로를 정의한다는 인상을 받았습니다.

누군가 Phi 노드가 무엇이며 어떻게 구현하는지 설명 할 ||수 있습니까?


1
phi노드는 "정적 단일 배정"형태로 IR을 변환하는 컴파일러의 문제에 대한 솔루션입니다. 해결책을 더 잘 이해하려면 문제를 더 잘 이해하는 것이 좋습니다. 그래서 나는 당신에게 " phi노드 인가 "를 하나 할 것 입니다.
Vraj Pandya

답변:


76

파이 노드는 현재 블록의 전임자에 따라 값을 선택하는 데 사용되는 명령어입니다 ( 전체 계층 구조를 보려면 여기 를 참조하십시오. 상속되는 클래스 중 하나 인 값으로도 사용됨).

LLVM 코드의 SSA (정적 단일 할당) 스타일의 구조로 인해 Phi 노드가 필요합니다 (예 : 다음 C ++ 함수).

void m(bool r, bool y){
    bool l = y || r ;
}

다음 IR로 변환됩니다. (만들어진 clang -c -emit-llvm file.c -o out.bc후를 통해 봅니다 llvm-dis)

define void @_Z1mbb(i1 zeroext %r, i1 zeroext %y) nounwind {
entry:
  %r.addr = alloca i8, align 1
  %y.addr = alloca i8, align 1
  %l = alloca i8, align 1
  %frombool = zext i1 %r to i8
  store i8 %frombool, i8* %r.addr, align 1
  %frombool1 = zext i1 %y to i8
  store i8 %frombool1, i8* %y.addr, align 1
  %0 = load i8* %y.addr, align 1
  %tobool = trunc i8 %0 to i1
  br i1 %tobool, label %lor.end, label %lor.rhs

lor.rhs:                                          ; preds = %entry
  %1 = load i8* %r.addr, align 1
  %tobool2 = trunc i8 %1 to i1
  br label %lor.end

lor.end:                                          ; preds = %lor.rhs, %entry
  %2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
  %frombool3 = zext i1 %2 to i8
  store i8 %frombool3, i8* %l, align 1
  ret void
}

그래서 여기서 무슨 일이 일어나나요? 변수 bool l가 0 또는 1 일 수 있는 C ++ 코드와 달리 LLVM IR에서는 한 번 정의해야합니다 . 그래서 우리 %tobool는 참 인지 확인한 다음 lor.end또는로 이동 lor.rhs합니다.

에서 lor.end우리는 마지막의 값이 || 운영자. 진입 블록에서 도착했다면 그것은 사실입니다. 그렇지 않으면 %tobool2- 의 값과 같 으며 다음 IR 라인에서 얻은 결과입니다.

%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]

6
TL; DR φ 노드는 삼항 표현식입니다. 어떤 사람은 그것이 조건을 포함하지 않는다고 주장 할 수도 있지만, 실제로 최종 코드로 변환 할 때 어떤 인수가 라이브인지 확인할 수 없으므로 φ도 조건을 가져야합니다.
Hi-Angel

31

파이를 전혀 사용할 필요가 없습니다. 임시 변수를 많이 만드십시오. LLVM 최적화 패스는 임시 변수 최적화를 처리하고 자동으로 phi 노드를 사용합니다.

예를 들어 이렇게하려면 다음을 수행하십시오.

x = 4;
if (something) x = x + 2;
print(x);

이를 위해 (의사 코드에서) phi 노드를 사용할 수 있습니다.

  1. x1에 4를 할당
  2. if (! something) 분기가 4
  3. 2를 더하여 x1에서 x2 계산
  4. x1 및 x2에서 x3 파이 할당
  5. x3로 전화 인쇄

그러나 phi 노드없이 (의사 코드에서) 할 수 있습니다.

  1. x라는 스택에 지역 변수 할당
  2. 임시 x1 값 4에로드
  3. x1을 x에 저장
  4. if (! something) 분기가 8
  5. x를 임시 x2에로드
  6. 임시 x3에 4와 x2 추가
  7. x3을 x에 저장
  8. x를 임시 x4에로드
  9. x4로 전화 인쇄

llvm으로 최적화 패스를 실행하면이 두 번째 코드가 첫 번째 코드에 최적화됩니다.


4
내가 읽은 바에 따르면 여기에서 염두에 두어야 할 몇 가지 제한 사항이있는 것 같습니다. mem2reg 는 문제의 최적화 단계이며 Kaleidoscope 예제 에서 지적한 몇 가지 제한 사항이 있습니다 . 그러나 이것이 문제를 처리하는 데 선호되는 방법이며 Clang에서 사용하는 것 같습니다.
매튜 샌더스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.