LLVM Pass를 제작하기 위해 공부한 내용을 정리하기 위한 글임니다
https://youtu.be/MagR2KY8MQI?si=_TIQhqHYNhkbBbfz
해당 영상을 참고해서 정리하였고, 틀린부분이 있을 수 있습니다 :)
LLVM IR이란?
소스코드는 컴파일과정에서 몇가지 단계를 거쳐 실행파일로 변환됩니다.
단계들을 간단하게 살펴보겠습니다,
1. Source Code -> Intermediate Representation
첫번째 단계에서 소스코드는 parsing 후 변환과정을 거쳐 Intermediate Representation(IR)로 변환됩니다.
해당 단계는 front-end 컴파일러가 담당하며, llvm에서는 clang이 c와 c++대상으로 해당 단계를 수행하지요.
그러고 나면 LLVM-IR이라는 중간언어가 탄생하게 됩니다.
이때, IR은 machine independent 하여 어떤 기계던 적용이 가능합니다.
2. Intermediate Representation -> Machine Intermediate Representation
고로 해당 IR을 실행하기 위해서, machine에 맞는 Machine IR로 lowering해주는 단계를 거칩니다.
3. Machine IR -> Assembly -> Object Code
LLVM을 타겟으로 한 back-end는 이 코드들을 어셈블리로 변환합니다.
4. Object Code -> Executable
linker가 해당 Object code들을 받아서 linking을 수행한 후 executable파일을 생성해줍니다.
이제 LLVM-IR에 대해서 알아보겠습니다.
Module은 IR객체중 가장 높은 레벨입니다.
각 module은 global 변수 리스트, 함수, dependency로 가지고있는 라이브러리 리스트 등을 포함하고 있습니다.
function은 basic block들로, basic block은 instruction으로 구성되어 있죠.
pass에서는 분석이나 transformation을 위해 instruction들을 주로 사용하게 됩니다.
해당 LLVM-IR은 SSA form으로 구현되어 있습니다.
모든 변수들은 한번만 할당이 진행되고, 사용하기 전에 정의가 되어있어야 하죠.
https://llvm.org/docs/LangRef.html
LLVM Language Reference Manual — LLVM 19.0.0git documentation
llvm.org
LLVM-IR은 공식 Docs가 굉장히 잘 되어 있습니다.
참고하셔도 좋을 것 같네요.
#include <stdio.h>
int main() {
int a = 1;
a = a + 1;
}
간단한 코드를 LLVM-IR로 변환해보겠습니다.
clang -emit-llvm -S <source code> -o <source code.ll>
을 사용하면 .ll 파일로 변환이 됩니다.
위의 코드를 llvm-ir로 변환한 모습입니다.
하나씩 알아가봅시다
해당 부분은 원본 소스코드의 이름, target의 정보를 나타내는 부분입니다.
datalayout에서는 byte순서(little endian, big endian), elf mangling, abi alignment등을 나타내고, triple에서는 아키텍쳐등의 정보를 표시합니다.
main함수를 보면, 변수 %1을 선언하고, 1을 저장한 후 %2에 %1을 저장하는 모습을 볼 수 있습니다.
이후 %2에 1을 증가시킨 후 %3에 저장한 후 다시 %1에 해당 값을 저장합니다.
왜 %1 = %1 + 1을 안하고 이렇게 복잡하게 하느냐 하면
위에 설명했듯이 LLVM-IR은 SSA form을 사용하고 있기에, 변수엔 한번의 할당만이 가능하기 때문입니다.
%1 = %1 + 1을하게 되면, %1에 초기 1, 그리고 바뀐 값. 이렇게 두번 할당되기때문에 불가능한것이죠.
이렇게 기본적인 LLVM-IR의 개념에 대해 알아보았는데요, 완전재밋져
궁금한 내용은 댓글부탁드리겠습니다~