1P by GN⁺ | ★ favorite | 댓글 1개
  • crustcrustc 1.98.0-nightly (c712ea946 2026-06-16) 전체를 4,600만 줄 C 코드로 변환한 데모이며, GCCmake로 빌드하면 동작하는 Rust 컴파일러가 생성됨
  • 기반 도구인 cilly는 Rust를 C로 컴파일하는 Rust 컴파일러 백엔드이며, 이 저장소는 컴파일러가 자기 자신을 컴파일하는 가장 눈에 띄는 쇼케이스로 구성됨
  • cilly는 대상 C 컴파일러와 플랫폼의 타입 레이아웃, 크기, 정렬, 문자 인코딩, 정수 형식 등을 witness 프로그램으로 질의해, 특정 C 컴파일러가 받아들일 수 있는 C 코드를 생성함
  • 주요 목표는 LLVM/GCC 지원이 없지만 C 컴파일러는 있는 오래되거나 특이한 하드웨어에서 Rust 사용을 가능하게 하는 것이며, TCP로 원격 C 컴파일러와 통신하는 네트워크 투명성도 포함함
  • 현재 생성된 C는 작성자의 워크스테이션 ISA인 ARM64 Linux를 대상으로 하며, cilly 전체 도구체인은 아직 공개 사용 준비가 되지 않았고 최적화 관련 버그도 추적 중임

rustc를 C로 변환한 데모

  • crustcrustc 1.98.0-nightly (c712ea946 2026-06-16)4,600만 줄 C 코드로 변환한 저장소임
  • 이 C 코드는 GCCmake로 빌드할 수 있으며, 빌드 결과는 동작하는 Rust 컴파일러가 됨
  • 실행 예시는 LLVM 라이브러리 경로를 지정한 뒤 ./rustc/rustc --version을 실행해 동일한 rustc 1.98.0-nightly 버전을 출력함
  • 생성된 Rust 컴파일러는 코드 컴파일, core, alloc, std 빌드가 가능함
  • 코드에는 C 코드 외에 일부 C++ LLVM 래퍼가 포함됨
    • Rust가 LLVM의 일부 기능을 노출하기 위해 C++를 사용함
    • 해당 래퍼는 LLVM 버전에 종속적이고 단독 빌드가 번거로워 미리 컴파일된 상태로 제공됨

cilly의 역할

  • crustc는 새 Rust-to-C 컴파일러 도구체인인 cilly의 데모/티저
  • 전체 cilly 도구체인은 사용자의 Rust 코드를 임의의 대상에 맞춰 C로 컴파일하는 것을 목표로 함
  • 이 저장소는 cilly가 컴파일러 자체를 컴파일하는 모습을 보여주기 위해 구성됨
  • cilly는 Rust 라이브러리이자 Rust 컴파일러 백엔드, 즉 플러그인 형태로 Rust를 C로 컴파일함
  • 작성자는 지난 3년간 Rust를 C로 컴파일하는 작업을 해왔고, rustc_codegen_clr 같은 공개 시도와 여러 비공개 시도 이후 cilly가 14번째 시도라고 밝힘

C 컴파일러에 맞춰 코드를 생성하는 방식

  • cilly의 주요 특징은 C 컴파일러에 적응한다는 점임
  • 특정 컴파일러와 플랫폼이 무엇을 지원하는지 확인하는 witness 프로그램을 생성할 수 있음
    • 예시는 _Thread_local int KEYWORD_TLS_SUPPORTED;이며, 해당 C 컴파일러가 _Thread_local을 지원할 때만 컴파일됨
  • cilly는 특정 C 컴파일러가 받아들일 수 있는 C 코드를 생성하려고 함
  • 타입 레이아웃, 크기, 정렬, 문자 인코딩, 정수 형식은 질의 대상임
    • 문자 인코딩은 ASCII 여부를 확인함
    • 정수 형식은 two's complement 여부를 확인함
  • 가능한 경우 폴백을 사용함
  • ANSI C 밖의 가정을 피하려고 하며, strict aliasing 같은 현대 C 표준 관련 동작에도 우회책을 둠
  • 드물게 (void*)(uintptr_t)(ptr) 왕복 변환 같은 합리적 가정이 필요할 수 있음
    • 이런 가정은 문서화하고, 가능하면 CHAR_BIT = 8 같은 assert를 추가함

