컴파일러의 ‘공용어’이자 ‘심장’인 LLVM IR을 더 깊게 파보려고 해요.
SIL이 Swift의 언어적 특성을 품은 중간 단계였다면, LLVM IR은 하드웨어(CPU)로 가기 직전의 최종 논리 설계도에요. 여기서부터는 Swift 뿐만 아니라 C, C++, Rust 등 모든 언어가 똑같은 모습으로 모이게 돼요.
LLVM IR은 하나가 아니라, 상황에 따라 세 가지 형태로 존재해요. 모습은 달라도 담고 있는 내용은 100% 동일해요.
.bc ) : 기계가 읽기 좋게 압축된 이진 파일이에요. (App Store 제출 시 언급되는 그 비트코드에요.).ll ) : 사람이 읽을 수 있도록 만든 텍스트 형식이에요.SSA (Static Single Assignment)
모든 변수는 딱 한 번만 할당되어야 해요.
x = 1; x = 2; (x의 값이 변함)x1 = 1; x2 = 2 (새로운 이름을 부여) 이 방식 덕분에 컴파일러는 “이 변수가 어디서 왔지?”를 추적할 필요가 없어져요. 데이터의 흐름이 명확해져서 최적화 (ex. 사용되지 않는 코드 삭제)가 압도적으로 빨라져요.무한 레지스터 (Infinte Registers)
실제 CPU(ARM, x86)는 데이터를 담는 칸(레지스터)이 몇십 개 뿐이라 매우 귀해요. 하지만 LLVM IR은 레지스터가 무한대라고 가정하고 %1, %2, %3... 식으로 붙여 마음껏 써요. 실제 제한된 개수로 분배하는 건 나중에 Backend가 할 일이거든요.
강한 타입 시스템 (Strongly Typed)
단순한 메모리 주소가 아니라 i32 (32비트 정수), float , pointer 등 구체적인 타입을 명시해요.
// Swift Code
func add(a: Int, b: Int) -> Int {
return a + b
}
위 add 함수가 LLVM IR로 변하면 이런 모습이에요.
; LLVM IR 텍스트 형식 (.ll)
define i32 @add(i32 %0, i32 %1) {
%3 = add i32 %0, %1 ; 두 정수를 더해서 새로운 레지스터 %3에 저장
ret i32 %3 ; 결과 반환
}
%0 , %1 : 입력받은 파라미터(레지스터)add i32 : “32비트 정수 덧셈을 수행하라”는 명령어이 단계에서는 더 이상 Swift의 Int 구조체나 enum 같은 고수준 개념이 없어요. 오직 비트와 연산만 남게 돼요.
우리가 Xcode 에서 앱을 아카이빙할 때 “Include Bitcode” 라는 옵션을 본 적이 있을거에요

Swift -> LLVM IR -> 기계어(ARM) 로 다 만들어서 App Store에 올렸어요.Swift -> LLVM IR(Bitcode) 까지만 만들어 올려요.결과적으로 개발자가 새 기기나 나올 때마다 앱을 다시 업데이트하지 않아도 Apple 이 대신 최적화해 줄 수 있는 근거가 바로 이 LLVM IR 이에요.