diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index 06eed706e9..6df15bd86c 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -15,6 +15,7 @@ path = "src/cton-util.rs" [dependencies] cretonne = { path = "lib/cretonne" } cretonne-reader = { path = "lib/reader" } +cretonne-frontend = { path ="lib/frontend" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/cranelift/test-all.sh b/cranelift/test-all.sh index 07d6fa2bdd..72b07f8856 100755 --- a/cranelift/test-all.sh +++ b/cranelift/test-all.sh @@ -40,7 +40,7 @@ if [ -n "$needcheck" ]; then touch $tsfile || echo no target directory fi -PKGS="cretonne cretonne-reader cretonne-tools filecheck" +PKGS="cretonne cretonne-reader cretonne-tools cretonne-frontend filecheck" cd "$topdir" for PKG in $PKGS do diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index 256845bcfb..332ce43b03 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/lib/cretonne/src/ir/builder.rs @@ -23,6 +23,8 @@ pub trait InstBuilderBase<'f>: Sized { /// Get an immutable reference to the data flow graph that will hold the constructed /// instructions. fn data_flow_graph(&self) -> &DataFlowGraph; + /// Get a mutable reference to the data flow graph that will hold the constructed + /// instructions. fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph; /// Insert an instruction and return a reference to it, consuming the builder. diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 51ada16775..85e971a9fc 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -728,6 +728,37 @@ impl DataFlowGraph { num as usize } + /// Removes `val` from `ebb`'s arguments by a standard linear time list removal which preserves + /// ordering. Also updates the values' data. + pub fn remove_ebb_arg(&mut self, val: Value) { + let (ebb, num) = if let ValueData::Arg { num, ebb, .. } = self.values[val] { + (ebb, num) + } else { + panic!("{} must be an EBB argument", val); + }; + self.ebbs[ebb] + .args + .remove(num as usize, &mut self.value_lists); + for index in num..(self.ebb_args(ebb).len() as u16) { + match self.values[self.ebbs[ebb] + .args + .get(index as usize, &self.value_lists) + .unwrap()] { + ValueData::Arg { ref mut num, .. } => { + *num -= 1; + } + _ => { + panic!("{} must be an EBB argument", + self.ebbs[ebb] + .args + .get(index as usize, &self.value_lists) + .unwrap()) + } + } + } + } + + /// Append an existing argument value to `ebb`. /// /// The appended value can't already be attached to something else. diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index c79edfa4c6..ebe39a3fcb 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -31,6 +31,7 @@ pub use ir::function::Function; pub use ir::builder::InstBuilder; pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint}; pub use ir::memflags::MemFlags; +pub use ir::builder::InstBuilderBase; use binemit; use entity_map::EntityMap; diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index 08c1c4354b..0b09a8f10d 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -24,6 +24,7 @@ pub mod flowgraph; pub mod ir; pub mod isa; pub mod loop_analysis; +pub mod packed_option; pub mod regalloc; pub mod result; pub mod settings; @@ -36,7 +37,6 @@ mod context; mod iterators; mod legalizer; mod licm; -mod packed_option; mod partition_slice; mod predicates; mod ref_slice; diff --git a/lib/frontend/Cargo.toml b/lib/frontend/Cargo.toml new file mode 100644 index 0000000000..7ff44a1f6f --- /dev/null +++ b/lib/frontend/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["The Cretonne Project Developers"] +name = "cretonne-frontend" +version = "0.0.0" +description = "Cretonne IL builder helper" +license = "Apache-2.0" +documentation = "https://cretonne.readthedocs.io/" +repository = "https://github.com/stoklund/cretonne" +publish = false + +[lib] +name = "cton_frontend" + +[dependencies] +cretonne = { path = "../cretonne" } diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs new file mode 100644 index 0000000000..7f9fb9edb6 --- /dev/null +++ b/lib/frontend/src/frontend.rs @@ -0,0 +1,682 @@ +//! A frontend for building Cretonne IL from other languages. +use cretonne::ir::{Ebb, Type, Value, Function, Inst, JumpTable, StackSlot, JumpTableData, + StackSlotData, DataFlowGraph, InstructionData, ExtFuncData, FuncRef, SigRef, + Signature, InstBuilderBase}; +use cretonne::ir::instructions::BranchInfo; +use cretonne::ir::function::DisplayFunction; +use cretonne::isa::TargetIsa; +use ssa::{SSABuilder, SideEffects, Block}; +use cretonne::entity_map::{EntityMap, PrimaryEntityData}; +use cretonne::entity_ref::EntityRef; +use std::hash::Hash; + +/// Permanent structure used for translating into Cretonne IL. +pub struct ILBuilder + where Variable: EntityRef + Hash + Default +{ + ssa: SSABuilder, + ebbs: EntityMap, + types: EntityMap, + function_args_values: Vec, +} + + +/// Temporary object used to build a Cretonne IL `Function`. +pub struct FunctionBuilder<'a, Variable: 'a> + where Variable: EntityRef + Hash + Default +{ + func: &'a mut Function, + builder: &'a mut ILBuilder, + position: Position, + pristine: bool, +} + +#[derive(Clone, Default)] +struct EbbData { + filled: bool, + pristine: bool, + user_arg_count: usize, +} + +impl PrimaryEntityData for EbbData {} + +struct Position { + ebb: Ebb, + basic_block: Block, +} + +impl ILBuilder + where Variable: EntityRef + Hash + Default +{ + /// Creates a ILBuilder structure. The structure is automatically cleared each time it is + /// passed to a [`FunctionBuilder`](struct.FunctionBuilder.html) for creation. + pub fn new() -> ILBuilder { + ILBuilder { + ssa: SSABuilder::new(), + ebbs: EntityMap::new(), + types: EntityMap::new(), + function_args_values: Vec::new(), + } + } + + fn clear(&mut self) { + self.ssa.clear(); + self.ebbs.clear(); + self.types.clear(); + self.function_args_values.clear(); + } +} + +/// Implementation of the [`InstBuilder`](../cretonne/ir/builder/trait.InstBuilder.html) that has +/// one convenience method per Cretonne IL instruction. +pub struct FuncInstBuilder<'short, 'long: 'short, Variable: 'long> + where Variable: EntityRef + Hash + Default +{ + builder: &'short mut FunctionBuilder<'long, Variable>, + ebb: Ebb, +} + +impl<'short, 'long, Variable> FuncInstBuilder<'short, 'long, Variable> + where Variable: EntityRef + Hash + Default +{ + fn new<'s, 'l>(builder: &'s mut FunctionBuilder<'l, Variable>, + ebb: Ebb) + -> FuncInstBuilder<'s, 'l, Variable> { + FuncInstBuilder { builder, ebb } + } +} + +impl<'short, 'long, Variable> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long, Variable> + where Variable: EntityRef + Hash + Default +{ + fn data_flow_graph(&self) -> &DataFlowGraph { + &self.builder.func.dfg + } + + fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph { + &mut self.builder.func.dfg + } + + // This implementation is richer than `InsertBuilder` because we use the data of the + // instruction being inserted to add related info to the DFG and the SSA building system, + // and perform debug sanity checks. + 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)) + } + // 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"); + } + 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); + if data.opcode().is_branch() { + match data.branch_destination() { + Some(dest_ebb) => { + // If the user has supplied jump arguments we must adapt the arguments of + // the destination ebb + // TODO: find a way not to allocate a vector + let args_types: Vec = + match data.analyze_branch(&self.builder.func.dfg.value_lists) { + BranchInfo::SingleDest(_, args) => { + args.iter() + .map(|arg| self.builder.func.dfg.value_type(arg.clone())) + .collect() + } + _ => panic!("should not happen"), + }; + self.builder + .ebb_args_adjustement(dest_ebb, args_types.as_slice()); + self.builder.declare_successor(dest_ebb, inst); + } + None => { + // branch_destination() doesn't detect jump_tables + match data { + // If jump table we declare all entries successor + // TODO: not collect with vector? + InstructionData::BranchTable { table, .. } => { + for dest_ebb in self.builder + .func + .jump_tables + .get(table) + .expect("you are referencing an undeclared jump table") + .entries() + .map(|(_, ebb)| ebb) + .collect::>() { + self.builder.declare_successor(dest_ebb, inst) + } + } + // If not we do nothing + _ => {} + } + } + } + } + if data.opcode().is_terminator() { + self.builder.fill_current_block() + } else if data.opcode().is_branch() { + self.builder.move_to_next_basic_block() + } + (inst, &mut self.builder.func.dfg) + } +} + +/// This module allows you to create a function in Cretonne IL in a straightforward way, hiding +/// all the complexity of its internal representation. +/// +/// The module is parametrized by one type which is the representation of variables in your +/// origin language. It offers a way to conveniently append instruction to your program flow. +/// You are responsible to split your instruction flow into extended blocks (declared with +/// `create_ebb`) whose properties are: +/// +/// - branch and jump instructions can only point at the top of extended blocks; +/// - the last instruction of each block is a terminator instruction which has no natural sucessor, +/// and those instructions can only appear at the end of extended blocks. +/// +/// The parameters of Cretonne IL instructions are Cretonne IL values, which can only be created +/// as results of other Cretonne IL instructions. To be able to create variables redefined multiple +/// times in your program, use the `def_var` and `use_var` command, that will maintain the +/// correspondance between your variables and Cretonne IL SSA values. +/// +/// The first block for which you call `switch_to_block` will be assumed to be the beginning of +/// the function. +/// +/// At creation, a `FunctionBuilder` instance borrows an already allocated `Function` which it +/// modifies with the information stored in the mutable borrowed +/// [`ILBuilder`](struct.ILBuilder.html). The function passed in argument should be newly created +/// with [`Function::with_name_signature()`](../function/struct.Function.html), whereas the +/// `ILBuilder` can be kept as is between two function translations. +/// +/// # Errors +/// +/// The functions below will panic in debug mode whenever you try to modify the Cretonne IL +/// function in a way that violate the coherence of the code. For instance: switching to a new +/// `Ebb` when you haven't filled the current one with a terminator instruction, inserting a +/// return instruction with arguments that don't match the function's signature. +impl<'a, Variable> FunctionBuilder<'a, Variable> + where Variable: EntityRef + Hash + Default +{ + /// Creates a new FunctionBuilder structure that will operate on a `Function` using a + /// `IlBuilder`. + pub fn new(func: &'a mut Function, + builder: &'a mut ILBuilder) + -> FunctionBuilder<'a, Variable> { + builder.clear(); + FunctionBuilder { + func: func, + builder: builder, + position: Position { + ebb: Ebb::new(0), + basic_block: Block::new(0), + }, + pristine: true, + } + } + + /// Creates a new `Ebb` for the function and returns its reference. + pub fn create_ebb(&mut self) -> Ebb { + let ebb = self.func.dfg.make_ebb(); + self.builder.ssa.declare_ebb_header_block(ebb); + *self.builder.ebbs.ensure(ebb) = EbbData { + filled: false, + pristine: true, + user_arg_count: 0, + }; + ebb + } + + /// After the call to this function, new instructions will be inserted into the designated + /// block, in the order they are declared. You must declare the types of the Ebb arguments + /// you will use here. + /// + /// When inserting the terminator instruction (which doesn't have a falltrough to its immediate + /// successor), the block will be declared filled and it will not be possible to append + /// instructions to it. + pub fn switch_to_block(&mut self, ebb: Ebb, jump_args: &[Type]) -> &[Value] { + if self.pristine { + self.fill_function_args_values(ebb); + } + if !self.builder.ebbs[self.position.ebb].pristine { + // First we check that the previous block has been filled. + debug_assert!(self.is_unreachable() || self.builder.ebbs[self.position.ebb].filled, + "you have to fill your block before switching"); + } + // We cannot switch to a filled block + debug_assert!(!self.builder.ebbs[ebb].filled, + "you cannot switch to a block which is already filled"); + + let basic_block = self.builder.ssa.header_block(ebb); + // Then we change the cursor position. + self.position = Position { + ebb: ebb, + basic_block: basic_block, + }; + self.ebb_args_adjustement(ebb, jump_args); + self.func.dfg.ebb_args(ebb) + } + + /// Declares that all the predecessors of this block are known. + /// + /// Function to call with `ebb` as soon as the last branch instruction to `ebb` has been + /// created. Forgetting to call this method on every block will cause inconsistences in the + /// produced functions. + pub fn seal_block(&mut self, ebb: Ebb) { + let side_effects = self.builder + .ssa + .seal_ebb_header_block(ebb, + &mut self.func.dfg, + &mut self.func.layout, + &mut self.func.jump_tables); + self.handle_ssa_side_effects(side_effects); + } + + /// In order to use a variable in a `use_var`, you need to declare its type with this method. + pub fn declare_var(&mut self, var: Variable, ty: Type) { + *self.builder.types.ensure(var) = ty; + } + + /// Returns the Cretonne IL value corresponding to the utilization at the current program + /// position of a previously defined user variable. + pub fn use_var(&mut self, var: Variable) -> Value { + let ty = *self.builder + .types + .get(var) + .expect("this variable is used but its type has not been declared"); + let (val, side_effects) = self.builder + .ssa + .use_var(&mut self.func.dfg, + &mut self.func.layout, + &mut self.func.jump_tables, + var, + ty, + self.position.basic_block); + self.handle_ssa_side_effects(side_effects); + val + } + + /// 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. + pub fn def_var(&mut self, var: Variable, val: Value) { + debug_assert!(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(var, val, self.position.basic_block); + } + + /// Returns the value corresponding to the `i`-th argument 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 arg_value(&self, i: usize) -> Value { + debug_assert!(!self.pristine, "you have to call switch_to_block first."); + self.builder.function_args_values[i] + } + + /// Creates a jump table in the function, to be used by `br_table` instructions. + pub fn create_jump_table(&mut self) -> JumpTable { + self.func.jump_tables.push(JumpTableData::new()) + } + + /// Inserts an entry in a previously declared jump table. + pub fn insert_jump_table_entry(&mut self, jt: JumpTable, index: usize, ebb: Ebb) { + self.func.jump_tables[jt].set_entry(index, ebb); + } + + /// Creates a stack slot in the function, to be used by `stack_load`, `stack_store` and + /// `stack_addr` instructions. + pub fn create_stack_slot(&mut self, data: StackSlotData) -> StackSlot { + self.func.stack_slots.push(data) + } + + /// Adds a signature which can later be used to declare an external function import. + pub fn import_signature(&mut self, signature: Signature) -> SigRef { + self.func.dfg.signatures.push(signature) + } + + /// Declare an external function import. + pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef { + self.func.dfg.ext_funcs.push(data) + } + + /// Returns an object with the [`InstBuilder`](../cretonne/ir/builder/trait.InstBuilder.html) + /// trait that allows to conveniently append an instruction to the current `Ebb` being built. + pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a, Variable> { + let ebb = self.position.ebb; + FuncInstBuilder::new(self, ebb) + } +} + +/// All the functions documented in the previous block are write-only and help you build a valid +/// Cretonne IL functions via multiple debug asserts. However, you might need to improve the +/// performance of your translation perform more complex transformations to your Cretonne IL +/// function. The functions below help you inspect the function you're creating and modify it +/// in ways that can be unsafe if used incorrectly. +impl<'a, Variable> FunctionBuilder<'a, Variable> + where Variable: EntityRef + Hash + Default +{ + /// Retrieves all the arguments for an `Ebb` currently infered from the jump instructions + /// inserted that target it and the SSA construction. + pub fn ebb_args(&self, ebb: Ebb) -> &[Value] { + self.func.dfg.ebb_args(ebb) + } + + /// Retrieves the signature with reference `sigref` previously added with `import_signature`. + pub fn signature(&self, sigref: SigRef) -> Option<&Signature> { + self.func.dfg.signatures.get(sigref) + } + + /// Creates a argument for a specific `Ebb` by appending it to the list of already existing + /// arguments. + /// + /// **Note:** this function has to be called at the creation of the `Ebb` before adding + /// instructions to it, otherwise this could interfere with SSA construction. + pub fn append_ebb_arg(&mut self, ebb: Ebb, ty: Type) -> Value { + debug_assert!(self.builder.ebbs[ebb].pristine); + self.func.dfg.append_ebb_arg(ebb, ty) + } + + /// Returns the result values of an instruction. + pub fn inst_results(&self, inst: Inst) -> &[Value] { + self.func.dfg.inst_results(inst) + } + + /// Changes the destination of a jump instruction after creation. + /// + /// **Note:** You are responsible for maintaining the coherence with the arguments of + /// other jump instructions. + pub fn change_jump_destination(&mut self, inst: Inst, new_dest: Ebb) { + let old_dest = + self.func.dfg[inst] + .branch_destination_mut() + .expect("you want to change the jump destination of a non-jump instruction"); + let pred = self.builder.ssa.remove_ebb_predecessor(*old_dest, inst); + *old_dest = new_dest; + self.builder + .ssa + .declare_ebb_predecessor(new_dest, pred, inst); + } + + /// Returns `true` if and only if the current `Ebb` is sealed and has no predecessors declared. + /// + /// The entry block of a function is never unreachable. + pub fn is_unreachable(&self) -> bool { + let is_entry = match self.func.layout.entry_block() { + None => false, + Some(entry) => self.position.ebb == entry, + }; + (!is_entry && self.builder.ssa.is_sealed(self.position.ebb) && + self.builder.ssa.predecessors(self.position.ebb).is_empty()) + } + + /// Returns `true` if and only if no instructions have been added since the last call to + /// `switch_to_block`. + pub fn is_pristine(&self) -> bool { + self.builder.ebbs[self.position.ebb].pristine + } + + /// Returns `true` if and only if a terminator instruction has been inserted since the + /// last call to `switch_to_block`. + pub fn is_filled(&self) -> bool { + self.builder.ebbs[self.position.ebb].filled + } + + /// Returns a displayable object for the function as it is. + /// + /// Useful for debug purposes. Use it with `None` for standard printing. + pub fn display<'b, I: Into>>(&'b self, isa: I) -> DisplayFunction { + self.func.display(isa) + } +} + +impl<'a, Variable> Drop for FunctionBuilder<'a, Variable> + where Variable: EntityRef + Hash + Default +{ + /// When a `FunctionBuilder` goes out of scope, it means that the function is fully built. + /// We then proceed to check if all the `Ebb`s are filled and sealed + fn drop(&mut self) { + debug_assert!(self.builder + .ebbs + .keys() + .all(|ebb| { + self.builder.ebbs[ebb].pristine || + (self.builder.ssa.is_sealed(ebb) && + self.builder.ebbs[ebb].filled) + }), + "all blocks should be filled and sealed before dropping a FunctionBuilder") + } +} + +// Helper functions +impl<'a, Variable> FunctionBuilder<'a, Variable> + where Variable: EntityRef + Hash + Default +{ + fn move_to_next_basic_block(&mut self) { + self.position.basic_block = self.builder + .ssa + .declare_ebb_body_block(self.position.basic_block); + } + + fn fill_current_block(&mut self) { + self.builder.ebbs[self.position.ebb].filled = true; + } + + fn declare_successor(&mut self, dest_ebb: Ebb, jump_inst: Inst) { + self.builder + .ssa + .declare_ebb_predecessor(dest_ebb, self.position.basic_block, jump_inst); + } + + fn check_return_args(&self, args: &[Value]) { + debug_assert_eq!(args.len(), + self.func.signature.return_types.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.return_types[i].value_type, + "the types of the values returned don't match the \ + function signature"); + } + } + + fn fill_function_args_values(&mut self, ebb: Ebb) { + debug_assert!(self.pristine); + for argtyp in self.func.signature.argument_types.iter() { + self.builder + .function_args_values + .push(self.func.dfg.append_ebb_arg(ebb, argtyp.value_type)); + } + self.pristine = false; + } + + + fn ebb_args_adjustement(&mut self, dest_ebb: Ebb, jump_args: &[Type]) { + let ty_to_append: Option> = + if self.builder.ssa.predecessors(dest_ebb).len() == 0 || + 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_args = self.func.dfg.ebb_args(dest_ebb); + debug_assert!(dest_ebb_args + .iter() + .zip(jump_args.iter().take(dest_ebb_args.len())) + .all(|(dest_arg, jump_arg)| { + *jump_arg == self.func.dfg.value_type(*dest_arg) + }), + "the jump argument supplied has not the \ + same type as the corresponding dest ebb argument"); + self.builder.ebbs[dest_ebb].user_arg_count = jump_args.len(); + Some(jump_args + .iter() + .skip(dest_ebb_args.len()) + .cloned() + .collect()) + } else { + let dest_ebb_args = self.func.dfg.ebb_args(dest_ebb); + // The Ebb already has predecessors + // We check that the arguments supplied match those supplied + // previously. + debug_assert!(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 \ + ({} vs {}).", + jump_args.len(), + dest_ebb_args.len()); + debug_assert!(jump_args + .iter() + .zip(dest_ebb_args + .iter() + .take(self.builder.ebbs[dest_ebb].user_arg_count) + ) + .all(|(jump_arg, dest_arg)| { + *jump_arg == self.func.dfg.value_type(*dest_arg) + }), + "the jump argument supplied has not the \ + same type as the corresponding dest ebb argument"); + None + }; + if let Some(ty_args) = ty_to_append { + for ty in ty_args { + self.func.dfg.append_ebb_arg(dest_ebb, ty); + } + } + } + + fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) { + for split_ebb in side_effects.split_ebbs_created { + self.builder.ebbs.ensure(split_ebb).filled = true + } + for modified_ebb in side_effects.instructions_added_to_ebbs { + self.builder.ebbs[modified_ebb].pristine = false + } + } +} + +#[cfg(test)] +mod tests { + + use cretonne::entity_ref::EntityRef; + use cretonne::ir::{FunctionName, Function, Signature, ArgumentType, InstBuilder}; + use cretonne::ir::types::*; + use frontend::{ILBuilder, FunctionBuilder}; + use cretonne::verifier::verify_function; + + use std::u32; + + // An opaque reference to variable. + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct Variable(u32); + impl EntityRef for Variable { + fn new(index: usize) -> Self { + assert!(index < (u32::MAX as usize)); + Variable(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } + } + impl Default for Variable { + fn default() -> Variable { + Variable(u32::MAX) + } + } + + #[test] + fn sample_function() { + let mut sig = Signature::new(); + sig.return_types.push(ArgumentType::new(I32)); + sig.argument_types.push(ArgumentType::new(I32)); + + let mut il_builder = ILBuilder::::new(); + let mut func = Function::with_name_signature(FunctionName::new("sample_function"), sig); + { + let mut builder = FunctionBuilder::::new(&mut func, &mut il_builder); + + let block0 = builder.create_ebb(); + let block1 = builder.create_ebb(); + let block2 = builder.create_ebb(); + let x = Variable(0); + let y = Variable(1); + let z = Variable(2); + builder.declare_var(x, I32); + builder.declare_var(y, I32); + builder.declare_var(z, I32); + + builder.switch_to_block(block0, &[]); + builder.seal_block(block0); + { + let tmp = builder.arg_value(0); + builder.def_var(x, tmp); + } + { + let tmp = builder.ins().iconst(I32, 2); + builder.def_var(y, tmp); + } + { + let arg1 = builder.use_var(x); + let arg2 = builder.use_var(y); + let tmp = builder.ins().iadd(arg1, arg2); + builder.def_var(z, tmp); + } + builder.ins().jump(block1, &[]); + + builder.switch_to_block(block1, &[]); + { + let arg1 = builder.use_var(y); + let arg2 = builder.use_var(z); + let tmp = builder.ins().iadd(arg1, arg2); + builder.def_var(z, tmp); + } + { + let arg = builder.use_var(y); + builder.ins().brnz(arg, block2, &[]); + } + { + let arg1 = builder.use_var(z); + let arg2 = builder.use_var(x); + let tmp = builder.ins().isub(arg1, arg2); + builder.def_var(z, tmp); + } + { + let arg = builder.use_var(y); + builder.ins().return_(&[arg]); + } + + builder.switch_to_block(block2, &[]); + builder.seal_block(block2); + + { + let arg1 = builder.use_var(y); + let arg2 = builder.use_var(x); + let tmp = builder.ins().isub(arg1, arg2); + builder.def_var(y, tmp); + } + builder.ins().jump(block1, &[]); + builder.seal_block(block1); + } + + let res = verify_function(&func, None); + // println!("{}", func.display(None)); + match res { + Ok(_) => {} + Err(err) => panic!("{}{}", func.display(None), err), + } + } +} diff --git a/lib/frontend/src/lib.rs b/lib/frontend/src/lib.rs new file mode 100644 index 0000000000..2cbbc30459 --- /dev/null +++ b/lib/frontend/src/lib.rs @@ -0,0 +1,152 @@ +//! Cretonne IL builder library. +//! +//! Provides a straightforward way to create a Cretonne IL function and fill it with instructions +//! translated from another language. Contains a SSA construction module that lets you translate +//! your non-SSA variables into SSA Cretonne IL values via `use_var` and `def_var` calls. +//! +//! To get started, create an [`IlBuilder`](struct.ILBuilder.html) and pass it as an argument +//! to a [`FunctionBuilder`](struct.FunctionBuilder.html). +//! +//! # Example +//! +//! Here is a pseudo-program we want to transform into Cretonne IL: +//! +//! ```cton +//! function(x) { +//! x, y, z : i32 +//! block0: +//! y = 2; +//! z = x + y; +//! jump block1 +//! block1: +//! z = z + y; +//! brnz y, block2; +//! z = z - x; +//! return y +//! block2: +//! y = y - x +//! jump block1 +//! } +//! ``` +//! +//! Here is how you build the corresponding Cretonne IL function using `ILBuilder`: +//! +//! ```rust +//! extern crate cretonne; +//! extern crate cton_frontend; +//! +//! use cretonne::entity_ref::EntityRef; +//! use cretonne::ir::{FunctionName, Function, Signature, ArgumentType, InstBuilder}; +//! use cretonne::ir::types::*; +//! use cton_frontend::{ILBuilder, FunctionBuilder}; +//! use cretonne::verifier::verify_function; +//! use std::u32; +//! +//! // An opaque reference to variable. +//! #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +//! pub struct Variable(u32); +//! impl EntityRef for Variable { +//! fn new(index: usize) -> Self { +//! assert!(index < (u32::MAX as usize)); +//! Variable(index as u32) +//! } +//! +//! fn index(self) -> usize { +//! self.0 as usize +//! } +//! } +//! impl Default for Variable { +//! fn default() -> Variable { +//! Variable(u32::MAX) +//! } +//! } +//! +//! fn main() { +//! let mut sig = Signature::new(); +//! sig.return_types.push(ArgumentType::new(I32)); +//! sig.argument_types.push(ArgumentType::new(I32)); +//! let mut il_builder = ILBuilder::::new(); +//! let mut func = Function::with_name_signature(FunctionName::new("sample_function"), sig); +//! { +//! let mut builder = FunctionBuilder::::new(&mut func, &mut il_builder); +//! +//! let block0 = builder.create_ebb(); +//! let block1 = builder.create_ebb(); +//! let block2 = builder.create_ebb(); +//! let x = Variable(0); +//! let y = Variable(1); +//! let z = Variable(2); +//! builder.declare_var(x, I32); +//! builder.declare_var(y, I32); +//! builder.declare_var(z, I32); +//! +//! builder.switch_to_block(block0, &[]); +//! builder.seal_block(block0); +//! { +//! let tmp = builder.arg_value(0); +//! builder.def_var(x, tmp); +//! } +//! { +//! let tmp = builder.ins().iconst(I32, 2); +//! builder.def_var(y, tmp); +//! } +//! { +//! let arg1 = builder.use_var(x); +//! let arg2 = builder.use_var(y); +//! let tmp = builder.ins().iadd(arg1, arg2); +//! builder.def_var(z, tmp); +//! } +//! builder.ins().jump(block1, &[]); +//! +//! builder.switch_to_block(block1, &[]); +//! { +//! let arg1 = builder.use_var(y); +//! let arg2 = builder.use_var(z); +//! let tmp = builder.ins().iadd(arg1, arg2); +//! builder.def_var(z, tmp); +//! } +//! { +//! let arg = builder.use_var(y); +//! builder.ins().brnz(arg, block2, &[]); +//! } +//! { +//! let arg1 = builder.use_var(z); +//! let arg2 = builder.use_var(x); +//! let tmp = builder.ins().isub(arg1, arg2); +//! builder.def_var(z, tmp); +//! } +//! { +//! let arg = builder.use_var(y); +//! builder.ins().return_(&[arg]); +//! } +//! +//! builder.switch_to_block(block2, &[]); +//! builder.seal_block(block2); +//! +//! { +//! let arg1 = builder.use_var(y); +//! let arg2 = builder.use_var(x); +//! let tmp = builder.ins().isub(arg1, arg2); +//! builder.def_var(y, tmp); +//! } +//! builder.ins().jump(block1, &[]); +//! builder.seal_block(block1); +//! } +//! +//! let res = verify_function(&func, None); +//! println!("{}", func.display(None)); +//! match res { +//! Ok(_) => {} +//! Err(err) => panic!("{}", err), +//! } +//! } +//! ``` + +#![deny(missing_docs)] + +extern crate cretonne; + +pub use frontend::{ILBuilder, FunctionBuilder}; + +mod frontend; +mod ssa; diff --git a/lib/frontend/src/ssa.rs b/lib/frontend/src/ssa.rs new file mode 100644 index 0000000000..46b44d5251 --- /dev/null +++ b/lib/frontend/src/ssa.rs @@ -0,0 +1,1276 @@ +//! A SSA-building API that handles incomplete CFGs. +//! +//! The algorithm is based upon Braun M., Buchwald S., Hack S., Leißa R., Mallon C., +//! Zwinkau A. (2013) Simple and Efficient Construction of Static Single Assignment Form. +//! In: Jhala R., De Bosschere K. (eds) Compiler Construction. CC 2013. +//! Lecture Notes in Computer Science, vol 7791. Springer, Berlin, Heidelberg + +use cretonne::ir::{Ebb, Value, Inst, Type, DataFlowGraph, JumpTables, Layout, Cursor, InstBuilder}; +use cretonne::ir::instructions::BranchInfo; +use std::hash::Hash; +use cretonne::entity_map::{EntityMap, PrimaryEntityData}; +use cretonne::entity_ref::EntityRef; +use cretonne::packed_option::PackedOption; +use cretonne::packed_option::ReservedValue; +use std::u32; +use cretonne::ir::types::{F32, F64}; +use cretonne::ir::immediates::{Ieee32, Ieee64}; +use std::collections::HashMap; + +/// Structure containing the data relevant the construction of SSA for a given function. +/// +/// The parameter struct `Variable` corresponds to the way variables are represented in the +/// non-SSA language you're translating from. +/// +/// The SSA building relies on information about the variables used and defined, as well as +/// their position relative to basic blocks which are stricter than extended basic blocks since +/// they don't allow branching in the middle of them. +/// +/// This SSA building module allows you to def and use variables on the fly while you are +/// constructing the CFG, no need for a separate SSA pass after the CFG is completed. +/// +/// A basic block is said _filled_ if all the instruction that it contains have been translated, +/// and it is said _sealed_ if all of its predecessors have been declared. Only filled predecessors +/// can be declared. +pub struct SSABuilder + where Variable: EntityRef + Default +{ + // Records for every variable and for every revelant block, the last definition of + // the variable in the block. + variables: EntityMap>, + // Records the position of the basic blocks and the list of values used but not defined in the + // block. + blocks: EntityMap>, + // Records the basic blocks at the beginning of the `Ebb`s. + ebb_headers: EntityMap>, +} + +/// Side effects of a `use_var` or a `seal_ebb_header_block` method call. +pub struct SideEffects { + /// When we want to append jump arguments to a `br_table` instruction, the critical edge is + /// splitted and the newly created `Ebb`s are signaled here. + pub split_ebbs_created: Vec, + /// When a variable is used but has never been defined before (this happens in the case of + /// unreachable code), a placeholder `iconst` or `fconst` value is added to the right `Ebb`. + /// This field signals if it is the case and return the `Ebb` to which the initialization has + /// been added. + pub instructions_added_to_ebbs: Vec, +} + +// Describes the current position of a basic block in the control flow graph. +enum BlockData { + // A block at the top of an `Ebb`. + EbbHeader(EbbHeaderBlockData), + // A block inside an `Ebb` with an unique other block as its predecessor. + // The block is implicitely sealed at creation. + EbbBody { predecessor: Block }, +} +impl PrimaryEntityData for BlockData {} + +impl BlockData { + fn add_predecessor(&mut self, pred: Block, inst: Inst) { + match self { + &mut BlockData::EbbBody { .. } => panic!("you can't add a predecessor to a body block"), + &mut BlockData::EbbHeader(ref mut data) => { + data.predecessors.insert(pred, inst); + () + } + } + } + fn remove_predecessor(&mut self, inst: Inst) -> Block { + match self { + &mut BlockData::EbbBody { .. } => panic!("should not happen"), + &mut BlockData::EbbHeader(ref mut data) => { + // This a linear complexity operation but the number of predecessors is low + // in all non-pathological cases + let pred: Block = match data.predecessors + .iter() + .find(|&(_, &jump_inst)| jump_inst == inst) { + None => panic!("the predecessor you are trying to remove is not declared"), + Some((&b, _)) => b.clone(), + }; + data.predecessors.remove(&pred); + pred + } + } + } +} + +struct EbbHeaderBlockData { + // The predecessors of the Ebb header block, with the block and branch instruction. + predecessors: HashMap, + // A ebb header block is sealed if all of its predecessors have been declared. + sealed: bool, + // The ebb which this block is part of. + ebb: Ebb, + // List of current Ebb arguments for which a earlier def has not been found yet. + undef_variables: Vec<(Variable, Value)>, +} + +/// A opaque reference to a basic block. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Block(u32); +impl EntityRef for Block { + fn new(index: usize) -> Self { + assert!(index < (u32::MAX as usize)); + Block(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } +} + +impl ReservedValue for Block { + fn reserved_value() -> Block { + Block(u32::MAX) + } +} + +impl SSABuilder + where Variable: EntityRef + Default +{ + /// Allocate a new blank SSA builder struct. Use the API function to interact with the struct. + pub fn new() -> SSABuilder { + SSABuilder { + variables: EntityMap::new(), + blocks: EntityMap::new(), + ebb_headers: EntityMap::new(), + } + } + + /// Clears a `SSABuilder` from all its data, letting it in a pristine state without + /// deallocating memory. + pub fn clear(&mut self) { + self.variables.clear(); + self.blocks.clear(); + self.ebb_headers.clear(); + } +} + +// Small enum used for clarity in some functions. +#[derive(Debug)] +enum ZeroOneOrMore { + Zero(), + One(T), + More(), +} + +/// TODO: use entity list instead of vec +#[derive(Debug)] +enum UseVarCases { + Unsealed(Value), + SealedOnePredecessor(Block), + SealedMultiplePredecessors(Vec<(Block, Inst)>, Value, Ebb), +} + +/// The following methods are the API of the SSA builder. Here is how it should be used when +/// translating to Cretonne IL: +/// +/// - for each sequence of contiguous instructions (with no branches), create a corresponding +/// basic block with `declare_ebb_body_block` or `declare_ebb_header_block` depending on the +/// position of the basic block; +/// +/// - while traversing a basic block and translating instruction, use `def_var` and `use_var` +/// to record definitions and uses of variables, these methods will give you the corresponding +/// SSA values; +/// +/// - when all the instructions in a basic block have translated, the block is said _filled_ and +/// only then you can add it as a predecessor to other blocks with `declare_ebb_predecessor`; +/// +/// - when you have constructed all the predecessor to a basic block at the beginning of an `Ebb`, +/// call `seal_ebb_header_block` on it with the `Function` that you are building. +/// +/// This API will give you the correct SSA values to use as arguments of your instructions, +/// as well as modify the jump instruction and `Ebb` headers arguments to account for the SSA +/// Phi functions. +/// +impl SSABuilder + where Variable: EntityRef + Hash + Default +{ + /// Declares a new definition of a variable in a given basic block. + /// The SSA value is passed as an argument because it should be created with + /// `ir::DataFlowGraph::append_result`. + pub fn def_var(&mut self, var: Variable, val: Value, block: Block) { + self.variables.ensure(var).insert(block, val); + } + + /// Declares a use of a variable in a given basic block. Returns the SSA value corresponding + /// to the current SSA definition of this variable and a list of newly created Ebbs that + /// are the results of critical edge splitting for `br_table` with arguments. + /// + /// If the variable has never been defined in this blocks or recursively in its predecessors, + /// this method will silently create an initializer with `iconst` or `fconst`. You are + /// responsible for making sure that you initialize your variables. + pub fn use_var(&mut self, + dfg: &mut DataFlowGraph, + layout: &mut Layout, + jts: &mut JumpTables, + var: Variable, + ty: Type, + block: Block) + -> (Value, SideEffects) { + // First we lookup for the current definition of the variable in this block + if let Some(var_defs) = self.variables.get(var) { + if let Some(val) = var_defs.get(&block) { + return (*val, + SideEffects { + split_ebbs_created: Vec::new(), + instructions_added_to_ebbs: Vec::new(), + }); + } + }; + // At this point if we haven't returned it means that we have to search in the + // predecessors. + let case = match self.blocks[block] { + BlockData::EbbHeader(ref mut data) => { + // The block has multiple predecessors so we append an Ebb argument that + // will serve as a value. + if data.sealed { + if data.predecessors.len() == 1 { + // Only one predecessor, straightforward case + UseVarCases::SealedOnePredecessor(*data.predecessors.keys().next().unwrap()) + } else { + let val = dfg.append_ebb_arg(data.ebb, ty); + let preds = data.predecessors + .iter() + .map(|(&pred, &inst)| (pred, inst)) + .collect(); + UseVarCases::SealedMultiplePredecessors(preds, val, data.ebb) + } + } else { + let val = dfg.append_ebb_arg(data.ebb, ty); + data.undef_variables.push((var, val)); + UseVarCases::Unsealed(val) + } + } + BlockData::EbbBody { predecessor: pred, .. } => UseVarCases::SealedOnePredecessor(pred), + }; + // TODO: avoid recursion for the calls to use_var and predecessors_lookup. + match case { + // The block has a single predecessor or multiple predecessor with + // the same value, we look into it. + UseVarCases::SealedOnePredecessor(pred) => { + let (val, mids) = self.use_var(dfg, layout, jts, var, ty, pred); + self.def_var(var, val, block); + return (val, mids); + } + // The block has multiple predecessors, we register the ebb argument as the current + // definition for the variable. + UseVarCases::Unsealed(val) => { + self.def_var(var, val, block); + return (val, + SideEffects { + split_ebbs_created: Vec::new(), + instructions_added_to_ebbs: Vec::new(), + }); + } + UseVarCases::SealedMultiplePredecessors(preds, val, ebb) => { + // If multiple predecessor we look up a use_var in each of them: + // if they all yield the same value no need for an Ebb argument + self.def_var(var, val, block); + return self.predecessors_lookup(dfg, layout, jts, val, var, ebb, &preds); + } + }; + + } + + /// Declares a new basic block belonging to the body of a certain `Ebb` and having `pred` + /// as a predecessor. `pred` is the only predecessor of the block and the block is sealed + /// at creation. + /// + /// To declare a `Ebb` header block, see `declare_ebb_header_block`. + pub fn declare_ebb_body_block(&mut self, pred: Block) -> Block { + self.blocks.push(BlockData::EbbBody { predecessor: pred }) + } + + /// Declares a new basic block at the beginning of an `Ebb`. No predecessors are declared + /// here and the block is not sealed. + /// Predecessors have to be added with `declare_ebb_predecessor`. + pub fn declare_ebb_header_block(&mut self, ebb: Ebb) -> Block { + let block = self.blocks + .push(BlockData::EbbHeader(EbbHeaderBlockData { + predecessors: HashMap::new(), + sealed: false, + ebb: ebb, + undef_variables: Vec::new(), + })); + *self.ebb_headers.ensure(ebb) = block.into(); + block + } + /// Gets the header block corresponding to an Ebb, panics if the Ebb or the header block + /// isn't declared. + pub fn header_block(&self, ebb: Ebb) -> Block { + match self.ebb_headers.get(ebb) { + Some(&header) => { + match header.expand() { + Some(header) => header, + None => panic!("the header block has not been defined"), + } + } + None => panic!("the ebb has not been declared"), + } + } + + /// Declares a new predecessor for an `Ebb` header block and record the branch instruction + /// of the predecessor that leads to it. + /// + /// Note that the predecessor is a `Block` and not an `Ebb`. This `Block` must be filled + /// before added as predecessor. Note that you must provide no jump arguments to the branch + /// instruction when you create it since `SSABuilder` will fill them for you. + pub fn declare_ebb_predecessor(&mut self, ebb: Ebb, pred: Block, inst: Inst) { + let header_block = match self.blocks[self.header_block(ebb)] { + BlockData::EbbBody { .. } => panic!("you can't add predecessors to an Ebb body block"), + BlockData::EbbHeader(ref data) => { + assert!(!data.sealed); + self.header_block(ebb) + } + }; + self.blocks[header_block].add_predecessor(pred, inst) + } + + /// Remove a previously declared Ebb predecessor by giving a reference to the jump + /// instruction. Returns the basic block containing the instruction. + /// + /// Note: use only when you know what you are doing, this might break the SSA bbuilding problem + pub fn remove_ebb_predecessor(&mut self, ebb: Ebb, inst: Inst) -> Block { + let header_block = match self.blocks[self.header_block(ebb)] { + BlockData::EbbBody { .. } => panic!("you can't add predecessors to an Ebb body block"), + BlockData::EbbHeader(ref data) => { + assert!(!data.sealed); + self.header_block(ebb) + } + }; + self.blocks[header_block].remove_predecessor(inst) + } + + /// Completes the global value numbering for an `Ebb`, all of its predecessors having been + /// already sealed. + /// + /// This method modifies the function's `Layout` by adding arguments to the `Ebb`s to + /// take into account the Phi function placed by the SSA algorithm. + pub fn seal_ebb_header_block(&mut self, + ebb: Ebb, + dfg: &mut DataFlowGraph, + layout: &mut Layout, + jts: &mut JumpTables) + -> SideEffects { + let block = self.header_block(ebb); + + // Sanity check + match self.blocks[block] { + BlockData::EbbBody { .. } => panic!("you can't seal an Ebb body block"), + BlockData::EbbHeader(ref data) => { + assert!(!data.sealed); + } + } + + // Recurse over the predecessors to find good definitions. + let side_effects = self.resolve_undef_vars(block, dfg, layout, jts); + + // Then we mark the block as sealed. + match self.blocks[block] { + BlockData::EbbBody { .. } => panic!("this should not happen"), + BlockData::EbbHeader(ref mut data) => data.sealed = true, + }; + side_effects + } + + // For each undef_var in an Ebb header block, lookup in the predecessors to append the right + // jump argument to the branch instruction. + // Panics if called with a non-header block. + // Returns the list of newly created ebbs for critical edge splitting. + fn resolve_undef_vars(&mut self, + block: Block, + dfg: &mut DataFlowGraph, + layout: &mut Layout, + jts: &mut JumpTables) + -> SideEffects { + // TODO: find a way to not allocate vectors + let (predecessors, undef_vars, ebb): (Vec<(Block, Inst)>, + Vec<(Variable, Value)>, + Ebb) = match self.blocks[block] { + BlockData::EbbBody { .. } => panic!("this should not happen"), + BlockData::EbbHeader(ref mut data) => { + (data.predecessors.iter().map(|(&x, &y)| (x, y)).collect(), + data.undef_variables.clone(), + data.ebb) + } + }; + + let mut side_effects = SideEffects { + split_ebbs_created: Vec::new(), + instructions_added_to_ebbs: Vec::new(), + }; + // For each undef var we look up values in the predecessors and create an Ebb argument + // only if necessary. + for &(var, val) in undef_vars.iter() { + let (_, mut local_side_effects) = + self.predecessors_lookup(dfg, layout, jts, val, var, ebb, &predecessors); + side_effects + .split_ebbs_created + .append(&mut local_side_effects.split_ebbs_created); + side_effects + .instructions_added_to_ebbs + .append(&mut local_side_effects.instructions_added_to_ebbs); + } + + // Then we clear the undef_vars and mark the block as sealed. + match self.blocks[block] { + BlockData::EbbBody { .. } => panic!("this should not happen"), + BlockData::EbbHeader(ref mut data) => { + data.undef_variables.clear(); + } + }; + side_effects + } + + /// Look up in the predecessors of an Ebb the def for a value an decides wether or not + /// to keep the eeb arg, and act accordingly. Returns the chosen value and optionnaly a + /// list of Ebb that are the middle of newly created critical edges splits. + fn predecessors_lookup(&mut self, + dfg: &mut DataFlowGraph, + layout: &mut Layout, + jts: &mut JumpTables, + temp_arg_val: Value, + temp_arg_var: Variable, + dest_ebb: Ebb, + preds: &Vec<(Block, Inst)>) + -> (Value, SideEffects) { + let mut pred_values: ZeroOneOrMore = ZeroOneOrMore::Zero(); + // TODO: find a way not not allocate a vector + let mut jump_args_to_append: Vec<(Block, Inst, Value)> = Vec::new(); + let ty = dfg.value_type(temp_arg_val); + let mut side_effects = SideEffects { + split_ebbs_created: Vec::new(), + instructions_added_to_ebbs: Vec::new(), + }; + for &(pred, last_inst) in preds.iter() { + // For undef value and each predecessor we query what is the local SSA value + // corresponding to var and we put it as an argument of the branch instruction. + let (pred_val, mut local_side_effects) = + self.use_var(dfg, layout, jts, temp_arg_var, ty, pred); + pred_values = match pred_values { + ZeroOneOrMore::Zero() => { + if pred_val == temp_arg_val { + ZeroOneOrMore::Zero() + } else { + ZeroOneOrMore::One(pred_val) + } + } + ZeroOneOrMore::One(old_val) => { + if pred_val == temp_arg_val || pred_val == old_val { + ZeroOneOrMore::One(old_val) + } else { + ZeroOneOrMore::More() + } + } + ZeroOneOrMore::More() => ZeroOneOrMore::More(), + }; + jump_args_to_append.push((pred, last_inst, pred_val)); + side_effects + .split_ebbs_created + .append(&mut local_side_effects.split_ebbs_created); + side_effects + .instructions_added_to_ebbs + .append(&mut local_side_effects.instructions_added_to_ebbs); + } + match pred_values { + ZeroOneOrMore::Zero() => { + // The variable is used but never defined before. This is an irregularity in the + // code, but rather than throwing an error we silently initialize the variable to + // 0. This will have no effect since this situation happens in unreachable code. + if !layout.is_ebb_inserted(dest_ebb) { + layout.append_ebb(dest_ebb) + }; + let mut cur = Cursor::new(layout); + cur.goto_top(dest_ebb); + cur.next_inst(); + let ty = dfg.value_type(temp_arg_val); + let val = if ty.is_int() { + dfg.ins(&mut cur).iconst(ty, 0) + } else if ty == F32 { + dfg.ins(&mut cur).f32const(Ieee32::new(0.0)) + } else if ty == F64 { + dfg.ins(&mut cur).f64const(Ieee64::new(0.0)) + } else { + panic!("value used but never declared and initialization not supported") + }; + side_effects.instructions_added_to_ebbs.push(dest_ebb); + (val, side_effects) + } + ZeroOneOrMore::One(pred_val) => { + // Here all the predecessors use a single value to represent our variable + // so we don't need to have it as an ebb argument. + // We need to replace all the occurences of val with pred_val but since + // we can't afford a re-writing pass right now we just declare an alias. + dfg.remove_ebb_arg(temp_arg_val); + dfg.change_to_alias(temp_arg_val, pred_val); + (pred_val, side_effects) + } + ZeroOneOrMore::More() => { + // There is disagreement in the predecessors on which value to use so we have + // to keep the ebb argument. + for (pred_block, last_inst, pred_val) in jump_args_to_append { + match self.append_jump_argument(dfg, + layout, + last_inst, + pred_block, + dest_ebb, + pred_val, + temp_arg_var, + jts) { + None => (), + Some(middle_ebb) => side_effects.split_ebbs_created.push(middle_ebb), + }; + } + (temp_arg_val, side_effects) + } + } + } + + /// Appends a jump argument to a jump instruction, returns ebb created in case of + /// critical edge splitting. + fn append_jump_argument(&mut self, + dfg: &mut DataFlowGraph, + layout: &mut Layout, + jump_inst: Inst, + jump_inst_block: Block, + dest_ebb: Ebb, + val: Value, + var: Variable, + jts: &mut JumpTables) + -> Option { + match dfg[jump_inst].analyze_branch(&dfg.value_lists) { + BranchInfo::NotABranch => { + panic!("you have declared a non-branch instruction as a predecessor to an ebb"); + } + // For a single destination appending a jump argument to the instruction + // is sufficient. + BranchInfo::SingleDest(_, _) => { + dfg.append_inst_arg(jump_inst, val); + None + } + BranchInfo::Table(jt) => { + // In the case of a jump table, the situation is tricky because br_table doesn't + // support arguments. + // We have to split the critical edge + let indexes: Vec = jts[jt] + .entries() + .fold(Vec::new(), |mut acc, (index, dest)| if dest == dest_ebb { + acc.push(index); + acc + } else { + acc + }); + let middle_ebb = dfg.make_ebb(); + layout.append_ebb(middle_ebb); + let block = self.declare_ebb_header_block(middle_ebb); + self.blocks[block].add_predecessor(jump_inst_block, jump_inst); + self.seal_ebb_header_block(middle_ebb, dfg, layout, jts); + for index in indexes { + jts[jt].set_entry(index, middle_ebb) + } + let mut cur = Cursor::new(layout); + cur.goto_bottom(middle_ebb); + let middle_jump_inst = dfg.ins(&mut cur).jump(dest_ebb, &[val]); + let dest_header_block = self.header_block(dest_ebb); + self.blocks[dest_header_block].add_predecessor(block, middle_jump_inst); + self.blocks[dest_header_block].remove_predecessor(jump_inst); + self.def_var(var, val, block); + Some(middle_ebb) + } + } + } + + /// Returns the list of `Ebb`s that have been declared as predecessors of the argument. + pub fn predecessors(&self, ebb: Ebb) -> &HashMap { + let block = match self.ebb_headers[ebb].expand() { + Some(block) => block, + None => panic!("the ebb has not been declared yet"), + }; + match self.blocks[block] { + BlockData::EbbBody { .. } => panic!("should not happen"), + BlockData::EbbHeader(ref data) => &data.predecessors, + } + } + + /// Returns `true` if and only if `seal_ebb_header_block` has been called on the argument. + pub fn is_sealed(&self, ebb: Ebb) -> bool { + match self.blocks[self.header_block(ebb)] { + BlockData::EbbBody { .. } => panic!("should not happen"), + BlockData::EbbHeader(ref data) => data.sealed, + } + } +} + +#[cfg(test)] +mod tests { + use cretonne::entity_ref::EntityRef; + use cretonne::ir::{Function, InstBuilder, Cursor, Inst, JumpTableData}; + use cretonne::ir::types::*; + use cretonne::verify_function; + use cretonne::ir::instructions::BranchInfo; + use ssa::SSABuilder; + use std::u32; + + /// An opaque reference to variable. + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct Variable(u32); + impl EntityRef for Variable { + fn new(index: usize) -> Self { + assert!(index < (u32::MAX as usize)); + Variable(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } + } + impl Default for Variable { + fn default() -> Variable { + Variable(u32::MAX) + } + } + + #[test] + fn simple_block() { + let mut func = Function::new(); + let mut ssa: SSABuilder = SSABuilder::new(); + let ebb0 = func.dfg.make_ebb(); + // Here is the pseudo-program we want to translate: + // x = 1; + // y = 2; + // z = x + y; + // z = x + z; + + let block = ssa.declare_ebb_header_block(ebb0); + let x_var = Variable(0); + let x_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.insert_ebb(ebb0); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 1) + }; + ssa.def_var(x_var, x_ssa, block); + let y_var = Variable(1); + let y_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 2) + }; + ssa.def_var(y_var, y_ssa, block); + + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block) + .0, + x_ssa); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block) + .0, + y_ssa); + let z_var = Variable(2); + let x_use1 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block) + .0; + let y_use1 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block) + .0; + let z1_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iadd(x_use1, y_use1) + }; + ssa.def_var(z_var, z1_ssa, block); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block) + .0, + z1_ssa); + let x_use2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block) + .0; + let z_use1 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block) + .0; + let z2_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iadd(x_use2, z_use1) + }; + ssa.def_var(z_var, z2_ssa, block); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block) + .0, + z2_ssa); + } + + #[test] + fn sequence_of_blocks() { + let mut func = Function::new(); + let mut ssa: SSABuilder = SSABuilder::new(); + let ebb0 = func.dfg.make_ebb(); + let ebb1 = func.dfg.make_ebb(); + // Here is the pseudo-program we want to translate: + // ebb0: + // x = 1; + // y = 2; + // z = x + y; + // brnz y, ebb1; + // z = x + z; + // ebb1: + // y = x + y; + + let block0 = ssa.declare_ebb_header_block(ebb0); + let x_var = Variable(0); + let x_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.insert_ebb(ebb0); + cur.insert_ebb(ebb1); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 1) + }; + ssa.def_var(x_var, x_ssa, block0); + let y_var = Variable(1); + let y_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 2) + }; + ssa.def_var(y_var, y_ssa, block0); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block0) + .0, + x_ssa); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block0) + .0, + y_ssa); + let z_var = Variable(2); + let x_use1 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block0) + .0; + let y_use1 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block0) + .0; + let z1_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iadd(x_use1, y_use1) + }; + ssa.def_var(z_var, z1_ssa, block0); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block0) + .0, + z1_ssa); + let y_use2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block0) + .0; + let jump_inst: Inst = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).brnz(y_use2, ebb1, &[]) + }; + let block1 = ssa.declare_ebb_body_block(block0); + let x_use2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block1) + .0; + assert_eq!(x_use2, x_ssa); + let z_use1 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block1) + .0; + assert_eq!(z_use1, z1_ssa); + let z2_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iadd(x_use2, z_use1) + }; + ssa.def_var(z_var, z2_ssa, block1); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block1) + .0, + z2_ssa); + ssa.seal_ebb_header_block(ebb0, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let block2 = ssa.declare_ebb_header_block(ebb1); + ssa.declare_ebb_predecessor(ebb1, block0, jump_inst); + ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let x_use3 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block2) + .0; + assert_eq!(x_ssa, x_use3); + let y_use3 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block2) + .0; + assert_eq!(y_ssa, y_use3); + let y2_ssa = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).iadd(x_use3, y_use3) + }; + ssa.def_var(y_var, y2_ssa, block2); + match func.dfg[jump_inst].analyze_branch(&func.dfg.value_lists) { + BranchInfo::SingleDest(dest, jump_args) => { + assert_eq!(dest, ebb1); + assert_eq!(jump_args.len(), 0); + } + _ => assert!(false), + }; + } + + #[test] + fn program_with_loop() { + let mut func = Function::new(); + let mut ssa: SSABuilder = SSABuilder::new(); + let ebb0 = func.dfg.make_ebb(); + let ebb1 = func.dfg.make_ebb(); + let ebb2 = func.dfg.make_ebb(); + // Here is the pseudo-program we want to translate: + // ebb0: + // x = 1; + // y = 2; + // z = x + y; + // jump ebb1 + // ebb1: + // z = z + y; + // brnz y, ebb1; + // z = z - x; + // return y + // ebb2: + // y = y - x + // jump ebb1 + + let block0 = ssa.declare_ebb_header_block(ebb0); + ssa.seal_ebb_header_block(ebb0, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let x_var = Variable(0); + let x1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.insert_ebb(ebb0); + cur.insert_ebb(ebb1); + cur.insert_ebb(ebb2); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 1) + }; + ssa.def_var(x_var, x1, block0); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block0) + .0, + x1); + let y_var = Variable(1); + let y1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 2) + }; + ssa.def_var(y_var, y1, block0); + assert_eq!(ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block0) + .0, + y1); + let z_var = Variable(2); + let x2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block0) + .0; + assert_eq!(x2, x1); + let y2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block0) + .0; + assert_eq!(y2, y1); + let z1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iadd(x2, y2) + }; + ssa.def_var(z_var, z1, block0); + let jump_ebb0_ebb1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).jump(ebb1, &[]) + }; + let block1 = ssa.declare_ebb_header_block(ebb1); + ssa.declare_ebb_predecessor(ebb1, block0, jump_ebb0_ebb1); + let z2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block1) + .0; + let y3 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block1) + .0; + let z3 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).iadd(z2, y3) + }; + ssa.def_var(z_var, z3, block1); + let y4 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block1) + .0; + assert_eq!(y4, y3); + let jump_ebb1_ebb2 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).brnz(y4, ebb2, &[]) + }; + let block2 = ssa.declare_ebb_body_block(block1); + let z4 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block2) + .0; + assert_eq!(z4, z3); + let x3 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block2) + .0; + let z5 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).isub(z4, x3) + }; + ssa.def_var(z_var, z5, block2); + let y5 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block2) + .0; + assert_eq!(y5, y3); + { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).return_(&[y5]) + }; + + let block3 = ssa.declare_ebb_header_block(ebb2); + ssa.declare_ebb_predecessor(ebb2, block1, jump_ebb1_ebb2); + ssa.seal_ebb_header_block(ebb2, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let y6 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block3) + .0; + assert_eq!(y6, y3); + let x4 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block3) + .0; + assert_eq!(x4, x3); + let y7 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb2); + func.dfg.ins(cur).isub(y6, x4) + }; + ssa.def_var(y_var, y7, block3); + let jump_ebb2_ebb1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb2); + func.dfg.ins(cur).jump(ebb1, &[]) + }; + + ssa.declare_ebb_predecessor(ebb1, block3, jump_ebb2_ebb1); + ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + assert_eq!(func.dfg.ebb_args(ebb1)[0], z2); + assert_eq!(func.dfg.ebb_args(ebb1)[1], y3); + assert_eq!(func.dfg.resolve_aliases(x3), x1); + + } + + #[test] + fn br_table_with_args() { + // This tests the on-demand splitting of critical edges for br_table with jump arguments + let mut func = Function::new(); + let mut ssa: SSABuilder = SSABuilder::new(); + let ebb0 = func.dfg.make_ebb(); + let ebb1 = func.dfg.make_ebb(); + // Here is the pseudo-program we want to translate: + // ebb0: + // x = 0; + // br_table x ebb1 + // x = 1 + // jump ebb1 + // ebb1: + // x = x + 1 + // return + // + let block0 = ssa.declare_ebb_header_block(ebb0); + ssa.seal_ebb_header_block(ebb0, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let x_var = Variable(0); + let x1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.insert_ebb(ebb0); + cur.insert_ebb(ebb1); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 1) + }; + ssa.def_var(x_var, x1, block0); + let mut jt_data = JumpTableData::new(); + jt_data.set_entry(0, ebb1); + let jt = func.jump_tables.push(jt_data); + ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block0) + .0; + let br_table = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).br_table(x1, jt) + }; + let block1 = ssa.declare_ebb_body_block(block0); + let x3 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 2) + }; + ssa.def_var(x_var, x3, block1); + let jump_inst = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).jump(ebb1, &[]) + }; + let block2 = ssa.declare_ebb_header_block(ebb1); + ssa.declare_ebb_predecessor(ebb1, block1, jump_inst); + ssa.declare_ebb_predecessor(ebb1, block0, br_table); + ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let x4 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block2) + .0; + { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).iadd_imm(x4, 1) + }; + { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).return_(&[]) + }; + match verify_function(&func, None) { + Ok(()) => {} + Err(err) => panic!(err.message), + } + } + + #[test] + fn undef_values_reordering() { + let mut func = Function::new(); + let mut ssa: SSABuilder = SSABuilder::new(); + let ebb0 = func.dfg.make_ebb(); + let ebb1 = func.dfg.make_ebb(); + // Here is the pseudo-program we want to translate: + // ebb0: + // x = 0 + // y = 1 + // z = 2 + // jump ebb1 + // ebb1: + // x = z + x + // y = y - x + // jump ebb1 + // + let block0 = ssa.declare_ebb_header_block(ebb0); + let x_var = Variable(0); + let y_var = Variable(1); + let z_var = Variable(2); + ssa.seal_ebb_header_block(ebb0, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + let x1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.insert_ebb(ebb0); + cur.insert_ebb(ebb1); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 0) + }; + ssa.def_var(x_var, x1, block0); + let y1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 1) + }; + ssa.def_var(y_var, y1, block0); + let z1 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).iconst(I32, 2) + }; + ssa.def_var(z_var, z1, block0); + let jump_inst = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb0); + func.dfg.ins(cur).jump(ebb1, &[]) + }; + let block1 = ssa.declare_ebb_header_block(ebb1); + ssa.declare_ebb_predecessor(ebb1, block0, jump_inst); + let z2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + z_var, + I32, + block1) + .0; + assert_eq!(func.dfg.ebb_args(ebb1)[0], z2); + let x2 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block1) + .0; + assert_eq!(func.dfg.ebb_args(ebb1)[1], x2); + let x3 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).iadd(x2, z2) + }; + ssa.def_var(x_var, x3, block1); + let x4 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + x_var, + I32, + block1) + .0; + let y3 = ssa.use_var(&mut func.dfg, + &mut func.layout, + &mut func.jump_tables, + y_var, + I32, + block1) + .0; + assert_eq!(func.dfg.ebb_args(ebb1)[2], y3); + let y4 = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).isub(y3, x4) + }; + ssa.def_var(y_var, y4, block1); + let jump_inst = { + let cur = &mut Cursor::new(&mut func.layout); + cur.goto_bottom(ebb1); + func.dfg.ins(cur).jump(ebb1, &[]) + }; + ssa.declare_ebb_predecessor(ebb1, block1, jump_inst); + ssa.seal_ebb_header_block(ebb1, &mut func.dfg, &mut func.layout, &mut func.jump_tables); + // At sealing the "z" argument disappear but the remaining "x" and "y" args have to be + // in the right order. + assert_eq!(func.dfg.ebb_args(ebb1)[1], y3); + assert_eq!(func.dfg.ebb_args(ebb1)[0], x2); + } +}