1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
- using System ;
5
4
using System . Collections . Generic ;
6
- using System . Collections . Immutable ;
7
5
using System . Diagnostics ;
8
- using System . Linq ;
9
6
using System . Runtime . InteropServices ;
10
7
using Internal . TypeSystem ;
11
8
@@ -29,7 +26,19 @@ private sealed class LoweringVisitor(int pointerSize) : FieldLayoutIntervalCalcu
29
26
protected override bool IntervalsHaveCompatibleTags ( LoweredType existingTag , LoweredType nextTag )
30
27
{
31
28
// Adjacent ranges mapped to opaque or empty can be combined.
32
- return existingTag is LoweredType . Opaque or LoweredType . Empty && nextTag is LoweredType . Opaque or LoweredType . Empty ;
29
+ if ( existingTag is LoweredType . Empty
30
+ && nextTag is LoweredType . Empty )
31
+ {
32
+ return true ;
33
+ }
34
+
35
+ if ( existingTag is LoweredType . Opaque
36
+ && nextTag is LoweredType . Opaque )
37
+ {
38
+ return true ;
39
+ }
40
+
41
+ return false ;
33
42
}
34
43
35
44
protected override FieldLayoutInterval CombineIntervals ( FieldLayoutInterval firstInterval , FieldLayoutInterval nextInterval )
@@ -94,13 +103,39 @@ protected override LoweredType GetIntervalDataForType(int offset, TypeDesc field
94
103
95
104
protected override bool NeedsRecursiveLayout ( int offset , TypeDesc fieldType ) => fieldType . IsValueType && ! fieldType . IsPrimitiveNumeric ;
96
105
97
- public List < CorInfoType > GetLoweredTypeSequence ( )
106
+ private List < FieldLayoutInterval > CreateConsolidatedIntervals ( )
98
107
{
99
- // We need to track the sequence size to ensure we break up the opaque ranges
100
- // into correctly-sized integers that do not require padding.
101
- int loweredSequenceSize = 0 ;
102
- List < CorInfoType > loweredTypes = new ( ) ;
108
+ // First, let's make a list of exclusively non-empty intervals.
109
+ List < FieldLayoutInterval > consolidatedIntervals = new ( Intervals . Count ) ;
103
110
foreach ( var interval in Intervals )
111
+ {
112
+ if ( interval . Tag != LoweredType . Empty )
113
+ {
114
+ consolidatedIntervals . Add ( interval ) ;
115
+ }
116
+ }
117
+
118
+ // Now, we'll look for adjacent opaque intervals and combine them.
119
+ for ( int i = 0 ; i < consolidatedIntervals . Count - 1 ; i ++ )
120
+ {
121
+ // Only merge sequential opaque intervals that are within the same PointerSize-sized chunk.
122
+ if ( consolidatedIntervals [ i ] . Tag == LoweredType . Opaque
123
+ && consolidatedIntervals [ i + 1 ] . Tag == LoweredType . Opaque
124
+ && ( consolidatedIntervals [ i ] . EndSentinel - 1 ) / PointerSize == consolidatedIntervals [ i + 1 ] . Start / PointerSize )
125
+ {
126
+ consolidatedIntervals [ i ] = CombineIntervals ( consolidatedIntervals [ i ] , consolidatedIntervals [ i + 1 ] ) ;
127
+ consolidatedIntervals . RemoveAt ( i + 1 ) ;
128
+ i -- ;
129
+ }
130
+ }
131
+
132
+ return consolidatedIntervals ;
133
+ }
134
+
135
+ public List < ( CorInfoType , int ) > GetLoweredTypeSequence ( )
136
+ {
137
+ List < ( CorInfoType , int ) > loweredTypes = new ( ) ;
138
+ foreach ( var interval in CreateConsolidatedIntervals ( ) )
104
139
{
105
140
// Empty intervals at this point don't need to be represented in the lowered type sequence.
106
141
// We want to skip over them.
@@ -109,26 +144,20 @@ public List<CorInfoType> GetLoweredTypeSequence()
109
144
110
145
if ( interval . Tag == LoweredType . Float )
111
146
{
112
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_FLOAT ) ;
113
- loweredSequenceSize = loweredSequenceSize . AlignUp ( 4 ) ;
114
- loweredSequenceSize += 4 ;
147
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_FLOAT , interval . Start ) ) ;
115
148
}
116
149
117
150
if ( interval . Tag == LoweredType . Double )
118
151
{
119
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_DOUBLE ) ;
120
- loweredSequenceSize = loweredSequenceSize . AlignUp ( 8 ) ;
121
- loweredSequenceSize += 8 ;
152
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_DOUBLE , interval . Start ) ) ;
122
153
}
123
154
124
155
if ( interval . Tag == LoweredType . Int64 )
125
156
{
126
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_LONG ) ;
127
- loweredSequenceSize = loweredSequenceSize . AlignUp ( 8 ) ;
128
- loweredSequenceSize += 8 ;
157
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_LONG , interval . Start ) ) ;
129
158
}
130
159
131
- if ( interval . Tag == LoweredType . Opaque )
160
+ if ( interval . Tag is LoweredType . Opaque )
132
161
{
133
162
// We need to split the opaque ranges into integer parameters.
134
163
// As part of this splitting, we must ensure that we don't introduce alignment padding.
@@ -138,40 +167,42 @@ public List<CorInfoType> GetLoweredTypeSequence()
138
167
// The lowered range is allowed to extend past the end of the opaque range (including past the end of the struct),
139
168
// but not into the next non-empty interval.
140
169
// However, due to the properties of the lowering (the only non-8 byte elements of the lowering are 4-byte floats),
141
- // we'll never encounter a scneario where we need would need to account for a correctly-aligned
170
+ // we'll never encounter a scenario where we need would need to account for a correctly-aligned
142
171
// opaque range of > 4 bytes that we must not pad to 8 bytes.
143
172
144
173
145
- // As long as we need to fill more than 4 bytes and the sequence is currently 8-byte aligned, we'll split into 8-byte integers.
146
- // If we have more than 2 bytes but less than 4 and the sequence is 4-byte aligned, we'll use a 4-byte integer to represent the rest of the parameters.
147
- // If we have 2 bytes and the sequence is 2-byte aligned, we'll use a 2-byte integer to represent the rest of the parameters.
148
- // If we have 1 byte, we'll use a 1-byte integer to represent the rest of the parameters.
174
+ // While we need to fill more than 4 bytes and the sequence is currently 8-byte aligned, we'll split into 8-byte integers.
175
+ // While we need to fill more than 2 bytes but less than 4 and the sequence is 4-byte aligned, we'll use a 4-byte integer to represent the rest of the parameters.
176
+ // While we need to fill more than 1 bytes and the sequence is 2-byte aligned, we'll use a 2-byte integer to represent the rest of the parameters.
177
+ // While we need to fill at least 1 byte, we'll use a 1-byte integer to represent the rest of the parameters.
178
+ int opaqueIntervalStart = interval . Start ;
149
179
int remainingIntervalSize = interval . Size ;
150
- while ( remainingIntervalSize > 4 && loweredSequenceSize == loweredSequenceSize . AlignUp ( 8 ) )
180
+ while ( remainingIntervalSize > 0 )
151
181
{
152
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_LONG ) ;
153
- loweredSequenceSize += 8 ;
154
- remainingIntervalSize -= 8 ;
155
- }
156
-
157
- if ( remainingIntervalSize > 2 && loweredSequenceSize == loweredSequenceSize . AlignUp ( 4 ) )
158
- {
159
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_INT ) ;
160
- loweredSequenceSize += 4 ;
161
- remainingIntervalSize -= 4 ;
162
- }
163
-
164
- if ( remainingIntervalSize > 1 && loweredSequenceSize == loweredSequenceSize . AlignUp ( 2 ) )
165
- {
166
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_SHORT ) ;
167
- loweredSequenceSize += 2 ;
168
- remainingIntervalSize -= 2 ;
169
- }
170
-
171
- if ( remainingIntervalSize == 1 )
172
- {
173
- loweredSequenceSize += 1 ;
174
- loweredTypes . Add ( CorInfoType . CORINFO_TYPE_BYTE ) ;
182
+ if ( remainingIntervalSize > 4 && opaqueIntervalStart == opaqueIntervalStart . AlignUp ( 8 ) )
183
+ {
184
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_LONG , opaqueIntervalStart ) ) ;
185
+ opaqueIntervalStart += 8 ;
186
+ remainingIntervalSize -= 8 ;
187
+ }
188
+ else if ( remainingIntervalSize > 2 && opaqueIntervalStart == opaqueIntervalStart . AlignUp ( 4 ) )
189
+ {
190
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_INT , opaqueIntervalStart ) ) ;
191
+ opaqueIntervalStart += 4 ;
192
+ remainingIntervalSize -= 4 ;
193
+ }
194
+ else if ( remainingIntervalSize > 1 && opaqueIntervalStart == opaqueIntervalStart . AlignUp ( 2 ) )
195
+ {
196
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_SHORT , opaqueIntervalStart ) ) ;
197
+ opaqueIntervalStart += 2 ;
198
+ remainingIntervalSize -= 2 ;
199
+ }
200
+ else
201
+ {
202
+ opaqueIntervalStart ++ ;
203
+ remainingIntervalSize -- ;
204
+ loweredTypes . Add ( ( CorInfoType . CORINFO_TYPE_BYTE , opaqueIntervalStart ) ) ;
205
+ }
175
206
}
176
207
}
177
208
}
@@ -190,7 +221,7 @@ public static CORINFO_SWIFT_LOWERING LowerTypeForSwiftSignature(TypeDesc type)
190
221
LoweringVisitor visitor = new ( type . Context . Target . PointerSize ) ;
191
222
visitor . AddFields ( type , addTrailingEmptyInterval : false ) ;
192
223
193
- List < CorInfoType > loweredTypes = visitor . GetLoweredTypeSequence ( ) ;
224
+ List < ( CorInfoType type , int offset ) > loweredTypes = visitor . GetLoweredTypeSequence ( ) ;
194
225
195
226
// If a type has a primitive sequence with more than 4 elements, Swift passes it by reference.
196
227
if ( loweredTypes . Count > 4 )
@@ -204,7 +235,11 @@ public static CORINFO_SWIFT_LOWERING LowerTypeForSwiftSignature(TypeDesc type)
204
235
numLoweredElements = loweredTypes . Count
205
236
} ;
206
237
207
- CollectionsMarshal . AsSpan ( loweredTypes ) . CopyTo ( lowering . LoweredElements ) ;
238
+ for ( int i = 0 ; i < loweredTypes . Count ; i ++ )
239
+ {
240
+ lowering . LoweredElements [ i ] = loweredTypes [ i ] . type ;
241
+ lowering . Offsets [ i ] = ( uint ) loweredTypes [ i ] . offset ;
242
+ }
208
243
209
244
return lowering ;
210
245
}
0 commit comments