1 poin oleh GN⁺ 2024-08-26 | 1 komentar | Bagikan ke WhatsApp
  • Dozer adalah kompiler Rust berbasis C murni yang bertujuan memungkinkan Rust digunakan pada tahap bootstrap yang lebih awal, dan sedang ditulis tanpa C++, flex, yacc, maupun Makefile
  • Kompiler resmi rustc ditulis dalam Rust, sehingga versi baru dibangun dengan rustc versi sebelumnya; rantai ini dapat ditelusuri kembali hingga kompiler Rust awal yang ditulis dalam OCaml serta lapisan Guile dan C
  • Bootstrap Linux dari Bootstrappable Builds dimulai dari seed biner 512 byte, lalu berkembang ke kompiler sederhana, shell, subset C, TinyCC, yacc, coreutils, Bash, autotools, GCC, dan Linux
  • Saat ini Rust muncul pada tahap yang terlambat lewat mrustc yang ditulis dalam C++ untuk mengompilasi rustc 1.56, sehingga Rust sulit digunakan sebelum C++ diperkenalkan
  • Dozer menargetkan menjadi kompiler Rust yang dapat di-bootstrap dengan TinyCC, lalu ingin menghubungkannya ke libcore, backend Cranelift untuk rustc, alat pengganti cargo, hingga pembangunan ulang rustc/cargo canonical

Tujuan dan batasan Dozer

  • Dozer adalah kompiler Rust yang sedang ditulis dalam C murni
  • Tidak menggunakan C++, maupun flex, yacc, atau Makefile
  • Tujuan utamanya adalah membuat kompiler yang memungkinkan Rust di-bootstrap dari C
  • Secara khusus, kompiler ini harus dapat di-bootstrap dari TinyCC, dengan asumsi sistem tidak memiliki alat berguna selain kompiler C dan shell yang sangat dasar

Masalah kompiler Rust yang membangun dirinya sendiri

  • Untuk menjalankan kode Rust, diperlukan kompilasi, dan umumnya cargo build secara internal memanggil rustc
  • rustc sendiri juga merupakan kompiler Rust yang ditulis dalam Rust, sehingga rustc baru dikompilasi dengan rustc versi sebelumnya
    • rustc 1.80.0 dikompilasi dengan rustc 1.79.0
    • Rantai ini terus berlanjut ke versi yang lebih lama seperti rustc 1.78.0
  • Tahap awal dapat ditelusuri kembali hingga Rust 0.7, saat kompiler pada titik ini ditulis dalam OCaml
  • Karena kompiler OCaml juga diperlukan, rantai bootstrap kembali berlanjut ke implementasi bahasa lain
    • camlboot dapat menggunakan Guile untuk mengompilasi kompiler OCaml
    • Interpreter Guile ditulis dalam C

Rantai bawah dari Bootstrappable Builds

  • Bootstrappable Builds membahas alur mem-bootstrap seluruh sistem dari seed biner kecil
  • Linux bootstrap process dimulai dari seed biner 512 byte
    • Seed ini berisi kompiler yang sangat sederhana yang menerima angka heksadesimal dan mengeluarkan byte mentah yang sesuai
    • Daftar byte heksadesimal, jika komentar dan spasi diabaikan, juga secara teknis diperlakukan sebagai kode sumber yang dapat dianalisis
  • Tahap-tahap berikutnya secara bertahap membangun alat dengan tingkat yang lebih tinggi
    • Sistem operasi yang sangat sederhana
    • Shell dasar
    • Kompiler yang sedikit lebih maju
    • Tahap yang tampak seperti kode assembly
    • Subset C yang sangat dasar
    • Kompiler C yang lebih maju yang ditulis dengan subset C tersebut
  • Setelah beberapa tahap, TinyCC dapat dikompilasi, lalu berlanjut ke yacc, coreutils dasar, Bash, autotools, GCC, hingga Linux
  • Setiap tahap dicantumkan di live-bootstrap parts.rst

Rust muncul terlalu terlambat dalam rantai bootstrap

  • Saat ini Rust muncul pada tahap yang sangat terlambat dalam proses ini
  • Implementasi yang digunakan adalah mrustc, yaitu implementasi Rust alternatif yang ditulis dalam C++
  • mrustc dapat mengompilasi rustc 1.56, lalu kompilasi berlanjut hingga kode Rust modern setelahnya
  • Namun, saat C++ diperkenalkan ke dalam rantai bootstrap, bootstrap pada dasarnya sudah hampir selesai
  • Untuk menggunakan Rust pada tahap sebelum C++ diperkenalkan, diperlukan kompiler Rust yang dapat di-bootstrap dari C

