Skip to content

Commit 7e93076

Browse files
authored
Optimize codegen for collections expression of single spread of ReadOnlySpan for collection builder emit strategy (#73102)
1 parent 4c02e37 commit 7e93076

File tree

2 files changed

+758
-2
lines changed

2 files changed

+758
-2
lines changed

src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs

+30-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ private BoundExpression RewriteCollectionExpressionConversion(Conversion convers
5656
case CollectionExpressionTypeKind.CollectionBuilder:
5757
// If the collection type is ImmutableArray<T>, then construction is optimized to use
5858
// ImmutableCollectionsMarshal.AsImmutableArray.
59+
// The only exception is when collection expression is just `[.. readOnlySpan]` of the same element type, in such cases
60+
// it is more efficient to emit a direct call of `ImmutableArray.Create`
5961
if (ConversionsBase.IsSpanOrListType(_compilation, node.Type, WellKnownType.System_Collections_Immutable_ImmutableArray_T, out var arrayElementType) &&
60-
_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T) is MethodSymbol asImmutableArray)
62+
_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T) is MethodSymbol asImmutableArray &&
63+
!CanOptimizeSingleSpreadAsCollectionBuilderArgument(node, out _))
6164
{
6265
return VisitImmutableArrayCollectionExpression(node, arrayElementType, asImmutableArray);
6366
}
@@ -138,6 +141,24 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele
138141
}
139142
}
140143

144+
private static bool CanOptimizeSingleSpreadAsCollectionBuilderArgument(BoundCollectionExpression node, [NotNullWhen(true)] out BoundExpression? spreadExpression)
145+
{
146+
spreadExpression = null;
147+
148+
if (node is
149+
{
150+
CollectionBuilderMethod: { } builder,
151+
Elements: [BoundCollectionExpressionSpreadElement { Expression: { Type: NamedTypeSymbol spreadType } expr }],
152+
} &&
153+
ConversionsBase.HasIdentityConversion(builder.Parameters[0].Type, spreadType) &&
154+
(!builder.ReturnType.IsRefLikeType || builder.Parameters[0].EffectiveScope == ScopedKind.ScopedValue))
155+
{
156+
spreadExpression = expr;
157+
}
158+
159+
return spreadExpression is not null;
160+
}
161+
141162
private BoundExpression VisitImmutableArrayCollectionExpression(BoundCollectionExpression node, TypeWithAnnotations elementType, MethodSymbol asImmutableArray)
142163
{
143164
var arrayCreation = VisitArrayOrSpanCollectionExpression(
@@ -375,7 +396,14 @@ private BoundExpression VisitCollectionBuilderCollectionExpression(BoundCollecti
375396
Debug.Assert(spanType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions));
376397

377398
var elementType = spanType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
378-
BoundExpression span = VisitArrayOrSpanCollectionExpression(node, CollectionExpressionTypeKind.ReadOnlySpan, spanType, elementType);
399+
400+
// If collection expression is of form `[.. anotherReadOnlySpan]`
401+
// with `anotherReadOnlySpan` being a ReadOnlySpan of the same type as target collection type
402+
// and that span cannot be captured in a returned ref struct
403+
// we can directly use `anotherReadOnlySpan` as collection builder argument and skip the copying assignment.
404+
BoundExpression span = CanOptimizeSingleSpreadAsCollectionBuilderArgument(node, out var spreadExpression)
405+
? spreadExpression
406+
: VisitArrayOrSpanCollectionExpression(node, CollectionExpressionTypeKind.ReadOnlySpan, spanType, elementType);
379407

380408
var invocation = new BoundCall(
381409
node.Syntax,

0 commit comments

Comments
 (0)