Apa itu LLVM? Kekuatan di sebalik Swift, Rust, Clang, dan banyak lagi

Bahasa baru, dan penambahbaikan pada bahasa yang ada, menjamur di seluruh lanskap pembangunan. Mozilla's Rust, Apple's Swift, Jetbrains's Kotlin, dan banyak bahasa lain menyediakan pelbagai pilihan baru untuk kelajuan, keselamatan, kemudahan, mudah alih, dan daya.

Kenapa sekarang? Satu sebab besar adalah alat baru untuk membina bahasa — khususnya, penyusun. Dan yang utama ialah LLVM, sebuah projek sumber terbuka yang pada mulanya dikembangkan oleh pencipta bahasa Swift, Chris Lattner sebagai projek penyelidikan di University of Illinois.

LLVM menjadikannya lebih mudah untuk tidak hanya membuat bahasa baru, tetapi untuk meningkatkan pengembangan bahasa yang ada. Ini menyediakan alat untuk mengotomatisasi banyak bahagian yang paling berterima kasih dalam tugas penciptaan bahasa: membuat penyusun, memindahkan kod yang dikeluarkan ke pelbagai platform dan seni bina, menghasilkan pengoptimuman khusus seni bina seperti vektorisasi, dan menulis kod untuk menangani metafora bahasa biasa pengecualian. Perlesenan liberalnya bermaksud dapat digunakan kembali secara bebas sebagai komponen perisian atau digunakan sebagai perkhidmatan.

Daftar bahasa yang menggunakan LLVM mempunyai banyak nama yang tidak asing lagi. Bahasa Swift Apple menggunakan LLVM sebagai kerangka penyusunnya, dan Rust menggunakan LLVM sebagai komponen teras rantai alatnya. Juga, banyak penyusun mempunyai edisi LLVM, seperti Clang, penyusun C / C ++ (nama ini, "C-lang"), itu sendiri merupakan projek yang berkait rapat dengan LLVM. Mono, implementasi .NET, mempunyai pilihan untuk mengkompilasi ke kod asli menggunakan ujung belakang LLVM. Dan Kotlin, secara nominal bahasa JVM, sedang mengembangkan versi bahasa yang disebut Kotlin Native yang menggunakan LLVM untuk menyusun kod asli mesin.

LLVM ditakrifkan

Pada dasarnya, LLVM adalah perpustakaan untuk membuat kod asli mesin secara program. Pembangun menggunakan API untuk menghasilkan arahan dalam format yang disebut perwakilan perantaraan , atau IR. LLVM kemudian dapat menyusun IR menjadi binari mandiri atau melakukan penyusunan JIT (tepat pada waktunya) pada kod untuk dijalankan dalam konteks program lain, seperti jurubahasa atau runtime untuk bahasa tersebut.

API LLVM menyediakan primitif untuk mengembangkan banyak struktur dan corak umum yang terdapat dalam bahasa pengaturcaraan. Sebagai contoh, hampir setiap bahasa mempunyai konsep fungsi dan pemboleh ubah global, dan banyak yang mempunyai coroutine dan C fungsi antara muka. LLVM mempunyai fungsi dan pemboleh ubah global sebagai elemen standard dalam IRnya, dan mempunyai metafora untuk membuat coroutine dan berinteraksi dengan perpustakaan C.

Daripada menghabiskan masa dan tenaga untuk mencipta semula roda tertentu, anda hanya boleh menggunakan pelaksanaan LLVM dan fokus pada bahagian bahasa anda yang memerlukan perhatian.

Baca lebih lanjut mengenai Go, Kotlin, Python, dan Rust 

Pergi:

  • Ketik kehebatan bahasa Go Google
  • IDE dan editor bahasa Go terbaik

Kotlin:

  • Apa itu Kotlin? Alternatif Java menjelaskan
  • Kerangka kerja Kotlin: Satu tinjauan alat pengembangan JVM

Python:

  • Apa itu Python? Semua yang anda perlu tahu
  • Tutorial: Cara memulakan Python
  • 6 perpustakaan penting untuk setiap pembangun Python

