Skip to content
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

Merged
merged 27 commits into from
Sep 21, 2022

Conversation

elliottt
Copy link
Member

@elliottt elliottt commented Sep 13, 2022

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):

13: (rule (sse_xor_op $F32X4) (SseOpcode.Xorps))
14: (rule (sse_xor_op $F64X2) (SseOpcode.Xorpd))
15: (rule (sse_xor_op (multi_lane _bits _lanes)) (SseOpcode.Pxor))

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.

Error:
  × overlap error: rules are overlapping
  │   ../x64.isle:15:0
  │   ../x64.isle:13:0
  │   ../x64.isle:14:0

For another example, consider the following:

1: (rule (example $F32 25) ...)
2: (rule (example $F32 (fallible_extractor y)) ...)
3: (rule (example $F64 _) ...)

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:

1: (rule (example $F32 (fallible_extractor 25)) ...)
2: (rule (example $F32 (fallible_extractor 31)) ...)
3: (rule (example $F64 _) ...)

We consider there to be no overlap in the rules as fallible extractors are expected to be pure.

@elliottt elliottt marked this pull request as ready for review September 13, 2022 17:14
Comment on lines +122 to +124
Error::OverlapError { msg, rules, .. } => {
writeln!(f, "overlap error: {}\n{}", msg, OverlappingRules(&rules))
}
Copy link
Member Author

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.

@elliottt elliottt changed the title wip: Add an overlap checker to ISLE Add an overlap checker to ISLE Sep 13, 2022
Copy link
Contributor

@jameysharp jameysharp left a 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/compile.rs Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
Comment on lines 264 to 256
match self.get_term(id).kind {
TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor),
..
} => true,
_ => false,
}
Copy link
Contributor

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:

Suggested change
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),
..
},
)

Copy link
Member Author

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?

Copy link
Contributor

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 Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
cranelift/isle/isle/src/overlap.rs Outdated Show resolved Hide resolved
@github-actions github-actions bot added cranelift Issues related to the Cranelift code generator isle Related to the ISLE domain-specific language labels Sep 13, 2022
@github-actions
Copy link

Subscribe to Label Action

cc @cfallin, @fitzgen

This issue or pull request has been labeled: "cranelift", "isle"

Thus the following users have been cc'd because of the following labels:

  • cfallin: isle
  • fitzgen: isle

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

@elliottt elliottt marked this pull request as draft September 14, 2022 17:22

/// The patterns of a rule turned into a work-queue for overlap checking.
#[derive(Debug, Clone)]
struct Row {
Copy link
Member Author

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?

@elliottt elliottt marked this pull request as ready for review September 15, 2022 21:18
Comment on lines 264 to 256
match self.get_term(id).kind {
TermKind::Decl {
constructor_kind: Some(ConstructorKind::InternalConstructor),
..
} => true,
_ => false,
}
Copy link
Contributor

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!

}

/// The ids of all [`Rule`]s defined for this term.
fn rules_for_term(&self, id: TermId) -> Vec<RuleId> {
Copy link
Contributor

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().

/// Enum variant constructors.
Variant {
id: TermId,
single_case: bool,
Copy link
Contributor

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.

Copy link
Member Author

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 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.

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.

Copy link
Contributor

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. 😅

Copy link
Member

@cfallin cfallin left a 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:

cranelift/isle/isle/src/overlap.rs Show resolved Hide resolved
@elliottt elliottt merged commit b167172 into bytecodealliance:main Sep 21, 2022
@@ -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"
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member

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!

Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cranelift Issues related to the Cranelift code generator isle Related to the ISLE domain-specific language
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants