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
@@ -29,44 +33,97 @@ private void EmitStackAllocInitializers(TypeSymbol type, BoundArrayInitializatio
2933var initializationStyle = ShouldEmitBlockInitializerForStackAlloc ( elementType , initExprs ) ;
3034if ( initializationStyle == ArrayInitializerStyle . Element )
3135{
36+ emitLocalloc ( ) ;
3237EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : true ) ;
3338}
3439else
3540{
36- ImmutableArray < byte > data = this . GetRawData ( initExprs ) ;
41+ bool mixedInitialized = false ;
42+
43+ emitLocalloc ( ) ;
44+
45+ var sizeInBytes = elementType . EnumUnderlyingTypeOrSelf ( ) . SpecialType . SizeInBytes ( ) ;
46+
47+ ImmutableArray < byte > data = GetRawData ( initExprs ) ;
3748if ( data . All ( datum=> datum == data [ 0 ] ) )
3849{
3950// All bytes are the same, no need for metadata blob, just initblk to fill it with the repeated value.
4051_builder . EmitOpCode ( ILOpCode . Dup ) ;
4152_builder . EmitIntConstant ( data [ 0 ] ) ;
4253_builder . EmitIntConstant ( data . Length ) ;
4354_builder . EmitOpCode ( ILOpCode . Initblk , - 3 ) ;
44-
45- if ( initializationStyle == ArrayInitializerStyle . Mixed )
46- {
47- EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : false ) ;
48- }
4955}
50- else if ( elementType . EnumUnderlyingTypeOrSelf ( ) . SpecialType . SizeInBytes ( ) == 1 )
56+ else if ( sizeInBytes == 1 )
5157{
5258// Initialize the stackalloc by copying the data from a metadata blob
5359var field = _builder . module . GetFieldForData ( data , alignment : 1 , inits . Syntax , _diagnostics . DiagnosticBag ) ;
5460_builder . EmitOpCode ( ILOpCode . Dup ) ;
5561_builder . EmitOpCode ( ILOpCode . Ldsflda ) ;
5662_builder . EmitToken ( field , inits . Syntax , _diagnostics . DiagnosticBag ) ;
5763_builder . EmitIntConstant ( data . Length ) ;
64+ _builder . EmitUnaligned ( alignment : 1 ) ;
5865_builder . EmitOpCode ( ILOpCode . Cpblk , - 3 ) ;
59-
60- if ( initializationStyle == ArrayInitializerStyle . Mixed )
66+ }
67+ else
68+ {
69+ var syntaxNode = inits . Syntax ;
70+ if ( Binder . GetWellKnownTypeMember ( _module . Compilation , WellKnownMember . System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle , _diagnostics , syntax : syntaxNode , isOptional : true ) is MethodSymbol createSpanHelper &&
71+ Binder . GetWellKnownTypeMember ( _module . Compilation , WellKnownMember . System_ReadOnlySpan_T__get_Item , _diagnostics , syntax : syntaxNode , isOptional : true ) is MethodSymbol spanGetItemDefinition )
6172{
62- EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : false ) ;
73+ // Use RuntimeHelpers.CreateSpan and cpblk.
74+ var readOnlySpan = spanGetItemDefinition . ContainingType . Construct ( elementType ) ;
75+ Debug . Assert ( TypeSymbol . Equals ( readOnlySpan . OriginalDefinition , _module . Compilation . GetWellKnownType ( WellKnownType . System_ReadOnlySpan_T ) , TypeCompareKind . ConsiderEverything ) ) ;
76+ var spanGetItem = spanGetItemDefinition . AsMember ( readOnlySpan ) ;
77+
78+ _builder . EmitOpCode ( ILOpCode . Dup ) ;
79+
80+ // ldtoken <PrivateImplementationDetails>...
81+ // call ReadOnlySpan<elementType> RuntimeHelpers::CreateSpan<elementType>(fldHandle)
82+ var field = _builder . module . GetFieldForData ( data , alignment : ( ushort ) sizeInBytes , syntaxNode , _diagnostics . DiagnosticBag ) ;
83+ _builder . EmitOpCode ( ILOpCode . Ldtoken ) ;
84+ _builder . EmitToken ( field , syntaxNode , _diagnostics . DiagnosticBag ) ;
85+ _builder . EmitOpCode ( ILOpCode . Call , 0 ) ;
86+ var createSpanHelperReference = createSpanHelper . Construct ( elementType ) . GetCciAdapter ( ) ;
87+ _builder . EmitToken ( createSpanHelperReference , syntaxNode , _diagnostics . DiagnosticBag ) ;
88+
89+ var temp = AllocateTemp ( readOnlySpan , syntaxNode ) ;
90+ _builder . EmitLocalStore ( temp ) ;
91+ _builder . EmitLocalAddress ( temp ) ;
92+
93+ // span.get_Item[0]
94+ _builder . EmitIntConstant ( 0 ) ;
95+ _builder . EmitOpCode ( ILOpCode . Call , 0 ) ;
96+ EmitSymbolToken ( spanGetItem , syntaxNode , optArgList : null ) ;
97+
98+ _builder . EmitIntConstant ( data . Length ) ;
99+ if ( sizeInBytes != 8 )
100+ {
101+ _builder . EmitUnaligned ( ( sbyte ) sizeInBytes ) ;
102+ }
103+ _builder . EmitOpCode ( ILOpCode . Cpblk , - 3 ) ;
104+
105+ FreeTemp ( temp ) ;
106+ }
107+ else
108+ {
109+ EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : true ) ;
110+ mixedInitialized = true ;
63111}
64112}
65- else
113+
114+ if ( initializationStyle == ArrayInitializerStyle . Mixed && ! mixedInitialized )
66115{
67- EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : true ) ;
116+ EmitElementStackAllocInitializers ( elementType , initExprs , includeConstants : false ) ;
68117}
69118}
119+
120+ void emitLocalloc ( )
121+ {
122+ EmitExpression ( count , used : true ) ;
123+
124+ _sawStackalloc = true ;
125+ _builder . EmitOpCode ( ILOpCode . Localloc ) ;
126+ }
70127}
71128
72129private ArrayInitializerStyle ShouldEmitBlockInitializerForStackAlloc ( TypeSymbol elementType , ImmutableArray < BoundExpression > inits )
@@ -78,7 +135,7 @@ private ArrayInitializerStyle ShouldEmitBlockInitializerForStackAlloc(TypeSymbol
78135return ArrayInitializerStyle . Element ;
79136}
80137
81- if ( elementType . EnumUnderlyingTypeOrSelf ( ) . SpecialType . IsBlittable ( ) )
138+ if ( IsTypeAllowedInBlobWrapper ( elementType . EnumUnderlyingTypeOrSelf ( ) . SpecialType ) )
82139{
83140int initCount = 0 ;
84141int constCount = 0 ;