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:
@@ -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
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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, _) => {
|
||||
|
||||
@@ -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),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user