diff --git a/lib/cretonne/src/cursor.rs b/lib/cretonne/src/cursor.rs index 8de400a1f1..442d820c09 100644 --- a/lib/cretonne/src/cursor.rs +++ b/lib/cretonne/src/cursor.rs @@ -19,6 +19,8 @@ pub use ir::layout::Cursor as LayoutCursor; /// encoding. pub struct FuncCursor<'f> { pos: CursorPosition, + + /// The referenced 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> { pos: CursorPosition, built_inst: Option, + + /// The referenced function. pub func: &'f mut ir::Function, + + /// The target ISA that will be used to encode instructions. pub isa: &'f TargetIsa, } diff --git a/lib/cretonne/src/ir/layout.rs b/lib/cretonne/src/ir/layout.rs index 7f3247c080..33fbaf943d 100644 --- a/lib/cretonne/src/ir/layout.rs +++ b/lib/cretonne/src/ir/layout.rs @@ -728,6 +728,27 @@ pub trait CursorBase { 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. fn current_ebb(&self) -> Option { use self::CursorPosition::*; diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index a63b00b3ae..6949f92f5f 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -17,6 +17,7 @@ pub mod entity; pub mod binemit; pub mod bitset; +pub mod cursor; pub mod dominator_tree; pub mod flowgraph; pub mod ir; @@ -31,7 +32,6 @@ pub mod verifier; mod abi; mod constant_hash; mod context; -mod cursor; mod iterators; mod legalizer; mod licm; diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index 8eb90dd1d6..4f6d4ef640 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -1,4 +1,5 @@ //! 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, StackSlotData, DataFlowGraph, InstructionData, ExtFuncData, FuncRef, SigRef, 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) { if data.opcode().is_return() { 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 - if self.builder.builder.ebbs[self.builder.position.ebb].pristine { - 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"); - } + self.builder.ensure_inserted_ebb(); + let inst = self.builder.func.dfg.make_inst(data.clone()); self.builder.func.dfg.make_inst_results(inst, ctrl_typevar); self.builder.func.layout.append_inst(inst, self.ebb); @@ -372,6 +360,31 @@ where let ebb = self.position.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 diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 59bb306817..02cd7e1b10 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -27,8 +27,8 @@ use cretonne::ir::immediates::{Ieee32, Ieee64}; use cretonne::ir::condcodes::{IntCC, FloatCC}; use cton_frontend::{ILBuilder, FunctionBuilder}; use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate}; -use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local, - FunctionIndex}; +use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local}; +use translation_utils::{TableIndex, SignatureIndex, FunctionIndex}; use state::{TranslationState, ControlStackFrame}; use std::collections::HashMap; use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; @@ -429,26 +429,30 @@ fn translate_operator( ************************************************************************************/ Operator::Call { function_index } => { 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 - // arguments. - let call_inst = builder.ins().call(fref, &state.peekn(num_args)); + let call = runtime.translate_call( + builder.cursor(), + function_index as FunctionIndex, + fref, + state.peekn(num_args), + ); state.popn(num_args); - let ret_values = builder.inst_results(call_inst); - state.pushn(ret_values); + state.pushn(builder.func.dfg.inst_results(call)); } - Operator::CallIndirect { - index, - table_index: _, - } => { + Operator::CallIndirect { 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, num_args) = state.get_indirect_sig(builder.func, index, runtime); - let index_val = state.pop1(); - let ret_values = - runtime.translate_call_indirect(builder, sigref, index_val, &state.peekn(num_args)); + let callee = state.pop1(); + let call = runtime.translate_call_indirect( + builder.cursor(), + table_index as TableIndex, + index as SignatureIndex, + sigref, + callee, + &state.peekn(num_args), + ); state.popn(num_args); - state.pushn(ret_values); + state.pushn(builder.func.dfg.inst_results(call)); } /******************************* Memory management *********************************** * Memory management is handled by runtime. It is usually translated into calls to diff --git a/lib/wasm/src/runtime/dummy.rs b/lib/wasm/src/runtime/dummy.rs index 1bb48257c9..178d39c5d1 100644 --- a/lib/wasm/src/runtime/dummy.rs +++ b/lib/wasm/src/runtime/dummy.rs @@ -2,8 +2,9 @@ use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; 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::{self, Value, InstBuilder}; use cretonne::ir::types::*; +use cretonne::cursor::FuncCursor; /// 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 @@ -75,6 +76,18 @@ impl FuncEnvironment for DummyRuntime { 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 { @@ -84,16 +97,6 @@ impl WasmRuntime for DummyRuntime { fn translate_current_memory(&mut self, builder: &mut FunctionBuilder) -> Value { builder.ins().iconst(I32, -1) } - fn translate_call_indirect<'a>( - &self, - builder: &'a mut FunctionBuilder, - 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) { self.signatures.push(sig.clone()); diff --git a/lib/wasm/src/runtime/spec.rs b/lib/wasm/src/runtime/spec.rs index b261181491..5750fe5520 100644 --- a/lib/wasm/src/runtime/spec.rs +++ b/lib/wasm/src/runtime/spec.rs @@ -1,7 +1,8 @@ //! All the runtime support necessary for the wasm to cretonne translation is formalized by the //! trait `WasmRuntime`. 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, 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 /// indirect calls with the same WebAssembly type. 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 @@ -108,13 +145,4 @@ pub trait WasmRuntime: FuncEnvironment { fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder, val: Value) -> Value; /// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory. fn translate_current_memory(&mut self, builder: &mut FunctionBuilder) -> 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, - sig_ref: SigRef, - index_val: Value, - call_args: &[Value], - ) -> &'a [Value]; }