-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Avoid creating result temp for switch-expressions #69194
base: main
Are you sure you want to change the base?
Conversation
PopEvalStack(); | ||
_counter++; | ||
EnsureOnlyEvalStack(); |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
@@ -1508,6 +1508,18 @@ | |||
<Field Name="WasTargetTyped" Type="bool" /> | |||
</Node> | |||
|
|||
<Node Name="BoundLoweredSwitchExpression" Base="BoundExpression" HasValidate="true"> | |||
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/> | |||
<Field Name="Statements" Type="ImmutableArray<BoundStatement>"/> |
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.
</Node> | ||
|
||
<Node Name="BoundLoweredSwitchExpressionArm" Base="BoundNode"> | ||
<Field Name="Locals" Type="ImmutableArray<LocalSymbol>"/> |
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.
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.
These are the same locals in BoundScope.Locals
roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
Lines 114 to 116 in 37bdfe3
// Note the language scope of the locals, even though they are included for the purposes of | |
// lifetime analysis in the enclosing scope. | |
result.Add(new BoundScope(arm.Syntax, arm.Locals, statements)); |
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