From 27e9e16077e456a8e6db131d211054b49f6275e1 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 6 Sep 2017 10:28:11 -0700 Subject: [PATCH] Add a FuncEnvironment::make_indirect_sig() callback. The function environment is now expected to keep track of the function signatures in the module, and it is asked to generate Cretonne signatures to be used for indirect calls. The combination of make_indirect_sig() and translate_call_indirect() callbacks allow the runtime to insert additional function arguments for indirect calls such as vmctx pointers and CFI-style signature identifiers. --- cranelift/wasmtests/icall.wast | 7 ++++ lib/wasm/src/code_translator.rs | 38 +++++---------------- lib/wasm/src/module_translator.rs | 2 +- lib/wasm/src/runtime/dummy.rs | 22 +++++++++++-- lib/wasm/src/runtime/spec.rs | 18 ++++++++-- lib/wasm/src/sections_translator.rs | 2 ++ lib/wasm/src/state.rs | 51 ++++++++++++++++++++++++++++- 7 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 cranelift/wasmtests/icall.wast 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) + }) + } }