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.
(pragma overlap_errors)
;; The main lowering constructor term: takes a clif `Inst` and returns the
;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput)

View File

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

View File

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

View File

@@ -1,8 +1,5 @@
;; 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
;; register(s) within which the lowered instruction's result values live.
(decl lower (Inst) InstOutput)

View File

@@ -11,7 +11,7 @@
(decl pure predicate () u32)
(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 h (A a b))
(A h a))
@@ -20,10 +20,10 @@
(if (predicate))
42)
(rule (C a b a b)
(rule 1 (C a b a b)
(if-let x (D a b))
x)
(decl pure D (u32 u32) u32)
(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)
(rule (Rule (E1 a idx))
(rule 1 (Rule (E1 a idx))
(if-let (A.B) a)
idx)
(rule (Rule _)

View File

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

View File

@@ -51,12 +51,12 @@
(extern constructor put_in_reg put_in_reg)
;; Simple rule for lowering adds.
(rule (lower (HighLevelInst.Add a b))
(rule -1 (lower (HighLevelInst.Add a b))
(LowLevelInst.Add
(AddrMode.RegReg (put_in_reg a) (put_in_reg b))))
;; Simple rule for lowering loads.
(rule (lower (HighLevelInst.Load addr))
(rule -1 (lower (HighLevelInst.Load addr))
(LowLevelInst.Load 0 (put_in_reg addr)))
;; Declare an external extractor for extracting the instruction that defined a
@@ -72,7 +72,7 @@
0)))
;; 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
(inst_result (HighLevelInst.Load
(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.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Pragma {
/// Enable overlap errors in the source.
OverlapErrors,
// currently, no pragmas are defined, but the infrastructure is useful to keep around
}
/// 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.
pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<()> {
let mut errors = check_overlaps(termenv).report(tyenv, termenv);
if termenv.overlap_errors {
errors.sort_by_key(|err| match err {
Error::OverlapError { rules, .. } => rules.first().unwrap().1.from,
_ => Pos::default(),
@@ -21,11 +20,6 @@ pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<()> {
1 => Err(errors.pop().unwrap()),
_ => 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.

View File

@@ -200,9 +200,9 @@ impl<'a> Parser<'a> {
fn parse_pragma(&mut self) -> Result<Pragma> {
let ident = self.parse_ident()?;
match ident.0.as_ref() {
"overlap_errors" => Ok(Pragma::OverlapErrors),
_ => Err(self.error(ident.1, format!("Unknown pragma '{}'", ident.0))),
// currently, no pragmas are defined, but the infrastructure is useful to keep around
match ident.0.as_str() {
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
/// types together.
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.
@@ -789,7 +786,6 @@ impl TermEnv {
term_map: StableMap::new(),
rules: vec![],
converters: StableMap::new(),
overlap_errors: false,
};
env.collect_pragmas(defs);
@@ -811,13 +807,9 @@ impl TermEnv {
Ok(env)
}
fn collect_pragmas(&mut self, defs: &ast::Defs) {
for def in &defs.defs {
match def {
ast::Def::Pragma(ast::Pragma::OverlapErrors) => self.overlap_errors = true,
_ => (),
}
}
fn collect_pragmas(&mut self, _: &ast::Defs) {
// currently, no pragmas are defined, but the infrastructure is useful to keep around
return;
}
fn collect_term_sigs(&mut self, tyenv: &mut TypeEnv, defs: &ast::Defs) {