Path: blob/main/cranelift/frontend/src/ssa.rs
1693 views
//! A SSA-building API that handles incomplete CFGs.1//!2//! The algorithm is based upon Braun M., Buchwald S., Hack S., Leißa R., Mallon C.,3//! Zwinkau A. (2013) Simple and Efficient Construction of Static Single Assignment Form.4//! In: Jhala R., De Bosschere K. (eds) Compiler Construction. CC 2013.5//! Lecture Notes in Computer Science, vol 7791. Springer, Berlin, Heidelberg6//!7//! <https://link.springer.com/content/pdf/10.1007/978-3-642-37051-9_6.pdf>89use crate::Variable;10use alloc::vec::Vec;11use core::mem;12use cranelift_codegen::cursor::{Cursor, FuncCursor};13use cranelift_codegen::entity::{EntityList, EntitySet, ListPool, SecondaryMap};14use cranelift_codegen::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128};15use cranelift_codegen::ir::types::{F16, F32, F64, F128, I64, I128};16use cranelift_codegen::ir::{Block, Function, Inst, InstBuilder, Type, Value};17use cranelift_codegen::packed_option::PackedOption;1819/// Structure containing the data relevant the construction of SSA for a given function.20///21/// The parameter struct [`Variable`] corresponds to the way variables are represented in the22/// non-SSA language you're translating from.23///24/// The SSA building relies on information about the variables used and defined.25///26/// This SSA building module allows you to def and use variables on the fly while you are27/// constructing the CFG, no need for a separate SSA pass after the CFG is completed.28///29/// A basic block is said _filled_ if all the instruction that it contains have been translated,30/// and it is said _sealed_ if all of its predecessors have been declared. Only filled predecessors31/// can be declared.32#[derive(Default)]33pub struct SSABuilder {34// TODO: Consider a sparse representation rather than SecondaryMap-of-SecondaryMap.35/// Records for every variable and for every relevant block, the last definition of36/// the variable in the block.37variables: SecondaryMap<Variable, SecondaryMap<Block, PackedOption<Value>>>,3839/// Records the position of the basic blocks and the list of values used but not defined in the40/// block.41ssa_blocks: SecondaryMap<Block, SSABlockData>,4243/// Call stack for use in the `use_var`/`predecessors_lookup` state machine.44calls: Vec<Call>,45/// Result stack for use in the `use_var`/`predecessors_lookup` state machine.46results: Vec<Value>,4748/// Side effects accumulated in the `use_var`/`predecessors_lookup` state machine.49side_effects: SideEffects,5051/// Reused storage for cycle-detection.52visited: EntitySet<Block>,5354/// Storage for pending variable definitions.55variable_pool: ListPool<Variable>,5657/// Storage for predecessor definitions.58inst_pool: ListPool<Inst>,59}6061/// Side effects of a `use_var` or a `seal_block` method call.62#[derive(Default)]63pub struct SideEffects {64/// When a variable is used but has never been defined before (this happens in the case of65/// unreachable code), a placeholder `iconst` or `fconst` value is added to the right `Block`.66/// This field signals if it is the case and return the `Block` to which the initialization has67/// been added.68pub instructions_added_to_blocks: Vec<Block>,69}7071impl SideEffects {72fn is_empty(&self) -> bool {73let Self {74instructions_added_to_blocks,75} = self;76instructions_added_to_blocks.is_empty()77}78}7980#[derive(Clone)]81enum Sealed {82No {83// List of current Block arguments for which an earlier def has not been found yet.84undef_variables: EntityList<Variable>,85},86Yes,87}8889impl Default for Sealed {90fn default() -> Self {91Sealed::No {92undef_variables: EntityList::new(),93}94}95}9697#[derive(Clone, Default)]98struct SSABlockData {99// The predecessors of the Block with the block and branch instruction.100predecessors: EntityList<Inst>,101// A block is sealed if all of its predecessors have been declared.102sealed: Sealed,103// If this block is sealed and it has exactly one predecessor, this is that predecessor.104single_predecessor: PackedOption<Block>,105}106107impl SSABuilder {108/// Clears a `SSABuilder` from all its data, letting it in a pristine state without109/// deallocating memory.110pub fn clear(&mut self) {111self.variables.clear();112self.ssa_blocks.clear();113self.variable_pool.clear();114self.inst_pool.clear();115debug_assert!(self.calls.is_empty());116debug_assert!(self.results.is_empty());117debug_assert!(self.side_effects.is_empty());118}119120/// Tests whether an `SSABuilder` is in a cleared state.121pub fn is_empty(&self) -> bool {122self.variables.is_empty()123&& self.ssa_blocks.is_empty()124&& self.calls.is_empty()125&& self.results.is_empty()126&& self.side_effects.is_empty()127}128}129130/// States for the `use_var`/`predecessors_lookup` state machine.131enum Call {132UseVar(Inst),133FinishPredecessorsLookup(Value, Block),134}135136/// Emit instructions to produce a zero value in the given type.137fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value {138match ty {139I128 => {140let zero = cur.ins().iconst(I64, 0);141cur.ins().uextend(I128, zero)142}143ty if ty.is_int() => cur.ins().iconst(ty, 0),144F16 => cur.ins().f16const(Ieee16::with_bits(0)),145F32 => cur.ins().f32const(Ieee32::with_bits(0)),146F64 => cur.ins().f64const(Ieee64::with_bits(0)),147F128 => {148let zero = cur.func.dfg.constants.insert(Ieee128::with_bits(0).into());149cur.ins().f128const(zero)150}151ty if ty.is_vector() => match ty.lane_type() {152scalar_ty if scalar_ty.is_int() => {153let zero = cur154.func155.dfg156.constants157.insert(vec![0; ty.bytes().try_into().unwrap()].into());158cur.ins().vconst(ty, zero)159}160F16 => {161let scalar = cur.ins().f16const(Ieee16::with_bits(0));162cur.ins().splat(ty, scalar)163}164F32 => {165let scalar = cur.ins().f32const(Ieee32::with_bits(0));166cur.ins().splat(ty, scalar)167}168F64 => {169let scalar = cur.ins().f64const(Ieee64::with_bits(0));170cur.ins().splat(ty, scalar)171}172F128 => {173let zero = cur.func.dfg.constants.insert(Ieee128::with_bits(0).into());174let scalar = cur.ins().f128const(zero);175cur.ins().splat(ty, scalar)176}177_ => panic!("unimplemented scalar type: {ty:?}"),178},179ty => panic!("unimplemented type: {ty:?}"),180}181}182183/// The following methods are the API of the SSA builder. Here is how it should be used when184/// translating to Cranelift IR:185///186/// - for each basic block, create a corresponding data for SSA construction with `declare_block`;187///188/// - while traversing a basic block and translating instruction, use `def_var` and `use_var`189/// to record definitions and uses of variables, these methods will give you the corresponding190/// SSA values;191///192/// - when all the instructions in a basic block have translated, the block is said _filled_ and193/// only then you can add it as a predecessor to other blocks with `declare_block_predecessor`;194///195/// - when you have constructed all the predecessor to a basic block,196/// call `seal_block` on it with the `Function` that you are building.197///198/// This API will give you the correct SSA values to use as arguments of your instructions,199/// as well as modify the jump instruction and `Block` parameters to account for the SSA200/// Phi functions.201///202impl SSABuilder {203/// Get all of the values associated with the given variable that we have204/// inserted in the function thus far.205pub fn values_for_var(&self, var: Variable) -> impl Iterator<Item = Value> + '_ {206self.variables[var].values().filter_map(|v| v.expand())207}208209/// Declares a new definition of a variable in a given basic block.210/// The SSA value is passed as an argument because it should be created with211/// `ir::DataFlowGraph::append_result`.212pub fn def_var(&mut self, var: Variable, val: Value, block: Block) {213self.variables[var][block] = PackedOption::from(val);214}215216/// Declares a use of a variable in a given basic block. Returns the SSA value corresponding217/// to the current SSA definition of this variable and a list of newly created Blocks that218/// are the results of critical edge splitting for `br_table` with arguments.219///220/// If the variable has never been defined in this blocks or recursively in its predecessors,221/// this method will silently create an initializer with `iconst` or `fconst`. You are222/// responsible for making sure that you initialize your variables.223pub fn use_var(224&mut self,225func: &mut Function,226var: Variable,227ty: Type,228block: Block,229) -> (Value, SideEffects) {230debug_assert!(self.calls.is_empty());231debug_assert!(self.results.is_empty());232debug_assert!(self.side_effects.is_empty());233234// Prepare the 'calls' and 'results' stacks for the state machine.235self.use_var_nonlocal(func, var, ty, block);236let value = self.run_state_machine(func, var, ty);237238let side_effects = mem::take(&mut self.side_effects);239(value, side_effects)240}241242/// Resolve the minimal SSA Value of `var` in `block` by traversing predecessors.243///244/// This function sets up state for `run_state_machine()` but does not execute it.245fn use_var_nonlocal(&mut self, func: &mut Function, var: Variable, ty: Type, mut block: Block) {246// First, try Local Value Numbering (Algorithm 1 in the paper).247// If the variable already has a known Value in this block, use that.248if let Some(val) = self.variables[var][block].expand() {249self.results.push(val);250return;251}252253// Otherwise, use Global Value Numbering (Algorithm 2 in the paper).254// This resolves the Value with respect to its predecessors.255// Find the most recent definition of `var`, and the block the definition comes from.256let (val, from) = self.find_var(func, var, ty, block);257258// The `from` block returned from `find_var` is guaranteed to be on the path we follow by259// traversing only single-predecessor edges. It might be equal to `block` if there is no260// such path, but in that case `find_var` ensures that the variable is defined in this block261// by a new block parameter. It also might be somewhere in a cycle, but even then this loop262// will terminate the first time it encounters that block, rather than continuing around the263// cycle forever.264//265// Why is it okay to copy the definition to all intervening blocks? For the initial block,266// this may not be the final definition of this variable within this block, but if we've267// gotten here then we know there is no earlier definition in the block already.268//269// For the remaining blocks: Recall that a block is only allowed to be set as a predecessor270// after all its instructions have already been filled in, so when we follow a predecessor271// edge to a block, we know there will never be any more local variable definitions added to272// that block. We also know that `find_var` didn't find a definition for this variable in273// any of the blocks before `from`.274//275// So in either case there is no definition in these blocks yet and we can blindly set one.276let var_defs = &mut self.variables[var];277while block != from {278debug_assert!(var_defs[block].is_none());279var_defs[block] = PackedOption::from(val);280block = self.ssa_blocks[block].single_predecessor.unwrap();281}282}283284/// Find the most recent definition of this variable, returning both the definition and the285/// block in which it was found. If we can't find a definition that's provably the right one for286/// all paths to the current block, then append a block parameter to some block and use that as287/// the definition. Either way, also arrange that the definition will be on the `results` stack288/// when `run_state_machine` is done processing the current step.289///290/// If a block has exactly one predecessor, and the block is sealed so we know its predecessors291/// will never change, then its definition for this variable is the same as the definition from292/// that one predecessor. In this case it's easy to see that no block parameter is necessary,293/// but we need to look at the predecessor to see if a block parameter might be needed there.294/// That holds transitively across any chain of sealed blocks with exactly one predecessor each.295///296/// This runs into a problem, though, if such a chain has a cycle: Blindly following a cyclic297/// chain that never defines this variable would lead to an infinite loop in the compiler. It298/// doesn't really matter what code we generate in that case. Since each block in the cycle has299/// exactly one predecessor, there's no way to enter the cycle from the function's entry block;300/// and since all blocks in the cycle are sealed, the entire cycle is permanently dead code. But301/// we still have to prevent the possibility of an infinite loop.302///303/// To break cycles, we can pick any block within the cycle as the one where we'll add a block304/// parameter. It's convenient to pick the block at which we entered the cycle, because that's305/// the first place where we can detect that we just followed a cycle. Adding a block parameter306/// gives us a definition we can reuse throughout the rest of the cycle.307fn find_var(308&mut self,309func: &mut Function,310var: Variable,311ty: Type,312mut block: Block,313) -> (Value, Block) {314// Try to find an existing definition along single-predecessor edges first.315self.visited.clear();316let var_defs = &mut self.variables[var];317while let Some(pred) = self.ssa_blocks[block].single_predecessor.expand() {318if !self.visited.insert(block) {319break;320}321block = pred;322if let Some(val) = var_defs[block].expand() {323self.results.push(val);324return (val, block);325}326}327328// We've promised to return the most recent block where `var` was defined, but we didn't329// find a usable definition. So create one.330let val = func.dfg.append_block_param(block, ty);331var_defs[block] = PackedOption::from(val);332333// Now every predecessor needs to pass its definition of this variable to the newly added334// block parameter. To do that we have to "recursively" call `use_var`, but there are two335// problems with doing that. First, we need to keep a fixed bound on stack depth, so we336// can't actually recurse; instead we defer to `run_state_machine`. Second, if we don't337// know all our predecessors yet, we have to defer this work until the block gets sealed.338match &mut self.ssa_blocks[block].sealed {339// Once all the `calls` added here complete, this leaves either `val` or an equivalent340// definition on the `results` stack.341Sealed::Yes => self.begin_predecessors_lookup(val, block),342Sealed::No { undef_variables } => {343undef_variables.push(var, &mut self.variable_pool);344self.results.push(val);345}346}347(val, block)348}349350/// Declares a new basic block to construct corresponding data for SSA construction.351/// No predecessors are declared here and the block is not sealed.352/// Predecessors have to be added with `declare_block_predecessor`.353pub fn declare_block(&mut self, block: Block) {354// Ensure the block exists so seal_all_blocks will see it even if no predecessors or355// variables get declared for this block. But don't assign anything to it:356// SecondaryMap automatically sets all blocks to `default()`.357let _ = &mut self.ssa_blocks[block];358}359360/// Declares a new predecessor for a `Block` and record the branch instruction361/// of the predecessor that leads to it.362///363/// The precedent `Block` must be filled before added as predecessor.364/// Note that you must provide no jump arguments to the branch365/// instruction when you create it since `SSABuilder` will fill them for you.366///367/// Callers are expected to avoid adding the same predecessor more than once in the case368/// of a jump table.369pub fn declare_block_predecessor(&mut self, block: Block, inst: Inst) {370debug_assert!(!self.is_sealed(block));371self.ssa_blocks[block]372.predecessors373.push(inst, &mut self.inst_pool);374}375376/// Remove a previously declared Block predecessor by giving a reference to the jump377/// instruction. Returns the basic block containing the instruction.378///379/// Note: use only when you know what you are doing, this might break the SSA building problem380pub fn remove_block_predecessor(&mut self, block: Block, inst: Inst) {381debug_assert!(!self.is_sealed(block));382let data = &mut self.ssa_blocks[block];383let pred = data384.predecessors385.as_slice(&self.inst_pool)386.iter()387.position(|&branch| branch == inst)388.expect("the predecessor you are trying to remove is not declared");389data.predecessors.swap_remove(pred, &mut self.inst_pool);390}391392/// Completes the global value numbering for a `Block`, all of its predecessors having been393/// already sealed.394///395/// This method modifies the function's `Layout` by adding arguments to the `Block`s to396/// take into account the Phi function placed by the SSA algorithm.397///398/// Returns the list of newly created blocks for critical edge splitting.399pub fn seal_block(&mut self, block: Block, func: &mut Function) -> SideEffects {400debug_assert!(401!self.is_sealed(block),402"Attempting to seal {block} which is already sealed."403);404self.seal_one_block(block, func);405mem::take(&mut self.side_effects)406}407408/// Completes the global value numbering for all unsealed `Block`s in `func`.409///410/// It's more efficient to seal `Block`s as soon as possible, during411/// translation, but for frontends where this is impractical to do, this412/// function can be used at the end of translating all blocks to ensure413/// that everything is sealed.414pub fn seal_all_blocks(&mut self, func: &mut Function) -> SideEffects {415// Seal all `Block`s currently in the function. This can entail splitting416// and creation of new blocks, however such new blocks are sealed on417// the fly, so we don't need to account for them here.418for block in self.ssa_blocks.keys() {419self.seal_one_block(block, func);420}421mem::take(&mut self.side_effects)422}423424/// Helper function for `seal_block` and `seal_all_blocks`.425fn seal_one_block(&mut self, block: Block, func: &mut Function) {426// For each undef var we look up values in the predecessors and create a block parameter427// only if necessary.428let mut undef_variables =429match mem::replace(&mut self.ssa_blocks[block].sealed, Sealed::Yes) {430Sealed::No { undef_variables } => undef_variables,431Sealed::Yes => return,432};433let ssa_params = undef_variables.len(&self.variable_pool);434435let predecessors = self.predecessors(block);436if predecessors.len() == 1 {437let pred = func.layout.inst_block(predecessors[0]).unwrap();438self.ssa_blocks[block].single_predecessor = PackedOption::from(pred);439}440441// Note that begin_predecessors_lookup requires visiting these variables in the same order442// that they were defined by find_var, because it appends arguments to the jump instructions443// in all the predecessor blocks one variable at a time.444for idx in 0..ssa_params {445let var = undef_variables.get(idx, &self.variable_pool).unwrap();446447// We need the temporary Value that was assigned to this Variable. If that Value shows448// up as a result from any of our predecessors, then it never got assigned on the loop449// through that block. We get the value from the next block param, where it was first450// allocated in find_var.451let block_params = func.dfg.block_params(block);452453// On each iteration through this loop, there are (ssa_params - idx) undefined variables454// left to process. Previous iterations through the loop may have removed earlier block455// parameters, but the last (ssa_params - idx) block parameters always correspond to the456// remaining undefined variables. So index from the end of the current block params.457let val = block_params[block_params.len() - (ssa_params - idx)];458459debug_assert!(self.calls.is_empty());460debug_assert!(self.results.is_empty());461// self.side_effects may be non-empty here so that callers can462// accumulate side effects over multiple calls.463self.begin_predecessors_lookup(val, block);464self.run_state_machine(func, var, func.dfg.value_type(val));465}466467undef_variables.clear(&mut self.variable_pool);468}469470/// Given the local SSA Value of a Variable in a Block, perform a recursive lookup on471/// predecessors to determine if it is redundant with another Value earlier in the CFG.472///473/// If such a Value exists and is redundant, the local Value is replaced by the474/// corresponding non-local Value. If the original Value was a Block parameter,475/// the parameter may be removed if redundant. Parameters are placed eagerly by callers476/// to avoid infinite loops when looking up a Value for a Block that is in a CFG loop.477///478/// Doing this lookup for each Value in each Block preserves SSA form during construction.479///480/// ## Arguments481///482/// `sentinel` is a dummy Block parameter inserted by `use_var_nonlocal()`.483/// Its purpose is to allow detection of CFG cycles while traversing predecessors.484fn begin_predecessors_lookup(&mut self, sentinel: Value, dest_block: Block) {485self.calls486.push(Call::FinishPredecessorsLookup(sentinel, dest_block));487// Iterate over the predecessors.488self.calls.extend(489self.ssa_blocks[dest_block]490.predecessors491.as_slice(&self.inst_pool)492.iter()493.rev()494.copied()495.map(Call::UseVar),496);497}498499/// Examine the values from the predecessors and compute a result value, creating500/// block parameters as needed.501fn finish_predecessors_lookup(502&mut self,503func: &mut Function,504sentinel: Value,505dest_block: Block,506) -> Value {507// Determine how many predecessors are yielding unique, non-temporary Values. If a variable508// is live and unmodified across several control-flow join points, earlier blocks will509// introduce aliases for that variable's definition, so we resolve aliases eagerly here to510// ensure that we can tell when the same definition has reached this block via multiple511// paths. Doing so also detects cyclic references to the sentinel, which can occur in512// unreachable code.513let num_predecessors = self.predecessors(dest_block).len();514// When this `Drain` is dropped, these elements will get truncated.515let results = self.results.drain(self.results.len() - num_predecessors..);516517let pred_val = {518let mut iter = results519.as_slice()520.iter()521.map(|&val| func.dfg.resolve_aliases(val))522.filter(|&val| val != sentinel);523if let Some(val) = iter.next() {524// This variable has at least one non-temporary definition. If they're all the same525// value, we can remove the block parameter and reference that value instead.526if iter.all(|other| other == val) {527Some(val)528} else {529None530}531} else {532// The variable is used but never defined before. This is an irregularity in the533// code, but rather than throwing an error we silently initialize the variable to534// 0. This will have no effect since this situation happens in unreachable code.535if !func.layout.is_block_inserted(dest_block) {536func.layout.append_block(dest_block);537}538self.side_effects539.instructions_added_to_blocks540.push(dest_block);541let zero = emit_zero(542func.dfg.value_type(sentinel),543FuncCursor::new(func).at_first_insertion_point(dest_block),544);545Some(zero)546}547};548549if let Some(pred_val) = pred_val {550// Here all the predecessors use a single value to represent our variable551// so we don't need to have it as a block argument.552// We need to replace all the occurrences of val with pred_val but since553// we can't afford a re-writing pass right now we just declare an alias.554func.dfg.remove_block_param(sentinel);555func.dfg.change_to_alias(sentinel, pred_val);556pred_val557} else {558// There is disagreement in the predecessors on which value to use so we have559// to keep the block argument.560let mut preds = self.ssa_blocks[dest_block].predecessors;561let dfg = &mut func.stencil.dfg;562for (idx, &val) in results.as_slice().iter().enumerate() {563let pred = preds.get_mut(idx, &mut self.inst_pool).unwrap();564let branch = *pred;565566let dests = dfg.insts[branch]567.branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables);568assert!(569!dests.is_empty(),570"you have declared a non-branch instruction as a predecessor to a block!"571);572for block in dests {573if block.block(&dfg.value_lists) == dest_block {574block.append_argument(val, &mut dfg.value_lists);575}576}577}578sentinel579}580}581582/// Returns the list of `Block`s that have been declared as predecessors of the argument.583fn predecessors(&self, block: Block) -> &[Inst] {584self.ssa_blocks[block]585.predecessors586.as_slice(&self.inst_pool)587}588589/// Returns whether the given Block has any predecessor or not.590pub fn has_any_predecessors(&self, block: Block) -> bool {591!self.predecessors(block).is_empty()592}593594/// Returns `true` if and only if `seal_block` has been called on the argument.595pub fn is_sealed(&self, block: Block) -> bool {596matches!(self.ssa_blocks[block].sealed, Sealed::Yes)597}598599/// The main algorithm is naturally recursive: when there's a `use_var` in a600/// block with no corresponding local defs, it recurses and performs a601/// `use_var` in each predecessor. To avoid risking running out of callstack602/// space, we keep an explicit stack and use a small state machine rather603/// than literal recursion.604fn run_state_machine(&mut self, func: &mut Function, var: Variable, ty: Type) -> Value {605// Process the calls scheduled in `self.calls` until it is empty.606while let Some(call) = self.calls.pop() {607match call {608Call::UseVar(branch) => {609let block = func.layout.inst_block(branch).unwrap();610self.use_var_nonlocal(func, var, ty, block);611}612Call::FinishPredecessorsLookup(sentinel, dest_block) => {613let val = self.finish_predecessors_lookup(func, sentinel, dest_block);614self.results.push(val);615}616}617}618debug_assert_eq!(self.results.len(), 1);619self.results.pop().unwrap()620}621}622623#[cfg(test)]624mod tests {625use crate::Variable;626use crate::ssa::SSABuilder;627use cranelift_codegen::cursor::{Cursor, FuncCursor};628use cranelift_codegen::entity::EntityRef;629use cranelift_codegen::ir;630use cranelift_codegen::ir::types::*;631use cranelift_codegen::ir::{Function, Inst, InstBuilder, JumpTableData, Opcode};632use cranelift_codegen::settings;633use cranelift_codegen::verify_function;634635#[test]636fn simple_block() {637let mut func = Function::new();638let mut ssa = SSABuilder::default();639let block0 = func.dfg.make_block();640// Here is the pseudo-program we want to translate:641// block0:642// x = 1;643// y = 2;644// z = x + y;645// z = x + z;646647ssa.declare_block(block0);648let x_var = Variable::new(0);649let x_ssa = {650let mut cur = FuncCursor::new(&mut func);651cur.insert_block(block0);652cur.ins().iconst(I32, 1)653};654ssa.def_var(x_var, x_ssa, block0);655let y_var = Variable::new(1);656let y_ssa = {657let mut cur = FuncCursor::new(&mut func).at_bottom(block0);658cur.ins().iconst(I32, 2)659};660ssa.def_var(y_var, y_ssa, block0);661assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa);662assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa);663664let z_var = Variable::new(2);665let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0;666let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0;667let z1_ssa = {668let mut cur = FuncCursor::new(&mut func).at_bottom(block0);669cur.ins().iadd(x_use1, y_use1)670};671ssa.def_var(z_var, z1_ssa, block0);672assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa);673674let x_use2 = ssa.use_var(&mut func, x_var, I32, block0).0;675let z_use1 = ssa.use_var(&mut func, z_var, I32, block0).0;676let z2_ssa = {677let mut cur = FuncCursor::new(&mut func).at_bottom(block0);678cur.ins().iadd(x_use2, z_use1)679};680ssa.def_var(z_var, z2_ssa, block0);681assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z2_ssa);682}683684#[test]685fn sequence_of_blocks() {686let mut func = Function::new();687let mut ssa = SSABuilder::default();688let block0 = func.dfg.make_block();689let block1 = func.dfg.make_block();690let block2 = func.dfg.make_block();691// Here is the pseudo-program we want to translate:692// block0:693// x = 1;694// y = 2;695// z = x + y;696// brif y, block1, block1;697// block1:698// z = x + z;699// jump block2;700// block2:701// y = x + y;702{703let mut cur = FuncCursor::new(&mut func);704cur.insert_block(block0);705cur.insert_block(block1);706cur.insert_block(block2);707}708709// block0710ssa.declare_block(block0);711ssa.seal_block(block0, &mut func);712let x_var = Variable::new(0);713let x_ssa = {714let mut cur = FuncCursor::new(&mut func).at_bottom(block0);715cur.ins().iconst(I32, 1)716};717ssa.def_var(x_var, x_ssa, block0);718let y_var = Variable::new(1);719let y_ssa = {720let mut cur = FuncCursor::new(&mut func).at_bottom(block0);721cur.ins().iconst(I32, 2)722};723ssa.def_var(y_var, y_ssa, block0);724let z_var = Variable::new(2);725let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0;726let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0;727let z1_ssa = {728let mut cur = FuncCursor::new(&mut func).at_bottom(block0);729cur.ins().iadd(x_use1, y_use1)730};731ssa.def_var(z_var, z1_ssa, block0);732let y_use2 = ssa.use_var(&mut func, y_var, I32, block0).0;733let brif_block0_block2_block1: Inst = {734let mut cur = FuncCursor::new(&mut func).at_bottom(block0);735cur.ins().brif(y_use2, block2, &[], block1, &[])736};737738assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa);739assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa);740assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa);741742// block1743ssa.declare_block(block1);744ssa.declare_block_predecessor(block1, brif_block0_block2_block1);745ssa.seal_block(block1, &mut func);746747let x_use2 = ssa.use_var(&mut func, x_var, I32, block1).0;748let z_use1 = ssa.use_var(&mut func, z_var, I32, block1).0;749let z2_ssa = {750let mut cur = FuncCursor::new(&mut func).at_bottom(block1);751cur.ins().iadd(x_use2, z_use1)752};753ssa.def_var(z_var, z2_ssa, block1);754let jump_block1_block2: Inst = {755let mut cur = FuncCursor::new(&mut func).at_bottom(block1);756cur.ins().jump(block2, &[])757};758759assert_eq!(x_use2, x_ssa);760assert_eq!(z_use1, z1_ssa);761assert_eq!(ssa.use_var(&mut func, z_var, I32, block1).0, z2_ssa);762763// block2764ssa.declare_block(block2);765ssa.declare_block_predecessor(block2, brif_block0_block2_block1);766ssa.declare_block_predecessor(block2, jump_block1_block2);767ssa.seal_block(block2, &mut func);768let x_use3 = ssa.use_var(&mut func, x_var, I32, block2).0;769let y_use3 = ssa.use_var(&mut func, y_var, I32, block2).0;770let y2_ssa = {771let mut cur = FuncCursor::new(&mut func).at_bottom(block2);772cur.ins().iadd(x_use3, y_use3)773};774ssa.def_var(y_var, y2_ssa, block2);775776assert_eq!(x_ssa, x_use3);777assert_eq!(y_ssa, y_use3);778match func.dfg.insts[brif_block0_block2_block1] {779ir::InstructionData::Brif {780blocks: [block_then, block_else],781..782} => {783assert_eq!(block_then.block(&func.dfg.value_lists), block2);784assert_eq!(block_then.args(&func.dfg.value_lists).len(), 0);785assert_eq!(block_else.block(&func.dfg.value_lists), block1);786assert_eq!(block_else.args(&func.dfg.value_lists).len(), 0);787}788_ => assert!(false),789};790match func.dfg.insts[brif_block0_block2_block1] {791ir::InstructionData::Brif {792blocks: [block_then, block_else],793..794} => {795assert_eq!(block_then.block(&func.dfg.value_lists), block2);796assert_eq!(block_then.args(&func.dfg.value_lists).len(), 0);797assert_eq!(block_else.block(&func.dfg.value_lists), block1);798assert_eq!(block_else.args(&func.dfg.value_lists).len(), 0);799}800_ => assert!(false),801};802match func.dfg.insts[jump_block1_block2] {803ir::InstructionData::Jump {804destination: dest, ..805} => {806assert_eq!(dest.block(&func.dfg.value_lists), block2);807assert_eq!(dest.args(&func.dfg.value_lists).len(), 0);808}809_ => assert!(false),810};811}812813#[test]814fn program_with_loop() {815let mut func = Function::new();816let mut ssa = SSABuilder::default();817let block0 = func.dfg.make_block();818let block1 = func.dfg.make_block();819let block2 = func.dfg.make_block();820let block3 = func.dfg.make_block();821{822let mut cur = FuncCursor::new(&mut func);823cur.insert_block(block0);824cur.insert_block(block1);825cur.insert_block(block2);826cur.insert_block(block3);827}828// Here is the pseudo-program we want to translate:829// block0:830// x = 1;831// y = 2;832// z = x + y;833// jump block1834// block1:835// z = z + y;836// brif y, block3, block2;837// block2:838// z = z - x;839// return y840// block3:841// y = y - x842// jump block1843844// block0845ssa.declare_block(block0);846ssa.seal_block(block0, &mut func);847let x_var = Variable::new(0);848let x1 = {849let mut cur = FuncCursor::new(&mut func).at_bottom(block0);850cur.ins().iconst(I32, 1)851};852ssa.def_var(x_var, x1, block0);853let y_var = Variable::new(1);854let y1 = {855let mut cur = FuncCursor::new(&mut func).at_bottom(block0);856cur.ins().iconst(I32, 2)857};858ssa.def_var(y_var, y1, block0);859let z_var = Variable::new(2);860let x2 = ssa.use_var(&mut func, x_var, I32, block0).0;861let y2 = ssa.use_var(&mut func, y_var, I32, block0).0;862let z1 = {863let mut cur = FuncCursor::new(&mut func).at_bottom(block0);864cur.ins().iadd(x2, y2)865};866ssa.def_var(z_var, z1, block0);867let jump_block0_block1 = {868let mut cur = FuncCursor::new(&mut func).at_bottom(block0);869cur.ins().jump(block1, &[])870};871assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x1);872assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y1);873assert_eq!(x2, x1);874assert_eq!(y2, y1);875876// block1877ssa.declare_block(block1);878ssa.declare_block_predecessor(block1, jump_block0_block1);879let z2 = ssa.use_var(&mut func, z_var, I32, block1).0;880let y3 = ssa.use_var(&mut func, y_var, I32, block1).0;881let z3 = {882let mut cur = FuncCursor::new(&mut func).at_bottom(block1);883cur.ins().iadd(z2, y3)884};885ssa.def_var(z_var, z3, block1);886let y4 = ssa.use_var(&mut func, y_var, I32, block1).0;887assert_eq!(y4, y3);888let brif_block1_block3_block2 = {889let mut cur = FuncCursor::new(&mut func).at_bottom(block1);890cur.ins().brif(y4, block3, &[], block2, &[])891};892893// block2894ssa.declare_block(block2);895ssa.declare_block_predecessor(block2, brif_block1_block3_block2);896ssa.seal_block(block2, &mut func);897let z4 = ssa.use_var(&mut func, z_var, I32, block2).0;898assert_eq!(z4, z3);899let x3 = ssa.use_var(&mut func, x_var, I32, block2).0;900let z5 = {901let mut cur = FuncCursor::new(&mut func).at_bottom(block2);902cur.ins().isub(z4, x3)903};904ssa.def_var(z_var, z5, block2);905let y5 = ssa.use_var(&mut func, y_var, I32, block2).0;906assert_eq!(y5, y3);907{908let mut cur = FuncCursor::new(&mut func).at_bottom(block2);909cur.ins().return_(&[y5])910};911912// block3913ssa.declare_block(block3);914ssa.declare_block_predecessor(block3, brif_block1_block3_block2);915ssa.seal_block(block3, &mut func);916let y6 = ssa.use_var(&mut func, y_var, I32, block3).0;917assert_eq!(y6, y3);918let x4 = ssa.use_var(&mut func, x_var, I32, block3).0;919assert_eq!(x4, x3);920let y7 = {921let mut cur = FuncCursor::new(&mut func).at_bottom(block3);922cur.ins().isub(y6, x4)923};924ssa.def_var(y_var, y7, block3);925let jump_block3_block1 = {926let mut cur = FuncCursor::new(&mut func).at_bottom(block3);927cur.ins().jump(block1, &[])928};929930// block1 after all predecessors have been visited.931ssa.declare_block_predecessor(block1, jump_block3_block1);932ssa.seal_block(block1, &mut func);933assert_eq!(func.dfg.block_params(block1)[0], z2);934assert_eq!(func.dfg.block_params(block1)[1], y3);935assert_eq!(func.dfg.resolve_aliases(x3), x1);936}937938#[test]939fn br_table_with_args() {940// This tests the on-demand splitting of critical edges for br_table with jump arguments941//942// Here is the pseudo-program we want to translate:943//944// function %f {945// block0:946// x = 1;947// br_table x, block2, [block2, block1]948// block1:949// x = 2950// jump block2951// block2:952// x = x + 1953// return954// }955956let mut func = Function::new();957let mut ssa = SSABuilder::default();958let block0 = func.dfg.make_block();959let block1 = func.dfg.make_block();960let block2 = func.dfg.make_block();961{962let mut cur = FuncCursor::new(&mut func);963cur.insert_block(block0);964cur.insert_block(block1);965cur.insert_block(block2);966}967968// block0969let x1 = {970let mut cur = FuncCursor::new(&mut func).at_bottom(block0);971cur.ins().iconst(I32, 1)972};973ssa.declare_block(block0);974ssa.seal_block(block0, &mut func);975let x_var = Variable::new(0);976ssa.def_var(x_var, x1, block0);977ssa.use_var(&mut func, x_var, I32, block0).0;978let br_table = {979let jump_table = JumpTableData::new(980func.dfg.block_call(block2, &[]),981&[982func.dfg.block_call(block2, &[]),983func.dfg.block_call(block1, &[]),984],985);986let jt = func.create_jump_table(jump_table);987let mut cur = FuncCursor::new(&mut func).at_bottom(block0);988cur.ins().br_table(x1, jt)989};990991// block1992ssa.declare_block(block1);993ssa.declare_block_predecessor(block1, br_table);994ssa.seal_block(block1, &mut func);995let x2 = {996let mut cur = FuncCursor::new(&mut func).at_bottom(block1);997cur.ins().iconst(I32, 2)998};999ssa.def_var(x_var, x2, block1);1000let jump_block1_block2 = {1001let mut cur = FuncCursor::new(&mut func).at_bottom(block1);1002cur.ins().jump(block2, &[])1003};10041005// block21006ssa.declare_block(block2);1007ssa.declare_block_predecessor(block2, jump_block1_block2);1008ssa.declare_block_predecessor(block2, br_table);1009ssa.seal_block(block2, &mut func);1010let x3 = ssa.use_var(&mut func, x_var, I32, block2).0;1011let x4 = {1012let mut cur = FuncCursor::new(&mut func).at_bottom(block2);1013cur.ins().iadd_imm(x3, 1)1014};1015ssa.def_var(x_var, x4, block2);1016{1017let mut cur = FuncCursor::new(&mut func).at_bottom(block2);1018cur.ins().return_(&[])1019};10201021let flags = settings::Flags::new(settings::builder());1022match verify_function(&func, &flags) {1023Ok(()) => {}1024Err(_errors) => {1025#[cfg(feature = "std")]1026panic!("{}", _errors);1027#[cfg(not(feature = "std"))]1028panic!("function failed to verify");1029}1030}1031}10321033#[test]1034fn undef_values_reordering() {1035// Here is the pseudo-program we want to translate:1036// block0:1037// x = 0;1038// y = 1;1039// z = 2;1040// jump block1;1041// block1:1042// x = z + x;1043// y = y - x;1044// jump block1;1045//1046let mut func = Function::new();1047let mut ssa = SSABuilder::default();1048let block0 = func.dfg.make_block();1049let block1 = func.dfg.make_block();1050{1051let mut cur = FuncCursor::new(&mut func);1052cur.insert_block(block0);1053cur.insert_block(block1);1054}10551056// block01057ssa.declare_block(block0);1058let x_var = Variable::new(0);1059ssa.seal_block(block0, &mut func);1060let x1 = {1061let mut cur = FuncCursor::new(&mut func).at_bottom(block0);1062cur.ins().iconst(I32, 0)1063};1064ssa.def_var(x_var, x1, block0);1065let y_var = Variable::new(1);1066let y1 = {1067let mut cur = FuncCursor::new(&mut func).at_bottom(block0);1068cur.ins().iconst(I32, 1)1069};1070ssa.def_var(y_var, y1, block0);1071let z_var = Variable::new(2);1072let z1 = {1073let mut cur = FuncCursor::new(&mut func).at_bottom(block0);1074cur.ins().iconst(I32, 2)1075};1076ssa.def_var(z_var, z1, block0);1077let jump_block0_block1 = {1078let mut cur = FuncCursor::new(&mut func).at_bottom(block0);1079cur.ins().jump(block1, &[])1080};10811082// block11083ssa.declare_block(block1);1084ssa.declare_block_predecessor(block1, jump_block0_block1);1085let z2 = ssa.use_var(&mut func, z_var, I32, block1).0;1086assert_eq!(func.dfg.block_params(block1)[0], z2);1087let x2 = ssa.use_var(&mut func, x_var, I32, block1).0;1088assert_eq!(func.dfg.block_params(block1)[1], x2);1089let x3 = {1090let mut cur = FuncCursor::new(&mut func).at_bottom(block1);1091cur.ins().iadd(x2, z2)1092};1093ssa.def_var(x_var, x3, block1);1094let x4 = ssa.use_var(&mut func, x_var, I32, block1).0;1095let y3 = ssa.use_var(&mut func, y_var, I32, block1).0;1096assert_eq!(func.dfg.block_params(block1)[2], y3);1097let y4 = {1098let mut cur = FuncCursor::new(&mut func).at_bottom(block1);1099cur.ins().isub(y3, x4)1100};1101ssa.def_var(y_var, y4, block1);1102let jump_block1_block1 = {1103let mut cur = FuncCursor::new(&mut func).at_bottom(block1);1104cur.ins().jump(block1, &[])1105};1106ssa.declare_block_predecessor(block1, jump_block1_block1);1107ssa.seal_block(block1, &mut func);1108// At sealing the "z" argument disappear but the remaining "x" and "y" args have to be1109// in the right order.1110assert_eq!(func.dfg.block_params(block1)[1], y3);1111assert_eq!(func.dfg.block_params(block1)[0], x2);1112}11131114#[test]1115fn undef() {1116// Use vars of various types which have not been defined.1117let mut func = Function::new();1118let mut ssa = SSABuilder::default();1119let block0 = func.dfg.make_block();1120ssa.declare_block(block0);1121ssa.seal_block(block0, &mut func);1122let i32_var = Variable::new(0);1123let f32_var = Variable::new(1);1124let f64_var = Variable::new(2);1125let i8_var = Variable::new(3);1126let f32x4_var = Variable::new(4);1127ssa.use_var(&mut func, i32_var, I32, block0);1128ssa.use_var(&mut func, f32_var, F32, block0);1129ssa.use_var(&mut func, f64_var, F64, block0);1130ssa.use_var(&mut func, i8_var, I8, block0);1131ssa.use_var(&mut func, f32x4_var, F32X4, block0);1132assert_eq!(func.dfg.num_block_params(block0), 0);1133}11341135#[test]1136fn undef_in_entry() {1137// Use a var which has not been defined. The search should hit the1138// top of the entry block, and then fall back to inserting an iconst.1139let mut func = Function::new();1140let mut ssa = SSABuilder::default();1141let block0 = func.dfg.make_block();1142ssa.declare_block(block0);1143ssa.seal_block(block0, &mut func);1144let x_var = Variable::new(0);1145assert_eq!(func.dfg.num_block_params(block0), 0);1146ssa.use_var(&mut func, x_var, I32, block0);1147assert_eq!(func.dfg.num_block_params(block0), 0);1148assert_eq!(1149func.dfg.insts[func.layout.first_inst(block0).unwrap()].opcode(),1150Opcode::Iconst1151);1152}11531154#[test]1155fn undef_in_entry_sealed_after() {1156// Use a var which has not been defined, but the block is not sealed1157// until afterward. Before sealing, the SSA builder should insert an1158// block param; after sealing, it should be removed.1159let mut func = Function::new();1160let mut ssa = SSABuilder::default();1161let block0 = func.dfg.make_block();1162ssa.declare_block(block0);1163let x_var = Variable::new(0);1164assert_eq!(func.dfg.num_block_params(block0), 0);1165ssa.use_var(&mut func, x_var, I32, block0);1166assert_eq!(func.dfg.num_block_params(block0), 1);1167ssa.seal_block(block0, &mut func);1168assert_eq!(func.dfg.num_block_params(block0), 0);1169assert_eq!(1170func.dfg.insts[func.layout.first_inst(block0).unwrap()].opcode(),1171Opcode::Iconst1172);1173}11741175#[test]1176fn unreachable_use() {1177// Here is the pseudo-program we want to translate:1178// block0:1179// return;1180// block1:1181// brif x, block1, block1;1182let mut func = Function::new();1183let mut ssa = SSABuilder::default();1184let block0 = func.dfg.make_block();1185let block1 = func.dfg.make_block();1186{1187let mut cur = FuncCursor::new(&mut func);1188cur.insert_block(block0);1189cur.insert_block(block1);1190}11911192// block01193ssa.declare_block(block0);1194ssa.seal_block(block0, &mut func);1195{1196let mut cur = FuncCursor::new(&mut func).at_bottom(block0);1197cur.ins().return_(&[]);1198}11991200// block11201ssa.declare_block(block1);1202{1203let mut cur = FuncCursor::new(&mut func).at_bottom(block1);1204let x_var = Variable::new(0);1205let x_val = ssa.use_var(&mut cur.func, x_var, I32, block1).0;1206let brif = cur.ins().brif(x_val, block1, &[], block1, &[]);1207ssa.declare_block_predecessor(block1, brif);1208}1209ssa.seal_block(block1, &mut func);12101211let flags = settings::Flags::new(settings::builder());1212match verify_function(&func, &flags) {1213Ok(()) => {}1214Err(_errors) => {1215#[cfg(feature = "std")]1216panic!("{}", _errors);1217#[cfg(not(feature = "std"))]1218panic!("function failed to verify");1219}1220}1221}12221223#[test]1224fn unreachable_use_with_multiple_preds() {1225// Here is the pseudo-program we want to translate:1226// block0:1227// return;1228// block1:1229// brif x, block1, block2;1230// block2:1231// jump block1;1232let mut func = Function::new();1233let mut ssa = SSABuilder::default();1234let block0 = func.dfg.make_block();1235let block1 = func.dfg.make_block();1236let block2 = func.dfg.make_block();1237{1238let mut cur = FuncCursor::new(&mut func);1239cur.insert_block(block0);1240cur.insert_block(block1);1241cur.insert_block(block2);1242}12431244// block01245ssa.declare_block(block0);1246ssa.seal_block(block0, &mut func);1247{1248let mut cur = FuncCursor::new(&mut func).at_bottom(block0);1249cur.ins().return_(&[]);1250}12511252// block11253ssa.declare_block(block1);1254let brif = {1255let mut cur = FuncCursor::new(&mut func).at_bottom(block1);1256let x_var = Variable::new(0);1257let x_val = ssa.use_var(&mut cur.func, x_var, I32, block1).0;1258cur.ins().brif(x_val, block2, &[], block1, &[])1259};12601261// block21262ssa.declare_block(block2);1263ssa.declare_block_predecessor(block1, brif);1264ssa.declare_block_predecessor(block2, brif);1265ssa.seal_block(block2, &mut func);1266let jump_block2_block1 = {1267let mut cur = FuncCursor::new(&mut func).at_bottom(block2);1268cur.ins().jump(block1, &[])1269};12701271// seal block11272ssa.declare_block_predecessor(block1, jump_block2_block1);1273ssa.seal_block(block1, &mut func);1274let flags = settings::Flags::new(settings::builder());1275match verify_function(&func, &flags) {1276Ok(()) => {}1277Err(_errors) => {1278#[cfg(feature = "std")]1279panic!("{}", _errors);1280#[cfg(not(feature = "std"))]1281panic!("function failed to verify");1282}1283}1284}12851286#[test]1287fn reassign_with_predecessor_loop_hangs() {1288// Here is the pseudo-program we want to translate:1289// block0:1290// var0 = iconst 01291// return;1292// block1:1293// jump block2;1294// block2:1295// ; phantom use of var01296// var0 = iconst 11297// jump block1;12981299let mut func = Function::new();1300let mut ssa = SSABuilder::default();1301let block0 = func.dfg.make_block();1302let block1 = func.dfg.make_block();1303let block2 = func.dfg.make_block();1304let var0 = Variable::new(0);13051306{1307let mut cur = FuncCursor::new(&mut func);1308for block in [block0, block1, block2] {1309cur.insert_block(block);1310ssa.declare_block(block);1311}1312}13131314// block01315{1316let mut cur = FuncCursor::new(&mut func).at_bottom(block0);13171318let var0_iconst = cur.ins().iconst(I32, 0);1319ssa.def_var(var0, var0_iconst, block0);13201321cur.ins().return_(&[]);1322}13231324// block11325{1326let mut cur = FuncCursor::new(&mut func).at_bottom(block1);13271328let jump = cur.ins().jump(block2, &[]);1329ssa.declare_block_predecessor(block2, jump);1330}13311332// block21333{1334let mut cur = FuncCursor::new(&mut func).at_bottom(block2);13351336let _ = ssa.use_var(&mut cur.func, var0, I32, block2).0;1337let var0_iconst = cur.ins().iconst(I32, 1);1338ssa.def_var(var0, var0_iconst, block2);13391340let jump = cur.ins().jump(block1, &[]);1341ssa.declare_block_predecessor(block1, jump);1342}13431344// The sealing algorithm would enter a infinite loop here1345ssa.seal_all_blocks(&mut func);1346}1347}134813491350