diff --git a/cranelift/filetests/isa/riscv/verify-encoding.cton b/cranelift/filetests/isa/riscv/verify-encoding.cton new file mode 100644 index 0000000000..73d28c842a --- /dev/null +++ b/cranelift/filetests/isa/riscv/verify-encoding.cton @@ -0,0 +1,21 @@ +test verifier +isa riscv + +function RV32I(i32 link [%x1]) -> i32 link [%x1] { + fn0 = function foo() + +ebb0(v9999: i32): + ; iconst.i32 needs legalizing, so it should throw a + [R#0,-] v1 = iconst.i32 1 ; error: Instruction failed to re-encode + return v9999 +} + +function RV32I(i32 link [%x1]) -> i32 link [%x1] { + fn0 = function foo() + +ebb0(v9999: i32): + v1 = iconst.i32 1 + v2 = iconst.i32 2 + [R#0,-] v3 = iadd v1, v2 ; error: Instruction re-encoding + return v9999 +} diff --git a/cranelift/src/filetest/runone.rs b/cranelift/src/filetest/runone.rs index bc16725d74..60231d22f4 100644 --- a/cranelift/src/filetest/runone.rs +++ b/cranelift/src/filetest/runone.rs @@ -116,7 +116,7 @@ fn run_one_test<'a>(tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>), // Should we run the verifier before this test? if !context.verified && test.needs_verifier() { - verify_function(&func).map_err(|e| pretty_verifier_error(&func, e))?; + verify_function(&func, isa).map_err(|e| pretty_verifier_error(&func, e))?; context.verified = true; } diff --git a/cranelift/src/filetest/verifier.rs b/cranelift/src/filetest/verifier.rs index 5812dc9db0..1dd5cc88b4 100644 --- a/cranelift/src/filetest/verifier.rs +++ b/cranelift/src/filetest/verifier.rs @@ -51,7 +51,7 @@ impl SubTest for TestVerifier { } } - match verify_function(func) { + match verify_function(func, context.isa) { Ok(_) => { match expected { None => Ok(()), diff --git a/lib/cretonne/src/context.rs b/lib/cretonne/src/context.rs index e4ba8a0667..d8b6af4d0b 100644 --- a/lib/cretonne/src/context.rs +++ b/lib/cretonne/src/context.rs @@ -53,8 +53,8 @@ impl Context { /// /// The `TargetIsa` argument is currently unused, but the verifier will soon be able to also /// check ISA-dependent constraints. - pub fn verify<'a, ISA: Into>>(&self, _isa: ISA) -> verifier::Result { - verifier::verify_context(&self.func, &self.cfg, &self.domtree) + pub fn verify<'a, ISA: Into>>(&self, isa: ISA) -> verifier::Result { + verifier::verify_context(&self.func, &self.cfg, &self.domtree, isa.into()) } /// Run the verifier only if the `enable_verifier` setting is true. diff --git a/lib/cretonne/src/regalloc/context.rs b/lib/cretonne/src/regalloc/context.rs index 8f399c65de..b57edebb5f 100644 --- a/lib/cretonne/src/regalloc/context.rs +++ b/lib/cretonne/src/regalloc/context.rs @@ -63,7 +63,7 @@ impl Context { .run(isa, func, domtree, &mut self.liveness, &mut self.tracker); if isa.flags().enable_verifier() { - verify_context(func, cfg, domtree)?; + verify_context(func, cfg, domtree, Some(isa))?; verify_liveness(isa, func, cfg, &self.liveness)?; } Ok(()) diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index 1cc8f740dd..6bf9b155c6 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -58,6 +58,7 @@ use ir::entities::AnyEntity; use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo}; use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot, Value, Type}; +use isa::TargetIsa; use std::error as std_error; use std::fmt::{self, Display, Formatter}; use std::result; @@ -109,14 +110,18 @@ impl std_error::Error for Error { pub type Result = result::Result<(), Error>; /// Verify `func`. -pub fn verify_function(func: &Function) -> Result { - Verifier::new(func).run() +pub fn verify_function(func: &Function, isa: Option<&TargetIsa>) -> Result { + Verifier::new(func, isa).run() } /// Verify `func` after checking the integrity of associated context data structures `cfg` and /// `domtree`. -pub fn verify_context(func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) -> Result { - let verifier = Verifier::new(func); +pub fn verify_context(func: &Function, + cfg: &ControlFlowGraph, + domtree: &DominatorTree, + isa: Option<&TargetIsa>) + -> Result { + let verifier = Verifier::new(func, isa); verifier.cfg_integrity(cfg)?; verifier.domtree_integrity(domtree)?; verifier.run() @@ -126,16 +131,18 @@ struct Verifier<'a> { func: &'a Function, cfg: ControlFlowGraph, domtree: DominatorTree, + isa: Option<&'a TargetIsa>, } impl<'a> Verifier<'a> { - pub fn new(func: &'a Function) -> Verifier { + pub fn new(func: &'a Function, isa: Option<&'a TargetIsa>) -> Verifier<'a> { let cfg = ControlFlowGraph::with_function(func); let domtree = DominatorTree::with_function(func, &cfg); Verifier { func: func, cfg: cfg, domtree: domtree, + isa: isa, } } @@ -657,6 +664,42 @@ impl<'a> Verifier<'a> { Ok(()) } + /// If the verifier has been set up with an ISA, make sure that the recorded encoding for the + /// instruction (if any) matches how the ISA would encode it. + fn verify_encoding(&self, inst: Inst) -> Result { + if let Some(isa) = self.isa { + let encoding = self.func + .encodings + .get(inst) + .cloned() + .unwrap_or_default(); + if encoding.is_legal() { + let verify_encoding = + isa.encode(&self.func.dfg, + &self.func.dfg[inst], + self.func.dfg.ctrl_typevar(inst)); + match verify_encoding { + Ok(verify_encoding) => { + if verify_encoding != encoding { + return err!(inst, + "Instruction re-encoding {} doesn't match {}", + isa.encoding_info().display(verify_encoding), + isa.encoding_info().display(encoding)); + } + } + Err(e) => { + return err!(inst, + "Instruction failed to re-encode {}: {:?}", + isa.encoding_info().display(encoding), + e) + } + } + } + } + + Ok(()) + } + pub fn run(&self) -> Result { self.typecheck_entry_block_arguments()?; for ebb in self.func.layout.ebbs() { @@ -664,8 +707,10 @@ impl<'a> Verifier<'a> { self.ebb_integrity(ebb, inst)?; self.instruction_integrity(inst)?; self.typecheck(inst)?; + self.verify_encoding(inst)?; } } + Ok(()) } } @@ -692,7 +737,7 @@ mod tests { #[test] fn empty() { let func = Function::new(); - let verifier = Verifier::new(&func); + let verifier = Verifier::new(&func, None); assert_eq!(verifier.run(), Ok(())); } @@ -705,7 +750,7 @@ mod tests { func.dfg .make_inst(InstructionData::Nullary { opcode: Opcode::Jump }); func.layout.append_inst(nullary_with_bad_opcode, ebb0); - let verifier = Verifier::new(&func); + let verifier = Verifier::new(&func, None); assert_err_with_msg!(verifier.run(), "instruction format"); } }