Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/frontend/src/ssa.rs
1693 views
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 {
73
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 {
91
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.
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.
122
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.
138
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.
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`.
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.
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.
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.
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`.
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.
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
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.
400
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.
415
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`.
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.
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.
502
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.
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.
591
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.
596
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.
605
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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
1350