diff --git a/cranelift/wasmtests/icall.wast b/cranelift/wasmtests/icall.wast new file mode 100644 index 0000000000..21894c55ca --- /dev/null +++ b/cranelift/wasmtests/icall.wast @@ -0,0 +1,7 @@ +(module + (type $ft (func (param f32) (result i32))) + (func $foo (export "foo") (param i32 f32) (result i32) + (call_indirect $ft (get_local 1) (get_local 0)) + ) + (table (;0;) 23 23 anyfunc) +) diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index d7e895cd9a..ee3c6c6f57 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -489,23 +489,15 @@ fn translate_operator( index, table_index: _, } => { - // index is the index of the function's signature and table_index is the index - // of the table to search the function in - // TODO: have runtime support for tables - let sigref = find_signature_import(index as usize, builder, func_imports, signatures); - let args_num = builder.signature(sigref).unwrap().argument_types.len(); + // `index` is the index of the function's signature and `table_index` is the index of + // the table to search the function in. + // TODO: Have runtime support for tables. + let (sigref, num_args) = state.get_indirect_sig(builder.func, index, runtime); let index_val = state.pop1(); - let cut_index = state.stack.len() - args_num; - let ret_values = runtime.translate_call_indirect( - builder, - sigref, - index_val, - &state.stack[cut_index..], - ); - state.stack.truncate(cut_index); - for val in ret_values { - state.push1(*val); - } + let ret_values = + runtime.translate_call_indirect(builder, sigref, index_val, &state.peekn(num_args)); + state.popn(num_args); + state.pushn(ret_values); } /******************************* Memory management *********************************** * Memory management is handled by runtime. It is usually translated into calls to @@ -1163,17 +1155,3 @@ fn find_function_import( func_imports.functions.insert(index, local_func_index); local_func_index } - -fn find_signature_import( - sig_index: SignatureIndex, - builder: &mut FunctionBuilder, - func_imports: &mut FunctionImports, - signatures: &[Signature], -) -> SigRef { - if let Some(local_sig_index) = func_imports.signatures.get(&sig_index) { - return *local_sig_index; - } - let sig_local_index = builder.import_signature(signatures[sig_index].clone()); - func_imports.signatures.insert(sig_index, sig_local_index); - sig_local_index -} diff --git a/lib/wasm/src/module_translator.rs b/lib/wasm/src/module_translator.rs index 6a2e5f3963..c2346e52b3 100644 --- a/lib/wasm/src/module_translator.rs +++ b/lib/wasm/src/module_translator.rs @@ -81,7 +81,7 @@ pub fn translate_module( loop { match *parser.read_with_input(next_input) { ParserState::BeginSection { code: SectionCode::Type, .. } => { - match parse_function_signatures(&mut parser) { + match parse_function_signatures(&mut parser, runtime) { Ok(sigs) => signatures = Some(sigs), Err(SectionParsingError::WrongSectionContent(s)) => { return Err(format!("wrong content in the type section: {}", s)) diff --git a/lib/wasm/src/runtime/dummy.rs b/lib/wasm/src/runtime/dummy.rs index 9b0df58174..7ff9747dc6 100644 --- a/lib/wasm/src/runtime/dummy.rs +++ b/lib/wasm/src/runtime/dummy.rs @@ -1,6 +1,6 @@ use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; -use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, FunctionIndex, - MemoryIndex}; +use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex, + FunctionIndex, MemoryIndex}; use cton_frontend::FunctionBuilder; use cretonne::ir::{self, Value, InstBuilder, SigRef}; use cretonne::ir::types::*; @@ -9,13 +9,18 @@ use cretonne::ir::types::*; /// placeholders when forced to. Don't try to execute code translated with this runtime, it is /// essentially here for translation debug purposes. pub struct DummyRuntime { + // Unprocessed signatures exactly as provided by `declare_signature()`. + signatures: Vec, globals: Vec, } impl DummyRuntime { /// Allocates the runtime data structures. pub fn new() -> Self { - Self { globals: Vec::new() } + Self { + signatures: Vec::new(), + globals: Vec::new(), + } } } @@ -42,6 +47,12 @@ impl FuncEnvironment for DummyRuntime { style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() }, }) } + + fn make_indirect_sig(&self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef { + // A real implementation would probably change the calling convention and add `vmctx` and + // signature index arguments. + func.dfg.signatures.push(self.signatures[index].clone()) + } } impl WasmRuntime for DummyRuntime { @@ -61,6 +72,11 @@ impl WasmRuntime for DummyRuntime { let call_inst = builder.ins().call_indirect(sig_ref, index_val, call_args); builder.inst_results(call_inst) } + + fn declare_signature(&mut self, sig: &ir::Signature) { + self.signatures.push(sig.clone()); + } + fn declare_global(&mut self, global: Global) { self.globals.push(global); } diff --git a/lib/wasm/src/runtime/spec.rs b/lib/wasm/src/runtime/spec.rs index 5333d84c95..3dee0833bf 100644 --- a/lib/wasm/src/runtime/spec.rs +++ b/lib/wasm/src/runtime/spec.rs @@ -2,8 +2,8 @@ //! trait `WasmRuntime`. use cton_frontend::FunctionBuilder; use cretonne::ir::{self, Value, SigRef}; -use translation_utils::{Local, FunctionIndex, TableIndex, GlobalIndex, MemoryIndex, Global, Table, - Memory}; +use translation_utils::{Local, SignatureIndex, FunctionIndex, TableIndex, GlobalIndex, + MemoryIndex, Global, Table, Memory}; /// The value of a WebAssembly global variable. #[derive(Clone, Copy)] @@ -40,12 +40,26 @@ pub trait FuncEnvironment { /// /// The index space covers both imported and locally declared memories. fn make_heap(&self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap; + + /// Set up a signature definition in the preamble of `func` that can be used for an indirect + /// call with signature `index`. + /// + /// The signature may contain additional arguments needed for an indirect call, but the + /// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature + /// arguments. + /// + /// The signature will only be used for indirect calls, even if the module has direct function + /// calls with the same WebAssembly type. + fn make_indirect_sig(&self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef; } /// An object satisfyng the `WasmRuntime` trait can be passed as argument to the /// [`translate_module`](fn.translate_module.html) function. These methods should not be called /// by the user, they are only for the `wasm2cretonne` internal use. pub trait WasmRuntime: FuncEnvironment { + /// Declares a function signature to the runtime. + fn declare_signature(&mut self, sig: &ir::Signature); + /// Declares a global to the runtime. fn declare_global(&mut self, global: Global); /// Declares a table to the runtime. diff --git a/lib/wasm/src/sections_translator.rs b/lib/wasm/src/sections_translator.rs index 6c97d928f7..591146c716 100644 --- a/lib/wasm/src/sections_translator.rs +++ b/lib/wasm/src/sections_translator.rs @@ -26,6 +26,7 @@ pub enum SectionParsingError { /// Reads the Type Section of the wasm module and returns the corresponding function signatures. pub fn parse_function_signatures( parser: &mut Parser, + runtime: &mut WasmRuntime, ) -> Result, SectionParsingError> { let mut signatures: Vec = Vec::new(); loop { @@ -53,6 +54,7 @@ pub fn parse_function_signatures( }; ArgumentType::new(cret_arg) })); + runtime.declare_signature(&sig); signatures.push(sig); } ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), diff --git a/lib/wasm/src/state.rs b/lib/wasm/src/state.rs index 8479b5cc8a..5747ceda72 100644 --- a/lib/wasm/src/state.rs +++ b/lib/wasm/src/state.rs @@ -6,7 +6,7 @@ use cretonne::ir::{self, Ebb, Inst, Type, Value}; use runtime::{FuncEnvironment, GlobalValue}; use std::collections::HashMap; -use translation_utils::{GlobalIndex, MemoryIndex}; +use translation_utils::{GlobalIndex, MemoryIndex, SignatureIndex}; /// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following /// fields: @@ -113,6 +113,11 @@ pub struct TranslationState { // Map of heaps that have been created by `FuncEnvironment::make_heap`. heaps: HashMap, + + // Map of indirect call signatures that have been created by + // `FuncEnvironment::make_indirect_sig()`. + // Stores both the signature reference and the number of WebAssembly arguments + signatures: HashMap, } impl TranslationState { @@ -124,6 +129,7 @@ impl TranslationState { real_unreachable_stack_depth: 0, globals: HashMap::new(), heaps: HashMap::new(), + signatures: HashMap::new(), } } @@ -134,6 +140,7 @@ impl TranslationState { self.real_unreachable_stack_depth = 0; self.globals.clear(); self.heaps.clear(); + self.signatures.clear(); } /// Initialize the state for compiling a function with the given signature. @@ -157,6 +164,11 @@ impl TranslationState { self.stack.push(val); } + /// Push multiple values. + pub fn pushn(&mut self, vals: &[Value]) { + self.stack.extend_from_slice(vals); + } + /// Pop one value. pub fn pop1(&mut self) -> Value { self.stack.pop().unwrap() @@ -182,6 +194,19 @@ impl TranslationState { (v1, v2, v3) } + /// Pop the top `n` values on the stack. + /// + /// The popped values are not returned. Use `peekn` to look at them before popping. + pub fn popn(&mut self, n: usize) { + let new_len = self.stack.len() - n; + self.stack.truncate(new_len); + } + + /// Peek at the top `n` values on the stack in the order they were pushed. + pub fn peekn(&self, n: usize) -> &[Value] { + &self.stack[self.stack.len() - n..] + } + // Push a block on the control stack. pub fn push_block(&mut self, following_code: Ebb, result_types: Vec) { self.control_stack.push(ControlStackFrame::Block { @@ -255,4 +280,28 @@ impl TranslationState { || environ.make_heap(func, index), ) } + + /// Get the `SigRef` reference that should be used to make an indirect call with signature + /// `index`. Also return the number of WebAssembly arguments in the signature. + /// + /// Create the signature if necessary. + pub fn get_indirect_sig( + &mut self, + func: &mut ir::Function, + index: u32, + environ: &FE, + ) -> (ir::SigRef, usize) { + let index = index as MemoryIndex; + *self.signatures.entry(index).or_insert_with(|| { + let sig = environ.make_indirect_sig(func, index); + // Count the number of normal arguments. + // The environment is allowed to add special purpose arguments to the signature. + let args = func.dfg.signatures[sig] + .argument_types + .iter() + .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal) + .count(); + (sig, args) + }) + } }