Simplify Ebb parameter creation.

Ebb parameters are appended explicitly by whoever calls create_block,
so they don't need to also be inferred from branches. This makes the
frontend code more flexible for producers that want to create things
in a different order, and it eliminates more temporary allocations.

FunctionBuilder no longer maintains its own list of the function
parameter values; these can be obtained from the ebb parameter list
on the entry block.
This commit is contained in:
Dan Gohman
2017-11-07 14:55:08 -08:00
parent cce2384ede
commit 9b15fe7dfd
3 changed files with 47 additions and 145 deletions

View File

@@ -4,7 +4,6 @@ use cretonne::ir;
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, GlobalVarData, GlobalVar, HeapData, Heap}; Signature, InstBuilderBase, GlobalVarData, GlobalVar, HeapData, Heap};
use cretonne::ir::instructions::BranchInfo;
use cretonne::ir::function::DisplayFunction; use cretonne::ir::function::DisplayFunction;
use cretonne::isa::TargetIsa; use cretonne::isa::TargetIsa;
use ssa::{SSABuilder, SideEffects, Block}; use ssa::{SSABuilder, SideEffects, Block};
@@ -22,7 +21,6 @@ where
ssa: SSABuilder<Variable>, ssa: SSABuilder<Variable>,
ebbs: EntityMap<Ebb, EbbData>, ebbs: EntityMap<Ebb, EbbData>,
types: EntityMap<Variable, Type>, types: EntityMap<Variable, Type>,
function_params_values: Vec<Value>,
} }
@@ -40,9 +38,6 @@ where
builder: &'a mut ILBuilder<Variable>, builder: &'a mut ILBuilder<Variable>,
position: Position, position: Position,
/// Has builder.function_params_values been populated yet?
params_values_initialized: bool,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
@@ -68,7 +63,6 @@ where
ssa: SSABuilder::new(), ssa: SSABuilder::new(),
ebbs: EntityMap::new(), ebbs: EntityMap::new(),
types: EntityMap::new(), types: EntityMap::new(),
function_params_values: Vec::new(),
} }
} }
@@ -76,12 +70,10 @@ where
self.ssa.clear(); self.ssa.clear();
self.ebbs.clear(); self.ebbs.clear();
self.types.clear(); self.types.clear();
self.function_params_values.clear();
} }
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.ssa.is_empty() && self.ebbs.is_empty() && self.types.is_empty() && self.ssa.is_empty() && self.ebbs.is_empty() && self.types.is_empty()
self.function_params_values.is_empty()
} }
} }
@@ -122,10 +114,6 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
// instruction being inserted to add related info to the DFG and the SSA building system, // instruction being inserted to add related info to the DFG and the SSA building system,
// and perform debug sanity checks. // and perform debug sanity checks.
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 cfg!(debug_assertions) && data.opcode().is_return() {
self.builder
.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
self.builder.ensure_inserted_ebb(); self.builder.ensure_inserted_ebb();
@@ -141,15 +129,6 @@ impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short
Some(dest_ebb) => { Some(dest_ebb) => {
// If the user has supplied jump arguments we must adapt the arguments of // If the user has supplied jump arguments we must adapt the arguments of
// the destination ebb // the destination ebb
// TODO: find a way not to allocate a vector
let args_types: Vec<Value> =
match data.analyze_branch(&self.builder.func.dfg.value_lists) {
BranchInfo::SingleDest(_, args) => {
args.to_vec()
}
_ => panic!("should not happen"),
};
self.builder.ebb_params_adjustment(dest_ebb, &args_types);
self.builder.declare_successor(dest_ebb, inst); self.builder.declare_successor(dest_ebb, inst);
} }
None => { None => {
@@ -238,7 +217,6 @@ where
ebb: Ebb::new(0), ebb: Ebb::new(0),
basic_block: Block::new(0), basic_block: Block::new(0),
}, },
params_values_initialized: false,
} }
} }
@@ -247,7 +225,7 @@ where
self.srcloc = srcloc; self.srcloc = srcloc;
} }
/// Creates a new `Ebb` for the function and returns its reference. /// Creates a new `Ebb` and returns its reference.
pub fn create_ebb(&mut self) -> Ebb { pub fn create_ebb(&mut self) -> Ebb {
let ebb = self.func.dfg.make_ebb(); let ebb = self.func.dfg.make_ebb();
self.builder.ssa.declare_ebb_header_block(ebb); self.builder.ssa.declare_ebb_header_block(ebb);
@@ -267,9 +245,6 @@ where
/// successor), the block will be declared filled and it will not be possible to append /// successor), the block will be declared filled and it will not be possible to append
/// instructions to it. /// instructions to it.
pub fn switch_to_block(&mut self, ebb: Ebb) { pub fn switch_to_block(&mut self, ebb: Ebb) {
if !self.params_values_initialized {
self.fill_function_params_values(ebb);
}
if !self.builder.ebbs[self.position.ebb].pristine { if !self.builder.ebbs[self.position.ebb].pristine {
// First we check that the previous block has been filled. // First we check that the previous block has been filled.
debug_assert!( debug_assert!(
@@ -333,11 +308,6 @@ where
/// Register a new definition of a user variable. Panics if the type of the value is not the /// Register a new definition of a user variable. Panics if the type of the value is not the
/// same as the type registered for the variable. /// same as the type registered for the variable.
pub fn def_var(&mut self, var: Variable, val: Value) { pub fn def_var(&mut self, var: Variable, val: Value) {
debug_assert_eq!(
self.func.dfg.value_type(val),
self.builder.types[var],
"the type of the value is not the type registered for the variable"
);
self.builder.ssa.def_var( self.builder.ssa.def_var(
var, var,
val, val,
@@ -345,23 +315,6 @@ where
); );
} }
/// Returns the value corresponding to the `i`-th parameter of the function as defined by
/// the function signature. Panics if `i` is out of bounds or if called before the first call
/// to `switch_to_block`.
pub fn param_value(&self, i: usize) -> Value {
debug_assert!(
self.params_values_initialized,
"you have to call switch_to_block first."
);
self.builder.function_params_values[i]
}
/// Use param_value instead.
#[deprecated(since = "forever", note = "arg_value is renamed to param_value")]
pub fn arg_value(&self, i: usize) -> Value {
self.param_value(i)
}
/// Creates a jump table in the function, to be used by `br_table` instructions. /// Creates a jump table in the function, to be used by `br_table` instructions.
pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable { pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
self.func.create_jump_table(data) self.func.create_jump_table(data)
@@ -431,6 +384,28 @@ where
.with_srcloc(self.srcloc) .with_srcloc(self.srcloc)
.at_bottom(self.position.ebb) .at_bottom(self.position.ebb)
} }
/// Append parameters to the given `Ebb` corresponding to the function
/// parameters. This can be used to set up the ebb parameters for the
/// entry block.
pub fn append_ebb_params_for_function_params(&mut self, ebb: Ebb) {
let user_arg_count = &mut self.builder.ebbs[ebb].user_arg_count;
for argtyp in &self.func.signature.params {
*user_arg_count += 1;
self.func.dfg.append_ebb_param(ebb, argtyp.value_type);
}
}
/// Append parameters to the given `Ebb` corresponding to the function
/// return values. This can be used to set up the ebb parameters for a
/// function exit block.
pub fn append_ebb_params_for_function_returns(&mut self, ebb: Ebb) {
let user_arg_count = &mut self.builder.ebbs[ebb].user_arg_count;
for argtyp in &self.func.signature.returns {
*user_arg_count += 1;
self.func.dfg.append_ebb_param(ebb, argtyp.value_type);
}
}
} }
/// 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
@@ -460,6 +435,11 @@ where
/// instructions to it, otherwise this could interfere with SSA construction. /// instructions to it, otherwise this could interfere with SSA construction.
pub fn append_ebb_param(&mut self, ebb: Ebb, ty: Type) -> Value { pub fn append_ebb_param(&mut self, ebb: Ebb, ty: Type) -> Value {
debug_assert!(self.builder.ebbs[ebb].pristine); debug_assert!(self.builder.ebbs[ebb].pristine);
debug_assert_eq!(
self.builder.ebbs[ebb].user_arg_count,
self.func.dfg.num_ebb_params(ebb)
);
self.builder.ebbs[ebb].user_arg_count += 1;
self.func.dfg.append_ebb_param(ebb, ty) self.func.dfg.append_ebb_param(ebb, ty)
} }
@@ -497,12 +477,6 @@ where
self.builder.ssa.predecessors(self.position.ebb).is_empty() self.builder.ssa.predecessors(self.position.ebb).is_empty()
} }
/// Returns `true` if and only if the entry block has been started and `param_value`
/// may be called.
pub fn entry_block_started(&self) -> bool {
self.params_values_initialized
}
/// Returns `true` if and only if no instructions have been added since the last call to /// Returns `true` if and only if no instructions have been added since the last call to
/// `switch_to_block`. /// `switch_to_block`.
pub fn is_pristine(&self) -> bool { pub fn is_pristine(&self) -> bool {
@@ -532,10 +506,15 @@ where
// Check that all the `Ebb`s are filled and sealed. // Check that all the `Ebb`s are filled and sealed.
debug_assert!( debug_assert!(
self.builder.ebbs.keys().all(|ebb| { self.builder.ebbs.keys().all(|ebb| {
self.builder.ebbs[ebb].pristine || self.builder.ebbs[ebb].pristine || self.builder.ssa.is_sealed(ebb)
(self.builder.ssa.is_sealed(ebb) && self.builder.ebbs[ebb].filled)
}), }),
"all blocks should be filled and sealed before dropping a FunctionBuilder" "all blocks should be sealed before dropping a FunctionBuilder"
);
debug_assert!(
self.builder.ebbs.keys().all(|ebb| {
self.builder.ebbs[ebb].pristine || self.builder.ebbs[ebb].filled
}),
"all blocks should be filled before dropping a FunctionBuilder"
); );
// Clear the state (but preserve the allocated buffers) in preparation // Clear the state (but preserve the allocated buffers) in preparation
@@ -567,87 +546,6 @@ where
); );
} }
#[cfg(debug_assertions)]
fn check_return_args(&self, args: &[Value]) {
debug_assert_eq!(
args.len(),
self.func.signature.returns.len(),
"the number of returned values doesn't match the function signature "
);
for (i, arg) in args.iter().enumerate() {
let valty = self.func.dfg.value_type(*arg);
debug_assert_eq!(
valty,
self.func.signature.returns[i].value_type,
"the types of the values returned don't match the \
function signature"
);
}
}
fn fill_function_params_values(&mut self, ebb: Ebb) {
debug_assert!(!self.params_values_initialized);
for argtyp in &self.func.signature.params {
self.builder.function_params_values.push(
self.func.dfg.append_ebb_param(ebb, argtyp.value_type),
);
}
self.params_values_initialized = true;
}
fn ebb_params_adjustment(&mut self, dest_ebb: Ebb, jump_args: &[Value]) {
if self.builder.ssa.predecessors(dest_ebb).is_empty() ||
self.builder.ebbs[dest_ebb].pristine
{
// This is the first jump instruction targeting this Ebb
// so the jump arguments supplied here are this Ebb' arguments
// However some of the arguments might already be there
// in the Ebb so we have to check they're consistent
let dest_ebb_params_len = {
let dest_ebb_params = self.func.dfg.ebb_params(dest_ebb);
debug_assert!(
dest_ebb_params
.iter()
.zip(jump_args.iter().take(dest_ebb_params.len()))
.all(|(dest_arg, jump_arg)| {
self.func.dfg.value_type(*jump_arg) ==
self.func.dfg.value_type(*dest_arg)
}),
"the jump argument supplied has not the \
same type as the corresponding dest ebb argument"
);
dest_ebb_params.len()
};
self.builder.ebbs[dest_ebb].user_arg_count = jump_args.len();
for val in jump_args.iter().skip(dest_ebb_params_len) {
let ty = self.func.dfg.value_type(*val);
self.func.dfg.append_ebb_param(dest_ebb, ty);
}
} else {
// The Ebb already has predecessors
// We check that the arguments supplied match those supplied
// previously.
debug_assert_eq!(
jump_args.len(),
self.builder.ebbs[dest_ebb].user_arg_count,
"the jump instruction doesn't have the same \
number of arguments as its destination Ebb.",
);
debug_assert!(
jump_args
.iter()
.zip(self.func.dfg.ebb_params(dest_ebb).iter().take(
self.builder.ebbs[dest_ebb].user_arg_count,
))
.all(|(jump_arg, dest_arg)| {
self.func.dfg.value_type(*jump_arg) == self.func.dfg.value_type(*dest_arg)
}),
"the jump argument supplied has not the \
same type as the corresponding dest ebb argument"
);
}
}
fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) { fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
for split_ebb in side_effects.split_ebbs_created { for split_ebb in side_effects.split_ebbs_created {
self.builder.ebbs[split_ebb].filled = true self.builder.ebbs[split_ebb].filled = true
@@ -703,13 +601,14 @@ mod tests {
builder.declare_var(x, I32); builder.declare_var(x, I32);
builder.declare_var(y, I32); builder.declare_var(y, I32);
builder.declare_var(z, I32); builder.declare_var(z, I32);
builder.append_ebb_params_for_function_params(block0);
builder.switch_to_block(block0); builder.switch_to_block(block0);
if !lazy_seal { if !lazy_seal {
builder.seal_block(block0); builder.seal_block(block0);
} }
{ {
let tmp = builder.param_value(0); let tmp = builder.ebb_params(block0)[0]; // the first function parameter
builder.def_var(x, tmp); builder.def_var(x, tmp);
} }
{ {

View File

@@ -75,11 +75,12 @@
//! builder.declare_var(x, I32); //! builder.declare_var(x, I32);
//! builder.declare_var(y, I32); //! builder.declare_var(y, I32);
//! builder.declare_var(z, I32); //! builder.declare_var(z, I32);
//! builder.append_ebb_params_for_function_params(block0);
//! //!
//! builder.switch_to_block(block0); //! builder.switch_to_block(block0);
//! builder.seal_block(block0); //! builder.seal_block(block0);
//! { //! {
//! let tmp = builder.param_value(0); //! let tmp = builder.ebb_params(block0)[0]; // the first function parameter
//! builder.def_var(x, tmp); //! builder.def_var(x, tmp);
//! } //! }
//! { //! {

View File

@@ -6,7 +6,7 @@
use code_translator::translate_operator; use code_translator::translate_operator;
use cretonne::entity::EntityRef; use cretonne::entity::EntityRef;
use cretonne::ir::{self, InstBuilder}; use cretonne::ir::{self, InstBuilder, Ebb};
use cretonne::result::{CtonResult, CtonError}; use cretonne::result::{CtonResult, CtonError};
use cton_frontend::{ILBuilder, FunctionBuilder}; use cton_frontend::{ILBuilder, FunctionBuilder};
use environ::FuncEnvironment; use environ::FuncEnvironment;
@@ -82,17 +82,19 @@ impl FuncTranslator {
// This clears the `ILBuilder`. // This clears the `ILBuilder`.
let mut builder = FunctionBuilder::new(func, &mut self.il_builder); let mut builder = FunctionBuilder::new(func, &mut self.il_builder);
let entry_block = builder.create_ebb(); let entry_block = builder.create_ebb();
builder.append_ebb_params_for_function_params(entry_block);
builder.switch_to_block(entry_block); // This also creates values for the arguments. builder.switch_to_block(entry_block); // This also creates values for the arguments.
builder.seal_block(entry_block); builder.seal_block(entry_block);
// Make sure the entry block is inserted in the layout before we make any callbacks to // Make sure the entry block is inserted in the layout before we make any callbacks to
// `environ`. The callback functions may need to insert things in the entry block. // `environ`. The callback functions may need to insert things in the entry block.
builder.ensure_inserted_ebb(); builder.ensure_inserted_ebb();
let num_params = declare_wasm_parameters(&mut builder); let num_params = declare_wasm_parameters(&mut builder, entry_block);
// Set up the translation state with a single pushed control block representing the whole // Set up the translation state with a single pushed control block representing the whole
// function and its return values. // function and its return values.
let exit_block = builder.create_ebb(); let exit_block = builder.create_ebb();
builder.append_ebb_params_for_function_returns(exit_block);
self.state.initialize(&builder.func.signature, exit_block); self.state.initialize(&builder.func.signature, exit_block);
parse_local_decls(&mut reader, &mut builder, num_params)?; parse_local_decls(&mut reader, &mut builder, num_params)?;
@@ -103,7 +105,7 @@ impl FuncTranslator {
/// Declare local variables for the signature parameters that correspond to WebAssembly locals. /// Declare local variables for the signature parameters that correspond to WebAssembly locals.
/// ///
/// Return the number of local variables declared. /// Return the number of local variables declared.
fn declare_wasm_parameters(builder: &mut FunctionBuilder<Local>) -> usize { fn declare_wasm_parameters(builder: &mut FunctionBuilder<Local>, entry_block: Ebb) -> usize {
let sig_len = builder.func.signature.params.len(); let sig_len = builder.func.signature.params.len();
let mut next_local = 0; let mut next_local = 0;
for i in 0..sig_len { for i in 0..sig_len {
@@ -116,7 +118,7 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder<Local>) -> usize {
builder.declare_var(local, param_type.value_type); builder.declare_var(local, param_type.value_type);
next_local += 1; next_local += 1;
let param_value = builder.param_value(i); let param_value = builder.ebb_params(entry_block)[i];
builder.def_var(local, param_value); builder.def_var(local, param_value);
} }
} }