대상별 C 코드와 ABI 제약

  • cilly의 출력 C 코드는 컴파일러별
    • Arm64용으로 생성한 cilly C를 riscv32에서 그대로 실행할 수는 없음
    • riscv32용 cilly C를 별도로 생성할 수는 있음
  • 이 저장소의 rustc 생성 C는 작성자 워크스테이션의 ISA 때문에 ARM64 Linux를 대상으로 함
  • cilly가 생성한 코드는 일반 rustc가 컴파일한 코드와 대체로 ABI 호환됨
  • 일부 플랫폼에서는 rustc가 C로 표현할 수 없는 ABI를 선택해 완전한 호환이 어려움
  • Arm64에서는 구조체 반환 포인터인 sret 때문에 제약이 있음
    • 대부분의 플랫폼에서는 sret이 첫 번째 인자와 같은 레지스터로 전달되어 첫 인자를 출력 포인터로 두는 방식이 가능함
    • Arm64에서는 sret 포인터가 다른 레지스터로 전달됨
    • 네이티브 C 컴파일러가 작은 구조체에 대해 return-by-sret을 선택해야 하지만, 16바이트 미만 작은 구조체에서는 그렇게 하지 않는다고 설명함

오래되거나 특이한 대상 지원

  • 이 프로젝트의 주요 목표는 LLVM/GCC 지원이 없지만 C는 지원하는 오래되거나 특이한 하드웨어에서 Rust를 사용할 수 있게 하는 것임
  • 어떤 프로젝트가 Rust에서 C로 이동하거나 C 프로젝트의 Rust 대안이 만들어질 때, 그런 대상 지원 부족은 Rust의 단점으로 제기될 수 있음
  • cillyrustc와 C 컴파일러를 감싸고 Rust 코드를 즉석에서 C로 변환함
  • 사용자 관점에서는 특정 대상에 사용할 C 컴파일러를 정의하는 방식에 가까움
  • 예시 설정은 sdcc_z180-unknown-none 트리플과 /usr/bin/sdcc, -mz180, --std-c89, -c 인자를 사용함

네트워크 투명성과 원격 C 컴파일러

  • cilly네트워크 투명성을 갖고 TCP를 통해 C 컴파일러와 통신할 수 있음
  • 필요하다면 UART 같은 더 특이한 통신 방식으로 확장될 수 있음
  • 이 방식은 C 크로스 컴파일러가 없는 플랫폼의 부트스트랩 역설을 해결하기 위한 방법임
  • 대상 OS에서 작은 C 서버를 빌드해 실행하고, Linux 같은 일반 플랫폼에서 rustc를 실행한 뒤 cilly가 네트워크로 통신하게 할 수 있음
  • 작성자는 Arm64 Linux에서 rustc를 실행하면서 x86 Plan 9 VM용 작은 Rust 프로그램을 성공적으로 컴파일함
    • Plan 9 환경 출력은 gnot osversion 2000 cputype 386
    • /tmp/hello_plan9 실행 결과는 Hello, world!
    • nm 결과에는 rust_begin_unwind 심볼이 표시됨

makefile 생성 기능

  • cilly는 선택적으로 오브젝트 파일 안에 마커를 삽입하고 IR을 캐시 디렉터리에 저장할 수 있음
  • 이후 해당 마커를 읽어 함수와 전역을 정의 위치별로 나눌 수 있음
  • 이 정보를 바탕으로 makefile이 들어 있는 디렉터리를 생성해, C 컴파일러와 make만으로 Rust를 빌드할 수 있게 함

빌드와 실행 조건

  • 데모 빌드에 사용된 시스템은 Ubuntu 기반 ARM64 Linux임
    • 커널 문자열은 Linux spark-2773 6.17.0-1021-nvidia ... aarch64
  • 사용된 C 컴파일러 정보는 GCC 13.3.0Ubuntu LLD 18.1.3
  • 빌드하려면 올바른 LLVM 라이브러리가 필요하며, 가장 쉬운 방법은 rustup install nightly-2026-06-16으로 해당 nightly를 설치하는 것임
  • 빌드 명령은 LLVM_LIB_DIRlibLLVM.so.22.1-rust-1.98.0-nightly 경로를 지정해 make -j20을 실행함
  • CFLAGS는 동작하지만 일부 플래그는 컴파일을 느리게 만들 수 있음
  • 최적화는 권장되지 않음
    • 데모가 아직 거칠고 최적화가 문제를 일으킬 수 있음
    • 이 규모에서는 최적화에 시간이 많이 걸림
  • 최적화 없이 작성자 머신에서는 몇 분 안에 빌드됨
    • 측정값은 937.98s user, 123.77s system, 1352% cpu, 1:18.48 total
  • 최적화를 켜면 대부분의 코드는 빠르게 지나가지만 일부 큰 Rust 파일에서 막힐 수 있음

