crustc: chuyển toàn bộ `rustc` sang C
(github.com/FractalFir)crustclà bản demo đã chuyển toàn bộrustc 1.98.0-nightly (c712ea946 2026-06-16)thành 46 triệu dòng mã C, và khi build bằngGCCcùngmakesẽ tạo ra một trình biên dịch Rust hoạt động được- Công cụ nền tảng
cillylà backend trình biên dịch Rust biên dịch Rust sang C, và kho lưu trữ này là màn trình diễn nổi bật nhất cho việc trình biên dịch tự biên dịch chính nó cillytruy vấn bố cục kiểu, kích thước, căn chỉnh, mã hóa ký tự, định dạng số nguyên của trình biên dịch C và nền tảng đích bằng các witness program, từ đó sinh ra mã C mà trình biên dịch C cụ thể đó có thể chấp nhận- Mục tiêu chính là cho phép dùng Rust trên phần cứng cũ hoặc khác thường không có hỗ trợ LLVM/GCC nhưng có trình biên dịch C, đồng thời bao gồm cả tính trong suốt mạng để giao tiếp với trình biên dịch C từ xa qua TCP
- Mã C hiện được sinh ra đang nhắm tới ARM64 Linux là ISA của máy trạm tác giả, toàn bộ toolchain
cillyvẫn chưa sẵn sàng để công khai sử dụng và tác giả vẫn đang theo dõi các lỗi liên quan đến tối ưu hóa
Bản demo chuyển rustc sang C
crustclà kho lưu trữ đã chuyểnrustc 1.98.0-nightly (c712ea946 2026-06-16)thành 46 triệu dòng mã C- Mã C này có thể được build bằng
GCCvàmake, và kết quả build là một trình biên dịch Rust hoạt động được - Ví dụ chạy thử là chỉ định đường dẫn thư viện LLVM rồi chạy
./rustc/rustc --versionđể in ra cùng phiên bảnrustc 1.98.0-nightly - Trình biên dịch Rust được tạo ra có thể biên dịch mã, build
core,alloc,std - Ngoài mã C, còn có một số wrapper LLVM viết bằng C++
- Rust dùng C++ để phơi bày một số chức năng của LLVM
- Các wrapper này phụ thuộc vào phiên bản LLVM và khá bất tiện nếu build riêng, nên được cung cấp ở dạng đã biên dịch sẵn
Vai trò của cilly
crustclà bản demo/teaser củacilly, một toolchain Rust-to-C mới- Toàn bộ toolchain
cillyhướng tới việc biên dịch mã Rust của người dùng sang C phù hợp với bất kỳ đích nào - Kho lưu trữ này được dựng lên để cho thấy
cillybiên dịch chính trình biên dịch cillyvừa là thư viện Rust vừa là backend trình biên dịch Rust, tức biên dịch Rust sang C dưới dạng plugin- Tác giả cho biết trong 3 năm qua đã làm việc về biên dịch Rust sang C, và sau các nỗ lực công khai như
rustc_codegen_clrcùng nhiều thử nghiệm riêng tư khác,cillylà lần thử thứ 14
Cách sinh mã phù hợp với trình biên dịch C
- Đặc điểm chính của
cillylà thích ứng với trình biên dịch C - Nó có thể tạo ra các witness program để kiểm tra một trình biên dịch và nền tảng cụ thể hỗ trợ những gì
- Ví dụ là
_Thread_local int KEYWORD_TLS_SUPPORTED;, chỉ biên dịch được khi trình biên dịch C đó hỗ trợ_Thread_local
- Ví dụ là
cillycố gắng sinh ra mã C mà một trình biên dịch C cụ thể có thể chấp nhận- Bố cục kiểu, kích thước, căn chỉnh, mã hóa ký tự, định dạng số nguyên đều là đối tượng được truy vấn
- Với mã hóa ký tự, nó kiểm tra có phải ASCII hay không
- Với định dạng số nguyên, nó kiểm tra có phải two's complement hay không
- Khi có thể, nó dùng các phương án fallback
- Nó cố tránh các giả định vượt ra ngoài ANSI C, đồng thời có cách lách với các hành vi liên quan đến chuẩn C hiện đại như strict aliasing
- Trong một số trường hợp hiếm, có thể cần các giả định hợp lý như phép chuyển đổi khứ hồi
(void*)(uintptr_t)(ptr)- Các giả định này sẽ được ghi lại và nếu có thể sẽ thêm các assert như
CHAR_BIT = 8
- Các giả định này sẽ được ghi lại và nếu có thể sẽ thêm các assert như
Ràng buộc mã C theo đích và ABI
- Mã C do
cillysinh ra là theo từng trình biên dịch- Mã C
cillyđược tạo cho Arm64 không thể chạy nguyên xi trên riscv32 - Nhưng có thể sinh riêng mã C
cillycho riscv32
- Mã C
- Mã C
rustcđược sinh trong kho này nhắm tới ARM64 Linux vì ISA máy trạm của tác giả - Mã do
cillysinh ra nhìn chung tương thích ABI với mã dorustcthông thường biên dịch - Trên một số nền tảng,
rustcchọn ABI mà C không thể biểu diễn nên khó đạt tương thích hoàn toàn - Trên Arm64, có ràng buộc do con trỏ trả về cấu trúc
sret- Trên hầu hết nền tảng,
sretđược truyền bằng cùng thanh ghi với đối số đầu tiên nên có thể dùng cách đặt đối số đầu tiên làm con trỏ đầu ra - Trên Arm64, con trỏ
sretđược truyền bằng thanh ghi khác - Tác giả giải thích rằng trình biên dịch C native cần chọn return-by-sret cho các cấu trúc nhỏ, nhưng thực tế lại không làm vậy với các cấu trúc nhỏ dưới 16 byte
- Trên hầu hết nền tảng,
Hỗ trợ các đích cũ hoặc khác thường
- Mục tiêu chính của dự án là giúp Rust có thể dùng được trên phần cứng cũ hoặc khác thường không có hỗ trợ LLVM/GCC nhưng có hỗ trợ C
- Khi một dự án chuyển từ Rust sang C hoặc khi xuất hiện bản thay thế bằng Rust cho một dự án C, việc thiếu hỗ trợ cho các đích như vậy có thể bị xem là nhược điểm của Rust
cillybọc quanhrustcvà trình biên dịch C, rồi chuyển mã Rust sang C ngay tại chỗ- Từ góc nhìn người dùng, cách này gần giống như định nghĩa trình biên dịch C sẽ dùng cho một đích cụ thể
- Cấu hình ví dụ dùng triple
sdcc_z180-unknown-nonecùng các đối số/usr/bin/sdcc,-mz180,--std-c89,-c
Tính trong suốt mạng và trình biên dịch C từ xa
cillycó tính trong suốt mạng và có thể giao tiếp với trình biên dịch C qua TCP- Nếu cần, có thể mở rộng sang các phương thức giao tiếp khác thường hơn như UART
- Cách này nhằm giải quyết nghịch lý bootstrap trên các nền tảng không có C cross-compiler
- Có thể build và chạy một C server nhỏ trên hệ điều hành đích, rồi chạy
rustctrên một nền tảng phổ biến như Linux đểcillygiao tiếp qua mạng - Tác giả đã biên dịch thành công một chương trình Rust nhỏ cho VM x86 Plan 9 trong khi chạy
rustctrên Arm64 Linux- Đầu ra của môi trường Plan 9 là
gnot osversion 2000 cputype 386 - Kết quả chạy
/tmp/hello_plan9làHello, world! - Kết quả
nmhiển thị symbolrust_begin_unwind
- Đầu ra của môi trường Plan 9 là
Tính năng tạo makefile
cillycó thể tùy chọn chèn marker vào trong object file và lưu IR vào thư mục cache- Sau đó có thể đọc marker đó để tách các hàm và biến toàn cục theo vị trí định nghĩa
- Dựa trên thông tin này, nó có thể tạo ra một thư mục chứa makefile để build Rust chỉ với trình biên dịch C và
make
Điều kiện build và chạy
- Hệ thống được dùng để build demo là ARM64 Linux dựa trên Ubuntu
- Chuỗi kernel là
Linux spark-2773 6.17.0-1021-nvidia ... aarch64
- Chuỗi kernel là
- Thông tin trình biên dịch C được dùng là GCC 13.3.0 và Ubuntu LLD 18.1.3
- Để build cần đúng thư viện LLVM, và cách dễ nhất là cài nightly tương ứng bằng
rustup install nightly-2026-06-16 - Lệnh build là chạy
make -j20vớiLLVM_LIB_DIRtrỏ tới đường dẫnlibLLVM.so.22.1-rust-1.98.0-nightly CFLAGScó hoạt động, nhưng một số cờ có thể làm việc biên dịch chậm hơn- Không khuyến nghị bật tối ưu hóa
- Bản demo vẫn còn thô và tối ưu hóa có thể gây ra vấn đề
- Ở quy mô này, tối ưu hóa tốn rất nhiều thời gian
- Không bật tối ưu hóa thì trên máy của tác giả có thể build trong vài phút
- Số đo là
937.98s user,123.77s system,1352% cpu,1:18.48 total
- Số đo là
- Nếu bật tối ưu hóa, phần lớn mã sẽ qua nhanh nhưng có thể bị kẹt ở một số tệp Rust lớn
Kiểm thử và vấn đề đã biết
- Bài kiểm thử build là đặt nightly LLVM library và
./rustc_drivervàoLD_LIBRARY_PATH, rồi chạy./rustc/rustc --version - Để build chương trình thông thường thì cần build
std- Nếu không có
stdsẽ gặp lỗierror[E0463]: can't find crate for std - Việc build thư viện chuẩn cần tham khảo
BUILDING_STD.md
- Nếu không có
- Một lỗi đã biết là
crustccó thể bị crash khi chạy trong chính thư mục đã build nó, tức thư mục gốc của kho lưu trữ, do vấn đề chuẩn hóa đường dẫn kỳ lạ - Khi chạy ở vị trí khác thì hoạt động bình thường
Trạng thái công khai của cilly
cillyvẫn chưa sẵn sàng để công khai sử dụng- Tác giả cho biết sẽ cố công bố sớm nhất có thể
- Lý do công bố chậm bao gồm công việc, luận văn đại học và chấn thương tay
- Một trong các lý do toàn bộ toolchain
cillychưa được công khai là vì tác giả vẫn đang theo dõi các lỗi liên quan đến tối ưu hóa
1 bình luận
Các ý kiến trên Lobste.rs
Việc chuỗi build
configureC truyền thống về cơ bản hoạt động theo cách này nghe khá lạ, nhưng cũng dễ hiểu khi compiler, hay transpiler này đi theo mô hình đó“Lần thử thứ 14: cilly” — đúng là sự bền bỉ đáng nể, và tôi cũng thấy ngưỡng mộ sự kiên trì đó
Quy mô là 4,6 triệu dòng, gần như nhỏ hơn dự án này đúng một bậc độ lớn. Mã C được tạo ra khác nhau theo từng target, nhưng không khác nhau theo từng compiler
Các điểm mạnh của Zig gồm hỗ trợ nhiều target cross-compile, toolchain tự host, và LLVM là phụ thuộc tùy chọn; vì vậy lời hứa có thể compile Rust thành C cho các platform hiếm cũng có vẻ như một tín hiệu nhắm về phía Zig
crustcthì không phải vậy. Vì compiler đã được chuyển đổi cũng chỉ hỗ trợ cùng các target compile như compiler gốcTuy nhiên, có thể chuyển đổi các dự án Rust khác sang C rồi chạy và compile chúng trên những target chỉ hỗ trợ C
Dù vậy, không nên đánh giá quá cao tác động. Đây giống một giải pháp hơi rườm rà cho một vấn đề rất ngách.
rustcvốn đã hỗ trợ rất nhiều target, và dự kiến còn mở rộng thêm thông qua backendgccCông cụ cross-compile của Zig rất hay, nhưng chủ yếu là tăng tính tiện lợi, đặc biệt ở phần C vốn khó xử lý hơn Zig hay Rust; ý nghĩa về việc hỗ trợ nhiều target hơn thì tương đối nhỏ
Cuối cùng, hỗ trợ target của Zig và Rust vốn đã khá tương đồng, nên khó trở thành yếu tố quyết định; giữa hai ngôn ngữ còn có nhiều khác biệt quan trọng hơn nhiều
crustcchỉ là một ví dụ cho thấycilly, toolchain chuyển Rust sang C, có thể làm được gìToàn bộ toolchain
cillycompile mã Rust của người dùng thành C cho target bất kỳ. Repository này chỉ cho thấy compiler tự compile chính nó, vì họ nghĩ đây là màn demo ấn tượng nhấtrustcsang C