ISLE: Enable the overlap checker (#5011)

This PR turns the overlap checker on by default, requiring the use of priorities to resolve overlap between rules.
This commit is contained in:
Trevor Elliott
2022-10-04 14:56:49 -07:00
committed by GitHub
parent 2607590d8c
commit a209cb63f5
14 changed files with 23 additions and 89 deletions

View File

@@ -1,7 +1,5 @@
;; aarch64 instruction selection and CLIF-to-MachInst lowering. ;; aarch64 instruction selection and CLIF-to-MachInst lowering.
(pragma overlap_errors)
;; The main lowering constructor term: takes a clif `Inst` and returns the ;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live. ;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput) (decl lower (Inst) InstOutput)

View File

@@ -1,7 +1,5 @@
;; riscv64 instruction selection and CLIF-to-MachInst lowering. ;; riscv64 instruction selection and CLIF-to-MachInst lowering.
(pragma overlap_errors)
;; The main lowering constructor term: takes a clif `Inst` and returns the ;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live. ;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput) (decl lower (Inst) InstOutput)

View File

@@ -1,7 +1,5 @@
;; s390x instruction selection and CLIF-to-MachInst lowering. ;; s390x instruction selection and CLIF-to-MachInst lowering.
(pragma overlap_errors)
;; The main lowering constructor term: takes a clif `Inst` and returns the ;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live. ;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput) (decl lower (Inst) InstOutput)

View File

@@ -1,8 +1,5 @@
;; x86-64 instruction selection and CLIF-to-MachInst lowering. ;; x86-64 instruction selection and CLIF-to-MachInst lowering.
;; Enable overlap checking for the x64 backend
(pragma overlap_errors)
;; The main lowering constructor term: takes a clif `Inst` and returns the ;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live. ;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput) (decl lower (Inst) InstOutput)

View File

@@ -11,7 +11,7 @@
(decl pure predicate () u32) (decl pure predicate () u32)
(rule (predicate) 1) (rule (predicate) 1)
(rule (C a b c (B d e)) (rule 2 (C a b c (B d e))
(if-let (B f g) d) (if-let (B f g) d)
(if-let h (A a b)) (if-let h (A a b))
(A h a)) (A h a))
@@ -20,10 +20,10 @@
(if (predicate)) (if (predicate))
42) 42)
(rule (C a b a b) (rule 1 (C a b a b)
(if-let x (D a b)) (if-let x (D a b))
x) x)
(decl pure D (u32 u32) u32) (decl pure D (u32 u32) u32)
(rule (D x 0) x) (rule (D x 0) x)
(rule (D 0 x) x) (rule 1 (D 0 x) x)

View File

