QBE 탐색: LLVM의 경량 대안

컴파일러를 구축하는 것은 시스템 프로그래밍에서 가장 벅찬 작업 중 하나로 여겨지곤 합니다. 수년 동안 업계는 막대한 최적화와 광범위한 아키텍처 지원을 제공하는 강력한 도구인 LLVM에 크게 의존해 왔습니다. 하지만 많은 개발자에게 LLVM의 복잡성과 컴파일 시간 오버헤드는 과도할 수 있습니다. 이때 등장한 것이 QBE입니다. QBE는 더 가벼운 대안이 되도록 설계된 작고 예측 가능하며 효과적인 컴파일러 백엔드입니다.

QBE란 무엇인가?

QBE는 특정 중간 표현(IR)을 입력받아 다양한 아키텍처를 위한 머신 코드를 생성하는 컴파일러 백엔드입니다. 방대한 최적화 세트와 명시적인 phi 노드를 포함한 복잡한 Static Single Assignment (SSA) 형식을 사용하는 LLVM과 달리, QBE는 단순함을 목표로 합니다. 이는 언어 구현자들이 모든 아키텍처에 대해 개별적으로 어셈블리 생성기를 작성할 필요 없이 머신 코드를 타겟팅할 수 있는 방법을 제공합니다.

에코시스템: cproc에서 Blaise까지

QBE의 가장 매력적인 측면 중 하나는 그 위에 구축된 작지만 헌신적인 도구와 언어들의 에코시스템이 등장하고 있다는 점입니다. QBE는 매우 가볍기 때문에 실험적이거나 미니멀리스트 컴파일러를 위한 데일리 타겟으로 매우 훌륭합니다.

cproc

cproc은 QBE를 기반으로 구현된 C 컴파일러입니다. 사용자들은 QBE와 결합했을 때 cprocgcc -O2 성능의 약 70%를 정도 달성하면서도 코드 크기는 훨씬 작게 유지할 수 있다는 점에 주목했습니다. 이는 GCC나 LLVM의 거대한 코드베이스 대신 "plain and simple C coded compiler"를 선호하는 사람들에게 매력적인 옵션이 됩니다.

Blaise

C를 넘어, QBE는 새로운 언어들을 구동하는 데 사용되고 있습니다. 현대적인 Object Pascal 구현체인 Blaise는 QBE를 백엔드로 활용하여, 서로 다른 프로그래밍 패러다임에 걸친 프로젝트의 다재다능함을 보여줍니다.

기술적 트레이드오프와 한계

QBE의 미니멀리즘은 가장 큰 강점이지만, 그에 따른 기술적 트레이드오프가 수반됩니다. 시니어 개발자와 컴파일러 엔지니어들은 LLVM과 같이 더 강력한 시스템을 선택하는 대신 QBE를 선택할 때 고려해야 할 몇 가지 주요 한계를 다음과 같이 강조했습니다:

  • ABI 및 구조체 처리: QBE는 레지스터를 통한 구조체 전달 및 반환을 위한 ABI를 처리하지만, 종종 스택으로의 불필요한 복사를 수행하여 약간의 성능 오버헤드를 유발할 수 있습니다.
  • Debug Information: 데이터에 대한 디버그 정보를 생성하는 것은 현재 과제입니다. 이는 포괄적인 어셈블러 지원의 부족이나 DWARF 형식의 순수한 복잡성 때문일 가능성이 높습니다.
  • Inlining 및 Line Numbers: 라인 넘버 디버그 정보 지시어는 현재 문서화되지 않았으며 인라인 함수를 지원하지 않습니다. 이로 인해 최적화된 코드의 심층 디버깅이 어려워집니다.
  • Memory Management: IR은 GC (Garbage Collection) 루트를 식별하는 기능을 기본적으로 지원하지 않습니다. 즉, QBE를 타겟으로 하는 언어들은 보수적(conservative) GC를 구현하거나 스택을 명시적으로 관리해야 합니다.

단순함의 매력

이러한 한계에도 불구하고, 커뮤니티는 QBE의 "smol"한 특성을 매우 매력적으로 느낍니다. 한 사용자는 확장이 매우 쉽다고 언급했습니다. 예를 들어, AI 프롬프트(Claude)를 통해 MIPS 지원을 추가하는 것이 성공적이었으며, 이를 통해 커스텀 언어를 Nintendo 64로 컴파일할 수 있었다고 합니다.

"But it's smol, effective, and it doesn't make you deal with phi nodes!"

현대 컴파일러 인프라의 복잡성에 지친 개발자들에게, QBE는 예측 가능하고 사용 가능하며 작은 도구 세트가 대부분의 특수한 작업에 충분한 힘을 제공할 수 있음을 상기시켜 줍니다.

Sources