-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Use constant indices when populating Lists and arrays in collection expressions #80999
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
Changes from 10 commits
536a406
fcb6eb3
c210cc9
738dcea
7aa7cab
877b12b
bd54c95
9ffa846
f9b7579
b5ed80e
38fdece
2fab349
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -831,12 +831,19 @@ private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, A | |
|
|
||
| RewriteCollectionExpressionElementsIntoTemporaries(elements, numberIncludingLastSpread, localsBuilder, sideEffects); | ||
|
|
||
| // int index = 0; | ||
| BoundLocal indexTemp = _factory.StoreToTemp( | ||
| _factory.Literal(0), | ||
| out assignmentToTemp); | ||
| localsBuilder.Add(indexTemp); | ||
| sideEffects.Add(assignmentToTemp); | ||
| // indexTemp is null when we can use constant compile-time indices. | ||
| // indexTemp is non-null when we need a runtime-tracked index variable (for spread elements). | ||
| BoundLocal? indexTemp = null; | ||
|
|
||
| if (numberIncludingLastSpread != 0) | ||
| { | ||
| // int index = 0; | ||
| indexTemp = _factory.StoreToTemp( | ||
| _factory.Literal(0), | ||
| out assignmentToTemp); | ||
| localsBuilder.Add(indexTemp); | ||
| sideEffects.Add(assignmentToTemp); | ||
| } | ||
|
|
||
| // ElementType[] array = new ElementType[N + s1.Length + ...]; | ||
| BoundLocal arrayTemp = _factory.StoreToTemp( | ||
|
|
@@ -848,6 +855,7 @@ private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, A | |
| localsBuilder.Add(arrayTemp); | ||
| sideEffects.Add(assignmentToTemp); | ||
|
|
||
| int currentElementIndex = 0; | ||
| AddCollectionExpressionElements( | ||
| elements, | ||
| arrayTemp, | ||
|
|
@@ -857,33 +865,49 @@ private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, A | |
| addElement: (ArrayBuilder<BoundExpression> expressions, BoundExpression arrayTemp, BoundExpression rewrittenValue, bool isLastElement) => | ||
| { | ||
| Debug.Assert(arrayTemp.Type is ArrayTypeSymbol); | ||
| Debug.Assert(indexTemp.Type is { SpecialType: SpecialType.System_Int32 }); | ||
|
|
||
| var expressionSyntax = rewrittenValue.Syntax; | ||
| var elementType = ((ArrayTypeSymbol)arrayTemp.Type).ElementType; | ||
|
|
||
| // array[index] = element; | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| _factory.ArrayAccess(arrayTemp, indexTemp), | ||
| rewrittenValue, | ||
| isRef: false, | ||
| elementType)); | ||
| if (!isLastElement) | ||
| if (indexTemp is null) | ||
| { | ||
| // index = index + 1; | ||
| // array[0] = element; array[1] = element; etc. | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| indexTemp, | ||
| _factory.Binary(BinaryOperatorKind.Addition, indexTemp.Type, indexTemp, _factory.Literal(1)), | ||
| _factory.ArrayAccess(arrayTemp, _factory.Literal(currentElementIndex)), | ||
| rewrittenValue, | ||
| isRef: false, | ||
| indexTemp.Type)); | ||
| elementType)); | ||
| currentElementIndex++; | ||
| } | ||
| else | ||
| { | ||
| // array[index] = element; | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| _factory.ArrayAccess(arrayTemp, indexTemp), | ||
| rewrittenValue, | ||
| isRef: false, | ||
| elementType)); | ||
| if (!isLastElement) | ||
| { | ||
| // index = index + 1; | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| indexTemp, | ||
| _factory.Binary(BinaryOperatorKind.Addition, indexTemp.Type, indexTemp, _factory.Literal(1)), | ||
| isRef: false, | ||
| indexTemp.Type)); | ||
| } | ||
| } | ||
| }, | ||
| tryOptimizeSpreadElement: (ArrayBuilder<BoundExpression> sideEffects, BoundExpression arrayTemp, BoundCollectionExpressionSpreadElement spreadElement, BoundExpression rewrittenSpreadOperand) => | ||
| { | ||
| Debug.Assert(indexTemp is not null); | ||
|
|
||
| if (PrepareCopyToOptimization(spreadElement, rewrittenSpreadOperand) is not var (spanSliceMethod, spreadElementAsSpan, getLengthMethod, copyToMethod)) | ||
| return false; | ||
|
|
||
|
|
@@ -1177,13 +1201,21 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty | |
| // Populate the span. | ||
| var spanGetItem = ((MethodSymbol)_factory.WellKnownMember(WellKnownMember.System_Span_T__get_Item)).AsMember((NamedTypeSymbol)spanTemp.Type); | ||
|
|
||
| // int index = 0; | ||
| BoundLocal indexTemp = _factory.StoreToTemp( | ||
| _factory.Literal(0), | ||
| out assignmentToTemp); | ||
| localsBuilder.Add(indexTemp); | ||
| sideEffects.Add(assignmentToTemp); | ||
| // indexTemp is null when we can use constant compile-time indices. | ||
| // indexTemp is non-null when we need a runtime-tracked index variable (for spread elements). | ||
| BoundLocal? indexTemp = null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. review with whitespace off. |
||
|
|
||
| if (numberIncludingLastSpread != 0) | ||
| { | ||
| // int index = 0; | ||
| indexTemp = _factory.StoreToTemp( | ||
| _factory.Literal(0), | ||
| out assignmentToTemp); | ||
| localsBuilder.Add(indexTemp); | ||
| sideEffects.Add(assignmentToTemp); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot CreateAndPopulateArray contains very similar logic for arrays. can you update the PR to use constant indices for those as well when possible?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applied constant indices optimization to arrays in commit 9ffa846. Arrays now use the same optimization as Lists when there are no spread elements. |
||
|
|
||
| int currentElementIndex = 0; | ||
| AddCollectionExpressionElements( | ||
| elements, | ||
| spanTemp, | ||
|
|
@@ -1193,33 +1225,49 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty | |
| addElement: (ArrayBuilder<BoundExpression> expressions, BoundExpression spanTemp, BoundExpression rewrittenValue, bool isLastElement) => | ||
| { | ||
| Debug.Assert(spanTemp.Type is NamedTypeSymbol); | ||
| Debug.Assert(indexTemp.Type is { SpecialType: SpecialType.System_Int32 }); | ||
|
|
||
| var expressionSyntax = rewrittenValue.Syntax; | ||
| var elementType = ((NamedTypeSymbol)spanTemp.Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type; | ||
|
|
||
| // span[index] = element; | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| _factory.Call(spanTemp, spanGetItem, indexTemp), | ||
| rewrittenValue, | ||
| isRef: false, | ||
| elementType)); | ||
| if (!isLastElement) | ||
| if (indexTemp is null) | ||
| { | ||
| // index = index + 1; | ||
| // span[0] = element; span[1] = element; etc. | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| indexTemp, | ||
| _factory.Binary(BinaryOperatorKind.Addition, indexTemp.Type, indexTemp, _factory.Literal(1)), | ||
| _factory.Call(spanTemp, spanGetItem, _factory.Literal(currentElementIndex)), | ||
| rewrittenValue, | ||
| isRef: false, | ||
| indexTemp.Type)); | ||
| elementType)); | ||
| currentElementIndex++; | ||
| } | ||
| else | ||
| { | ||
| // span[index] = element; | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| _factory.Call(spanTemp, spanGetItem, indexTemp), | ||
| rewrittenValue, | ||
| isRef: false, | ||
| elementType)); | ||
| if (!isLastElement) | ||
| { | ||
| // index = index + 1; | ||
| expressions.Add( | ||
| new BoundAssignmentOperator( | ||
| expressionSyntax, | ||
| indexTemp, | ||
| _factory.Binary(BinaryOperatorKind.Addition, indexTemp.Type, indexTemp, _factory.Literal(1)), | ||
| isRef: false, | ||
| indexTemp.Type)); | ||
| } | ||
| } | ||
| }, | ||
| tryOptimizeSpreadElement: (ArrayBuilder<BoundExpression> sideEffects, BoundExpression spanTemp, BoundCollectionExpressionSpreadElement spreadElement, BoundExpression rewrittenSpreadOperand) => | ||
| { | ||
| Debug.Assert(indexTemp is not null); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot have a similar comment here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment was already added in commit 38fdece at line 1270 (same location). |
||
|
|
||
| if (PrepareCopyToOptimization(spreadElement, rewrittenSpreadOperand) is not var (spanSliceMethod, spreadElementAsSpan, getLengthMethod, copyToMethod)) | ||
| return false; | ||
|
|
||
|
|
||
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.
@copilot add a comment mentioning that if we have spreads we expect o always need an index currently.
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.
Added comments in commit 38fdece to clarify that when spreads are present, we always need a runtime-tracked index variable.