@@ -7,7 +7,7 @@
(decl Rule (u32) u32) (decl Rule (u32) u32)
(rule (Rule (E1 a idx)) (rule 1 (Rule (E1 a idx))
(if-let (A.B) a) (if-let (A.B) a)
idx) idx)
(rule (Rule _) (rule (Rule _)

View File

@@ -56,7 +56,7 @@
(rule (rule
(Lower (Iadd ra rb)) (Lower (Iadd ra rb))
(MachInst.Add (UseInput ra) (UseInput rb))) (MachInst.Add (UseInput ra) (UseInput rb)))
(rule (rule 1
(Lower (Iadd (Producer (Iadd ra rb)) rc)) (Lower (Iadd (Producer (Iadd ra rb)) rc))
(MachInst.Add3 (UseInput ra) (UseInput rb) (UseInput rc))) (MachInst.Add3 (UseInput ra) (UseInput rb) (UseInput rc)))
(rule (rule

View File

@@ -51,12 +51,12 @@
(extern constructor put_in_reg put_in_reg) (extern constructor put_in_reg put_in_reg)
;; Simple rule for lowering adds. ;; Simple rule for lowering adds.
(rule (lower (HighLevelInst.Add a b)) (rule -1 (lower (HighLevelInst.Add a b))
(LowLevelInst.Add (LowLevelInst.Add
(AddrMode.RegReg (put_in_reg a) (put_in_reg b)))) (AddrMode.RegReg (put_in_reg a) (put_in_reg b))))
;; Simple rule for lowering loads. ;; Simple rule for lowering loads.
(rule (lower (HighLevelInst.Load addr)) (rule -1 (lower (HighLevelInst.Load addr))
(LowLevelInst.Load 0 (put_in_reg addr))) (LowLevelInst.Load 0 (put_in_reg addr)))
;; Declare an external extractor for extracting the instruction that defined a ;; Declare an external extractor for extracting the instruction that defined a
@@ -72,7 +72,7 @@
0))) 0)))
;; Rule to sink a load of a base address with a static offset into a single add. ;; Rule to sink a load of a base address with a static offset into a single add.
(rule (lower (HighLevelInst.Add (rule 1 (lower (HighLevelInst.Add
a a
(inst_result (HighLevelInst.Load (inst_result (HighLevelInst.Load
(inst_result (HighLevelInst.Add (inst_result (HighLevelInst.Add

View File

@@ -1,19 +0,0 @@
(type u32 (primitive u32))
(decl identity (u32) u32)
(extern extractor infallible identity identity)
(decl is_zero (u32) u32)
(extern extractor is_zero is_zero)
(decl test (u32) u32)
;; This exposes a bug where infallible extractors were running before fallible
;; ones, as the derived ordering for the `Extract` type was ordering them ahead
;; of the fallible ones. The result is that the fallible `is_zero` extractor
;; never runs, as the `identity` extractor will always succeed before it's
;; called.
(rule (test (identity x)) x)
(rule (test (is_zero x)) 2)

View File

@@ -1,23 +0,0 @@
mod extractor_ordering_bug;
struct Context;
impl extractor_ordering_bug::Context for Context {
fn is_zero(&mut self, val: u32) -> Option<u32> {
if val == 0 {
Some(val)
} else {
None
}
}
fn identity(&mut self, val: u32) -> u32 {
val
}
}
fn main() {
let mut ctx = Context;
assert_eq!(extractor_ordering_bug::constructor_test(&mut ctx, 0), Some(2));
assert_eq!(extractor_ordering_bug::constructor_test(&mut ctx, 1), Some(1));
}

View File

@@ -33,8 +33,7 @@ pub struct Ident(pub String, pub Pos);
/// Pragmas parsed with the `(pragma <ident>)` syntax. /// Pragmas parsed with the `(pragma <ident>)` syntax.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum Pragma { pub enum Pragma {
/// Enable overlap errors in the source. // currently, no pragmas are defined, but the infrastructure is useful to keep around
OverlapErrors,
} }
/// A declaration of a type. /// A declaration of a type.

View File

@@ -11,7 +11,6 @@ use crate::sema::{self, Rule, RuleId, Sym, TermEnv, TermId, TermKind, TypeEnv, V
/// Check for overlap. /// Check for overlap.
pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<()> { pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<()> {
let mut errors = check_overlaps(termenv).report(tyenv, termenv); let mut errors = check_overlaps(termenv).report(tyenv, termenv);
if termenv.overlap_errors {
errors.sort_by_key(|err| match err { errors.sort_by_key(|err| match err {
Error::OverlapError { rules, .. } => rules.first().unwrap().1.from, Error::OverlapError { rules, .. } => rules.first().unwrap().1.from,
_ => Pos::default(), _ => Pos::default(),
@@ -21,11 +20,6 @@ pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<()> {
1 => Err(errors.pop().unwrap()), 1 => Err(errors.pop().unwrap()),
_ => Err(Error::Errors(errors)), _ => Err(Error::Errors(errors)),
} }
} else {
use crate::log;
log!("found {} overlap errors", errors.len());
Ok(())
}
} }
/// A graph of rules that overlap in the ISLE source. The edges are undirected. /// A graph of rules that overlap in the ISLE source. The edges are undirected.

View File

@@ -200,9 +200,9 @@ impl<'a> Parser<'a> {
fn parse_pragma(&mut self) -> Result<Pragma> { fn parse_pragma(&mut self) -> Result<Pragma> {
let ident = self.parse_ident()?; let ident = self.parse_ident()?;
match ident.0.as_ref() { // currently, no pragmas are defined, but the infrastructure is useful to keep around
"overlap_errors" => Ok(Pragma::OverlapErrors), match ident.0.as_str() {
_ => Err(self.error(ident.1, format!("Unknown pragma '{}'", ident.0))), pragma => Err(self.error(ident.1, format!("Unknown pragma '{}'", pragma))),
} }
} }

View File

@@ -200,9 +200,6 @@ pub struct TermEnv {
/// defined implicit type-converter terms we can try to use to fit /// defined implicit type-converter terms we can try to use to fit
/// types together. /// types together.
pub converters: StableMap<(TypeId, TypeId), TermId>, pub converters: StableMap<(TypeId, TypeId), TermId>,
/// A flag indicating whether or not overlap between rules should be considered an error.
pub overlap_errors: bool,
} }
/// A term. /// A term.
@@ -789,7 +786,6 @@ impl TermEnv {
term_map: StableMap::new(), term_map: StableMap::new(),
rules: vec![], rules: vec![],
converters: StableMap::new(), converters: StableMap::new(),
overlap_errors: false,
}; };
env.collect_pragmas(defs); env.collect_pragmas(defs);
@@ -811,13 +807,9 @@ impl TermEnv {
Ok(env) Ok(env)
} }
fn collect_pragmas(&mut self, defs: &ast::Defs) { fn collect_pragmas(&mut self, _: &ast::Defs) {
for def in &defs.defs { // currently, no pragmas are defined, but the infrastructure is useful to keep around
match def { return;
ast::Def::Pragma(ast::Pragma::OverlapErrors) => self.overlap_errors = true,
_ => (),
}
}
} }
fn collect_term_sigs(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) { fn collect_term_sigs(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {