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:
7
cranelift/wasmtests/icall.wast
Normal file
7
cranelift/wasmtests/icall.wast
Normal 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)
|
||||
)
|
||||
@@ -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<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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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<ir::Signature>,
|
||||
globals: Vec<Global>,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<Vec<Signature>, SectionParsingError> {
|
||||
let mut signatures: Vec<Signature> = 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))),
|
||||
|
||||
@@ -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<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 {
|
||||
@@ -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<Type>) {
|
||||
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<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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user