Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commiteb6497d

Browse files
author
Julien Couvreur
committed
Cached ROS constructed from arrays of constants (remaining types)
1 parentd8b5411 commiteb6497d

File tree

6 files changed

+756
-114
lines changed

6 files changed

+756
-114
lines changed

‎src/Compilers/CSharp/Portable/CodeGen/EmitArrayInitializer.cs‎

Lines changed: 153 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
usingSystem;
88
usingSystem.Collections.Immutable;
99
usingSystem.Diagnostics;
10+
usingSystem.Diagnostics.CodeAnalysis;
1011
usingSystem.Linq;
1112
usingSystem.Reflection.Metadata;
1213
usingMicrosoft.CodeAnalysis.CSharp.Symbols;
@@ -449,6 +450,13 @@ private bool TryEmitReadonlySpanAsBlobWrapper(NamedTypeSymbol spanType, BoundExp
449450
returnfalse;
450451
}
451452

453+
if(inPlaceTargetisnull&&!used)
454+
{
455+
// The caller has specified that we're creating a ReadOnlySpan expression that won't be used.
456+
// We needn't emit anything.
457+
returntrue;
458+
}
459+
452460
// The primary optimization here is for byte-sized primitives that can wrap a ReadOnlySpan directly around a pointer
453461
// into a blob. That requires the ReadOnlySpan(void*, int) ctor. If this constructor isn't available, we give up on
454462
// all optimizations. Technically, if this ctor isn't available but the ReadOnlySpan(T[]) constructor is, we could still
@@ -475,7 +483,7 @@ private bool TryEmitReadonlySpanAsBlobWrapper(NamedTypeSymbol spanType, BoundExp
475483
specialElementType=elementType.EnumUnderlyingTypeOrSelf().SpecialType;
476484
if(!IsTypeAllowedInBlobWrapper(specialElementType))
477485
{
478-
returnfalse;
486+
returnstartisnull&&lengthisnull&&tryEmitAsCachedArrayOfConstants(ac,arrayType,elementType,spanType,used,inPlaceTarget);
479487
}
480488

481489
// Get the data and number of elements that compose the initialization.
@@ -522,35 +530,9 @@ private bool TryEmitReadonlySpanAsBlobWrapper(NamedTypeSymbol spanType, BoundExp
522530
lengthForConstructor=elementCount;
523531
}
524532

525-
if(inPlaceTargetisnull&&!used)
526-
{
527-
// The caller has specified that we're creating a ReadOnlySpan expression that won't be used.
528-
// We needn't emit anything.
529-
returntrue;
530-
}
531-
532533
if(elementCount==0)
533534
{
534-
// The span is empty. Optimize away the array. This works regardless of the size of the type.
535-
// (We could optimize this even for non-primitives, but it's not currently worthwhile.)
536-
537-
// If this is in-place initialization, call the default ctor.
538-
if(inPlaceTargetis notnull)
539-
{
540-
EmitAddress(inPlaceTarget,Binder.AddressKind.Writeable);
541-
_builder.EmitOpCode(ILOpCode.Initobj);
542-
EmitSymbolToken(spanType,wrappedExpression.Syntax);
543-
if(used)
544-
{
545-
EmitExpression(inPlaceTarget,used:true);
546-
}
547-
}
548-
else
549-
{
550-
// Otherwise, assign it to a default value / empty span.
551-
Debug.Assert(used);
552-
EmitDefaultValue(spanType,used,wrappedExpression.Syntax);
553-
}
535+
emitEmptyReadonlySpan(spanType,wrappedExpression,used,inPlaceTarget);
554536
returntrue;
555537
}
556538

@@ -607,7 +589,7 @@ private bool TryEmitReadonlySpanAsBlobWrapper(NamedTypeSymbol spanType, BoundExp
607589
// We need to use RuntimeHelpers.CreateSpan / cached array, but the code has requested a subset of the elements.
608590
// That means the code is something like `new ReadOnlySpan<char>(new[] { 'a', 'b', 'c' }, 1, 2)`
609591
// rather than `new ReadOnlySpan<char>(new[] { 'b', 'c' })`. If such a pattern is found to be
610-
// common, this could be augmented toaccomodate it. For now, we just return false to fail
592+
// common, this could be augmented toaccommodate it. For now, we just return false to fail
611593
// to optimize this case.
612594
returnfalse;
613595
}
@@ -643,55 +625,151 @@ private bool TryEmitReadonlySpanAsBlobWrapper(NamedTypeSymbol spanType, BoundExp
643625
// We're dealing with a multi-byte primitive, and CreateSpan was not available. Get a static field from PrivateImplementationDetails,
644626
// and use it as a lazily-initialized cache for an array for this data:
645627
// new ReadOnlySpan<T>(PrivateImplementationDetails.ArrayField ??= RuntimeHelpers.InitializeArray(new int[Length], PrivateImplementationDetails.DataField));
628+
returnemitAsCachedArrayFromBlob(spanType,wrappedExpression,elementCount,data,refarrayType,elementType);
646629

647-
varrosArrayCtor=(MethodSymbol?)Binder.GetWellKnownTypeMember(_module.Compilation,WellKnownMember.System_ReadOnlySpan_T__ctor_Array,_diagnostics,syntax:wrappedExpression.Syntax,isOptional:true);
648-
if(rosArrayCtorisnull)
630+
// Emit: new ReadOnlySpan<T>(PrivateImplementationDetails.ArrayField ??= RuntimeHelpers.InitializeArray(new int[Length], PrivateImplementationDetails.DataField));
631+
boolemitAsCachedArrayFromBlob(NamedTypeSymbolspanType,BoundExpressionwrappedExpression,intelementCount,ImmutableArray<byte>data,refArrayTypeSymbolarrayType,TypeSymbolelementType)
649632
{
650-
// The ReadOnlySpan<T>(T[] array) constructor we need is missing or something went wrong.
651-
returnfalse;
633+
if(!tryGetReadOnlySpanArrayCtor(wrappedExpression.Syntax,outvarrosArrayCtor))
634+
{
635+
returnfalse;
636+
}
637+
638+
// If we're dealing with an array of enums, we need to handle the possibility that the data blob
639+
// is the same for multiple enums all with the same underlying type, or even with the underlying type
640+
// itself. This is addressed by always caching an array for the underlying type, and then relying on
641+
// arrays being covariant between the underlying type and the enum type, so that it's safe to do:
642+
// new ReadOnlySpan<EnumType>(arrayOfUnderlyingType);
643+
// It's important to have a consistent type here, as otherwise the type of the caching field could
644+
// end up changing non-deterministically based on which type for a given blob was encountered first.
645+
// Also, even if we're not dealing with an enum, we still create a new array type that drops any
646+
// annotations that may have initially been associated with the element type; this is similarly to
647+
// ensure deterministic behavior.
648+
arrayType=arrayType.WithElementType(TypeWithAnnotations.Create(elementType.EnumUnderlyingTypeOrSelf()));
649+
650+
varcachingField=_builder.module.GetArrayCachingFieldForData(data,_module.Translate(arrayType),wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
651+
vararrayNotNullLabel=newobject();
652+
653+
// T[]? array = PrivateImplementationDetails.cachingField;
654+
// if (array is not null) goto arrayNotNull;
655+
_builder.EmitOpCode(ILOpCode.Ldsfld);
656+
_builder.EmitToken(cachingField,wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
657+
_builder.EmitOpCode(ILOpCode.Dup);
658+
_builder.EmitBranch(ILOpCode.Brtrue,arrayNotNullLabel);
659+
660+
// array = new T[elementCount];
661+
// RuntimeHelpers.InitializeArray(token, array);
662+
// PrivateImplementationDetails.cachingField = array;
663+
_builder.EmitOpCode(ILOpCode.Pop);
664+
_builder.EmitIntConstant(elementCount);
665+
_builder.EmitOpCode(ILOpCode.Newarr);
666+
EmitSymbolToken(arrayType.ElementType,wrappedExpression.Syntax);
667+
_builder.EmitArrayBlockInitializer(data,wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
668+
_builder.EmitOpCode(ILOpCode.Dup);
669+
_builder.EmitOpCode(ILOpCode.Stsfld);
670+
_builder.EmitToken(cachingField,wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
671+
672+
// arrayNotNullLabel:
673+
// new ReadOnlySpan<T>(array)
674+
_builder.MarkLabel(arrayNotNullLabel);
675+
_builder.EmitOpCode(ILOpCode.Newobj,0);
676+
EmitSymbolToken(rosArrayCtor.AsMember(spanType),wrappedExpression.Syntax,optArgList:null);
677+
returntrue;
678+
}
679+
680+
// Emit: new ReadOnlySpan<ElementType>(PrivateImplementationDetails.cachingField ??= new ElementType[] { ... constants ... })
681+
booltryEmitAsCachedArrayOfConstants(BoundArrayCreationarrayCreation,ArrayTypeSymbolarrayType,TypeSymbolelementType,NamedTypeSymbolspanType,boolused,BoundExpression?inPlaceTarget)
682+
{
683+
varinitializer=arrayCreation.InitializerOpt;
684+
if(initializer==null)
685+
{
686+
returnfalse;
687+
}
688+
689+
varinitializers=initializer.Initializers;
690+
if(initializers.Any(static init=>init.ConstantValueOpt==null))
691+
{
692+
returnfalse;
693+
}
694+
695+
if(!tryGetReadOnlySpanArrayCtor(arrayCreation.Syntax,outvarrosArrayCtor))
696+
{
697+
returnfalse;
698+
}
699+
700+
Debug.Assert(!elementType.IsEnumType());
701+
702+
ImmutableArray<ConstantValue>constants=initializers.Select(static init=>init.ConstantValueOpt!).ToImmutableArray();
703+
704+
if(constants.IsEmpty)
705+
{
706+
emitEmptyReadonlySpan(spanType,arrayCreation,used,inPlaceTarget);
707+
returntrue;
708+
}
709+
710+
Cci.IFieldReferencecachingField=_builder.module.GetArrayCachingFieldForConstants(constants,_module.Translate(arrayType),
711+
arrayCreation.Syntax,_diagnostics.DiagnosticBag);
712+
713+
vararrayNotNullLabel=newobject();
714+
715+
// T[]? array = PrivateImplementationDetails.cachingField;
716+
// if (array is not null) goto arrayNotNull;
717+
_builder.EmitOpCode(ILOpCode.Ldsfld);
718+
_builder.EmitToken(cachingField,arrayCreation.Syntax,_diagnostics.DiagnosticBag);
719+
_builder.EmitOpCode(ILOpCode.Dup);
720+
_builder.EmitBranch(ILOpCode.Brtrue,arrayNotNullLabel);
721+
722+
// array = arrayCreation;
723+
// PrivateImplementationDetails.cachingField = array;
724+
_builder.EmitOpCode(ILOpCode.Pop);
725+
EmitExpression(arrayCreation,used:true);
726+
_builder.EmitOpCode(ILOpCode.Dup);
727+
_builder.EmitOpCode(ILOpCode.Stsfld);
728+
_builder.EmitToken(cachingField,arrayCreation.Syntax,_diagnostics.DiagnosticBag);
729+
730+
// arrayNotNullLabel:
731+
// new ReadOnlySpan<T>(array)
732+
_builder.MarkLabel(arrayNotNullLabel);
733+
_builder.EmitOpCode(ILOpCode.Newobj,0);
734+
EmitSymbolToken(rosArrayCtor.AsMember(spanType),arrayCreation.Syntax,optArgList:null);
735+
736+
returntrue;
737+
}
738+
739+
// The span is empty. Optimize away the array. This works regardless of the size of the type.
740+
voidemitEmptyReadonlySpan(NamedTypeSymbolspanType,BoundExpressionwrappedExpression,boolused,BoundExpression?inPlaceTarget)
741+
{
742+
// If this is in-place initialization, call the default ctor.
743+
if(inPlaceTargetis notnull)
744+
{
745+
EmitAddress(inPlaceTarget,Binder.AddressKind.Writeable);
746+
_builder.EmitOpCode(ILOpCode.Initobj);
747+
EmitSymbolToken(spanType,wrappedExpression.Syntax);
748+
if(used)
749+
{
750+
EmitExpression(inPlaceTarget,used:true);
751+
}
752+
}
753+
else
754+
{
755+
// Otherwise, assign it to a default value / empty span.
756+
Debug.Assert(used);
757+
EmitDefaultValue(spanType,used,wrappedExpression.Syntax);
758+
}
759+
}
760+
761+
booltryGetReadOnlySpanArrayCtor(SyntaxNodesyntax,[NotNullWhen(true)]outMethodSymbol?rosArrayCtor)
762+
{
763+
rosArrayCtor=(MethodSymbol?)Binder.GetWellKnownTypeMember(_module.Compilation,WellKnownMember.System_ReadOnlySpan_T__ctor_Array,_diagnostics,syntax:syntax,isOptional:true);
764+
if(rosArrayCtorisnull)
765+
{
766+
// The ReadOnlySpan<T>(T[] array) constructor we need is missing or something went wrong.
767+
returnfalse;
768+
}
769+
770+
Debug.Assert(!rosArrayCtor.HasUnsupportedMetadata);
771+
returntrue;
652772
}
653-
Debug.Assert(!rosArrayCtor.HasUnsupportedMetadata);
654-
655-
// If we're dealing with an array of enums, we need to handle the possibility that the data blob
656-
// is the same for multiple enums all with the same underlying type, or even with the underlying type
657-
// itself. This is addressed by always caching an array for the underlying type, and then relying on
658-
// arrays being covariant between the underlying type and the enum type, so that it's safe to do:
659-
// new ReadOnlySpan<EnumType>(arrayOfUnderlyingType);
660-
// It's important to have a consistent type here, as otherwise the type of the caching field could
661-
// end up changing non-deterministically based on which type for a given blob was encountered first.
662-
// Also, even if we're not dealing with an enum, we still create a new array type that drops any
663-
// annotations that may have initially been associated with the element type; this is similarly to
664-
// ensure deterministic behavior.
665-
arrayType=arrayType.WithElementType(TypeWithAnnotations.Create(elementType.EnumUnderlyingTypeOrSelf()));
666-
667-
varcachingField=_builder.module.GetArrayCachingFieldForData(data,_module.Translate(arrayType),wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
668-
vararrayNotNullLabel=newobject();
669-
670-
// T[]? array = PrivateImplementationDetails.cachingField;
671-
// if (array is not null) goto arrayNotNull;
672-
_builder.EmitOpCode(ILOpCode.Ldsfld);
673-
_builder.EmitToken(cachingField,wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
674-
_builder.EmitOpCode(ILOpCode.Dup);
675-
_builder.EmitBranch(ILOpCode.Brtrue,arrayNotNullLabel);
676-
677-
// array = new T[elementCount];
678-
// RuntimeHelpers.InitializeArray(token, array);
679-
// PrivateImplementationDetails.cachingField = array;
680-
_builder.EmitOpCode(ILOpCode.Pop);
681-
_builder.EmitIntConstant(elementCount);
682-
_builder.EmitOpCode(ILOpCode.Newarr);
683-
EmitSymbolToken(arrayType.ElementType,wrappedExpression.Syntax);
684-
_builder.EmitArrayBlockInitializer(data,wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
685-
_builder.EmitOpCode(ILOpCode.Dup);
686-
_builder.EmitOpCode(ILOpCode.Stsfld);
687-
_builder.EmitToken(cachingField,wrappedExpression.Syntax,_diagnostics.DiagnosticBag);
688-
689-
// arrayNotNullLabel:
690-
// new ReadOnlySpan<T>(array)
691-
_builder.MarkLabel(arrayNotNullLabel);
692-
_builder.EmitOpCode(ILOpCode.Newobj,0);
693-
EmitSymbolToken(rosArrayCtor.AsMember(spanType),wrappedExpression.Syntax,optArgList:null);
694-
returntrue;
695773
}
696774

697775
/// <summary>Gets whether the element type of an array is appropriate for storing in a blob.</summary>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp