C++ compiler

진영민·2023년 5월 12일
0

잡다한 상식

목록 보기
18/22

컴파일 과정

전처리

#include나 #define, #ifdef 등을 컴파일러가 이해할 수 있도록 코드로 변환시키는 작업.
$gcc -E -o test.i test.c

1. 문자 해석

C++ 코드에는 96개의 문자들로 이루어진 Basic source character set이 있다.

  • 5종류의 공백 문자
  • 10종류의 숫자
  • 52종류의 알파벳 대소문자
  • 29종류의 특수 문자

이 문자 셋에 포함되지 않는 다른 문자는 \u를 통해 유니코드로 치환되거나 따로 해석한다.

2. \문자 해석

\문자를 처리한다.

abc \
def

라는 코드는

abcdef

라는 코드로 변경된다.

3. 전처리 토큰들로 분리

전처리 토큰의 종류

  • 헤더 이름
  • 식별자
  • 문자/문자열 리터럴
  • 연산자

1~2단계를 통하면서 안에 내용이 변경되었다면 해당 변경을 취소한다.
주석 또한 공백문자 하나로 변경된다.

4. 전처리기 실행

  • #include 파일 내용 복사
  • #define 코드 치환
  • #if, #ifdef 구문을 실행하여 코드 치환
  • #pragma와 같은 컴파일러 명령문 해석

5. 실행 문자 셋으로 변경

모든 문자들은 소스 코드 문자 셋에서 실행 문자 셋의 문자들로 변경된다.

6. 인접한 문자열 합침

"abc"
"def"

"abcdef"

로 변경된다.

컴파일

$gcc -S -o test.s test.i

front-end

  • 어휘 분석
    소스 코드를 의미 있는 최소 단위 토큰으로 나눈다.
  • 구문 분석
    토큰으로 parse tree를 생성하여 문법 오류를 검출한다.
  • 의미 분석
    parse tree를 이용해 의미상 오류가 있는지 검사한다.
  • 중간 코드 생성
    트리 형태의 GIMPLE tree를 생성한다.

middle-end

최적화를 수행한다.

back-end

최적화를 수행한다.
최종적으로 어셈블리 코드를 생성한다.

어셈블러

어셈블 코드를 0과 1의 기계어로 변환한다.
최종적으로 생성되는 object file은 바이너리 포맷 구조를 갖는다.
$gcc -c -o test.o test.s

링킹

object파일과 외부 라이브러리 파일을 모아 실행 파일을 생성한다.
운영체제가 로딩할 수 있도록 주소 정보를 할당한 파일을 만든다.
$gcc -g -o test test.o

GCC

옵션

-o : 출력 파일명을 지정
-c : 링킹 과정을 진행하지 않고 .o파일인 오브젝트 파일까지만 생성
-S : 어셈블러까지 진행하지 않고, 컴파일러의 출력인 .S어셈블러 파일을 생성
-E : 컴파일러까지 진행 하지 않고, 전처리까지의 출력인 .i 파일을 생성
-O1 ~ O3 : 최적화 수준을 지정, 숫자가 클 수록 높은 최적화
-g : 디버깅을 위한 정보를 컴파일 하면서 생성
-D : define을 할 수 있는 옵션
-l(L소문자) : 라이브러리 이름 지정
-L : 추가 라이브러리 디렉토리 지정
-Wall : 가능한 한 모든 경고를 출력한다.
-Wextra : 진짜 모든 경고를 출력한다.
-Werror : 어떤 경고라도 발생하면 컴파일되지 않는다.
-I(i대문자) : 추가 헤더 파일이 있는 디렉토리를 지정
--save-temps : 컴파일 과정에서 생성되는 중간 파일을 지우지 않고, 현재 디렉토리에 저장한다.

O, O1

코드 크기와 실행 시간을 줄이는 것을 제외한 최적화는 실행하지 않는다.

O2

메모리 공간과 속도를 희생하지 않는 범위내의 모든 최적화를 수행한다.

Os

O2최적화에서 코드의 크기를 증가시키는 최적화 기능은 빼고 최적화를 수행한다.

O3

O2최적화에 인라인 함수와 레지스터에 대한 최적화를 추가로 생성한다.
코드의 크기는 생각하지 않고 빠른 코드를 만들어 내기 위한 최적화를 수행한다. 하지만, cache에 코드가 다 들어가지 않을 수 있기 때문에 오히려 성능이 느려질 가능성이 있다.

LLVM

low level virtual machine의 약자지만, 이 자체가 하나의 프로젝트로 굳어졌다.

컴파일러는 언어마다, 아키텍처의 종류마다 다른 컴파일러가 필요하다. 이를 해결하는 컴파일러 구조가 LLVM이다.

원래 LLVM은 미들엔드-백엔드의 단계를 담당했지만, LLVM의 자체 프론트엔드인 Clang이 등장한 이후 컴파일 전 과정을 LLVM으로 진행할 수 있게 되었다.

최근에 apple이 GCC 컴파일러를 LLVM/Clang으로 대체하였다.
이는 GCC가 GPLv3 라이선스를 채택함에 따라 GCC를 사용하고 있는 모든 SW는 소스코드를 공개해야 하기 때문이다.

LLVM Core

미들엔드/백엔드를 구성하는 라이브러리. LLVM IR은 특정 아키텍처로부터 독립적이며, 어떤 아키텍처에 설치된 LLVM Core로 빌드했냐에 따라 타겟 플랫폼이 결정된다.
JIT컴파일러도 포함되어 있다.

Clang

C, C++ Object-C용 컴파일러.
소스 코드를 LLVM IR로 컴파일하는 역할을 담당한다.

참고자료

https://modoocode.com/319
https://inpages.tistory.com/157
https://0xd00d00.github.io/2022/05/29/gcc_clang.html
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=cestlavie_01&logNo=40171911634

profile
코린이

0개의 댓글