더북(TheBook)

2.1.4 중간 표현

컴파일러는 각 단계별 작업이 다음 단계를 구현하기 쉽게 만드는 방향으로 사용자 코드를 나타내는, 일종의 데이터를 구성하는 파이프라인(pipeline)이라고 볼 수 있다. 이 파이프라인의 프런트엔드는 프로그램이 작성된 소스 언어(source language)에 따라 달라지는 반면, 백엔드는 프로그램이 최종 실행될 타깃 아키텍처(target architecture)와 깊이 연관되어 있다.

그 중간에서 코드는 소스/타깃 포맷 어느 쪽에도 엮이지 않는 중간 표현(IR, Intermediate Representation) 형태로 보관할 수 있다. 양쪽 언어 사이의 징검다리인 셈이다.

몇 가지 잘 만들어진 IR 스타일이 있다. 'control flow graph(제어 흐름 그래프)', 'static single-assignment(정적 단일 할당)', 'continuation-passing style(연속 전달 스타일)', 'three-address code(3중 주소 코드)'를 검색 엔진에서 찾아보라.

IR 덕분에 적은 노력으로도 여러 소스 언어 및 타깃 플랫폼을 지원할 수 있다. 이를테면, x86, ARM, SPARC 아키텍처에서 실행되는 파스칼, C, 포트란 컴파일러를 개발하는데 IR이 없으면 파스칼 → x86, C → ARM 식으로 총 9가지 컴파일러를 전부 다 구현해야 할 것이다.

IR을 공유하면 작업이 획기적으로 줄어든다. 소스 언어마다 IR을 만들어내는 프런트엔드를 하나씩 작성한 다음, 타깃 아키텍처마다 백엔드를 하나씩 작성하면 된다. 그런 다음, 둘을 섞으면 어떤 조합의 컴파일러라도 만들 수 있다.

이제 GCC3가 모토로라(Motorola) 68k 기반의 모듈라(Modula)-3 같은 수많은 괴짜 언어와 아키텍처를 어떻게 지원하는지 궁금증이 풀렸을 것이다. 언어 프런트엔드는 주로 김플(GIMPLE)4, RTL5 등의 몇몇 IR 중 하나를 타깃으로 삼고, 타깃 백엔드(예: 68k)는 이런 IR을 가져와 네이티브 코드를 생성하는 것이다.

시맨틱이 좀 더 분명하게 드러나게 코드를 바꾸려는 데에는 또 다른 중요한 이유가 있다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.