Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
native_calling_convention_test.cc
Go to the documentation of this file.
1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
6
7#include "platform/syslog.h"
9
10namespace dart {
11namespace compiler {
12namespace ffi {
13
15 dart::Zone* zone,
16 const char* name,
17 const NativeFunctionType& native_signature) {
18 const auto& native_calling_convention =
19 NativeCallingConvention::FromSignature(zone, native_signature);
20
21 const char* test_result =
22 native_calling_convention.ToCString(zone, /*multi_line=*/true);
23
24 const int kFilePathLength = 100;
25 char expectation_file_path[kFilePathLength];
26 Utils::SNPrint(expectation_file_path, kFilePathLength,
27 "runtime/vm/compiler/ffi/unit_tests/%s/%s_%s.expect", name,
28 kTargetArchitectureName, kOs);
29
31 Syslog::Print("Updating %s\n", expectation_file_path);
32 WriteToFile(expectation_file_path, test_result);
33 }
34
35 char* expectation_file_contents = nullptr;
36 ReadFromFile(expectation_file_path, &expectation_file_contents);
37 EXPECT_NOTNULL(expectation_file_contents);
38 if (expectation_file_contents != nullptr) {
39 EXPECT_STREQ(expectation_file_contents, test_result);
40 free(expectation_file_contents);
41 }
42
43 return native_calling_convention;
44}
45
47 dart::Zone* zone,
48 const char* name,
49 const NativeTypes& argument_types,
50 const NativeType& return_type) {
51 const auto& native_signature =
52 *new (zone) NativeFunctionType(argument_types, return_type);
53
54 return RunSignatureTest(zone, name, native_signature);
55}
56
57UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_int8x10) {
58 const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
59
60 auto& arguments = *new (Z) NativeTypes(Z, 10);
61 arguments.Add(&int8type);
62 arguments.Add(&int8type);
63 arguments.Add(&int8type);
64 arguments.Add(&int8type);
65 arguments.Add(&int8type);
66 arguments.Add(&int8type);
67 arguments.Add(&int8type);
68 arguments.Add(&int8type);
69 arguments.Add(&int8type);
70 arguments.Add(&int8type);
71
72 RunSignatureTest(Z, "int8x10", arguments, int8type);
73}
74
75UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_floatx20) {
76 const auto& floatType = *new (Z) NativePrimitiveType(kFloat);
77
78 auto& arguments = *new (Z) NativeTypes(Z, 20);
79 arguments.Add(&floatType);
80 arguments.Add(&floatType);
81 arguments.Add(&floatType);
82 arguments.Add(&floatType);
83 arguments.Add(&floatType);
84 arguments.Add(&floatType);
85 arguments.Add(&floatType);
86 arguments.Add(&floatType);
87 arguments.Add(&floatType);
88 arguments.Add(&floatType);
89 arguments.Add(&floatType);
90 arguments.Add(&floatType);
91 arguments.Add(&floatType);
92 arguments.Add(&floatType);
93 arguments.Add(&floatType);
94 arguments.Add(&floatType);
95 arguments.Add(&floatType);
96 arguments.Add(&floatType);
97 arguments.Add(&floatType);
98 arguments.Add(&floatType);
99
100 RunSignatureTest(Z, "floatx20", arguments, floatType);
101}
102
103UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_doublex20) {
104 const auto& doubleType = *new (Z) NativePrimitiveType(kDouble);
105
106 auto& arguments = *new (Z) NativeTypes(Z, 20);
107 arguments.Add(&doubleType);
108 arguments.Add(&doubleType);
109 arguments.Add(&doubleType);
110 arguments.Add(&doubleType);
111 arguments.Add(&doubleType);
112 arguments.Add(&doubleType);
113 arguments.Add(&doubleType);
114 arguments.Add(&doubleType);
115 arguments.Add(&doubleType);
116 arguments.Add(&doubleType);
117 arguments.Add(&doubleType);
118 arguments.Add(&doubleType);
119 arguments.Add(&doubleType);
120 arguments.Add(&doubleType);
121 arguments.Add(&doubleType);
122 arguments.Add(&doubleType);
123 arguments.Add(&doubleType);
124 arguments.Add(&doubleType);
125 arguments.Add(&doubleType);
126 arguments.Add(&doubleType);
127
128 RunSignatureTest(Z, "doublex20", arguments, doubleType);
129}
130
131UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_mixedx20) {
132#if defined(TARGET_ARCH_IS_32_BIT)
133 const auto& intptrType = *new (Z) NativePrimitiveType(kInt32);
134#elif defined(TARGET_ARCH_IS_64_BIT)
135 const auto& intptrType = *new (Z) NativePrimitiveType(kInt64);
136#endif
137 const auto& floatType = *new (Z) NativePrimitiveType(kFloat);
138 const auto& doubleType = *new (Z) NativePrimitiveType(kDouble);
139
140 auto& arguments = *new (Z) NativeTypes(Z, 20);
141 arguments.Add(&intptrType);
142 arguments.Add(&floatType);
143 arguments.Add(&intptrType);
144 arguments.Add(&doubleType);
145 arguments.Add(&intptrType);
146 arguments.Add(&floatType);
147 arguments.Add(&intptrType);
148 arguments.Add(&doubleType);
149 arguments.Add(&intptrType);
150 arguments.Add(&floatType);
151 arguments.Add(&intptrType);
152 arguments.Add(&doubleType);
153 arguments.Add(&intptrType);
154 arguments.Add(&floatType);
155 arguments.Add(&intptrType);
156 arguments.Add(&doubleType);
157 arguments.Add(&intptrType);
158 arguments.Add(&floatType);
159 arguments.Add(&intptrType);
160 arguments.Add(&doubleType);
161
162 RunSignatureTest(Z, "mixedx20", arguments, doubleType);
163}
164
165// Test with 3-byte struct.
166//
167// On ia32, result pointer is passed on stack and passed back in eax.
168//
169// On x64, is passed and returned in registers, except for on Windows where it
170// is passed on stack because of its size not being a power of two.
171//
172// See the *.expect in ./unit_tests for this behavior.
173UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct3bytesx10) {
174 const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
175
176 auto& member_types = *new (Z) NativeTypes(Z, 3);
177 member_types.Add(&int8type);
178 member_types.Add(&int8type);
179 member_types.Add(&int8type);
180 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
181
182 auto& arguments = *new (Z) NativeTypes(Z, 10);
183 arguments.Add(&struct_type);
184 arguments.Add(&struct_type);
185 arguments.Add(&struct_type);
186 arguments.Add(&struct_type);
187 arguments.Add(&struct_type);
188 arguments.Add(&struct_type);
189 arguments.Add(&struct_type);
190 arguments.Add(&struct_type);
191 arguments.Add(&struct_type);
192 arguments.Add(&struct_type);
193
194 RunSignatureTest(Z, "struct3bytesx10", arguments, struct_type);
195}
196
197// Test with homogenous struct.
198//
199// On arm softfp, the return pointer is passed in the first int register, and
200// the first struct is passed in the next 3 registers and 1 stack slot.
201//
202// On arm hardfp, arm64, and x64 non-Windows the structs are passed in FPU
203// registers until exhausted, the rest is passed on the stack, and struct is
204// returned in FPU registers.
205//
206// On ia32 a return pointer and all arguments are passed on the stack.
207//
208// On x64 on Windows the structs are passed by pointer and pointer to the
209// return value is passed in.
210//
211// See the *.expect in ./unit_tests for this behavior.
212UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesHomogenousx10) {
213 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
214 const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
215
216 // If passed in FPU registers, uses an even amount of them.
217 auto& member_types = *new (Z) NativeTypes(Z, 4);
218 member_types.Add(&float_type);
219 member_types.Add(&float_type);
220 member_types.Add(&float_type);
221 member_types.Add(&float_type);
222 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
223
224 auto& arguments = *new (Z) NativeTypes(Z, 13);
225 arguments.Add(&struct_type);
226 arguments.Add(&float_type); // Claim a single FPU register.
227 arguments.Add(&struct_type);
228 arguments.Add(&struct_type);
229 arguments.Add(&struct_type);
230 arguments.Add(&struct_type);
231 arguments.Add(&struct_type);
232 arguments.Add(&struct_type);
233 arguments.Add(&struct_type);
234 arguments.Add(&struct_type);
235 arguments.Add(&float_type); // Check float register back filling, if any.
236 arguments.Add(&int8type); // Check integer register back filling, if any.
237 arguments.Add(&struct_type); // Check stack alignment of struct.
238
239 RunSignatureTest(Z, "struct16bytesHomogenousx10", arguments, struct_type);
240}
241
242// Test with homogenous struct (2).
243//
244// This time with nested structs and inline arrays.
245//
246// See the *.expect in ./unit_tests for this behavior.
247UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesHomogenousx10_2) {
248 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
249 const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
250
251 const auto& float_1_array_type = *new (Z) NativeArrayType(float_type, 1);
252
253 const auto& float_2_array_type = *new (Z) NativeArrayType(float_type, 2);
254 auto& full_float_member_types = *new (Z) NativeTypes(Z, 1);
255 full_float_member_types.Add(&float_2_array_type);
256 const auto& float_array_struct_type =
257 NativeStructType::FromNativeTypes(Z, full_float_member_types);
258
259 auto& member_types = *new (Z) NativeTypes(Z, 3);
260 member_types.Add(&float_1_array_type);
261 member_types.Add(&float_array_struct_type);
262 member_types.Add(&float_type);
263 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
264
265 auto& arguments = *new (Z) NativeTypes(Z, 13);
266 arguments.Add(&struct_type);
267 arguments.Add(&float_type); // Claim a single FPU register.
268 arguments.Add(&struct_type);
269 arguments.Add(&struct_type);
270 arguments.Add(&struct_type);
271 arguments.Add(&struct_type);
272 arguments.Add(&struct_type);
273 arguments.Add(&struct_type);
274 arguments.Add(&struct_type);
275 arguments.Add(&struct_type);
276 arguments.Add(&float_type); // Check float register back filling, if any.
277 arguments.Add(&int8type); // Check integer register back filling, if any.
278 arguments.Add(&struct_type); // Check stack alignment of struct.
279
280 // Identical expectation files as previous test, struct contains the same
281 // members, but nested in arrays and nested structs.
282 RunSignatureTest(Z, "struct16bytesHomogenousx10", arguments, struct_type);
283}
284
285// Test with homogenous union.
286//
287// Even though the number of floats nested is different, this is still laid
288// out as a homogeneous aggregate in arm64 and arm hardfp.
289//
290// Even though the member sizes are different, these unions are still passed in
291// xmm registers on Linux/MacOS x64.
292//
293// See the *.expect in ./unit_tests for this behavior.
294UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_union16bytesHomogenousx10) {
295 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
296 const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
297
298 const auto& float_array_type = *new (Z) NativeArrayType(float_type, 3);
299
300 auto& struct_member_types = *new (Z) NativeTypes(Z, 4);
301 struct_member_types.Add(&float_type);
302 struct_member_types.Add(&float_type);
303 struct_member_types.Add(&float_type);
304 struct_member_types.Add(&float_type);
305 const auto& struct_type =
306 NativeStructType::FromNativeTypes(Z, struct_member_types);
307
308 auto& member_types = *new (Z) NativeTypes(Z, 2);
309 member_types.Add(&float_array_type);
310 member_types.Add(&struct_type);
311 const auto& union_type = NativeUnionType::FromNativeTypes(Z, member_types);
312
313 EXPECT_EQ(16, union_type.SizeInBytes());
314 EXPECT(union_type.ContainsHomogeneousFloats());
315
316 auto& arguments = *new (Z) NativeTypes(Z, 13);
317 arguments.Add(&union_type);
318 arguments.Add(&union_type);
319 arguments.Add(&union_type);
320 arguments.Add(&union_type);
321 arguments.Add(&union_type);
322 arguments.Add(&union_type);
323 arguments.Add(&union_type);
324 arguments.Add(&union_type);
325 arguments.Add(&union_type);
326 arguments.Add(&int8type); // Check integer register back filling, if any.
327 arguments.Add(&union_type); // Check stack alignment of struct.
328
329 // Identical expectation files as previous test, struct contains the same
330 // members, but nested in arrays and nested structs.
331 RunSignatureTest(Z, "union16bytesHomogenousx10", arguments, union_type);
332}
333
334// A fairly big struct.
335//
336// On arm, split up in 8-byte chunks. The first chunk goes into two registers,
337// the rest on the stack. Note that r1 goes unused and is not backfilled.
338//
339// On arm64 and Windows x64 passed by a pointer to copy.
340//
341// On ia32, wholly passed on stack.
342//
343// On non-Windows x64, wholly passed on stack, and the integer argument
344// backfills a still unoccupied integer register.
345//
346// See the *.expect in ./unit_tests for this behavior.
347UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct128bytesx1) {
348 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
349 const auto& int64_type = *new (Z) NativePrimitiveType(kInt64);
350
351 auto& member_types = *new (Z) NativeTypes(Z, 16);
352 member_types.Add(&int64_type);
353 member_types.Add(&int64_type);
354 member_types.Add(&int64_type);
355 member_types.Add(&int64_type);
356 member_types.Add(&int64_type);
357 member_types.Add(&int64_type);
358 member_types.Add(&int64_type);
359 member_types.Add(&int64_type);
360 member_types.Add(&int64_type);
361 member_types.Add(&int64_type);
362 member_types.Add(&int64_type);
363 member_types.Add(&int64_type);
364 member_types.Add(&int64_type);
365 member_types.Add(&int64_type);
366 member_types.Add(&int64_type);
367 member_types.Add(&int64_type);
368 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
369
370 auto& arguments = *new (Z) NativeTypes(Z, 2);
371 arguments.Add(&struct_type);
372 arguments.Add(&int32_type); // Check integer register backfilling, if any.
373
374 RunSignatureTest(Z, "struct128bytesx1", arguments, struct_type);
375}
376
377#if defined(TARGET_ARCH_X64)
378// On x64 non-Windows a struct can be spread over an FPU and int register.
379//
380// See the *.expect in ./unit_tests for this behavior.
381UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10) {
382 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
383 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
384
385 auto& member_types = *new (Z) NativeTypes(Z, 4);
386 member_types.Add(&float_type);
387 member_types.Add(&float_type);
388 member_types.Add(&int32_type);
389 member_types.Add(&int32_type);
390 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
391
392 auto& arguments = *new (Z) NativeTypes(Z, 11);
393 arguments.Add(&struct_type);
394 arguments.Add(&struct_type);
395 arguments.Add(&struct_type);
396 arguments.Add(&struct_type);
397 arguments.Add(&struct_type);
398 arguments.Add(&struct_type);
399 arguments.Add(&struct_type); // Integer registers exhausted, on stack.
400 arguments.Add(&struct_type);
401 arguments.Add(&struct_type);
402 arguments.Add(&struct_type);
403 arguments.Add(&float_type); // Use remaining FPU register.
404
405 RunSignatureTest(Z, "struct16bytesMixedx10", arguments, struct_type);
406}
407
408// On x64 non-Windows a struct can be spread over an FPU and int register.
409//
410// See the *.expect in ./unit_tests for this behavior.
411UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10_2) {
412 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
413 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
414
415 auto& member_types = *new (Z) NativeTypes(Z, 4);
416 member_types.Add(&float_type);
417 member_types.Add(&float_type);
418 member_types.Add(&int32_type);
419 member_types.Add(&int32_type);
420 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
421
422 auto& arguments = *new (Z) NativeTypes(Z, 15);
423 arguments.Add(&float_type);
424 arguments.Add(&float_type);
425 arguments.Add(&float_type);
426 arguments.Add(&float_type);
427 arguments.Add(&struct_type);
428 arguments.Add(&struct_type);
429 arguments.Add(&struct_type);
430 arguments.Add(&struct_type);
431 arguments.Add(&struct_type); // FPU registers exhausted, on stack.
432 arguments.Add(&struct_type);
433 arguments.Add(&struct_type);
434 arguments.Add(&struct_type);
435 arguments.Add(&struct_type);
436 arguments.Add(&struct_type);
437 arguments.Add(&int32_type); // Use remaining integer register.
438
439 RunSignatureTest(Z, "struct16bytesMixedx10_2", arguments, struct_type);
440}
441
442// On x64 non-Windows a struct can be spread over an FPU and int register.
443//
444// This behavior also happens with nested structs and inline arrays.
445//
446// typedef struct {
447// int32_t a0;
448// float a1;
449// } HalfFloat;
450//
451// typedef struct {
452// float a1[1];
453// } FullFloat;
454//
455// typedef struct {
456// int32_t a0;
457// HalfFloat a1;
458// FullFloat a2;
459// } HalfFloat2;
460//
461// See the *.expect in ./unit_tests for this behavior.
462UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10_3) {
463 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
464 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
465
466 auto& half_float_member_types = *new (Z) NativeTypes(Z, 2);
467 half_float_member_types.Add(&int32_type);
468 half_float_member_types.Add(&float_type);
469 const auto& half_float_type =
470 NativeStructType::FromNativeTypes(Z, half_float_member_types);
471
472 const auto& float_array_type = *new (Z) NativeArrayType(float_type, 1);
473 auto& full_float_member_types = *new (Z) NativeTypes(Z, 1);
474 full_float_member_types.Add(&float_array_type);
475 const auto& full_float_type =
476 NativeStructType::FromNativeTypes(Z, full_float_member_types);
477
478 auto& member_types = *new (Z) NativeTypes(Z, 3);
479 member_types.Add(&int32_type);
480 member_types.Add(&half_float_type);
481 member_types.Add(&full_float_type);
482 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
483
484 auto& arguments = *new (Z) NativeTypes(Z, 11);
485 arguments.Add(&struct_type);
486 arguments.Add(&struct_type);
487 arguments.Add(&struct_type);
488 arguments.Add(&struct_type);
489 arguments.Add(&struct_type);
490 arguments.Add(&struct_type);
491 arguments.Add(&struct_type); // Integer registers exhausted, on stack.
492 arguments.Add(&struct_type);
493 arguments.Add(&struct_type);
494 arguments.Add(&struct_type);
495 arguments.Add(&float_type); // Use remaining FPU register.
496
497 RunSignatureTest(Z, "struct16bytesMixedx10_3", arguments, struct_type);
498}
499#endif // defined(TARGET_ARCH_X64)
500
501// On ia32 Windows a struct can be returned in registers, on non-Windows not.
502//
503// See the *.expect in ./unit_tests for this behavior.
504UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct8bytesx1) {
505 const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
506
507 auto& member_types = *new (Z) NativeTypes(Z, 4);
508 member_types.Add(&int8type);
509 member_types.Add(&int8type);
510 member_types.Add(&int8type);
511 member_types.Add(&int8type);
512 member_types.Add(&int8type);
513 member_types.Add(&int8type);
514 member_types.Add(&int8type);
515 member_types.Add(&int8type);
516 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
517
518 auto& arguments = *new (Z) NativeTypes(Z, 1);
519 arguments.Add(&struct_type);
520
521 RunSignatureTest(Z, "struct8bytesx1", arguments, struct_type);
522}
523
524// The struct is only 8 bytes with packing enabled.
525//
526// Many calling conventions pass this struct in single registers or less
527// stack slots because of this.
528//
529// Non-windows x64 passes this struct on the stack instead of in a single
530// CPU register, because it contains a mis-aligned member.
531//
532// See the *.expect in ./unit_tests for this behavior.
533UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct8bytesPackedx10) {
534 const auto& int8_type = *new (Z) NativePrimitiveType(kInt8);
535 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
536
537 auto& member_types = *new (Z) NativeTypes(Z, 5);
538 member_types.Add(&int8_type);
539 member_types.Add(&int32_type);
540 member_types.Add(&int8_type);
541 member_types.Add(&int8_type);
542 member_types.Add(&int8_type);
543 const auto& struct_type =
544 NativeStructType::FromNativeTypes(Z, member_types, /*packing=*/1);
545 EXPECT_EQ(8, struct_type.SizeInBytes());
546 EXPECT(struct_type.ContainsUnalignedMembers());
547
548 auto& arguments = *new (Z) NativeTypes(Z, 10);
549 arguments.Add(&struct_type);
550 arguments.Add(&struct_type);
551 arguments.Add(&struct_type);
552 arguments.Add(&struct_type);
553 arguments.Add(&struct_type);
554 arguments.Add(&struct_type);
555 arguments.Add(&struct_type);
556 arguments.Add(&struct_type);
557 arguments.Add(&struct_type);
558 arguments.Add(&struct_type);
559
560 RunSignatureTest(Z, "struct8bytesPackedx10", arguments, struct_type);
561}
562
563// Without packing, this would be a 16 byte struct. However, because of packing
564// it's 9 bytes.
565//
566// #pragma pack(push,1)
567// typedef struct {
568// int8_t a0;
569// double a1
570// } StructPacked;
571// #pragma pack(pop)
572//
573// See the *.expect in ./unit_tests for this behavior.
574UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_structPacked) {
575 const auto& int8_type = *new (Z) NativePrimitiveType(kInt8);
576 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
577 const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
578
579 auto& member_types = *new (Z) NativeTypes(Z, 2);
580 member_types.Add(&int8_type);
581 member_types.Add(&double_type);
582 const auto& struct_type =
583 NativeStructType::FromNativeTypes(Z, member_types, /*packing=*/1);
584 EXPECT_EQ(9, struct_type.SizeInBytes());
585 EXPECT(struct_type.ContainsUnalignedMembers());
586
587 auto& arguments = *new (Z) NativeTypes(Z, 13);
588 arguments.Add(&struct_type);
589 arguments.Add(&struct_type);
590 arguments.Add(&struct_type);
591 arguments.Add(&struct_type);
592 arguments.Add(&struct_type);
593 arguments.Add(&struct_type);
594 arguments.Add(&struct_type);
595 arguments.Add(&struct_type);
596 arguments.Add(&struct_type);
597 arguments.Add(&struct_type);
598 arguments.Add(&double_type); // Backfilling float registers.
599 arguments.Add(&int32_type); // Backfilling int registers.
600 arguments.Add(&int32_type); // Backfilling int registers.
601
602 RunSignatureTest(Z, "structPacked", arguments, double_type);
603}
604
605// The union is only 5 bytes because it's members are packed.
606//
607// Many calling conventions pass this struct in single registers or less
608// stack slots because of this.
609//
610// Non-windows x64 passes this struct on the stack instead of in a single
611// CPU register, because it contains a mis-aligned member.
612//
613// See the *.expect in ./unit_tests for this behavior.
614UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_union5bytesPackedx10) {
615 const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
616 const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
617
618 auto& inner_members = *new (Z) NativeTypes(Z, 2);
619 inner_members.Add(&uint8_type);
620 inner_members.Add(&uint32_type);
621 const intptr_t packing = 1;
622 const auto& struct_type =
623 NativeStructType::FromNativeTypes(Z, inner_members, packing);
624
625 const auto& array_type = *new (Z) NativeArrayType(uint8_type, 5);
626
627 auto& member_types = *new (Z) NativeTypes(Z, 2);
628 member_types.Add(&array_type);
629 member_types.Add(&struct_type);
630 const auto& union_type = NativeUnionType::FromNativeTypes(Z, member_types);
631
632 EXPECT_EQ(5, union_type.SizeInBytes());
633 EXPECT_EQ(1, union_type.AlignmentInBytesField());
634
635 auto& arguments = *new (Z) NativeTypes(Z, 10);
636 arguments.Add(&union_type);
637 arguments.Add(&union_type);
638 arguments.Add(&union_type);
639 arguments.Add(&union_type);
640 arguments.Add(&union_type);
641 arguments.Add(&union_type);
642 arguments.Add(&union_type);
643 arguments.Add(&union_type);
644 arguments.Add(&union_type);
645 arguments.Add(&union_type);
646
647 RunSignatureTest(Z, "union5bytesPackedx10", arguments, union_type);
648}
649
650// http://dartbug.com/46127
651//
652// See the *.expect in ./unit_tests for this behavior.
653UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress46127) {
654 const auto& uint64_type = *new (Z) NativePrimitiveType(kUint64);
655
656 auto& member_types = *new (Z) NativeTypes(Z, 1);
657 member_types.Add(&uint64_type);
658 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
659
660 EXPECT_EQ(8, struct_type.SizeInBytes());
661
662 auto& arguments = *new (Z) NativeTypes(Z, 0);
663
664 const auto& native_calling_convention =
665 RunSignatureTest(Z, "regress46127", arguments, struct_type);
666
667#if defined(TARGET_ARCH_IA32) && \
668 (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX))
669 // We must count the result pointer passed on the stack as well.
670 EXPECT_EQ(4, native_calling_convention.StackTopInBytes());
671#else
672 EXPECT_EQ(0, native_calling_convention.StackTopInBytes());
673#endif
674}
675
676// MacOS arm64 alignment of 12-byte homogenous float structs.
677//
678// http://dartbug.com/46305
679//
680// See the *.expect in ./unit_tests for this behavior.
681UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct12bytesFloatx6) {
682 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
683 const auto& int64_type = *new (Z) NativePrimitiveType(kInt64);
684
685 auto& member_types = *new (Z) NativeTypes(Z, 3);
686 member_types.Add(&float_type);
687 member_types.Add(&float_type);
688 member_types.Add(&float_type);
689 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
690
691#if defined(TARGET_ARCH_ARM64) && \
692 (defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS))
693 EXPECT_EQ(4, struct_type.AlignmentInBytesStack());
694#endif
695
696 auto& arguments = *new (Z) NativeTypes(Z, 6);
697 arguments.Add(&struct_type);
698 arguments.Add(&struct_type);
699 arguments.Add(&struct_type);
700 arguments.Add(&struct_type);
701 arguments.Add(&struct_type);
702 arguments.Add(&struct_type);
703
704 RunSignatureTest(Z, "struct12bytesFloatx6", arguments, int64_type);
705}
706
707// typedef void (*YogaDartMeasureFunc)(intptr_t node_id,
708// double available_width,
709// int32_t width_mode,
710// double available_height,
711// int32_t height_mode,
712// double *measured_width,
713// double *measured_height);
714// https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=105336
715//
716// See the *.expect in ./unit_tests for this behavior.
717UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress_fuchsia105336) {
718#if defined(TARGET_ARCH_IS_32_BIT)
719 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt32);
720#elif defined(TARGET_ARCH_IS_64_BIT)
721 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt64);
722#endif
723 const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
724 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
725 const auto& void_type = *new (Z) NativePrimitiveType(kVoid);
726
727 auto& arguments = *new (Z) NativeTypes(Z, 6);
728 arguments.Add(&intptr_type);
729 arguments.Add(&double_type);
730 arguments.Add(&int32_type);
731 arguments.Add(&double_type);
732 arguments.Add(&int32_type);
733 arguments.Add(&intptr_type); // pointer
734 arguments.Add(&intptr_type); // pointer
735
736 RunSignatureTest(Z, "regress_fuchsia105336", arguments, void_type);
737}
738
739// Binding in Dart with variadic arguments:
740// `IntPtr Function(IntPtr, VarArgs<(IntPtr, IntPtr, IntPtr, IntPtr)>)`
741//
742// See the *.expect in ./unit_tests for this behavior.
743UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_variadic_int) {
744#if defined(TARGET_ARCH_IS_32_BIT)
745 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt32);
746#elif defined(TARGET_ARCH_IS_64_BIT)
747 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt64);
748#endif
749
750 auto& arguments = *new (Z) NativeTypes(Z, 5);
751 arguments.Add(&intptr_type);
752 arguments.Add(&intptr_type);
753 arguments.Add(&intptr_type);
754 arguments.Add(&intptr_type);
755 arguments.Add(&intptr_type);
756
757 const auto& native_signature = *new (Z) NativeFunctionType(
758 arguments, intptr_type, /*variadic_arguments_index=*/1);
759
760 RunSignatureTest(Z, "variadic_int", native_signature);
761}
762
763// Binding in Dart with variadic arguments:
764// `Double Function(Double, VarArgs<(Double, Double, Double, Double)>)`
765//
766// See the *.expect in ./unit_tests for this behavior.
767UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_variadic_double) {
768 const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
769
770 auto& arguments = *new (Z) NativeTypes(Z, 5);
771 arguments.Add(&double_type);
772 arguments.Add(&double_type);
773 arguments.Add(&double_type);
774 arguments.Add(&double_type);
775 arguments.Add(&double_type);
776
777 const auto& native_signature = *new (Z) NativeFunctionType(
778 arguments, double_type, /*variadic_arguments_index=*/1);
779
780 RunSignatureTest(Z, "variadic_double", native_signature);
781}
782
783// Binding in Dart with variadic arguments:
784// `Double Function(Double, VarArgs<(Struct20BytesHomogeneousFloat, Double)>)`
785//
786// See the *.expect in ./unit_tests for this behavior.
787UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_variadic_with_struct) {
788 const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
789 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
790
791 auto& member_types = *new (Z) NativeTypes(Z, 5);
792 member_types.Add(&float_type);
793 member_types.Add(&float_type);
794 member_types.Add(&float_type);
795 member_types.Add(&float_type);
796 member_types.Add(&float_type);
797 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
798
799 auto& arguments = *new (Z) NativeTypes(Z, 3);
800 arguments.Add(&double_type);
801 arguments.Add(&struct_type);
802 arguments.Add(&double_type);
803
804 const auto& native_signature = *new (Z) NativeFunctionType(
805 arguments, double_type, /*variadic_arguments_index=*/1);
806
807 RunSignatureTest(Z, "variadic_with_struct", native_signature);
808}
809
810// Binding in Dart with variadic arguments.
811//
812// Especially macos_arm64 is interesting due to stack alignment.
813//
814// See the *.expect in ./unit_tests for this behavior.
816 NativeCallingConvention_variadic_with_homogenous_struct) {
817 const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
818 const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
819 const auto& int64_type = *new (Z) NativePrimitiveType(kInt64);
820 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
821
822 auto& member_types = *new (Z) NativeTypes(Z, 3);
823 member_types.Add(&float_type);
824 member_types.Add(&float_type);
825 member_types.Add(&float_type);
826 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
827
828 auto& arguments = *new (Z) NativeTypes(Z, 13);
829 arguments.Add(&double_type);
830 arguments.Add(&double_type);
831 arguments.Add(&double_type);
832 arguments.Add(&double_type);
833 arguments.Add(&double_type);
834 arguments.Add(&double_type);
835 arguments.Add(&double_type);
836 arguments.Add(&double_type); // Exhaust FPU registers
837 arguments.Add(&float_type); // Misalign stack.
838 arguments.Add(
839 &struct_type); // Homogenous struct, not aligned to wordsize on stack.
840 arguments.Add(&int64_type); // Start varargs.
841 arguments.Add(&int32_type); // Misalign stack again.
842 arguments.Add(
843 &struct_type); // Homogenous struct, aligned to wordsize on stack.
844
845 const auto& native_signature = *new (Z) NativeFunctionType(
846 arguments, double_type, /*variadic_arguments_index=*/11);
847
848 RunSignatureTest(Z, "variadic_with_homogenous_struct", native_signature);
849}
850
851// Binding in Dart with variadic arguments.
852//
853// Especially linux_riscv32 is interesting due to register alignment.
854//
855// See the *.expect in ./unit_tests for this behavior.
856UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_variadic_register_alignment) {
857 const auto& double_type = *new (Z) NativePrimitiveType(kDouble);
858
859 auto& member_types = *new (Z) NativeTypes(Z, 4);
860 member_types.Add(&double_type);
861 member_types.Add(&double_type);
862 member_types.Add(&double_type);
863 member_types.Add(&double_type);
864 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
865
866 auto& arguments = *new (Z) NativeTypes(Z, 13);
867 arguments.Add(&double_type);
868 arguments.Add(&double_type); // Passed in int register pair on RISC-V 32.
869 arguments.Add(
870 &struct_type); // Passed using single integer register on RISC-V 32.
871 arguments.Add(
872 &double_type); // Passed in _aligned_ int register pair on RISC-V 32.
873
874 const auto& native_signature = *new (Z) NativeFunctionType(
875 arguments, double_type, /*variadic_arguments_index=*/1);
876
877 RunSignatureTest(Z, "variadic_register_alignment", native_signature);
878}
879
880// Variadic function in C:
881// `int ioctl(int, unsigned long, ...)`
882//
883// Binding in Dart with single variadic argument:
884// `Int32 Function(Int32, Int64, VarArgs<(Pointer<Void>,)>)`
885//
886// https://github.com/dart-lang/sdk/issues/49460
887//
888// See the *.expect in ./unit_tests for this behavior.
889UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress49460) {
890 const auto& int32_type = *new (Z) NativePrimitiveType(kInt32);
891 const auto& int64_type = *new (Z) NativePrimitiveType(kInt64);
892#if defined(TARGET_ARCH_IS_32_BIT)
893 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt32);
894#elif defined(TARGET_ARCH_IS_64_BIT)
895 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt64);
896#endif
897
898 auto& arguments = *new (Z) NativeTypes(Z, 3);
899 arguments.Add(&int32_type);
900 arguments.Add(&int64_type);
901 arguments.Add(&intptr_type); // pointer
902
903 const auto& native_signature = *new (Z) NativeFunctionType(
904 arguments, int32_type, /*variadic_arguments_index=*/2);
905
906 RunSignatureTest(Z, "regress49460", native_signature);
907}
908
909// Struct parameter that potentially is partially allocated to a register and
910// partially to the stack. Mainly interesting on ARM64 and RISC-V.
911//
912// See the *.expect in ./unit_tests for this behavior.
913UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_stradle_last_register) {
914#if defined(TARGET_ARCH_IS_32_BIT)
915 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt32);
916 const auto& halfptr_type = *new (Z) NativePrimitiveType(kInt16);
917#elif defined(TARGET_ARCH_IS_64_BIT)
918 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt64);
919 const auto& halfptr_type = *new (Z) NativePrimitiveType(kInt32);
920#endif
921
922 auto& member_types = *new (Z) NativeTypes(Z, 3);
923 member_types.Add(&halfptr_type);
924 member_types.Add(&halfptr_type);
925 member_types.Add(&halfptr_type);
926 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
927
928 auto& arguments = *new (Z) NativeTypes(Z, CallingConventions::kNumArgRegs);
929 for (intptr_t i = 1; i < CallingConventions::kNumArgRegs; i++) {
930 arguments.Add(&intptr_type);
931 }
932 arguments.Add(&struct_type);
933
934 const auto& native_signature =
935 *new (Z) NativeFunctionType(arguments, intptr_type);
936
937 RunSignatureTest(Z, "stradle_last_register", native_signature);
938}
939
940// Struct parameter that potentially is partially allocated to a register and
941// partially to the stack. Mainly interesting on ARM64 and RISC-V.
942//
943// See the *.expect in ./unit_tests for this behavior.
945 NativeCallingConvention_variadic_stradle_last_register) {
946#if defined(TARGET_ARCH_IS_32_BIT)
947 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt32);
948 const auto& halfptr_type = *new (Z) NativePrimitiveType(kInt16);
949#elif defined(TARGET_ARCH_IS_64_BIT)
950 const auto& intptr_type = *new (Z) NativePrimitiveType(kInt64);
951 const auto& halfptr_type = *new (Z) NativePrimitiveType(kInt32);
952#endif
953
954 auto& member_types = *new (Z) NativeTypes(Z, 3);
955 member_types.Add(&halfptr_type);
956 member_types.Add(&halfptr_type);
957 member_types.Add(&halfptr_type);
958 const auto& struct_type = NativeStructType::FromNativeTypes(Z, member_types);
959
960 auto& arguments = *new (Z) NativeTypes(Z, CallingConventions::kNumArgRegs);
961 for (intptr_t i = 1; i < CallingConventions::kNumArgRegs; i++) {
962 arguments.Add(&intptr_type);
963 }
964 arguments.Add(&struct_type);
965
966 const auto& native_signature = *new (Z) NativeFunctionType(
967 arguments, intptr_type, /*variadic_arguments_index=*/1);
968
969 RunSignatureTest(Z, "variadic_stradle_last_register", native_signature);
970}
971
972// Struct parameter that potentially is partially allocated to a register and
973// partially to the stack. Mainly interesting on ARM64 and RISC-V.
974//
975// See the *.expect in ./unit_tests for this behavior.
976UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_variadic_less_than_word) {
977#if defined(TARGET_ARCH_IS_32_BIT)
978 const auto& halfptr_type = *new (Z) NativePrimitiveType(kInt16);
979#elif defined(TARGET_ARCH_IS_64_BIT)
980 const auto& halfptr_type = *new (Z) NativePrimitiveType(kInt32);
981#endif
982
983 auto& arguments = *new (Z) NativeTypes(Z, 12);
984 for (intptr_t i = 0; i < 12; i++) {
985 arguments.Add(&halfptr_type);
986 }
987
988 const auto& native_signature = *new (Z) NativeFunctionType(
989 arguments, halfptr_type, /*variadic_arguments_index=*/1);
990
991 RunSignatureTest(Z, "variadic_less_than_word", native_signature);
992}
993
994} // namespace ffi
995} // namespace compiler
996} // namespace dart
#define EXPECT(type, expectedAlignment, expectedSize)
#define Z
static constexpr intptr_t kNumArgRegs
static void Print(const char *format,...) PRINTF_ATTRIBUTE(1
static int SNPrint(char *str, size_t size, const char *format,...) PRINTF_ATTRIBUTE(3
static const NativeCallingConvention & FromSignature(Zone *zone, const NativeFunctionType &signature)
static NativeStructType & FromNativeTypes(Zone *zone, const NativeTypes &members, intptr_t member_packing=kMaxInt32)
static NativeUnionType & FromNativeTypes(Zone *zone, const NativeTypes &members)
#define UNIT_TEST_CASE_WITH_ZONE(name)
Definition unit_test.h:35
void WriteToFile(char *path, const char *contents)
Definition unit_test.cc:20
const NativeCallingConvention & RunSignatureTest(dart::Zone *zone, const char *name, const NativeFunctionType &native_signature)
ZoneGrowableArray< const NativeType * > NativeTypes
void ReadFromFile(char *path, char **buffer_pointer)
Definition unit_test.cc:31
const char * kOs
Definition unit_test.cc:17
const char *const name