From 1eb419cddada269bcedc23b989579bc71c3d4d20 Mon Sep 17 00:00:00 2001 From: Jef Date: Fri, 15 Mar 2019 11:51:11 +0100 Subject: [PATCH] Implement `memory.size`/`memory.grow`, globals, `call_indirect` --- README.md | 54 +++-- src/backend.rs | 427 ++++++++++++++++++++++++++++++++------ src/function_body.rs | 46 ++-- src/microwasm.rs | 123 ++++++++++- src/module.rs | 150 ++++++++----- src/tests.rs | 2 + src/translate_sections.rs | 26 ++- 7 files changed, 651 insertions(+), 177 deletions(-) diff --git a/README.md b/README.md index 611d9986bc..a0c6d6aef2 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ Now obviously I'm not advocating for replacing FireFox's optimising compiler wit ## Specification compliance -It's hard to judge, since each test in the spec testsuite covers a wide range of features (to check their interactions), but currently 33 out of 76 of the spec suite tests pass when run in Wasmtime with Lightbeam as a backend. Here's the full test output: +It's hard to judge, since each test in the spec testsuite covers a wide range of features (to check their interactions), but currently 40 out of 76 of the spec suite tests pass when run in Wasmtime with Lightbeam as a backend. Here's the full test output: ``` running 76 tests @@ -173,22 +173,22 @@ test misc_testsuite::stack_overflow ... ok test spec_testsuite::binary ... ok test misc_testsuite::misc_traps ... ok test spec_testsuite::align ... FAILED -test spec_testsuite::block ... FAILED -test spec_testsuite::br_if ... FAILED +test spec_testsuite::address ... FAILED +test spec_testsuite::block ... ok test spec_testsuite::break_drop ... ok test spec_testsuite::call ... FAILED +test spec_testsuite::br ... ok test spec_testsuite::call_indirect ... FAILED -test spec_testsuite::address ... FAILED test spec_testsuite::comments ... ok test spec_testsuite::const_ ... ok test spec_testsuite::custom ... ok test spec_testsuite::custom_section ... ok test spec_testsuite::data ... ok test spec_testsuite::conversions ... FAILED -test spec_testsuite::elem ... FAILED test spec_testsuite::endianness ... FAILED +test spec_testsuite::elem ... FAILED +test spec_testsuite::br_if ... ok test spec_testsuite::exports ... ok -test spec_testsuite::br ... ok test spec_testsuite::f32_bitwise ... FAILED test spec_testsuite::br_table ... FAILED test spec_testsuite::f64_bitwise ... FAILED @@ -196,61 +196,59 @@ test spec_testsuite::f32 ... FAILED test spec_testsuite::f32_cmp ... FAILED test spec_testsuite::fac ... ok test spec_testsuite::f64 ... FAILED -test spec_testsuite::float_memory ... ok test spec_testsuite::f64_cmp ... FAILED +test spec_testsuite::float_memory ... ok test spec_testsuite::forward ... ok test spec_testsuite::float_misc ... FAILED test spec_testsuite::func_ptrs ... FAILED test spec_testsuite::get_local ... FAILED test spec_testsuite::float_exprs ... FAILED -test spec_testsuite::globals ... FAILED test spec_testsuite::float_literals ... ok +test spec_testsuite::globals ... ok test spec_testsuite::if_ ... FAILED -test spec_testsuite::imports ... FAILED +test spec_testsuite::func ... ok test spec_testsuite::inline_module ... ok -test spec_testsuite::func ... FAILED +test spec_testsuite::imports ... FAILED test spec_testsuite::i32 ... ok test spec_testsuite::i64 ... ok -test spec_testsuite::left_to_right ... FAILED -test spec_testsuite::linking ... FAILED -test spec_testsuite::loop_ ... FAILED -test spec_testsuite::labels ... ok test spec_testsuite::int_literals ... ok +test spec_testsuite::labels ... ok +test spec_testsuite::linking ... FAILED +test spec_testsuite::left_to_right ... FAILED +test spec_testsuite::loop_ ... FAILED +test spec_testsuite::memory ... FAILED test spec_testsuite::memory_grow ... FAILED test spec_testsuite::memory_redundancy ... ok -test spec_testsuite::memory ... FAILED test spec_testsuite::memory_trap ... FAILED test spec_testsuite::resizing ... FAILED test spec_testsuite::nop ... FAILED test spec_testsuite::return_minimal ... ok -test spec_testsuite::select ... FAILED -test spec_testsuite::set_local ... FAILED -test spec_testsuite::skip_stack_guard_page ... FAILED -test spec_testsuite::stack ... FAILED test spec_testsuite::int_exprs ... ok +test spec_testsuite::set_local ... FAILED +test spec_testsuite::select ... FAILED +test spec_testsuite::skip_stack_guard_page ... FAILED +test spec_testsuite::stack ... ok test spec_testsuite::store_retval ... ok test spec_testsuite::start ... FAILED -test spec_testsuite::switch ... ok -test spec_testsuite::token ... ok test spec_testsuite::tee_local ... FAILED +test spec_testsuite::token ... ok +test spec_testsuite::switch ... ok test spec_testsuite::type_ ... ok -test spec_testsuite::traps ... FAILED -test spec_testsuite::typecheck ... ok test spec_testsuite::return_ ... ok -test spec_testsuite::unreached_invalid ... ok +test spec_testsuite::typecheck ... ok +test spec_testsuite::traps ... FAILED test spec_testsuite::unwind ... FAILED +test spec_testsuite::unreached_invalid ... ok test spec_testsuite::utf8_custom_section_id ... ok test spec_testsuite::utf8_import_field ... ok -test spec_testsuite::utf8_invalid_encoding ... ok test spec_testsuite::utf8_import_module ... ok +test spec_testsuite::utf8_invalid_encoding ... ok test spec_testsuite::unreachable ... ok test spec_testsuite::names ... FAILED -test result: FAILED. 35 passed; 41 failed; 0 ignored; 0 measured; 0 filtered out +test result: FAILED. 40 passed; 36 failed; 0 ignored; 0 measured; 0 filtered out ``` -All the failed tests apart from `address` and `unwind` (whose failure hasn't been investigated yet) are due to features that have yet to be implemented. - ## Getting involved Our [issue tracker][issue tracker] is pretty barren right now since this is currently more-or-less a one-person project, but if you want to get involved jump into the [CraneStation Gitter room][cranestation-gitter] and someone can direct you to the right place. I wish I could say "the most useful thing you can do is play with it and open issues where you find problems" but until it passes the spec suite that won't be very helpful. diff --git a/src/backend.rs b/src/backend.rs index f47ea3169a..59c67dd354 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -6,6 +6,7 @@ use self::registers::*; use crate::error::Error; use crate::microwasm::Value; use crate::module::{ModuleContext, RuntimeFunc}; +use cranelift_codegen::binemit; use dynasmrt::x64::Assembler; use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer}; use std::{ @@ -14,6 +15,35 @@ use std::{ ops::RangeInclusive, }; +// TODO: Get rid of this! It's a total hack. +mod magic { + use cranelift_codegen::ir; + + /// Compute an `ir::ExternalName` for the `memory.grow` libcall for + /// 32-bit locally-defined memories. + pub fn get_memory32_grow_name() -> ir::ExternalName { + ir::ExternalName::user(1, 0) + } + + /// Compute an `ir::ExternalName` for the `memory.grow` libcall for + /// 32-bit imported memories. + pub fn get_imported_memory32_grow_name() -> ir::ExternalName { + ir::ExternalName::user(1, 1) + } + + /// Compute an `ir::ExternalName` for the `memory.size` libcall for + /// 32-bit locally-defined memories. + pub fn get_memory32_size_name() -> ir::ExternalName { + ir::ExternalName::user(1, 2) + } + + /// Compute an `ir::ExternalName` for the `memory.size` libcall for + /// 32-bit imported memories. + pub fn get_imported_memory32_size_name() -> ir::ExternalName { + ir::ExternalName::user(1, 3) + } +} + /// Size of a pointer on the target in bytes. const WORD_SIZE: u32 = 8; @@ -467,6 +497,7 @@ pub struct FunctionEnd { pub struct CodeGenSession<'a, M> { assembler: Assembler, pub module_context: &'a M, + labels: Labels, func_starts: Vec<(Option, DynamicLabel)>, } @@ -479,12 +510,17 @@ impl<'a, M> CodeGenSession<'a, M> { CodeGenSession { assembler, + labels: Default::default(), func_starts, module_context, } } - pub fn new_context(&mut self, func_idx: u32) -> Context<'_, M> { + pub fn new_context<'this>( + &'this mut self, + func_idx: u32, + reloc_sink: &'this mut dyn binemit::RelocSink, + ) -> Context<'this, M> { { let func_start = &mut self.func_starts[func_idx as usize]; @@ -496,8 +532,10 @@ impl<'a, M> CodeGenSession<'a, M> { Context { asm: &mut self.assembler, + current_function: func_idx, + reloc_sink: reloc_sink, func_starts: &self.func_starts, - labels: Default::default(), + labels: &mut self.labels, block_state: Default::default(), module_context: self.module_context, } @@ -627,22 +665,64 @@ pub enum MemoryAccessMode { Unchecked, } +struct PendingLabel { + label: Label, + is_defined: bool, +} + +impl PendingLabel { + fn undefined(label: Label) -> Self { + PendingLabel { + label, + is_defined: false, + } + } + + fn defined(label: Label) -> Self { + PendingLabel { + label, + is_defined: true, + } + } + + fn as_undefined(&self) -> Option