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
This commit is contained in:
@@ -5,30 +5,31 @@
|
|||||||
|
|
||||||
//! SSA-related utilities.
|
//! SSA-related utilities.
|
||||||
|
|
||||||
use crate::cfg::CFGInfo;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::{Block, Function, Inst, OperandKind, RegAllocError};
|
use crate::cfg::CFGInfo;
|
||||||
|
use crate::{Block, Function, Inst, OperandKind, RegAllocError, VReg};
|
||||||
|
|
||||||
pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllocError> {
|
pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllocError> {
|
||||||
// For each vreg, the instruction that defines it, if any.
|
// For every block param and inst def, check that this is the only def.
|
||||||
let mut vreg_def_inst = vec![Inst::invalid(); f.num_vregs()];
|
let mut defined_in = vec![Block::invalid(); f.num_vregs()];
|
||||||
// For each vreg, the block that defines it as a blockparam, if
|
|
||||||
// any. (Every vreg must have a valid entry in either
|
|
||||||
// `vreg_def_inst` or `vreg_def_blockparam`.)
|
|
||||||
let mut vreg_def_blockparam = vec![(Block::invalid(), 0); f.num_vregs()];
|
|
||||||
|
|
||||||
for block in 0..f.num_blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
for (i, param) in f.block_params(block).iter().enumerate() {
|
let mut def = |vreg: VReg, inst| {
|
||||||
vreg_def_blockparam[param.vreg()] = (block, i as u32);
|
if defined_in[vreg.vreg()].is_valid() {
|
||||||
|
Err(RegAllocError::SSA(vreg, inst))
|
||||||
|
} else {
|
||||||
|
defined_in[vreg.vreg()] = block;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for ¶m in f.block_params(block) {
|
||||||
|
def(param, Inst::invalid())?;
|
||||||
}
|
}
|
||||||
for inst in f.block_insns(block).iter() {
|
for inst in f.block_insns(block).iter() {
|
||||||
for operand in f.inst_operands(inst) {
|
for operand in f.inst_operands(inst) {
|
||||||
match operand.kind() {
|
if let OperandKind::Def = operand.kind() {
|
||||||
OperandKind::Def => {
|
def(operand.vreg(), inst)?;
|
||||||
vreg_def_inst[operand.vreg().vreg()] = inst;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,39 +38,32 @@ pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllo
|
|||||||
// Walk the blocks in arbitrary order. Check, for every use, that
|
// Walk the blocks in arbitrary order. Check, for every use, that
|
||||||
// the def is either in the same block in an earlier inst, or is
|
// the def is either in the same block in an earlier inst, or is
|
||||||
// defined (by inst or blockparam) in some other block that
|
// defined (by inst or blockparam) in some other block that
|
||||||
// dominates this one. Also check that for every block param and
|
// dominates this one.
|
||||||
// inst def, that this is the only def.
|
let mut local = HashSet::new();
|
||||||
let mut defined = vec![false; f.num_vregs()];
|
|
||||||
for block in 0..f.num_blocks() {
|
for block in 0..f.num_blocks() {
|
||||||
let block = Block::new(block);
|
let block = Block::new(block);
|
||||||
for blockparam in f.block_params(block) {
|
local.clear();
|
||||||
if defined[blockparam.vreg()] {
|
local.extend(f.block_params(block));
|
||||||
return Err(RegAllocError::SSA(*blockparam, Inst::invalid()));
|
|
||||||
}
|
|
||||||
defined[blockparam.vreg()] = true;
|
|
||||||
}
|
|
||||||
for iix in f.block_insns(block).iter() {
|
for iix in f.block_insns(block).iter() {
|
||||||
let operands = f.inst_operands(iix);
|
let operands = f.inst_operands(iix);
|
||||||
for operand in operands {
|
for operand in operands {
|
||||||
match operand.kind() {
|
match operand.kind() {
|
||||||
OperandKind::Use => {
|
OperandKind::Use => {
|
||||||
let def_block = if vreg_def_inst[operand.vreg().vreg()].is_valid() {
|
let def_block = defined_in[operand.vreg().vreg()];
|
||||||
cfginfo.insn_block[vreg_def_inst[operand.vreg().vreg()].index()]
|
let okay = def_block.is_valid()
|
||||||
|
&& if def_block == block {
|
||||||
|
local.contains(&operand.vreg())
|
||||||
} else {
|
} else {
|
||||||
vreg_def_blockparam[operand.vreg().vreg()].0
|
cfginfo.dominates(def_block, block)
|
||||||
};
|
};
|
||||||
if def_block.is_invalid() {
|
if !okay {
|
||||||
return Err(RegAllocError::SSA(operand.vreg(), iix));
|
|
||||||
}
|
|
||||||
if !cfginfo.dominates(def_block, block) {
|
|
||||||
return Err(RegAllocError::SSA(operand.vreg(), iix));
|
return Err(RegAllocError::SSA(operand.vreg(), iix));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperandKind::Def => {
|
OperandKind::Def => {
|
||||||
if defined[operand.vreg().vreg()] {
|
// Check all the uses in this instruction
|
||||||
return Err(RegAllocError::SSA(operand.vreg(), iix));
|
// first, before recording its defs below.
|
||||||
}
|
|
||||||
defined[operand.vreg().vreg()] = true;
|
|
||||||
}
|
}
|
||||||
OperandKind::Mod => {
|
OperandKind::Mod => {
|
||||||
// Mod (modify) operands are not used in SSA,
|
// Mod (modify) operands are not used in SSA,
|
||||||
@@ -79,6 +73,14 @@ pub fn validate_ssa<F: Function>(f: &F, cfginfo: &CFGInfo) -> Result<(), RegAllo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// In SSA form, an instruction can't use a VReg that it
|
||||||
|
// also defines. So only record this instruction's defs
|
||||||
|
// after its uses have been checked.
|
||||||
|
for operand in operands {
|
||||||
|
if let OperandKind::Def = operand.kind() {
|
||||||
|
local.insert(operand.vreg());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user