@@ -80,6 +80,221 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_floatx10) {
80
80
RunSignatureTest (Z, " floatx10" , arguments, floatType);
81
81
}
82
82
83
+ // Test with 3-byte struct.
84
+ //
85
+ // On ia32, result pointer is passed on stack and passed back in eax.
86
+ //
87
+ // On x64, is passed and returned in registers, except for on Windows where it
88
+ // is passed on stack because of its size not being a power of two.
89
+ //
90
+ // See the *.expect in ./unit_tests for this behavior.
91
+ UNIT_TEST_CASE_WITH_ZONE (NativeCallingConvention_struct3bytesx10) {
92
+ const auto & int8type = *new (Z) NativePrimitiveType (kInt8 );
93
+
94
+ auto & member_types = *new (Z) NativeTypes (Z, 3 );
95
+ member_types.Add (&int8type);
96
+ member_types.Add (&int8type);
97
+ member_types.Add (&int8type);
98
+ const auto & struct_type =
99
+ NativeCompoundType::FromNativeTypes (Z, member_types);
100
+
101
+ auto & arguments = *new (Z) NativeTypes (Z, 10 );
102
+ arguments.Add (&struct_type);
103
+ arguments.Add (&struct_type);
104
+ arguments.Add (&struct_type);
105
+ arguments.Add (&struct_type);
106
+ arguments.Add (&struct_type);
107
+ arguments.Add (&struct_type);
108
+ arguments.Add (&struct_type);
109
+ arguments.Add (&struct_type);
110
+ arguments.Add (&struct_type);
111
+ arguments.Add (&struct_type);
112
+
113
+ RunSignatureTest (Z, " struct3bytesx10" , arguments, struct_type);
114
+ }
115
+
116
+ // Test with homogenous struct.
117
+ //
118
+ // On arm softfp, the return pointer is passed in the first int register, and
119
+ // the first struct is passed in the next 3 registers and 1 stack slot.
120
+ //
121
+ // On arm hardfp, arm64, and x64 non-Windows the structs are passed in FPU
122
+ // registers until exhausted, the rest is passed on the stack, and struct is
123
+ // returned in FPU registers.
124
+ //
125
+ // On ia32 a return pointer and all arguments are passed on the stack.
126
+ //
127
+ // On x64 on Windows the structs are passed by pointer and pointer to the
128
+ // return value is passed in.
129
+ //
130
+ // See the *.expect in ./unit_tests for this behavior.
131
+ UNIT_TEST_CASE_WITH_ZONE (NativeCallingConvention_struct16bytesHomogenousx10) {
132
+ const auto & float_type = *new (Z) NativePrimitiveType (kFloat );
133
+ const auto & int8type = *new (Z) NativePrimitiveType (kInt8 );
134
+
135
+ // If passed in FPU registers, uses an even amount of them.
136
+ auto & member_types = *new (Z) NativeTypes (Z, 4 );
137
+ member_types.Add (&float_type);
138
+ member_types.Add (&float_type);
139
+ member_types.Add (&float_type);
140
+ member_types.Add (&float_type);
141
+ const auto & struct_type =
142
+ NativeCompoundType::FromNativeTypes (Z, member_types);
143
+
144
+ auto & arguments = *new (Z) NativeTypes (Z, 13 );
145
+ arguments.Add (&struct_type);
146
+ arguments.Add (&float_type); // Claim a single FPU register.
147
+ arguments.Add (&struct_type);
148
+ arguments.Add (&struct_type);
149
+ arguments.Add (&struct_type);
150
+ arguments.Add (&struct_type);
151
+ arguments.Add (&struct_type);
152
+ arguments.Add (&struct_type);
153
+ arguments.Add (&struct_type);
154
+ arguments.Add (&struct_type);
155
+ arguments.Add (&float_type); // Check float register back filling, if any.
156
+ arguments.Add (&int8type); // Check integer register back filling, if any.
157
+ arguments.Add (&struct_type); // Check stack alignment of struct.
158
+
159
+ RunSignatureTest (Z, " struct16bytesHomogenousx10" , arguments, struct_type);
160
+ }
161
+
162
+ // A fairly big struct.
163
+ //
164
+ // On arm, split up in 8-byte chunks. The first chunk goes into two registers,
165
+ // the rest on the stack. Note that r1 goes unused and is not backfilled.
166
+ //
167
+ // On arm64 and Windows x64 passed by a pointer to copy.
168
+ //
169
+ // On ia32, wholly passed on stack.
170
+ //
171
+ // On non-Windows x64, wholly passed on stack, and the integer argument
172
+ // backfills a still unoccupied integer register.
173
+ //
174
+ // See the *.expect in ./unit_tests for this behavior.
175
+ UNIT_TEST_CASE_WITH_ZONE (NativeCallingConvention_struct128bytesx1) {
176
+ const auto & int32_type = *new (Z) NativePrimitiveType (kInt32 );
177
+ const auto & int64_type = *new (Z) NativePrimitiveType (kInt64 );
178
+
179
+ auto & member_types = *new (Z) NativeTypes (Z, 16 );
180
+ member_types.Add (&int64_type);
181
+ member_types.Add (&int64_type);
182
+ member_types.Add (&int64_type);
183
+ member_types.Add (&int64_type);
184
+ member_types.Add (&int64_type);
185
+ member_types.Add (&int64_type);
186
+ member_types.Add (&int64_type);
187
+ member_types.Add (&int64_type);
188
+ member_types.Add (&int64_type);
189
+ member_types.Add (&int64_type);
190
+ member_types.Add (&int64_type);
191
+ member_types.Add (&int64_type);
192
+ member_types.Add (&int64_type);
193
+ member_types.Add (&int64_type);
194
+ member_types.Add (&int64_type);
195
+ member_types.Add (&int64_type);
196
+ const auto & struct_type =
197
+ NativeCompoundType::FromNativeTypes (Z, member_types);
198
+
199
+ auto & arguments = *new (Z) NativeTypes (Z, 2 );
200
+ arguments.Add (&struct_type);
201
+ arguments.Add (&int32_type); // Check integer register backfilling, if any.
202
+
203
+ RunSignatureTest (Z, " struct128bytesx1" , arguments, struct_type);
204
+ }
205
+
206
+ #if defined(TARGET_ARCH_X64)
207
+ // On x64 non-Windows a struct can be spread over an FPU and int register.
208
+ //
209
+ // See the *.expect in ./unit_tests for this behavior.
210
+ UNIT_TEST_CASE_WITH_ZONE (NativeCallingConvention_struct16bytesMixedx10) {
211
+ const auto & float_type = *new (Z) NativePrimitiveType (kFloat );
212
+ const auto & int32_type = *new (Z) NativePrimitiveType (kInt32 );
213
+
214
+ auto & member_types = *new (Z) NativeTypes (Z, 4 );
215
+ member_types.Add (&float_type);
216
+ member_types.Add (&float_type);
217
+ member_types.Add (&int32_type);
218
+ member_types.Add (&int32_type);
219
+ const auto & struct_type =
220
+ NativeCompoundType::FromNativeTypes (Z, member_types);
221
+
222
+ auto & arguments = *new (Z) NativeTypes (Z, 11 );
223
+ arguments.Add (&struct_type);
224
+ arguments.Add (&struct_type);
225
+ arguments.Add (&struct_type);
226
+ arguments.Add (&struct_type);
227
+ arguments.Add (&struct_type);
228
+ arguments.Add (&struct_type);
229
+ arguments.Add (&struct_type); // Integer registers exhausted, on stack.
230
+ arguments.Add (&struct_type);
231
+ arguments.Add (&struct_type);
232
+ arguments.Add (&struct_type);
233
+ arguments.Add (&float_type); // Use remaining FPU register.
234
+
235
+ RunSignatureTest (Z, " struct16bytesMixedx10" , arguments, struct_type);
236
+ }
237
+
238
+ // On x64 non-Windows a struct can be spread over an FPU and int register.
239
+ //
240
+ // See the *.expect in ./unit_tests for this behavior.
241
+ UNIT_TEST_CASE_WITH_ZONE (NativeCallingConvention_struct16bytesMixedx10_2) {
242
+ const auto & float_type = *new (Z) NativePrimitiveType (kFloat );
243
+ const auto & int32_type = *new (Z) NativePrimitiveType (kInt32 );
244
+
245
+ auto & member_types = *new (Z) NativeTypes (Z, 4 );
246
+ member_types.Add (&float_type);
247
+ member_types.Add (&float_type);
248
+ member_types.Add (&int32_type);
249
+ member_types.Add (&int32_type);
250
+ const auto & struct_type =
251
+ NativeCompoundType::FromNativeTypes (Z, member_types);
252
+
253
+ auto & arguments = *new (Z) NativeTypes (Z, 15 );
254
+ arguments.Add (&float_type);
255
+ arguments.Add (&float_type);
256
+ arguments.Add (&float_type);
257
+ arguments.Add (&float_type);
258
+ arguments.Add (&struct_type);
259
+ arguments.Add (&struct_type);
260
+ arguments.Add (&struct_type);
261
+ arguments.Add (&struct_type);
262
+ arguments.Add (&struct_type); // FPU registers exhausted, on stack.
263
+ arguments.Add (&struct_type);
264
+ arguments.Add (&struct_type);
265
+ arguments.Add (&struct_type);
266
+ arguments.Add (&struct_type);
267
+ arguments.Add (&struct_type);
268
+ arguments.Add (&int32_type); // Use remaining integer register.
269
+
270
+ RunSignatureTest (Z, " struct16bytesMixedx10_2" , arguments, struct_type);
271
+ }
272
+ #endif // defined(TARGET_ARCH_X64)
273
+
274
+ // On ia32 Windows a struct can be returned in registers, on non-Windows not.
275
+ //
276
+ // See the *.expect in ./unit_tests for this behavior.
277
+ UNIT_TEST_CASE_WITH_ZONE (NativeCallingConvention_struct8bytesx1) {
278
+ const auto & int8type = *new (Z) NativePrimitiveType (kInt8 );
279
+
280
+ auto & member_types = *new (Z) NativeTypes (Z, 4 );
281
+ member_types.Add (&int8type);
282
+ member_types.Add (&int8type);
283
+ member_types.Add (&int8type);
284
+ member_types.Add (&int8type);
285
+ member_types.Add (&int8type);
286
+ member_types.Add (&int8type);
287
+ member_types.Add (&int8type);
288
+ member_types.Add (&int8type);
289
+ const auto & struct_type =
290
+ NativeCompoundType::FromNativeTypes (Z, member_types);
291
+
292
+ auto & arguments = *new (Z) NativeTypes (Z, 1 );
293
+ arguments.Add (&struct_type);
294
+
295
+ RunSignatureTest (Z, " struct8bytesx1" , arguments, struct_type);
296
+ }
297
+
83
298
} // namespace ffi
84
299
} // namespace compiler
85
300
} // namespace dart
0 commit comments