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.
This commit is contained in:
Jakob Stoklund Olesen
2017-09-06 10:28:11 -07:00
parent 320c88f365
commit 27e9e16077
7 changed files with 103 additions and 37 deletions

View File

@@ -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)
)

View File

@@ -489,23 +489,15 @@ fn translate_operator(
index, index,
table_index: _, table_index: _,
} => { } => {
// index is the index of the function's signature and table_index is the index // `index` is the index of the function's signature and `table_index` is the index of
// of the table to search the function in // the table to search the function in.
// TODO: have runtime support for tables // TODO: Have runtime support for tables.
let sigref = find_signature_import(index as usize, builder, func_imports, signatures); let (sigref, num_args) = state.get_indirect_sig(builder.func, index, runtime);
let args_num = builder.signature(sigref).unwrap().argument_types.len();
let index_val = state.pop1(); let index_val = state.pop1();
let cut_index = state.stack.len() - args_num; let ret_values =
let ret_values = runtime.translate_call_indirect( runtime.translate_call_indirect(builder, sigref, index_val, &state.peekn(num_args));
builder, state.popn(num_args);
sigref, state.pushn(ret_values);
index_val,
&state.stack[cut_index..],
);
state.stack.truncate(cut_index);
for val in ret_values {
state.push1(*val);
}
} }
/******************************* Memory management *********************************** /******************************* Memory management ***********************************
* Memory management is handled by runtime. It is usually translated into calls to * 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); func_imports.functions.insert(index, local_func_index);
local_func_index local_func_index
} }
fn find_signature_import(
sig_index: SignatureIndex,
builder: &mut FunctionBuilder<Local>,
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
}

View File

@@ -81,7 +81,7 @@ pub fn translate_module(
loop { loop {
match *parser.read_with_input(next_input) { match *parser.read_with_input(next_input) {
ParserState::BeginSection { code: SectionCode::Type, .. } => { ParserState::BeginSection { code: SectionCode::Type, .. } => {
match parse_function_signatures(&mut parser) { match parse_function_signatures(&mut parser, runtime) {
Ok(sigs) => signatures = Some(sigs), Ok(sigs) => signatures = Some(sigs),
Err(SectionParsingError::WrongSectionContent(s)) => { Err(SectionParsingError::WrongSectionContent(s)) => {
return Err(format!("wrong content in the type section: {}", s)) return Err(format!("wrong content in the type section: {}", s))

View File

@@ -1,6 +1,6 @@
use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; use runtime::{FuncEnvironment, GlobalValue, WasmRuntime};
use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, FunctionIndex, use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex,
MemoryIndex}; FunctionIndex, MemoryIndex};
use cton_frontend::FunctionBuilder; use cton_frontend::FunctionBuilder;
use cretonne::ir::{self, Value, InstBuilder, SigRef}; use cretonne::ir::{self, Value, InstBuilder, SigRef};
use cretonne::ir::types::*; 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 /// placeholders when forced to. Don't try to execute code translated with this runtime, it is
/// essentially here for translation debug purposes. /// essentially here for translation debug purposes.
pub struct DummyRuntime { pub struct DummyRuntime {
// Unprocessed signatures exactly as provided by `declare_signature()`.
signatures: Vec<ir::Signature>,
globals: Vec<Global>, globals: Vec<Global>,
} }
impl DummyRuntime { impl DummyRuntime {
/// Allocates the runtime data structures. /// Allocates the runtime data structures.
pub fn new() -> Self { 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() }, 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 { 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); let call_inst = builder.ins().call_indirect(sig_ref, index_val, call_args);
builder.inst_results(call_inst) 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) { fn declare_global(&mut self, global: Global) {
self.globals.push(global); self.globals.push(global);
} }

View File

@@ -2,8 +2,8 @@
//! trait `WasmRuntime`. //! trait `WasmRuntime`.
use cton_frontend::FunctionBuilder; use cton_frontend::FunctionBuilder;
use cretonne::ir::{self, Value, SigRef}; use cretonne::ir::{self, Value, SigRef};
use translation_utils::{Local, FunctionIndex, TableIndex, GlobalIndex, MemoryIndex, Global, Table, use translation_utils::{Local, SignatureIndex, FunctionIndex, TableIndex, GlobalIndex,
Memory}; MemoryIndex, Global, Table, Memory};
/// The value of a WebAssembly global variable. /// The value of a WebAssembly global variable.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -40,12 +40,26 @@ pub trait FuncEnvironment {
/// ///
/// The index space covers both imported and locally declared memories. /// The index space covers both imported and locally declared memories.
fn make_heap(&self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap; 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 /// 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 /// [`translate_module`](fn.translate_module.html) function. These methods should not be called
/// by the user, they are only for the `wasm2cretonne` internal use. /// by the user, they are only for the `wasm2cretonne` internal use.
pub trait WasmRuntime: FuncEnvironment { 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. /// Declares a global to the runtime.
fn declare_global(&mut self, global: Global); fn declare_global(&mut self, global: Global);
/// Declares a table to the runtime. /// Declares a table to the runtime.

View File

@@ -26,6 +26,7 @@ pub enum SectionParsingError {
/// Reads the Type Section of the wasm module and returns the corresponding function signatures. /// Reads the Type Section of the wasm module and returns the corresponding function signatures.
pub fn parse_function_signatures( pub fn parse_function_signatures(
parser: &mut Parser, parser: &mut Parser,
runtime: &mut WasmRuntime,
) -> Result<Vec<Signature>, SectionParsingError> { ) -> Result<Vec<Signature>, SectionParsingError> {
let mut signatures: Vec<Signature> = Vec::new(); let mut signatures: Vec<Signature> = Vec::new();
loop { loop {
@@ -53,6 +54,7 @@ pub fn parse_function_signatures(
}; };
ArgumentType::new(cret_arg) ArgumentType::new(cret_arg)
})); }));
runtime.declare_signature(&sig);
signatures.push(sig); signatures.push(sig);
} }
ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))),

View File

@@ -6,7 +6,7 @@
use cretonne::ir::{self, Ebb, Inst, Type, Value}; use cretonne::ir::{self, Ebb, Inst, Type, Value};
use runtime::{FuncEnvironment, GlobalValue}; use runtime::{FuncEnvironment, GlobalValue};
use std::collections::HashMap; 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 /// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
/// fields: /// fields:
@@ -113,6 +113,11 @@ pub struct TranslationState {
// Map of heaps that have been created by `FuncEnvironment::make_heap`. // Map of heaps that have been created by `FuncEnvironment::make_heap`.
heaps: HashMap<MemoryIndex, ir::Heap>, heaps: HashMap<MemoryIndex, ir::Heap>,
// 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<SignatureIndex, (ir::SigRef, usize)>,
} }
impl TranslationState { impl TranslationState {
@@ -124,6 +129,7 @@ impl TranslationState {
real_unreachable_stack_depth: 0, real_unreachable_stack_depth: 0,
globals: HashMap::new(), globals: HashMap::new(),
heaps: HashMap::new(), heaps: HashMap::new(),
signatures: HashMap::new(),
} }
} }
@@ -134,6 +140,7 @@ impl TranslationState {
self.real_unreachable_stack_depth = 0; self.real_unreachable_stack_depth = 0;
self.globals.clear(); self.globals.clear();
self.heaps.clear(); self.heaps.clear();
self.signatures.clear();
} }
/// Initialize the state for compiling a function with the given signature. /// Initialize the state for compiling a function with the given signature.
@@ -157,6 +164,11 @@ impl TranslationState {
self.stack.push(val); self.stack.push(val);
} }
/// Push multiple values.
pub fn pushn(&mut self, vals: &[Value]) {
self.stack.extend_from_slice(vals);
}
/// Pop one value. /// Pop one value.
pub fn pop1(&mut self) -> Value { pub fn pop1(&mut self) -> Value {
self.stack.pop().unwrap() self.stack.pop().unwrap()
@@ -182,6 +194,19 @@ impl TranslationState {
(v1, v2, v3) (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. // Push a block on the control stack.
pub fn push_block(&mut self, following_code: Ebb, result_types: Vec<Type>) { pub fn push_block(&mut self, following_code: Ebb, result_types: Vec<Type>) {
self.control_stack.push(ControlStackFrame::Block { self.control_stack.push(ControlStackFrame::Block {
@@ -255,4 +280,28 @@ impl TranslationState {
|| environ.make_heap(func, index), || 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<FE: FuncEnvironment + ?Sized>(
&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)
})
}
} }