[Computer Science] C++ 빌드의 동작
컴파일러의 동작 과정을 확인하기 전, 먼저 프로그램의 생성 과정을 살펴보겠습니다.
프로그램을 만들기 위해서는 소스코드가 필요하며, 소스코드는
전처리기, 컴파일러, 어셈블러, 링커 총 4가지의 과정을 통해 프로그램으로 출력됩니다.
위의 4가지 과정을 빌드 라고 부릅니다.
프로그램 생성 과정
과정 이름 | 전처리기 | 컴파일러 | 어셈블러 | 링커 |
---|---|---|---|---|
입력 파일 | 고급언어 파일 (.c, .cpp, etc…) | .i 파일 (intermediate file) | .s 파일 (assembly file) | .o 파일 (individual object file) |
출력 파일 | .i 파일 (intermediate file) | .s 파일 (assembly file) | .o 파일 (individual object file) | .out 파일 (binary file) |
특징 | 헤더파일, 매크로를 치환한 결과를 .i 파일로 저장 | 고급언어 파일을 어셈블리어로 컴파일 후 .s 파일로 저장 | 외부 파일과 연결을 위해 링커가 읽을 수 있는 목적파일로 변환 후 .o 파일로 저장 | 목적 파일들을 하나로 묶어 실행 파일을 생성 |
전처리기
전처리기는 고급 언어의 매크로 혹은 상수를 변환하는 과정입니다.
C언어의 경우 #define 매크로를 사용한 코드를 상수로 변환하거나
코드에 작성된 주석을 제거하고 #include 지시문에 있는 헤더파일을 찾아
현재의 소스코드에 복사합니다.
컴파일러
컴파일러 내부에는 Front-end, Middle-end, Back-end 3가지의 과정이 존재합니다.
3가지 과정을 거쳐 출력된 어셈블리 파일을 어셈블러에게 전달합니다.
Front-end 과정
Front-end 과정에서는 입력된 고급언어 파일에서 문법 검사를 실행합니다.
세부적으로는 어휘, 구문, 의미 등을 분석하고 Middle-end에게 넘겨주기 위해
소스코드를 트리 형태로 표현한 GIMPLE Tree 를 생성합니다.
- 어휘 분석: 소스코드를 의미 있는 최소 단위인 토큰으로 나눈다.
- 구문 분석: 문법적 오류 검출을 위해 (Parse)트리 형태로 변환한다.
- 의미 분석: 트리를 순회하며 의미상 오류를 검출한다. (매개변수의 잘못된 사용, 자료형 오류 등)
- 중간 표현 생성: GIMPLE Tree를 생성합니다.
Middle-end 과정
Middle-end 과정에서는 GIMPLE Tree를 SSA 형태로 변환한 후 아키텍쳐(CPU) 의존성 제거 작업을 실행합니다.
이후 고급 언어와 어셈블리어 중간 단계 언어인 Register Transfer Language(RTL)를 생성합니다.
이 과정에서 최적화가 일어나며 컴파일러에 따라 성능의 차이가 발생할 수 있습니다.
1
2
3
SSA (Static Single Assignment)
데이터 흐름 분석과 코드 최적화를 위해 사용되는 중간 표현입니다.
보통 정적으로 값과 데이터 타입을 결정하기 위해 변수를 배정 또는 흐름을 분리하는 형태에 사용됩니다.
Back-end 과정
Back-end 과정에서는 RTL을 최적화한 뒤
실행되는 아키텍쳐에 맞춰 코드를 최적화 합니다.
위의 과정으로 출력되는 파일이 .s 확장자 파일인 어셈블리 코드 파일 입니다.
컴파일러의 최적화, 확장성에 따라 성능에 차이가 있을 수 있습니다.
어셈블러
어셈블러 과정은 컴파일러가 출력한 어셈블리 파일을 받아서
오브젝트 파일로 변환하는 과정입니다.
이제 오브젝트 파일에 있는 정의된 내용를 구현된 내용과 연결해야 합니다.
정의과 구현을 연결하는 과정이 링킹이라고 합니다.
링커
연결하는 과정인 링킹의 주체를 링커라고 합니다.
링커는 오브젝트 파일들과 프로그램에서 사용할 .lib(라이브러리)파일들을 링크하여
하나의 실행파일을 출력합니다.
(이때, 링크하는 방법에 따라 정적 링킹과 동적 링킹이 존재합니다.)