테스트와 알려진 문제

  • 빌드 테스트는 LD_LIBRARY_PATH에 nightly LLVM 라이브러리와 ./rustc_driver를 지정하고 ./rustc/rustc --version을 실행하는 방식임
  • 일반 프로그램을 빌드하려면 std를 빌드해야 함
    • std가 없으면 error[E0463]: can't find crate for std 오류가 발생함
    • 표준 라이브러리 빌드는 BUILDING_STD.md를 참고해야 함
  • 알려진 버그로, 이상한 경로 정규화 문제 때문에 crustc가 빌드된 디렉터리, 즉 저장소 루트에서 실행될 때 크래시할 수 있음
  • 다른 위치에서는 정상 동작함

cilly 공개 상태

  • cilly는 아직 공개 사용 준비가 되지 않았음
  • 작성자는 가능한 빨리 공개할 계획이라고 밝힘
  • 공개가 늦어지는 이유로 직장, 대학 논문, 손 부상을 들고 있음
  • 전체 cilly 도구체인이 아직 공개되지 않은 이유 중 하나는 최적화 관련 버그를 추적 중이기 때문임

댓글과 토론

Lobste.rs 의견들
  • 주어진 컴파일러와 플랫폼이 무엇을 지원하는지 확인하는 witness 프로그램을 생성한다는 점이 흥미롭다
    전통적인 C configure 빌드 체인이 대체로 이런 방식으로 동작한다는 게 꽤 이상하게 느껴지지만, 이 컴파일러, 혹은 트랜스파일러가 그 패턴을 따르는 건 납득됨
    “14번째 시도: cilly”라니 대단한 집요함이고, 그런 끈기가 부럽다
  • 비교하자면, Zig 컴파일러 전체를 C로 변환한 버전을 갖고 있는데, 이는 소스에서 빌드하는 일반 절차의 일부임
    규모는 460만 줄이라 이 프로젝트보다 거의 정확히 한 자릿수 작다. 생성된 C 코드는 대상별로 달라지지만, 컴파일러별로 달라지지는 않음
  • 시스템 프로그래머는 아니지만, 이런 프로젝트가 Rust와 Zig 중 무엇을 쓸지에 영향을 줄 수 있을지 궁금함
    Zig의 장점에는 다양한 교차 컴파일 대상 지원과 자체 호스팅 도구체인, LLVM 선택 의존성이 포함되는데, Rust를 희귀 플랫폼용 C로 컴파일할 수 있다는 약속은 Zig 쪽을 겨냥한 신호처럼도 보임
    • crustc를 쓰는 것만으로는 그렇지 않음. 변환된 컴파일러도 원래 컴파일러와 같은 컴파일 대상만 지원하기 때문임
      다만 다른 Rust 프로젝트를 C로 변환해서 C만 지원하는 대상에서 실행하고 컴파일하는 것은 가능함
      그래도 영향을 과대평가하면 안 됨. 아주 틈새 문제를 위한 약간 번거로운 해법에 가깝다. rustc는 이미 대상 지원이 넓고, gcc 백엔드를 통해 더 늘어날 예정임
      Zig의 교차 컴파일 도구는 멋지지만, 특히 Zig나 Rust보다 까다로운 C 부분에서 편의성을 높이는 쪽에 가깝고, 더 많은 대상을 지원한다는 의미는 상대적으로 작음
      결국 Zig와 Rust의 대상 지원은 이미 꽤 비슷해서 결정적 요인이 되긴 어렵고, 두 언어 사이에는 훨씬 중요한 차이가 많다
  • 멋지긴 한데, 결국 LLVM의 능력에 제한되는 것 아닌가?
    • 왜 그래야 하는지 모르겠음. crustc는 Rust를 C로 바꾸는 도구체인인 cilly가 무엇을 할 수 있는지 보여주는 예시일 뿐임
      전체 cilly 도구체인은 사용자의 Rust 코드를 임의의 대상용 C로 컴파일한다. 이 저장소는 가장 화려한 시연이라고 생각해서 컴파일러가 자기 자신을 컴파일하는 모습을 보여주는 것임
  • mrustc(https://github.com/thepowersgang/mrustc)도 볼 만함. C++로 작성된 Rust 컴파일러로, C로 트랜스파일한 뒤 GCC에 넘기는 방식이라 rustc도 C로 변환할 수 있음