1000sj
SJ CODE
1000sj
전체 방문자
오늘
어제
  • 분류 전체보기
    • Algorithms
      • Crypto
      • Formal Methods
    • Security
      • Fuzzing
      • Exploit
    • System Programming
      • Kernel
      • Compiler
      • Device Driver
      • Emulator
      • Assembly
      • Memory
      • Network
    • Architecture
      • ARM
      • RISC-V
    • Cloud Computing
      • Infrastructure
      • SDN
    • TroubleShooting
    • Performance improvements
      • Parrelel Processing
      • HPC
    • ETC
      • 문화 생활
      • 커뮤니티

인기 글

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
1000sj

SJ CODE

System Programming/Compiler

LLVM #0 LLVM 구조와 동작원리

2025. 11. 10. 10:40

 

#include <stdio.h>

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

int main() {
    int result = factorial(5);
    printf("5! = %d\n", result);
    return 0;
}

 

1단계: Clang으로 LLVM IR 생성

clang -O3 -emit-llvm hello.c -c -o hello.bc

 

  • -O3: 최대 최적화 수준 (인라이닝, 루프 언롤링 등)
  • -emit-llvm: C 코드를 기계어 대신 LLVM IR로 변환
  • -c: 컴파일만 하고 링크는 안 함
  • -o hello.bc: 출력 파일 (비트코드 형식)

생성된 파일:

  • hello.bc: LLVM 비트코드 (바이너리 형식)

비트코드를 사람이 읽을 수 있는 형태로 보려면

llvm-dis hello.bc -o hello.ll

 

 

hello.ll 내용

; ModuleID = 'hello.bc'
source_filename = "hello.c"

define dso_local i32 @factorial(i32 %n) {
entry:
  %cmp = icmp slt i32 %n, 2
  br i1 %cmp, label %return, label %if.end

if.end:
  %sub = add nsw i32 %n, -1
  %call = tail call i32 @factorial(i32 %sub)
  %mul = mul nsw i32 %call, %n
  ret i32 %mul

return:
  ret i32 1
}

define dso_local i32 @main() {
entry:
  %call = tail call i32 @factorial(i32 5)
  %call1 = tail call i32 (i8*, ...) @printf(
    i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i64 0, i64 0), 
    i32 %call)
  ret i32 0
}

LLVM IR 특징

 

  • SSA (Static Single Assignment) 형식
  • 타입 명시적: i32 (32비트 정수), i8* (char 포인터)
  • 플랫폼 독립적: x86, ARM, MIPS 어디서나 실행 가능

 

2단계: opt로 LLVM IR 최적화

opt -O3 hello.bc -o hello-opt.bc

 

opt의 역할

  • 이미 -O3로 컴파일했지만, 추가 최적화 패스 적용
  • 플랫폼 독립적인 최적화 수행

적용되는 최적화 예시

 

  1. 재귀 → 루프 변환 (tail recursion elimination)
  2. 상수 폴딩: factorial(5) → 컴파일 타임에 120 계산
  3. 데드 코드 제거
  4. 함수 인라이닝

 

 

최적화 전후 비교

# 최적화 과정을 보고 싶다면
opt -O3 -print-before-all -print-after-all hello.bc -o hello-opt.bc 2>&1 | less

 

최적화된 IR을 보면

llvm-dis hello-opt.bc -o hello-opt.ll

 

hello-opt.ll (최적화 후)

define dso_local i32 @main() {
entry:
  ; factorial(5)가 컴파일 타임에 계산됨!
  %call = tail call i32 (i8*, ...) @printf(
    i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i64 0, i64 0), 
    i32 120)  ; <-- 120으로 상수화됨
  ret i32 0
}

; factorial 함수가 인라인되고 완전히 제거될 수 있음

 

3단계: llc로 네이티브 어셈블리 생성

llc hello-opt.bc -o hello.s

llc의 역할:

  • LLVM IR → 타겟 아키텍처의 어셈블리 코드 생성
  • 레지스터 할당
  • 명령어 스케줄링
  • 플랫폼별 최적화

생성된 hello.s (x86-64)

.text
	.globl	main
	.type	main,@function
main:
	pushq	%rbp
	movq	%rsp, %rbp
	
	# printf 호출 준비
	movl	$.L.str, %edi     # 첫 번째 인자: 포맷 스트링
	movl	$120, %esi        # 두 번째 인자: 120 (factorial 결과)
	xorl	%eax, %eax
	callq	printf
	
	xorl	%eax, %eax        # return 0
	popq	%rbp
	retq

.L.str:
	.asciz	"5! = %d\n"

 

다른 아키텍처로 생성하기

# MIPS 어셈블리 생성
llc -march=mips -mcpu=mips32r2 hello-opt.bc -o hello-mips.s

# ARM 어셈블리 생성
llc -march=arm hello-opt.bc -o hello-arm.s

# 어셈블리 대신 오브젝트 파일 직접 생성
llc -filetype=obj hello-opt.bc -o hello.o

 

최종 링크 및 실행

# 어셈블리를 오브젝트 파일로 어셈블
as hello.s -o hello.o

# 링크하여 실행 파일 생성
ld hello.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello

# 또는 clang으로 한 번에
clang hello.s -o hello

# 실행
./hello
# 출력: 5! = 120

 

 


전체 과정 비교

 

일반적인 컴파일 (한 번에)

clang -O3 hello.c -o hello

내부적으로는 같은 과정을 거치지만, 중간 단계가 숨겨짐

 

LLVM 툴체인 (단계별)

# C → LLVM IR
clang -O3 -emit-llvm hello.c -c -o hello.bc

# LLVM IR 최적화
opt -O3 hello.bc -o hello-opt.bc

# LLVM IR → 어셈블리
llc hello-opt.bc -o hello.s

# 어셈블리 → 실행 파일
clang hello.s -o hello

 

 

Writing an LLVM Analysis (part 2)

https://www.youtube.com/watch?v=v82DmHqmXMA&list=PLDSTpI7ZVmVnvqtebWnnI8YeB8bJoGOyv&index=8

 

 

References

  • https://llvm.org/docs/GettingStartedTutorials.html
  • https://llvm.org/docs/WritingAnLLVMPass.html
  • https://jonathan2251.github.io/lbd/
  • https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/index.html
  • https://lowlevelbits.org/how-to-learn-compilers-llvm-edition/
  • https://craftinginterpreters.com/contents.html
  • https://interpreterbook.com/
  • https://www.cs.princeton.edu/~appel/modern/java/
  • https://www.packtpub.com/en-us/product/llvm-techniques-tips-and-best-practices-clang-and-middle-end-libraries-9781838829728
  • https://github.com/joydo/CompilerLearning
  • https://pangyoalto.com/clang-and-optimization/
  • https://widatama.github.io/hn/item?id=40842173
  • https://mukulrathi.com/create-your-own-programming-language/llvm-ir-cpp-api-tutorial/
  • https://www.cs.cmu.edu/afs/cs/academic/class/15745-s12/public/lectures/L3-LLVM-Part1.pdf
  • dfsdf

'System Programming > Compiler' 카테고리의 다른 글

LLVM Compiler Under the hood #1 컴파일 흐름 이해  (0) 2026.01.14
Compiler 개발 #1 컴파일러 동작 원리  (0) 2025.12.19
LLVM #1 IR 이해 하기  (0) 2025.11.11
    'System Programming/Compiler' 카테고리의 다른 글
    • LLVM Compiler Under the hood #1 컴파일 흐름 이해
    • Compiler 개발 #1 컴파일러 동작 원리
    • LLVM #1 IR 이해 하기
    1000sj
    1000sj

    티스토리툴바