Karat:

  • Apa itu Karat? Cara untuk melakukan pembangunan perisian yang selamat, cepat, dan mudah
  • Ketahui cara memulakan Rust 

LLVM: Direka untuk mudah alih

Untuk memahami LLVM, ada baiknya mempertimbangkan analogi dengan bahasa pengaturcaraan C: C kadang-kadang digambarkan sebagai bahasa pemasangan tingkat tinggi yang mudah alih, kerana mempunyai konstruksi yang dapat memetakan dekat dengan perkakasan sistem, dan ia telah diserahkan ke hampir setiap senibina sistem. Tetapi C berguna sebagai bahasa pemasangan mudah alih sehingga satu titik; ia tidak dirancang untuk tujuan tertentu.

Sebaliknya, IR LLVM dirancang sejak awal untuk menjadi pemasangan mudah alih. Salah satu cara untuk mencapai kemudahan ini adalah dengan menawarkan primitif yang bebas daripada apa-apa seni bina mesin tertentu. Sebagai contoh, jenis bilangan bulat tidak terhad pada lebar bit maksimum perkakasan yang mendasari (seperti 32 atau 64 bit). Anda boleh membuat jenis integer primitif menggunakan sebilangan bit yang diperlukan, seperti bilangan bulat 128-bit. Anda juga tidak perlu risau untuk membuat output agar sesuai dengan set arahan pemproses tertentu; LLVM menguruskannya untuk anda juga.

Reka bentuk LLVM yang berkecuali-netral menjadikannya lebih mudah untuk menyokong perkakasan dari semua jenis, masa kini dan masa depan. Sebagai contoh, IBM baru-baru ini menyumbang kod untuk menyokong z / OSnya, Linux on Power (termasuk sokongan untuk perpustakaan vektorisasi MASS IBM), dan seni bina AIX untuk projek C, C ++, dan Fortran LLVM. 

Sekiranya anda ingin melihat contoh langsung LLVM IR, pergi ke laman web Projek ELLCC dan cuba demo langsung yang menukar kod C menjadi LLVM IR langsung di penyemak imbas.

Bagaimana bahasa pengaturcaraan menggunakan LLVM

Kes penggunaan yang paling biasa untuk LLVM adalah sebagai penyusun masa depan (AOT) untuk bahasa. Contohnya, projek Clang lebih awal menyusun C dan C ++ ke binari asli. Tetapi LLVM menjadikan perkara lain mungkin juga.

Menyusun tepat pada masanya dengan LLVM

Beberapa situasi memerlukan kod dibuat dengan cepat pada waktu runtime, dan bukannya disusun lebih awal. Bahasa Julia, misalnya, JIT menyusun kodnya, kerana ia perlu berjalan pantas dan berinteraksi dengan pengguna melalui REPL (baca-eval-print loop) atau arahan interaktif. 

Numba, pakej pecutan matematik untuk Python, JIT-menyusun fungsi Python terpilih ke kod mesin. Ia juga dapat menyusun kod yang dihiasi Numba sebelumnya, tetapi (seperti Julia) Python menawarkan pengembangan yang cepat dengan menjadi bahasa yang ditafsirkan. Menggunakan kompilasi JIT untuk menghasilkan kod seperti itu melengkapkan aliran kerja interaktif Python lebih baik daripada penyusunan sebelumnya.

Yang lain bereksperimen dengan cara baru untuk menggunakan LLVM sebagai JIT, seperti menyusun pertanyaan PostgreSQL, menghasilkan peningkatan prestasi hingga lima kali ganda.

Pengoptimuman kod automatik dengan LLVM

LLVM tidak hanya menyusun IR ke kod mesin asli. Anda juga dapat mengarahkannya secara terprogram untuk mengoptimumkan kod dengan perincian yang tinggi, sepanjang proses penghubung. Pengoptimumannya boleh dilakukan secara agresif, termasuk perkara seperti menyisipkan fungsi, menghilangkan kod mati (termasuk deklarasi jenis yang tidak digunakan dan argumen fungsi), dan melepaskan gelung.

