1 //! A SSA-building API that handles incomplete CFGs.
2 //!
3 //! The algorithm is based upon Braun M., Buchwald S., Hack S., Leißa R., Mallon C.,
4 //! Zwinkau A. (2013) Simple and Efficient Construction of Static Single Assignment Form.
5 //! In: Jhala R., De Bosschere K. (eds) Compiler Construction. CC 2013.
6 //! Lecture Notes in Computer Science, vol 7791. Springer, Berlin, Heidelberg
7 //!
8 //! <https://link.springer.com/content/pdf/10.1007/978-3-642-37051-9_6.pdf>
9
10 use crate::Variable;
11 use alloc::vec::Vec;
12 use core::mem;
13 use cranelift_codegen::cursor::{Cursor, FuncCursor};
14 use cranelift_codegen::entity::{EntityList, EntitySet, ListPool, SecondaryMap};
15 use cranelift_codegen::ir::immediates::{Ieee16, Ieee32, Ieee64, Ieee128};
16 use cranelift_codegen::ir::types::{F16, F32, F64, F128, I64, I128};
17 use cranelift_codegen::ir::{Block, Function, Inst, InstBuilder, Type, Value};
18 use cranelift_codegen::packed_option::PackedOption;
19
20 /// Structure containing the data relevant the construction of SSA for a given function.
21 ///
22 /// The parameter struct [`Variable`] corresponds to the way variables are represented in the
23 /// non-SSA language you're translating from.
24 ///
25 /// The SSA building relies on information about the variables used and defined.
26 ///
27 /// This SSA building module allows you to def and use variables on the fly while you are
28 /// constructing the CFG, no need for a separate SSA pass after the CFG is completed.
29 ///
30 /// A basic block is said _filled_ if all the instruction that it contains have been translated,
31 /// and it is said _sealed_ if all of its predecessors have been declared. Only filled predecessors
32 /// can be declared.
33 #[derive(Default)]
34 pub struct SSABuilder {
35 // TODO: Consider a sparse representation rather than SecondaryMap-of-SecondaryMap.
36 /// Records for every variable and for every relevant block, the last definition of
37 /// the variable in the block.
38 variables: SecondaryMap<Variable, SecondaryMap<Block, PackedOption<Value>>>,
39
40 /// Records the position of the basic blocks and the list of values used but not defined in the
41 /// block.
42 ssa_blocks: SecondaryMap<Block, SSABlockData>,
43
44 /// Call stack for use in the `use_var`/`predecessors_lookup` state machine.
45 calls: Vec<Call>,
46 /// Result stack for use in the `use_var`/`predecessors_lookup` state machine.
47 results: Vec<Value>,
48
49 /// Side effects accumulated in the `use_var`/`predecessors_lookup` state machine.
50 side_effects: SideEffects,
51
52 /// Reused storage for cycle-detection.
53 visited: EntitySet<Block>,
54
55 /// Storage for pending variable definitions.
56 variable_pool: ListPool<Variable>,
57
58 /// Storage for predecessor definitions.
59 inst_pool: ListPool<Inst>,
60 }
61
62 /// Side effects of a `use_var` or a `seal_block` method call.
63 #[derive(Default)]
64 pub struct SideEffects {
65 /// When a variable is used but has never been defined before (this happens in the case of
66 /// unreachable code), a placeholder `iconst` or `fconst` value is added to the right `Block`.
67 /// This field signals if it is the case and return the `Block` to which the initialization has
68 /// been added.
69 pub instructions_added_to_blocks: Vec<Block>,
70 }
71
72 impl SideEffects {
is_empty(&self) -> bool73 fn is_empty(&self) -> bool {
74 let Self {
75 instructions_added_to_blocks,
76 } = self;
77 instructions_added_to_blocks.is_empty()
78 }
79 }
80
81 #[derive(Clone)]
82 enum Sealed {
83 No {
84 // List of current Block arguments for which an earlier def has not been found yet.
85 undef_variables: EntityList<Variable>,
86 },
87 Yes,
88 }
89
90 impl Default for Sealed {
default() -> Self91 fn default() -> Self {
92 Sealed::No {
93 undef_variables: EntityList::new(),
94 }
95 }
96 }
97
98 #[derive(Clone, Default)]
99 struct SSABlockData {
100 // The predecessors of the Block with the block and branch instruction.
101 predecessors: EntityList<Inst>,
102 // A block is sealed if all of its predecessors have been declared.
103 sealed: Sealed,
104 // If this block is sealed and it has exactly one predecessor, this is that predecessor.
105 single_predecessor: PackedOption<Block>,
106 }
107
108 impl SSABuilder {
109 /// Clears a `SSABuilder` from all its data, letting it in a pristine state without
110 /// deallocating memory.
clear(&mut self)111 pub fn clear(&mut self) {
112 self.variables.clear();
113 self.ssa_blocks.clear();
114 self.variable_pool.clear();
115 self.inst_pool.clear();
116 debug_assert!(self.calls.is_empty());
117 debug_assert!(self.results.is_empty());
118 debug_assert!(self.side_effects.is_empty());
119 }
120
121 /// Tests whether an `SSABuilder` is in a cleared state.
is_empty(&self) -> bool122 pub fn is_empty(&self) -> bool {
123 self.variables.is_empty()
124 && self.ssa_blocks.is_empty()
125 && self.calls.is_empty()
126 && self.results.is_empty()
127 && self.side_effects.is_empty()
128 }
129 }
130
131 /// States for the `use_var`/`predecessors_lookup` state machine.
132 enum Call {
133 UseVar(Inst),
134 FinishPredecessorsLookup(Value, Block),
135 }
136
137 /// Emit instructions to produce a zero value in the given type.
emit_zero(ty: Type, mut cur: FuncCursor) -> Value138 fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value {
139 match ty {
140 I128 => {
141 let zero = cur.ins().iconst(I64, 0);
142 cur.ins().uextend(I128, zero)
143 }
144 ty if ty.is_int() => cur.ins().iconst(ty, 0),
145 F16 => cur.ins().f16const(Ieee16::with_bits(0)),
146 F32 => cur.ins().f32const(Ieee32::with_bits(0)),
147 F64 => cur.ins().f64const(Ieee64::with_bits(0)),
148 F128 => {
149 let zero = cur.func.dfg.constants.insert(Ieee128::with_bits(0).into());
150 cur.ins().f128const(zero)
151 }
152 ty if ty.is_vector() => match ty.lane_type() {
153 scalar_ty if scalar_ty.is_int() => {
154 let zero = cur
155 .func
156 .dfg
157 .constants
158 .insert(vec![0; ty.bytes().try_into().unwrap()].into());
159 cur.ins().vconst(ty, zero)
160 }
161 F16 => {
162 let scalar = cur.ins().f16const(Ieee16::with_bits(0));
163 cur.ins().splat(ty, scalar)
164 }
165 F32 => {
166 let scalar = cur.ins().f32const(Ieee32::with_bits(0));
167 cur.ins().splat(ty, scalar)
168 }
169 F64 => {
170 let scalar = cur.ins().f64const(Ieee64::with_bits(0));
171 cur.ins().splat(ty, scalar)
172 }
173 F128 => {
174 let zero = cur.func.dfg.constants.insert(Ieee128::with_bits(0).into());
175 let scalar = cur.ins().f128const(zero);
176 cur.ins().splat(ty, scalar)
177 }
178 _ => panic!("unimplemented scalar type: {ty:?}"),
179 },
180 ty => panic!("unimplemented type: {ty:?}"),
181 }
182 }
183
184 /// The following methods are the API of the SSA builder. Here is how it should be used when
185 /// translating to Cranelift IR:
186 ///
187 /// - for each basic block, create a corresponding data for SSA construction with `declare_block`;
188 ///
189 /// - while traversing a basic block and translating instruction, use `def_var` and `use_var`
190 /// to record definitions and uses of variables, these methods will give you the corresponding
191 /// SSA values;
192 ///
193 /// - when all the instructions in a basic block have translated, the block is said _filled_ and
194 /// only then you can add it as a predecessor to other blocks with `declare_block_predecessor`;
195 ///
196 /// - when you have constructed all the predecessor to a basic block,
197 /// call `seal_block` on it with the `Function` that you are building.
198 ///
199 /// This API will give you the correct SSA values to use as arguments of your instructions,
200 /// as well as modify the jump instruction and `Block` parameters to account for the SSA
201 /// Phi functions.
202 ///
203 impl SSABuilder {
204 /// Get all of the values associated with the given variable that we have
205 /// inserted in the function thus far.
values_for_var(&self, var: Variable) -> impl Iterator<Item = Value> + '_206 pub fn values_for_var(&self, var: Variable) -> impl Iterator<Item = Value> + '_ {
207 self.variables[var].values().filter_map(|v| v.expand())
208 }
209
210 /// Declares a new definition of a variable in a given basic block.
211 /// The SSA value is passed as an argument because it should be created with
212 /// `ir::DataFlowGraph::append_result`.
def_var(&mut self, var: Variable, val: Value, block: Block)213 pub fn def_var(&mut self, var: Variable, val: Value, block: Block) {
214 self.variables[var][block] = PackedOption::from(val);
215 }
216
217 /// Declares a use of a variable in a given basic block. Returns the SSA value corresponding
218 /// to the current SSA definition of this variable and a list of newly created Blocks that
219 /// are the results of critical edge splitting for `br_table` with arguments.
220 ///
221 /// If the variable has never been defined in this blocks or recursively in its predecessors,
222 /// this method will silently create an initializer with `iconst` or `fconst`. You are
223 /// responsible for making sure that you initialize your variables.
use_var( &mut self, func: &mut Function, var: Variable, ty: Type, block: Block, ) -> (Value, SideEffects)224 pub fn use_var(
225 &mut self,
226 func: &mut Function,
227 var: Variable,
228 ty: Type,
229 block: Block,
230 ) -> (Value, SideEffects) {
231 debug_assert!(self.calls.is_empty());
232 debug_assert!(self.results.is_empty());
233 debug_assert!(self.side_effects.is_empty());
234
235 // Prepare the 'calls' and 'results' stacks for the state machine.
236 self.use_var_nonlocal(func, var, ty, block);
237 let value = self.run_state_machine(func, var, ty);
238
239 let side_effects = mem::take(&mut self.side_effects);
240 (value, side_effects)
241 }
242
243 /// Resolve the minimal SSA Value of `var` in `block` by traversing predecessors.
244 ///
245 /// This function sets up state for `run_state_machine()` but does not execute it.
use_var_nonlocal(&mut self, func: &mut Function, var: Variable, ty: Type, mut block: Block)246 fn use_var_nonlocal(&mut self, func: &mut Function, var: Variable, ty: Type, mut block: Block) {
247 // First, try Local Value Numbering (Algorithm 1 in the paper).
248 // If the variable already has a known Value in this block, use that.
249 if let Some(val) = self.variables[var][block].expand() {
250 self.results.push(val);
251 return;
252 }
253
254 // Otherwise, use Global Value Numbering (Algorithm 2 in the paper).
255 // This resolves the Value with respect to its predecessors.
256 // Find the most recent definition of `var`, and the block the definition comes from.
257 let (val, from) = self.find_var(func, var, ty, block);
258
259 // The `from` block returned from `find_var` is guaranteed to be on the path we follow by
260 // traversing only single-predecessor edges. It might be equal to `block` if there is no
261 // such path, but in that case `find_var` ensures that the variable is defined in this block
262 // by a new block parameter. It also might be somewhere in a cycle, but even then this loop
263 // will terminate the first time it encounters that block, rather than continuing around the
264 // cycle forever.
265 //
266 // Why is it okay to copy the definition to all intervening blocks? For the initial block,
267 // this may not be the final definition of this variable within this block, but if we've
268 // gotten here then we know there is no earlier definition in the block already.
269 //
270 // For the remaining blocks: Recall that a block is only allowed to be set as a predecessor
271 // after all its instructions have already been filled in, so when we follow a predecessor
272 // edge to a block, we know there will never be any more local variable definitions added to
273 // that block. We also know that `find_var` didn't find a definition for this variable in
274 // any of the blocks before `from`.
275 //
276 // So in either case there is no definition in these blocks yet and we can blindly set one.
277 let var_defs = &mut self.variables[var];
278 while block != from {
279 debug_assert!(var_defs[block].is_none());
280 var_defs[block] = PackedOption::from(val);
281 block = self.ssa_blocks[block].single_predecessor.unwrap();
282 }
283 }
284
285 /// Find the most recent definition of this variable, returning both the definition and the
286 /// block in which it was found. If we can't find a definition that's provably the right one for
287 /// all paths to the current block, then append a block parameter to some block and use that as
288 /// the definition. Either way, also arrange that the definition will be on the `results` stack
289 /// when `run_state_machine` is done processing the current step.
290 ///
291 /// If a block has exactly one predecessor, and the block is sealed so we know its predecessors
292 /// will never change, then its definition for this variable is the same as the definition from
293 /// that one predecessor. In this case it's easy to see that no block parameter is necessary,
294 /// but we need to look at the predecessor to see if a block parameter might be needed there.
295 /// That holds transitively across any chain of sealed blocks with exactly one predecessor each.
296 ///
297 /// This runs into a problem, though, if such a chain has a cycle: Blindly following a cyclic
298 /// chain that never defines this variable would lead to an infinite loop in the compiler. It
299 /// doesn't really matter what code we generate in that case. Since each block in the cycle has
300 /// exactly one predecessor, there's no way to enter the cycle from the function's entry block;
301 /// and since all blocks in the cycle are sealed, the entire cycle is permanently dead code. But
302 /// we still have to prevent the possibility of an infinite loop.
303 ///
304 /// To break cycles, we can pick any block within the cycle as the one where we'll add a block
305 /// parameter. It's convenient to pick the block at which we entered the cycle, because that's
306 /// the first place where we can detect that we just followed a cycle. Adding a block parameter
307 /// gives us a definition we can reuse throughout the rest of the cycle.
find_var( &mut self, func: &mut Function, var: Variable, ty: Type, mut block: Block, ) -> (Value, Block)308 fn find_var(
309 &mut self,
310 func: &mut Function,
311 var: Variable,
312 ty: Type,
313 mut block: Block,
314 ) -> (Value, Block) {
315 // Try to find an existing definition along single-predecessor edges first.
316 self.visited.clear();
317 let var_defs = &mut self.variables[var];
318 while let Some(pred) = self.ssa_blocks[block].single_predecessor.expand() {
319 if !self.visited.insert(block) {
320 break;
321 }
322 block = pred;
323 if let Some(val) = var_defs[block].expand() {
324 self.results.push(val);
325 return (val, block);
326 }
327 }
328
329 // We've promised to return the most recent block where `var` was defined, but we didn't
330 // find a usable definition. So create one.
331 let val = func.dfg.append_block_param(block, ty);
332 var_defs[block] = PackedOption::from(val);
333
334 // Now every predecessor needs to pass its definition of this variable to the newly added
335 // block parameter. To do that we have to "recursively" call `use_var`, but there are two
336 // problems with doing that. First, we need to keep a fixed bound on stack depth, so we
337 // can't actually recurse; instead we defer to `run_state_machine`. Second, if we don't
338 // know all our predecessors yet, we have to defer this work until the block gets sealed.
339 match &mut self.ssa_blocks[block].sealed {
340 // Once all the `calls` added here complete, this leaves either `val` or an equivalent
341 // definition on the `results` stack.
342 Sealed::Yes => self.begin_predecessors_lookup(val, block),
343 Sealed::No { undef_variables } => {
344 undef_variables.push(var, &mut self.variable_pool);
345 self.results.push(val);
346 }
347 }
348 (val, block)
349 }
350
351 /// Declares a new basic block to construct corresponding data for SSA construction.
352 /// No predecessors are declared here and the block is not sealed.
353 /// Predecessors have to be added with `declare_block_predecessor`.
declare_block(&mut self, block: Block)354 pub fn declare_block(&mut self, block: Block) {
355 // Ensure the block exists so seal_all_blocks will see it even if no predecessors or
356 // variables get declared for this block. But don't assign anything to it:
357 // SecondaryMap automatically sets all blocks to `default()`.
358 let _ = &mut self.ssa_blocks[block];
359 }
360
361 /// Declares a new predecessor for a `Block` and record the branch instruction
362 /// of the predecessor that leads to it.
363 ///
364 /// The precedent `Block` must be filled before added as predecessor.
365 /// Note that you must provide no jump arguments to the branch
366 /// instruction when you create it since `SSABuilder` will fill them for you.
367 ///
368 /// Callers are expected to avoid adding the same predecessor more than once in the case
369 /// of a jump table.
declare_block_predecessor(&mut self, block: Block, inst: Inst)370 pub fn declare_block_predecessor(&mut self, block: Block, inst: Inst) {
371 debug_assert!(!self.is_sealed(block));
372 self.ssa_blocks[block]
373 .predecessors
374 .push(inst, &mut self.inst_pool);
375 }
376
377 /// Remove a previously declared Block predecessor by giving a reference to the jump
378 /// instruction. Returns the basic block containing the instruction.
379 ///
380 /// Note: use only when you know what you are doing, this might break the SSA building problem
remove_block_predecessor(&mut self, block: Block, inst: Inst)381 pub fn remove_block_predecessor(&mut self, block: Block, inst: Inst) {
382 debug_assert!(!self.is_sealed(block));
383 let data = &mut self.ssa_blocks[block];
384 let pred = data
385 .predecessors
386 .as_slice(&self.inst_pool)
387 .iter()
388 .position(|&branch| branch == inst)
389 .expect("the predecessor you are trying to remove is not declared");
390 data.predecessors.swap_remove(pred, &mut self.inst_pool);
391 }
392
393 /// Completes the global value numbering for a `Block`, all of its predecessors having been
394 /// already sealed.
395 ///
396 /// This method modifies the function's `Layout` by adding arguments to the `Block`s to
397 /// take into account the Phi function placed by the SSA algorithm.
398 ///
399 /// Returns the list of newly created blocks for critical edge splitting.
seal_block(&mut self, block: Block, func: &mut Function) -> SideEffects400 pub fn seal_block(&mut self, block: Block, func: &mut Function) -> SideEffects {
401 debug_assert!(
402 !self.is_sealed(block),
403 "Attempting to seal {block} which is already sealed."
404 );
405 self.seal_one_block(block, func);
406 mem::take(&mut self.side_effects)
407 }
408
409 /// Completes the global value numbering for all unsealed `Block`s in `func`.
410 ///
411 /// It's more efficient to seal `Block`s as soon as possible, during
412 /// translation, but for frontends where this is impractical to do, this
413 /// function can be used at the end of translating all blocks to ensure
414 /// that everything is sealed.
seal_all_blocks(&mut self, func: &mut Function) -> SideEffects415 pub fn seal_all_blocks(&mut self, func: &mut Function) -> SideEffects {
416 // Seal all `Block`s currently in the function. This can entail splitting
417 // and creation of new blocks, however such new blocks are sealed on
418 // the fly, so we don't need to account for them here.
419 for block in self.ssa_blocks.keys() {
420 self.seal_one_block(block, func);
421 }
422 mem::take(&mut self.side_effects)
423 }
424
425 /// Helper function for `seal_block` and `seal_all_blocks`.
seal_one_block(&mut self, block: Block, func: &mut Function)426 fn seal_one_block(&mut self, block: Block, func: &mut Function) {
427 // For each undef var we look up values in the predecessors and create a block parameter
428 // only if necessary.
429 let mut undef_variables =
430 match mem::replace(&mut self.ssa_blocks[block].sealed, Sealed::Yes) {
431 Sealed::No { undef_variables } => undef_variables,
432 Sealed::Yes => return,
433 };
434 let ssa_params = undef_variables.len(&self.variable_pool);
435
436 let predecessors = self.predecessors(block);
437 if predecessors.len() == 1 {
438 let pred = func.layout.inst_block(predecessors[0]).unwrap();
439 self.ssa_blocks[block].single_predecessor = PackedOption::from(pred);
440 }
441
442 // Note that begin_predecessors_lookup requires visiting these variables in the same order
443 // that they were defined by find_var, because it appends arguments to the jump instructions
444 // in all the predecessor blocks one variable at a time.
445 for idx in 0..ssa_params {
446 let var = undef_variables.get(idx, &self.variable_pool).unwrap();
447
448 // We need the temporary Value that was assigned to this Variable. If that Value shows
449 // up as a result from any of our predecessors, then it never got assigned on the loop
450 // through that block. We get the value from the next block param, where it was first
451 // allocated in find_var.
452 let block_params = func.dfg.block_params(block);
453
454 // On each iteration through this loop, there are (ssa_params - idx) undefined variables
455 // left to process. Previous iterations through the loop may have removed earlier block
456 // parameters, but the last (ssa_params - idx) block parameters always correspond to the
457 // remaining undefined variables. So index from the end of the current block params.
458 let val = block_params[block_params.len() - (ssa_params - idx)];
459
460 debug_assert!(self.calls.is_empty());
461 debug_assert!(self.results.is_empty());
462 // self.side_effects may be non-empty here so that callers can
463 // accumulate side effects over multiple calls.
464 self.begin_predecessors_lookup(val, block);
465 self.run_state_machine(func, var, func.dfg.value_type(val));
466 }
467
468 undef_variables.clear(&mut self.variable_pool);
469 }
470
471 /// Given the local SSA Value of a Variable in a Block, perform a recursive lookup on
472 /// predecessors to determine if it is redundant with another Value earlier in the CFG.
473 ///
474 /// If such a Value exists and is redundant, the local Value is replaced by the
475 /// corresponding non-local Value. If the original Value was a Block parameter,
476 /// the parameter may be removed if redundant. Parameters are placed eagerly by callers
477 /// to avoid infinite loops when looking up a Value for a Block that is in a CFG loop.
478 ///
479 /// Doing this lookup for each Value in each Block preserves SSA form during construction.
480 ///
481 /// ## Arguments
482 ///
483 /// `sentinel` is a dummy Block parameter inserted by `use_var_nonlocal()`.
484 /// Its purpose is to allow detection of CFG cycles while traversing predecessors.
begin_predecessors_lookup(&mut self, sentinel: Value, dest_block: Block)485 fn begin_predecessors_lookup(&mut self, sentinel: Value, dest_block: Block) {
486 self.calls
487 .push(Call::FinishPredecessorsLookup(sentinel, dest_block));
488 // Iterate over the predecessors.
489 self.calls.extend(
490 self.ssa_blocks[dest_block]
491 .predecessors
492 .as_slice(&self.inst_pool)
493 .iter()
494 .rev()
495 .copied()
496 .map(Call::UseVar),
497 );
498 }
499
500 /// Examine the values from the predecessors and compute a result value, creating
501 /// block parameters as needed.
finish_predecessors_lookup( &mut self, func: &mut Function, sentinel: Value, dest_block: Block, ) -> Value502 fn finish_predecessors_lookup(
503 &mut self,
504 func: &mut Function,
505 sentinel: Value,
506 dest_block: Block,
507 ) -> Value {
508 // Determine how many predecessors are yielding unique, non-temporary Values. If a variable
509 // is live and unmodified across several control-flow join points, earlier blocks will
510 // introduce aliases for that variable's definition, so we resolve aliases eagerly here to
511 // ensure that we can tell when the same definition has reached this block via multiple
512 // paths. Doing so also detects cyclic references to the sentinel, which can occur in
513 // unreachable code.
514 let num_predecessors = self.predecessors(dest_block).len();
515 // When this `Drain` is dropped, these elements will get truncated.
516 let results = self.results.drain(self.results.len() - num_predecessors..);
517
518 let pred_val = {
519 let mut iter = results
520 .as_slice()
521 .iter()
522 .map(|&val| func.dfg.resolve_aliases(val))
523 .filter(|&val| val != sentinel);
524 if let Some(val) = iter.next() {
525 // This variable has at least one non-temporary definition. If they're all the same
526 // value, we can remove the block parameter and reference that value instead.
527 if iter.all(|other| other == val) {
528 Some(val)
529 } else {
530 None
531 }
532 } else {
533 // The variable is used but never defined before. This is an irregularity in the
534 // code, but rather than throwing an error we silently initialize the variable to
535 // 0. This will have no effect since this situation happens in unreachable code.
536 if !func.layout.is_block_inserted(dest_block) {
537 func.layout.append_block(dest_block);
538 }
539 self.side_effects
540 .instructions_added_to_blocks
541 .push(dest_block);
542 let zero = emit_zero(
543 func.dfg.value_type(sentinel),
544 FuncCursor::new(func).at_first_insertion_point(dest_block),
545 );
546 Some(zero)
547 }
548 };
549
550 if let Some(pred_val) = pred_val {
551 // Here all the predecessors use a single value to represent our variable
552 // so we don't need to have it as a block argument.
553 // We need to replace all the occurrences of val with pred_val but since
554 // we can't afford a re-writing pass right now we just declare an alias.
555 func.dfg.remove_block_param(sentinel);
556 func.dfg.change_to_alias(sentinel, pred_val);
557 pred_val
558 } else {
559 // There is disagreement in the predecessors on which value to use so we have
560 // to keep the block argument.
561 let mut preds = self.ssa_blocks[dest_block].predecessors;
562 let dfg = &mut func.stencil.dfg;
563 for (idx, &val) in results.as_slice().iter().enumerate() {
564 let pred = preds.get_mut(idx, &mut self.inst_pool).unwrap();
565 let branch = *pred;
566
567 let dests = dfg.insts[branch]
568 .branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables);
569 assert!(
570 !dests.is_empty(),
571 "you have declared a non-branch instruction as a predecessor to a block!"
572 );
573 for block in dests {
574 if block.block(&dfg.value_lists) == dest_block {
575 block.append_argument(val, &mut dfg.value_lists);
576 }
577 }
578 }
579 sentinel
580 }
581 }
582
583 /// Returns the list of `Block`s that have been declared as predecessors of the argument.
predecessors(&self, block: Block) -> &[Inst]584 fn predecessors(&self, block: Block) -> &[Inst] {
585 self.ssa_blocks[block]
586 .predecessors
587 .as_slice(&self.inst_pool)
588 }
589
590 /// Returns whether the given Block has any predecessor or not.
has_any_predecessors(&self, block: Block) -> bool591 pub fn has_any_predecessors(&self, block: Block) -> bool {
592 !self.predecessors(block).is_empty()
593 }
594
595 /// Returns `true` if and only if `seal_block` has been called on the argument.
is_sealed(&self, block: Block) -> bool596 pub fn is_sealed(&self, block: Block) -> bool {
597 matches!(self.ssa_blocks[block].sealed, Sealed::Yes)
598 }
599
600 /// The main algorithm is naturally recursive: when there's a `use_var` in a
601 /// block with no corresponding local defs, it recurses and performs a
602 /// `use_var` in each predecessor. To avoid risking running out of callstack
603 /// space, we keep an explicit stack and use a small state machine rather
604 /// than literal recursion.
run_state_machine(&mut self, func: &mut Function, var: Variable, ty: Type) -> Value605 fn run_state_machine(&mut self, func: &mut Function, var: Variable, ty: Type) -> Value {
606 // Process the calls scheduled in `self.calls` until it is empty.
607 while let Some(call) = self.calls.pop() {
608 match call {
609 Call::UseVar(branch) => {
610 let block = func.layout.inst_block(branch).unwrap();
611 self.use_var_nonlocal(func, var, ty, block);
612 }
613 Call::FinishPredecessorsLookup(sentinel, dest_block) => {
614 let val = self.finish_predecessors_lookup(func, sentinel, dest_block);
615 self.results.push(val);
616 }
617 }
618 }
619 debug_assert_eq!(self.results.len(), 1);
620 self.results.pop().unwrap()
621 }
622 }
623
624 #[cfg(test)]
625 mod tests {
626 use crate::Variable;
627 use crate::ssa::SSABuilder;
628 use cranelift_codegen::cursor::{Cursor, FuncCursor};
629 use cranelift_codegen::entity::EntityRef;
630 use cranelift_codegen::ir;
631 use cranelift_codegen::ir::types::*;
632 use cranelift_codegen::ir::{Function, Inst, InstBuilder, JumpTableData, Opcode};
633 use cranelift_codegen::settings;
634 use cranelift_codegen::verify_function;
635
636 #[test]
simple_block()637 fn simple_block() {
638 let mut func = Function::new();
639 let mut ssa = SSABuilder::default();
640 let block0 = func.dfg.make_block();
641 // Here is the pseudo-program we want to translate:
642 // block0:
643 // x = 1;
644 // y = 2;
645 // z = x + y;
646 // z = x + z;
647
648 ssa.declare_block(block0);
649 let x_var = Variable::new(0);
650 let x_ssa = {
651 let mut cur = FuncCursor::new(&mut func);
652 cur.insert_block(block0);
653 cur.ins().iconst(I32, 1)
654 };
655 ssa.def_var(x_var, x_ssa, block0);
656 let y_var = Variable::new(1);
657 let y_ssa = {
658 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
659 cur.ins().iconst(I32, 2)
660 };
661 ssa.def_var(y_var, y_ssa, block0);
662 assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa);
663 assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa);
664
665 let z_var = Variable::new(2);
666 let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0;
667 let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0;
668 let z1_ssa = {
669 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
670 cur.ins().iadd(x_use1, y_use1)
671 };
672 ssa.def_var(z_var, z1_ssa, block0);
673 assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa);
674
675 let x_use2 = ssa.use_var(&mut func, x_var, I32, block0).0;
676 let z_use1 = ssa.use_var(&mut func, z_var, I32, block0).0;
677 let z2_ssa = {
678 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
679 cur.ins().iadd(x_use2, z_use1)
680 };
681 ssa.def_var(z_var, z2_ssa, block0);
682 assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z2_ssa);
683 }
684
685 #[test]
sequence_of_blocks()686 fn sequence_of_blocks() {
687 let mut func = Function::new();
688 let mut ssa = SSABuilder::default();
689 let block0 = func.dfg.make_block();
690 let block1 = func.dfg.make_block();
691 let block2 = func.dfg.make_block();
692 // Here is the pseudo-program we want to translate:
693 // block0:
694 // x = 1;
695 // y = 2;
696 // z = x + y;
697 // brif y, block1, block1;
698 // block1:
699 // z = x + z;
700 // jump block2;
701 // block2:
702 // y = x + y;
703 {
704 let mut cur = FuncCursor::new(&mut func);
705 cur.insert_block(block0);
706 cur.insert_block(block1);
707 cur.insert_block(block2);
708 }
709
710 // block0
711 ssa.declare_block(block0);
712 ssa.seal_block(block0, &mut func);
713 let x_var = Variable::new(0);
714 let x_ssa = {
715 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
716 cur.ins().iconst(I32, 1)
717 };
718 ssa.def_var(x_var, x_ssa, block0);
719 let y_var = Variable::new(1);
720 let y_ssa = {
721 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
722 cur.ins().iconst(I32, 2)
723 };
724 ssa.def_var(y_var, y_ssa, block0);
725 let z_var = Variable::new(2);
726 let x_use1 = ssa.use_var(&mut func, x_var, I32, block0).0;
727 let y_use1 = ssa.use_var(&mut func, y_var, I32, block0).0;
728 let z1_ssa = {
729 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
730 cur.ins().iadd(x_use1, y_use1)
731 };
732 ssa.def_var(z_var, z1_ssa, block0);
733 let y_use2 = ssa.use_var(&mut func, y_var, I32, block0).0;
734 let brif_block0_block2_block1: Inst = {
735 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
736 cur.ins().brif(y_use2, block2, &[], block1, &[])
737 };
738
739 assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x_ssa);
740 assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y_ssa);
741 assert_eq!(ssa.use_var(&mut func, z_var, I32, block0).0, z1_ssa);
742
743 // block1
744 ssa.declare_block(block1);
745 ssa.declare_block_predecessor(block1, brif_block0_block2_block1);
746 ssa.seal_block(block1, &mut func);
747
748 let x_use2 = ssa.use_var(&mut func, x_var, I32, block1).0;
749 let z_use1 = ssa.use_var(&mut func, z_var, I32, block1).0;
750 let z2_ssa = {
751 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
752 cur.ins().iadd(x_use2, z_use1)
753 };
754 ssa.def_var(z_var, z2_ssa, block1);
755 let jump_block1_block2: Inst = {
756 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
757 cur.ins().jump(block2, &[])
758 };
759
760 assert_eq!(x_use2, x_ssa);
761 assert_eq!(z_use1, z1_ssa);
762 assert_eq!(ssa.use_var(&mut func, z_var, I32, block1).0, z2_ssa);
763
764 // block2
765 ssa.declare_block(block2);
766 ssa.declare_block_predecessor(block2, brif_block0_block2_block1);
767 ssa.declare_block_predecessor(block2, jump_block1_block2);
768 ssa.seal_block(block2, &mut func);
769 let x_use3 = ssa.use_var(&mut func, x_var, I32, block2).0;
770 let y_use3 = ssa.use_var(&mut func, y_var, I32, block2).0;
771 let y2_ssa = {
772 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
773 cur.ins().iadd(x_use3, y_use3)
774 };
775 ssa.def_var(y_var, y2_ssa, block2);
776
777 assert_eq!(x_ssa, x_use3);
778 assert_eq!(y_ssa, y_use3);
779 match func.dfg.insts[brif_block0_block2_block1] {
780 ir::InstructionData::Brif {
781 blocks: [block_then, block_else],
782 ..
783 } => {
784 assert_eq!(block_then.block(&func.dfg.value_lists), block2);
785 assert_eq!(block_then.args(&func.dfg.value_lists).len(), 0);
786 assert_eq!(block_else.block(&func.dfg.value_lists), block1);
787 assert_eq!(block_else.args(&func.dfg.value_lists).len(), 0);
788 }
789 _ => assert!(false),
790 };
791 match func.dfg.insts[brif_block0_block2_block1] {
792 ir::InstructionData::Brif {
793 blocks: [block_then, block_else],
794 ..
795 } => {
796 assert_eq!(block_then.block(&func.dfg.value_lists), block2);
797 assert_eq!(block_then.args(&func.dfg.value_lists).len(), 0);
798 assert_eq!(block_else.block(&func.dfg.value_lists), block1);
799 assert_eq!(block_else.args(&func.dfg.value_lists).len(), 0);
800 }
801 _ => assert!(false),
802 };
803 match func.dfg.insts[jump_block1_block2] {
804 ir::InstructionData::Jump {
805 destination: dest, ..
806 } => {
807 assert_eq!(dest.block(&func.dfg.value_lists), block2);
808 assert_eq!(dest.args(&func.dfg.value_lists).len(), 0);
809 }
810 _ => assert!(false),
811 };
812 }
813
814 #[test]
program_with_loop()815 fn program_with_loop() {
816 let mut func = Function::new();
817 let mut ssa = SSABuilder::default();
818 let block0 = func.dfg.make_block();
819 let block1 = func.dfg.make_block();
820 let block2 = func.dfg.make_block();
821 let block3 = func.dfg.make_block();
822 {
823 let mut cur = FuncCursor::new(&mut func);
824 cur.insert_block(block0);
825 cur.insert_block(block1);
826 cur.insert_block(block2);
827 cur.insert_block(block3);
828 }
829 // Here is the pseudo-program we want to translate:
830 // block0:
831 // x = 1;
832 // y = 2;
833 // z = x + y;
834 // jump block1
835 // block1:
836 // z = z + y;
837 // brif y, block3, block2;
838 // block2:
839 // z = z - x;
840 // return y
841 // block3:
842 // y = y - x
843 // jump block1
844
845 // block0
846 ssa.declare_block(block0);
847 ssa.seal_block(block0, &mut func);
848 let x_var = Variable::new(0);
849 let x1 = {
850 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
851 cur.ins().iconst(I32, 1)
852 };
853 ssa.def_var(x_var, x1, block0);
854 let y_var = Variable::new(1);
855 let y1 = {
856 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
857 cur.ins().iconst(I32, 2)
858 };
859 ssa.def_var(y_var, y1, block0);
860 let z_var = Variable::new(2);
861 let x2 = ssa.use_var(&mut func, x_var, I32, block0).0;
862 let y2 = ssa.use_var(&mut func, y_var, I32, block0).0;
863 let z1 = {
864 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
865 cur.ins().iadd(x2, y2)
866 };
867 ssa.def_var(z_var, z1, block0);
868 let jump_block0_block1 = {
869 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
870 cur.ins().jump(block1, &[])
871 };
872 assert_eq!(ssa.use_var(&mut func, x_var, I32, block0).0, x1);
873 assert_eq!(ssa.use_var(&mut func, y_var, I32, block0).0, y1);
874 assert_eq!(x2, x1);
875 assert_eq!(y2, y1);
876
877 // block1
878 ssa.declare_block(block1);
879 ssa.declare_block_predecessor(block1, jump_block0_block1);
880 let z2 = ssa.use_var(&mut func, z_var, I32, block1).0;
881 let y3 = ssa.use_var(&mut func, y_var, I32, block1).0;
882 let z3 = {
883 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
884 cur.ins().iadd(z2, y3)
885 };
886 ssa.def_var(z_var, z3, block1);
887 let y4 = ssa.use_var(&mut func, y_var, I32, block1).0;
888 assert_eq!(y4, y3);
889 let brif_block1_block3_block2 = {
890 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
891 cur.ins().brif(y4, block3, &[], block2, &[])
892 };
893
894 // block2
895 ssa.declare_block(block2);
896 ssa.declare_block_predecessor(block2, brif_block1_block3_block2);
897 ssa.seal_block(block2, &mut func);
898 let z4 = ssa.use_var(&mut func, z_var, I32, block2).0;
899 assert_eq!(z4, z3);
900 let x3 = ssa.use_var(&mut func, x_var, I32, block2).0;
901 let z5 = {
902 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
903 cur.ins().isub(z4, x3)
904 };
905 ssa.def_var(z_var, z5, block2);
906 let y5 = ssa.use_var(&mut func, y_var, I32, block2).0;
907 assert_eq!(y5, y3);
908 {
909 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
910 cur.ins().return_(&[y5])
911 };
912
913 // block3
914 ssa.declare_block(block3);
915 ssa.declare_block_predecessor(block3, brif_block1_block3_block2);
916 ssa.seal_block(block3, &mut func);
917 let y6 = ssa.use_var(&mut func, y_var, I32, block3).0;
918 assert_eq!(y6, y3);
919 let x4 = ssa.use_var(&mut func, x_var, I32, block3).0;
920 assert_eq!(x4, x3);
921 let y7 = {
922 let mut cur = FuncCursor::new(&mut func).at_bottom(block3);
923 cur.ins().isub(y6, x4)
924 };
925 ssa.def_var(y_var, y7, block3);
926 let jump_block3_block1 = {
927 let mut cur = FuncCursor::new(&mut func).at_bottom(block3);
928 cur.ins().jump(block1, &[])
929 };
930
931 // block1 after all predecessors have been visited.
932 ssa.declare_block_predecessor(block1, jump_block3_block1);
933 ssa.seal_block(block1, &mut func);
934 assert_eq!(func.dfg.block_params(block1)[0], z2);
935 assert_eq!(func.dfg.block_params(block1)[1], y3);
936 assert_eq!(func.dfg.resolve_aliases(x3), x1);
937 }
938
939 #[test]
br_table_with_args()940 fn br_table_with_args() {
941 // This tests the on-demand splitting of critical edges for br_table with jump arguments
942 //
943 // Here is the pseudo-program we want to translate:
944 //
945 // function %f {
946 // block0:
947 // x = 1;
948 // br_table x, block2, [block2, block1]
949 // block1:
950 // x = 2
951 // jump block2
952 // block2:
953 // x = x + 1
954 // return
955 // }
956
957 let mut func = Function::new();
958 let mut ssa = SSABuilder::default();
959 let block0 = func.dfg.make_block();
960 let block1 = func.dfg.make_block();
961 let block2 = func.dfg.make_block();
962 {
963 let mut cur = FuncCursor::new(&mut func);
964 cur.insert_block(block0);
965 cur.insert_block(block1);
966 cur.insert_block(block2);
967 }
968
969 // block0
970 let x1 = {
971 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
972 cur.ins().iconst(I32, 1)
973 };
974 ssa.declare_block(block0);
975 ssa.seal_block(block0, &mut func);
976 let x_var = Variable::new(0);
977 ssa.def_var(x_var, x1, block0);
978 ssa.use_var(&mut func, x_var, I32, block0).0;
979 let br_table = {
980 let jump_table = JumpTableData::new(
981 func.dfg.block_call(block2, &[]),
982 &[
983 func.dfg.block_call(block2, &[]),
984 func.dfg.block_call(block1, &[]),
985 ],
986 );
987 let jt = func.create_jump_table(jump_table);
988 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
989 cur.ins().br_table(x1, jt)
990 };
991
992 // block1
993 ssa.declare_block(block1);
994 ssa.declare_block_predecessor(block1, br_table);
995 ssa.seal_block(block1, &mut func);
996 let x2 = {
997 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
998 cur.ins().iconst(I32, 2)
999 };
1000 ssa.def_var(x_var, x2, block1);
1001 let jump_block1_block2 = {
1002 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1003 cur.ins().jump(block2, &[])
1004 };
1005
1006 // block2
1007 ssa.declare_block(block2);
1008 ssa.declare_block_predecessor(block2, jump_block1_block2);
1009 ssa.declare_block_predecessor(block2, br_table);
1010 ssa.seal_block(block2, &mut func);
1011 let x3 = ssa.use_var(&mut func, x_var, I32, block2).0;
1012 let x4 = {
1013 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
1014 cur.ins().iadd_imm(x3, 1)
1015 };
1016 ssa.def_var(x_var, x4, block2);
1017 {
1018 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
1019 cur.ins().return_(&[])
1020 };
1021
1022 let flags = settings::Flags::new(settings::builder());
1023 match verify_function(&func, &flags) {
1024 Ok(()) => {}
1025 Err(_errors) => {
1026 #[cfg(feature = "std")]
1027 panic!("{}", _errors);
1028 #[cfg(not(feature = "std"))]
1029 panic!("function failed to verify");
1030 }
1031 }
1032 }
1033
1034 #[test]
undef_values_reordering()1035 fn undef_values_reordering() {
1036 // Here is the pseudo-program we want to translate:
1037 // block0:
1038 // x = 0;
1039 // y = 1;
1040 // z = 2;
1041 // jump block1;
1042 // block1:
1043 // x = z + x;
1044 // y = y - x;
1045 // jump block1;
1046 //
1047 let mut func = Function::new();
1048 let mut ssa = SSABuilder::default();
1049 let block0 = func.dfg.make_block();
1050 let block1 = func.dfg.make_block();
1051 {
1052 let mut cur = FuncCursor::new(&mut func);
1053 cur.insert_block(block0);
1054 cur.insert_block(block1);
1055 }
1056
1057 // block0
1058 ssa.declare_block(block0);
1059 let x_var = Variable::new(0);
1060 ssa.seal_block(block0, &mut func);
1061 let x1 = {
1062 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1063 cur.ins().iconst(I32, 0)
1064 };
1065 ssa.def_var(x_var, x1, block0);
1066 let y_var = Variable::new(1);
1067 let y1 = {
1068 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1069 cur.ins().iconst(I32, 1)
1070 };
1071 ssa.def_var(y_var, y1, block0);
1072 let z_var = Variable::new(2);
1073 let z1 = {
1074 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1075 cur.ins().iconst(I32, 2)
1076 };
1077 ssa.def_var(z_var, z1, block0);
1078 let jump_block0_block1 = {
1079 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1080 cur.ins().jump(block1, &[])
1081 };
1082
1083 // block1
1084 ssa.declare_block(block1);
1085 ssa.declare_block_predecessor(block1, jump_block0_block1);
1086 let z2 = ssa.use_var(&mut func, z_var, I32, block1).0;
1087 assert_eq!(func.dfg.block_params(block1)[0], z2);
1088 let x2 = ssa.use_var(&mut func, x_var, I32, block1).0;
1089 assert_eq!(func.dfg.block_params(block1)[1], x2);
1090 let x3 = {
1091 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1092 cur.ins().iadd(x2, z2)
1093 };
1094 ssa.def_var(x_var, x3, block1);
1095 let x4 = ssa.use_var(&mut func, x_var, I32, block1).0;
1096 let y3 = ssa.use_var(&mut func, y_var, I32, block1).0;
1097 assert_eq!(func.dfg.block_params(block1)[2], y3);
1098 let y4 = {
1099 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1100 cur.ins().isub(y3, x4)
1101 };
1102 ssa.def_var(y_var, y4, block1);
1103 let jump_block1_block1 = {
1104 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1105 cur.ins().jump(block1, &[])
1106 };
1107 ssa.declare_block_predecessor(block1, jump_block1_block1);
1108 ssa.seal_block(block1, &mut func);
1109 // At sealing the "z" argument disappear but the remaining "x" and "y" args have to be
1110 // in the right order.
1111 assert_eq!(func.dfg.block_params(block1)[1], y3);
1112 assert_eq!(func.dfg.block_params(block1)[0], x2);
1113 }
1114
1115 #[test]
undef()1116 fn undef() {
1117 // Use vars of various types which have not been defined.
1118 let mut func = Function::new();
1119 let mut ssa = SSABuilder::default();
1120 let block0 = func.dfg.make_block();
1121 ssa.declare_block(block0);
1122 ssa.seal_block(block0, &mut func);
1123 let i32_var = Variable::new(0);
1124 let f32_var = Variable::new(1);
1125 let f64_var = Variable::new(2);
1126 let i8_var = Variable::new(3);
1127 let f32x4_var = Variable::new(4);
1128 ssa.use_var(&mut func, i32_var, I32, block0);
1129 ssa.use_var(&mut func, f32_var, F32, block0);
1130 ssa.use_var(&mut func, f64_var, F64, block0);
1131 ssa.use_var(&mut func, i8_var, I8, block0);
1132 ssa.use_var(&mut func, f32x4_var, F32X4, block0);
1133 assert_eq!(func.dfg.num_block_params(block0), 0);
1134 }
1135
1136 #[test]
undef_in_entry()1137 fn undef_in_entry() {
1138 // Use a var which has not been defined. The search should hit the
1139 // top of the entry block, and then fall back to inserting an iconst.
1140 let mut func = Function::new();
1141 let mut ssa = SSABuilder::default();
1142 let block0 = func.dfg.make_block();
1143 ssa.declare_block(block0);
1144 ssa.seal_block(block0, &mut func);
1145 let x_var = Variable::new(0);
1146 assert_eq!(func.dfg.num_block_params(block0), 0);
1147 ssa.use_var(&mut func, x_var, I32, block0);
1148 assert_eq!(func.dfg.num_block_params(block0), 0);
1149 assert_eq!(
1150 func.dfg.insts[func.layout.first_inst(block0).unwrap()].opcode(),
1151 Opcode::Iconst
1152 );
1153 }
1154
1155 #[test]
undef_in_entry_sealed_after()1156 fn undef_in_entry_sealed_after() {
1157 // Use a var which has not been defined, but the block is not sealed
1158 // until afterward. Before sealing, the SSA builder should insert an
1159 // block param; after sealing, it should be removed.
1160 let mut func = Function::new();
1161 let mut ssa = SSABuilder::default();
1162 let block0 = func.dfg.make_block();
1163 ssa.declare_block(block0);
1164 let x_var = Variable::new(0);
1165 assert_eq!(func.dfg.num_block_params(block0), 0);
1166 ssa.use_var(&mut func, x_var, I32, block0);
1167 assert_eq!(func.dfg.num_block_params(block0), 1);
1168 ssa.seal_block(block0, &mut func);
1169 assert_eq!(func.dfg.num_block_params(block0), 0);
1170 assert_eq!(
1171 func.dfg.insts[func.layout.first_inst(block0).unwrap()].opcode(),
1172 Opcode::Iconst
1173 );
1174 }
1175
1176 #[test]
unreachable_use()1177 fn unreachable_use() {
1178 // Here is the pseudo-program we want to translate:
1179 // block0:
1180 // return;
1181 // block1:
1182 // brif x, block1, block1;
1183 let mut func = Function::new();
1184 let mut ssa = SSABuilder::default();
1185 let block0 = func.dfg.make_block();
1186 let block1 = func.dfg.make_block();
1187 {
1188 let mut cur = FuncCursor::new(&mut func);
1189 cur.insert_block(block0);
1190 cur.insert_block(block1);
1191 }
1192
1193 // block0
1194 ssa.declare_block(block0);
1195 ssa.seal_block(block0, &mut func);
1196 {
1197 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1198 cur.ins().return_(&[]);
1199 }
1200
1201 // block1
1202 ssa.declare_block(block1);
1203 {
1204 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1205 let x_var = Variable::new(0);
1206 let x_val = ssa.use_var(&mut cur.func, x_var, I32, block1).0;
1207 let brif = cur.ins().brif(x_val, block1, &[], block1, &[]);
1208 ssa.declare_block_predecessor(block1, brif);
1209 }
1210 ssa.seal_block(block1, &mut func);
1211
1212 let flags = settings::Flags::new(settings::builder());
1213 match verify_function(&func, &flags) {
1214 Ok(()) => {}
1215 Err(_errors) => {
1216 #[cfg(feature = "std")]
1217 panic!("{}", _errors);
1218 #[cfg(not(feature = "std"))]
1219 panic!("function failed to verify");
1220 }
1221 }
1222 }
1223
1224 #[test]
unreachable_use_with_multiple_preds()1225 fn unreachable_use_with_multiple_preds() {
1226 // Here is the pseudo-program we want to translate:
1227 // block0:
1228 // return;
1229 // block1:
1230 // brif x, block1, block2;
1231 // block2:
1232 // jump block1;
1233 let mut func = Function::new();
1234 let mut ssa = SSABuilder::default();
1235 let block0 = func.dfg.make_block();
1236 let block1 = func.dfg.make_block();
1237 let block2 = func.dfg.make_block();
1238 {
1239 let mut cur = FuncCursor::new(&mut func);
1240 cur.insert_block(block0);
1241 cur.insert_block(block1);
1242 cur.insert_block(block2);
1243 }
1244
1245 // block0
1246 ssa.declare_block(block0);
1247 ssa.seal_block(block0, &mut func);
1248 {
1249 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1250 cur.ins().return_(&[]);
1251 }
1252
1253 // block1
1254 ssa.declare_block(block1);
1255 let brif = {
1256 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1257 let x_var = Variable::new(0);
1258 let x_val = ssa.use_var(&mut cur.func, x_var, I32, block1).0;
1259 cur.ins().brif(x_val, block2, &[], block1, &[])
1260 };
1261
1262 // block2
1263 ssa.declare_block(block2);
1264 ssa.declare_block_predecessor(block1, brif);
1265 ssa.declare_block_predecessor(block2, brif);
1266 ssa.seal_block(block2, &mut func);
1267 let jump_block2_block1 = {
1268 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
1269 cur.ins().jump(block1, &[])
1270 };
1271
1272 // seal block1
1273 ssa.declare_block_predecessor(block1, jump_block2_block1);
1274 ssa.seal_block(block1, &mut func);
1275 let flags = settings::Flags::new(settings::builder());
1276 match verify_function(&func, &flags) {
1277 Ok(()) => {}
1278 Err(_errors) => {
1279 #[cfg(feature = "std")]
1280 panic!("{}", _errors);
1281 #[cfg(not(feature = "std"))]
1282 panic!("function failed to verify");
1283 }
1284 }
1285 }
1286
1287 #[test]
reassign_with_predecessor_loop_hangs()1288 fn reassign_with_predecessor_loop_hangs() {
1289 // Here is the pseudo-program we want to translate:
1290 // block0:
1291 // var0 = iconst 0
1292 // return;
1293 // block1:
1294 // jump block2;
1295 // block2:
1296 // ; phantom use of var0
1297 // var0 = iconst 1
1298 // jump block1;
1299
1300 let mut func = Function::new();
1301 let mut ssa = SSABuilder::default();
1302 let block0 = func.dfg.make_block();
1303 let block1 = func.dfg.make_block();
1304 let block2 = func.dfg.make_block();
1305 let var0 = Variable::new(0);
1306
1307 {
1308 let mut cur = FuncCursor::new(&mut func);
1309 for block in [block0, block1, block2] {
1310 cur.insert_block(block);
1311 ssa.declare_block(block);
1312 }
1313 }
1314
1315 // block0
1316 {
1317 let mut cur = FuncCursor::new(&mut func).at_bottom(block0);
1318
1319 let var0_iconst = cur.ins().iconst(I32, 0);
1320 ssa.def_var(var0, var0_iconst, block0);
1321
1322 cur.ins().return_(&[]);
1323 }
1324
1325 // block1
1326 {
1327 let mut cur = FuncCursor::new(&mut func).at_bottom(block1);
1328
1329 let jump = cur.ins().jump(block2, &[]);
1330 ssa.declare_block_predecessor(block2, jump);
1331 }
1332
1333 // block2
1334 {
1335 let mut cur = FuncCursor::new(&mut func).at_bottom(block2);
1336
1337 let _ = ssa.use_var(&mut cur.func, var0, I32, block2).0;
1338 let var0_iconst = cur.ins().iconst(I32, 1);
1339 ssa.def_var(var0, var0_iconst, block2);
1340
1341 let jump = cur.ins().jump(block1, &[]);
1342 ssa.declare_block_predecessor(block1, jump);
1343 }
1344
1345 // The sealing algorithm would enter a infinite loop here
1346 ssa.seal_all_blocks(&mut func);
1347 }
1348 }
1349