
Nanopass Framework는 컴파일러를 "많은 아주 작은 변환 단계"로 쪼개 설계하게 돕는 프레임워크다.1 얼핏 보면 오래된 컴파일러 공학의 한 갈래처럼 보이지만, 지금 다시 볼 이유는 분명하다. 언어와 도구가 빨리 바뀌고, 팀 단위 유지보수가 중요해진 시대에는 "똑똑한 큰 변환 하나"보다 "이해 가능한 작은 변환 여러 개"가 더 큰 생산성을 줄 때가 많기 때문이다.
다만 여기서 흔한 오해 하나를 먼저 버려야 한다. 패스가 많다고 자동으로 더 깔끔한 컴파일러가 되지는 않는다. Nanopass는 미학이 아니라 트레이드오프다. 가독성, 변경 용이성, 디버깅 편의는 좋아질 수 있지만, IR 수가 늘고 패스 경계 관리 비용도 함께 커진다. 핵심은 "작게 나누면 좋다"가 아니라 "어떤 복잡성을 어디에 배치할 것인가"다.
작은 패스는 느려 보이지만, 바꾸기는 훨씬 쉬워진다
Nanopass의 기본 생각은 단순하다. 큰 AST나 IR 하나에서 많은 일을 한 번에 처리하지 말고, 각 패스가 아주 제한된 변화만 담당하게 만드는 것이다.1 예를 들어 문법 설탕 제거, 클로저 변환, 정규화, 레지스터 할당 전 준비 같은 작업을 각기 다른 작은 단계로 분리한다.
이 방식의 실제 이점은 성능보다 인지 부하에서 나온다. 한 패스가 한 가지 규칙만 다루면, 개발자는 "이 단계가 무엇을 보장하고 무엇을 깨뜨리지 않는지"를 국소적으로 이해할 수 있다. 이는 디버깅에도 유리하다. 문제가 생겼을 때 "전체 최적화 파이프라인 어디선가"가 아니라 "이 패스 전후 IR 중 어디서 깨졌는지"를 좁히기 쉽다.
이 점은 현대 도구 체인 전반의 흐름과도 닿아 있다. AI 코드 생성에서도 한 번에 최종 코드를 뽑는 단일 패스보다, 먼저 구조화된 중간 표현을 만들고 그다음 결정론적으로 산출물을 만드는 2단계 접근이 신뢰성을 높인다는 논의가 나온다.2 맥락은 다르지만 교훈은 같다. 복잡한 작업을 한 번에 해결하려 들수록 수정 가능성과 검증 가능성이 떨어진다.
실무적으로는 특히 언어가 아직 흔들릴 때 강하다. 새 문법을 넣고 의미 규칙을 바꾸고 런타임 모델을 시험하는 시기라면, 작은 패스 구조는 실험 비용을 낮춘다. 한 군데를 고쳐 전체를 흔드는 대신, 특정 단계의 불변식만 다시 맞추면 되기 때문이다.
손익분기점은 분명하다: 모든 컴파일러에 Nanopass가 맞지는 않는다
여기서 반론이 중요하다. 패스를 잘게 나누면 결국 IR이 많아진다. 각 IR의 문서화, 검사, 프린터, 테스트, 디버그 도구도 필요해진다. 팀이 작고, 언어가 안정적이며, 최적화 요구가 낮다면 이 구조는 과설계가 될 수 있다.
비교 기준은 대략 네 가지다.
첫째, 언어의 변동성이다. 문법과 의미론이 자주 바뀌면 Nanopass의 장점이 커진다. 반대로 입력 언어와 출력 언어가 오래 고정돼 있다면, 전통적 다중 패스 컴파일러나 비교적 굵은 단계 설계가 더 단순할 수 있다.
둘째, 팀의 경험이다. Nanopass는 "작게 쪼개기" 자체보다 "패스마다 명시적 불변식을 유지하는 규율"이 핵심이다. 이 규율이 없으면 패스 수만 늘어난다. 새 팀원에게는 쉬워질 수도 있지만, IR 설계 원칙이 정리돼 있지 않으면 오히려 더 헷갈린다.
셋째, 최적화 목표다. 극단적 성능 최적화 단계에서는 여러 변환을 함께 보며 전역적으로 판단해야 할 때가 있다. 이때는 너무 미세한 분리가 오히려 최적화 결정을 흩어 놓을 수 있다. 컴파일러 역사에서도 백엔드와 코드 생성은 종종 재설계 압력을 크게 받았고, 실제로 Free Pascal은 기존 코드 생성기 구조가 새로운 CPU 지원과 레지스터 할당 면에서 한계를 드러내며 큰 구조 변경을 겪었다.3 구조를 잘게 나누는 것만으로 성능 문제나 백엔드 복잡성이 해결되지는 않는다는 뜻이다.
넷째, 유지보수 수명이다. GCC 바깥 프론트엔드로 유지되던 GNU Pascal은 기반 플랫폼 변화에 계속 적응해야 했고, 결국 유지보수 부담이 커졌다.4 이것은 Nanopass 자체의 문제는 아니지만, 컴파일러 아키텍처는 코드의 우아함보다 장기 적응 비용에서 평가된다는 점을 보여준다.
Nanopass를 도입하지 않아도, 그 사고방식은 바로 가져올 수 있다
그래서 실용적 결론은 극단적이지 않다. Nanopass Framework를 통째로 채택할지 여부와 별개로, Nanopass적 사고는 거의 모든 언어 도구에 도움이 된다.
예를 들어 트랜스파일러나 정적 분석기에서도 바로 적용할 수 있다. 큰 방문자 하나에 의미 변환을 몰아넣는 대신, 작은 단계로 나누고 각 단계의 입력/출력 불변식을 적는다. 패스 경계마다 테스트를 둔다. "이 단계 이후에는 설탕 문법이 없다", "이 단계 이후에는 모든 변수 참조가 해석돼 있다" 같은 식이다. 이 습관만으로도 코드베이스는 훨씬 덜 신비로워진다.
결국 Nanopass를 봐야 하는 사람은 "컴파일러를 더 순수하게 만들고 싶은 사람"이 아니다. 언어가 계속 바뀌고, 팀이 함께 만지며, 버그를 빨리 좁혀야 하는 사람이다. 반대로 작고 안정적인 컴파일러를 혼자 관리하고 있다면, 패스를 늘리는 일 자체가 부채가 될 수 있다. 판단 기준은 단순하다. 당신의 문제는 변환 로직의 복잡성인가, 아니면 파이프라인 운영 비용인가. Nanopass는 전자를 줄이는 도구이지, 후자를 공짜로 없애주는 도구는 아니다.
참고
1Nanopass Framework: Clean Compiler Creation Language
2The two-pass compiler is back - this time, it's fixing AI code generation
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