Sekali lagi, kekuatannya tidak perlu melaksanakan semua ini sendiri. LLVM dapat mengendalikannya untuk anda, atau anda boleh mengarahkannya untuk menukarnya jika diperlukan. Sebagai contoh, jika anda menginginkan binari yang lebih kecil dengan kos beberapa prestasi, anda boleh meminta penyusun depan memberitahu LLVM untuk mematikan gelung yang tidak dapat dikeluarkan.

Bahasa khusus domain dengan LLVM

LLVM telah digunakan untuk menghasilkan penyusun untuk banyak bahasa tujuan umum, tetapi juga berguna untuk menghasilkan bahasa yang sangat menegak atau eksklusif untuk domain yang bermasalah. Dalam beberapa cara, di sinilah LLVM bersinar terang, kerana ia menghilangkan banyak kekhawatiran dalam membuat bahasa seperti itu dan membuatnya berfungsi dengan baik.

Projek Emscripten, misalnya, mengambil kod IR LLVM dan mengubahnya menjadi JavaScript, secara teori membenarkan bahasa apa pun dengan hujung belakang LLVM untuk mengeksport kod yang dapat dijalankan dalam penyemak imbas. Rancangan jangka panjang adalah mempunyai hujung belakang berasaskan LLVM yang dapat menghasilkan WebAssembly, tetapi Emscripten adalah contoh yang baik tentang bagaimana LLVM fleksibel.

Cara lain LLVM dapat digunakan adalah dengan menambahkan peluasan khusus domain ke bahasa yang ada. Nvidia menggunakan LLVM untuk membuat Nvidia CUDA Compiler, yang membolehkan bahasa menambahkan sokongan asli untuk CUDA yang menyusun sebagai sebahagian daripada kod asli yang anda hasilkan (lebih cepat), bukannya dipanggil melalui perpustakaan yang dihantar dengannya (lebih lambat).

Kejayaan LLVM dengan bahasa khusus domain telah mendorong projek baru dalam LLVM untuk mengatasi masalah yang mereka buat. Isu terbesar adalah bagaimana sebilangan DSL sukar diterjemahkan ke dalam LLVM IR tanpa banyak kerja keras di bahagian depan. Salah satu jalan penyelesaiannya adalah Perwakilan Menengah Tahap Multi, atau projek MLIR.

MLIR menyediakan cara mudah untuk merepresentasikan struktur dan operasi data yang kompleks, yang kemudian dapat diterjemahkan secara automatik ke LLVM IR. Sebagai contoh, kerangka pembelajaran mesin TensorFlow dapat mempunyai banyak operasi grafik alur data yang kompleks disusun dengan cekap ke kod asli dengan MLIR.

Bekerja dengan LLVM dalam pelbagai bahasa

Cara tipikal untuk bekerja dengan LLVM adalah melalui kod dalam bahasa yang anda selesa (dan tentu saja ia mempunyai sokongan untuk perpustakaan LLVM).

Dua pilihan bahasa biasa ialah C dan C ++. Banyak pemaju LLVM melalaikan salah satu dari dua kerana beberapa sebab yang baik: 

  • LLVM itu sendiri ditulis dalam C ++.
  • API LLVM tersedia dalam penjelmaan C dan C ++.
  • Banyak perkembangan bahasa cenderung berlaku dengan C / C ++ sebagai asas

Namun, kedua-dua bahasa itu bukan satu-satunya pilihan. Banyak bahasa dapat memanggil secara asli ke perpustakaan C, jadi secara teorinya mungkin untuk melakukan pengembangan LLVM dengan bahasa seperti itu. Tetapi sangat berguna untuk memiliki perpustakaan sebenar dalam bahasa yang membungkus API LLVM dengan elegan. Nasib baik, banyak bahasa dan jangka masa bahasa mempunyai perpustakaan seperti itu, termasuk C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go, dan Python.

