일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Obfuscator
- custom packer
- linux thread
- tracing
- initial-exec
- TLS
- android inject
- LLVM 난독화
- linux debugging
- LLVM Obfuscator
- so inject
- on-stack replacement
- uftrace
- thread local storage
- Android
- 난독화
- anti debugging
- on stack replacement
- apm
- tracerpid
- v8 tracing
- pthread
- OSR
- pinpoint
- LLVM
- v8 optimizing
- 안티디버깅
- Injection
- Linux packer
- Linux custom packer
- Today
- Total
Why should I know this?
LLVM] phi 간략 살펴보기 본문
LLVM IR 중에 PHI 라는 것이 종종 보인다.
PREDS와 PHI 는 처음 LLVM 을 접하게 된다면 생소한 개념이라 약간 헤깔린다.
phi 관련 내용은 wiki 에 자세히 나와있으니 참고하시고
(https://en.wikipedia.org/wiki/Static_single-assignment_form)
몇 가지 핵심만 요약정리 해보자.
1. 일단 이름부터
왜 phi 인가하면 기호가 진짜 phi 이다 = called a Φ (Phi) function
2. phi 의 정의
preds가 명목상 모든 연관 노드라고 하면, phi 는 특별하게 연관된 노드이다.
구체적으로 이전 노드에 의존적인 관계에 있는 노드이다.
%retval = phi i32 [%a, %btrue], [%b, %bfalse]
위의 phi 예에서 %btrue 에서 온 경우 %a가 %bfalse 에서 온 경우 %b의 값이 %retval에 대입된다.
3. phi 는 왜 필요한가?
(참조 : https://mapping-high-level-constructs-to-llvm-ir.readthedocs.io/en/latest/control-structures/ssa-phi.html)
다음 코드를 통해 phi 가 어떻게 활용되는지 확인해보자.
int max(int a, int b) {
if (a > b) {
return a;
} else {
return b;
}
}
int max_phi(int a, int b) {
return (a > b) ? a : b;
}
번역된 IR 코드를 보면 btrue, bfalse 분기에서 true 때는 a가 false 때는 b가 각각 retval에 대입되지만
retval는 end: block에서 다시 한번 로드된 후 리턴된다. 이것은 SSA의 특징 때문이다.
이럴 때 phi를 사용하면 최적화가 가능하다.
define i32 @max(i32 %a, i32 %b) {
entry:
%0 = icmp sgt i32 %a, %b
br i1 %0, label %btrue, label %bfalse
btrue: ; preds = %2
br label %end
bfalse: ; preds = %2
br label %end
end: ; preds = %btrue, %bfalse
%retval = phi i32 [%a, %btrue], [%b, %bfalse]
ret i32 %retval
}
예에서 다뤘듯, 다음 구문에서 %btrue에서 온 경우 %a가, %bfalse에서 온 경우 %b가 %retval에 대입된다.
%retval = phi i32 [%a, %btrue], [%b, %bfalse]
4. phi 코드가 기계어로 번역된다면
-O[1~3]의 경우
max: # @max
# %bb.0:
pushq %rbp
movq %rsp, %rbp
movl %edi, -8(%rbp)
movl %esi, -4(%rbp)
movl -8(%rbp), %eax
cmpl -4(%rbp), %eax
jle .LBB0_2
# %bb.1:
movl -8(%rbp), %eax
movl %eax, -12(%rbp)
jmp .LBB0_3
.LBB0_2:
movl -4(%rbp), %eax
movl %eax, -12(%rbp)
.LBB0_3:
movl -12(%rbp), %eax
popq %rbp
retq
max_phi: # @max_phi
# %bb.0:
pushq %rbp
movq %rsp, %rbp
movl %edi, -8(%rbp)
movl %esi, -4(%rbp)
movl -8(%rbp), %eax
cmpl -4(%rbp), %eax
jle .LBB1_2
# %bb.1:
movl -8(%rbp), %eax
jmp .LBB1_3
.LBB1_2:
movl -4(%rbp), %eax
.LBB1_3:
popq %rbp
retq
최적화 옵션을 줬을 때 생성되는 기계어에서 차이를 볼 수있다.
구체적으로
1. 값을 로드 후 저장하고
2. 마지막에 저장된 값을 꺼내 리턴하는
두 부분이 사라지고
%eax 레지스터에 값을 세팅한 채로 리턴하는 것을 볼 수 있다.
이것이 PHI 구문의 효용이다.
'LLVM-STUDY' 카테고리의 다른 글
LoopLatch (0) | 2023.02.26 |
---|---|
LLVM Optimization study - LoopFlatten (0) | 2023.02.23 |
LLVM 기반 TOOL을 제작할 때 라이브러리 링킹하는 법 (0) | 2022.09.27 |
LLVM compile option (0) | 2022.03.21 |
LLVM tutor] implements CFI (0) | 2021.04.12 |