Skip to content

Commit

Permalink
Optimize codegen for collections expression of single spread of `Read…
Browse files Browse the repository at this point in the history
…OnlySpan` for collection builder emit strategy (#73102)
  • Loading branch information
DoctorKrolic committed Jul 11, 2024
1 parent 4c02e37 commit 7e93076
Show file tree
Hide file tree
Showing 2 changed files with 758 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ private BoundExpression RewriteCollectionExpressionConversion(Conversion convers
case CollectionExpressionTypeKind.CollectionBuilder:
// If the collection type is ImmutableArray<T>, then construction is optimized to use
// ImmutableCollectionsMarshal.AsImmutableArray.
// The only exception is when collection expression is just `[.. readOnlySpan]` of the same element type, in such cases
// it is more efficient to emit a direct call of `ImmutableArray.Create`
if (ConversionsBase.IsSpanOrListType(_compilation, node.Type, WellKnownType.System_Collections_Immutable_ImmutableArray_T, out var arrayElementType) &&
_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T) is MethodSymbol asImmutableArray)
_compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T) is MethodSymbol asImmutableArray &&
!CanOptimizeSingleSpreadAsCollectionBuilderArgument(node, out _))
{
return VisitImmutableArrayCollectionExpression(node, arrayElementType, asImmutableArray);
}
Expand Down Expand Up @@ -138,6 +141,24 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele
}
}

private static bool CanOptimizeSingleSpreadAsCollectionBuilderArgument(BoundCollectionExpression node, [NotNullWhen(true)] out BoundExpression? spreadExpression)
{
spreadExpression = null;

if (node is
{
CollectionBuilderMethod: { } builder,
Elements: [BoundCollectionExpressionSpreadElement { Expression: { Type: NamedTypeSymbol spreadType } expr }],
} &&
ConversionsBase.HasIdentityConversion(builder.Parameters[0].Type, spreadType) &&
(!builder.ReturnType.IsRefLikeType || builder.Parameters[0].EffectiveScope == ScopedKind.ScopedValue))
{
spreadExpression = expr;
}

return spreadExpression is not null;
}

private BoundExpression VisitImmutableArrayCollectionExpression(BoundCollectionExpression node, TypeWithAnnotations elementType, MethodSymbol asImmutableArray)
{
var arrayCreation = VisitArrayOrSpanCollectionExpression(
Expand Down Expand Up @@ -375,7 +396,14 @@ private BoundExpression VisitCollectionBuilderCollectionExpression(BoundCollecti
Debug.Assert(spanType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions));

var elementType = spanType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
BoundExpression span = VisitArrayOrSpanCollectionExpression(node, CollectionExpressionTypeKind.ReadOnlySpan, spanType, elementType);

// If collection expression is of form `[.. anotherReadOnlySpan]`
// with `anotherReadOnlySpan` being a ReadOnlySpan of the same type as target collection type
// and that span cannot be captured in a returned ref struct
// we can directly use `anotherReadOnlySpan` as collection builder argument and skip the copying assignment.
BoundExpression span = CanOptimizeSingleSpreadAsCollectionBuilderArgument(node, out var spreadExpression)
? spreadExpression
: VisitArrayOrSpanCollectionExpression(node, CollectionExpressionTypeKind.ReadOnlySpan, spanType, elementType);

var invocation = new BoundCall(
node.Syntax,
Expand Down
Loading

0 comments on commit 7e93076

Please sign in to comment.