diff --git a/cranelift/frontend/src/frontend.rs b/cranelift/frontend/src/frontend.rs index ab384fe484..6234649e9f 100644 --- a/cranelift/frontend/src/frontend.rs +++ b/cranelift/frontend/src/frontend.rs @@ -1,5 +1,5 @@ //! A frontend for building Cranelift IR from other languages. -use crate::ssa::{SSABlock, SSABuilder, SideEffects}; +use crate::ssa::{SSABuilder, SideEffects}; use crate::variable::Variable; use cranelift_codegen::cursor::{Cursor, FuncCursor}; use cranelift_codegen::entity::{EntitySet, SecondaryMap}; @@ -35,7 +35,7 @@ pub struct FunctionBuilder<'a> { srcloc: ir::SourceLoc, func_ctx: &'a mut FunctionBuilderContext, - position: Position, + position: PackedOption, } #[derive(Clone, Default)] @@ -54,25 +54,6 @@ struct BlockData { user_param_count: usize, } -#[derive(Default)] -struct Position { - block: PackedOption, - basic_block: PackedOption, -} - -impl Position { - fn at(block: Block, basic_block: SSABlock) -> Self { - Self { - block: PackedOption::from(block), - basic_block: PackedOption::from(basic_block), - } - } - - fn is_default(&self) -> bool { - self.block.is_none() && self.basic_block.is_none() - } -} - impl FunctionBuilderContext { /// Creates a FunctionBuilderContext structure. The structure is automatically cleared after /// each [`FunctionBuilder`](struct.FunctionBuilder.html) completes translating a function. @@ -158,25 +139,22 @@ impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> { .iter() .filter(|&dest_block| unique.insert(*dest_block)) { + // Call `declare_block_predecessor` instead of `declare_successor` for + // avoiding the borrow checker. self.builder.func_ctx.ssa.declare_block_predecessor( *dest_block, - self.builder.position.basic_block.unwrap(), + self.builder.position.unwrap(), inst, ); } - self.builder.func_ctx.ssa.declare_block_predecessor( - destination, - self.builder.position.basic_block.unwrap(), - inst, - ); + self.builder.declare_successor(destination, inst); } } } } + 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) } @@ -224,7 +202,7 @@ impl<'a> FunctionBuilder<'a> { func, srcloc: Default::default(), func_ctx, - position: Position::default(), + position: Default::default(), } } @@ -236,7 +214,7 @@ impl<'a> FunctionBuilder<'a> { /// Creates a new `Block` and returns its reference. pub fn create_block(&mut self) -> Block { let block = self.func.dfg.make_block(); - self.func_ctx.ssa.declare_block_header_block(block); + self.func_ctx.ssa.declare_block(block); self.func_ctx.blocks[block] = BlockData { filled: false, pristine: true, @@ -255,7 +233,7 @@ impl<'a> FunctionBuilder<'a> { pub fn switch_to_block(&mut self, block: Block) { // First we check that the previous block has been filled. debug_assert!( - self.position.is_default() + self.position.is_none() || self.is_unreachable() || self.is_pristine() || self.is_filled(), @@ -267,9 +245,8 @@ impl<'a> FunctionBuilder<'a> { "you cannot switch to a block which is already filled" ); - let basic_block = self.func_ctx.ssa.header_block(block); // Then we change the cursor position. - self.position = Position::at(block, basic_block); + self.position = PackedOption::from(block); } /// Declares that all the predecessors of this block are known. @@ -278,7 +255,7 @@ impl<'a> FunctionBuilder<'a> { /// created. Forgetting to call this method on every block will cause inconsistencies in the /// produced functions. pub fn seal_block(&mut self, block: Block) { - let side_effects = self.func_ctx.ssa.seal_block_header_block(block, self.func); + let side_effects = self.func_ctx.ssa.seal_block(block, self.func); self.handle_ssa_side_effects(side_effects); } @@ -289,7 +266,7 @@ impl<'a> FunctionBuilder<'a> { /// function can be used at the end of translating all blocks to ensure /// that everything is sealed. pub fn seal_all_blocks(&mut self) { - let side_effects = self.func_ctx.ssa.seal_all_block_header_blocks(self.func); + let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func); self.handle_ssa_side_effects(side_effects); } @@ -310,7 +287,7 @@ impl<'a> FunctionBuilder<'a> { }); self.func_ctx .ssa - .use_var(self.func, var, ty, self.position.basic_block.unwrap()) + .use_var(self.func, var, ty, self.position.unwrap()) }; self.handle_ssa_side_effects(side_effects); val @@ -330,9 +307,7 @@ impl<'a> FunctionBuilder<'a> { val ); - self.func_ctx - .ssa - .def_var(var, val, self.position.basic_block.unwrap()); + self.func_ctx.ssa.def_var(var, val, self.position.unwrap()); } /// Set label for Value @@ -395,14 +370,13 @@ impl<'a> FunctionBuilder<'a> { pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> { let block = self .position - .block .expect("Please call switch_to_block before inserting instructions"); FuncInstBuilder::new(self, block) } /// Make sure that the current block is inserted in the layout. pub fn ensure_inserted_block(&mut self) { - let block = self.position.block.unwrap(); + let block = self.position.unwrap(); if self.func_ctx.blocks[block].pristine { if !self.func.layout.is_block_inserted(block) { self.func.layout.append_block(block); @@ -424,7 +398,7 @@ impl<'a> FunctionBuilder<'a> { self.ensure_inserted_block(); FuncCursor::new(self.func) .with_srcloc(self.srcloc) - .at_bottom(self.position.block.unwrap()) + .at_bottom(self.position.unwrap()) } /// Append parameters to the given `Block` corresponding to the function @@ -495,7 +469,7 @@ impl<'a> FunctionBuilder<'a> { // Reset srcloc and position to initial states. self.srcloc = Default::default(); - self.position = Position::default(); + self.position = Default::default(); } } @@ -560,26 +534,26 @@ impl<'a> FunctionBuilder<'a> { pub fn is_unreachable(&self) -> bool { let is_entry = match self.func.layout.entry_block() { None => false, - Some(entry) => self.position.block.unwrap() == entry, + Some(entry) => self.position.unwrap() == entry, }; !is_entry - && self.func_ctx.ssa.is_sealed(self.position.block.unwrap()) + && self.func_ctx.ssa.is_sealed(self.position.unwrap()) && !self .func_ctx .ssa - .has_any_predecessors(self.position.block.unwrap()) + .has_any_predecessors(self.position.unwrap()) } /// 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.func_ctx.blocks[self.position.block.unwrap()].pristine + self.func_ctx.blocks[self.position.unwrap()].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.func_ctx.blocks[self.position.block.unwrap()].filled + self.func_ctx.blocks[self.position.unwrap()].filled } /// Returns a displayable object for the function as it is. @@ -824,25 +798,15 @@ fn greatest_divisible_power_of_two(size: u64) -> u64 { // Helper functions impl<'a> FunctionBuilder<'a> { - fn move_to_next_basic_block(&mut self) { - self.position.basic_block = PackedOption::from( - self.func_ctx - .ssa - .declare_block_body_block(self.position.basic_block.unwrap()), - ); - } - /// A Block is 'filled' when a terminator instruction is present. fn fill_current_block(&mut self) { - self.func_ctx.blocks[self.position.block.unwrap()].filled = true; + self.func_ctx.blocks[self.position.unwrap()].filled = true; } fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) { - self.func_ctx.ssa.declare_block_predecessor( - dest_block, - self.position.basic_block.unwrap(), - jump_inst, - ); + self.func_ctx + .ssa + .declare_block_predecessor(dest_block, self.position.unwrap(), jump_inst); } fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) { diff --git a/cranelift/frontend/src/ssa.rs b/cranelift/frontend/src/ssa.rs index 2cc3c0e23b..2ae03d3a57 100644 --- a/cranelift/frontend/src/ssa.rs +++ b/cranelift/frontend/src/ssa.rs @@ -10,15 +10,13 @@ use crate::Variable; use alloc::vec::Vec; use core::mem; -use core::u32; use cranelift_codegen::cursor::{Cursor, FuncCursor}; -use cranelift_codegen::entity::{EntityRef, PrimaryMap, SecondaryMap}; +use cranelift_codegen::entity::SecondaryMap; use cranelift_codegen::ir::immediates::{Ieee32, Ieee64}; use cranelift_codegen::ir::instructions::BranchInfo; use cranelift_codegen::ir::types::{F32, F64}; use cranelift_codegen::ir::{Block, Function, Inst, InstBuilder, InstructionData, Type, Value}; use cranelift_codegen::packed_option::PackedOption; -use cranelift_codegen::packed_option::ReservedValue; use smallvec::SmallVec; /// Structure containing the data relevant the construction of SSA for a given function. @@ -26,9 +24,7 @@ use smallvec::SmallVec; /// 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 tha basic blocks since -/// they don't allow branching in the middle of them. +/// The SSA building relies on information about the variables used and defined. /// /// 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. @@ -40,14 +36,11 @@ pub struct SSABuilder { // TODO: Consider a sparse representation rather than SecondaryMap-of-SecondaryMap. /// Records for every variable and for every relevant block, the last definition of /// the variable in the block. - variables: SecondaryMap>>, + variables: SecondaryMap>>, /// Records the position of the basic blocks and the list of values used but not defined in the /// block. - ssa_blocks: PrimaryMap, - - /// Records the basic blocks at the beginning of the `Block`s. - block_headers: SecondaryMap>, + ssa_blocks: SecondaryMap, /// Call stack for use in the `use_var`/`predecessors_lookup` state machine. calls: Vec, @@ -58,7 +51,7 @@ pub struct SSABuilder { side_effects: SideEffects, } -/// Side effects of a `use_var` or a `seal_block_header_block` method call. +/// Side effects of a `use_var` or a `seal_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 `Block`s are signaled here. @@ -83,83 +76,43 @@ impl SideEffects { } } -/// Describes the current position of a basic block in the control flow graph. -enum SSABlockData { - /// A block at the top of a `Block`. - BlockHeader(BlockHeaderSSABlockData), - /// A block inside a `Block` with an unique other block as its predecessor. - /// The block is implicitly sealed at creation. - BlockBody { ssa_pred: SSABlock }, -} - -impl SSABlockData { - fn add_predecessor(&mut self, ssa_pred: SSABlock, inst: Inst) { - match *self { - Self::BlockBody { .. } => panic!("you can't add a predecessor to a body block"), - Self::BlockHeader(ref mut data) => { - debug_assert!(!data.sealed, "sealed blocks cannot accept new predecessors"); - data.predecessors.push(PredBlock::new(ssa_pred, inst)); - } - } - } - fn remove_predecessor(&mut self, inst: Inst) -> SSABlock { - match *self { - Self::BlockBody { .. } => panic!("should not happen"), - Self::BlockHeader(ref mut data) => { - // This a linear complexity operation but the number of predecessors is low - // in all non-pathological cases - let pred: usize = data - .predecessors - .iter() - .position(|&PredBlock { branch, .. }| branch == inst) - .expect("the predecessor you are trying to remove is not declared"); - data.predecessors.swap_remove(pred).ssa_block - } - } - } -} - +#[derive(Clone)] struct PredBlock { - ssa_block: SSABlock, + block: Block, branch: Inst, } impl PredBlock { - fn new(ssa_block: SSABlock, branch: Inst) -> Self { - Self { ssa_block, branch } + fn new(block: Block, branch: Inst) -> Self { + Self { block, branch } } } type PredBlockSmallVec = SmallVec<[PredBlock; 4]>; -struct BlockHeaderSSABlockData { - // The predecessors of the Block header block, with the block and branch instruction. +#[derive(Clone, Default)] +struct SSABlockData { + // The predecessors of the Block with the block and branch instruction. predecessors: PredBlockSmallVec, - // A block header block is sealed if all of its predecessors have been declared. + // A block is sealed if all of its predecessors have been declared. sealed: bool, - // The block which this block is part of. - block: Block, // List of current Block arguments for which an earlier def has not been found yet. undef_variables: Vec<(Variable, Value)>, } -/// A opaque reference to a basic block. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct SSABlock(u32); -impl EntityRef for SSABlock { - fn new(index: usize) -> Self { - debug_assert!(index < (u32::MAX as usize)); - Self(index as u32) +impl SSABlockData { + fn add_predecessor(&mut self, pred: Block, inst: Inst) { + debug_assert!(!self.sealed, "sealed blocks cannot accept new predecessors"); + self.predecessors.push(PredBlock::new(pred, inst)); } - fn index(self) -> usize { - self.0 as usize - } -} - -impl ReservedValue for SSABlock { - fn reserved_value() -> Self { - Self(u32::MAX) + fn remove_predecessor(&mut self, inst: Inst) -> Block { + let pred = self + .predecessors + .iter() + .position(|&PredBlock { branch, .. }| branch == inst) + .expect("the predecessor you are trying to remove is not declared"); + self.predecessors.swap_remove(pred).block } } @@ -168,8 +121,7 @@ impl SSABuilder { pub fn new() -> Self { Self { variables: SecondaryMap::with_default(SecondaryMap::new()), - ssa_blocks: PrimaryMap::new(), - block_headers: SecondaryMap::new(), + ssa_blocks: SecondaryMap::new(), calls: Vec::new(), results: Vec::new(), side_effects: SideEffects::new(), @@ -181,7 +133,6 @@ impl SSABuilder { pub fn clear(&mut self) { self.variables.clear(); self.ssa_blocks.clear(); - self.block_headers.clear(); debug_assert!(self.calls.is_empty()); debug_assert!(self.results.is_empty()); debug_assert!(self.side_effects.is_empty()); @@ -191,7 +142,6 @@ impl SSABuilder { pub fn is_empty(&self) -> bool { self.variables.is_empty() && self.ssa_blocks.is_empty() - && self.block_headers.is_empty() && self.calls.is_empty() && self.results.is_empty() && self.side_effects.is_empty() @@ -210,14 +160,14 @@ enum ZeroOneOrMore { #[derive(Debug)] enum UseVarCases { Unsealed(Value), - SealedOnePredecessor(SSABlock), + SealedOnePredecessor(Block), SealedMultiplePredecessors(Value, Block), } /// States for the `use_var`/`predecessors_lookup` state machine. enum Call { - UseVar(SSABlock), - FinishSealedOnePredecessor(SSABlock), + UseVar(Block), + FinishSealedOnePredecessor(Block), FinishPredecessorsLookup(Value, Block), } @@ -256,9 +206,7 @@ fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value { /// The following methods are the API of the SSA builder. Here is how it should be used when /// translating to Cranelift IR: /// -/// - for each sequence of contiguous instructions (with no branches), create a corresponding -/// basic block with `declare_block_body_block` or `declare_block_header_block` depending on the -/// position of the basic block; +/// - for each basic block, create a corresponding data for SSA construction with `declare_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 @@ -267,19 +215,19 @@ fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value { /// - 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_block_predecessor`; /// -/// - when you have constructed all the predecessor to a basic block at the beginning of a `Block`, -/// call `seal_block_header_block` on it with the `Function` that you are building. +/// - when you have constructed all the predecessor to a basic block, +/// call `seal_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 `Block` headers parameters to account for the SSA +/// as well as modify the jump instruction and `Block` parameters to account for the SSA /// Phi functions. /// impl SSABuilder { /// 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, ssa_block: SSABlock) { - self.variables[var][ssa_block] = PackedOption::from(val); + pub fn def_var(&mut self, var: Variable, val: Value, block: Block) { + self.variables[var][block] = PackedOption::from(val); } /// Declares a use of a variable in a given basic block. Returns the SSA value corresponding @@ -294,12 +242,12 @@ impl SSABuilder { func: &mut Function, var: Variable, ty: Type, - ssa_block: SSABlock, + block: Block, ) -> (Value, SideEffects) { // First, try Local Value Numbering (Algorithm 1 in the paper). // If the variable already has a known Value in this block, use that. if let Some(var_defs) = self.variables.get(var) { - if let Some(val) = var_defs[ssa_block].expand() { + if let Some(val) = var_defs[block].expand() { return (val, SideEffects::new()); } } @@ -311,7 +259,7 @@ impl SSABuilder { debug_assert!(self.side_effects.is_empty()); // Prepare the 'calls' and 'results' stacks for the state machine. - self.use_var_nonlocal(func, var, ty, ssa_block); + self.use_var_nonlocal(func, var, ty, block); let value = self.run_state_machine(func, var, ty); let side_effects = mem::replace(&mut self.side_effects, SideEffects::new()); @@ -322,54 +270,44 @@ impl SSABuilder { /// Resolve the minimal SSA Value of `var` in `block` by traversing predecessors. /// /// This function sets up state for `run_state_machine()` but does not execute it. - fn use_var_nonlocal( - &mut self, - func: &mut Function, - var: Variable, - ty: Type, - ssa_block: SSABlock, - ) { + fn use_var_nonlocal(&mut self, func: &mut Function, var: Variable, ty: Type, block: Block) { // This function is split into two parts to appease the borrow checker. // Part 1: With a mutable borrow of self, update the DataFlowGraph if necessary. - let case = match self.ssa_blocks[ssa_block] { - SSABlockData::BlockHeader(ref mut data) => { - // The block has multiple predecessors so we append a Block parameter that - // will serve as a value. - if data.sealed { - if data.predecessors.len() == 1 { - // Optimize the common case of one predecessor: no param needed. - UseVarCases::SealedOnePredecessor(data.predecessors[0].ssa_block) - } else { - // Break potential cycles by eagerly adding an operandless param. - let val = func.dfg.append_block_param(data.block, ty); - UseVarCases::SealedMultiplePredecessors(val, data.block) - } - } else { - let val = func.dfg.append_block_param(data.block, ty); - data.undef_variables.push((var, val)); - UseVarCases::Unsealed(val) - } + let data = &mut self.ssa_blocks[block]; + let case = if data.sealed { + // The block has multiple predecessors so we append a Block parameter that + // will serve as a value. + if data.predecessors.len() == 1 { + // Optimize the common case of one predecessor: no param needed. + UseVarCases::SealedOnePredecessor(data.predecessors[0].block) + } else { + // Break potential cycles by eagerly adding an operandless param. + let val = func.dfg.append_block_param(block, ty); + UseVarCases::SealedMultiplePredecessors(val, block) } - SSABlockData::BlockBody { ssa_pred } => UseVarCases::SealedOnePredecessor(ssa_pred), + } else { + let val = func.dfg.append_block_param(block, ty); + data.undef_variables.push((var, val)); + UseVarCases::Unsealed(val) }; // Part 2: Prepare SSABuilder state for run_state_machine(). match case { UseVarCases::SealedOnePredecessor(pred) => { // Get the Value directly from the single predecessor. - self.calls.push(Call::FinishSealedOnePredecessor(ssa_block)); + self.calls.push(Call::FinishSealedOnePredecessor(block)); self.calls.push(Call::UseVar(pred)); } UseVarCases::Unsealed(val) => { // Define the operandless param added above to prevent lookup cycles. - self.def_var(var, val, ssa_block); + self.def_var(var, val, block); // Nothing more can be known at this point. self.results.push(val); } UseVarCases::SealedMultiplePredecessors(val, block) => { // Define the operandless param added above to prevent lookup cycles. - self.def_var(var, val, ssa_block); + self.def_var(var, val, block); // Look up a use_var for each precessor. self.begin_predecessors_lookup(val, block); @@ -379,68 +317,43 @@ impl SSABuilder { /// For blocks with a single predecessor, once we've determined the value, /// record a local def for it for future queries to find. - fn finish_sealed_one_predecessor(&mut self, var: Variable, ssa_block: SSABlock) { + fn finish_sealed_one_predecessor(&mut self, var: Variable, block: Block) { let val = *self.results.last().unwrap(); - self.def_var(var, val, ssa_block); + self.def_var(var, val, block); } - /// Declares a new basic block belonging to the body of a certain `Block` and having `pred` - /// as a predecessor. `pred` is the only predecessor of the block and the block is sealed - /// at creation. - /// - /// To declare a `Block` header block, see `declare_block_header_block`. - pub fn declare_block_body_block(&mut self, ssa_pred: SSABlock) -> SSABlock { - self.ssa_blocks.push(SSABlockData::BlockBody { ssa_pred }) - } - - /// Declares a new basic block at the beginning of a `Block`. No predecessors are declared - /// here and the block is not sealed. + /// Declares a new basic block to construct corresponding data for SSA construction. + /// No predecessors are declared here and the block is not sealed. /// Predecessors have to be added with `declare_block_predecessor`. - pub fn declare_block_header_block(&mut self, block: Block) -> SSABlock { - let ssa_block = self - .ssa_blocks - .push(SSABlockData::BlockHeader(BlockHeaderSSABlockData { - predecessors: PredBlockSmallVec::new(), - sealed: false, - block, - undef_variables: Vec::new(), - })); - self.block_headers[block] = ssa_block.into(); - ssa_block - } - /// Gets the header block corresponding to a Block, panics if the Block or the header block - /// isn't declared. - pub fn header_block(&self, block: Block) -> SSABlock { - self.block_headers - .get(block) - .expect("the block has not been declared") - .expand() - .expect("the header block has not been defined") + pub fn declare_block(&mut self, block: Block) { + self.ssa_blocks[block] = SSABlockData { + predecessors: PredBlockSmallVec::new(), + sealed: false, + undef_variables: Vec::new(), + }; } - /// Declares a new predecessor for a `Block` header block and record the branch instruction + /// Declares a new predecessor for a `Block` and record the branch instruction /// of the predecessor that leads to it. /// - /// Note that the predecessor is a `SSABlock` and not a `Block`. This `SSABlock` must be filled - /// before added as predecessor. Note that you must provide no jump arguments to the branch + /// The precedent `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. /// /// Callers are expected to avoid adding the same predecessor more than once in the case /// of a jump table. - pub fn declare_block_predecessor(&mut self, block: Block, ssa_pred: SSABlock, inst: Inst) { + pub fn declare_block_predecessor(&mut self, block: Block, pred: Block, inst: Inst) { debug_assert!(!self.is_sealed(block)); - let header_block = self.header_block(block); - self.ssa_blocks[header_block].add_predecessor(ssa_pred, inst) + self.ssa_blocks[block].add_predecessor(pred, inst) } /// Remove a previously declared Block 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 building problem - pub fn remove_block_predecessor(&mut self, block: Block, inst: Inst) -> SSABlock { + pub fn remove_block_predecessor(&mut self, block: Block, inst: Inst) -> Block { debug_assert!(!self.is_sealed(block)); - let header_block = self.header_block(block); - self.ssa_blocks[header_block].remove_predecessor(inst) + self.ssa_blocks[block].remove_predecessor(inst) } /// Completes the global value numbering for a `Block`, all of its predecessors having been @@ -450,8 +363,8 @@ impl SSABuilder { /// take into account the Phi function placed by the SSA algorithm. /// /// Returns the list of newly created blocks for critical edge splitting. - pub fn seal_block_header_block(&mut self, block: Block, func: &mut Function) -> SideEffects { - self.seal_one_block_header_block(block, func); + pub fn seal_block(&mut self, block: Block, func: &mut Function) -> SideEffects { + self.seal_one_block(block, func); mem::replace(&mut self.side_effects, SideEffects::new()) } @@ -461,35 +374,29 @@ impl SSABuilder { /// translation, but for frontends where this is impractical to do, this /// function can be used at the end of translating all blocks to ensure /// that everything is sealed. - pub fn seal_all_block_header_blocks(&mut self, func: &mut Function) -> SideEffects { + pub fn seal_all_blocks(&mut self, func: &mut Function) -> SideEffects { // Seal all `Block`s currently in the function. This can entail splitting // and creation of new blocks, however such new blocks are sealed on // the fly, so we don't need to account for them here. - for block in self.block_headers.keys() { - self.seal_one_block_header_block(block, func); + for block in self.ssa_blocks.keys() { + self.seal_one_block(block, func); } mem::replace(&mut self.side_effects, SideEffects::new()) } - /// Helper function for `seal_block_header_block` and - /// `seal_all_block_header_blocks`. - fn seal_one_block_header_block(&mut self, block: Block, func: &mut Function) { - let ssa_block = self.header_block(block); + /// Helper function for `seal_block` and + /// `seal_all_blocks`. + fn seal_one_block(&mut self, block: Block, func: &mut Function) { + let block_data = &mut self.ssa_blocks[block]; + debug_assert!( + !block_data.sealed, + "Attempting to seal {} which is already sealed.", + block + ); - let undef_vars = match self.ssa_blocks[ssa_block] { - SSABlockData::BlockBody { .. } => panic!("this should not happen"), - SSABlockData::BlockHeader(ref mut data) => { - debug_assert!( - !data.sealed, - "Attempting to seal {} which is already sealed.", - block - ); - debug_assert_eq!(block, data.block); - // Extract the undef_variables data from the block so that we - // can iterate over it without borrowing the whole builder. - mem::replace(&mut data.undef_variables, Vec::new()) - } - }; + // Extract the undef_variables data from the block so that we + // can iterate over it without borrowing the whole builder. + let undef_vars = mem::replace(&mut block_data.undef_variables, Vec::new()); // For each undef var we look up values in the predecessors and create a block parameter // only if necessary. @@ -497,23 +404,20 @@ impl SSABuilder { let ty = func.dfg.value_type(val); self.predecessors_lookup(func, val, var, ty, block); } - self.mark_block_header_block_sealed(ssa_block); + self.mark_block_sealed(block); } /// Set the `sealed` flag for `block`. - fn mark_block_header_block_sealed(&mut self, ssa_block: SSABlock) { + fn mark_block_sealed(&mut self, block: Block) { // Then we mark the block as sealed. - match self.ssa_blocks[ssa_block] { - SSABlockData::BlockBody { .. } => panic!("this should not happen"), - SSABlockData::BlockHeader(ref mut data) => { - debug_assert!(!data.sealed); - debug_assert!(data.undef_variables.is_empty()); - data.sealed = true; - // We could call data.predecessors.shrink_to_fit() here, if - // important, because no further predecessors will be added - // to this block. - } - } + let block_data = &mut self.ssa_blocks[block]; + debug_assert!(!block_data.sealed); + debug_assert!(block_data.undef_variables.is_empty()); + block_data.sealed = true; + + // We could call data.predecessors.shrink_to_fit() here, if + // important, because no further predecessors will be added + // to this block. } /// Given the local SSA Value of a Variable in a Block, perform a recursive lookup on @@ -559,11 +463,12 @@ impl SSABuilder { .push(Call::FinishPredecessorsLookup(sentinel, dest_block)); // Iterate over the predecessors. let mut calls = mem::replace(&mut self.calls, Vec::new()); - calls.extend(self.predecessors(dest_block).iter().rev().map( - |&PredBlock { - ssa_block: pred, .. - }| Call::UseVar(pred), - )); + calls.extend( + self.predecessors(dest_block) + .iter() + .rev() + .map(|&PredBlock { block: pred, .. }| Call::UseVar(pred)), + ); self.calls = calls; } @@ -647,23 +552,23 @@ impl SSABuilder { let mut preds = mem::replace(self.predecessors_mut(dest_block), PredBlockSmallVec::new()); for &mut PredBlock { - ssa_block: ref mut pred_ssa_block, + block: ref mut pred_block, branch: ref mut last_inst, } in &mut preds { // We already did a full `use_var` above, so we can do just the fast path. let ssa_block_map = self.variables.get(var).unwrap(); - let pred_val = ssa_block_map.get(*pred_ssa_block).unwrap().unwrap(); + let pred_val = ssa_block_map.get(*pred_block).unwrap().unwrap(); let jump_arg = self.append_jump_argument( func, *last_inst, - *pred_ssa_block, + *pred_block, dest_block, pred_val, var, ); - if let Some((middle_block, middle_ssa_block, middle_jump_inst)) = jump_arg { - *pred_ssa_block = middle_ssa_block; + if let Some((middle_block, middle_jump_inst)) = jump_arg { + *pred_block = middle_block; *last_inst = middle_jump_inst; self.side_effects.split_blocks_created.push(middle_block); } @@ -685,11 +590,11 @@ impl SSABuilder { &mut self, func: &mut Function, jump_inst: Inst, - jump_inst_ssa_block: SSABlock, + jump_inst_block: Block, dest_block: Block, val: Value, var: Variable, - ) -> Option<(Block, SSABlock, Inst)> { + ) -> Option<(Block, Inst)> { match func.dfg.analyze_branch(jump_inst) { BranchInfo::NotABranch => { panic!("you have declared a non-branch instruction as a predecessor to a block"); @@ -706,9 +611,9 @@ impl SSABuilder { // We have to split the critical edge let middle_block = func.dfg.make_block(); func.layout.append_block(middle_block); - let middle_ssa_block = self.declare_block_header_block(middle_block); - self.ssa_blocks[middle_ssa_block].add_predecessor(jump_inst_ssa_block, jump_inst); - self.mark_block_header_block_sealed(middle_ssa_block); + self.declare_block(middle_block); + self.ssa_blocks[middle_block].add_predecessor(jump_inst_block, jump_inst); + self.mark_block_sealed(middle_block); if let Some(default_block) = default_block { if dest_block == default_block { @@ -731,19 +636,15 @@ impl SSABuilder { } let mut cur = FuncCursor::new(func).at_bottom(middle_block); let middle_jump_inst = cur.ins().jump(dest_block, &[val]); - self.def_var(var, val, middle_ssa_block); - Some((middle_block, middle_ssa_block, middle_jump_inst)) + self.def_var(var, val, middle_block); + Some((middle_block, middle_jump_inst)) } } } /// Returns the list of `Block`s that have been declared as predecessors of the argument. fn predecessors(&self, block: Block) -> &[PredBlock] { - let ssa_block = self.header_block(block); - match self.ssa_blocks[ssa_block] { - SSABlockData::BlockBody { .. } => panic!("should not happen"), - SSABlockData::BlockHeader(ref data) => &data.predecessors, - } + &self.ssa_blocks[block].predecessors } /// Returns whether the given Block has any predecessor or not. @@ -753,19 +654,12 @@ impl SSABuilder { /// Same as predecessors, but for &mut. fn predecessors_mut(&mut self, block: Block) -> &mut PredBlockSmallVec { - let ssa_block = self.header_block(block); - match self.ssa_blocks[ssa_block] { - SSABlockData::BlockBody { .. } => panic!("should not happen"), - SSABlockData::BlockHeader(ref mut data) => &mut data.predecessors, - } + &mut self.ssa_blocks[block].predecessors } - /// Returns `true` if and only if `seal_block_header_block` has been called on the argument. + /// Returns `true` if and only if `seal_block` has been called on the argument. pub fn is_sealed(&self, block: Block) -> bool { - match self.ssa_blocks[self.header_block(block)] { - SSABlockData::BlockBody { .. } => panic!("should not happen"), - SSABlockData::BlockHeader(ref data) => data.sealed, - } + self.ssa_blocks[block].sealed } /// The main algorithm is naturally recursive: when there's a `use_var` in a @@ -818,45 +712,47 @@ mod tests { let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); // Here is the pseudo-program we want to translate: - // x = 1; - // y = 2; - // z = x + y; - // z = x + z; + // block0: + // x = 1; + // y = 2; + // z = x + y; + // z = x + z; - let ssa_block = ssa.declare_block_header_block(block0); + ssa.declare_block(block0); let x_var = Variable::new(0); let x_ssa = { let mut cur = FuncCursor::new(&mut func); cur.insert_block(block0); cur.ins().iconst(I32, 1) }; - ssa.def_var(x_var, x_ssa, ssa_block); + ssa.def_var(x_var, x_ssa, block0); let y_var = Variable::new(1); let y_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 2) }; - ssa.def_var(y_var, y_ssa, ssa_block); + ssa.def_var(y_var, y_ssa, block0); + assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa); + assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa); - assert_eq!(ssa.use_var(&mut func, x_var, I32, ssa_block).0, x_ssa); - assert_eq!(ssa.use_var(&mut func, y_var, I32, ssa_block).0, y_ssa); let z_var = Variable::new(2); - let x_use1 = ssa.use_var(&mut func, x_var, I32, ssa_block).0; - let y_use1 = ssa.use_var(&mut func, y_var, I32, ssa_block).0; + let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0; + let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0; let z1_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iadd(x_use1, y_use1) }; - ssa.def_var(z_var, z1_ssa, ssa_block); - assert_eq!(ssa.use_var(&mut func, z_var, I32, ssa_block).0, z1_ssa); - let x_use2 = ssa.use_var(&mut func, x_var, I32, ssa_block).0; - let z_use1 = ssa.use_var(&mut func, z_var, I32, ssa_block).0; + ssa.def_var(z_var, z1_ssa, block0); + assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa); + + let x_use2 = ssa.use_var(&mut func, x_var, I32, block0).0; + let z_use1 = ssa.use_var(&mut func, z_var, I32, block0).0; let z2_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iadd(x_use2, z_use1) }; - ssa.def_var(z_var, z2_ssa, ssa_block); - assert_eq!(ssa.use_var(&mut func, z_var, I32, ssa_block).0, z2_ssa); + ssa.def_var(z_var, z2_ssa, block0); + assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z2_ssa); } #[test] @@ -865,79 +761,120 @@ mod tests { let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); let block1 = func.dfg.make_block(); + let block2 = func.dfg.make_block(); // Here is the pseudo-program we want to translate: // block0: // x = 1; // y = 2; // z = x + y; // brnz y, block1; - // z = x + z; + // jump block1; // block1: + // z = x + z; + // jump block2; + // block2: // y = x + y; - - let ssa_block0 = ssa.declare_block_header_block(block0); - let x_var = Variable::new(0); - let x_ssa = { + { let mut cur = FuncCursor::new(&mut func); cur.insert_block(block0); cur.insert_block(block1); - cur.goto_bottom(block0); + cur.insert_block(block2); + } + + // block0 + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); + let x_var = Variable::new(0); + let x_ssa = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 1) }; - ssa.def_var(x_var, x_ssa, ssa_block0); + ssa.def_var(x_var, x_ssa, block0); let y_var = Variable::new(1); let y_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 2) }; - ssa.def_var(y_var, y_ssa, ssa_block0); - assert_eq!(ssa.use_var(&mut func, x_var, I32, ssa_block0).0, x_ssa); - assert_eq!(ssa.use_var(&mut func, y_var, I32, ssa_block0).0, y_ssa); + ssa.def_var(y_var, y_ssa, block0); let z_var = Variable::new(2); - let x_use1 = ssa.use_var(&mut func, x_var, I32, ssa_block0).0; - let y_use1 = ssa.use_var(&mut func, y_var, I32, ssa_block0).0; + let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0; + let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0; let z1_ssa = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iadd(x_use1, y_use1) }; - ssa.def_var(z_var, z1_ssa, ssa_block0); - assert_eq!(ssa.use_var(&mut func, z_var, I32, ssa_block0).0, z1_ssa); - let y_use2 = ssa.use_var(&mut func, y_var, I32, ssa_block0).0; - let jump_inst: Inst = { + ssa.def_var(z_var, z1_ssa, block0); + let y_use2 = ssa.use_var(&mut func, y_var, I32, block0).0; + let brnz_block0_block2: Inst = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); - cur.ins().brnz(y_use2, block1, &[]) + cur.ins().brnz(y_use2, block2, &[]) }; - let ssa_block1 = ssa.declare_block_body_block(ssa_block0); - let x_use2 = ssa.use_var(&mut func, x_var, I32, ssa_block1).0; - assert_eq!(x_use2, x_ssa); - let z_use1 = ssa.use_var(&mut func, z_var, I32, ssa_block1).0; - assert_eq!(z_use1, z1_ssa); - let z2_ssa = { + let jump_block0_block1: Inst = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); + cur.ins().jump(block1, &[]) + }; + + assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa); + assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa); + assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa); + + // block1 + ssa.declare_block(block1); + ssa.declare_block_predecessor(block1, block0, jump_block0_block1); + ssa.seal_block(block1, &mut func); + + let x_use2 = ssa.use_var(&mut func, x_var, I32, block1).0; + let z_use1 = ssa.use_var(&mut func, z_var, I32, block1).0; + let z2_ssa = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().iadd(x_use2, z_use1) }; - ssa.def_var(z_var, z2_ssa, ssa_block1); - assert_eq!(ssa.use_var(&mut func, z_var, I32, ssa_block1).0, z2_ssa); - ssa.seal_block_header_block(block0, &mut func); - let ssa_block2 = ssa.declare_block_header_block(block1); - ssa.declare_block_predecessor(block1, ssa_block0, jump_inst); - ssa.seal_block_header_block(block1, &mut func); - let x_use3 = ssa.use_var(&mut func, x_var, I32, ssa_block2).0; - assert_eq!(x_ssa, x_use3); - let y_use3 = ssa.use_var(&mut func, y_var, I32, ssa_block2).0; - assert_eq!(y_ssa, y_use3); + ssa.def_var(z_var, z2_ssa, block1); + let jump_block1_block2: Inst = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block1); + cur.ins().jump(block2, &[]) + }; + + assert_eq!(x_use2, x_ssa); + assert_eq!(z_use1, z1_ssa); + assert_eq!(ssa.use_var(&mut func, z_var, I32, block1).0, z2_ssa); + + // block2 + ssa.declare_block(block2); + ssa.declare_block_predecessor(block2, block0, brnz_block0_block2); + ssa.declare_block_predecessor(block2, block1, jump_block1_block2); + ssa.seal_block(block2, &mut func); + let x_use3 = ssa.use_var(&mut func, x_var, I32, block2).0; + let y_use3 = ssa.use_var(&mut func, y_var, I32, block2).0; let y2_ssa = { - let mut cur = FuncCursor::new(&mut func).at_bottom(block0); + let mut cur = FuncCursor::new(&mut func).at_bottom(block2); cur.ins().iadd(x_use3, y_use3) }; - ssa.def_var(y_var, y2_ssa, ssa_block2); - match func.dfg.analyze_branch(jump_inst) { + ssa.def_var(y_var, y2_ssa, block2); + + assert_eq!(x_ssa, x_use3); + assert_eq!(y_ssa, y_use3); + match func.dfg.analyze_branch(brnz_block0_block2) { + BranchInfo::SingleDest(dest, jump_args) => { + assert_eq!(dest, block2); + assert_eq!(jump_args.len(), 0); + } + _ => assert!(false), + }; + match func.dfg.analyze_branch(jump_block0_block1) { BranchInfo::SingleDest(dest, jump_args) => { assert_eq!(dest, block1); assert_eq!(jump_args.len(), 0); } _ => assert!(false), }; + match func.dfg.analyze_branch(jump_block1_block2) { + BranchInfo::SingleDest(dest, jump_args) => { + assert_eq!(dest, block2); + assert_eq!(jump_args.len(), 0); + } + _ => assert!(false), + }; } #[test] @@ -947,6 +884,14 @@ mod tests { let block0 = func.dfg.make_block(); let block1 = func.dfg.make_block(); let block2 = func.dfg.make_block(); + let block3 = func.dfg.make_block(); + { + let mut cur = FuncCursor::new(&mut func); + cur.insert_block(block0); + cur.insert_block(block1); + cur.insert_block(block2); + cur.insert_block(block3); + } // Here is the pseudo-program we want to translate: // block0: // x = 1; @@ -955,97 +900,108 @@ mod tests { // jump block1 // block1: // z = z + y; - // brnz y, block1; + // brnz y, block3; + // jump block2; + // block2: // z = z - x; // return y - // block2: + // block3: // y = y - x // jump block1 - let ssa_block0 = ssa.declare_block_header_block(block0); - ssa.seal_block_header_block(block0, &mut func); + // block0 + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); let x_var = Variable::new(0); let x1 = { - let mut cur = FuncCursor::new(&mut func); - cur.insert_block(block0); - cur.insert_block(block1); - cur.insert_block(block2); - cur.goto_bottom(block0); + let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 1) }; - ssa.def_var(x_var, x1, ssa_block0); - assert_eq!(ssa.use_var(&mut func, x_var, I32, ssa_block0).0, x1); + ssa.def_var(x_var, x1, block0); let y_var = Variable::new(1); let y1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 2) }; - ssa.def_var(y_var, y1, ssa_block0); - assert_eq!(ssa.use_var(&mut func, y_var, I32, ssa_block0).0, y1); + ssa.def_var(y_var, y1, block0); let z_var = Variable::new(2); - let x2 = ssa.use_var(&mut func, x_var, I32, ssa_block0).0; - assert_eq!(x2, x1); - let y2 = ssa.use_var(&mut func, y_var, I32, ssa_block0).0; - assert_eq!(y2, y1); + let x2 = ssa.use_var(&mut func, x_var, I32, block0).0; + let y2 = ssa.use_var(&mut func, y_var, I32, block0).0; let z1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iadd(x2, y2) }; - ssa.def_var(z_var, z1, ssa_block0); + ssa.def_var(z_var, z1, block0); let jump_block0_block1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().jump(block1, &[]) }; - let ssa_block1 = ssa.declare_block_header_block(block1); - ssa.declare_block_predecessor(block1, ssa_block0, jump_block0_block1); - let z2 = ssa.use_var(&mut func, z_var, I32, ssa_block1).0; - let y3 = ssa.use_var(&mut func, y_var, I32, ssa_block1).0; + assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x1); + assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y1); + assert_eq!(x2, x1); + assert_eq!(y2, y1); + + // block1 + ssa.declare_block(block1); + ssa.declare_block_predecessor(block1, block0, jump_block0_block1); + let z2 = ssa.use_var(&mut func, z_var, I32, block1).0; + let y3 = ssa.use_var(&mut func, y_var, I32, block1).0; let z3 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().iadd(z2, y3) }; - ssa.def_var(z_var, z3, ssa_block1); - let y4 = ssa.use_var(&mut func, y_var, I32, ssa_block1).0; + ssa.def_var(z_var, z3, block1); + let y4 = ssa.use_var(&mut func, y_var, I32, block1).0; assert_eq!(y4, y3); + let brnz_block1_block3 = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block1); + cur.ins().brnz(y4, block3, &[]) + }; let jump_block1_block2 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); - cur.ins().brnz(y4, block2, &[]) + cur.ins().jump(block2, &[]) }; - let ssa_block2 = ssa.declare_block_body_block(ssa_block1); - let z4 = ssa.use_var(&mut func, z_var, I32, ssa_block2).0; + + // block2 + ssa.declare_block(block2); + ssa.declare_block_predecessor(block2, block1, jump_block1_block2); + ssa.seal_block(block2, &mut func); + let z4 = ssa.use_var(&mut func, z_var, I32, block2).0; assert_eq!(z4, z3); - let x3 = ssa.use_var(&mut func, x_var, I32, ssa_block2).0; + let x3 = ssa.use_var(&mut func, x_var, I32, block2).0; let z5 = { - let mut cur = FuncCursor::new(&mut func).at_bottom(block1); + let mut cur = FuncCursor::new(&mut func).at_bottom(block2); cur.ins().isub(z4, x3) }; - ssa.def_var(z_var, z5, ssa_block2); - let y5 = ssa.use_var(&mut func, y_var, I32, ssa_block2).0; + ssa.def_var(z_var, z5, block2); + let y5 = ssa.use_var(&mut func, y_var, I32, block2).0; assert_eq!(y5, y3); { - let mut cur = FuncCursor::new(&mut func).at_bottom(block1); + let mut cur = FuncCursor::new(&mut func).at_bottom(block2); cur.ins().return_(&[y5]) }; - let ssa_block3 = ssa.declare_block_header_block(block2); - ssa.declare_block_predecessor(block2, ssa_block1, jump_block1_block2); - ssa.seal_block_header_block(block2, &mut func); - let y6 = ssa.use_var(&mut func, y_var, I32, ssa_block3).0; + // block3 + ssa.declare_block(block3); + ssa.declare_block_predecessor(block3, block1, brnz_block1_block3); + ssa.seal_block(block3, &mut func); + let y6 = ssa.use_var(&mut func, y_var, I32, block3).0; assert_eq!(y6, y3); - let x4 = ssa.use_var(&mut func, x_var, I32, ssa_block3).0; + let x4 = ssa.use_var(&mut func, x_var, I32, block3).0; assert_eq!(x4, x3); let y7 = { - let mut cur = FuncCursor::new(&mut func).at_bottom(block2); + let mut cur = FuncCursor::new(&mut func).at_bottom(block3); cur.ins().isub(y6, x4) }; - ssa.def_var(y_var, y7, ssa_block3); - let jump_block2_block1 = { - let mut cur = FuncCursor::new(&mut func).at_bottom(block2); + ssa.def_var(y_var, y7, block3); + let jump_block3_block1 = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block3); cur.ins().jump(block1, &[]) }; - ssa.declare_block_predecessor(block1, ssa_block3, jump_block2_block1); - ssa.seal_block_header_block(block1, &mut func); + // block1 after all predecessors have been visited. + ssa.declare_block_predecessor(block1, block3, jump_block3_block1); + ssa.seal_block(block1, &mut func); assert_eq!(func.dfg.block_params(block1)[0], z2); assert_eq!(func.dfg.block_params(block1)[1], y3); assert_eq!(func.dfg.resolve_aliases(x3), x1); @@ -1072,69 +1028,60 @@ mod tests { let mut func = Function::new(); let mut ssa = SSABuilder::new(); - let mut jump_table = JumpTableData::new(); let block0 = func.dfg.make_block(); let block1 = func.dfg.make_block(); let block2 = func.dfg.make_block(); - - // block0: - // x = 1; - let ssa_block0 = ssa.declare_block_header_block(block0); - ssa.seal_block_header_block(block0, &mut func); - let x_var = Variable::new(0); - let x1 = { + let mut jump_table = JumpTableData::new(); + jump_table.push_entry(block2); + jump_table.push_entry(block1); + { let mut cur = FuncCursor::new(&mut func); cur.insert_block(block0); cur.insert_block(block1); cur.insert_block(block2); - cur.goto_bottom(block0); + } + + // block0 + let x1 = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 1) }; - ssa.def_var(x_var, x1, ssa_block0); - - // jt = jump_table [block2, block1] - jump_table.push_entry(block2); - jump_table.push_entry(block1); - let jt = func.create_jump_table(jump_table); - - // block0: - // ... - // br_table x, block2, jt - ssa.use_var(&mut func, x_var, I32, ssa_block0).0; + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); + let x_var = Variable::new(0); + ssa.def_var(x_var, x1, block0); + ssa.use_var(&mut func, x_var, I32, block0).0; let br_table = { + let jt = func.create_jump_table(jump_table); let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().br_table(x1, block2, jt) }; - // block1: - // x = 2 - // jump block2 - let ssa_block1 = ssa.declare_block_header_block(block1); - ssa.seal_block_header_block(block1, &mut func); + // block1 + ssa.declare_block(block1); + ssa.declare_block_predecessor(block1, block0, br_table); + ssa.seal_block(block1, &mut func); let x2 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().iconst(I32, 2) }; - ssa.def_var(x_var, x2, ssa_block1); - let jump_inst = { + ssa.def_var(x_var, x2, block1); + let jump_block1_block2 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().jump(block2, &[]) }; - // block2: - // x = x + 1 - // return - let ssa_block3 = ssa.declare_block_header_block(block2); - ssa.declare_block_predecessor(block2, ssa_block1, jump_inst); - ssa.declare_block_predecessor(block2, ssa_block0, br_table); - ssa.seal_block_header_block(block2, &mut func); - let ssa_block4 = ssa.declare_block_body_block(ssa_block3); - let x3 = ssa.use_var(&mut func, x_var, I32, ssa_block4).0; + // block2 + ssa.declare_block(block2); + ssa.declare_block_predecessor(block2, block1, jump_block1_block2); + ssa.declare_block_predecessor(block2, block0, br_table); + ssa.seal_block(block2, &mut func); + let x3 = ssa.use_var(&mut func, x_var, I32, block2).0; let x4 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block2); cur.ins().iadd_imm(x3, 1) }; - ssa.def_var(x_var, x4, ssa_block4); + ssa.def_var(x_var, x4, block2); { let mut cur = FuncCursor::new(&mut func).at_bottom(block2); cur.ins().return_(&[]) @@ -1154,73 +1101,79 @@ mod tests { #[test] fn undef_values_reordering() { + // Here is the pseudo-program we want to translate: + // block0: + // x = 0; + // y = 1; + // z = 2; + // jump block1; + // block1: + // x = z + x; + // y = y - x; + // jump block1; + // let mut func = Function::new(); let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); let block1 = func.dfg.make_block(); - // Here is the pseudo-program we want to translate: - // block0: - // x = 0 - // y = 1 - // z = 2 - // jump block1 - // block1: - // x = z + x - // y = y - x - // jump block1 - // - let ssa_block0 = ssa.declare_block_header_block(block0); - let x_var = Variable::new(0); - let y_var = Variable::new(1); - let z_var = Variable::new(2); - ssa.seal_block_header_block(block0, &mut func); - let x1 = { + { let mut cur = FuncCursor::new(&mut func); cur.insert_block(block0); cur.insert_block(block1); - cur.goto_bottom(block0); + } + + // block0 + ssa.declare_block(block0); + let x_var = Variable::new(0); + ssa.seal_block(block0, &mut func); + let x1 = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 0) }; - ssa.def_var(x_var, x1, ssa_block0); + ssa.def_var(x_var, x1, block0); + let y_var = Variable::new(1); let y1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 1) }; - ssa.def_var(y_var, y1, ssa_block0); + ssa.def_var(y_var, y1, block0); + let z_var = Variable::new(2); let z1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().iconst(I32, 2) }; - ssa.def_var(z_var, z1, ssa_block0); - let jump_inst = { + ssa.def_var(z_var, z1, block0); + let jump_block0_block1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block0); cur.ins().jump(block1, &[]) }; - let ssa_block1 = ssa.declare_block_header_block(block1); - ssa.declare_block_predecessor(block1, ssa_block0, jump_inst); - let z2 = ssa.use_var(&mut func, z_var, I32, ssa_block1).0; + + // block1 + ssa.declare_block(block1); + ssa.declare_block_predecessor(block1, block0, jump_block0_block1); + let z2 = ssa.use_var(&mut func, z_var, I32, block1).0; assert_eq!(func.dfg.block_params(block1)[0], z2); - let x2 = ssa.use_var(&mut func, x_var, I32, ssa_block1).0; + let x2 = ssa.use_var(&mut func, x_var, I32, block1).0; assert_eq!(func.dfg.block_params(block1)[1], x2); let x3 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().iadd(x2, z2) }; - ssa.def_var(x_var, x3, ssa_block1); - let x4 = ssa.use_var(&mut func, x_var, I32, ssa_block1).0; - let y3 = ssa.use_var(&mut func, y_var, I32, ssa_block1).0; + ssa.def_var(x_var, x3, block1); + let x4 = ssa.use_var(&mut func, x_var, I32, block1).0; + let y3 = ssa.use_var(&mut func, y_var, I32, block1).0; assert_eq!(func.dfg.block_params(block1)[2], y3); let y4 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().isub(y3, x4) }; - ssa.def_var(y_var, y4, ssa_block1); - let jump_inst = { + ssa.def_var(y_var, y4, block1); + let jump_block1_block1 = { let mut cur = FuncCursor::new(&mut func).at_bottom(block1); cur.ins().jump(block1, &[]) }; - ssa.declare_block_predecessor(block1, ssa_block1, jump_inst); - ssa.seal_block_header_block(block1, &mut func); + ssa.declare_block_predecessor(block1, block1, jump_block1_block1); + ssa.seal_block(block1, &mut func); // At sealing the "z" argument disappear but the remaining "x" and "y" args have to be // in the right order. assert_eq!(func.dfg.block_params(block1)[1], y3); @@ -1233,18 +1186,18 @@ mod tests { let mut func = Function::new(); let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); - let ssa_block = ssa.declare_block_header_block(block0); - ssa.seal_block_header_block(block0, &mut func); + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); let i32_var = Variable::new(0); let f32_var = Variable::new(1); let f64_var = Variable::new(2); let b1_var = Variable::new(3); let f32x4_var = Variable::new(4); - ssa.use_var(&mut func, i32_var, I32, ssa_block); - ssa.use_var(&mut func, f32_var, F32, ssa_block); - ssa.use_var(&mut func, f64_var, F64, ssa_block); - ssa.use_var(&mut func, b1_var, B1, ssa_block); - ssa.use_var(&mut func, f32x4_var, F32X4, ssa_block); + ssa.use_var(&mut func, i32_var, I32, block0); + ssa.use_var(&mut func, f32_var, F32, block0); + ssa.use_var(&mut func, f64_var, F64, block0); + ssa.use_var(&mut func, b1_var, B1, block0); + ssa.use_var(&mut func, f32x4_var, F32X4, block0); assert_eq!(func.dfg.num_block_params(block0), 0); } @@ -1255,11 +1208,11 @@ mod tests { let mut func = Function::new(); let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); - let ssa_block = ssa.declare_block_header_block(block0); - ssa.seal_block_header_block(block0, &mut func); + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); let x_var = Variable::new(0); assert_eq!(func.dfg.num_block_params(block0), 0); - ssa.use_var(&mut func, x_var, I32, ssa_block); + ssa.use_var(&mut func, x_var, I32, block0); assert_eq!(func.dfg.num_block_params(block0), 0); assert_eq!( func.dfg[func.layout.first_inst(block0).unwrap()].opcode(), @@ -1275,12 +1228,12 @@ mod tests { let mut func = Function::new(); let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); - let ssa_block = ssa.declare_block_header_block(block0); + ssa.declare_block(block0); let x_var = Variable::new(0); assert_eq!(func.dfg.num_block_params(block0), 0); - ssa.use_var(&mut func, x_var, I32, ssa_block); + ssa.use_var(&mut func, x_var, I32, block0); assert_eq!(func.dfg.num_block_params(block0), 1); - ssa.seal_block_header_block(block0, &mut func); + ssa.seal_block(block0, &mut func); assert_eq!(func.dfg.num_block_params(block0), 0); assert_eq!( func.dfg[func.layout.first_inst(block0).unwrap()].opcode(), @@ -1290,35 +1243,43 @@ mod tests { #[test] fn unreachable_use() { + // Here is the pseudo-program we want to translate: + // block0: + // return; + // block1: + // brz x, block1; + // jump block1; let mut func = Function::new(); let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); let block1 = func.dfg.make_block(); - // Here is the pseudo-program we want to translate: - // block0: - // return - // block1: - // brz v1, block1 - // jump block1 - let _ssa_block0 = ssa.declare_block_header_block(block0); - ssa.seal_block_header_block(block0, &mut func); - let ssa_block1 = ssa.declare_block_header_block(block1); - let ssa_block2 = ssa.declare_block_body_block(ssa_block1); { let mut cur = FuncCursor::new(&mut func); cur.insert_block(block0); cur.insert_block(block1); - cur.goto_bottom(block0); - cur.ins().return_(&[]); - let x_var = Variable::new(0); - cur.goto_bottom(block1); - let val = ssa.use_var(&mut cur.func, x_var, I32, ssa_block1).0; - let brz = cur.ins().brz(val, block1, &[]); - ssa.declare_block_predecessor(block1, ssa_block1, brz); - let j = cur.ins().jump(block1, &[]); - ssa.declare_block_predecessor(block1, ssa_block2, j); } - ssa.seal_block_header_block(block1, &mut func); + + // block0 + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); + { + let mut cur = FuncCursor::new(&mut func).at_bottom(block0); + cur.ins().return_(&[]); + } + + // block1 + ssa.declare_block(block1); + { + let mut cur = FuncCursor::new(&mut func).at_bottom(block1); + let x_var = Variable::new(0); + let x_val = ssa.use_var(&mut cur.func, x_var, I32, block1).0; + let brz = cur.ins().brz(x_val, block1, &[]); + let jump_block1_block1 = cur.ins().jump(block1, &[]); + ssa.declare_block_predecessor(block1, block1, brz); + ssa.declare_block_predecessor(block1, block1, jump_block1_block1); + } + ssa.seal_block(block1, &mut func); + let flags = settings::Flags::new(settings::builder()); match verify_function(&func, &flags) { Ok(()) => {} @@ -1333,43 +1294,58 @@ mod tests { #[test] fn unreachable_use_with_multiple_preds() { + // Here is the pseudo-program we want to translate: + // block0: + // return; + // block1: + // brz x, block2; + // jump block1; + // block2: + // jump block1; let mut func = Function::new(); let mut ssa = SSABuilder::new(); let block0 = func.dfg.make_block(); let block1 = func.dfg.make_block(); let block2 = func.dfg.make_block(); - // Here is the pseudo-program we want to translate: - // block0: - // return - // block1: - // brz v1, block2 - // jump block1 - // block2: - // jump block1 - let _ssa_block0 = ssa.declare_block_header_block(block0); - ssa.seal_block_header_block(block0, &mut func); - let ssa_block1 = ssa.declare_block_header_block(block1); - let ssa_block2 = ssa.declare_block_header_block(block2); { let mut cur = FuncCursor::new(&mut func); - let x_var = Variable::new(0); cur.insert_block(block0); cur.insert_block(block1); cur.insert_block(block2); - cur.goto_bottom(block0); - cur.ins().return_(&[]); - cur.goto_bottom(block1); - let v = ssa.use_var(&mut cur.func, x_var, I32, ssa_block1).0; - let brz = cur.ins().brz(v, block2, &[]); - let j0 = cur.ins().jump(block1, &[]); - cur.goto_bottom(block2); - let j1 = cur.ins().jump(block1, &[]); - ssa.declare_block_predecessor(block1, ssa_block2, brz); - ssa.declare_block_predecessor(block1, ssa_block1, j0); - ssa.declare_block_predecessor(block2, ssa_block1, j1); } - ssa.seal_block_header_block(block1, &mut func); - ssa.seal_block_header_block(block2, &mut func); + + // block0 + ssa.declare_block(block0); + ssa.seal_block(block0, &mut func); + { + let mut cur = FuncCursor::new(&mut func).at_bottom(block0); + cur.ins().return_(&[]); + } + + // block1 + ssa.declare_block(block1); + let brz = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block1); + let x_var = Variable::new(0); + let x_val = ssa.use_var(&mut cur.func, x_var, I32, block1).0; + let brz = cur.ins().brz(x_val, block2, &[]); + let jump_block1_block1 = cur.ins().jump(block1, &[]); + ssa.declare_block_predecessor(block1, block1, jump_block1_block1); + brz + }; + + // block2 + ssa.declare_block(block2); + ssa.declare_block_predecessor(block2, block1, brz); + ssa.seal_block(block2, &mut func); + let jump_block2_block1 = { + let mut cur = FuncCursor::new(&mut func).at_bottom(block2); + cur.ins().jump(block1, &[]) + }; + + // seal block1 + ssa.declare_block_predecessor(block1, block2, jump_block2_block1); + ssa.seal_block(block1, &mut func); let flags = settings::Flags::new(settings::builder()); match verify_function(&func, &flags) { Ok(()) => {}