Skip to content

Commit 1f915bb

Browse files
authored
validate_ssa: Check def uniqueness first (#68)
* validate_ssa: Check def uniqueness first I had trouble understanding `validate_ssa`, so this changes the division of work to establish one invariant at a time: - First, check that no VReg is defined in more than one place, and record which block it's defined in. - Second, check that each use refers to a VReg that was defined either earlier in the same block, or in a block dominating it. I'm also enhancing the validation to check that every VReg use is in an instruction that is strictly later than its def. Previously, an earlier operand in the same instruction would also be considered a valid def, but that isn't actually legal in SSA. * Address review comments
1 parent 1955c6d commit 1f915bb

1 file changed

Lines changed: 40 additions & 38 deletions

File tree

src/fuzzing/ssa.rs

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,31 @@
55

66
//! SSA-related utilities.
77
8-
use crate::cfg::CFGInfo;
8+
use std::collections::HashSet;
99

10-
use crate::{Block, Function, Inst, OperandKind, RegAllocError};
10+
use crate::cfg::CFGInfo;
11+
use crate::{Block, Function, Inst, OperandKind, RegAllocError, VReg};
1112

1213
pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllocError> {
13-
// For each vreg, the instruction that defines it, if any.
14-
let mut vreg_def_inst = vec![Inst::invalid(); f.num_vregs()];
15-
// For each vreg, the block that defines it as a blockparam, if
16-
// any. (Every vreg must have a valid entry in either
17-
// `vreg_def_inst` or `vreg_def_blockparam`.)
18-
let mut vreg_def_blockparam = vec![(Block::invalid(), 0); f.num_vregs()];
19-
14+
// For every block param and inst def, check that this is the only def.
15+
let mut defined_in = vec![Block::invalid(); f.num_vregs()];
2016
for block in 0..f.num_blocks() {
2117
let block = Block::new(block);
22-
for (i, param) in f.block_params(block).iter().enumerate() {
23-
vreg_def_blockparam[param.vreg()] = (block, i as u32);
18+
let mut def = |vreg: VReg, inst| {
19+
if defined_in[vreg.vreg()].is_valid() {
20+
Err(RegAllocError::SSA(vreg, inst))
21+
} else {
22+
defined_in[vreg.vreg()] = block;
23+
Ok(())
24+
}
25+
};
26+
for &param in f.block_params(block) {
27+
def(param, Inst::invalid())?;
2428
}
2529
for inst in f.block_insns(block).iter() {
2630
for operand in f.inst_operands(inst) {
27-
match operand.kind() {
28-
OperandKind::Def => {
29-
vreg_def_inst[operand.vreg().vreg()] = inst;
30-
}
31-
_ => {}
31+
if let OperandKind::Def = operand.kind() {
32+
def(operand.vreg(), inst)?;
3233
}
3334
}
3435
}
@@ -37,39 +38,32 @@ pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllo
3738
// Walk the blocks in arbitrary order. Check, for every use, that
3839
// the def is either in the same block in an earlier inst, or is
3940
// defined (by inst or blockparam) in some other block that
40-
// dominates this one. Also check that for every block param and
41-
// inst def, that this is the only def.
42-
let mut defined = vec![false; f.num_vregs()];
41+
// dominates this one.
42+
let mut local = HashSet::new();
4343
for block in 0..f.num_blocks() {
4444
let block = Block::new(block);
45-
for blockparam in f.block_params(block) {
46-
if defined[blockparam.vreg()] {
47-
return Err(RegAllocError::SSA(*blockparam, Inst::invalid()));
48-
}
49-
defined[blockparam.vreg()] = true;
50-
}
45+
local.clear();
46+
local.extend(f.block_params(block));
47+
5148
for iix in f.block_insns(block).iter() {
5249
let operands = f.inst_operands(iix);
5350
for operand in operands {
5451
match operand.kind() {
5552
OperandKind::Use => {
56-
let def_block = if vreg_def_inst[operand.vreg().vreg()].is_valid() {
57-
cfginfo.insn_block[vreg_def_inst[operand.vreg().vreg()].index()]
58-
} else {
59-
vreg_def_blockparam[operand.vreg().vreg()].0
60-
};
61-
if def_block.is_invalid() {
62-
return Err(RegAllocError::SSA(operand.vreg(), iix));
63-
}
64-
if !cfginfo.dominates(def_block, block) {
53+
let def_block = defined_in[operand.vreg().vreg()];
54+
let okay = def_block.is_valid()
55+
&& if def_block == block {
56+
local.contains(&operand.vreg())
57+
} else {
58+
cfginfo.dominates(def_block, block)
59+
};
60+
if !okay {
6561
return Err(RegAllocError::SSA(operand.vreg(), iix));
6662
}
6763
}
6864
OperandKind::Def => {
69-
if defined[operand.vreg().vreg()] {
70-
return Err(RegAllocError::SSA(operand.vreg(), iix));
71-
}
72-
defined[operand.vreg().vreg()] = true;
65+
// Check all the uses in this instruction
66+
// first, before recording its defs below.
7367
}
7468
OperandKind::Mod => {
7569
// Mod (modify) operands are not used in SSA,
@@ -79,6 +73,14 @@ pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllo
7973
}
8074
}
8175
}
76+
// In SSA form, an instruction can't use a VReg that it
77+
// also defines. So only record this instruction's defs
78+
// after its uses have been checked.
79+
for operand in operands {
80+
if let OperandKind::Def = operand.kind() {
81+
local.insert(operand.vreg());
82+
}
83+
}
8284
}
8385
}
8486

0 commit comments

Comments
 (0)