Status implementasi Dozer saat ini

  • Dozer telah dikerjakan selama sekitar dua bulan, dan ditulis tanpa ekstensi
  • Saat ini dapat dikompilasi tanpa masalah dengan TinyCC maupun cproc
  • Backend menggunakan QBE
  • Implementasinya masih pada tahap awal
    • Lexer sudah selesai
    • Parser sudah diimplementasikan dalam porsi cukup besar
    • Ekspansi makro/modul ditunda sejauh mungkin
    • Pemeriksaan tipe saat ini hanya mendukung i32
    • Generasi kode masih kasar
  • Saat ini kode Rust berikut dapat dikompilasi dengan sukses
fn rust_main() -> i32 {
    (2 - 1) * 6 + 3
}

Rencana menuju rustc

  • Tujuannya adalah mengembangkan Dozer secara bertahap agar dapat mengompilasi contoh penggunaan libc dasar, lalu kemudian mengompilasi libcore dan rustc
  • Untuk mengompilasi rustc, rencananya akan digunakan backend Cranelift
    • Backend Cranelift seluruhnya ditulis dalam Rust
    • Karena diasumsikan tidak ada C++, LLVM tidak dapat dikompilasi
  • Ada juga rencana membuat alat pengganti cargo yang dapat mengompilasi paket Rust dengan Dozer
  • File yang dibuat otomatis di sumber rustc harus ditemukan dan dihapus
    • Dalam aturan proyek Bootstrappable, kode yang dibuat otomatis tidak diperbolehkan
  • Tujuan akhirnya adalah mengompilasi rustc dan cargo, lalu membuat proses untuk mengompilasi ulang rustc/cargo canonical dengan rustc/cargo yang dikompilasi sendiri
  • Proyek ini adalah pekerjaan tersulit yang pernah ditangani sejauh ini, dan posisinya adalah tetap mencoba meski ada keraguan soal kemungkinan penyelesaiannya

