Avoid creating result temp for switch-expressions#69194
Avoid creating result temp for switch-expressions#69194alrz wants to merge 12 commits intodotnet:mainfrom
Conversation
| PopEvalStack(); | ||
| _counter++; | ||
| EnsureOnlyEvalStack(); |
There was a problem hiding this comment.
Same as exception filters which basically throws away the value and fence it against whatever comes after. I wonder if this is more restrictive than what's done for conditional operators? Also, I didn't understand what is being compensated for here since there's no skipped node.
| } | ||
|
|
||
| private BoundExpression LowerSwitchExpression(BoundConvertedSwitchExpression node) | ||
| private BoundExpression LowerSwitchExpressionForDebug(BoundConvertedSwitchExpression node) |
There was a problem hiding this comment.
From Expression Breakpoints.md
Sequence points are constrained to appear only at locations in the code where the evaluation stack is empty
I've separated the lowering for debug/release, not sure if it's possible to combine the two somehow.
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs
Show resolved
Hide resolved
| // share them as temps in the decision dag. | ||
| outerVariables.AddRange(arm.Locals); | ||
| switchArmsBuilder.Add(new BoundLoweredSwitchExpressionArm( | ||
| arm.Syntax, arm.Locals, statements.Add(_factory.Label(arm.Label)), loweredValue)); |
There was a problem hiding this comment.
Doing this instead of a separate label field because we want "statements" emitted before the label while "value prologue" produced in the spiller should be added afterwards. So this saves two fields and simplify lots of places especially spiller.
|
|
||
| partial class BoundLoweredSwitchExpression | ||
| { | ||
| private partial void Validate() |
There was a problem hiding this comment.
It feels like a BoundNode_Validate.cs file could be used to gather all these in one place.
|
@AlekseyTs I'd like a high level review pass on this if you got time before I update the baseline. There's still bootstrap failures to investigate so there's definitely missing pieces that I'm not aware of (explained above). Thanks. |
Sorry, I will not be able to do that at least until September. Perhaps someone from @dotnet/roslyn-compiler will be able to pick this up before then. |
|
When we're ready, we could also use this PR to reintroduce the is-pattern temp optimization and fix the issue we found with it. Reproducer for the issue that caused us to revert the is-pattern temp optimization can be found in #69615. |
33f8da2 to
32c7a8a
Compare
| CreateCompilation(source, options: TestOptions.ReleaseDll).VerifyDiagnostics().VerifyEmitDiagnostics( | ||
| // (9,17): error CS4013: Instance of type 'S' cannot be used inside a nested function, query expression, iterator block or async method | ||
| // Q { F: { P1: true } } when await c => r, // error: cached Q.F is alive | ||
| Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "F").WithArguments("S").WithLocation(9, 17) | ||
| Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"o switch | ||
| { |
There was a problem hiding this comment.
How this could be gracefully reported as ERR_SpecialByRefInLambda? The test introduced in #37818
|
I applied the same fix (see #72273) for switch here. The first commit is already reviewed in #68694, but let me know if you'd prefer two separate PRs. @AlekseyTs @jcouv for reviews. Thanks. |
|
@alrz Could you please provide a meaningful description for this PR. What you are trying to achieve, and how you trying to achieve that. |
|
This is the exact same change in #68694 but for switch-expressions. Currently the result of either expression is set to a local but that's not necessary since in both cases we will have a (label->value) jump table at the end that could be pushed onto the the stack. |
|
|
||
| <Node Name="BoundLoweredSwitchExpression" Base="BoundExpression" HasValidate="true"> | ||
| <Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/> | ||
| <Field Name="Statements" Type="ImmutableArray<BoundStatement>"/> |
| </Node> | ||
|
|
||
| <Node Name="BoundLoweredSwitchExpressionArm" Base="BoundNode"> | ||
| <Field Name="Locals" Type="ImmutableArray<LocalSymbol>"/> |
There was a problem hiding this comment.
These are the same locals in BoundScope.Locals
BoundScope is rewritten in MethodToClassRewriter, otherwise DefineScopeLocals is used on locals. To cover the first part, I think we could use this existing node in the spiller (I suppose that's where this is relevant) and insert BoundScope instead of rewriting the node itself with assignments, if that sounds right to you I can try it in the next iteration.
|
It looks like #68694 got reverted. I think rather than bundling that with more changes, we should revert the revert and address whatever problems that PR was causing. Then we can look at more changes on top of |
# Conflicts: # src/Compilers/CSharp/Portable/BoundTree/BoundLoweredIsPatternExpression.cs # src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs # src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs # src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs # src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs # src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs # src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
|
@AlekseyTs @jcouv @dotnet/roslyn-compiler Can I get a review pass on this please? thanks. |
|
Sorry @alrz, we're super heads-down on a couple of changes at the moment. We'll get to this when we can, hopefully sometime next week. |
|
Leaving a note here, this is blocked because the compiler doesn't expect labels in expressions. I think that ought to be covered as part of block-expressions feature, then both of these could be lowered to that instead. if ( x is nonlinear-pattern )
if ( { lowered-dag-statements; whenTrue: break true; whenFalse: break false; } )return e switch {
p0 => 0,
p1 => 1,
_ => 2
};
return {
lowered-dag-statements;
armLabel0: break 0;
armLabel1: break 1;
defaultLabel: break 2;
};With that I'd expect no difference in codegen compared to this PR. |
Follow-up to #68694
Closes #44546