Satu peringatan adalah bahawa beberapa pengikatan bahasa untuk LLVM mungkin kurang lengkap daripada yang lain. Dengan Python, misalnya, ada banyak pilihan, tetapi masing-masing berbeza dalam kelengkapan dan kegunaannya:

  • llvmlite, yang dikembangkan oleh pasukan yang mencipta Numba, telah muncul sebagai pesaing semasa untuk bekerja dengan LLVM di Python. Ini hanya menerapkan subset fungsi LLVM, seperti yang ditentukan oleh keperluan projek Numba. Tetapi subset itu menyediakan sebahagian besar dari apa yang pengguna LLVM perlukan. (llvmlite umumnya merupakan pilihan terbaik untuk bekerja dengan LLVM di Python.)
  • Projek LLVM mengekalkan rangkaian pengikatannya sendiri ke C API LLVM, tetapi pada masa ini ia tidak dikekalkan.
  • llvmpy, pengikatan Python pertama yang popular untuk LLVM, tidak dapat diselesaikan pada tahun 2015. Buruk bagi mana-mana projek perisian, tetapi lebih teruk lagi ketika bekerja dengan LLVM, memandangkan jumlah perubahan yang berlaku dalam setiap edisi LLVM.
  • llvmcpy bertujuan untuk mengemas kini ikatan Python untuk perpustakaan C, menjadikannya kemas kini secara automatik, dan menjadikannya mudah diakses menggunakan simpulan bahasa asli Python. llvmcpy masih di peringkat awal, tetapi sudah dapat melakukan beberapa kerja dasar dengan API LLVM.

Sekiranya anda ingin tahu bagaimana menggunakan perpustakaan LLVM untuk membina bahasa, pencipta LLVM sendiri mempunyai tutorial, menggunakan C ++ atau OCAML, yang akan membantu anda membuat bahasa mudah yang disebut Kaleidoscope. Sejak itu disalurkan ke bahasa lain:

  • Haskell:  Port langsung tutorial asal.
  • Python: Satu port tersebut mengikuti tutorial dengan teliti, sementara port yang lain adalah penulisan semula yang lebih bercita-cita tinggi dengan baris arahan interaktif. Kedua-duanya menggunakan llvmlite sebagai ikatan kepada LLVM.
  • Rust  and  Swift: Nampaknya tidak dapat dielakkan kita akan mendapat port tutorial ke dua bahasa yang LLVM bantu mewujudkan.

Akhirnya, tutorial ini juga tersedia dalam  bahasa manusia . Ia telah diterjemahkan ke dalam bahasa Cina, menggunakan C ++ dan Python yang asli.

Apa yang tidak dilakukan oleh LLVM

Dengan semua yang disediakan oleh LLVM, berguna untuk mengetahui apa yang tidak dilakukannya.

Contohnya, LLVM tidak menguraikan tatabahasa bahasa. Banyak alat sudah melakukan tugas itu, seperti lex / yacc, flex / bison, Lark, dan ANTLR. Menghuraikan dimaksudkan untuk dipisahkan dari kompilasi, jadi tidak menghairankan bahawa LLVM tidak berusaha menangani semua ini.

LLVM juga tidak secara langsung menangani budaya perisian yang lebih besar di sekitar bahasa tertentu. Memasang binari pengkompil, menguruskan pakej dalam pemasangan, dan menaik taraf rantai alat — anda perlu melakukannya sendiri.

Akhirnya, dan yang paling penting, masih ada bahagian bahasa yang biasa tidak disediakan oleh LLVM. Banyak bahasa mempunyai beberapa cara pengurusan memori yang dikumpulkan sampah, baik sebagai cara utama untuk mengurus memori atau sebagai tambahan kepada strategi seperti RAII (yang digunakan C ++ dan Rust). LLVM tidak memberi anda mekanisme pengumpul sampah, tetapi ia menyediakan alat untuk melaksanakan pengumpulan sampah dengan membiarkan kod ditandai dengan metadata yang menjadikan penulisan sampah menjadi lebih mudah.

Walau bagaimanapun, tidak ada yang menolak kemungkinan LLVM akhirnya dapat menambahkan mekanisme asli untuk melaksanakan pengumpulan sampah. LLVM berkembang dengan cepat, dengan pelepasan utama setiap enam bulan atau lebih. Dan laju pembangunan cenderung hanya meningkat berkat cara banyak bahasa semasa meletakkan LLVM sebagai nadi proses pembangunan mereka.