Skip to content
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

Prefer converting to ReadOnlySpan for spread optimization when we do not need to write #71733

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -538,11 +538,11 @@ private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, A
return _factory.Call(rewrittenSpreadExpression, listToArrayMethod.AsMember((NamedTypeSymbol)spreadExpression.Type!));
}

if (TryGetSpanConversion(spreadExpression.Type, out var asSpanMethod))
if (TryGetSpanConversion(spreadExpression.Type, writableOnly: false, out var asSpanMethod))
{
var spanType = CallAsSpanMethod(spreadExpression, asSpanMethod).Type!.OriginalDefinition;
if (tryGetToArrayMethod(spanType, WellKnownType.System_Span_T, WellKnownMember.System_Span_T__ToArray, out var toArrayMethod)
|| tryGetToArrayMethod(spanType, WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__ToArray, out toArrayMethod))
if (tryGetToArrayMethod(spanType, WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__ToArray, out var toArrayMethod)
|| tryGetToArrayMethod(spanType, WellKnownType.System_Span_T, WellKnownMember.System_Span_T__ToArray, out toArrayMethod))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reordered these as I think the ReadOnlySpan case will now be more common

{
var rewrittenSpreadExpression = CallAsSpanMethod(VisitExpression(spreadExpression), asSpanMethod);
return _factory.Call(rewrittenSpreadExpression, toArrayMethod.AsMember((NamedTypeSymbol)rewrittenSpreadExpression.Type!));
Expand Down Expand Up @@ -649,7 +649,7 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType

// https://github.com/dotnet/roslyn/issues/71270
// Could save the targetSpan to temp in the enclosing scope, but need to make sure we are async-safe etc.
if (!TryConvertToSpanOrReadOnlySpan(arrayTemp, out var targetSpan))
if (!TryConvertToSpan(arrayTemp, writableOnly: true, out var targetSpan))
return false;

PerformCopyToOptimization(sideEffects, localsBuilder, indexTemp, targetSpan, rewrittenSpreadOperand, spanSliceMethod, spreadElementAsSpan, getLengthMethod, copyToMethod);
Expand All @@ -667,15 +667,16 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType
arrayType);
}

/// <summary>
/// Returns true if type is convertible to Span or ReadOnlySpan.
/// If non-identity conversion, also returns a non-null asSpanMethod.
/// </summary>
/// <returns>
/// If <paramref name="writableOnly"/> is <see langword="true"/>, will only return true with a conversion to writable Span.
/// Otherwise, will return a conversion directly to ReadOnlySpan if one is known, and may return a conversion to Span if no direct conversion to ReadOnlySpan is known, or if the type is already a Span.
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
/// If non-identity conversion, also returns a non-null <paramref name="asSpanMethod"/>.
/// </returns>
/// <remarks>We are assuming that the well-known types we are converting to/from do not have constraints on their type parameters.</remarks>
private bool TryGetSpanConversion(TypeSymbol type, out MethodSymbol? asSpanMethod)
private bool TryGetSpanConversion(TypeSymbol type, bool writableOnly, out MethodSymbol? asSpanMethod)
{
if (type is ArrayTypeSymbol { IsSZArray: true } arrayType
&& _factory.WellKnownMethod(WellKnownMember.System_Span_T__ctor_Array, isOptional: true) is { } spanCtorArray)
&& _factory.WellKnownMethod(writableOnly ? WellKnownMember.System_Span_T__ctor_Array : WellKnownMember.System_ReadOnlySpan_T__ctor_Array, isOptional: true) is { } spanCtorArray)
{
// conversion to 'object' will fail if, for example, 'arrayType.ElementType' is a pointer.
var useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
Expand All @@ -692,14 +693,15 @@ private bool TryGetSpanConversion(TypeSymbol type, out MethodSymbol? asSpanMetho
return false;
}

if (namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.ConsiderEverything)
|| namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.ConsiderEverything))
if ((!writableOnly && namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.ConsiderEverything))
|| namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.ConsiderEverything))
{
asSpanMethod = null;
return true;
}

if (namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T), TypeCompareKind.ConsiderEverything)
if (!writableOnly
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
&& namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T), TypeCompareKind.ConsiderEverything)
&& _factory.WellKnownMethod(WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan, isOptional: true) is { } immutableArrayAsSpanMethod)
{
asSpanMethod = immutableArrayAsSpanMethod.AsMember(namedType);
Expand All @@ -717,12 +719,12 @@ private bool TryGetSpanConversion(TypeSymbol type, out MethodSymbol? asSpanMetho
return false;
}

private bool TryConvertToSpanOrReadOnlySpan(BoundExpression expression, [NotNullWhen(true)] out BoundExpression? span)
private bool TryConvertToSpan(BoundExpression expression, bool writableOnly, [NotNullWhen(true)] out BoundExpression? span)
{
var type = expression.Type;
Debug.Assert(type is not null);

if (!TryGetSpanConversion(type, out var asSpanMethod))
if (!TryGetSpanConversion(type, writableOnly, out var asSpanMethod))
{
span = null;
return false;
Expand Down Expand Up @@ -768,7 +770,7 @@ private BoundExpression CallAsSpanMethod(BoundExpression spreadExpression, Metho
if (_factory.WellKnownMethod(WellKnownMember.System_Span_T__Slice_Int_Int, isOptional: true) is not { } spanSliceMethod)
return null;

if (!TryConvertToSpanOrReadOnlySpan(rewrittenSpreadOperand, out var spreadOperandAsSpan))
if (!TryConvertToSpan(rewrittenSpreadOperand, writableOnly: false, out var spreadOperandAsSpan))
return null;

if ((getSpanMethodsForSpread(WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__get_Length, WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T)
Expand Down
Loading