
Binaryen adalah perpustakaan infrastruktur kompiler dan toolchain untuk WebAssembly, yang ditulis dalam C ++. Ini bertujuan untuk membuat kompilasi ke WebAssembly mudah, cepat, dan efektif :
Easy : Binaryen memiliki API C sederhana dalam satu header, dan juga dapat digunakan dari JavaScript. Ia menerima input dalam bentuk seperti WebAssembly tetapi juga menerima grafik aliran kontrol umum untuk kompiler yang lebih suka itu.
Cepat : IR internal Bineren menggunakan struktur data yang ringkas dan dirancang untuk codegen dan optimasi paralel sepenuhnya, menggunakan semua inti CPU yang tersedia. IR Binaryen juga dikompilasi ke WebAssembly dengan sangat mudah dan cepat karena pada dasarnya adalah subset dari WebAssembly.
Efektif : Pengoptimal Binaryen memiliki banyak umpan (lihat gambaran umum nanti) yang dapat meningkatkan ukuran dan kecepatan kode. Optimalisasi ini bertujuan untuk membuat biner yang cukup kuat untuk digunakan sebagai backend kompiler dengan sendirinya. Salah satu bidang fokus spesifik adalah pada optimasi spesifik websAssembly (yang mungkin tidak dilakukan oleh kompiler tujuan umum), yang dapat Anda anggap sebagai minifikasi WASS, mirip dengan minifikasi untuk JavaScript, CSS, dll., Yang semuanya spesifik bahasa.
Toolchains Menggunakan Binaryen sebagai komponen (biasanya menjalankan wasm-opt ) meliputi:
Emscripten (C/C ++)wasm-pack (karat)J2CL (Java; J2Wasm )Kotlin (Kotlin/Wasm)Dart (Flutter)wasm_of_ocaml (ocaml)Untuk lebih lanjut tentang bagaimana beberapa dari itu bekerja, lihat bagian arsitektur toolchain dari v8 wasmgc porting blogpost.
Kompiler menggunakan Binaryen sebagai perpustakaan meliputi:
AssemblyScript yang mengkompilasi varian naskah ke webassemblywasm2js yang mengkompilasi WebAssembly ke JSAsterius yang mengkompilasi Haskell ke WebAssemblyGrain yang mengkompilasi biji -bijian ke WebAssemblyBinaryen juga menyediakan satu set utilitas toolchain yang bisa
Konsultasikan dengan instruksi yang berkontribusi jika Anda tertarik untuk berpartisipasi.
IR internal biner dirancang untuk menjadi
Ada beberapa perbedaan antara Binaryen IR dan bahasa WebAssembly:
catch di fitur penanganan pengecualian) direpresentasikan sebagai sub -ekspresi pop .--generate-stack-ir --print-stack-ir , yang mencetak tumpukan IR, ini dijamin berlaku untuk parser WASM.)ref.func harus ada di dalam tabel, atau dinyatakan melalui (elem declare func $..) . Binaryen akan memancarkan data itu bila perlu, tetapi tidak mewakili itu dalam IR. Artinya, IR dapat dikerjakan tanpa perlu berpikir tentang mendeklarasikan referensi fungsi.local.get Get harus didominasi secara struktural oleh local.set untuk divalidasi (yang memastikan kami tidak membaca nilai default NULL). Meskipun selaras dengan spesifikasi WASM, ada beberapa detail kecil yang mungkin Anda perhatikan:Block tanpa nama di Binaryen IR tidak mengganggu validasi. Blok tanpa nama tidak pernah dipancarkan ke dalam format biner (kami baru saja memancarkan isinya), jadi kami mengabaikannya untuk tujuan penduduk setempat yang tidak dapat dibatalkan. Akibatnya, jika Anda membaca teks Wasm yang dipancarkan oleh Binaryen maka Anda dapat melihat apa yang tampaknya menjadi kode yang tidak boleh memvalidasi per spec (dan mungkin tidak memvalidasi dalam parser teks Wasm), tetapi perbedaan itu tidak akan ada dalam format biner (binari yang dipancarkan oleh biner akan selalu berfungsi di mana -mana, di samping bugs yang tentu saja).LocalStructuralDominance lihat hook pass.h requiresNonNullableLocalFixups() .ref.func selalu merupakan tipe fungsi tertentu, dan bukan funcref biasa. Itu juga tidak dapat dibatasi.try_table pada cabang (jika kita bercabang, nol tidak pernah dikirim), yaitu mengirim (ref exn) dan bukan (ref null exn). Dalam kedua kasus jika GC tidak diaktifkan maka kami memancarkan tipe biner yang kurang halus. Saat membaca biner, jenis yang lebih halus akan diterapkan saat kita membangun IR.br_if lebih disempurnakan dalam Binaryen IR: Mereka memiliki jenis nilai, ketika nilai mengalir. Dalam spesifikasi WASM jenisnya adalah target cabang, yang mungkin kurang halus. Menggunakan tipe yang lebih halus di sini memastikan bahwa kami mengoptimalkan dengan cara terbaik, menggunakan semua informasi jenis, tetapi itu berarti bahwa beberapa operasi pembulatan mungkin terlihat sedikit berbeda. Secara khusus, ketika kita memancarkan br_if yang jenisnya lebih halus dalam biner, maka kita memancarkan gips tepat setelah itu, sehingga output memiliki tipe yang tepat dalam spesifikasi WASM. Itu dapat menyebabkan beberapa byte ukuran ekstra dalam kasus yang jarang terjadi (kami menghindari overhead ini dalam kasus umum di mana nilai br_if tidak digunakan).stringview_wtf16 dll.) Dilemparkan menggunakan ref.cast . Ini menyederhanakan IR, karena memungkinkan ref.cast untuk selalu digunakan di semua tempat (dan diturunkan ke ref.as_non_null jika memungkinkan di pengoptimal). Spesifikasi StringRef tampaknya tidak mengizinkan ini, dan untuk memperbaiki bahwa penulis biner akan menggantikan ref.cast yang melemparkan tampilan string ke tipe yang tidak dapat dibatalkan ke ref.as_non_null . ref.cast dari tampilan string yang merupakan no-op dilewati sepenuhnya.Akibatnya, Anda mungkin melihat bahwa konversi pulang-pergi (WASM => Bineren ir => wasm) mengubah sedikit kode dalam beberapa kasus sudut.
src/wasm-stack.h ). Stack IR memungkinkan banyak optimisasi yang dirancang untuk bentuk mesin tumpukan format biner WebAssembly (tetapi stack IR kurang efisien untuk optimasi umum daripada Bineren IR utama). Jika Anda memiliki file WASM yang telah dioptimalkan dengan sangat baik, konversi pulang-pergi sederhana (cukup baca dan tulis, tanpa optimasi) dapat menyebabkan perbedaan yang lebih nyata, karena bineryen cocok menjadi format biner IR yang lebih terstruktur. Jika Anda juga mengoptimalkan selama konversi pulang-pergi maka stack IR opts akan dijalankan dan Wasm Final akan lebih dioptimalkan.Catatan Saat bekerja dengan Binaryen IR:
Fungsi intrinsik biner terlihat seperti panggilan untuk mengimpor, misalnya,
( import " binaryen-intrinsics " " foo " ( func $foo ))Menerapkan mereka seperti itu memungkinkan mereka untuk dibaca dan ditulis oleh alat lain, dan menghindari kesalahan yang membingungkan pada kesalahan format biner yang dapat terjadi dalam alat -alat itu jika kami memiliki ekstensi format biner khusus.
Metode intrinsik dapat dioptimalkan oleh pengoptimal. Jika tidak, itu harus diturunkan sebelum mengirimkan WASM, karena jika tidak, ia akan terlihat seperti panggilan ke impor yang tidak ada (dan VMS akan menunjukkan kesalahan karena tidak memiliki nilai yang tepat untuk impor itu). Penurunan akhir itu tidak dilakukan secara otomatis. Pengguna intrinsik harus menjalankan umpan untuk itu secara eksplisit, karena alat tidak tahu kapan pengguna bermaksud untuk menyelesaikan optimalisasi, karena pengguna mungkin memiliki pipa dari beberapa langkah optimasi, atau mungkin melakukan eksperimen lokal, atau fuzzing/reduksi, dll. Hanya pengguna yang tahu kapan optimasi akhir terjadi sebelum WASM adalah "final" dan siap untuk diputuskan. Perhatikan bahwa, secara umum, beberapa optimasi tambahan dimungkinkan setelah penurunan akhir, dan pola yang berguna adalah untuk mengoptimalkan sekali secara normal dengan intrinsik, kemudian menurunkannya, lalu mengoptimalkan setelah itu, misalnya:
wasm-opt input.wasm -o output.wasm -O --intrinsic-lowering -OSetiap intrinsik mendefinisikan semantiknya, yang mencakup apa yang diizinkan oleh pengoptimal untuk itu dan apa yang akan diturunkan oleh penurunan akhir. Lihat intrinsik.h untuk definisi terperinci. Ringkasan cepat muncul di sini:
call.without.effects : Mirip dengan call_ref karena menerima parameter, dan referensi ke fungsi untuk dipanggil, dan panggilan yang berfungsi dengan parameter tersebut, kecuali bahwa pengoptimal dapat mengasumsikan panggilan tidak memiliki efek samping, dan mungkin dapat mengoptimalkannya (jika tidak memiliki hasil yang digunakan, secara umum). Repositori ini berisi kode yang membangun alat berikut di bin/ (lihat instruksi bangunan):
wasm-opt : Muat WebAssembly dan menjalankan Binaryen IR Pass di atasnya.wasm-as : Assembles WebAssembly dalam format teks (saat ini format S-Expression) ke dalam format biner (melalui Bineren IR).wasm-dis : WebAssembly yang tidak dirakit dalam format biner ke dalam format teks (melalui Binaryen IR).wasm2js : Kompiler WebAssembly-to-Js. Ini digunakan oleh Emscripten untuk menghasilkan JavaScript sebagai alternatif dari WebAssembly.wasm-reduce : Pengurangan testcase untuk file WebAssembly. Mengingat file WASM yang menarik karena beberapa alasan (katakanlah, itu merusak VM tertentu), wasm-reduce dapat menemukan file wasm yang lebih kecil yang memiliki properti yang sama, yang seringkali lebih mudah untuk debug. Lihat dokumen untuk lebih jelasnya.wasm-shell : Shell yang dapat memuat dan menafsirkan kode WebAssembly. Ini juga dapat menjalankan suite uji spec.wasm-emscripten-finalize : Take a Wasm Binary yang diproduksi oleh LLVM+LLD dan melakukan umpan spesifik emscripten di atasnya.wasm-ctor-eval : Alat yang dapat menjalankan fungsi (atau bagian fungsi) pada waktu kompilasi.wasm-merge : Menggabungkan beberapa file WASM ke dalam satu file, menghubungkan impor yang sesuai dengan ekspor seperti halnya. Seperti bundler untuk JS, tetapi untuk Wasm.wasm-metadce : Alat untuk menghapus bagian file WASM dengan cara yang fleksibel yang tergantung pada bagaimana modul digunakan.binaryen.js : Perpustakaan JavaScript mandiri yang memperlihatkan metode biner untuk membuat dan mengoptimalkan modul WASM. Untuk build, lihat Binaryen.js di NPM (atau unduh langsung dari GitHub atau UNPKG). Persyaratan Minimal: Node.js V15.8 atau Chrome V75 atau Firefox V78.Semua alat biner adalah deterministik, yaitu, mengingat input yang sama Anda harus selalu mendapatkan output yang sama. (Jika Anda melihat kasus yang berperilaku sebaliknya, silakan ajukan masalah.)
Instruksi penggunaan untuk masing -masing ada di bawah ini.
Binaryen berisi banyak operan optimasi untuk membuat WebAssembly lebih kecil dan lebih cepat. Anda dapat menjalankan Binaryen Optimizer dengan menggunakan wasm-opt , tetapi juga mereka dapat dijalankan saat menggunakan alat lain, seperti wasm2js dan wasm-metadce .
addDefaultFunctionOptimizationPasses .wasm-opt --help untuk cara mengaturnya dan detail lainnya.Lihat setiap pengoptimalan untuk perincian apa yang dilakukannya, tetapi di sini adalah gambaran cepat dari beberapa yang relevan:
if senjata memiliki beberapa instruksi bersama di ujungnya).block ke yang luar jika memungkinkan, mengurangi nomor mereka.local.set nilai yang sudah ada di lokal. (Tumpang tindih dengan coalescelocals; ini mencapai operasi spesifik yang baru saja disebutkan tanpa semua pekerjaan lainnya yang dilakukan coalescelocals, dan oleh karena itu berguna di tempat lain dalam pipa optimasi.)br atau br_table (seperti memutar block dengan br di tengah menjadi if jika memungkinkan).local.get/set/tee Optimization Pass, melakukan hal -hal seperti mengganti set dan get dengan memindahkan nilai set ke get (dan membuat tee) jika memungkinkan. Juga membuat nilai pengembalian block/if/loop alih -alih menggunakan lokal untuk melewati nilainya.if tidak memiliki konten, setetes nilai konstan tanpa efek samping, block dengan satu anak, dll."LTO" di atas berarti optimasi adalah pengoptimalan waktu tautan seperti bekerja di berbagai fungsi, tetapi dalam arti biner selalu "LTO" karena biasanya dijalankan pada Wasm Terkait Final.
Teknik optimasi canggih di Binaryen Optimizer termasuk ssaifikasi, IR datar, dan stack/poppy IR.
Lihat halaman Wiki Cookbook Optimizer untuk lebih lanjut tentang cara menggunakan pengoptimal secara efektif.
Binaryen juga berisi berbagai umpan yang melakukan hal -hal lain selain optimisasi, seperti legalisasi untuk JavaScript, asyncify, dll.
Binaryen menggunakan submodul git (pada saat penulisan hanya untuk gtest), jadi sebelum Anda membangun Anda harus menginisialisasi submodul:
git submodule init
git submodule updateSetelah itu Anda dapat membangun dengan cmake:
cmake . && make Diperlukan kompiler C ++ 17. Pada macOS, Anda perlu menginstal cmake , misalnya, melalui brew install cmake . Perhatikan bahwa Anda juga dapat menggunakan ninja sebagai generator Anda: cmake -G Ninja . && ninja .
Untuk menghindari ketergantungan gtest, Anda dapat melewati -DBUILD_TESTS=OFF ke cmake.
Binaryen.js dapat dibangun menggunakan Emscripten, yang dapat diinstal melalui SDK.
emcmake cmake . && emmake make binaryen_jsemcmake cmake -DBUILD_FOR_BROWSER=ON . && emmake makeMenggunakan penginstal Microsoft Visual Studio, instal komponen "Visual C ++ Tools for Cmake".
Menghasilkan proyek:
mkdir build
cd build
" %VISUAL_STUDIO_ROOT%Common7IDECommonExtensionsMicrosoftCMakeCMakebincmake.exe " ..Ganti visual_studio_root dengan jalur ke instalasi studio visual Anda. Jika Anda menggunakan Visual Studio Build Tools, jalurnya akan menjadi "C: Program Files (x86) Microsoft Visual Studio 2017 buildTools".
Dari prompt komando pengembang, membangun proyek yang diinginkan:
msbuild binaryen.vcxprojCMake menghasilkan proyek bernama "all_build.vcxproj" untuk membangun semua proyek dengan mudah.
Builds didistribusikan oleh berbagai toolchain yang menggunakan biner, seperti Emscripten, wasm-pack , dll. Ada juga rilis resmi di GitHub:
https://github.com/webassembly/binary/releases
Saat ini dibangun dari platform berikut termasuk:
Linux-x86_64Linux-arm64MacOS-x86_64MacOS-arm64Windows-x86_64Node.js (eksperimental): Port wasm-opt to JavaScript+WebAssembly. Jalankan node wasm-opt.js sebagai pengganti drop-in untuk build asli wasm-opt , pada platform apa pun yang dijalankan Node.js. Membutuhkan node.js 18+ (untuk wasm eh dan utas wasm). (Perhatikan bahwa build ini juga dapat berjalan di lingkungan Deno, Bun, atau JavaScript+WebAssembly lainnya, tetapi hanya diuji pada Node.js.) Berlari
bin/wasm-opt [.wasm or .wat file] [options] [passes, see --help] [--help]Pengoptimal WASM menerima WebAssembly sebagai input, dan dapat menjalankan transformasi operan di atasnya, serta mencetaknya (sebelum dan/atau setelah transformasi). Misalnya, coba
bin/wasm-opt test/lit/passes/name-types.wast -all -S -o -Itu akan menghasilkan salah satu kasus uji di suite tes. Untuk menjalankan transformasi lulus di atasnya, coba
bin/wasm-opt test/lit/passes/name-types.wast --name-types -all -S -o - Pass name-types Pass memastikan masing-masing jenis memiliki nama dan mengganti nama nama jenis yang sangat panjang. Anda dapat melihat perubahan transformasi penyebab dengan membandingkan output dari dua perintah.
Sangat mudah untuk menambahkan transformasi transformasi Anda sendiri ke shell, cukup tambahkan file .cpp ke src/passes , dan membangun kembali shell. Misalnya kode, lihatlah name-types Pass.
Beberapa catatan lagi:
bin/wasm-opt --help untuk daftar lengkap opsi dan pass.--debug akan memancarkan beberapa info debugging. Saluran debug individu (didefinisikan dalam kode sumber melalui #define DEBUG_TYPE xxx ) dapat diaktifkan dengan meneruskannya sebagai daftar string yang dipisahkan koma. Misalnya: bin/wasm-opt --debug=binary . Saluran debug ini juga dapat diaktifkan melalui variabel lingkungan BINARYEN_DEBUG .Berlari
bin/wasm2js [input.wasm file]Ini akan mencetak JavaScript ke konsol.
Misalnya, coba
bin/wasm2js test/hello_world.watOutput itu berisi
function add ( x , y ) {
x = x | 0 ;
y = y | 0 ;
return x + y | 0 | 0 ;
}Sebagai terjemahan
( func $add (; 0 ;) ( type $0 ) ( param $x i32 ) ( param $y i32 ) ( result i32 )
( i32.add
( local.get $x )
( local.get $y )
)
)Output WASM2JS adalah dalam format modul ES6 - pada dasarnya, ia mengubah modul WASM menjadi modul ES6 (untuk dijalankan pada browser yang lebih lama dan versi Node.js Anda dapat menggunakan Babel dll untuk mengubahnya menjadi ES5). Mari kita lihat contoh penuh menyebutkan bahwa Hello World Wat; Pertama, buat file JS utama:
// main.mjs
import { add } from "./hello_world.mjs" ;
console . log ( 'the sum of 1 and 2 is:' , add ( 1 , 2 ) ) ;Jalankan ini (perhatikan bahwa Anda memerlukan node.js yang cukup baru dengan dukungan modul ES6):
$ bin/wasm2js test/hello_world.wat -o hello_world.mjs
$ node --experimental-modules main.mjs
the sum of 1 and 2 is: 3Hal -hal yang diingat dengan output Wasm2js:
-O atau tingkat optimisasi lainnya. Itu akan mengoptimalkan seluruh pipa (Wasm dan JS). Itu tidak akan melakukan semua yang akan dilakukan oleh Minifer JS, seperti Minify Whitespace, jadi Anda masih harus menjalankan Minifer JS Normal sesudahnya. wasm-ctor-eval mengeksekusi fungsi, atau bagiannya, pada waktu kompilasi. Setelah melakukannya, ia membuat serial runtime ke Wasm, yang seperti mengambil "snapshot". Ketika WASM kemudian dimuat dan dijalankan dalam VM, itu akan melanjutkan eksekusi dari titik itu, tanpa melakukan kembali pekerjaan yang sudah dieksekusi.
Misalnya, pertimbangkan program kecil ini:
( module
;; A global variable that begins at 0.
( global $global ( mut i32 ) ( i32.const 0 ))
( import " import " " import " ( func $import ))
( func " main "
;; Set the global to 1.
( global.set $global
( i32.const 1 ))
;; Call the imported function. This *cannot* be executed at
;; compile time.
( call $import )
;; We will never get to this point, since we stop at the
;; import.
( global.set $global
( i32.const 2 ))
)
)Kita dapat mengevaluasi sebagian dari waktu kompilasi seperti ini:
wasm-ctor-eval input.wat --ctors=main -S -o - Ini mengatakan bahwa ada satu fungsi yang ingin kami jalankan ("CTOR" adalah kependekan dari "Global Constructor", nama yang berasal dari kode yang dieksekusi sebelum titik masuk program) dan kemudian mencetaknya sebagai teks ke stdout . Hasilnya adalah ini:
trying to eval main
...partial evalling successful, but stopping since could not eval: call import: import.import
...stopping
(module
(type $none_ = > _none (func))
(import " import " " import " (func $import ))
(global $global (mut i32) (i32.const 1))
(export " main " (func $0 _0))
(func $0 _0
(call $import )
(global.set $global
(i32.const 2)
)
)
) Pencatatan menunjukkan kepada kita mengelola untuk mengevaluasi bagian dari main() , tetapi tidak semuanya, seperti yang diharapkan: kita dapat mengevaluasi global.get pertama, tetapi kemudian kita berhenti pada panggilan ke fungsi yang diimpor (karena kita tidak tahu apa fungsi itu ketika WASM benar -benar dijalankan dalam VM nanti). Perhatikan bagaimana pada wasma output nilai global telah diperbarui dari 0 ke 1, dan bahwa global.get pertama. Get telah dihapus: WASM sekarang dalam keadaan bahwa, ketika kita menjalankannya dalam VM, akan terus berjalan dengan mulus untuk berjalan dari titik di mana wasm-ctor-eval berhenti.
Dalam contoh kecil ini kami baru saja menyimpan sejumlah kecil pekerjaan. Berapa banyak pekerjaan yang dapat disimpan tergantung pada program Anda. (Ini dapat membantu untuk melakukan perhitungan murni di depan, dan meninggalkan panggilan untuk mengimpor selambat mungkin.)
Perhatikan bahwa nama wasm-ctor-eval terkait dengan fungsi konstruktor global, seperti yang disebutkan sebelumnya, tetapi tidak ada batasan pada apa yang dapat Anda jalankan di sini. Ekspor apa pun dari WASM dapat dieksekusi, jika isinya cocok. Misalnya, dalam Emscripten wasm-ctor-eval bahkan dijalankan pada main() jika memungkinkan.
wasm-merge menggabungkan file wasm bersama-sama. Misalnya, bayangkan Anda memiliki proyek yang menggunakan file WASM dari beberapa toolchains. Maka dapat membantu untuk menggabungkan semuanya menjadi satu file WASM sebelum pengiriman, karena dalam satu file WASM panggilan antara modul menjadi hanya panggilan normal di dalam modul, yang memungkinkan mereka untuk dilapisi, kode mati dihilangkan, dan sebagainya, berpotensi meningkatkan kecepatan dan ukuran.
wasm-merge beroperasi pada file WASM normal. Ini berbeda dari wasm-ld dalam hal itu, karena wasm-ld beroperasi pada file objek WASM. wasm-merge dapat membantu dalam situasi multi-alat di mana setidaknya salah satu toolchain tidak menggunakan file objek WASM.
Misalnya, bayangkan kami memiliki dua file wasm ini:
;; a.wasm
( module
( import " second " " bar " ( func $second.bar ))
( export " main " ( func $func ))
( func $func
( call $second.bar )
)
) ;; b.wasm
( module
( import " outside " " log " ( func $log ( param i32 )))
( export " bar " ( func $func ))
( func $func
( call $log
( i32.const 42 )
)
)
) Nama file pada drive lokal Anda adalah a.wasm dan b.wasm , tetapi untuk tujuan penggabungan / bundling katakanlah bahwa yang pertama dikenal sebagai "first" dan yang kedua sebagai "second" . Artinya, kami ingin impor modul pertama "second.bar" untuk memanggil fungsi $func di modul kedua. Berikut adalah perintah Wasm-Merge untuk itu:
wasm-merge a.wasm first b.wasm second -o output.wasmKami memberikannya file Wasm pertama, kemudian namanya, dan kemudian file Wasm kedua dan kemudian namanya. Output gabungan adalah ini:
( module
( import " outside " " log " ( func $log ( param i32 )))
( export " main " ( func $func ))
( export " bar " ( func $func_2 ))
( func $func
( call $func_2 )
)
( func $func_2
( call $log
( i32.const 42 )
)
)
) wasm-merge menggabungkan kedua file menjadi satu, menggabungkan fungsi mereka, impor, dll., Semuanya sambil memperbaiki konflik nama dan menghubungkan impor yang sesuai dengan ekspor. Secara khusus, perhatikan bagaimana $func memanggil $func_2 , yang persis seperti yang kami inginkan: $func_2 adalah fungsi dari modul kedua (berganti nama untuk menghindari tabrakan nama).
Perhatikan bahwa output WASM dalam contoh ini dapat mengambil manfaat dari optimasi tambahan. Pertama, panggilan ke $func_2 sekarang dapat dengan mudah dilapisi, sehingga kami dapat menjalankan wasm-opt -O3 untuk melakukan itu untuk kami. Juga, kita mungkin tidak memerlukan semua impor dan ekspor, di mana kita dapat menjalankan Wasm-Metadce. Alur kerja yang baik bisa menjalankan wasm-merge , kemudian wasm-metadce , lalu selesai dengan wasm-opt .
wasm-merge seperti bundler untuk file wasm, dalam arti "js bundler" tetapi untuk wasm. Artinya, dengan file WASM di atas, bayangkan bahwa kami memiliki kode JS ini untuk membuat instansi dan menghubungkannya saat runtime:
// Compile the first module.
var first = await fetch ( "a.wasm" ) ;
first = new WebAssembly . Module ( first ) ;
// Compile the first module.
var second = await fetch ( "b.wasm" ) ;
second = new WebAssembly . Module ( second ) ;
// Instantiate the second, with a JS import.
second = new WebAssembly . Instance ( second , {
outside : {
log : ( value ) => {
console . log ( 'value:' , value ) ;
}
}
} ) ;
// Instantiate the first, importing from the second.
first = new WebAssembly . Instance ( first , {
second : second . exports
} ) ;
// Call the main function.
first . exports . main ( ) ; Apa yang dilakukan wasm-merge pada dasarnya adalah apa yang dilakukan JS: ia menghubungkan impor ke ekspor, menyelesaikan nama menggunakan nama modul yang Anda berikan. Artinya, dengan menjalankan wasm-merge kami memindahkan pekerjaan menghubungkan modul dari runtime ke waktu kompilasi. Akibatnya, setelah menjalankan wasm-merge kita membutuhkan JS jauh lebih sedikit untuk mendapatkan hasil yang sama:
// Compile the single module.
var merged = await fetch ( "merged.wasm" ) ;
merged = new WebAssembly . Module ( merged ) ;
// Instantiate it with a JS import.
merged = new WebAssembly . Instance ( merged , {
outside : {
log : ( value ) => {
console . log ( 'value:' , value ) ;
}
}
} ) ;
// Call the main function.
merged . exports . main ( ) ;Kita masih perlu mengambil dan menyusun Wasm yang digabungkan, dan untuk memberikan impor JS, tetapi pekerjaan untuk menghubungkan dua modul Wasm tidak diperlukan lagi.
Secara default kesalahan wasm-merge jika ada nama ekspor yang tumpang tindih. Artinya, wasm-merge akan secara otomatis menangani nama fungsi yang tumpang tindih dan sebagainya, karena itu tidak terlihat secara eksternal (kode masih berperilaku sama), tetapi jika kami berganti nama menjadi ekspor maka bagian luar perlu dimodifikasi untuk mengharapkan nama ekspor baru, dan karenanya kami salah melakukan konflik nama tersebut.
Jika Anda ingin ekspor diganti namanya, jalankan wasm-merge dengan --rename-export-conflicts . Ekspor selanjutnya akan memiliki akhiran yang ditambahkan kepada mereka untuk memastikan mereka tidak tumpang tindih dengan ekspor sebelumnya. Sufiksnya deterministik, jadi begitu Anda melihat apa yang bisa Anda sebut dari luar.
Pilihan lain adalah menggunakan --skip-export-conflicts yang hanya akan melewatkan ekspor nanti yang memiliki nama yang bertentangan. Misalnya, ini dapat berguna dalam kasus di mana modul pertama adalah satu -satunya yang berinteraksi dengan luar dan modul selanjutnya hanya berinteraksi dengan modul pertama.
wasm-merge menggunakan fitur multi-memori dan multi-meja. Artinya, jika beberapa modul input masing-masing memiliki memori maka WASM output akan memiliki beberapa memori, dan akan tergantung pada fitur multi-memori, yang berarti bahwa VM WASM yang lebih tua mungkin tidak dapat menjalankan WASM. (Sebagai solusi untuk VM yang lebih lama, Anda dapat menjalankan wasm-opt --multi-memory-lowering untuk menurunkan banyak ingatan ke dalam satu.)
./check.py (atau python check.py ) akan menjalankan wasm-shell , wasm-opt , dll. Pada testcases dalam test/ , dan memverifikasi output mereka.
Skrip check.py mendukung beberapa opsi:
./check.py [--interpreter = /path/to/interpreter] [TEST1] [TEST2].../check.py --list-suites .emcc atau nodejs di jalur. Mereka tidak akan berjalan jika alat tidak dapat ditemukan, dan Anda akan melihat peringatan.tests/spec , dalam submodules git. Menjalankan ./check.py harus memperbarui itu. Perhatikan bahwa kami mencoba untuk secara bertahap porta tes wasm-wasm untuk menggunakan lit dan filecheck saat kami memodifikasinya. Untuk tes passes output wast, ini dapat dilakukan secara otomatis dengan scripts/port_passes_tests_to_lit.py dan untuk tes non- passes yang output limbah, lihat #4779 untuk contoh bagaimana melakukan port manual sederhana.
Untuk tes yang menyala, ekspektasi tes (garis cek) seringkali dapat diperbarui secara otomatis karena perubahan dilakukan ke biner. Lihat scripts/update_lit_checks.py .
Tes non-lit juga dapat diperbarui secara otomatis dalam banyak kasus. Lihat scripts/auto_update_tests.py .
./third_party/setup.py [mozjs | v8 | wabt | all] (atau python third_party/setup.py ) menginstal dependensi yang diperlukan seperti shell spidermonkey js, shell js v8 dan wabt di third_party/ . Script lain secara otomatis mengambil ini saat diinstal.
Jalankan pip3 install -r requirements-dev.txt untuk mendapatkan persyaratan untuk tes lit Perhatikan bahwa Anda perlu memasang lokasi pip ke dalam $PATH Anda (di Linux, ~/.local/bin ).
./scripts/fuzz_opt.py [--binaryen-bin = build/bin] (atau python scripts/fuzz_opt.py ) akan menjalankan berbagai mode fuzzing pada input acak dengan tiket acak sampai menemukan kemungkinan bug. Lihat halaman Wiki untuk semua detailnya.
Binaryen can read and write source maps (see the -ism and -osm flags to wasm-opt ). It can also read and read source map annotations in the text format, that is,
;; @ src.cpp:100:33
( i32.const 42 ) That 42 constant is annotated as appearing in a file called src.cpp at line 100 and column 33 . Source maps and text format annotations are interchangeable, that is, they both lead to the same IR representation, so you can start with an annotated wat and have Binaryen write that to a binary + a source map file, or read a binary + source map file and print text which will contain those annotations.
The IR representation of source map info is simple: in each function we have a map of expressions to their locations. Optimization passes should update the map as relevant. Often this "just works" because the optimizer tries to reuse nodes when possible, so they keep the same debug info.
The text format annotations support a shorthand in which repeated annotations are not necessary. For example, children are tagged with the debug info of the parent, if they have no annotation of their own:
;; @ src.cpp:100:33
( i32.add
( i32.const 41 ) ;; This receives an annotation of src.cpp:100:33
;; @ src.cpp:111:44
( i32.const 1 )
)The first const will have debug info identical to the parent, because it has none specified, and generally such nesting indicates a "bundle" of instructions that all implement the same source code.
Note that text printing will not emit such repeated annotations, which can be confusing. To print out all the annotations, set BINARYEN_PRINT_FULL=1 in the environment. That will print this for the above add :
[i32] ;; @ src.cpp:100:33
( i32.add
[i32] ;; @ src.cpp:100:33
( i32.const 41 )
[i32] ;; @ src.cpp:111:44
( i32.const 1 )
) (full print mode also adds a [type] for each expression, right before the debug location).
The debug information is also propagated from an expression to its next sibling:
;; @ src.cpp:100:33
( local.set $x
( i32.const 0 )
)
( local.set $y ;; This receives an annotation of src.cpp:100:33
( i32.const 0 )
) You can prevent the propagation of debug info by explicitly mentioning that an expression has not debug info using the annotation ;;@ with nothing else:
;; @ src.cpp:100:33
( local.set $x
;; @
( i32.const 0 ) ;; This does not receive any annotation
)
;; @
( local.set $y ;; This does not receive any annotation
( i32.const 7 )
) This stops the propagatation to children and siblings as well. So, expression (i32.const 7) does not have any debug info either.
There is no shorthand in the binary format. That is, roundtripping (writing and reading) through a binary + source map should not change which expressions have debug info on them or the contents of that info.
The source maps format defines a mapping using segments , that is, if a segment starts at binary offset 10 then it applies to all instructions at that offset and until another segment begins (or the end of the input is reached). Binaryen's IR represents a mapping from expressions to locations, as mentioned, so we need to map to and from the segment-based format when writing and reading source maps.
That is mostly straightforward, but one thing we need to do is to handle the lack of debug info in between things that have it. If we have ABC where B lacks debug info, then just emitting a segment for A and C would lead A 's segment to also cover B , since in source maps segments do not have a size - rather they end when a new segment begins. To avoid B getting smeared in this manner, we emit a source maps entry to B of size 1, which just marks the binary offset it has, and without the later 3 fields of the source file, line number, and column. (This appears to be the intent of the source maps spec, and works in browsers and tools.)
Binaryen also has optional support for DWARF. This primarily just tracks the locations of expressions and rewrites the DWARF's locations accordingly; it does not handle things like re-indexing of locals, and so passes that might break DWARF are disabled by default. As a result, this mode is not suitable for a fully optimized release build, but it can be useful for local debugging.
Binaryen's name was inspired by Emscripten 's: Emscripten's name suggests it converts something into a script - specifically JavaScript - and Binaryen's suggests it converts something into a binary - specifically WebAssembly . Binaryen began as Emscripten's WebAssembly generation and optimization tool, so the name fit as it moved Emscripten from something that emitted the text-based format JavaScript (as it did from its early days) to the binary format WebAssembly (which it has done since WebAssembly launched).
"Binaryen" is pronounced in the same manner as "Targaryen".
Yes, it does. Here's a step-by-step tutorial on how to compile it under Windows 10 x64 with with CMake and Visual Studio 2015 . However, Visual Studio 2017 may now be required. Help would be appreciated on Windows and OS X as most of the core devs are on Linux.