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

Avoid creating result temp for switch-expressions #69194

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

alrz
Copy link
Contributor

@alrz alrz commented Jul 24, 2023

Follow-up to #68694
Closes #44546

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jul 24, 2023
@ghost ghost added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Jul 24, 2023
Comment on lines +1483 to +1485
PopEvalStack();
_counter++;
EnsureOnlyEvalStack();
Copy link
Contributor Author

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)
Copy link
Contributor Author

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.

// 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));
Copy link
Contributor Author

@alrz alrz Jul 25, 2023

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()
Copy link
Contributor Author

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.

@alrz
Copy link
Contributor Author

alrz commented Jul 25, 2023

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

@AlekseyTs
Copy link
Contributor

I'd like a high level review pass on this if you got time before I update the baseline.

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.

@RikkiGibson
Copy link
Contributor

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.

@alrz alrz force-pushed the lowered-switch branch 2 times, most recently from 33f8da2 to 32c7a8a Compare February 27, 2024 20:41
Comment on lines 3905 to +3907
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
{
Copy link
Contributor Author

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

@alrz alrz marked this pull request as ready for review February 27, 2024 23:46
@alrz alrz requested a review from a team as a code owner February 27, 2024 23:46
@alrz
Copy link
Contributor Author

alrz commented Feb 27, 2024

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.

@AlekseyTs
Copy link
Contributor

@alrz Could you please provide a meaningful description for this PR. What you are trying to achieve, and how you trying to achieve that.

@alrz
Copy link
Contributor Author

alrz commented Feb 29, 2024

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&lt;BoundStatement&gt;"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these statements? Either use more descriptive name, or consider adding a comment

</Node>

<Node Name="BoundLoweredSwitchExpressionArm" Base="BoundNode">
<Field Name="Locals" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be preferred to not introduce new nodes with locals that survive lowering. Can we somehow represent what we want with combination of existing nodes?

Copy link
Contributor Author

@alrz alrz Mar 22, 2024

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

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

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Mar 5, 2024

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 main with that change merged.

@alrz
Copy link
Contributor Author

alrz commented Mar 5, 2024

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 main with that change merged.

Thanks, I opened up the follow-up PR for review #72273

@alrz alrz marked this pull request as draft March 5, 2024 21:52
# 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
@alrz alrz marked this pull request as ready for review March 22, 2024 19:01
@alrz alrz requested review from AlekseyTs, jcouv and a team March 22, 2024 19:13
@jcouv jcouv self-assigned this Mar 25, 2024
@alrz
Copy link
Contributor Author

alrz commented Mar 28, 2024

@AlekseyTs @jcouv @dotnet/roslyn-compiler Can I get a review pass on this please? thanks.

@333fred
Copy link
Member

333fred commented Mar 29, 2024

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.

@jaredpar jaredpar added this to the Backlog milestone Mar 29, 2024
@alrz alrz marked this pull request as draft April 1, 2024 17:33
@alrz
Copy link
Contributor Author

alrz commented Sep 21, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Equivalent switch expression produces suboptimal IL compared to switch statement
6 participants