Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

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.

{
// 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(
Expand All @@ -848,6 +855,7 @@ private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, A
localsBuilder.Add(arrayTemp);
sideEffects.Add(assignmentToTemp);

int currentElementIndex = 0;
AddCollectionExpressionElements(
elements,
arrayTemp,
Expand All @@ -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;

Expand Down Expand Up @@ -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.
// When there are spread elements, we need a mutable index variable for the CopyTo optimization.
BoundLocal? indexTemp = null;
Copy link
Member

Choose a reason for hiding this comment

The 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);
}
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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,
Expand All @@ -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;

Expand Down
Loading
Loading