crustc: rustc 전체를 C로 변환
(github.com/FractalFir)crustc는rustc 1.98.0-nightly (c712ea946 2026-06-16)전체를 4,600만 줄 C 코드로 변환한 데모이며,GCC와make로 빌드하면 동작하는 Rust 컴파일러가 생성됨- 기반 도구인
cilly는 Rust를 C로 컴파일하는 Rust 컴파일러 백엔드이며, 이 저장소는 컴파일러가 자기 자신을 컴파일하는 가장 눈에 띄는 쇼케이스로 구성됨 cilly는 대상 C 컴파일러와 플랫폼의 타입 레이아웃, 크기, 정렬, 문자 인코딩, 정수 형식 등을 witness 프로그램으로 질의해, 특정 C 컴파일러가 받아들일 수 있는 C 코드를 생성함- 주요 목표는 LLVM/GCC 지원이 없지만 C 컴파일러는 있는 오래되거나 특이한 하드웨어에서 Rust 사용을 가능하게 하는 것이며, TCP로 원격 C 컴파일러와 통신하는 네트워크 투명성도 포함함
- 현재 생성된 C는 작성자의 워크스테이션 ISA인 ARM64 Linux를 대상으로 하며,
cilly전체 도구체인은 아직 공개 사용 준비가 되지 않았고 최적화 관련 버그도 추적 중임
rustc를 C로 변환한 데모
crustc는rustc 1.98.0-nightly (c712ea946 2026-06-16)을 4,600만 줄 C 코드로 변환한 저장소임- 이 C 코드는
GCC와make로 빌드할 수 있으며, 빌드 결과는 동작하는 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용으로 생성한
cillyC를 riscv32에서 그대로 실행할 수는 없음 - riscv32용
cillyC를 별도로 생성할 수는 있음
- Arm64용으로 생성한
- 이 저장소의
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의 단점으로 제기될 수 있음
cilly는rustc와 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심볼이 표시됨
- Plan 9 환경 출력은
makefile 생성 기능
cilly는 선택적으로 오브젝트 파일 안에 마커를 삽입하고 IR을 캐시 디렉터리에 저장할 수 있음- 이후 해당 마커를 읽어 함수와 전역을 정의 위치별로 나눌 수 있음
- 이 정보를 바탕으로 makefile이 들어 있는 디렉터리를 생성해, C 컴파일러와
make만으로 Rust를 빌드할 수 있게 함
빌드와 실행 조건
- 데모 빌드에 사용된 시스템은 Ubuntu 기반 ARM64 Linux임
- 커널 문자열은
Linux spark-2773 6.17.0-1021-nvidia ... aarch64임
- 커널 문자열은
- 사용된 C 컴파일러 정보는 GCC 13.3.0과 Ubuntu LLD 18.1.3임
- 빌드하려면 올바른 LLVM 라이브러리가 필요하며, 가장 쉬운 방법은
rustup install nightly-2026-06-16으로 해당 nightly를 설치하는 것임 - 빌드 명령은
LLVM_LIB_DIR로libLLVM.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 프로그램을 생성한다는 점이 흥미롭다
전통적인 Cconfigure빌드 체인이 대체로 이런 방식으로 동작한다는 게 꽤 이상하게 느껴지지만, 이 컴파일러, 혹은 트랜스파일러가 그 패턴을 따르는 건 납득됨
“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로 변환할 수 있음