Move WasmRuntime::translate_call_indirect() into FuncEnvironment.
Add two new arguments: - table_index is the WebAssembly table referenced in the indirect call. - sig_index is the WebAssembly signature index. We still have the SigRef that was created by make_indirect_sig(), but the WebAssembly signature index may be needed for detecting type mismatches at runtime. Change the insertion location to a plain FuncCursor rather than a FunctionBuilder<Local>. The fact that cretonne-wasm uses FunctionBuilder should be an implementation detail, and the callbacks don't need to access WebAssembly locals, so they don't need the extended interface. Add a FunctionBuilder::cursor() method which creates a FuncCursor for inserting instructions in the current EBB. Also add a FuncEnvironment::translate_call() method which allows the environment to override direct calls the same way as indirect calls.
This commit is contained in:
@@ -19,6 +19,8 @@ pub use ir::layout::Cursor as LayoutCursor;
|
|||||||
/// encoding.
|
/// encoding.
|
||||||
pub struct FuncCursor<'f> {
|
pub struct FuncCursor<'f> {
|
||||||
pos: CursorPosition,
|
pos: CursorPosition,
|
||||||
|
|
||||||
|
/// The referenced function.
|
||||||
pub func: &'f mut ir::Function,
|
pub func: &'f mut ir::Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +81,11 @@ impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut FuncCursor<'f> {
|
|||||||
pub struct EncCursor<'f> {
|
pub struct EncCursor<'f> {
|
||||||
pos: CursorPosition,
|
pos: CursorPosition,
|
||||||
built_inst: Option<ir::Inst>,
|
built_inst: Option<ir::Inst>,
|
||||||
|
|
||||||
|
/// The referenced function.
|
||||||
pub func: &'f mut ir::Function,
|
pub func: &'f mut ir::Function,
|
||||||
|
|
||||||
|
/// The target ISA that will be used to encode instructions.
|
||||||
pub isa: &'f TargetIsa,
|
pub isa: &'f TargetIsa,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -728,6 +728,27 @@ pub trait CursorBase {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rebuild this cursor positioned at the bottom of `ebb`.
|
||||||
|
///
|
||||||
|
/// This is intended to be used as a builder method:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use cretonne::ir::{Function, Ebb, Inst};
|
||||||
|
/// # use cretonne::ir::layout::{Cursor, CursorBase};
|
||||||
|
/// fn edit_func(func: &mut Function, ebb: Ebb) {
|
||||||
|
/// let mut pos = Cursor::new(&mut func.layout).at_bottom(ebb);
|
||||||
|
///
|
||||||
|
/// // Use `pos`...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn at_bottom(mut self, ebb: Ebb) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.goto_bottom(ebb);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the EBB corresponding to the current position.
|
/// Get the EBB corresponding to the current position.
|
||||||
fn current_ebb(&self) -> Option<Ebb> {
|
fn current_ebb(&self) -> Option<Ebb> {
|
||||||
use self::CursorPosition::*;
|
use self::CursorPosition::*;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ pub mod entity;
|
|||||||
|
|
||||||
pub mod binemit;
|
pub mod binemit;
|
||||||
pub mod bitset;
|
pub mod bitset;
|
||||||
|
pub mod cursor;
|
||||||
pub mod dominator_tree;
|
pub mod dominator_tree;
|
||||||
pub mod flowgraph;
|
pub mod flowgraph;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
@@ -31,7 +32,6 @@ pub mod verifier;
|
|||||||
mod abi;
|
mod abi;
|
||||||
mod constant_hash;
|
mod constant_hash;
|
||||||
mod context;
|
mod context;
|
||||||
mod cursor;
|
|
||||||
mod iterators;
|
mod iterators;
|
||||||
mod legalizer;
|
mod legalizer;
|
||||||
mod licm;
|
mod licm;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//! A frontend for building Cretonne IL from other languages.
|
//! A frontend for building Cretonne IL from other languages.
|
||||||
|
use cretonne::cursor::{Cursor, FuncCursor};
|
||||||
use cretonne::ir::{Ebb, Type, Value, Function, Inst, JumpTable, StackSlot, JumpTableData,
|
use cretonne::ir::{Ebb, Type, Value, Function, Inst, JumpTable, StackSlot, JumpTableData,
|
||||||
StackSlotData, DataFlowGraph, InstructionData, ExtFuncData, FuncRef, SigRef,
|
StackSlotData, DataFlowGraph, InstructionData, ExtFuncData, FuncRef, SigRef,
|
||||||
Signature, InstBuilderBase};
|
Signature, InstBuilderBase};
|
||||||
@@ -107,24 +108,11 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
|
|||||||
fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {
|
fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {
|
||||||
if data.opcode().is_return() {
|
if data.opcode().is_return() {
|
||||||
self.builder
|
self.builder
|
||||||
.check_return_args(data.arguments(&self.builder.func.dfg.value_lists))
|
.check_return_args(data.arguments(&self.builder.func.dfg.value_lists));
|
||||||
}
|
}
|
||||||
// We only insert the Ebb in the layout when an instruction is added to it
|
// We only insert the Ebb in the layout when an instruction is added to it
|
||||||
if self.builder.builder.ebbs[self.builder.position.ebb].pristine {
|
self.builder.ensure_inserted_ebb();
|
||||||
if !self.builder
|
|
||||||
.func
|
|
||||||
.layout
|
|
||||||
.is_ebb_inserted(self.builder.position.ebb) {
|
|
||||||
self.builder
|
|
||||||
.func
|
|
||||||
.layout
|
|
||||||
.append_ebb(self.builder.position.ebb);
|
|
||||||
}
|
|
||||||
self.builder.builder.ebbs[self.builder.position.ebb].pristine = false;
|
|
||||||
} else {
|
|
||||||
debug_assert!(!self.builder.builder.ebbs[self.builder.position.ebb].filled,
|
|
||||||
"you cannot add an instruction to a block already filled");
|
|
||||||
}
|
|
||||||
let inst = self.builder.func.dfg.make_inst(data.clone());
|
let inst = self.builder.func.dfg.make_inst(data.clone());
|
||||||
self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);
|
self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);
|
||||||
self.builder.func.layout.append_inst(inst, self.ebb);
|
self.builder.func.layout.append_inst(inst, self.ebb);
|
||||||
@@ -372,6 +360,31 @@ where
|
|||||||
let ebb = self.position.ebb;
|
let ebb = self.position.ebb;
|
||||||
FuncInstBuilder::new(self, ebb)
|
FuncInstBuilder::new(self, ebb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make sure that the current EBB is inserted in the layout.
|
||||||
|
fn ensure_inserted_ebb(&mut self) {
|
||||||
|
let ebb = self.position.ebb;
|
||||||
|
if self.builder.ebbs[ebb].pristine {
|
||||||
|
if !self.func.layout.is_ebb_inserted(ebb) {
|
||||||
|
self.func.layout.append_ebb(ebb);
|
||||||
|
}
|
||||||
|
self.builder.ebbs[ebb].pristine = false;
|
||||||
|
} else {
|
||||||
|
debug_assert!(
|
||||||
|
!self.builder.ebbs[ebb].filled,
|
||||||
|
"you cannot add an instruction to a block already filled"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `FuncCursor` pointed at the current position ready for inserting instructions.
|
||||||
|
///
|
||||||
|
/// This can be used to insert SSA code that doesn't need to access locals and that doesn't
|
||||||
|
/// need to know about `FunctionBuilder` at all.
|
||||||
|
pub fn cursor<'f>(&'f mut self) -> FuncCursor<'f> {
|
||||||
|
self.ensure_inserted_ebb();
|
||||||
|
FuncCursor::new(self.func).at_bottom(self.position.ebb)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the functions documented in the previous block are write-only and help you build a valid
|
/// All the functions documented in the previous block are write-only and help you build a valid
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ use cretonne::ir::immediates::{Ieee32, Ieee64};
|
|||||||
use cretonne::ir::condcodes::{IntCC, FloatCC};
|
use cretonne::ir::condcodes::{IntCC, FloatCC};
|
||||||
use cton_frontend::{ILBuilder, FunctionBuilder};
|
use cton_frontend::{ILBuilder, FunctionBuilder};
|
||||||
use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate};
|
use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate};
|
||||||
use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local,
|
use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local};
|
||||||
FunctionIndex};
|
use translation_utils::{TableIndex, SignatureIndex, FunctionIndex};
|
||||||
use state::{TranslationState, ControlStackFrame};
|
use state::{TranslationState, ControlStackFrame};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use runtime::{FuncEnvironment, GlobalValue, WasmRuntime};
|
use runtime::{FuncEnvironment, GlobalValue, WasmRuntime};
|
||||||
@@ -429,26 +429,30 @@ fn translate_operator(
|
|||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
Operator::Call { function_index } => {
|
Operator::Call { function_index } => {
|
||||||
let (fref, num_args) = state.get_direct_func(builder.func, function_index, runtime);
|
let (fref, num_args) = state.get_direct_func(builder.func, function_index, runtime);
|
||||||
// TODO: Let the function environment override the call instruction. It may want to add
|
let call = runtime.translate_call(
|
||||||
// arguments.
|
builder.cursor(),
|
||||||
let call_inst = builder.ins().call(fref, &state.peekn(num_args));
|
function_index as FunctionIndex,
|
||||||
|
fref,
|
||||||
|
state.peekn(num_args),
|
||||||
|
);
|
||||||
state.popn(num_args);
|
state.popn(num_args);
|
||||||
let ret_values = builder.inst_results(call_inst);
|
state.pushn(builder.func.dfg.inst_results(call));
|
||||||
state.pushn(ret_values);
|
|
||||||
}
|
}
|
||||||
Operator::CallIndirect {
|
Operator::CallIndirect { index, table_index } => {
|
||||||
index,
|
|
||||||
table_index: _,
|
|
||||||
} => {
|
|
||||||
// `index` is the index of the function's signature and `table_index` is the index of
|
// `index` is the index of the function's signature and `table_index` is the index of
|
||||||
// the table to search the function in.
|
// 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 (sigref, num_args) = state.get_indirect_sig(builder.func, index, runtime);
|
||||||
let index_val = state.pop1();
|
let callee = state.pop1();
|
||||||
let ret_values =
|
let call = runtime.translate_call_indirect(
|
||||||
runtime.translate_call_indirect(builder, sigref, index_val, &state.peekn(num_args));
|
builder.cursor(),
|
||||||
|
table_index as TableIndex,
|
||||||
|
index as SignatureIndex,
|
||||||
|
sigref,
|
||||||
|
callee,
|
||||||
|
&state.peekn(num_args),
|
||||||
|
);
|
||||||
state.popn(num_args);
|
state.popn(num_args);
|
||||||
state.pushn(ret_values);
|
state.pushn(builder.func.dfg.inst_results(call));
|
||||||
}
|
}
|
||||||
/******************************* 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
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use runtime::{FuncEnvironment, GlobalValue, WasmRuntime};
|
|||||||
use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex,
|
use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex,
|
||||||
FunctionIndex, MemoryIndex};
|
FunctionIndex, MemoryIndex};
|
||||||
use cton_frontend::FunctionBuilder;
|
use cton_frontend::FunctionBuilder;
|
||||||
use cretonne::ir::{self, Value, InstBuilder, SigRef};
|
use cretonne::ir::{self, Value, InstBuilder};
|
||||||
use cretonne::ir::types::*;
|
use cretonne::ir::types::*;
|
||||||
|
use cretonne::cursor::FuncCursor;
|
||||||
|
|
||||||
/// This runtime implementation is a "naïve" one, doing essentially nothing and emitting
|
/// This runtime implementation is a "naïve" one, doing essentially nothing and emitting
|
||||||
/// 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
|
||||||
@@ -75,6 +76,18 @@ impl FuncEnvironment for DummyRuntime {
|
|||||||
|
|
||||||
func.dfg.ext_funcs.push(ir::ExtFuncData { name, signature })
|
func.dfg.ext_funcs.push(ir::ExtFuncData { name, signature })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn translate_call_indirect(
|
||||||
|
&self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
_table_index: TableIndex,
|
||||||
|
_sig_index: SignatureIndex,
|
||||||
|
sig_ref: ir::SigRef,
|
||||||
|
callee: ir::Value,
|
||||||
|
call_args: &[ir::Value],
|
||||||
|
) -> ir::Inst {
|
||||||
|
pos.ins().call_indirect(sig_ref, callee, call_args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmRuntime for DummyRuntime {
|
impl WasmRuntime for DummyRuntime {
|
||||||
@@ -84,16 +97,6 @@ impl WasmRuntime for DummyRuntime {
|
|||||||
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value {
|
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value {
|
||||||
builder.ins().iconst(I32, -1)
|
builder.ins().iconst(I32, -1)
|
||||||
}
|
}
|
||||||
fn translate_call_indirect<'a>(
|
|
||||||
&self,
|
|
||||||
builder: &'a mut FunctionBuilder<Local>,
|
|
||||||
sig_ref: SigRef,
|
|
||||||
index_val: Value,
|
|
||||||
call_args: &[Value],
|
|
||||||
) -> &'a [Value] {
|
|
||||||
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) {
|
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||||
self.signatures.push(sig.clone());
|
self.signatures.push(sig.clone());
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
//! All the runtime support necessary for the wasm to cretonne translation is formalized by the
|
//! All the runtime support necessary for the wasm to cretonne translation is formalized by the
|
||||||
//! trait `WasmRuntime`.
|
//! trait `WasmRuntime`.
|
||||||
use cton_frontend::FunctionBuilder;
|
use cton_frontend::FunctionBuilder;
|
||||||
use cretonne::ir::{self, Value, SigRef};
|
use cretonne::ir::{self, Value, InstBuilder};
|
||||||
|
use cretonne::cursor::FuncCursor;
|
||||||
use translation_utils::{Local, SignatureIndex, FunctionIndex, TableIndex, GlobalIndex,
|
use translation_utils::{Local, SignatureIndex, FunctionIndex, TableIndex, GlobalIndex,
|
||||||
MemoryIndex, Global, Table, Memory};
|
MemoryIndex, Global, Table, Memory};
|
||||||
|
|
||||||
@@ -64,6 +65,42 @@ pub trait FuncEnvironment {
|
|||||||
/// The function's signature will only be used for direct calls, even if the module has
|
/// The function's signature will only be used for direct calls, even if the module has
|
||||||
/// indirect calls with the same WebAssembly type.
|
/// indirect calls with the same WebAssembly type.
|
||||||
fn make_direct_func(&self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef;
|
fn make_direct_func(&self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef;
|
||||||
|
|
||||||
|
/// Translate a `call_indirect` WebAssembly instruction at `pos`.
|
||||||
|
///
|
||||||
|
/// Insert instructions at `pos` for an indirect call to the function `callee` in the table
|
||||||
|
/// `table_index` with WebAssembly signature `sig_index`. The `callee` value will have type
|
||||||
|
/// `i32`.
|
||||||
|
///
|
||||||
|
/// The signature `sig_ref` was previously created by `make_indirect_sig()`.
|
||||||
|
///
|
||||||
|
/// Return the call instruction whose results are the WebAssembly return values.
|
||||||
|
fn translate_call_indirect(
|
||||||
|
&self,
|
||||||
|
pos: FuncCursor,
|
||||||
|
table_index: TableIndex,
|
||||||
|
sig_index: SignatureIndex,
|
||||||
|
sig_ref: ir::SigRef,
|
||||||
|
callee: ir::Value,
|
||||||
|
call_args: &[ir::Value],
|
||||||
|
) -> ir::Inst;
|
||||||
|
|
||||||
|
/// Translate a `call` WebAssembly instruction at `pos`.
|
||||||
|
///
|
||||||
|
/// Insert instructions at `pos` for a direct call to the function `callee_index`.
|
||||||
|
///
|
||||||
|
/// The function reference `callee` was previously created by `make_direct_func()`.
|
||||||
|
///
|
||||||
|
/// Return the call instruction whose results are the WebAssembly return values.
|
||||||
|
fn translate_call(
|
||||||
|
&self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
_callee_index: FunctionIndex,
|
||||||
|
callee: ir::FuncRef,
|
||||||
|
call_args: &[ir::Value],
|
||||||
|
) -> ir::Inst {
|
||||||
|
pos.ins().call(callee, call_args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -108,13 +145,4 @@ pub trait WasmRuntime: FuncEnvironment {
|
|||||||
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, val: Value) -> Value;
|
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, val: Value) -> Value;
|
||||||
/// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory.
|
/// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory.
|
||||||
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value;
|
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value;
|
||||||
/// Translates a `call_indirect` wasm instruction. It involves looking up the value contained
|
|
||||||
/// it the table at location `index_val` and calling the corresponding function.
|
|
||||||
fn translate_call_indirect<'a>(
|
|
||||||
&self,
|
|
||||||
builder: &'a mut FunctionBuilder<Local>,
|
|
||||||
sig_ref: SigRef,
|
|
||||||
index_val: Value,
|
|
||||||
call_args: &[Value],
|
|
||||||
) -> &'a [Value];
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user