1 คะแนน โดย GN⁺ 3 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • 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

โค้ด C ตามเป้าหมายและข้อจำกัด ABI

  • โค้ด C ที่ cilly ส่งออกเป็นแบบ เฉพาะคอมไพเลอร์
    • C ของ cilly ที่สร้างสำหรับ Arm64 ไม่สามารถนำไปรันบน riscv32 ตรง ๆ ได้
    • แต่สามารถสร้าง C ของ cilly สำหรับ riscv32 แยกต่างหากได้
  • 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

ฟังก์ชันสร้าง 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 ความคิดเห็น

 
GN⁺ 3 시간 전
ความคิดเห็นจาก Lobste.rs
  • น่าสนใจที่มันสร้าง โปรแกรม witness เพื่อใช้ตรวจสอบว่าคอมไพเลอร์และแพลตฟอร์มที่กำหนดรองรับอะไรบ้าง
    แม้จะรู้สึกแปลกพอสมควรว่าสายการบิลด์ C แบบ configure ดั้งเดิมโดยมากก็ทำงานในลักษณะนี้ แต่สำหรับคอมไพเลอร์หรือทรานสไพเลอร์ตัวนี้ การทำตามรูปแบบนั้นก็ฟังดูสมเหตุสมผล
    “ความพยายามครั้งที่ 14: cilly” ช่างเป็นความมุ่งมั่นที่น่าทึ่ง และน่าอิจฉาความอึดแบบนั้น
  • ถ้าเทียบกันแล้ว ฝั่ง Zig ก็มีเวอร์ชันที่แปลงคอมไพเลอร์ทั้งตัวเป็น C เช่นกัน และนี่เป็นส่วนหนึ่งของขั้นตอนปกติในการบิลด์จากซอร์ส
    ขนาดอยู่ที่ 4.6 ล้านบรรทัด ซึ่งเล็กกว่าโปรเจ็กต์นี้เกือบหนึ่งหลักพอดี C ที่ถูกสร้างขึ้นจะแตกต่างกันตามเป้าหมาย แต่จะไม่ต่างกันตามคอมไพเลอร์
  • ผมไม่ใช่โปรแกรมเมอร์สายระบบ แต่ก็สงสัยว่าโปรเจ็กต์แบบนี้จะมีผลต่อการเลือกระหว่าง Rust กับ Zig หรือไม่
    จุดเด่นของ Zig มีทั้งการรองรับ เป้าหมาย cross-compilation ที่หลากหลาย ทูลเชนแบบ self-hosting และการพึ่งพา LLVM แบบเลือกได้ ส่วนคำสัญญาว่าสามารถคอมไพล์ Rust เป็น C สำหรับแพลตฟอร์มหายากได้ ก็ดูเหมือนเป็นการส่งสัญญาณไปทางฝั่ง Zig อยู่เหมือนกัน
    • ถ้าแค่ใช้ crustc อย่างเดียว ยังไม่ถึงขั้นนั้น เพราะคอมไพเลอร์ที่ถูกแปลงแล้วก็ยังรองรับ เป้าหมายการคอมไพล์ เท่ากับคอมไพเลอร์ต้นฉบับ
      แต่ก็ยังสามารถแปลงโปรเจ็กต์ Rust อื่น ๆ เป็น C เพื่อให้รันและคอมไพล์บนเป้าหมายที่รองรับแค่ C ได้
      ถึงอย่างนั้นก็ไม่ควรประเมินผลกระทบสูงเกินไป มันใกล้เคียงกับทางแก้ที่ค่อนข้างยุ่งยากสำหรับปัญหาเฉพาะทางมากกว่า rustc เองก็รองรับเป้าหมายกว้างอยู่แล้ว และมีแนวโน้มจะเพิ่มได้อีกผ่านแบ็กเอนด์ gcc
      เครื่องมือ cross-compilation ของ Zig นั้นยอดเยี่ยม แต่ดูจะเน้นเพิ่มความสะดวก โดยเฉพาะกับส่วนของ C ที่จุกจิกกว่า Zig หรือ Rust มากกว่า และความหมายในแง่ “รองรับเป้าหมายได้มากกว่า” ก็มีน้ำหนักน้อยกว่า
      สุดท้ายแล้ว การรองรับเป้าหมายของ Zig กับ Rust ก็คล้ายกันมากอยู่แล้ว จึงยากจะเป็นปัจจัยชี้ขาด และยังมีความต่างที่สำคัญกว่านั้นระหว่างสองภาษานี้อีกมาก
  • มันเจ๋งดี แต่สุดท้ายแล้วก็ไม่ได้ถูกจำกัดด้วย ความสามารถของ LLVM หรอกหรือ?
    • ไม่เห็นว่าทำไมต้องเป็นแบบนั้น crustc เป็นเพียงตัวอย่างที่แสดงให้เห็นว่าทูลเชน cilly ซึ่งเป็นเครื่องมือแปลง Rust เป็น C ทำอะไรได้บ้าง
      ทูลเชน cilly แบบเต็มสามารถคอมไพล์โค้ด Rust ของผู้ใช้ให้เป็น C สำหรับ เป้าหมายตามต้องการ ได้ ส่วนรีโปนี้คงเพราะมันเป็นเดโมที่หวือหวาที่สุด จึงเลือกโชว์ให้เห็นว่าคอมไพเลอร์คอมไพล์ตัวเองได้
  • mrustc(https://github.com/thepowersgang/mrustc) ก็น่าลองดูเช่นกัน เป็นคอมไพเลอร์ Rust ที่เขียนด้วย C++ และทำงานโดยทรานสไพล์เป็น C ก่อนส่งต่อให้ GCC ดังนั้นจึงสามารถแปลง rustc เป็น C ได้เช่นกัน