Mass rename Ebb and relatives to Block (#1365)

* Manually rename BasicBlock to BlockPredecessor

BasicBlock is a pair of (Ebb, Inst) that is used to represent the
basic block subcomponent of an Ebb that is a predecessor to an Ebb.

Eventually we will be able to remove this struct, but for now it
makes sense to give it a non-conflicting name so that we can start
to transition Ebb to represent a basic block.

I have not updated any comments that refer to BasicBlock, as
eventually we will remove BlockPredecessor and replace with Block,
which is a basic block, so the comments will become correct.

* Manually rename SSABuilder block types to avoid conflict

SSABuilder has its own Block and BlockData types. These along with
associated identifier will cause conflicts in a later commit, so
they are renamed to be more verbose here.

* Automatically rename 'Ebb' to 'Block' in *.rs

* Automatically rename 'EBB' to 'block' in *.rs

* Automatically rename 'ebb' to 'block' in *.rs

* Automatically rename 'extended basic block' to 'basic block' in *.rs

* Automatically rename 'an basic block' to 'a basic block' in *.rs

* Manually update comment for `Block`

`Block`'s wikipedia article required an update.

* Automatically rename 'an `Block`' to 'a `Block`' in *.rs

* Automatically rename 'extended_basic_block' to 'basic_block' in *.rs

* Automatically rename 'ebb' to 'block' in *.clif

* Manually rename clif constant that contains 'ebb' as substring to avoid conflict

* Automatically rename filecheck uses of 'EBB' to 'BB'

'regex: EBB' -> 'regex: BB'
'$EBB' -> '$BB'

* Automatically rename 'EBB' 'Ebb' to 'block' in *.clif

* Automatically rename 'an block' to 'a block' in *.clif

* Fix broken testcase when function name length increases

Test function names are limited to 16 characters. This causes
the new longer name to be truncated and fail a filecheck test. An
outdated comment was also fixed.
This commit is contained in:
Ryan Hunt
2020-02-07 10:46:47 -06:00
committed by GitHub
parent a136d1cb00
commit 832666c45e
370 changed files with 8090 additions and 7988 deletions

View File

@@ -2,7 +2,7 @@
use crate::dbg::DisplayList;
use crate::dominator_tree::{DominatorTree, DominatorTreePreorder};
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir::{ExpandedProgramPoint, Function};
use crate::regalloc::liveness::Liveness;
use crate::regalloc::virtregs::VirtRegs;
@@ -13,7 +13,7 @@ use crate::verifier::{VerifierErrors, VerifierStepResult};
///
/// Conventional SSA form is represented in Cranelift with the help of virtual registers:
///
/// - Two values are said to be *PHI-related* if one is an EBB argument and the other is passed as
/// - Two values are said to be *PHI-related* if one is an block argument and the other is passed as
/// a branch argument in a location that matches the first value.
/// - PHI-related values must belong to the same virtual register.
/// - Two values in the same virtual register must not have overlapping live ranges.
@@ -76,10 +76,10 @@ impl<'a> CssaVerifier<'a> {
// Check topological ordering with the previous values in the virtual register.
let def: ExpandedProgramPoint = self.func.dfg.value_def(val).into();
let def_ebb = self.func.layout.pp_ebb(def);
let def_block = self.func.layout.pp_block(def);
for &prev_val in &values[0..idx] {
let prev_def: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
let prev_ebb = self.func.layout.pp_ebb(prev_def);
let prev_block = self.func.layout.pp_block(prev_def);
if prev_def == def {
return errors.fatal((
@@ -95,7 +95,7 @@ impl<'a> CssaVerifier<'a> {
}
// Enforce topological ordering of defs in the virtual register.
if self.preorder.dominates(def_ebb, prev_ebb)
if self.preorder.dominates(def_block, prev_block)
&& self.domtree.dominates(def, prev_def, &self.func.layout)
{
return errors.fatal((
@@ -115,12 +115,12 @@ impl<'a> CssaVerifier<'a> {
// We only have to check against the nearest dominating value.
for &prev_val in values[0..idx].iter().rev() {
let prev_def: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
let prev_ebb = self.func.layout.pp_ebb(prev_def);
let prev_block = self.func.layout.pp_block(prev_def);
if self.preorder.dominates(prev_ebb, def_ebb)
if self.preorder.dominates(prev_block, def_block)
&& self.domtree.dominates(prev_def, def, &self.func.layout)
{
if self.liveness[prev_val].overlaps_def(def, def_ebb, &self.func.layout) {
if self.liveness[prev_val].overlaps_def(def, def_block, &self.func.layout) {
return errors.fatal((
val,
format!(
@@ -142,24 +142,24 @@ impl<'a> CssaVerifier<'a> {
}
fn check_cssa(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
for ebb in self.func.layout.ebbs() {
let ebb_params = self.func.dfg.ebb_params(ebb);
for BasicBlock { inst: pred, .. } in self.cfg.pred_iter(ebb) {
for block in self.func.layout.blocks() {
let block_params = self.func.dfg.block_params(block);
for BlockPredecessor { inst: pred, .. } in self.cfg.pred_iter(block) {
let pred_args = self.func.dfg.inst_variable_args(pred);
// This should have been caught by an earlier verifier pass.
assert_eq!(
ebb_params.len(),
block_params.len(),
pred_args.len(),
"Wrong arguments on branch."
);
for (&ebb_param, &pred_arg) in ebb_params.iter().zip(pred_args) {
if !self.virtregs.same_class(ebb_param, pred_arg) {
for (&block_param, &pred_arg) in block_params.iter().zip(pred_args) {
if !self.virtregs.same_class(block_param, pred_arg) {
return errors.fatal((
pred,
format!(
"{} and {} must be in the same virtual register",
ebb_param, pred_arg
block_param, pred_arg
),
));
}

View File

@@ -1,7 +1,7 @@
//! Verify CPU flags values.
use crate::entity::{EntitySet, SecondaryMap};
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir;
use crate::ir::instructions::BranchInfo;
use crate::isa;
@@ -42,33 +42,33 @@ struct FlagsVerifier<'a> {
cfg: &'a ControlFlowGraph,
encinfo: Option<isa::EncInfo>,
/// The single live-in flags value (if any) for each EBB.
livein: SecondaryMap<ir::Ebb, PackedOption<ir::Value>>,
/// The single live-in flags value (if any) for each block.
livein: SecondaryMap<ir::Block, PackedOption<ir::Value>>,
}
impl<'a> FlagsVerifier<'a> {
fn check(&mut self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
// List of EBBs that need to be processed. EBBs may be re-added to this list when we detect
// List of blocks that need to be processed. blocks may be re-added to this list when we detect
// that one of their successor blocks needs a live-in flags value.
let mut worklist = EntitySet::with_capacity(self.func.layout.ebb_capacity());
for ebb in self.func.layout.ebbs() {
worklist.insert(ebb);
let mut worklist = EntitySet::with_capacity(self.func.layout.block_capacity());
for block in self.func.layout.blocks() {
worklist.insert(block);
}
while let Some(ebb) = worklist.pop() {
if let Some(value) = self.visit_ebb(ebb, errors)? {
// The EBB has live-in flags. Check if the value changed.
match self.livein[ebb].expand() {
// Revisit any predecessor blocks the first time we see a live-in for `ebb`.
while let Some(block) = worklist.pop() {
if let Some(value) = self.visit_block(block, errors)? {
// The block has live-in flags. Check if the value changed.
match self.livein[block].expand() {
// Revisit any predecessor blocks the first time we see a live-in for `block`.
None => {
self.livein[ebb] = value.into();
for BasicBlock { ebb: pred, .. } in self.cfg.pred_iter(ebb) {
self.livein[block] = value.into();
for BlockPredecessor { block: pred, .. } in self.cfg.pred_iter(block) {
worklist.insert(pred);
}
}
Some(old) if old != value => {
return errors.fatal((
ebb,
block,
format!("conflicting live-in CPU flags: {} and {}", old, value),
));
}
@@ -76,24 +76,24 @@ impl<'a> FlagsVerifier<'a> {
}
} else {
// Existing live-in flags should never be able to disappear.
assert_eq!(self.livein[ebb].expand(), None);
assert_eq!(self.livein[block].expand(), None);
}
}
Ok(())
}
/// Check flags usage in `ebb` and return the live-in flags value, if any.
fn visit_ebb(
/// Check flags usage in `block` and return the live-in flags value, if any.
fn visit_block(
&self,
ebb: ir::Ebb,
block: ir::Block,
errors: &mut VerifierErrors,
) -> VerifierStepResult<Option<ir::Value>> {
// The single currently live flags value.
let mut live_val = None;
// Visit instructions backwards so we can track liveness accurately.
for inst in self.func.layout.ebb_insts(ebb).rev() {
for inst in self.func.layout.block_insts(block).rev() {
// Check if `inst` interferes with existing live flags.
if let Some(live) = live_val {
for &res in self.func.dfg.inst_results(inst) {
@@ -130,7 +130,7 @@ impl<'a> FlagsVerifier<'a> {
}
}
// Include live-in flags to successor EBBs.
// Include live-in flags to successor blocks.
match self.func.dfg.analyze_branch(inst) {
BranchInfo::NotABranch => {}
BranchInfo::SingleDest(dest, _) => {

View File

@@ -1,6 +1,6 @@
//! Liveness verifier.
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir::entities::AnyEntity;
use crate::ir::{ExpandedProgramPoint, Function, ProgramPoint, Value};
use crate::isa::TargetIsa;
@@ -16,7 +16,7 @@ use crate::verifier::{VerifierErrors, VerifierStepResult};
/// - All values in the program must have a live range.
/// - The live range def point must match where the value is defined.
/// - The live range must reach all uses.
/// - When a live range is live-in to an EBB, it must be live at all the predecessors.
/// - When a live range is live-in to an block, it must be live at all the predecessors.
/// - The live range affinity must be compatible with encoding constraints.
///
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for
@@ -35,7 +35,7 @@ pub fn verify_liveness(
cfg,
liveness,
};
verifier.check_ebbs(errors)?;
verifier.check_blocks(errors)?;
verifier.check_insts(errors)?;
Ok(())
}
@@ -48,17 +48,18 @@ struct LivenessVerifier<'a> {
}
impl<'a> LivenessVerifier<'a> {
/// Check all EBB arguments.
fn check_ebbs(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
for ebb in self.func.layout.ebbs() {
for &val in self.func.dfg.ebb_params(ebb) {
/// Check all block arguments.
fn check_blocks(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
for block in self.func.layout.blocks() {
for &val in self.func.dfg.block_params(block) {
let lr = match self.liveness.get(val) {
Some(lr) => lr,
None => {
return errors.fatal((ebb, format!("EBB arg {} has no live range", val)))
return errors
.fatal((block, format!("block arg {} has no live range", val)))
}
};
self.check_lr(ebb.into(), val, lr, errors)?;
self.check_lr(block.into(), val, lr, errors)?;
}
}
Ok(())
@@ -66,8 +67,8 @@ impl<'a> LivenessVerifier<'a> {
/// Check all instructions.
fn check_insts(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
for ebb in self.func.layout.ebbs() {
for inst in self.func.layout.ebb_insts(ebb) {
for block in self.func.layout.blocks() {
for inst in self.func.layout.block_insts(block) {
let encoding = self.func.encodings[inst];
// Check the defs.
@@ -110,8 +111,8 @@ impl<'a> LivenessVerifier<'a> {
None => return errors.fatal((inst, format!("{} has no live range", val))),
};
debug_assert!(self.func.layout.inst_ebb(inst).unwrap() == ebb);
if !lr.reaches_use(inst, ebb, &self.func.layout) {
debug_assert!(self.func.layout.inst_block(inst).unwrap() == block);
if !lr.reaches_use(inst, block, &self.func.layout) {
return errors.fatal((inst, format!("{} is not live at this use", val)));
}
@@ -143,7 +144,7 @@ impl<'a> LivenessVerifier<'a> {
let l = &self.func.layout;
let loc: AnyEntity = match def.into() {
ExpandedProgramPoint::Ebb(e) => e.into(),
ExpandedProgramPoint::Block(e) => e.into(),
ExpandedProgramPoint::Inst(i) => i.into(),
};
if lr.def() != def {
@@ -159,66 +160,70 @@ impl<'a> LivenessVerifier<'a> {
return Ok(());
}
}
let def_ebb = match def.into() {
ExpandedProgramPoint::Ebb(e) => e,
ExpandedProgramPoint::Inst(i) => l.inst_ebb(i).unwrap(),
let def_block = match def.into() {
ExpandedProgramPoint::Block(e) => e,
ExpandedProgramPoint::Inst(i) => l.inst_block(i).unwrap(),
};
match lr.def_local_end().into() {
ExpandedProgramPoint::Ebb(e) => {
ExpandedProgramPoint::Block(e) => {
return errors.fatal((
loc,
format!("Def local range for {} can't end at {}", val, e),
));
}
ExpandedProgramPoint::Inst(i) => {
if self.func.layout.inst_ebb(i) != Some(def_ebb) {
return errors.fatal((loc, format!("Def local end for {} in wrong ebb", val)));
if self.func.layout.inst_block(i) != Some(def_block) {
return errors
.fatal((loc, format!("Def local end for {} in wrong block", val)));
}
}
}
// Now check the live-in intervals against the CFG.
for (mut ebb, end) in lr.liveins() {
if !l.is_ebb_inserted(ebb) {
for (mut block, end) in lr.liveins() {
if !l.is_block_inserted(block) {
return errors.fatal((
loc,
format!("{} livein at {} which is not in the layout", val, ebb),
format!("{} livein at {} which is not in the layout", val, block),
));
}
let end_ebb = match l.inst_ebb(end) {
let end_block = match l.inst_block(end) {
Some(e) => e,
None => {
return errors.fatal((
loc,
format!(
"{} livein for {} ends at {} which is not in the layout",
val, ebb, end
val, block, end
),
));
}
};
// Check all the EBBs in the interval independently.
// Check all the blocks in the interval independently.
loop {
// If `val` is live-in at `ebb`, it must be live at all the predecessors.
for BasicBlock { inst: pred, ebb } in self.cfg.pred_iter(ebb) {
if !lr.reaches_use(pred, ebb, &self.func.layout) {
// If `val` is live-in at `block`, it must be live at all the predecessors.
for BlockPredecessor { inst: pred, block } in self.cfg.pred_iter(block) {
if !lr.reaches_use(pred, block, &self.func.layout) {
return errors.fatal((
pred,
format!("{} is live in to {} but not live at predecessor", val, ebb),
format!(
"{} is live in to {} but not live at predecessor",
val, block
),
));
}
}
if ebb == end_ebb {
if block == end_block {
break;
}
ebb = match l.next_ebb(ebb) {
block = match l.next_block(block) {
Some(e) => e,
None => {
return errors.fatal((
loc,
format!("end of {} livein ({}) never reached", val, end_ebb),
format!("end of {} livein ({}) never reached", val, end_block),
));
}
};

View File

@@ -15,7 +15,7 @@ use crate::verifier::{VerifierErrors, VerifierStepResult};
/// instruction encoding recipes.
///
/// Values can be temporarily diverted to a different location by using the `regmove`, `regspill`,
/// and `regfill` instructions, but only inside an EBB.
/// and `regfill` instructions, but only inside an block.
///
/// If a liveness analysis is provided, it is used to verify that there are no active register
/// diversions across control flow edges.
@@ -54,11 +54,11 @@ impl<'a> LocationVerifier<'a> {
let dfg = &self.func.dfg;
let mut divert = RegDiversions::new();
for ebb in self.func.layout.ebbs() {
divert.at_ebb(&self.func.entry_diversions, ebb);
for block in self.func.layout.blocks() {
divert.at_block(&self.func.entry_diversions, block);
let mut is_after_branch = false;
for inst in self.func.layout.ebb_insts(ebb) {
for inst in self.func.layout.block_insts(block) {
let enc = self.func.encodings[inst];
if enc.is_legal() {
@@ -332,24 +332,24 @@ impl<'a> LocationVerifier<'a> {
"No branch information for {}",
dfg.display_inst(inst, self.isa)
),
SingleDest(ebb, _) => {
let unique_predecessor = self.cfg.pred_iter(ebb).count() == 1;
SingleDest(block, _) => {
let unique_predecessor = self.cfg.pred_iter(block).count() == 1;
let mut val_to_remove = vec![];
for (&value, d) in divert.iter() {
let lr = &liveness[value];
if is_after_branch && unique_predecessor {
// Forward diversions based on the targeted branch.
if !lr.is_livein(ebb, &self.func.layout) {
if !lr.is_livein(block, &self.func.layout) {
val_to_remove.push(value)
}
} else if lr.is_livein(ebb, &self.func.layout) {
} else if lr.is_livein(block, &self.func.layout) {
return errors.fatal((
inst,
format!(
"SingleDest: {} is diverted to {} and live in to {}",
value,
d.to.display(&self.reginfo),
ebb,
block,
),
));
}
@@ -358,34 +358,34 @@ impl<'a> LocationVerifier<'a> {
for val in val_to_remove.into_iter() {
divert.remove(val);
}
debug_assert!(divert.check_ebb_entry(&self.func.entry_diversions, ebb));
debug_assert!(divert.check_block_entry(&self.func.entry_diversions, block));
}
}
Table(jt, ebb) => {
Table(jt, block) => {
for (&value, d) in divert.iter() {
let lr = &liveness[value];
if let Some(ebb) = ebb {
if lr.is_livein(ebb, &self.func.layout) {
if let Some(block) = block {
if lr.is_livein(block, &self.func.layout) {
return errors.fatal((
inst,
format!(
"Table.default: {} is diverted to {} and live in to {}",
value,
d.to.display(&self.reginfo),
ebb,
block,
),
));
}
}
for ebb in self.func.jump_tables[jt].iter() {
if lr.is_livein(*ebb, &self.func.layout) {
for block in self.func.jump_tables[jt].iter() {
if lr.is_livein(*block, &self.func.layout) {
return errors.fatal((
inst,
format!(
"Table.case: {} is diverted to {} and live in to {}",
value,
d.to.display(&self.reginfo),
ebb,
block,
),
));
}

View File

@@ -1,40 +1,40 @@
//! A verifier for ensuring that functions are well formed.
//! It verifies:
//!
//! EBB integrity
//! block integrity
//!
//! - All instructions reached from the `ebb_insts` iterator must belong to
//! the EBB as reported by `inst_ebb()`.
//! - Every EBB must end in a terminator instruction, and no other instruction
//! - All instructions reached from the `block_insts` iterator must belong to
//! the block as reported by `inst_block()`.
//! - Every block must end in a terminator instruction, and no other instruction
//! can be a terminator.
//! - Every value in the `ebb_params` iterator belongs to the EBB as reported by `value_ebb`.
//! - Every value in the `block_params` iterator belongs to the block as reported by `value_block`.
//!
//! Instruction integrity
//!
//! - The instruction format must match the opcode.
//! - All result values must be created for multi-valued instructions.
//! - All referenced entities must exist. (Values, EBBs, stack slots, ...)
//! - All referenced entities must exist. (Values, blocks, stack slots, ...)
//! - Instructions must not reference (eg. branch to) the entry block.
//!
//! SSA form
//!
//! - Values must be defined by an instruction that exists and that is inserted in
//! an EBB, or be an argument of an existing EBB.
//! an block, or be an argument of an existing block.
//! - Values used by an instruction must dominate the instruction.
//!
//! Control flow graph and dominator tree integrity:
//!
//! - All predecessors in the CFG must be branches to the EBB.
//! - All branches to an EBB must be present in the CFG.
//! - All predecessors in the CFG must be branches to the block.
//! - All branches to an block must be present in the CFG.
//! - A recomputed dominator tree is identical to the existing one.
//!
//! Type checking
//!
//! - Compare input and output values against the opcode's type constraints.
//! For polymorphic opcodes, determine the controlling type variable first.
//! - Branches and jumps must pass arguments to destination EBBs that match the
//! - Branches and jumps must pass arguments to destination blocks that match the
//! expected types exactly. The number of arguments must match.
//! - All EBBs in a jump table must take no arguments.
//! - All blocks in a jump table must take no arguments.
//! - Function calls are type checked against their signature.
//! - The entry block must take arguments that match the signature of the current
//! function.
@@ -60,12 +60,12 @@ use self::flags::verify_flags;
use crate::dbg::DisplayList;
use crate::dominator_tree::DominatorTree;
use crate::entity::SparseSet;
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir;
use crate::ir::entities::AnyEntity;
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint};
use crate::ir::{
types, ArgumentLoc, Ebb, FuncRef, Function, GlobalValue, Inst, InstructionData, JumpTable,
types, ArgumentLoc, Block, FuncRef, Function, GlobalValue, Inst, InstructionData, JumpTable,
Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef, ValueList, ValueLoc,
};
use crate::isa::TargetIsa;
@@ -495,30 +495,30 @@ impl<'a> Verifier<'a> {
fn verify_jump_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
for (jt, jt_data) in &self.func.jump_tables {
for &ebb in jt_data.iter() {
self.verify_ebb(jt, ebb, errors)?;
for &block in jt_data.iter() {
self.verify_block(jt, block, errors)?;
}
}
Ok(())
}
/// Check that the given EBB can be encoded as a BB, by checking that only
/// branching instructions are ending the EBB.
fn encodable_as_bb(&self, ebb: Ebb, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
match self.func.is_ebb_basic(ebb) {
/// Check that the given block can be encoded as a BB, by checking that only
/// branching instructions are ending the block.
fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
match self.func.is_block_basic(block) {
Ok(()) => Ok(()),
Err((inst, message)) => errors.fatal((inst, self.context(inst), message)),
}
}
fn ebb_integrity(
fn block_integrity(
&self,
ebb: Ebb,
block: Block,
inst: Inst,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let is_terminator = self.func.dfg[inst].opcode().is_terminator();
let is_last_inst = self.func.layout.last_inst(ebb) == Some(inst);
let is_last_inst = self.func.layout.last_inst(block) == Some(inst);
if is_terminator && !is_last_inst {
// Terminating instructions only occur at the end of blocks.
@@ -527,30 +527,30 @@ impl<'a> Verifier<'a> {
self.context(inst),
format!(
"a terminator instruction was encountered before the end of {}",
ebb
block
),
));
}
if is_last_inst && !is_terminator {
return errors.fatal((ebb, "block does not end in a terminator instruction"));
return errors.fatal((block, "block does not end in a terminator instruction"));
}
// Instructions belong to the correct ebb.
let inst_ebb = self.func.layout.inst_ebb(inst);
if inst_ebb != Some(ebb) {
// Instructions belong to the correct block.
let inst_block = self.func.layout.inst_block(inst);
if inst_block != Some(block) {
return errors.fatal((
inst,
self.context(inst),
format!("should belong to {} not {:?}", ebb, inst_ebb),
format!("should belong to {} not {:?}", block, inst_block),
));
}
// Parameters belong to the correct ebb.
for &arg in self.func.dfg.ebb_params(ebb) {
// Parameters belong to the correct block.
for &arg in self.func.dfg.block_params(block) {
match self.func.dfg.value_def(arg) {
ValueDef::Param(arg_ebb, _) => {
if ebb != arg_ebb {
return errors.fatal((arg, format!("does not belong to {}", ebb)));
ValueDef::Param(arg_block, _) => {
if block != arg_block {
return errors.fatal((arg, format!("does not belong to {}", block)));
}
}
_ => {
@@ -656,13 +656,13 @@ impl<'a> Verifier<'a> {
ref args,
..
} => {
self.verify_ebb(inst, destination, errors)?;
self.verify_block(inst, destination, errors)?;
self.verify_value_list(inst, args, errors)?;
}
BranchTable {
table, destination, ..
} => {
self.verify_ebb(inst, destination, errors)?;
self.verify_block(inst, destination, errors)?;
self.verify_jump_table(inst, table, errors)?;
}
BranchTableBase { table, .. }
@@ -775,18 +775,18 @@ impl<'a> Verifier<'a> {
Ok(())
}
fn verify_ebb(
fn verify_block(
&self,
loc: impl Into<AnyEntity>,
e: Ebb,
e: Block,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
if !self.func.dfg.ebb_is_valid(e) || !self.func.layout.is_ebb_inserted(e) {
return errors.fatal((loc, format!("invalid ebb reference {}", e)));
if !self.func.dfg.block_is_valid(e) || !self.func.layout.is_block_inserted(e) {
return errors.fatal((loc, format!("invalid block reference {}", e)));
}
if let Some(entry_block) = self.func.layout.entry_block() {
if e == entry_block {
return errors.fatal((loc, format!("invalid reference to entry ebb {}", e)));
return errors.fatal((loc, format!("invalid reference to entry block {}", e)));
}
}
Ok(())
@@ -947,8 +947,8 @@ impl<'a> Verifier<'a> {
self.verify_value(loc_inst, v, errors)?;
let dfg = &self.func.dfg;
let loc_ebb = self.func.layout.pp_ebb(loc_inst);
let is_reachable = self.expected_domtree.is_reachable(loc_ebb);
let loc_block = self.func.layout.pp_block(loc_inst);
let is_reachable = self.expected_domtree.is_reachable(loc_block);
// SSA form
match dfg.value_def(v) {
@@ -961,12 +961,12 @@ impl<'a> Verifier<'a> {
format!("{} is defined by invalid instruction {}", v, def_inst),
));
}
// Defining instruction is inserted in an EBB.
if self.func.layout.inst_ebb(def_inst) == None {
// Defining instruction is inserted in an block.
if self.func.layout.inst_block(def_inst) == None {
return errors.fatal((
loc_inst,
self.context(loc_inst),
format!("{} is defined by {} which has no EBB", v, def_inst),
format!("{} is defined by {} which has no block", v, def_inst),
));
}
// Defining instruction dominates the instruction that uses the value.
@@ -990,33 +990,33 @@ impl<'a> Verifier<'a> {
}
}
}
ValueDef::Param(ebb, _) => {
// Value is defined by an existing EBB.
if !dfg.ebb_is_valid(ebb) {
ValueDef::Param(block, _) => {
// Value is defined by an existing block.
if !dfg.block_is_valid(block) {
return errors.fatal((
loc_inst,
self.context(loc_inst),
format!("{} is defined by invalid EBB {}", v, ebb),
format!("{} is defined by invalid block {}", v, block),
));
}
// Defining EBB is inserted in the layout
if !self.func.layout.is_ebb_inserted(ebb) {
// Defining block is inserted in the layout
if !self.func.layout.is_block_inserted(block) {
return errors.fatal((
loc_inst,
self.context(loc_inst),
format!("{} is defined by {} which is not in the layout", v, ebb),
format!("{} is defined by {} which is not in the layout", v, block),
));
}
// The defining EBB dominates the instruction using this value.
// The defining block dominates the instruction using this value.
if is_reachable
&& !self
.expected_domtree
.dominates(ebb, loc_inst, &self.func.layout)
.dominates(block, loc_inst, &self.func.layout)
{
return errors.fatal((
loc_inst,
self.context(loc_inst),
format!("uses value arg from non-dominating {}", ebb),
format!("uses value arg from non-dominating {}", block),
));
}
}
@@ -1081,17 +1081,17 @@ impl<'a> Verifier<'a> {
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
// We consider two `DominatorTree`s to be equal if they return the same immediate
// dominator for each EBB. Therefore the current domtree is valid if it matches the freshly
// dominator for each block. Therefore the current domtree is valid if it matches the freshly
// computed one.
for ebb in self.func.layout.ebbs() {
let expected = self.expected_domtree.idom(ebb);
let got = domtree.idom(ebb);
for block in self.func.layout.blocks() {
let expected = self.expected_domtree.idom(block);
let got = domtree.idom(block);
if got != expected {
return errors.fatal((
ebb,
block,
format!(
"invalid domtree, expected idom({}) = {:?}, got {:?}",
ebb, expected, got
block, expected, got
),
));
}
@@ -1100,37 +1100,37 @@ impl<'a> Verifier<'a> {
if domtree.cfg_postorder().len() != self.expected_domtree.cfg_postorder().len() {
return errors.fatal((
AnyEntity::Function,
"incorrect number of Ebbs in postorder traversal",
"incorrect number of Blocks in postorder traversal",
));
}
for (index, (&test_ebb, &true_ebb)) in domtree
for (index, (&test_block, &true_block)) in domtree
.cfg_postorder()
.iter()
.zip(self.expected_domtree.cfg_postorder().iter())
.enumerate()
{
if test_ebb != true_ebb {
if test_block != true_block {
return errors.fatal((
test_ebb,
test_block,
format!(
"invalid domtree, postorder ebb number {} should be {}, got {}",
index, true_ebb, test_ebb
"invalid domtree, postorder block number {} should be {}, got {}",
index, true_block, test_block
),
));
}
}
// We verify rpo_cmp on pairs of adjacent ebbs in the postorder
for (&prev_ebb, &next_ebb) in domtree.cfg_postorder().iter().adjacent_pairs() {
// We verify rpo_cmp on pairs of adjacent blocks in the postorder
for (&prev_block, &next_block) in domtree.cfg_postorder().iter().adjacent_pairs() {
if self
.expected_domtree
.rpo_cmp(prev_ebb, next_ebb, &self.func.layout)
.rpo_cmp(prev_block, next_block, &self.func.layout)
!= Ordering::Greater
{
return errors.fatal((
next_ebb,
next_block,
format!(
"invalid domtree, rpo_cmp does not says {} is greater than {}",
prev_ebb, next_ebb
prev_block, next_block
),
));
}
@@ -1139,26 +1139,26 @@ impl<'a> Verifier<'a> {
}
fn typecheck_entry_block_params(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
if let Some(ebb) = self.func.layout.entry_block() {
if let Some(block) = self.func.layout.entry_block() {
let expected_types = &self.func.signature.params;
let ebb_param_count = self.func.dfg.num_ebb_params(ebb);
let block_param_count = self.func.dfg.num_block_params(block);
if ebb_param_count != expected_types.len() {
if block_param_count != expected_types.len() {
return errors.fatal((
ebb,
block,
format!(
"entry block parameters ({}) must match function signature ({})",
ebb_param_count,
block_param_count,
expected_types.len()
),
));
}
for (i, &arg) in self.func.dfg.ebb_params(ebb).iter().enumerate() {
for (i, &arg) in self.func.dfg.block_params(block).iter().enumerate() {
let arg_type = self.func.dfg.value_type(arg);
if arg_type != expected_types[i].value_type {
errors.report((
ebb,
block,
format!(
"entry block parameter {} expected to have type {}, got {}",
i, expected_types[i], arg_type
@@ -1295,38 +1295,38 @@ impl<'a> Verifier<'a> {
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
match self.func.dfg.analyze_branch(inst) {
BranchInfo::SingleDest(ebb, _) => {
BranchInfo::SingleDest(block, _) => {
let iter = self
.func
.dfg
.ebb_params(ebb)
.block_params(block)
.iter()
.map(|&v| self.func.dfg.value_type(v));
self.typecheck_variable_args_iterator(inst, iter, errors)?;
}
BranchInfo::Table(table, ebb) => {
if let Some(ebb) = ebb {
let arg_count = self.func.dfg.num_ebb_params(ebb);
BranchInfo::Table(table, block) => {
if let Some(block) = block {
let arg_count = self.func.dfg.num_block_params(block);
if arg_count != 0 {
return errors.nonfatal((
inst,
self.context(inst),
format!(
"takes no arguments, but had target {} with {} arguments",
ebb, arg_count,
block, arg_count,
),
));
}
}
for ebb in self.func.jump_tables[table].iter() {
let arg_count = self.func.dfg.num_ebb_params(*ebb);
for block in self.func.jump_tables[table].iter() {
let arg_count = self.func.dfg.num_block_params(*block);
if arg_count != 0 {
return errors.nonfatal((
inst,
self.context(inst),
format!(
"takes no arguments, but had target {} with {} arguments",
ebb, arg_count,
block, arg_count,
),
));
}
@@ -1658,28 +1658,29 @@ impl<'a> Verifier<'a> {
cfg: &ControlFlowGraph,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let mut expected_succs = BTreeSet::<Ebb>::new();
let mut got_succs = BTreeSet::<Ebb>::new();
let mut expected_succs = BTreeSet::<Block>::new();
let mut got_succs = BTreeSet::<Block>::new();
let mut expected_preds = BTreeSet::<Inst>::new();
let mut got_preds = BTreeSet::<Inst>::new();
for ebb in self.func.layout.ebbs() {
expected_succs.extend(self.expected_cfg.succ_iter(ebb));
got_succs.extend(cfg.succ_iter(ebb));
for block in self.func.layout.blocks() {
expected_succs.extend(self.expected_cfg.succ_iter(block));
got_succs.extend(cfg.succ_iter(block));
let missing_succs: Vec<Ebb> = expected_succs.difference(&got_succs).cloned().collect();
let missing_succs: Vec<Block> =
expected_succs.difference(&got_succs).cloned().collect();
if !missing_succs.is_empty() {
errors.report((
ebb,
block,
format!("cfg lacked the following successor(s) {:?}", missing_succs),
));
continue;
}
let excess_succs: Vec<Ebb> = got_succs.difference(&expected_succs).cloned().collect();
let excess_succs: Vec<Block> = got_succs.difference(&expected_succs).cloned().collect();
if !excess_succs.is_empty() {
errors.report((
ebb,
block,
format!("cfg had unexpected successor(s) {:?}", excess_succs),
));
continue;
@@ -1687,15 +1688,18 @@ impl<'a> Verifier<'a> {
expected_preds.extend(
self.expected_cfg
.pred_iter(ebb)
.map(|BasicBlock { inst, .. }| inst),
.pred_iter(block)
.map(|BlockPredecessor { inst, .. }| inst),
);
got_preds.extend(
cfg.pred_iter(block)
.map(|BlockPredecessor { inst, .. }| inst),
);
got_preds.extend(cfg.pred_iter(ebb).map(|BasicBlock { inst, .. }| inst));
let missing_preds: Vec<Inst> = expected_preds.difference(&got_preds).cloned().collect();
if !missing_preds.is_empty() {
errors.report((
ebb,
block,
format!(
"cfg lacked the following predecessor(s) {:?}",
missing_preds
@@ -1707,7 +1711,7 @@ impl<'a> Verifier<'a> {
let excess_preds: Vec<Inst> = got_preds.difference(&expected_preds).cloned().collect();
if !excess_preds.is_empty() {
errors.report((
ebb,
block,
format!("cfg had unexpected predecessor(s) {:?}", excess_preds),
));
continue;
@@ -1969,12 +1973,12 @@ impl<'a> Verifier<'a> {
self.typecheck_entry_block_params(errors)?;
self.typecheck_function_signature(errors)?;
for ebb in self.func.layout.ebbs() {
if self.func.layout.first_inst(ebb).is_none() {
return errors.fatal((ebb, format!("{} cannot be empty", ebb)));
for block in self.func.layout.blocks() {
if self.func.layout.first_inst(block).is_none() {
return errors.fatal((block, format!("{} cannot be empty", block)));
}
for inst in self.func.layout.ebb_insts(ebb) {
self.ebb_integrity(ebb, inst, errors)?;
for inst in self.func.layout.block_insts(block) {
self.block_integrity(block, inst, errors)?;
self.instruction_integrity(inst, errors)?;
self.verify_safepoint_unused(inst, errors)?;
self.typecheck(inst, errors)?;
@@ -1982,7 +1986,7 @@ impl<'a> Verifier<'a> {
self.immediate_constraints(inst, errors)?;
}
self.encodable_as_bb(ebb, errors)?;
self.encodable_as_bb(block, errors)?;
}
verify_flags(self.func, &self.expected_cfg, self.isa, errors)?;
@@ -2039,20 +2043,20 @@ mod tests {
#[test]
fn bad_instruction_format() {
let mut func = Function::new();
let ebb0 = func.dfg.make_ebb();
func.layout.append_ebb(ebb0);
let block0 = func.dfg.make_block();
func.layout.append_block(block0);
let nullary_with_bad_opcode = func.dfg.make_inst(InstructionData::UnaryImm {
opcode: Opcode::F32const,
imm: 0.into(),
});
func.layout.append_inst(nullary_with_bad_opcode, ebb0);
func.layout.append_inst(nullary_with_bad_opcode, block0);
func.layout.append_inst(
func.dfg.make_inst(InstructionData::Jump {
opcode: Opcode::Jump,
destination: ebb0,
destination: block0,
args: EntityList::default(),
}),
ebb0,
block0,
);
let flags = &settings::Flags::new(settings::builder());
let verifier = Verifier::new(&func, flags.into());
@@ -2093,8 +2097,8 @@ mod tests {
fn test_printing_contextual_errors() {
// Build function.
let mut func = Function::new();
let ebb0 = func.dfg.make_ebb();
func.layout.append_ebb(ebb0);
let block0 = func.dfg.make_block();
func.layout.append_block(block0);
// Build instruction: v0, v1 = iconst 42
let inst = func.dfg.make_inst(InstructionData::UnaryImm {
@@ -2103,7 +2107,7 @@ mod tests {
});
func.dfg.append_result(inst, types::I32);
func.dfg.append_result(inst, types::I32);
func.layout.append_inst(inst, ebb0);
func.layout.append_inst(inst, block0);
// Setup verifier.
let mut errors = VerifierErrors::default();
@@ -2120,16 +2124,16 @@ mod tests {
}
#[test]
fn test_empty_ebb() {
fn test_empty_block() {
let mut func = Function::new();
let ebb0 = func.dfg.make_ebb();
func.layout.append_ebb(ebb0);
let block0 = func.dfg.make_block();
func.layout.append_block(block0);
let flags = &settings::Flags::new(settings::builder());
let verifier = Verifier::new(&func, flags.into());
let mut errors = VerifierErrors::default();
let _ = verifier.run(&mut errors);
assert_err_with_msg!(errors, "ebb0 cannot be empty");
assert_err_with_msg!(errors, "block0 cannot be empty");
}
}