1 komentar

 
GN⁺ 2024-08-26
Komentar Hacker News
  • Jika ingin melakukan bootstrap Rust, saya rasa saya akan membuat proto-Rust dalam C yang fiturnya lebih sedikit daripada Rust penuh, lalu menulis compiler Rust lengkap dengan proto-Rust itu
    Misalnya, proto-Rust tidak memiliki borrow checker, dukungan macro terbatas atau tidak ada, mungkin juga tidak membebaskan memori, dan tidak perlu menghasilkan kode yang bagus
    Pada dasarnya ini akan lebih mirip C dengan sintaks Rust, tetapi dari sudut pandang penggemar Rust, itu tampak lebih baik daripada menulis compiler Rust dalam “C dengan sintaks C” yang menjadi tujuan proyek ini
    Saya penasaran kenapa jalur seperti ini tidak dipilih

    • Sebagai referensi, mrustc, compiler Rust non-Rust yang paling dikenal saat ini, juga memang tidak memiliki borrow checker
      Menghapus borrow checker tidak akan merusak program yang benar, hanya akan membuat banyak program yang salah menjadi bisa dikompilasi
      Kegunaan utama mrustc adalah untuk mengompilasi rustc, dan kita sudah tahu bahwa rustc bisa mengompilasi dirinya sendiri tanpa error borrow checker, jadi itu tidak masalah
    • Di Mozart/Oz, mereka memang benar-benar melakukan itu. Ada compiler proto-Oz yang ditulis dalam Scala, dan itu digunakan untuk mengompilasi compiler asli yang ditulis dalam Oz
      Karena compiler Scala menghasilkan kode yang tidak efisien, compiler asli itu kemudian dikompilasi ulang dengan dirinya sendiri
      Dengan begitu, pada akhirnya didapat compiler asli yang efisien dan menghasilkan kode yang bagus, dan proses ini termasuk dalam build standar bahasa tersebut
      https://github.com/mozart/mozart2
    • Kalau begitu, pada akhirnya berarti memakai dua compiler, jadi saya tidak yakin apa manfaat nyata yang didapat selain pekerjaan tambahan
  • Sebagai hobi saya sedang membuat compiler C dalam Rust, dan sebagai lelucon bahwa Rust jelas lebih berat daripada C, saya menyebutnya Small C Compiler. Ini parodi dari “Tiny C Compiler”
    Saya memakai Cranelift sebagai backend, tetapi keseluruhan struktur compilernya dibuat agar mudah dibongkar-pasang lewat banyak trait dan mudah di-hack
    Saya belum berniat merilisnya sebagai open source sampai setidaknya bisa menangani printf("%s", "Hello World!")
    Saya sempat mencoba mengimplementasikan preprocessor dan parser, dan karena masalah typedef yang terkenal buruk itu saya juga sempat terlibat dengan rust-peg dan HimeCC
    Saya tahu di industri orang memakai symbol table untuk mempertahankan konteks typedef, tetapi ada keterbatasan karena tipe di bawahnya tidak bisa dibaca. Saya penasaran apa solusi ala akademis untuk ini; yang terpikir oleh saya hanya transactional memory
    Kalau nanti ada sesuatu yang cukup membantu, mungkin akhirnya akan saya buka juga

  • Benar-benar keren, dan yang menarik adalah jenis masalah bootstrap yang sama juga ada di hardware
    Komputer dibuat oleh apa? Oleh komputer yang sudah dibuat sebelumnya dan software yang berjalan di atasnya. Semakin dipikirkan, semakin menarik

    • Masalah bootstrap yang sama ada pada semua hal. Jalan dibuat oleh apa? Oleh alat berat konstruksi. Tapi kalau jalannya belum ada, bagaimana alat-alat itu dibawa ke lokasi kerja?
      Beberapa bulan lalu saya bertemu seseorang yang bekerja di startup pengiriman/pemenuhan material untuk proyek konstruksi
      Pekerjaan seperti ini memerlukan keahlian yang berbeda dari pengiriman umum seperti Amazon, karena materialnya sering punya sifat yang tidak biasa atau berbahaya, dan alamat tujuan pengiriman pun sering kali bahkan belum ada
      Ini memang bisa diatasi, tetapi tampaknya memerlukan keahlian khusus yang melampaui kemampuan umum perusahaan pengiriman modern
    • Saya pernah bekerja di perusahaan yang membangun data center, dan mereka berusaha membuat software sampai pada tingkat di mana seluruh data center bisa dinyalakan hanya dengan sebuah laptop
      Alasannya adalah karena mereka bekerja sama dengan perusahaan-perusahaan Eropa dan perlu membuktikan kepada regulator bahwa tidak ada backdoor
      Itu masalah yang sangat menarik tetapi juga sangat sulit; tim kami hanya terlibat secara tidak langsung, tetapi kami mengerjakan pengiriman data lewat proxy agar semua data dapat diaudit dan terjamin tidak mengirim hal-hal yang tidak boleh dikirim
      Saya keluar dari perusahaan itu sebelum proyeknya selesai, dan kemudian saya dengar proyek itu dibatalkan karena terlalu sulit
    • Jika melihat opcode oktal assembly pada Cray-1 lama atau opcode word pada IBM System/360, kita akan sadar bahwa semuanya dibuat sangat sederhana sampai orang bisa langsung menulis byte opcode dan melakukan assembly dengan tangan
      Setelah itu x86 muncul tanpa anggaran besar atau pembeli besar, dan assembly-nya dirancang seefisien dan sepadat mungkin
      Akibatnya, ia kehilangan karakteristik yang dulu bisa dimiliki mesin lain dengan lebih nyaman
    • Ini salah satu hal paling keren dari proyek bootstrap seperti ini dan reproducible build
      Secara teori, kita bisa membuat sendiri komputer yang sangat sederhana hanya dari komponen-komponen individual
      Komputer itu akan besar, tidak efisien, dan sangat lambat, tetapi tetap bisa dibuat mengikuti instruction set architecture tertentu, lalu dipakai untuk membangun program bootstrap di atasnya
      Dengan begitu, kita bisa mengklaim bahwa hasil dari komputer buruk yang sepenuhnya kita pahami itu sama dengan hasil dari hardware modern yang tidak sepenuhnya kita percayai
    • Ini juga menarik kalau dipikirkan pada level peradaban manusia. Jika umat manusia somehow kembali ke Zaman Batu pada titik waktu sekarang, apakah kita bisa membangun kembali sampai ke level saat ini?
      Itu semacam masalah bootstrap juga. Misalnya, cadangan minyak saat ini lebih sulit diekstraksi daripada 100 tahun lalu, jadi saya penasaran apakah kita bisa melakukan bootstrap kembali sampai ke sana
  • Agak menjengkelkan karena harus mengikuti tautan sampai 4 kali hanya untuk menemukan alasan tingkat tinggi yang menjelaskan manfaat bootstrapping
    Saya berharap bagian “Why” pada judul akan membahas itu
    https://bootstrappable.org/benefits.html

    • Menjelaskan mengapa bootstrapping itu penting memang bisa sulit. Karena itu saya juga menambahkan bagian “Why?” di README kompiler bootstrap saya
      Keamanan adalah alasan besar, dan itu yang terutama ditekankan oleh tim bootstrappable
      Untuk menghindari masalah trusting trust dan serangan seperti backdoor xz baru-baru ini, kita harus bisa mem-bootstrap semuanya dari source code murni
      Mereka bahkan menghapus semua berkas yang dihasilkan sebelumnya agar hanya bergantung pada hal-hal yang ditulis tangan dan bisa diaudit. Misalnya, bootstrapping Python jadi cukup rumit karena source-nya berisi kode yang dihasilkan oleh skrip Python
      Saya sendiri lebih tertarik pada sisi pelestarian budaya. Kita mungkin ingin menyimpan media modern di tempat seperti Arctic World Archive untuk arkeolog masa depan, tetapi itu tidak ada artinya jika tidak ada cara untuk mendekodenya
      Kita bisa menyimpan spesifikasinya, tetapi tidak realistis berharap mereka akan mengimplementasikan x265 dan semua yang dibutuhkan dari nol. Jika yang disimpan adalah biner, maka mereka harus menjalankan hardware berusia seribu tahun atau memvirtualisasikan CPU berusia seribu tahun
      Kita bisa memberi definisi Lisp sederhana beserta kode yang berjalan di atasnya, tetapi siapa yang akan mengimplementasikan x265 dengan Lisp dasar. Itu tidak realistis
      Karena itu, dalam proyek saya, saya membuat mesin virtual sederhana lalu mem-bootstrap C di atasnya
      Ini bisa dipindahkan dengan sangat mudah bukan hanya ke arsitektur saat ini, tetapi juga ke arsitektur masa depan atau bahkan arsitektur alien. Arkeolog masa depan atau peradaban alien bisa mengimplementasikan VM itu dalam sehari, menjalankan bootstrap C di atasnya, lalu mengompilasi ffmpeg dan sebagainya untuk mendekode media kita
      Tidak ada black box; semuanya bisa di-debug, diaudit, dan berupa source code terbuka yang ditulis tangan
      https://github.com/ludocode/onramp?tab=readme-ov-file#why-bo...
      https://en.wikipedia.org/wiki/Arctic_World_Archive
  • Agak membingungkan. Baru di tengah tulisan dijelaskan alasan memulai perjalanan yang disebut di judul, dan intinya adalah bahwa pada saat C++ masuk ke rantai bootstrap, proses bootstrap pada dasarnya sudah selesai, jadi tidak ada cara untuk memakai Rust sebelum titik itu
    Jadi maksudnya tampaknya adalah akan bagus jika ada kompiler Rust yang bisa dibootstrap dari TinyCC pada sistem yang diasumsikan belum punya alat yang berguna, yaitu C, atau lebih spesifik lagi
    Tetapi ini bertentangan dengan premis di bagian awal. rustc bisa ditelusuri mundur sehingga 1.80.0 dikompilasi oleh 1.79.0, 1.79.0 oleh 1.78.0, dan seterusnya sampai 0.7, dan kompiler pada masa itu ditulis dalam OCaml
    Selain itu, disebut juga ada proyek yang berhasil mengompilasi kompiler OCaml dengan Guile, dan interpreter Guile sendiri ditulis dalam C
    Kalau begitu, jalur tanpa C++ yang diinginkan penulis sebenarnya sudah ada, hanya saja itu bukan jalur yang dipakai tim rustc sehari-hari
    Jadi motivasinya pada akhirnya tidak jelas. Apakah ingin membuat proses bootstrap berbasis C yang lebih baik, atau ingin menjadikannya cara bootstrap harian untuk rustc, mengapa ingin menghilangkan tahap C++, dan mengapa lebih memilih tahap C, saya tidak tahu
    Kalau memang sekadar ingin melakukannya, ya tidak masalah, tetapi meskipun sudah membaca tulisan yang cukup panjang, saya masih belum melihat tujuan lain di luar itu

    • Secara teknis memang mungkin mem-bootstrap Rust dari Guile dan kompiler Rust 0.7, tetapi itu mengharuskan mengompilasi ulang kompiler Rust sekitar 100 kali
      Setiap tahap memakan waktu berjam-jam, dan karena 1.80 memerlukan 1.79, 1.79 memerlukan 1.78, dan seterusnya, tidak ada tahap yang bisa dilewati sampai kembali ke 0.7
      Bahkan jika sepenuhnya diotomatisasi, bootstrap ini bisa memakan waktu berbulan-bulan
      Selain itu, sejauh yang saya tahu, versi awal rustc hanya menghasilkan LLVM, jadi untuk mengompilasi LLVM tetap harus mem-bootstrap kompiler C++
      Jika sudah punya kompiler C++, Anda bisa langsung mengompilasi mrustc. Saat ini mrustc hanya mendukung sampai rustc 1.54, jadi tetap saja harus melewati sekitar 35 versi saat kompilasi
      Semua proses ini tidak praktis. Tujuan Dozer adalah mem-bootstrap kompiler C kecil, mengompilasi Dozer, lalu langsung mengompilasi rustc terbaru
      Dengan begitu, Anda bisa langsung mendapatkan Rust tanpa harus mem-bootstrap C++ atau tahap perantara
  • Jika GCC 4 dan binutils bisa dipisahkan dari skrip build aslinya, sepertinya sekitar setengah daftar itu bisa dipangkas
    Cukup banyak item di sana hanyalah membangun ulang berbagai hal sekelas autoconf dan dependensinya berulang kali
    https://github.com/fosslinux/live-bootstrap/blob/master/part...

  • Saya kurang paham inti maksudnya. Untuk membuat biner baru yang berjalan di mesin target, rustc harus mendukung arsitektur target
    Kalau dukungan itu sudah ditambahkan ke rustc, ya tinggal biarkan rustc membangun dirinya sendiri

    • Intinya bukan dukungan untuk arsitektur baru, melainkan memiliki proses bootstrap yang jauh lebih pendek dan dapat diaudit
  • Kadang saya membayangkan menulis interpreter atau kompiler C++ dalam Scheme
    Langsung melompat dari Scheme ke GCC modern bisa menjadi jalan pintas yang luar biasa
    Tetapi secara umum orang menganggap menulis kompiler C++ itu nyaris mustahil. Meski begitu, sepertinya akan membantu untuk belajar

  • Dari sub-assembler hingga seluruh stack, mungkinkah ini menjadi cara untuk menghindari masalah trusting trust?
    https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_Ref...

    • Itu hanya mungkin jika semuanya diaudit dan seluruh proses dijalankan sendiri.
      Meski begitu, ada hal seperti https://en.m.wikipedia.org/wiki/Underhanded_C_Contest, dan beberapa karya pesertanya terasa seperti tetap akan terlewat bahkan jika saya yang mengauditnya.
    • Bukankah itu memang intinya?
  • Saat sedikit belajar C, saya pernah mencari bagaimana orang melakukan hal-hal seperti C++ di C, dan melihat implementasi objek, pengecualian, konkurensi, dan semacamnya.
    Jika mrustc ditulis dalam C++, bukankah mungkin lebih mudah mem-porting kode C++ yang berfungsi dengan memakai primitif mentah bergaya C++ dalam C seperti itu ke C?
    Memanfaatkan interoperabilitas yang kuat antara C++ dan C untuk memindahkannya sedikit demi sedikit juga tampaknya memungkinkan.
    Tentu saya paham ini pekerjaan porting yang sulit dan penuh jebakan. Hanya saja, perlu diingat bahwa pembandingnya adalah menulis ulang kompiler Rust dalam C dari nol.
    Ini juga mengingatkan saya pada kompiler C++ ke C yang dulu pernah ada. Saya tidak tahu apakah itu masih ada.
    Bahkan sekarang pun, Rust ke C/C++ dan kompiler C++ ke C yang masih bisa dibaca manusia tampaknya akan berguna. Karena itu bisa menggabungkan keunggulan keamanan dari satu sisi dengan ekosistem alat dari sisi lain.