목록으로
'언젠가 읽기' 컨텐츠는 논문이나 영문 컨텐츠 등 언젠가 읽으려고 즐겨찾기 하고선 읽지 않고 계속 미룰만한 컨텐츠를 읽고 요약하거나 소개합니다.

러스트의 증분 컴파일러 아키텍처

러스트의 증분 컴파일러 아키텍처

전통적인 컴파일러 구조는 파이프라인 형태를 띠며, 일반적으로 파싱, 타입 검사, 최적화, 코드 생성의 순서로 진행됩니다. 그러나 현대 프로그래밍 언어는 이러한 구조에 적합하지 않은 요구사항을 가지고 있습니다. 이에 따라 컴파일러들은 증분 컴파일과 같은 기능을 지원하고, 통합 개발 환경(IDE)에 통합될 수 있는 저지연 응답을 제공하기 위해 기존 구조에서 벗어난 디자인을 채택하고 있습니다.

러스트는 지난 8년간 특히 독특한 컴파일러 디자인을 추구해 왔으며, 이 기간 동안 컴파일 시간이 상당히 개선되었습니다. 하지만 여전히 추가적인 개선이 필요합니다. 전통적인 컴파일러에서는 파이프라인의 각 단계가 완료되어야 다음 단계로 넘어갈 수 있습니다. 예를 들어, 최적화 단계가 완료되어야 컴파일러가 기계어 코드를 생성할 수 있습니다. 이를 개선하기 위해 병렬로 번역 단위를 처리하거나 일부 분석을 캐싱하는 등의 방법이 있지만, 컴파일러 자체의 아키텍처가 증분 컴파일을 지원하는 데 제한을 줍니다.

언어 설계는 이러한 문제의 심각성에 큰 영향을 미칩니다. 예를 들어, GCC 개발자들은 증분 컴파일러를 도입하는 방안을 연구했지만, C 언어가 각 파일을 독립적인 번역 단위로 정의하기 때문에 빌드 시스템 수준에서 증분 컴파일을 추가하는 것이 더 용이했습니다. 반면, 러스트에서는 전체 크레이트가 단일 번역 단위로 처리되기 때문에, 크레이트 내에서의 증분 컴파일을 지원하려면 컴파일러에 특별한 지원이 필요합니다.

러스트 컴파일러는 전통적인 컴파일러 구조를 대체하기 위해 쿼리 기반 모델로 전환하였습니다. 이 모델에서는 고정된 파이프라인 대신 프로그램의 다양한 속성을 조회하는 여러 쿼리를 사용합니다. 각 쿼리는 지속적인 컴파일 데이터베이스에서 답을 가져오거나, 캐시되지 않은 경우에는 다른 쿼리를 호출하여 결과를 결합함으로써 처음부터 계산합니다. 예를 들어, type_of() 쿼리는 지역 바인딩의 타입을 추론하는 데 사용되며, 이는 여러 다른 쿼리를 호출하여 필요한 정보를 수집합니다.

개발자가 크레이트 내의 상수 타입을 변경하면, 컴파일러는 최종 실행 파일을 생성하기 위해 최상위 쿼리를 평가합니다. 이 과정에서 대부분의 함수는 변경되지 않았기 때문에 컴파일 데이터베이스에서 답을 가져옵니다. 그러나 변경된 함수에 대해서는 새로운 쿼리가 실행되어 결과를 업데이트합니다. 이러한 접근 방식은 불필요한 재컴파일을 줄이고 전체 컴파일 시간을 단축시키는 데 기여합니다.

참고 자료

  • 러스트 공식 문서
  • 컴파일러 디자인의 기초
  • 증분 컴파일과 빌드 최적화

[출처] Rust’s Incremental Compiler Architecture