Verify that the instruction encoding matches what the ISA would encode.
Fixes #69
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
d0d5f3bb26
commit
43ce26e64b
21
cranelift/filetests/isa/riscv/verify-encoding.cton
Normal file
21
cranelift/filetests/isa/riscv/verify-encoding.cton
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -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?
|
// Should we run the verifier before this test?
|
||||||
if !context.verified && test.needs_verifier() {
|
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;
|
context.verified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ impl SubTest for TestVerifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match verify_function(func) {
|
match verify_function(func, context.isa) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
match expected {
|
match expected {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// The `TargetIsa` argument is currently unused, but the verifier will soon be able to also
|
/// The `TargetIsa` argument is currently unused, but the verifier will soon be able to also
|
||||||
/// check ISA-dependent constraints.
|
/// check ISA-dependent constraints.
|
||||||
pub fn verify<'a, ISA: Into<Option<&'a TargetIsa>>>(&self, _isa: ISA) -> verifier::Result {
|
pub fn verify<'a, ISA: Into<Option<&'a TargetIsa>>>(&self, isa: ISA) -> verifier::Result {
|
||||||
verifier::verify_context(&self.func, &self.cfg, &self.domtree)
|
verifier::verify_context(&self.func, &self.cfg, &self.domtree, isa.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the verifier only if the `enable_verifier` setting is true.
|
/// Run the verifier only if the `enable_verifier` setting is true.
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ impl Context {
|
|||||||
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
||||||
|
|
||||||
if isa.flags().enable_verifier() {
|
if isa.flags().enable_verifier() {
|
||||||
verify_context(func, cfg, domtree)?;
|
verify_context(func, cfg, domtree, Some(isa))?;
|
||||||
verify_liveness(isa, func, cfg, &self.liveness)?;
|
verify_liveness(isa, func, cfg, &self.liveness)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ use ir::entities::AnyEntity;
|
|||||||
use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo};
|
use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo};
|
||||||
use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot,
|
use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot,
|
||||||
Value, Type};
|
Value, Type};
|
||||||
|
use isa::TargetIsa;
|
||||||
use std::error as std_error;
|
use std::error as std_error;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::result;
|
use std::result;
|
||||||
@@ -109,14 +110,18 @@ impl std_error::Error for Error {
|
|||||||
pub type Result = result::Result<(), Error>;
|
pub type Result = result::Result<(), Error>;
|
||||||
|
|
||||||
/// Verify `func`.
|
/// Verify `func`.
|
||||||
pub fn verify_function(func: &Function) -> Result {
|
pub fn verify_function(func: &Function, isa: Option<&TargetIsa>) -> Result {
|
||||||
Verifier::new(func).run()
|
Verifier::new(func, isa).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify `func` after checking the integrity of associated context data structures `cfg` and
|
/// Verify `func` after checking the integrity of associated context data structures `cfg` and
|
||||||
/// `domtree`.
|
/// `domtree`.
|
||||||
pub fn verify_context(func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) -> Result {
|
pub fn verify_context(func: &Function,
|
||||||
let verifier = Verifier::new(func);
|
cfg: &ControlFlowGraph,
|
||||||
|
domtree: &DominatorTree,
|
||||||
|
isa: Option<&TargetIsa>)
|
||||||
|
-> Result {
|
||||||
|
let verifier = Verifier::new(func, isa);
|
||||||
verifier.cfg_integrity(cfg)?;
|
verifier.cfg_integrity(cfg)?;
|
||||||
verifier.domtree_integrity(domtree)?;
|
verifier.domtree_integrity(domtree)?;
|
||||||
verifier.run()
|
verifier.run()
|
||||||
@@ -126,16 +131,18 @@ struct Verifier<'a> {
|
|||||||
func: &'a Function,
|
func: &'a Function,
|
||||||
cfg: ControlFlowGraph,
|
cfg: ControlFlowGraph,
|
||||||
domtree: DominatorTree,
|
domtree: DominatorTree,
|
||||||
|
isa: Option<&'a TargetIsa>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Verifier<'a> {
|
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 cfg = ControlFlowGraph::with_function(func);
|
||||||
let domtree = DominatorTree::with_function(func, &cfg);
|
let domtree = DominatorTree::with_function(func, &cfg);
|
||||||
Verifier {
|
Verifier {
|
||||||
func: func,
|
func: func,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
domtree: domtree,
|
domtree: domtree,
|
||||||
|
isa: isa,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,6 +664,42 @@ impl<'a> Verifier<'a> {
|
|||||||
Ok(())
|
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 {
|
pub fn run(&self) -> Result {
|
||||||
self.typecheck_entry_block_arguments()?;
|
self.typecheck_entry_block_arguments()?;
|
||||||
for ebb in self.func.layout.ebbs() {
|
for ebb in self.func.layout.ebbs() {
|
||||||
@@ -664,8 +707,10 @@ impl<'a> Verifier<'a> {
|
|||||||
self.ebb_integrity(ebb, inst)?;
|
self.ebb_integrity(ebb, inst)?;
|
||||||
self.instruction_integrity(inst)?;
|
self.instruction_integrity(inst)?;
|
||||||
self.typecheck(inst)?;
|
self.typecheck(inst)?;
|
||||||
|
self.verify_encoding(inst)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -692,7 +737,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn empty() {
|
fn empty() {
|
||||||
let func = Function::new();
|
let func = Function::new();
|
||||||
let verifier = Verifier::new(&func);
|
let verifier = Verifier::new(&func, None);
|
||||||
assert_eq!(verifier.run(), Ok(()));
|
assert_eq!(verifier.run(), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,7 +750,7 @@ mod tests {
|
|||||||
func.dfg
|
func.dfg
|
||||||
.make_inst(InstructionData::Nullary { opcode: Opcode::Jump });
|
.make_inst(InstructionData::Nullary { opcode: Opcode::Jump });
|
||||||
func.layout.append_inst(nullary_with_bad_opcode, ebb0);
|
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");
|
assert_err_with_msg!(verifier.run(), "instruction format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user