-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Add #[loop_match]
for improved DFA codegen
#138780
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
Conversation
Some changes occurred in match checking cc @Nadrieril Some changes occurred in compiler/rustc_passes/src/check_attr.rs Some changes occurred in cc @BoxyUwU |
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.
Thanks @folkertdev for putting up this PR. The big picture looks right, in terms of the behavior of the tests and how to approach the experiment in terms of starting with the attributes for thiis.
This is a first partial pass on the details.
@rustbot author
This comment has been minimized.
This comment has been minimized.
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.
Thanks for the detailed review!
I've fixed a bunch of the low-hanging fruit (e.g. in the tests). For the actual pattern matching logic, I have a branch with what I believe is a better solution that re-uses more existing pattern matching infra. We'll come back to that here once björn has had a chance to look at it.
Some changes occurred in exhaustiveness checking cc @Nadrieril Some changes occurred in match lowering cc @Nadrieril |
☔ The latest upstream changes (presumably #138974) made this pull request unmergeable. Please resolve the merge conflicts. |
368f722
to
a89dcbe
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
f294773
to
6fe6909
Compare
This comment has been minimized.
This comment has been minimized.
b3a87ed
to
7d88da4
Compare
We've done a bunch of work here, and I believe all of the earlier review comments have now been dealt with. @rustbot ready |
@rustbot author As a lang matter, this is looking reasonable to me in terms of a lang experiment. As an impl matter, this is starting to look not unreasonable to me, but I'd like for @Nadrieril to also have a look if he's able. r? @Nadrieril @Nadrieril: I still need to raise this in a lang meeting to confirm that everyone is happy to see the experiment here in light of earlier objections, so please don't merge this just yet. You can leave it back in my hands after you're happy with the impl. Also CC @oli-obk as this work is carrying over some |
Reminder, once the PR becomes ready for a review, use |
☔ The latest upstream changes (presumably #142906) made this pull request unmergeable. Please resolve the merge conflicts. |
Co-authored-by: Folkert de Vries <[email protected]>
0025cda
to
ba5556d
Compare
don't have too much time rn and properly reviewing this PR is effort r? compiler If this doesn't get reviewed over the next ~2 weeks reassign to me |
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.
Seems fine to me as an experiment, but I don't see how to turn it into an actual feature later without outright replacing most of it and its tests. That doesn't mean this is badly coded or anything, just that adding first class syntax will make a rewrite very desirable to carry the higher level information properly downwards and avoid lots of situations that have to recover that information in lossy ways
@@ -742,6 +816,190 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | |||
self.cfg.start_new_block().unit() | |||
} | |||
|
|||
/// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`. |
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.
why is this duplicated and not shared?
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 didn't see a good way to share the code. The original is a private method on FunctionCx
in rustc_codegen_ssa
, we're in rustc_mir_build
, so that function does some things (like .monomorphize
) that we can't yet do here.
}; | ||
|
||
let Some(real_target) = | ||
self.static_pattern_match(&cx, valtree, &*scope.arms, &scope.built_match_tree) |
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.
yea I don't really have an idea for how to make this work well in general without redesigning the feature from scratch independently from pattern matching. Not sure how and what parts of the CTFE machinery we could extract out to have something reusable as it works on MIR and thinks only switch statements on integers exist.
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 only need to evaluate the scrutinee into some sort of value representation. I guess I don't really understand what you think the problem is here?
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.
my "complaint" is that this is new logic not shared with anything else in the compiler and that we likely won't share with anything else, even tho we do similar things e.g. in const eval. But there we do it post-mir-building and "just" process the built MIR. I just worry we'll have weird bugs if we accidentally diverge in some less tested use cases
let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern); | ||
|
||
// Peel off or-patterns if they exist. | ||
if let rustc_pattern_analysis::rustc::Constructor::Or = pat.ctor() { |
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.
couldn't this be part of static_pattern_match_inner
and just recurse on itself in Or
patterns?
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 peels off only the outer layer. Nested or-patterns are currently impossible to handle, because we can't always associate the pattern with the correct branch. @Nadrieril had some mid/long-term ideas of how to refactor the or-pattern logic so that this connection can be made.
Given the types that we accept now, only handling top-level or-patterns isn't a limitation, and so we're again defensive here and only implement that which we can guarantee will work.
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.
Thanks for the review!
You're right that we'd need to make a bunch of changes if syntax were added. However, discussions about syntax are just endless (everyone can have an opinion about syntax, way fewer people weigh in on the technical details). So syntax is not something that I think we'll push for for a while: the technical side has to be promising before we'll bother with that.
Having loop_match
on nightly will make it a lot easier to experiment with.
@@ -742,6 +816,190 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | |||
self.cfg.start_new_block().unit() | |||
} | |||
|
|||
/// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`. |
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 didn't see a good way to share the code. The original is a private method on FunctionCx
in rustc_codegen_ssa
, we're in rustc_mir_build
, so that function does some things (like .monomorphize
) that we can't yet do here.
}; | ||
|
||
let Some(real_target) = | ||
self.static_pattern_match(&cx, valtree, &*scope.arms, &scope.built_match_tree) |
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 only need to evaluate the scrutinee into some sort of value representation. I guess I don't really understand what you think the problem is here?
let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern); | ||
|
||
// Peel off or-patterns if they exist. | ||
if let rustc_pattern_analysis::rustc::Constructor::Or = pat.ctor() { |
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 peels off only the outer layer. Nested or-patterns are currently impossible to handle, because we can't always associate the pattern with the correct branch. @Nadrieril had some mid/long-term ideas of how to refactor the or-pattern logic so that this connection can be made.
Given the types that we accept now, only handling top-level or-patterns isn't a limitation, and so we're again defensive here and only implement that which we can guarantee will work.
@bors r+ |
…attr, r=oli-obk Add `#[loop_match]` for improved DFA codegen tracking issue: rust-lang#132306 project goal: rust-lang/rust-project-goals#258 This PR adds the `#[loop_match]` attribute, which aims to improve code generation for state machines. For some (very exciting) benchmarks, see rust-lang/rust-project-goals#258 (comment) Currently, a very restricted syntax pattern is accepted. We'd like to get feedback and merge this now before we go too far in a direction that others have concerns with. ## current state We accept code that looks like this ```rust #[loop_match] loop { state = 'blk: { match state { State::A => { #[const_continue] break 'blk State::B } State::B => { /* ... */ } /* ... */ } } } ``` - a loop should have the same semantics with and without `#[loop_match]`: normal `continue` and `break` continue to work - `#[const_continue]` is only allowed in loops annotated with `#[loop_match]` - the loop body needs to have this particular shape (a single assignment to the match scrutinee, with the body a labelled block containing just a match) ## future work - perform const evaluation on the `break` value - support more state/scrutinee types ## maybe future work - allow `continue 'label value` syntax, which `#[const_continue]` could then use. - allow the match to be on an arbitrary expression (e.g. `State::Initial`) - attempt to also optimize `break`/`continue` expressions that are not marked with `#[const_continue]` r? `@traviscross`
…attr, r=oli-obk,traviscross Add `#[loop_match]` for improved DFA codegen tracking issue: rust-lang#132306 project goal: rust-lang/rust-project-goals#258 This PR adds the `#[loop_match]` attribute, which aims to improve code generation for state machines. For some (very exciting) benchmarks, see rust-lang/rust-project-goals#258 (comment) Currently, a very restricted syntax pattern is accepted. We'd like to get feedback and merge this now before we go too far in a direction that others have concerns with. ## current state We accept code that looks like this ```rust #[loop_match] loop { state = 'blk: { match state { State::A => { #[const_continue] break 'blk State::B } State::B => { /* ... */ } /* ... */ } } } ``` - a loop should have the same semantics with and without `#[loop_match]`: normal `continue` and `break` continue to work - `#[const_continue]` is only allowed in loops annotated with `#[loop_match]` - the loop body needs to have this particular shape (a single assignment to the match scrutinee, with the body a labelled block containing just a match) ## future work - perform const evaluation on the `break` value - support more state/scrutinee types ## maybe future work - allow `continue 'label value` syntax, which `#[const_continue]` could then use. - allow the match to be on an arbitrary expression (e.g. `State::Initial`) - attempt to also optimize `break`/`continue` expressions that are not marked with `#[const_continue]` r? `@traviscross`
Rollup of 15 pull requests Successful merges: - #135731 (Implement parsing of pinned borrows) - #138780 (Add `#[loop_match]` for improved DFA codegen) - #142453 (Windows: make `read_dir` stop iterating after the first error is encountered) - #142633 (Error on invalid signatures for interrupt ABIs) - #142768 (Avoid a bitcast FFI call in transmuting) - #142825 (Port `#[track_caller]` to the new attribute system) - #142844 (Enable short-ice for Windows) - #142934 (Tweak `-Zmacro-stats` measurement.) - #142955 (Couple of test suite fixes for cg_clif) - #142977 (rustdoc: Don't mark `#[target_feature]` functions as ⚠) - #142980 (Reduce mismatched-lifetime-syntaxes suggestions to MaybeIncorrect) - #142982 (Corrected spelling mistake in c_str.rs) - #142983 (Taint body on invalid call ABI) - #142988 (Update wasm-component-ld to 0.5.14) - #142993 (Update cargo) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of #138780 - trifectatechfoundation:loop_match_attr, r=oli-obk,traviscross Add `#[loop_match]` for improved DFA codegen tracking issue: #132306 project goal: rust-lang/rust-project-goals#258 This PR adds the `#[loop_match]` attribute, which aims to improve code generation for state machines. For some (very exciting) benchmarks, see rust-lang/rust-project-goals#258 (comment) Currently, a very restricted syntax pattern is accepted. We'd like to get feedback and merge this now before we go too far in a direction that others have concerns with. ## current state We accept code that looks like this ```rust #[loop_match] loop { state = 'blk: { match state { State::A => { #[const_continue] break 'blk State::B } State::B => { /* ... */ } /* ... */ } } } ``` - a loop should have the same semantics with and without `#[loop_match]`: normal `continue` and `break` continue to work - `#[const_continue]` is only allowed in loops annotated with `#[loop_match]` - the loop body needs to have this particular shape (a single assignment to the match scrutinee, with the body a labelled block containing just a match) ## future work - perform const evaluation on the `break` value - support more state/scrutinee types ## maybe future work - allow `continue 'label value` syntax, which `#[const_continue]` could then use. - allow the match to be on an arbitrary expression (e.g. `State::Initial`) - attempt to also optimize `break`/`continue` expressions that are not marked with `#[const_continue]` r? ``@traviscross``
tracking issue: #132306
project goal: rust-lang/rust-project-goals#258
This PR adds the
#[loop_match]
attribute, which aims to improve code generation for state machines. For some (very exciting) benchmarks, see rust-lang/rust-project-goals#258 (comment)Currently, a very restricted syntax pattern is accepted. We'd like to get feedback and merge this now before we go too far in a direction that others have concerns with.
current state
We accept code that looks like this
#[loop_match]
: normalcontinue
andbreak
continue to work#[const_continue]
is only allowed in loops annotated with#[loop_match]
future work
break
valuemaybe future work
continue 'label value
syntax, which#[const_continue]
could then use.State::Initial
)break
/continue
expressions that are not marked with#[const_continue]
r? @traviscross