-
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 9 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); | ||
| // When there are no spread elements, we can use constant indices for better codegen. | ||
| // When there are spread elements, we need a mutable index variable for the CopyTo optimization. | ||
| 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,51 @@ 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 | ||
| { | ||
| Debug.Assert(indexTemp.Type is { SpecialType: SpecialType.System_Int32 }); | ||
|
|
||
| // 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, "Should not have spread elements when using constant indices"); | ||
|
|
||
| if (PrepareCopyToOptimization(spreadElement, rewrittenSpreadOperand) is not var (spanSliceMethod, spreadElementAsSpan, getLengthMethod, copyToMethod)) | ||
| return false; | ||
|
|
||
|
|
@@ -1177,13 +1203,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); | ||
| // When there are no spread elements, we can use constant indices for better codegen. | ||
CyrusNajmabadi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // When there are spread elements, we need a mutable index variable for the CopyTo optimization. | ||
| 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 +1227,51 @@ 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 | ||
| { | ||
| Debug.Assert(indexTemp.Type is { SpecialType: SpecialType.System_Int32 }); | ||
|
|
||
| // 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, "Should not have spread elements when using constant indices"); | ||
|
|
||
| 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.
Ah. Looking at the code from above, I recall now, that this optimization won't ever kick in, until we extend it so that normal elements before the first spread element use constant indices. Preconditions earlier on this code path, make it so the condition on this line will always be true, as currently written.
Sorry for the runaround.