-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an overlap checker to ISLE #4906
Add an overlap checker to ISLE #4906
Conversation
Error::OverlapError { msg, rules, .. } => { | ||
writeln!(f, "overlap error: {}\n{}", msg, OverlappingRules(&rules)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the most helpful error output, but I'm assuming that as we start burning down the list of overlaps we'll figure out what's most useful to identify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't gotten to reviewing the actual overlap checking, but here are some initial thoughts.
cranelift/isle/isle/src/overlap.rs
Outdated
match self.get_term(id).kind { | ||
TermKind::Decl { | ||
constructor_kind: Some(ConstructorKind::InternalConstructor), | ||
.. | ||
} => true, | ||
_ => false, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure whether it's an improvement here, but there's a handy macro in the standard library for this pattern:
match self.get_term(id).kind { | |
TermKind::Decl { | |
constructor_kind: Some(ConstructorKind::InternalConstructor), | |
.. | |
} => true, | |
_ => false, | |
} | |
matches!( | |
self.get_term(id).kind, | |
TermKind::Decl { | |
constructor_kind: Some(ConstructorKind::InternalConstructor), | |
.. | |
}, | |
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could go either way on this, do you feel strongly about switching to the macro?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not feel strongly about that, nope!
Subscribe to Label Action
This issue or pull request has been labeled: "cranelift", "isle"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
2aa1fca
to
2c0ea5a
Compare
cranelift/isle/isle/src/overlap.rs
Outdated
|
||
/// The patterns of a rule turned into a work-queue for overlap checking. | ||
#[derive(Debug, Clone)] | ||
struct Row { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a bad name for this type now, how about Patterns
?
cranelift/isle/isle/src/overlap.rs
Outdated
match self.get_term(id).kind { | ||
TermKind::Decl { | ||
constructor_kind: Some(ConstructorKind::InternalConstructor), | ||
.. | ||
} => true, | ||
_ => false, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not feel strongly about that, nope!
cranelift/isle/isle/src/overlap.rs
Outdated
} | ||
|
||
/// The ids of all [`Rule`]s defined for this term. | ||
fn rules_for_term(&self, id: TermId) -> Vec<RuleId> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is immediately converted back to an iterator by its only caller, so I'd return impl Iterator<Item = RuleId>
instead of calling .collect()
.
cranelift/isle/isle/src/overlap.rs
Outdated
/// Enum variant constructors. | ||
Variant { | ||
id: TermId, | ||
single_case: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, this field is never read and can be removed, along with the is_single_constructor_enum
method. I'm puzzled why you didn't get a build warning about this...
Once you do that I believe Variant
and Extractor
are treated the same way everywhere so you can merge them. I'm assuming that the same TermId
is never used both for an enum variant and for an extractor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, this field is never read and can be removed, along with the
is_single_constructor_enum
method. I'm puzzled why you didn't get a build warning about this...
This was originally when I was going to try to reason about the interaction of single-constructor enums to know that no other case was ever possible. I'm not sure there's anything we can do with that information now, so it should be removed.
Once you do that I believe
Variant
andExtractor
are treated the same way everywhere so you can merge them. I'm assuming that the sameTermId
is never used both for an enum variant and for an extractor.
We'd still need to be able to differentiate between a Variant and an Extractor for the case where the match fails: in that case the variant means that the two don't overlap, while the extractor could still potentially fire.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I figured that out after writing this comment. 😅
This also puts all rule-pairs in a single list that rayon can efficiently partition across multiple CPUs. I previously suggested two layers of par_iters and relying on work-stealing to balance things out, but this is simpler to read and also ought to work more smoothly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great overall -- I just have one question below regarding and-patterns:
@@ -11,12 +11,14 @@ version = "0.89.0" | |||
[dependencies] | |||
log = { version = "0.4", optional = true } | |||
miette = { version = "5.1.0", optional = true } | |||
rayon = "^1.5" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds a lot of dependencies to Cranelift. Is the overlap checking really so expensive that paralleling it makes a lot of difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked it. Takes less than half a second total even when not parallelizing. Will open a PR to remove rayon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We depend on rayon elsewhere in cranelift, which is why we added it to the overlap checker. Were you removing it completely from cranelift?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wasmtime-cranelift depends on rayon, but cranelift itself doesn't apart from cranelift-isle because of this PR. bjorn3/rustc_codegen_cranelift@266e967 reverted cg_clif back to cranelift 0.88.1 from before this PR. This commit removes rayon from cg_clif's Cargo.lock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see. My assumption had been that since it was in cranelift/Cargo.toml
that it would be okay to rely on it in isle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW cranelift/Cargo.toml
defines the crate for cranelift-tools
, the CLI utility; we do want to keep dependencies of cranelift-codegen
as minimal as possible, and since cranelift-isle
is a build-dep of that I'd consider the same policy to apply. Sorry for not catching this earlier!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember correctly, when we added the rayon dependency, overlap checking was dramatically slower, so parallelism shaved a lot of time off the wasmtime build. But by the time we actually merged overlap checking to main
, we'd fixed its performance, so rayon didn't matter any more; we just didn't re-evaluate it at that point.
So thank you for #5101: it's a good change.
Introduce overlap checking for ISLE as the start of addressing #4717.
The overlap checker detects cases where ISLE rules could fire for the same input. Consider the following example from the x64 lowerings (line numbers added for clarity):
Rule 15 overlaps separately with rules 13 and 14, but rules 13 and 14 don't overlap with each other. With overlap checking enabled this will be reported as an overlap of all three rules, where the rule on line 15 will be identified as the source of the overlap (error messages could definitely be improved here). In this case the overlap can be resolved by setting a lower priority for rule 3.
For another example, consider the following:
In this case, only the rules on lines 1 and 2 overlap, as we don't know that the fallible extractor won't fire successfully on the input
25
. As the rule on line 3 has a different leading pattern ($F64
) it won't be part of the overlap group generated.However, if the example looks instead like the following:
We consider there to be no overlap in the rules as fallible extractors are expected to be pure.