summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cranelift-codegen/src/verifier/liveness.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/verifier/liveness.rs')
-rw-r--r--third_party/rust/cranelift-codegen/src/verifier/liveness.rs235
1 files changed, 235 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/verifier/liveness.rs b/third_party/rust/cranelift-codegen/src/verifier/liveness.rs
new file mode 100644
index 0000000000..ac5ee62c42
--- /dev/null
+++ b/third_party/rust/cranelift-codegen/src/verifier/liveness.rs
@@ -0,0 +1,235 @@
+//! Liveness verifier.
+
+use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
+use crate::ir::entities::AnyEntity;
+use crate::ir::{ExpandedProgramPoint, Function, ProgramPoint, Value};
+use crate::isa::TargetIsa;
+use crate::regalloc::liveness::Liveness;
+use crate::regalloc::liverange::LiveRange;
+use crate::timing;
+use crate::verifier::{VerifierErrors, VerifierStepResult};
+
+/// Verify liveness information for `func`.
+///
+/// The provided control flow graph is assumed to be sound.
+///
+/// - 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 a 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
+/// all values.
+pub fn verify_liveness(
+ isa: &dyn TargetIsa,
+ func: &Function,
+ cfg: &ControlFlowGraph,
+ liveness: &Liveness,
+ errors: &mut VerifierErrors,
+) -> VerifierStepResult<()> {
+ let _tt = timing::verify_liveness();
+ let verifier = LivenessVerifier {
+ isa,
+ func,
+ cfg,
+ liveness,
+ };
+ verifier.check_blocks(errors)?;
+ verifier.check_insts(errors)?;
+ Ok(())
+}
+
+struct LivenessVerifier<'a> {
+ isa: &'a dyn TargetIsa,
+ func: &'a Function,
+ cfg: &'a ControlFlowGraph,
+ liveness: &'a Liveness,
+}
+
+impl<'a> LivenessVerifier<'a> {
+ /// 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((block, format!("block arg {} has no live range", val)))
+ }
+ };
+ self.check_lr(block.into(), val, lr, errors)?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Check all instructions.
+ fn check_insts(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
+ 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.
+ for &val in self.func.dfg.inst_results(inst) {
+ let lr = match self.liveness.get(val) {
+ Some(lr) => lr,
+ None => return errors.fatal((inst, format!("{} has no live range", val))),
+ };
+ self.check_lr(inst.into(), val, lr, errors)?;
+
+ if encoding.is_legal() {
+ // A legal instruction is not allowed to define ghost values.
+ if lr.affinity.is_unassigned() {
+ return errors.fatal((
+ inst,
+ format!(
+ "{} is a ghost value defined by a real [{}] instruction",
+ val,
+ self.isa.encoding_info().display(encoding)
+ ),
+ ));
+ }
+ } else if !lr.affinity.is_unassigned() {
+ // A non-encoded instruction can only define ghost values.
+ return errors.fatal((
+ inst,
+ format!(
+ "{} is a real {} value defined by a ghost instruction",
+ val,
+ lr.affinity.display(&self.isa.register_info())
+ ),
+ ));
+ }
+ }
+
+ // Check the uses.
+ for &val in self.func.dfg.inst_args(inst) {
+ let lr = match self.liveness.get(val) {
+ Some(lr) => lr,
+ None => return errors.fatal((inst, format!("{} has no live range", val))),
+ };
+
+ 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)));
+ }
+
+ // A legal instruction is not allowed to depend on ghost values.
+ if encoding.is_legal() && lr.affinity.is_unassigned() {
+ return errors.fatal((
+ inst,
+ format!(
+ "{} is a ghost value used by a real [{}] instruction",
+ val,
+ self.isa.encoding_info().display(encoding),
+ ),
+ ));
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Check the integrity of the live range `lr`.
+ fn check_lr(
+ &self,
+ def: ProgramPoint,
+ val: Value,
+ lr: &LiveRange,
+ errors: &mut VerifierErrors,
+ ) -> VerifierStepResult<()> {
+ let l = &self.func.layout;
+
+ let loc: AnyEntity = match def.into() {
+ ExpandedProgramPoint::Block(e) => e.into(),
+ ExpandedProgramPoint::Inst(i) => i.into(),
+ };
+ if lr.def() != def {
+ return errors.fatal((
+ loc,
+ format!("Wrong live range def ({}) for {}", lr.def(), val),
+ ));
+ }
+ if lr.is_dead() {
+ if !lr.is_local() {
+ return errors.fatal((loc, format!("Dead live range {} should be local", val)));
+ } else {
+ return Ok(());
+ }
+ }
+ 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::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_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 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, block),
+ ));
+ }
+ 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, block, end
+ ),
+ ));
+ }
+ };
+
+ // Check all the blocks in the interval independently.
+ loop {
+ // 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, block
+ ),
+ ));
+ }
+ }
+
+ if block == end_block {
+ break;
+ }
+ block = match l.next_block(block) {
+ Some(e) => e,
+ None => {
+ return errors.fatal((
+ loc,
+ format!("end of {} livein ({}) never reached", val, end_block),
+ ));
+ }
+ };
+ }
+ }
+
+ Ok(())
+ }
+}