crustc: แปลง rustc ทั้งหมดเป็น C
(github.com/FractalFir)crustcเป็นเดโมที่แปลงrustc 1.98.0-nightly (c712ea946 2026-06-16)ทั้งหมดให้เป็น โค้ด C 46 ล้านบรรทัด และเมื่อบิลด์ด้วยGCCกับmakeจะได้คอมไพเลอร์ Rust ที่ใช้งานได้- เครื่องมือพื้นฐานอย่าง
cillyเป็น แบ็กเอนด์คอมไพเลอร์ Rust ที่คอมไพล์ Rust เป็น C และรีโพนี้ถูกจัดทำขึ้นเป็นโชว์เคสที่โดดเด่นที่สุดของการที่คอมไพเลอร์คอมไพล์ตัวเอง cillyใช้ witness program เพื่อสอบถาม layout ของชนิดข้อมูล, ขนาด, alignment, encoding ของอักขระ, รูปแบบจำนวนเต็ม ฯลฯ จากคอมไพเลอร์ C และแพลตฟอร์มเป้าหมาย แล้วสร้างโค้ด C ที่คอมไพเลอร์ C เฉพาะนั้นยอมรับได้- เป้าหมายหลักคือทำให้ใช้ Rust ได้บน ฮาร์ดแวร์เก่าหรือแปลกเฉพาะทาง ที่ไม่มีการรองรับ LLVM/GCC แต่มีคอมไพเลอร์ C และยังรวมถึงความโปร่งใสผ่านเครือข่ายที่สื่อสารกับคอมไพเลอร์ C ระยะไกลผ่าน TCP
- C ที่สร้างขึ้นในปัจจุบันมีเป้าหมายเป็น ARM64 Linux ซึ่งเป็น ISA ของเวิร์กสเตชันผู้เขียน และ toolchain ทั้งหมดของ
cillyยังไม่พร้อมให้ใช้งานสาธารณะ อีกทั้งยังอยู่ระหว่างติดตามบั๊กที่เกี่ยวกับการปรับแต่งประสิทธิภาพ
เดโมการแปลง rustc เป็น C
crustcเป็นรีโพที่แปลงrustc 1.98.0-nightly (c712ea946 2026-06-16)ให้เป็น โค้ด C 46 ล้านบรรทัด- โค้ด C นี้สามารถบิลด์ด้วย
GCCและmakeได้ และผลลัพธ์ของการบิลด์คือ คอมไพเลอร์ Rust ที่ทำงานได้ - ตัวอย่างการรันคือระบุพาธไลบรารี LLVM แล้วรัน
./rustc/rustc --versionเพื่อแสดงเวอร์ชันrustc 1.98.0-nightlyเดียวกัน - คอมไพเลอร์ Rust ที่สร้างขึ้นสามารถคอมไพล์โค้ด และบิลด์
core,alloc,stdได้ - ในโค้ดมี C++ LLVM wrapper บางส่วนรวมอยู่ นอกเหนือจากโค้ด C
- Rust ใช้ C++ เพื่อเปิดเผยความสามารถบางส่วนของ LLVM
- wrapper ดังกล่าวขึ้นกับเวอร์ชัน LLVM และการบิลด์แยกค่อนข้างยุ่งยาก จึงให้มาในรูปแบบที่คอมไพล์ไว้ล่วงหน้า
บทบาทของ cilly
crustcเป็น เดโม/ทีเซอร์ของcillyซึ่งเป็น toolchain คอมไพเลอร์ Rust-to-C ตัวใหม่- toolchain ทั้งหมดของ
cillyมีเป้าหมายเพื่อคอมไพล์โค้ด Rust ของผู้ใช้เป็น C ให้เหมาะกับเป้าหมายใดก็ได้ - รีโพนี้จัดทำขึ้นเพื่อแสดงให้เห็นว่า
cillyคอมไพล์ตัวคอมไพเลอร์เอง cillyเป็นทั้งไลบรารี Rust และแบ็กเอนด์คอมไพเลอร์ Rust กล่าวคือคอมไพล์ Rust เป็น C ในรูปแบบปลั๊กอิน- ผู้เขียนระบุว่าได้ทำงานเรื่องการคอมไพล์ Rust เป็น C มาตลอด 3 ปีที่ผ่านมา และหลังจากความพยายามแบบเปิดเผยอย่าง
rustc_codegen_clrรวมถึงความพยายามแบบไม่เปิดเผยอีกหลายครั้งcillyเป็นความพยายามครั้งที่ 14
วิธีสร้างโค้ดให้เข้ากับคอมไพเลอร์ C
- คุณสมบัติหลักของ
cillyคือการ ปรับตัวตามคอมไพเลอร์ C - สามารถสร้าง witness program เพื่อตรวจสอบว่าคอมไพเลอร์และแพลตฟอร์มเฉพาะรองรับอะไรบ้าง
- ตัวอย่างคือ
_Thread_local int KEYWORD_TLS_SUPPORTED;ซึ่งจะคอมไพล์ได้ก็ต่อเมื่อคอมไพเลอร์ C นั้นรองรับ_Thread_local
- ตัวอย่างคือ
cillyพยายามสร้างโค้ด C ที่คอมไพเลอร์ C เฉพาะนั้นยอมรับได้- layout ของชนิดข้อมูล, ขนาด, alignment, encoding ของอักขระ และรูปแบบจำนวนเต็ม เป็นสิ่งที่ถูกสอบถาม
- encoding ของอักขระจะตรวจสอบว่าเป็น ASCII หรือไม่
- รูปแบบจำนวนเต็มจะตรวจสอบว่าเป็น two's complement หรือไม่
- ใช้ fallback เมื่อทำได้
- พยายามหลีกเลี่ยงสมมติฐานนอกเหนือจาก ANSI C และมีทางเลี่ยงสำหรับพฤติกรรมที่เกี่ยวกับมาตรฐาน C สมัยใหม่ เช่น strict aliasing
- ในบางกรณีที่พบไม่บ่อย อาจต้องใช้ สมมติฐานที่สมเหตุสมผล เช่น การแปลงไปกลับ
(void*)(uintptr_t)(ptr)- สมมติฐานเหล่านี้จะถูกบันทึกไว้ในเอกสาร และหากทำได้จะเพิ่ม assert เช่น
CHAR_BIT = 8
- สมมติฐานเหล่านี้จะถูกบันทึกไว้ในเอกสาร และหากทำได้จะเพิ่ม assert เช่น
โค้ด C ตามเป้าหมายและข้อจำกัด ABI
- โค้ด C ที่
cillyส่งออกเป็นแบบ เฉพาะคอมไพเลอร์- C ของ
cillyที่สร้างสำหรับ Arm64 ไม่สามารถนำไปรันบน riscv32 ตรง ๆ ได้ - แต่สามารถสร้าง C ของ
cillyสำหรับ riscv32 แยกต่างหากได้
- C ของ
- C ที่สร้างจาก
rustcในรีโพนี้มีเป้าหมายเป็น ARM64 Linux เนื่องจาก ISA ของเวิร์กสเตชันผู้เขียน - โค้ดที่
cillyสร้างโดยทั่วไปเข้ากันได้กับ ABI ของโค้ดที่rustcปกติคอมไพล์ - ในบางแพลตฟอร์ม
rustcเลือก ABI ที่ไม่สามารถแทนด้วย C ได้ ทำให้การเข้ากันได้อย่างสมบูรณ์เป็นเรื่องยาก - บน Arm64 มีข้อจำกัดจาก
sretซึ่งเป็นพอยน์เตอร์สำหรับการคืนค่า struct- บนแพลตฟอร์มส่วนใหญ่
sretจะถูกส่งผ่านรีจิสเตอร์เดียวกับอาร์กิวเมนต์แรก ทำให้ใช้วิธีวางอาร์กิวเมนต์แรกเป็นพอยน์เตอร์เอาต์พุตได้ - บน Arm64 พอยน์เตอร์
sretถูกส่งผ่านรีจิสเตอร์อื่น - อธิบายว่าคอมไพเลอร์ C แบบเนทีฟควรเลือก return-by-sret สำหรับ struct ขนาดเล็ก แต่ใน struct ขนาดเล็กต่ำกว่า 16 ไบต์กลับไม่ทำเช่นนั้น
- บนแพลตฟอร์มส่วนใหญ่
การรองรับเป้าหมายเก่าหรือแปลกเฉพาะทาง
- เป้าหมายหลักของโปรเจกต์นี้คือทำให้ใช้ Rust ได้บน ฮาร์ดแวร์เก่าหรือแปลกเฉพาะทาง ที่ไม่มีการรองรับ LLVM/GCC แต่รองรับ C
- เมื่อโปรเจกต์ใดเปลี่ยนจาก Rust ไปเป็น C หรือมีทางเลือกภาษา Rust สำหรับโปรเจกต์ C การขาดการรองรับเป้าหมายเหล่านี้อาจถูกยกเป็นข้อเสียของ Rust
cillyห่อหุ้มrustcและคอมไพเลอร์ C แล้วแปลงโค้ด Rust เป็น C แบบทันที- จากมุมมองผู้ใช้ วิธีนี้ใกล้เคียงกับการกำหนดคอมไพเลอร์ C ที่จะใช้กับเป้าหมายเฉพาะ
- ตัวอย่างการตั้งค่าใช้ triple
sdcc_z180-unknown-noneกับ/usr/bin/sdcc,-mz180,--std-c89, อาร์กิวเมนต์-c
ความโปร่งใสผ่านเครือข่ายและคอมไพเลอร์ C ระยะไกล
cillyมี ความโปร่งใสผ่านเครือข่าย และสามารถสื่อสารกับคอมไพเลอร์ C ผ่าน TCP ได้- หากจำเป็น อาจขยายไปยังวิธีสื่อสารที่แปลกยิ่งขึ้น เช่น UART ได้
- วิธีนี้เป็นแนวทางแก้ bootstrap paradox ของแพลตฟอร์มที่ไม่มี C cross-compiler
- สามารถบิลด์และรันเซิร์ฟเวอร์ C ขนาดเล็กบน OS เป้าหมาย แล้วรัน
rustcบนแพลตฟอร์มทั่วไปอย่าง Linux จากนั้นให้cillyสื่อสารผ่านเครือข่าย - ผู้เขียนรัน
rustcบน Arm64 Linux และคอมไพล์โปรแกรม Rust ขนาดเล็กสำหรับ VM x86 Plan 9 ได้สำเร็จ- เอาต์พุตของสภาพแวดล้อม Plan 9 คือ
gnot osversion 2000 cputype 386 - ผลการรัน
/tmp/hello_plan9คือHello, world! - ผลลัพธ์
nmแสดงสัญลักษณ์rust_begin_unwind
- เอาต์พุตของสภาพแวดล้อม Plan 9 คือ
ฟังก์ชันสร้าง makefile
cillyสามารถแทรก marker ลงในอ็อบเจ็กต์ไฟล์และเก็บ IR ไว้ในไดเรกทอรีแคชได้ตามตัวเลือก- จากนั้นสามารถอ่าน marker ดังกล่าวเพื่อแยกฟังก์ชันและ global ตามตำแหน่งที่นิยามได้
- จากข้อมูลนี้ จะสร้างไดเรกทอรีที่มี makefile เพื่อให้บิลด์ Rust ได้ด้วยคอมไพเลอร์ C และ
makeเท่านั้น
เงื่อนไขการบิลด์และการรัน
- ระบบที่ใช้บิลด์เดโมคือ ARM64 Linux ที่อิง Ubuntu
- สตริงเคอร์เนลคือ
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 ดังกล่าว - คำสั่งบิลด์คือระบุพาธ
libLLVM.so.22.1-rust-1.98.0-nightlyผ่านLLVM_LIB_DIRแล้วรันmake -j20 CFLAGSใช้งานได้ แต่บางแฟล็กอาจทำให้คอมไพล์ช้าลง- ไม่แนะนำให้เปิด optimization
- เดโมยังไม่เรียบร้อยนัก และ optimization อาจก่อปัญหาได้
- ในระดับขนาดนี้ optimization ใช้เวลามาก
- หากไม่เปิด optimization บนเครื่องของผู้เขียนจะบิลด์เสร็จภายในไม่กี่นาที
- ค่าที่วัดได้คือ
937.98s user,123.77s system,1352% cpu,1:18.48 total
- ค่าที่วัดได้คือ
- เมื่อเปิด optimization โค้ดส่วนใหญ่จะผ่านไปอย่างรวดเร็ว แต่อาจติดอยู่ที่ไฟล์ Rust ขนาดใหญ่บางไฟล์
การทดสอบและปัญหาที่ทราบ
- การทดสอบบิลด์ทำโดยกำหนดไลบรารี LLVM ของ nightly และ
./rustc_driverในLD_LIBRARY_PATHแล้วรัน./rustc/rustc --version - หากต้องการบิลด์โปรแกรมทั่วไป ต้องบิลด์
std- หากไม่มี
stdจะเกิดข้อผิดพลาดerror[E0463]: can't find crate for std - การบิลด์ไลบรารีมาตรฐานควรดู
BUILDING_STD.md
- หากไม่มี
- บั๊กที่ทราบคือ เนื่องจากปัญหาการ normalize พาธที่แปลก
crustcอาจ crash เมื่อรันจากไดเรกทอรีที่บิลด์crustcซึ่งก็คือรูทของรีโพ - ในตำแหน่งอื่นจะทำงานได้ตามปกติ
สถานะการเปิดเผย cilly
cillyยัง ไม่พร้อมสำหรับการใช้งานสาธารณะ- ผู้เขียนระบุว่ามีแผนจะเปิดเผยให้เร็วที่สุดเท่าที่ทำได้
- เหตุผลที่การเปิดเผยล่าช้า ได้แก่ งานประจำ, วิทยานิพนธ์มหาวิทยาลัย และอาการบาดเจ็บที่มือ
- หนึ่งในเหตุผลที่ toolchain ทั้งหมดของ
cillyยังไม่ถูกเปิดเผยคือกำลังติดตาม บั๊กที่เกี่ยวกับ optimization อยู่
1 ความคิดเห็น
ความคิดเห็นจาก Lobste.rs
แม้จะรู้สึกแปลกพอสมควรว่าสายการบิลด์ C แบบ
configureดั้งเดิมโดยมากก็ทำงานในลักษณะนี้ แต่สำหรับคอมไพเลอร์หรือทรานสไพเลอร์ตัวนี้ การทำตามรูปแบบนั้นก็ฟังดูสมเหตุสมผล“ความพยายามครั้งที่ 14: cilly” ช่างเป็นความมุ่งมั่นที่น่าทึ่ง และน่าอิจฉาความอึดแบบนั้น
ขนาดอยู่ที่ 4.6 ล้านบรรทัด ซึ่งเล็กกว่าโปรเจ็กต์นี้เกือบหนึ่งหลักพอดี C ที่ถูกสร้างขึ้นจะแตกต่างกันตามเป้าหมาย แต่จะไม่ต่างกันตามคอมไพเลอร์
จุดเด่นของ Zig มีทั้งการรองรับ เป้าหมาย cross-compilation ที่หลากหลาย ทูลเชนแบบ self-hosting และการพึ่งพา LLVM แบบเลือกได้ ส่วนคำสัญญาว่าสามารถคอมไพล์ Rust เป็น C สำหรับแพลตฟอร์มหายากได้ ก็ดูเหมือนเป็นการส่งสัญญาณไปทางฝั่ง Zig อยู่เหมือนกัน
crustcอย่างเดียว ยังไม่ถึงขั้นนั้น เพราะคอมไพเลอร์ที่ถูกแปลงแล้วก็ยังรองรับ เป้าหมายการคอมไพล์ เท่ากับคอมไพเลอร์ต้นฉบับแต่ก็ยังสามารถแปลงโปรเจ็กต์ Rust อื่น ๆ เป็น C เพื่อให้รันและคอมไพล์บนเป้าหมายที่รองรับแค่ C ได้
ถึงอย่างนั้นก็ไม่ควรประเมินผลกระทบสูงเกินไป มันใกล้เคียงกับทางแก้ที่ค่อนข้างยุ่งยากสำหรับปัญหาเฉพาะทางมากกว่า
rustcเองก็รองรับเป้าหมายกว้างอยู่แล้ว และมีแนวโน้มจะเพิ่มได้อีกผ่านแบ็กเอนด์gccเครื่องมือ cross-compilation ของ Zig นั้นยอดเยี่ยม แต่ดูจะเน้นเพิ่มความสะดวก โดยเฉพาะกับส่วนของ C ที่จุกจิกกว่า Zig หรือ Rust มากกว่า และความหมายในแง่ “รองรับเป้าหมายได้มากกว่า” ก็มีน้ำหนักน้อยกว่า
สุดท้ายแล้ว การรองรับเป้าหมายของ Zig กับ Rust ก็คล้ายกันมากอยู่แล้ว จึงยากจะเป็นปัจจัยชี้ขาด และยังมีความต่างที่สำคัญกว่านั้นระหว่างสองภาษานี้อีกมาก
crustcเป็นเพียงตัวอย่างที่แสดงให้เห็นว่าทูลเชนcillyซึ่งเป็นเครื่องมือแปลง Rust เป็น C ทำอะไรได้บ้างทูลเชน
cillyแบบเต็มสามารถคอมไพล์โค้ด Rust ของผู้ใช้ให้เป็น C สำหรับ เป้าหมายตามต้องการ ได้ ส่วนรีโปนี้คงเพราะมันเป็นเดโมที่หวือหวาที่สุด จึงเลือกโชว์ให้เห็นว่าคอมไพเลอร์คอมไพล์ตัวเองได้rustcเป็น C ได้เช่นกัน