22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5- #nullable disable
6-
75using System ;
86using System . Collections . Immutable ;
97using System . Diagnostics ;
108using System . Linq ;
119using System . Reflection . Metadata ;
1210using Microsoft . CodeAnalysis . CSharp . Symbols ;
13- using Microsoft . CodeAnalysis . PooledObjects ;
1411
1512namespace Microsoft . CodeAnalysis . CSharp . CodeGen
1613{
1714internal partial class CodeGenerator
1815{
19- private void EmitStackAllocInitializers ( TypeSymbol type , BoundArrayInitialization inits )
16+ private void EmitStackAlloc ( TypeSymbol type , BoundArrayInitialization ? inits , BoundExpression count )
2017{
18+ if ( inits is null )
19+ {
20+ emitLocalloc ( ) ;
21+ return ;
22+ }
23+
2124Debug . Assert ( type is PointerTypeSymbol || type is NamedTypeSymbol ) ;
25+ Debug . Assert ( _diagnostics . DiagnosticBag is notnull ) ;
2226
2327var elementType = ( type . TypeKind == TypeKind . Pointer
2428? ( ( PointerTypeSymbol ) type ) . PointedAtTypeWithAnnotations
2529: ( ( NamedTypeSymbol ) type ) . TypeArgumentsWithAnnotationsNoUseSiteDiagnostics [ 0 ] ) . Type ;
2630
31+ bool isReadOnlySpan = TypeSymbol . Equals (
32+ ( type as NamedTypeSymbol ) ? . OriginalDefinition , _module . Compilation . GetWellKnownType ( WellKnownType . System_ReadOnlySpan_T ) , TypeCompareKind . ConsiderEverything ) ;
33+
2734var initExprs = inits . Initializers ;
2835
29- var initializationStyle = ShouldEmitBlockInitializerForStackAlloc ( elementType , initExprs ) ;
30- if ( initializationStyle == ArrayInitializerStyle . Element )
36+ bool isEncDelta = _module . IsEncDelta ;
37+ var initializationStyle = ShouldEmitBlockInitializerForStackAlloc ( elementType , initExprs , isEncDelta ) ;
38+
39+ if ( isReadOnlySpan )
3140{
41+ var createSpanHelper = getCreateSpanHelper ( _module , elementType ) ;
42+
43+ // ROS<T> is only used here if it has already been decided to use CreateSpan
44+ Debug . Assert ( createSpanHelper is notnull ) ;
45+ Debug . Assert ( UseCreateSpanForReadOnlySpanStackAlloc ( elementType , inits , isEncDelta : isEncDelta ) ) ;
46+
47+ EmitExpression ( count , used : false ) ;
48+
49+ ImmutableArray < byte > data = GetRawData ( initExprs ) ;
50+ _builder . EmitCreateSpan ( data , createSpanHelper , inits . Syntax , _diagnostics . DiagnosticBag ) ;
51+ }
52+ else if ( initializationStyle == ArrayInitializerStyle . Element )
53+ {
54+ emitLocalloc ( ) ;
3255EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : true ) ;
3356}
3457else
3558{
36- ImmutableArray < byte > data = this . GetRawData ( initExprs ) ;
59+ bool mixedInitialized = false ;
60+
61+ emitLocalloc ( ) ;
62+
63+ ImmutableArray < byte > data = GetRawData ( initExprs ) ;
3764if ( data . All ( datum=> datum == data [ 0 ] ) )
3865{
3966// All bytes are the same, no need for metadata blob, just initblk to fill it with the repeated value.
4067_builder . EmitOpCode ( ILOpCode . Dup ) ;
4168_builder . EmitIntConstant ( data [ 0 ] ) ;
4269_builder . EmitIntConstant ( data . Length ) ;
4370_builder . EmitOpCode ( ILOpCode . Initblk , - 3 ) ;
44-
45- if ( initializationStyle == ArrayInitializerStyle . Mixed )
46- {
47- EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : false ) ;
48- }
4971}
5072else if ( elementType . EnumUnderlyingTypeOrSelf ( ) . SpecialType . SizeInBytes ( ) == 1 )
5173{
@@ -56,22 +78,74 @@ private void EmitStackAllocInitializers(TypeSymbol type, BoundArrayInitializatio
5678_builder . EmitToken ( field , inits . Syntax , _diagnostics . DiagnosticBag ) ;
5779_builder . EmitIntConstant ( data . Length ) ;
5880_builder . EmitOpCode ( ILOpCode . Cpblk , - 3 ) ;
59-
60- if ( initializationStyle == ArrayInitializerStyle . Mixed )
81+ }
82+ else
83+ {
84+ if ( getCreateSpanHelper ( _module , elementType ) is { } createSpanHelper &&
85+ _module . Compilation . GetWellKnownTypeMember ( WellKnownMember . System_ReadOnlySpan_T__GetPinnableReference ) is MethodSymbol getPinnableReference )
6186{
62- EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : false ) ;
87+ // Use RuntimeHelpers.CreateSpan and cpblk.
88+ EmitStackAllocBlockMultiByteInitializer ( data , createSpanHelper , getPinnableReference , elementType , inits . Syntax , _diagnostics . DiagnosticBag ) ;
89+ }
90+ else
91+ {
92+ EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : true ) ;
93+ mixedInitialized = true ;
6394}
6495}
65- else
96+
97+ if ( initializationStyle == ArrayInitializerStyle . Mixed && ! mixedInitialized )
6698{
67- EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : true ) ;
99+ EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : false ) ;
68100}
69101}
102+
103+ void emitLocalloc ( )
104+ {
105+ EmitExpression ( count , used : true ) ;
106+
107+ _sawStackalloc = true ;
108+ _builder . EmitOpCode ( ILOpCode . Localloc ) ;
109+ }
110+
111+ static Cci . IMethodReference ? getCreateSpanHelper ( Emit . PEModuleBuilder module , TypeSymbol elementType )
112+ {
113+ var member = module . Compilation . GetWellKnownTypeMember ( WellKnownMember . System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle ) ;
114+ return ( ( MethodSymbol ? ) member ) ? . Construct ( elementType ) . GetCciAdapter ( ) ;
115+ }
116+ }
117+
118+ private void EmitStackAllocBlockMultiByteInitializer ( ImmutableArray < byte > data , Cci . IMethodReference createSpanHelper , MethodSymbol getPinnableReferenceDefinition , TypeSymbol elementType , SyntaxNode syntaxNode , DiagnosticBag diagnostics )
119+ {
120+ var readOnlySpan = getPinnableReferenceDefinition . ContainingType . Construct ( elementType ) ;
121+ Debug . Assert ( TypeSymbol . Equals ( readOnlySpan . OriginalDefinition , _module . Compilation . GetWellKnownType ( WellKnownType . System_ReadOnlySpan_T ) , TypeCompareKind . ConsiderEverything ) ) ;
122+ var getPinnableReference = getPinnableReferenceDefinition . AsMember ( readOnlySpan ) ;
123+
124+ _builder . EmitOpCode ( ILOpCode . Dup ) ;
125+ _builder . EmitCreateSpan ( data , createSpanHelper , syntaxNode , diagnostics ) ;
126+
127+ var temp = AllocateTemp ( readOnlySpan , syntaxNode ) ;
128+ _builder . EmitLocalStore ( temp ) ;
129+ _builder . EmitLocalAddress ( temp ) ;
130+
131+ _builder . EmitOpCode ( ILOpCode . Call , 0 ) ;
132+ EmitSymbolToken ( getPinnableReference , syntaxNode , optArgList : null ) ;
133+ _builder . EmitIntConstant ( data . Length ) ;
134+ _builder . EmitOpCode ( ILOpCode . Cpblk , - 3 ) ;
135+
136+ FreeTemp ( temp ) ;
137+ }
138+
139+ internal static bool UseCreateSpanForReadOnlySpanStackAlloc ( TypeSymbol elementType , BoundArrayInitialization ? inits , bool isEncDelta )
140+ {
141+ return inits ? . Initializers is { } initExprs &&
142+ elementType . EnumUnderlyingTypeOrSelf ( ) . SpecialType . SizeInBytes ( ) >= 1 &&
143+ ShouldEmitBlockInitializerForStackAlloc ( elementType , initExprs , isEncDelta ) == ArrayInitializerStyle . Block ;
70144}
71145
72- private ArrayInitializerStyle ShouldEmitBlockInitializerForStackAlloc ( TypeSymbol elementType , ImmutableArray < BoundExpression > inits )
146+ private static ArrayInitializerStyle ShouldEmitBlockInitializerForStackAlloc ( TypeSymbol elementType , ImmutableArray < BoundExpression > inits , bool isEncDelta )
73147{
74- if ( _module . IsEncDelta )
148+ if ( isEncDelta )
75149{
76150// Avoid using FieldRva table. Can be allowed if tested on all supported runtimes.
77151// Consider removing: https://github.com/dotnet/roslyn/issues/69480
@@ -103,7 +177,7 @@ private ArrayInitializerStyle ShouldEmitBlockInitializerForStackAlloc(TypeSymbol
103177return ArrayInitializerStyle . Element ;
104178}
105179
106- private void StackAllocInitializerCount ( ImmutableArray < BoundExpression > inits , ref int initCount , ref int constInits )
180+ private static void StackAllocInitializerCount ( ImmutableArray < BoundExpression > inits , ref int initCount , ref int constInits )
107181{
108182if ( inits . Length == 0 )
109183{