Swift를 처음 배울 때 "왜 컴파일 언어지?"라는 의문이 들었어요. 플레이그라운드에서 실시간으로 결과가 나오니까 스크립트 언어 같아 보이거든요 🤔
스크립트 언어는 한 줄씩 읽어가며 바로 실행합니다. Python이나 JavaScript처럼요. 번역 속도는 빠르지만 매번 실행할 때마다 한 줄씩 해석해야 해서 실행 속도가 느려요. 그리고 실행해봐야 오류를 발견할 수 있죠.
반면 컴파일 언어는 소스코드를 미리 기계어로 번역해둡니다. 컴파일 과정이 필요하지만, 한 번 컴파일하면 빠르게 실행되고 컴파일 타임에 오류를 잡을 수 있어요.
Swift가 컴파일 언어인 이유는 성능과 안전성 때문입니다. iOS 앱은 부드러운 60fps를 유지해야 하고, 메모리도 효율적으로 관리해야 하거든요.
전통적인 컴파일러의 한계와 현대적 접근
50년 전 설계의 문제점
전통적인 컴파일러는 50년 전 기술입니다. 당시엔 메모리가 작고 성능이 낮아서 단순한 구조로 설계됐어요:
소스코드 → 렉싱 → 파싱 → AST → 의미분석 → IR → 최적화
문제는 AST가 포인터로 연결된 객체들이 메모리에 흩어져 있다는 점이에요.
간단한 x + 1 표현식도 3개의 객체를 만들고, 포인터를 따라가며 메모리를 여기저기 접근해야 합니다
Carbon의 혁신적 접근
구글이 개발 중인 Carbon 언어에서 흥미로운 접근을 보여줍니다. AST를 건너뛰고 바로 IR을 생성하는 거예요! 💡
// 전통적: 소스 → 렉싱 → 파싱 → AST → 의미분석 → IR
// Carbon: 소스 → 렉싱 → 파싱 → 파스트리 → 의미분석+IR 생성
핵심은 후위순회 기반 IR 구축입니다:
// 표현식: x + y * 2
// 트리 구조:
// +
// / \
// x *
// / \
// y 2
// 후위순회 순서: x, y, 2, *, +
// Carbon 파스트리: [x, y, 2, *, +] (배열)
파스트리를 한 번만 읽으면서 IR을 만들고, 문맥 정보는 스택으로 관리해요.
이렇게 하면 메모리 접근 패턴이 순차적이 되어 캐시 효율성이 극대화됩니다!
Swift 컴파일 과정 상세 분석
1. 전처리 (Preprocessing)
가장 먼저 #define, #include 같은 전처리 지시문을 처리합니다. Swift에서는 C/Objective-C 브리징 때문에 이 과정이 중요해요.
2. 파싱 (Parsing)
전처리된 소스코드를 토큰 단위로 분석하고 문법적으로 분석하여 AST를 생성합니다.
// 입력: "var a = A()"
// 토큰: [KEYWORD_VAR, IDENTIFIER("a"), ASSIGN, IDENTIFIER("A"), LPAREN, RPAREN]
문법 오류는 여기서 잡아냅니다. 괄호가 안 맞는다거나, 세미콜론이 빠졌다거나 하는 것들이요.
3. 의미 분석 (Semantic Analysis)
파싱된 AST를 받아서 타입 체크를 포함한 완전한 AST로 변환합니다. 여기가 진짜 중요한 부분이에요!
- 타입 추론: var x = 5에서 x가 Int라는 걸 알아내기
- 타입 체크: "hello" + 123같은 잘못된 코드 잡기
- 이름 해결: 변수가 실제로 선언되어 있는지 확인
이 과정을 거치면 타입이 완전히 확인된 상태가 되어 이후 단계에서 안전한 코드가 됩니다!
4. Clang 모듈 연동
Clang 모듈을 가져와서 C/Objective-C API를 Swift API로 매핑해줍니다.
Clang은 LLVM 기반의 C, C++, Objective-C 컴파일러 프론트엔드예요.
Foundation이나 UIKit 같은 시스템 프레임워크를 Swift에서 쓸 수 있는 이유가 바로 이 과정 때문입니다.
5. Raw SIL 생성
타입 체크된 완전한 AST를 Raw SIL(고수준의 Swift 특화 중간언어)로 변환합니다.
여기부터가 Swift 컴파일러가 매력적인 이유예요!
SIL은 Swift의 타입 시스템을 완전히 유지한 채로 존재하는 중간 코드입니다.
일반적인 LLVM IR과 달리 Swift의 고수준 개념들(프로토콜, 제네릭, ARC 등)을 그대로 표현할 수 있어요.
6. SIL 보장 변환 (SIL Guaranteed Transformations)
프로그램 정확성에 영향을 주는 추가적인 데이터 흐름 진단을 수행합니다:
- 확정적 초기화 검사: 모든 변수가 사용 전에 초기화되었는지
- 도달 불가능 코드 진단: 절대 실행되지 않는 코드 찾기
- 필수 인라이닝: 성능에 중요한 함수들 인라이닝
7. SIL 최적화
진짜 매력적인 부분입니다! Swift만의 고수준 최적화들이 여기서 일어나요:
- ARC 최적화: 불필요한 retain/release 호출 제거
- 제네릭 특수화: Array<Int>를 구체적 타입으로 변환
- 가상화 해제: 프로토콜 호출을 직접 호출로 변환
저는 처음에 ARC가 그냥 자동으로 메모리 관리해주는 줄 알았는데, 실제로는 컴파일러가 엄청나게 똑똑하게 최적화를 해준다는 걸 알고 감탄했어요
8. LLVM IR 변환
최적화된 SIL을 LLVM IR로 변환합니다.
LLVM IR은 어떤 CPU에서도 동작할 수 있는 중간 코드예요. 어떤 아키텍처에 특화되지 않은 가상 언어이면서, 기계어의 제네릭 표현이라고 할 수 있죠.
LLVM과 툴체인의 역할
LLVM이란?
LLVM은 Low Level Virtual Machine의 약자지만, 실제로는 컴파일러의 집합이자 툴체인 기술입니다.
What is ToolChian?
A ToolChain is a set of tools that compiles source code into executables that run on your target device and normally includes a compiler liner, and run time libiaries
즉 소스코드가 실행파일로 바뀌는데 필요한도구들이 다 툴체인이다. 우리가 흔히아는 컴파일러, 링커, 런타임라이브러리가 포함된다한다.
LLVM은 모듈화되어 있어서 내부 동작을 다 볼 수 있고, 원하는 대로 믹스매치할 수도 있어요.
가운데 있는 LLVM Optimizer가 가장 중요한 부분입니다!
애플이 LLVM을 사용하는 이유
프론트엔드 → LLVM IR → 백엔드
(Swift) (최적화) (각 플랫폼)
- 다양한 플랫폼 지원: macOS, iOS, watchOS, tvOS
- 다양한 CPU 아키텍처: x86, ARM, M1 칩셋 등
애플은 LLVM의 프론트엔드 부분에 SIL이라는 박스를 추가한 거예요. LLVM이 알아서 모든 애플 플랫폼에 맞는 어셈블리 코드와 오브젝트 파일을 만들어줍니다! 🍎
컴파일 과정 정리
- 🔴 프론트엔드: Swift 코드 → AST 생성 → SIL 변환 → 타입 검사, ARC 최적화
- 🟡 최적화: SIL 최적화 → LLVM IR 변환
- 🟡 백엔드: LLVM IR → CPU별 어셈블리 코드 변환
- 🟡 어셈블/링크: 어셈블리 → 오브젝트 파일(.o) → 실행 파일(.app)
우리가 Xcode에서 "빌드"한다고 할 때, 위의 모든 과정이 일어나는 거예요
1) 프런트엔드 (Swift 코드 → SIL 변환)
• Swift 코드 → AST(Abstract Syntax Tree) 생성
• AST를 SIL로 변환 (Swift 전용 중간 언어)
• 타입 검사, ARC 최적화 등 적용
2) 최적화 (SIL → 최적화된 SIL → LLVM IR 변환)
• SIL 최적화 (ARC 최적화, 함수 인라인, 제너릭 특수화)
• 최적화된 SIL을 LLVM IR로 변환
🟡 3) 백엔드 (LLVM IR → 기계어 변환)
• LLVM IR을 CPU에 맞는 **어셈블리 코드(Assembly)**로 변환
• 예) x86, ARM64, M1 칩셋 등 다양한 아키텍처 지원
🟡 4) 어셈블, 링크 (실행 파일 생성)
• 어셈블리 코드를 오브젝트 파일(.o)로 변환
• 여러 개의 .o 파일을 묶어 실행 파일(.app) 생성
우리가 XCODE에서 빌드한다 빌드한다 이러자나? 이게 위에서 작성한 모든과정을 하는거임!! 대단한거지 아주
참고:
'iOS' 카테고리의 다른 글
Function(1급시민 , inout & 클로저✨) (2) | 2024.03.04 |
---|---|
Assertions && Preconditions (2) | 2024.03.02 |
KeyChain & 암호화 (1) | 2023.10.08 |
TaskGroup (0) | 2023.08.13 |
Swift Concurrency - Async/Await⭐️ (0) | 2023.08.08 |