From 4d5055805fe7b8bf9bc2c0fb5b2cf23f4272e984 Mon Sep 17 00:00:00 2001 From: Clement Skau Date: Fri, 21 May 2021 11:12:02 +0000 Subject: [PATCH] [VM/FFI] Adds FFI leaf calls. This CL adds FFI leaf calls by adding `lookupFunction(.., isLeaf)` and `_asFunctionInternal(.., isLeaf)`, which generate FFI leaf calls. These calls skip a lot of the usual frame building and generated <-> native transition overhead. `benchmark/FfiCall/` shows a 1.1x - 4.3x speed-up between the regular FFI calls and their leaf call counterparts (JIT, x64, release). TEST=Adds `tests/ffi{,_2}/vmspecific_leaf_call_test.dart`. Tested FFI tests. Closes: https://github.com/dart-lang/sdk/issues/36707 Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,vm-ffi-android-release-arm64-try,vm-ffi-android-release-arm-try,vm-ffi-android-product-arm64-try,vm-ffi-android-product-arm-try,vm-ffi-android-debug-arm64-try,vm-ffi-android-debug-arm-try,vm-kernel-linux-debug-ia32-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-kernel-precomp-nnbd-mac-release-simarm64-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-linux-release-simarm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-precomp-ubsan-linux-release-x64-try,vm-kernel-precomp-tsan-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try Bug: https://github.com/dart-lang/sdk/issues/36707 Change-Id: Id8824f36b0006bf09951207bd004356fe6e9f46e Cq-Do-Not-Cancel-Tryjobs: true Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179768 Commit-Queue: Clement Skau Reviewed-by: Daco Harkes Reviewed-by: Martin Kustermann --- benchmarks/FfiCall/dart/FfiCall.dart | 388 +- benchmarks/FfiCall/dart2/FfiCall.dart | 388 +- .../lib/src/messages/codes_generated.dart | 39 + pkg/analyzer/lib/error/error.dart | 3 + pkg/analyzer/lib/src/dart/error/ffi_code.dart | 25 + .../lib/src/generated/ffi_verifier.dart | 78 + .../lib/src/test_utilities/mock_sdk.dart | 10 +- .../argument_must_be_a_constant_test.dart | 78 + ...fi_leaf_call_must_not_use_handle_test.dart | 77 + .../test/src/diagnostics/test_all.dart | 5 + pkg/front_end/lib/src/api_unstable/vm.dart | 3 + pkg/front_end/messages.status | 3 + pkg/front_end/messages.yaml | 15 + pkg/vm/lib/transformations/ffi.dart | 2 + pkg/vm/lib/transformations/ffi_use_sites.dart | 103 +- .../ffi_struct_constructors.dart.expect | 8 +- .../ffi_test/ffi_test_functions_vmspecific.cc | 36 + runtime/lib/ffi.cc | 2 +- runtime/vm/bootstrap_natives.h | 2 +- runtime/vm/compiler/backend/il.cc | 48 +- runtime/vm/compiler/backend/il.h | 23 +- runtime/vm/compiler/backend/il_arm.cc | 151 +- runtime/vm/compiler/backend/il_arm64.cc | 146 +- runtime/vm/compiler/backend/il_ia32.cc | 130 +- runtime/vm/compiler/backend/il_x64.cc | 164 +- runtime/vm/compiler/ffi/call.cc | 5 +- runtime/vm/compiler/ffi/call.h | 3 +- .../frontend/base_flow_graph_builder.cc | 6 +- .../frontend/base_flow_graph_builder.h | 3 +- .../frontend/kernel_binary_flowgraph.cc | 32 +- runtime/vm/compiler/frontend/kernel_to_il.cc | 3 +- runtime/vm/compiler/recognized_methods_list.h | 2 +- .../vm/compiler/runtime_offsets_extracted.h | 12 +- runtime/vm/native_api_impl.cc | 6 + runtime/vm/object.cc | 19 + runtime/vm/object.h | 9 + runtime/vm/raw_object.h | 3 + samples/ffi/sample_ffi_functions.dart | 10 + samples_2/ffi/sample_ffi_functions.dart | 10 + .../vm/lib/ffi_dynamic_library_patch.dart | 4 +- sdk/lib/_internal/vm/lib/ffi_patch.dart | 5 +- sdk/lib/ffi/dynamic_library.dart | 3 +- sdk/lib/ffi/ffi.dart | 3 +- tests/ffi/callback_tests_utils.dart | 14 +- ...ction_structs_by_value_generated_test.dart | 7651 +++++++++++++++++ tests/ffi/function_structs_test.dart | 26 +- .../structs_by_value_tests_generator.dart | 16 +- tests/ffi/vmspecific_leaf_call_test.dart | 61 + tests/ffi/vmspecific_static_checks_test.dart | 44 + tests/ffi_2/callback_tests_utils.dart | 14 +- ...ction_structs_by_value_generated_test.dart | 7651 +++++++++++++++++ tests/ffi_2/function_structs_test.dart | 23 +- .../structs_by_value_tests_generator.dart | 16 +- tests/ffi_2/vmspecific_leaf_call_test.dart | 60 + .../ffi_2/vmspecific_static_checks_test.dart | 44 + 55 files changed, 17073 insertions(+), 612 deletions(-) create mode 100644 pkg/analyzer/test/src/diagnostics/argument_must_be_a_constant_test.dart create mode 100644 pkg/analyzer/test/src/diagnostics/ffi_leaf_call_must_not_use_handle_test.dart create mode 100644 tests/ffi/vmspecific_leaf_call_test.dart create mode 100644 tests/ffi_2/vmspecific_leaf_call_test.dart diff --git a/benchmarks/FfiCall/dart/FfiCall.dart b/benchmarks/FfiCall/dart/FfiCall.dart index 7886ecfa59f..e6c922e6291 100644 --- a/benchmarks/FfiCall/dart/FfiCall.dart +++ b/benchmarks/FfiCall/dart/FfiCall.dart @@ -283,7 +283,10 @@ typedef Function20Object = Object Function( // abstract class FfiBenchmarkBase extends BenchmarkBase { - FfiBenchmarkBase(String name) : super(name); + final bool isLeaf; + + FfiBenchmarkBase(String name, {this.isLeaf: false}) + : super('$name${isLeaf ? 'Leaf' : ''}'); void expectEquals(actual, expected) { if (actual != expected) { @@ -307,10 +310,13 @@ abstract class FfiBenchmarkBase extends BenchmarkBase { class Uint8x01 extends FfiBenchmarkBase { final Function1int f; - Uint8x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Uint8'), - super('FfiCall.Uint8x01'); + Uint8x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint8', isLeaf: false), + super('FfiCall.Uint8x01', isLeaf: isLeaf); @override void run() { @@ -325,10 +331,13 @@ class Uint8x01 extends FfiBenchmarkBase { class Uint16x01 extends FfiBenchmarkBase { final Function1int f; - Uint16x01() - : f = ffiTestFunctions.lookupFunction('Function1Uint16'), - super('FfiCall.Uint16x01'); + Uint16x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint16', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint16', isLeaf: false), + super('FfiCall.Uint16x01', isLeaf: isLeaf); @override void run() { @@ -343,10 +352,13 @@ class Uint16x01 extends FfiBenchmarkBase { class Uint32x01 extends FfiBenchmarkBase { final Function1int f; - Uint32x01() - : f = ffiTestFunctions.lookupFunction('Function1Uint32'), - super('FfiCall.Uint32x01'); + Uint32x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint32', isLeaf: false), + super('FfiCall.Uint32x01', isLeaf: isLeaf); @override void run() { @@ -361,10 +373,13 @@ class Uint32x01 extends FfiBenchmarkBase { class Uint64x01 extends FfiBenchmarkBase { final Function1int f; - Uint64x01() - : f = ffiTestFunctions.lookupFunction('Function1Uint64'), - super('FfiCall.Uint64x01'); + Uint64x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint64', isLeaf: false), + super('FfiCall.Uint64x01', isLeaf: isLeaf); @override void run() { @@ -379,10 +394,13 @@ class Uint64x01 extends FfiBenchmarkBase { class Int8x01 extends FfiBenchmarkBase { final Function1int f; - Int8x01() - : f = ffiTestFunctions - .lookupFunction('Function1Int8'), - super('FfiCall.Int8x01'); + Int8x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int8', isLeaf: false), + super('FfiCall.Int8x01', isLeaf: isLeaf); @override void run() { @@ -397,10 +415,13 @@ class Int8x01 extends FfiBenchmarkBase { class Int16x01 extends FfiBenchmarkBase { final Function1int f; - Int16x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int16'), - super('FfiCall.Int16x01'); + Int16x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int16', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int16', isLeaf: false), + super('FfiCall.Int16x01', isLeaf: isLeaf); @override void run() { @@ -415,10 +436,13 @@ class Int16x01 extends FfiBenchmarkBase { class Int32x01 extends FfiBenchmarkBase { final Function1int f; - Int32x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int32'), - super('FfiCall.Int32x01'); + Int32x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int32', isLeaf: false), + super('FfiCall.Int32x01', isLeaf: isLeaf); @override void run() { @@ -433,10 +457,13 @@ class Int32x01 extends FfiBenchmarkBase { class Int32x02 extends FfiBenchmarkBase { final Function2int f; - Int32x02() - : f = ffiTestFunctions.lookupFunction( - 'Function2Int32'), - super('FfiCall.Int32x02'); + Int32x02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Int32', isLeaf: false), + super('FfiCall.Int32x02', isLeaf: isLeaf); @override void run() { @@ -451,10 +478,13 @@ class Int32x02 extends FfiBenchmarkBase { class Int32x04 extends FfiBenchmarkBase { final Function4int f; - Int32x04() - : f = ffiTestFunctions.lookupFunction( - 'Function4Int32'), - super('FfiCall.Int32x04'); + Int32x04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Int32', isLeaf: false), + super('FfiCall.Int32x04', isLeaf: isLeaf); @override void run() { @@ -469,10 +499,13 @@ class Int32x04 extends FfiBenchmarkBase { class Int32x10 extends FfiBenchmarkBase { final Function10int f; - Int32x10() - : f = ffiTestFunctions.lookupFunction('Function10Int32'), - super('FfiCall.Int32x10'); + Int32x10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Int32', isLeaf: false), + super('FfiCall.Int32x10', isLeaf: isLeaf); @override void run() { @@ -487,10 +520,13 @@ class Int32x10 extends FfiBenchmarkBase { class Int32x20 extends FfiBenchmarkBase { final Function20int f; - Int32x20() - : f = ffiTestFunctions.lookupFunction('Function20Int32'), - super('FfiCall.Int32x20'); + Int32x20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Int32', isLeaf: false), + super('FfiCall.Int32x20', isLeaf: isLeaf); @override void run() { @@ -505,10 +541,13 @@ class Int32x20 extends FfiBenchmarkBase { class Int64x01 extends FfiBenchmarkBase { final Function1int f; - Int64x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int64'), - super('FfiCall.Int64x01'); + Int64x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: false), + super('FfiCall.Int64x01', isLeaf: isLeaf); @override void run() { @@ -523,10 +562,13 @@ class Int64x01 extends FfiBenchmarkBase { class Int64x02 extends FfiBenchmarkBase { final Function2int f; - Int64x02() - : f = ffiTestFunctions.lookupFunction( - 'Function2Int64'), - super('FfiCall.Int64x02'); + Int64x02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Int64', isLeaf: false), + super('FfiCall.Int64x02', isLeaf: isLeaf); @override void run() { @@ -541,10 +583,13 @@ class Int64x02 extends FfiBenchmarkBase { class Int64x04 extends FfiBenchmarkBase { final Function4int f; - Int64x04() - : f = ffiTestFunctions.lookupFunction( - 'Function4Int64'), - super('FfiCall.Int64x04'); + Int64x04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Int64', isLeaf: false), + super('FfiCall.Int64x04', isLeaf: isLeaf); @override void run() { @@ -559,10 +604,13 @@ class Int64x04 extends FfiBenchmarkBase { class Int64x10 extends FfiBenchmarkBase { final Function10int f; - Int64x10() - : f = ffiTestFunctions.lookupFunction('Function10Int64'), - super('FfiCall.Int64x10'); + Int64x10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Int64', isLeaf: false), + super('FfiCall.Int64x10', isLeaf: isLeaf); @override void run() { @@ -577,10 +625,13 @@ class Int64x10 extends FfiBenchmarkBase { class Int64x20 extends FfiBenchmarkBase { final Function20int f; - Int64x20() - : f = ffiTestFunctions.lookupFunction('Function20Int64'), - super('FfiCall.Int64x20'); + Int64x20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Int64', isLeaf: false), + super('FfiCall.Int64x20', isLeaf: isLeaf); @override void run() { @@ -595,10 +646,13 @@ class Int64x20 extends FfiBenchmarkBase { class Int64Mintx01 extends FfiBenchmarkBase { final Function1int f; - Int64Mintx01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int64'), - super('FfiCall.Int64Mintx01'); + Int64Mintx01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: false), + super('FfiCall.Int64Mintx01', isLeaf: isLeaf); @override void run() { @@ -613,10 +667,13 @@ class Int64Mintx01 extends FfiBenchmarkBase { class Floatx01 extends FfiBenchmarkBase { final Function1double f; - Floatx01() - : f = ffiTestFunctions.lookupFunction('Function1Float'), - super('FfiCall.Floatx01'); + Floatx01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Float', isLeaf: false), + super('FfiCall.Floatx01', isLeaf: isLeaf); @override void run() { @@ -631,10 +688,13 @@ class Floatx01 extends FfiBenchmarkBase { class Floatx02 extends FfiBenchmarkBase { final Function2double f; - Floatx02() - : f = ffiTestFunctions.lookupFunction('Function2Float'), - super('FfiCall.Floatx02'); + Floatx02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Float', isLeaf: false), + super('FfiCall.Floatx02', isLeaf: isLeaf); @override void run() { @@ -649,10 +709,13 @@ class Floatx02 extends FfiBenchmarkBase { class Floatx04 extends FfiBenchmarkBase { final Function4double f; - Floatx04() - : f = ffiTestFunctions.lookupFunction('Function4Float'), - super('FfiCall.Floatx04'); + Floatx04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Float', isLeaf: false), + super('FfiCall.Floatx04', isLeaf: isLeaf); @override void run() { @@ -668,10 +731,13 @@ class Floatx04 extends FfiBenchmarkBase { class Floatx10 extends FfiBenchmarkBase { final Function10double f; - Floatx10() - : f = ffiTestFunctions.lookupFunction('Function10Float'), - super('FfiCall.Floatx10'); + Floatx10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Float', isLeaf: false), + super('FfiCall.Floatx10', isLeaf: isLeaf); @override void run() { @@ -688,10 +754,13 @@ class Floatx10 extends FfiBenchmarkBase { class Floatx20 extends FfiBenchmarkBase { final Function20double f; - Floatx20() - : f = ffiTestFunctions.lookupFunction('Function20Float'), - super('FfiCall.Floatx20'); + Floatx20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Float', isLeaf: false), + super('FfiCall.Floatx20', isLeaf: isLeaf); @override void run() { @@ -728,10 +797,13 @@ class Floatx20 extends FfiBenchmarkBase { class Doublex01 extends FfiBenchmarkBase { final Function1double f; - Doublex01() - : f = ffiTestFunctions.lookupFunction('Function1Double'), - super('FfiCall.Doublex01'); + Doublex01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Double', isLeaf: false), + super('FfiCall.Doublex01', isLeaf: isLeaf); @override void run() { @@ -747,10 +819,13 @@ class Doublex01 extends FfiBenchmarkBase { class Doublex02 extends FfiBenchmarkBase { final Function2double f; - Doublex02() - : f = ffiTestFunctions.lookupFunction('Function2Double'), - super('FfiCall.Doublex02'); + Doublex02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Double', isLeaf: false), + super('FfiCall.Doublex02', isLeaf: isLeaf); @override void run() { @@ -766,10 +841,13 @@ class Doublex02 extends FfiBenchmarkBase { class Doublex04 extends FfiBenchmarkBase { final Function4double f; - Doublex04() - : f = ffiTestFunctions.lookupFunction('Function4Double'), - super('FfiCall.Doublex04'); + Doublex04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Double', isLeaf: false), + super('FfiCall.Doublex04', isLeaf: isLeaf); @override void run() { @@ -785,10 +863,13 @@ class Doublex04 extends FfiBenchmarkBase { class Doublex10 extends FfiBenchmarkBase { final Function10double f; - Doublex10() - : f = ffiTestFunctions.lookupFunction('Function10Double'), - super('FfiCall.Doublex10'); + Doublex10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Double', isLeaf: false), + super('FfiCall.Doublex10', isLeaf: isLeaf); @override void run() { @@ -805,10 +886,13 @@ class Doublex10 extends FfiBenchmarkBase { class Doublex20 extends FfiBenchmarkBase { final Function20double f; - Doublex20() - : f = ffiTestFunctions.lookupFunction('Function20Double'), - super('FfiCall.Doublex20'); + Doublex20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Double', isLeaf: false), + super('FfiCall.Doublex20', isLeaf: isLeaf); @override void run() { @@ -845,10 +929,13 @@ class Doublex20 extends FfiBenchmarkBase { class PointerUint8x01 extends FfiBenchmarkBase { final Function1PointerUint8 f; - PointerUint8x01() - : f = ffiTestFunctions.lookupFunction('Function1PointerUint8'), - super('FfiCall.PointerUint8x01'); + PointerUint8x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1PointerUint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1PointerUint8', isLeaf: false), + super('FfiCall.PointerUint8x01', isLeaf: isLeaf); Pointer p1 = nullptr; @override @@ -869,10 +956,13 @@ class PointerUint8x01 extends FfiBenchmarkBase { class PointerUint8x02 extends FfiBenchmarkBase { final Function2PointerUint8 f; - PointerUint8x02() - : f = ffiTestFunctions.lookupFunction('Function2PointerUint8'), - super('FfiCall.PointerUint8x02'); + PointerUint8x02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2PointerUint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2PointerUint8', isLeaf: false), + super('FfiCall.PointerUint8x02', isLeaf: isLeaf); Pointer p1 = nullptr; Pointer p2 = nullptr; @@ -901,10 +991,13 @@ class PointerUint8x02 extends FfiBenchmarkBase { class PointerUint8x04 extends FfiBenchmarkBase { final Function4PointerUint8 f; - PointerUint8x04() - : f = ffiTestFunctions.lookupFunction('Function4PointerUint8'), - super('FfiCall.PointerUint8x04'); + PointerUint8x04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4PointerUint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4PointerUint8', isLeaf: false), + super('FfiCall.PointerUint8x04', isLeaf: isLeaf); Pointer p1 = nullptr; Pointer p2 = nullptr; @@ -937,10 +1030,15 @@ class PointerUint8x04 extends FfiBenchmarkBase { class PointerUint8x10 extends FfiBenchmarkBase { final Function10PointerUint8 f; - PointerUint8x10() - : f = ffiTestFunctions.lookupFunction('Function10PointerUint8'), - super('FfiCall.PointerUint8x10'); + PointerUint8x10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10PointerUint8', isLeaf: true) + : ffiTestFunctions + .lookupFunction( + 'Function10PointerUint8', + isLeaf: false), + super('FfiCall.PointerUint8x10', isLeaf: isLeaf); Pointer p1 = nullptr; Pointer p2 = nullptr; @@ -985,10 +1083,15 @@ class PointerUint8x10 extends FfiBenchmarkBase { class PointerUint8x20 extends FfiBenchmarkBase { final Function20PointerUint8 f; - PointerUint8x20() - : f = ffiTestFunctions.lookupFunction('Function20PointerUint8'), - super('FfiCall.PointerUint8x20'); + PointerUint8x20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20PointerUint8', isLeaf: true) + : ffiTestFunctions + .lookupFunction( + 'Function20PointerUint8', + isLeaf: false), + super('FfiCall.PointerUint8x20', isLeaf: isLeaf); Pointer p1 = nullptr; Pointer p2 = nullptr; @@ -1061,8 +1164,8 @@ class Handlex01 extends FfiBenchmarkBase { Handlex01() : f = ffiTestFunctions.lookupFunction('Function1Handle'), - super('FfiCall.Handlex01'); + Function1Object>('Function1Handle', isLeaf: false), + super('FfiCall.Handlex01', isLeaf: false); @override void run() { @@ -1080,8 +1183,8 @@ class Handlex02 extends FfiBenchmarkBase { Handlex02() : f = ffiTestFunctions.lookupFunction('Function2Handle'), - super('FfiCall.Handlex02'); + Function2Object>('Function2Handle', isLeaf: false), + super('FfiCall.Handlex02', isLeaf: false); @override void run() { @@ -1100,8 +1203,8 @@ class Handlex04 extends FfiBenchmarkBase { Handlex04() : f = ffiTestFunctions.lookupFunction('Function4Handle'), - super('FfiCall.Handlex04'); + Function4Object>('Function4Handle', isLeaf: false), + super('FfiCall.Handlex04', isLeaf: false); @override void run() { @@ -1122,8 +1225,8 @@ class Handlex10 extends FfiBenchmarkBase { Handlex10() : f = ffiTestFunctions.lookupFunction('Function10Handle'), - super('FfiCall.Handlex10'); + Function10Object>('Function10Handle', isLeaf: false), + super('FfiCall.Handlex10', isLeaf: false); @override void run() { @@ -1150,8 +1253,8 @@ class Handlex20 extends FfiBenchmarkBase { Handlex20() : f = ffiTestFunctions.lookupFunction('Function20Handle'), - super('FfiCall.Handlex20'); + Function20Object>('Function20Handle', isLeaf: false), + super('FfiCall.Handlex20', isLeaf: false); @override void run() { @@ -1191,10 +1294,12 @@ class Handlex20 extends FfiBenchmarkBase { void main() { final benchmarks = [ () => Uint8x01(), + () => Uint8x01(isLeaf: true), () => Uint16x01(), () => Uint32x01(), () => Uint64x01(), () => Int8x01(), + () => Int8x01(isLeaf: true), () => Int16x01(), () => Int32x01(), () => Int32x02(), @@ -1206,22 +1311,27 @@ void main() { () => Int64x04(), () => Int64x10(), () => Int64x20(), + () => Int64x20(isLeaf: true), () => Int64Mintx01(), + () => Int64Mintx01(isLeaf: true), () => Floatx01(), () => Floatx02(), () => Floatx04(), () => Floatx10(), () => Floatx20(), + () => Floatx20(isLeaf: true), () => Doublex01(), () => Doublex02(), () => Doublex04(), () => Doublex10(), () => Doublex20(), + () => Doublex20(isLeaf: true), () => PointerUint8x01(), () => PointerUint8x02(), () => PointerUint8x04(), () => PointerUint8x10(), () => PointerUint8x20(), + () => PointerUint8x20(isLeaf: true), () => Handlex01(), () => Handlex02(), () => Handlex04(), diff --git a/benchmarks/FfiCall/dart2/FfiCall.dart b/benchmarks/FfiCall/dart2/FfiCall.dart index 4f0e465fbfb..1fd1b025124 100644 --- a/benchmarks/FfiCall/dart2/FfiCall.dart +++ b/benchmarks/FfiCall/dart2/FfiCall.dart @@ -285,7 +285,10 @@ typedef Function20Object = Object Function( // abstract class FfiBenchmarkBase extends BenchmarkBase { - FfiBenchmarkBase(String name) : super(name); + final bool isLeaf; + + FfiBenchmarkBase(String name, {this.isLeaf: false}) + : super('$name${isLeaf ? 'Leaf' : ''}'); void expectEquals(actual, expected) { if (actual != expected) { @@ -309,10 +312,13 @@ abstract class FfiBenchmarkBase extends BenchmarkBase { class Uint8x01 extends FfiBenchmarkBase { final Function1int f; - Uint8x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Uint8'), - super('FfiCall.Uint8x01'); + Uint8x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint8', isLeaf: false), + super('FfiCall.Uint8x01', isLeaf: isLeaf); @override void run() { @@ -327,10 +333,13 @@ class Uint8x01 extends FfiBenchmarkBase { class Uint16x01 extends FfiBenchmarkBase { final Function1int f; - Uint16x01() - : f = ffiTestFunctions.lookupFunction('Function1Uint16'), - super('FfiCall.Uint16x01'); + Uint16x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint16', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint16', isLeaf: false), + super('FfiCall.Uint16x01', isLeaf: isLeaf); @override void run() { @@ -345,10 +354,13 @@ class Uint16x01 extends FfiBenchmarkBase { class Uint32x01 extends FfiBenchmarkBase { final Function1int f; - Uint32x01() - : f = ffiTestFunctions.lookupFunction('Function1Uint32'), - super('FfiCall.Uint32x01'); + Uint32x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint32', isLeaf: false), + super('FfiCall.Uint32x01', isLeaf: isLeaf); @override void run() { @@ -363,10 +375,13 @@ class Uint32x01 extends FfiBenchmarkBase { class Uint64x01 extends FfiBenchmarkBase { final Function1int f; - Uint64x01() - : f = ffiTestFunctions.lookupFunction('Function1Uint64'), - super('FfiCall.Uint64x01'); + Uint64x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Uint64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Uint64', isLeaf: false), + super('FfiCall.Uint64x01', isLeaf: isLeaf); @override void run() { @@ -381,10 +396,13 @@ class Uint64x01 extends FfiBenchmarkBase { class Int8x01 extends FfiBenchmarkBase { final Function1int f; - Int8x01() - : f = ffiTestFunctions - .lookupFunction('Function1Int8'), - super('FfiCall.Int8x01'); + Int8x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int8', isLeaf: false), + super('FfiCall.Int8x01', isLeaf: isLeaf); @override void run() { @@ -399,10 +417,13 @@ class Int8x01 extends FfiBenchmarkBase { class Int16x01 extends FfiBenchmarkBase { final Function1int f; - Int16x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int16'), - super('FfiCall.Int16x01'); + Int16x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int16', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int16', isLeaf: false), + super('FfiCall.Int16x01', isLeaf: isLeaf); @override void run() { @@ -417,10 +438,13 @@ class Int16x01 extends FfiBenchmarkBase { class Int32x01 extends FfiBenchmarkBase { final Function1int f; - Int32x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int32'), - super('FfiCall.Int32x01'); + Int32x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int32', isLeaf: false), + super('FfiCall.Int32x01', isLeaf: isLeaf); @override void run() { @@ -435,10 +459,13 @@ class Int32x01 extends FfiBenchmarkBase { class Int32x02 extends FfiBenchmarkBase { final Function2int f; - Int32x02() - : f = ffiTestFunctions.lookupFunction( - 'Function2Int32'), - super('FfiCall.Int32x02'); + Int32x02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Int32', isLeaf: false), + super('FfiCall.Int32x02', isLeaf: isLeaf); @override void run() { @@ -453,10 +480,13 @@ class Int32x02 extends FfiBenchmarkBase { class Int32x04 extends FfiBenchmarkBase { final Function4int f; - Int32x04() - : f = ffiTestFunctions.lookupFunction( - 'Function4Int32'), - super('FfiCall.Int32x04'); + Int32x04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Int32', isLeaf: false), + super('FfiCall.Int32x04', isLeaf: isLeaf); @override void run() { @@ -471,10 +501,13 @@ class Int32x04 extends FfiBenchmarkBase { class Int32x10 extends FfiBenchmarkBase { final Function10int f; - Int32x10() - : f = ffiTestFunctions.lookupFunction('Function10Int32'), - super('FfiCall.Int32x10'); + Int32x10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Int32', isLeaf: false), + super('FfiCall.Int32x10', isLeaf: isLeaf); @override void run() { @@ -489,10 +522,13 @@ class Int32x10 extends FfiBenchmarkBase { class Int32x20 extends FfiBenchmarkBase { final Function20int f; - Int32x20() - : f = ffiTestFunctions.lookupFunction('Function20Int32'), - super('FfiCall.Int32x20'); + Int32x20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Int32', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Int32', isLeaf: false), + super('FfiCall.Int32x20', isLeaf: isLeaf); @override void run() { @@ -507,10 +543,13 @@ class Int32x20 extends FfiBenchmarkBase { class Int64x01 extends FfiBenchmarkBase { final Function1int f; - Int64x01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int64'), - super('FfiCall.Int64x01'); + Int64x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: false), + super('FfiCall.Int64x01', isLeaf: isLeaf); @override void run() { @@ -525,10 +564,13 @@ class Int64x01 extends FfiBenchmarkBase { class Int64x02 extends FfiBenchmarkBase { final Function2int f; - Int64x02() - : f = ffiTestFunctions.lookupFunction( - 'Function2Int64'), - super('FfiCall.Int64x02'); + Int64x02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Int64', isLeaf: false), + super('FfiCall.Int64x02', isLeaf: isLeaf); @override void run() { @@ -543,10 +585,13 @@ class Int64x02 extends FfiBenchmarkBase { class Int64x04 extends FfiBenchmarkBase { final Function4int f; - Int64x04() - : f = ffiTestFunctions.lookupFunction( - 'Function4Int64'), - super('FfiCall.Int64x04'); + Int64x04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Int64', isLeaf: false), + super('FfiCall.Int64x04', isLeaf: isLeaf); @override void run() { @@ -561,10 +606,13 @@ class Int64x04 extends FfiBenchmarkBase { class Int64x10 extends FfiBenchmarkBase { final Function10int f; - Int64x10() - : f = ffiTestFunctions.lookupFunction('Function10Int64'), - super('FfiCall.Int64x10'); + Int64x10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Int64', isLeaf: false), + super('FfiCall.Int64x10', isLeaf: isLeaf); @override void run() { @@ -579,10 +627,13 @@ class Int64x10 extends FfiBenchmarkBase { class Int64x20 extends FfiBenchmarkBase { final Function20int f; - Int64x20() - : f = ffiTestFunctions.lookupFunction('Function20Int64'), - super('FfiCall.Int64x20'); + Int64x20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Int64', isLeaf: false), + super('FfiCall.Int64x20', isLeaf: isLeaf); @override void run() { @@ -597,10 +648,13 @@ class Int64x20 extends FfiBenchmarkBase { class Int64Mintx01 extends FfiBenchmarkBase { final Function1int f; - Int64Mintx01() - : f = ffiTestFunctions.lookupFunction( - 'Function1Int64'), - super('FfiCall.Int64Mintx01'); + Int64Mintx01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Int64', isLeaf: false), + super('FfiCall.Int64Mintx01', isLeaf: isLeaf); @override void run() { @@ -615,10 +669,13 @@ class Int64Mintx01 extends FfiBenchmarkBase { class Floatx01 extends FfiBenchmarkBase { final Function1double f; - Floatx01() - : f = ffiTestFunctions.lookupFunction('Function1Float'), - super('FfiCall.Floatx01'); + Floatx01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Float', isLeaf: false), + super('FfiCall.Floatx01', isLeaf: isLeaf); @override void run() { @@ -633,10 +690,13 @@ class Floatx01 extends FfiBenchmarkBase { class Floatx02 extends FfiBenchmarkBase { final Function2double f; - Floatx02() - : f = ffiTestFunctions.lookupFunction('Function2Float'), - super('FfiCall.Floatx02'); + Floatx02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Float', isLeaf: false), + super('FfiCall.Floatx02', isLeaf: isLeaf); @override void run() { @@ -651,10 +711,13 @@ class Floatx02 extends FfiBenchmarkBase { class Floatx04 extends FfiBenchmarkBase { final Function4double f; - Floatx04() - : f = ffiTestFunctions.lookupFunction('Function4Float'), - super('FfiCall.Floatx04'); + Floatx04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Float', isLeaf: false), + super('FfiCall.Floatx04', isLeaf: isLeaf); @override void run() { @@ -670,10 +733,13 @@ class Floatx04 extends FfiBenchmarkBase { class Floatx10 extends FfiBenchmarkBase { final Function10double f; - Floatx10() - : f = ffiTestFunctions.lookupFunction('Function10Float'), - super('FfiCall.Floatx10'); + Floatx10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Float', isLeaf: false), + super('FfiCall.Floatx10', isLeaf: isLeaf); @override void run() { @@ -690,10 +756,13 @@ class Floatx10 extends FfiBenchmarkBase { class Floatx20 extends FfiBenchmarkBase { final Function20double f; - Floatx20() - : f = ffiTestFunctions.lookupFunction('Function20Float'), - super('FfiCall.Floatx20'); + Floatx20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Float', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Float', isLeaf: false), + super('FfiCall.Floatx20', isLeaf: isLeaf); @override void run() { @@ -730,10 +799,13 @@ class Floatx20 extends FfiBenchmarkBase { class Doublex01 extends FfiBenchmarkBase { final Function1double f; - Doublex01() - : f = ffiTestFunctions.lookupFunction('Function1Double'), - super('FfiCall.Doublex01'); + Doublex01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1Double', isLeaf: false), + super('FfiCall.Doublex01', isLeaf: isLeaf); @override void run() { @@ -749,10 +821,13 @@ class Doublex01 extends FfiBenchmarkBase { class Doublex02 extends FfiBenchmarkBase { final Function2double f; - Doublex02() - : f = ffiTestFunctions.lookupFunction('Function2Double'), - super('FfiCall.Doublex02'); + Doublex02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2Double', isLeaf: false), + super('FfiCall.Doublex02', isLeaf: isLeaf); @override void run() { @@ -768,10 +843,13 @@ class Doublex02 extends FfiBenchmarkBase { class Doublex04 extends FfiBenchmarkBase { final Function4double f; - Doublex04() - : f = ffiTestFunctions.lookupFunction('Function4Double'), - super('FfiCall.Doublex04'); + Doublex04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4Double', isLeaf: false), + super('FfiCall.Doublex04', isLeaf: isLeaf); @override void run() { @@ -787,10 +865,13 @@ class Doublex04 extends FfiBenchmarkBase { class Doublex10 extends FfiBenchmarkBase { final Function10double f; - Doublex10() - : f = ffiTestFunctions.lookupFunction('Function10Double'), - super('FfiCall.Doublex10'); + Doublex10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function10Double', isLeaf: false), + super('FfiCall.Doublex10', isLeaf: isLeaf); @override void run() { @@ -807,10 +888,13 @@ class Doublex10 extends FfiBenchmarkBase { class Doublex20 extends FfiBenchmarkBase { final Function20double f; - Doublex20() - : f = ffiTestFunctions.lookupFunction('Function20Double'), - super('FfiCall.Doublex20'); + Doublex20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20Double', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function20Double', isLeaf: false), + super('FfiCall.Doublex20', isLeaf: isLeaf); @override void run() { @@ -847,10 +931,13 @@ class Doublex20 extends FfiBenchmarkBase { class PointerUint8x01 extends FfiBenchmarkBase { final Function1PointerUint8 f; - PointerUint8x01() - : f = ffiTestFunctions.lookupFunction('Function1PointerUint8'), - super('FfiCall.PointerUint8x01'); + PointerUint8x01({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function1PointerUint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function1PointerUint8', isLeaf: false), + super('FfiCall.PointerUint8x01', isLeaf: isLeaf); Pointer p1; @override @@ -871,10 +958,13 @@ class PointerUint8x01 extends FfiBenchmarkBase { class PointerUint8x02 extends FfiBenchmarkBase { final Function2PointerUint8 f; - PointerUint8x02() - : f = ffiTestFunctions.lookupFunction('Function2PointerUint8'), - super('FfiCall.PointerUint8x02'); + PointerUint8x02({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function2PointerUint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function2PointerUint8', isLeaf: false), + super('FfiCall.PointerUint8x02', isLeaf: isLeaf); Pointer p1, p2; @@ -902,10 +992,13 @@ class PointerUint8x02 extends FfiBenchmarkBase { class PointerUint8x04 extends FfiBenchmarkBase { final Function4PointerUint8 f; - PointerUint8x04() - : f = ffiTestFunctions.lookupFunction('Function4PointerUint8'), - super('FfiCall.PointerUint8x04'); + PointerUint8x04({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function4PointerUint8', isLeaf: true) + : ffiTestFunctions.lookupFunction('Function4PointerUint8', isLeaf: false), + super('FfiCall.PointerUint8x04', isLeaf: isLeaf); Pointer p1, p2, p3, p4; @@ -935,10 +1028,15 @@ class PointerUint8x04 extends FfiBenchmarkBase { class PointerUint8x10 extends FfiBenchmarkBase { final Function10PointerUint8 f; - PointerUint8x10() - : f = ffiTestFunctions.lookupFunction('Function10PointerUint8'), - super('FfiCall.PointerUint8x10'); + PointerUint8x10({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function10PointerUint8', isLeaf: true) + : ffiTestFunctions + .lookupFunction( + 'Function10PointerUint8', + isLeaf: false), + super('FfiCall.PointerUint8x10', isLeaf: isLeaf); Pointer p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; @@ -974,10 +1072,15 @@ class PointerUint8x10 extends FfiBenchmarkBase { class PointerUint8x20 extends FfiBenchmarkBase { final Function20PointerUint8 f; - PointerUint8x20() - : f = ffiTestFunctions.lookupFunction('Function20PointerUint8'), - super('FfiCall.PointerUint8x20'); + PointerUint8x20({isLeaf: false}) + : f = isLeaf + ? ffiTestFunctions.lookupFunction('Function20PointerUint8', isLeaf: true) + : ffiTestFunctions + .lookupFunction( + 'Function20PointerUint8', + isLeaf: false), + super('FfiCall.PointerUint8x20', isLeaf: isLeaf); Pointer p1, p2, @@ -1050,8 +1153,8 @@ class Handlex01 extends FfiBenchmarkBase { Handlex01() : f = ffiTestFunctions.lookupFunction('Function1Handle'), - super('FfiCall.Handlex01'); + Function1Object>('Function1Handle', isLeaf: false), + super('FfiCall.Handlex01', isLeaf: false); @override void run() { @@ -1069,8 +1172,8 @@ class Handlex02 extends FfiBenchmarkBase { Handlex02() : f = ffiTestFunctions.lookupFunction('Function2Handle'), - super('FfiCall.Handlex02'); + Function2Object>('Function2Handle', isLeaf: false), + super('FfiCall.Handlex02', isLeaf: false); @override void run() { @@ -1089,8 +1192,8 @@ class Handlex04 extends FfiBenchmarkBase { Handlex04() : f = ffiTestFunctions.lookupFunction('Function4Handle'), - super('FfiCall.Handlex04'); + Function4Object>('Function4Handle', isLeaf: false), + super('FfiCall.Handlex04', isLeaf: false); @override void run() { @@ -1111,8 +1214,8 @@ class Handlex10 extends FfiBenchmarkBase { Handlex10() : f = ffiTestFunctions.lookupFunction('Function10Handle'), - super('FfiCall.Handlex10'); + Function10Object>('Function10Handle', isLeaf: false), + super('FfiCall.Handlex10', isLeaf: false); @override void run() { @@ -1139,8 +1242,8 @@ class Handlex20 extends FfiBenchmarkBase { Handlex20() : f = ffiTestFunctions.lookupFunction('Function20Handle'), - super('FfiCall.Handlex20'); + Function20Object>('Function20Handle', isLeaf: false), + super('FfiCall.Handlex20', isLeaf: false); @override void run() { @@ -1180,10 +1283,12 @@ class Handlex20 extends FfiBenchmarkBase { void main() { final benchmarks = [ () => Uint8x01(), + () => Uint8x01(isLeaf: true), () => Uint16x01(), () => Uint32x01(), () => Uint64x01(), () => Int8x01(), + () => Int8x01(isLeaf: true), () => Int16x01(), () => Int32x01(), () => Int32x02(), @@ -1195,22 +1300,27 @@ void main() { () => Int64x04(), () => Int64x10(), () => Int64x20(), + () => Int64x20(isLeaf: true), () => Int64Mintx01(), + () => Int64Mintx01(isLeaf: true), () => Floatx01(), () => Floatx02(), () => Floatx04(), () => Floatx10(), () => Floatx20(), + () => Floatx20(isLeaf: true), () => Doublex01(), () => Doublex02(), () => Doublex04(), () => Doublex10(), () => Doublex20(), + () => Doublex20(isLeaf: true), () => PointerUint8x01(), () => PointerUint8x02(), () => PointerUint8x04(), () => PointerUint8x10(), () => PointerUint8x20(), + () => PointerUint8x20(isLeaf: true), () => Handlex01(), () => Handlex02(), () => Handlex04(), diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index 7600785775e..ed754b39630 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart @@ -3778,6 +3778,27 @@ const MessageCode messageFfiExpectedConstant = const MessageCode( "FfiExpectedConstant", message: r"""Exceptional return value must be a constant."""); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template templateFfiExpectedConstantArg = + const Template( + messageTemplate: r"""Argument '#name' must be a constant.""", + withArguments: _withArgumentsFfiExpectedConstantArg); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeFfiExpectedConstantArg = + const Code( + "FfiExpectedConstantArg", +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsFfiExpectedConstantArg(String name) { + if (name.isEmpty) throw 'No name provided'; + name = demangleMixinApplicationName(name); + return new Message(codeFfiExpectedConstantArg, + message: """Argument '${name}' must be a constant.""", + arguments: {'name': name}); +} + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template templateFfiExtendsOrImplementsSealedClass = @@ -3931,6 +3952,24 @@ Message _withArgumentsFfiFieldNull(String name) { arguments: {'name': name}); } +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeFfiLeafCallMustNotReturnHandle = + messageFfiLeafCallMustNotReturnHandle; + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const MessageCode messageFfiLeafCallMustNotReturnHandle = const MessageCode( + "FfiLeafCallMustNotReturnHandle", + message: r"""FFI leaf call must not have Handle return type."""); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeFfiLeafCallMustNotTakeHandle = + messageFfiLeafCallMustNotTakeHandle; + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const MessageCode messageFfiLeafCallMustNotTakeHandle = const MessageCode( + "FfiLeafCallMustNotTakeHandle", + message: r"""FFI leaf call must not have Handle argument types."""); + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template< Message Function(String name)> templateFfiNotStatic = const Template< diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart index d02d9377043..37074c3177f 100644 --- a/pkg/analyzer/lib/error/error.dart +++ b/pkg/analyzer/lib/error/error.dart @@ -462,6 +462,7 @@ const List errorCodeValues = [ CompileTimeErrorCode.YIELD_IN_NON_GENERATOR, CompileTimeErrorCode.YIELD_OF_INVALID_TYPE, FfiCode.ANNOTATION_ON_POINTER_FIELD, + FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, FfiCode.EMPTY_STRUCT, FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD, FfiCode.EXTRA_SIZE_ANNOTATION_CARRAY, @@ -470,6 +471,8 @@ const List errorCodeValues = [ FfiCode.GENERIC_STRUCT_SUBCLASS, FfiCode.INVALID_EXCEPTION_VALUE, FfiCode.INVALID_FIELD_TYPE_IN_STRUCT, + FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, + FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, FfiCode.MISMATCHED_ANNOTATION_ON_STRUCT_FIELD, FfiCode.MISSING_ANNOTATION_ON_STRUCT_FIELD, FfiCode.MISSING_EXCEPTION_VALUE, diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart index a9b49b3afb6..ef53b2c3a9b 100644 --- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart +++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart @@ -21,6 +21,15 @@ class FfiCode extends AnalyzerErrorCode { "any annotations.", correction: "Try removing the annotation."); + /** + * Parameters: + * 0: the name of the argument + */ + static const FfiCode ARGUMENT_MUST_BE_A_CONSTANT = FfiCode( + name: 'ARGUMENT_MUST_BE_A_CONSTANT', + message: "Argument '{0}' must be a constant.", + correction: "Try replacing the value with a literal or const."); + /** * Parameters: * 0: the name of the struct class @@ -103,6 +112,22 @@ class FfiCode extends AnalyzerErrorCode { "Try using 'int', 'double', 'Array', 'Pointer', or subtype of " "'Struct' or 'Union'."); + /** + * No parameters. + */ + static const FfiCode LEAF_CALL_MUST_NOT_RETURN_HANDLE = FfiCode( + name: 'LEAF_CALL_MUST_NOT_RETURN_HANDLE', + message: "FFI leaf call must not return a Handle.", + correction: "Try changing the return type to primitive or struct."); + + /** + * No parameters. + */ + static const FfiCode LEAF_CALL_MUST_NOT_TAKE_HANDLE = FfiCode( + name: 'LEAF_CALL_MUST_NOT_TAKE_HANDLE', + message: "FFI leaf call must not take arguments of type Handle.", + correction: "Try changing the argument type to primitive or struct."); + /** * No parameters. */ diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart index caff2221b27..d03a8150a0d 100644 --- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart +++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart @@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; +import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/dart/element/type_system.dart'; import 'package:analyzer/src/dart/error/ffi_code.dart'; @@ -20,6 +21,7 @@ class FfiVerifier extends RecursiveAstVisitor { static const _allocatorExtensionName = 'AllocatorAlloc'; static const _arrayClassName = 'Array'; static const _dartFfiLibraryName = 'dart.ffi'; + static const _isLeafParamName = 'isLeaf'; static const _opaqueClassName = 'Opaque'; static const List _primitiveIntegerNativeTypes = [ @@ -341,6 +343,26 @@ class FfiVerifier extends RecursiveAstVisitor { return false; } + // Get the const bool value of `expr` if it exists. + // Return null if it isn't a const bool. + bool? _maybeGetBoolConstValue(Expression expr) { + if (expr is BooleanLiteral) { + return expr.value; + } else if (expr is Identifier) { + final staticElm = expr.staticElement; + if (staticElm is ConstVariableElement) { + return staticElm.computeConstantValue()?.toBoolValue(); + } + if (staticElm is PropertyAccessorElementImpl) { + final v = staticElm.variable; + if (v is ConstVariableElement) { + return v.computeConstantValue()?.toBoolValue(); + } + } + } + return null; + } + _PrimitiveDartType _primitiveNativeType(DartType nativeType) { if (nativeType is InterfaceType) { final element = nativeType.element; @@ -461,7 +483,9 @@ class FfiVerifier extends RecursiveAstVisitor { _errorReporter.reportErrorForNode( FfiCode.MUST_BE_A_SUBTYPE, node, [TPrime, F, 'asFunction']); } + _validateFfiLeafCallUsesNoHandles(node, TPrime, node); } + _validateIsLeafIsConst(node); } /// Validates that the given [nativeType] is, when native types are converted @@ -551,6 +575,37 @@ class FfiVerifier extends RecursiveAstVisitor { } } + void _validateFfiLeafCallUsesNoHandles( + MethodInvocation node, DartType nativeType, AstNode errorNode) { + final args = node.argumentList.arguments; + if (args.isNotEmpty) { + for (final arg in args) { + if (arg is NamedExpression) { + if (arg.element?.name == _isLeafParamName) { + // Handles are ok for regular (non-leaf) calls. Check `isLeaf:true`. + final bool? isLeaf = _maybeGetBoolConstValue(arg.expression); + if (isLeaf != null && isLeaf) { + if (nativeType is FunctionType) { + if (_primitiveNativeType(nativeType.returnType) == + _PrimitiveDartType.handle) { + _errorReporter.reportErrorForNode( + FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, errorNode); + } + for (final param in nativeType.normalParameterTypes) { + if (_primitiveNativeType(param) == + _PrimitiveDartType.handle) { + _errorReporter.reportErrorForNode( + FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, errorNode); + } + } + } + } + } + } + } + } + } + /// Validate that the fields declared by the given [node] meet the /// requirements for fields within a struct or union class. void _validateFieldsInCompound(FieldDeclaration node) { @@ -654,6 +709,27 @@ class FfiVerifier extends RecursiveAstVisitor { } } + void _validateIsLeafIsConst(MethodInvocation node) { + // Ensure `isLeaf` is const as we need the value at compile time to know + // which trampoline to generate. + final args = node.argumentList.arguments; + if (args.isNotEmpty) { + for (final arg in args) { + if (arg is NamedExpression) { + if (arg.element?.name == _isLeafParamName) { + if (_maybeGetBoolConstValue(arg.expression) == null) { + final AstNode errorNode = node; + _errorReporter.reportErrorForNode( + FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, + errorNode, + [_isLeafParamName]); + } + } + } + } + } + } + /// Validate the invocation of the instance method /// `DynamicLibrary.lookupFunction()`. void _validateLookupFunction(MethodInvocation node) { @@ -678,6 +754,8 @@ class FfiVerifier extends RecursiveAstVisitor { _errorReporter.reportErrorForNode( FfiCode.MUST_BE_A_SUBTYPE, errorNode, [S, F, 'lookupFunction']); } + _validateIsLeafIsConst(node); + _validateFfiLeafCallUsesNoHandles(node, S, typeArguments![0]); } /// Validate that none of the [annotations] are from `dart:ffi`. diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart index d2def94e6b5..a7a7b278699 100644 --- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart +++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart @@ -620,6 +620,8 @@ class NativeType { const NativeType(); } +class Handle extends NativeType {} + class Void extends NativeType {} class Int8 extends NativeType { @@ -674,7 +676,7 @@ final Pointer nullptr = Pointer.fromAddress(0); extension NativeFunctionPointer on Pointer> { - external DF asFunction(); + external DF asFunction({bool isLeaf:false}); } class _Compound extends NativeType {} @@ -689,11 +691,13 @@ class Packed { const Packed(this.memberAlignment); } -abstract class DynamicLibrary {} +abstract class DynamicLibrary { + external factory DynamicLibrary.open(String name); +} extension DynamicLibraryExtension on DynamicLibrary { external F lookupFunction( - String symbolName); + String symbolName, {bool isLeaf:false}); } abstract class NativeFunction extends NativeType {} diff --git a/pkg/analyzer/test/src/diagnostics/argument_must_be_a_constant_test.dart b/pkg/analyzer/test/src/diagnostics/argument_must_be_a_constant_test.dart new file mode 100644 index 00000000000..a60b3750b45 --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/argument_must_be_a_constant_test.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/src/dart/error/ffi_code.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(ArgumentMustBeAConstantTest); + }); +} + +@reflectiveTest +class ArgumentMustBeAConstantTest extends PubPackageResolutionTest { + test_AsFunctionIsLeafGlobal() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef Int8UnOp = Int8 Function(Int8); +typedef IntUnOp = int Function(int); +bool isLeaf = false; +doThings() { + Pointer> p = Pointer.fromAddress(1337); + IntUnOp f = p.asFunction(isLeaf:isLeaf); + f(8); +} +''', [ + error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 211, 27), + ]); + } + + test_AsFunctionIsLeafLocal() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef Int8UnOp = Int8 Function(Int8); +typedef IntUnOp = int Function(int); +doThings() { + bool isLeaf = false; + Pointer> p = Pointer.fromAddress(1337); + IntUnOp f = p.asFunction(isLeaf:isLeaf); + f(8); +} +''', [ + error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 213, 27), + ]); + } + + test_AsFunctionIsLeafParam() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef Int8UnOp = Int8 Function(Int8); +typedef IntUnOp = int Function(int); +doThings(bool isLeaf) { + Pointer> p = Pointer.fromAddress(1337); + IntUnOp f = p.asFunction(isLeaf:isLeaf); + f(8); +} +''', [ + error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 201, 27), + ]); + } + + test_LookupFunctionIsLeaf() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef Int8UnOp = Int8 Function(Int8); +typedef IntUnOp = int Function(int); +doThings(bool isLeaf) { + DynamicLibrary l = DynamicLibrary.open("my_lib"); + l.lookupFunction("timesFour", isLeaf:isLeaf); +} +''', [ + error(FfiCode.ARGUMENT_MUST_BE_A_CONSTANT, 174, 63), + ]); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/ffi_leaf_call_must_not_use_handle_test.dart b/pkg/analyzer/test/src/diagnostics/ffi_leaf_call_must_not_use_handle_test.dart new file mode 100644 index 00000000000..55320adad26 --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/ffi_leaf_call_must_not_use_handle_test.dart @@ -0,0 +1,77 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/src/dart/error/ffi_code.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(LeafCallMustNotUseHandle); + }); +} + +@reflectiveTest +class LeafCallMustNotUseHandle extends PubPackageResolutionTest { + test_AsFunctionReturnsHandle() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef NativeReturnsHandle = Handle Function(); +typedef ReturnsHandle = Object Function(); +doThings() { + Pointer> p = Pointer.fromAddress(1337); + ReturnsHandle f = p.asFunction(isLeaf:true); + f(); +} +''', [ + error(FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, 222, 25), + ]); + } + + test_AsFunctionTakesHandle() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef NativeTakesHandle = Void Function(Handle); +typedef TakesHandle = void Function(Object); +class MyClass {} +doThings() { + Pointer> p = Pointer.fromAddress(1337); + TakesHandle f = p.asFunction(isLeaf:true); + f(MyClass()); +} +''', [ + error(FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, 239, 25), + ]); + } + + test_LookupFunctionReturnsHandle() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef NativeReturnsHandle = Handle Function(); +typedef ReturnsHandle = Object Function(); +doThings() { + DynamicLibrary l = DynamicLibrary.open("my_lib"); + l.lookupFunction("timesFour", isLeaf:true); +} +''', [ + error(FfiCode.LEAF_CALL_MUST_NOT_RETURN_HANDLE, 195, 19), + ]); + } + + test_LookupFunctionTakesHandle() async { + await assertErrorsInCode(r''' +import 'dart:ffi'; +typedef NativeTakesHandle = Void Function(Handle); +typedef TakesHandle = void Function(Object); +class MyClass {} +doThings() { + DynamicLibrary l = DynamicLibrary.open("my_lib"); + l.lookupFunction("timesFour", isLeaf:true); +} +''', [ + error(FfiCode.LEAF_CALL_MUST_NOT_TAKE_HANDLE, 216, 17), + ]); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index c6fcbe03cd0..66018e3d876 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -17,6 +17,7 @@ import 'ambiguous_import_test.dart' as ambiguous_import; import 'ambiguous_set_or_map_literal_test.dart' as ambiguous_set_or_map_literal; import 'annotation_on_pointer_field_test.dart' as annotation_on_pointer_field; import 'annotation_syntax_test.dart' as annotation_syntax; +import 'argument_must_be_a_constant_test.dart' as argument_must_be_a_constant; import 'argument_type_not_assignable_test.dart' as argument_type_not_assignable; import 'argument_type_not_assignable_to_error_handler_test.dart' as argument_type_not_assignable_to_error_handler; @@ -188,6 +189,8 @@ import 'extra_positional_arguments_test.dart' as extra_positional_arguments; import 'extra_size_annotation_carray_test.dart' as extra_size_annotation_carray; import 'ffi_array_multi_non_positive_input_test.dart' as ffi_array_multi_non_positive_input_test; +import 'ffi_leaf_call_must_not_use_handle_test.dart' + as ffi_leaf_call_must_not_use_handle; import 'field_in_struct_with_initializer_test.dart' as field_in_struct_with_initializer; import 'field_initialized_by_multiple_initializers_test.dart' @@ -700,6 +703,7 @@ main() { ambiguous_set_or_map_literal.main(); annotation_on_pointer_field.main(); annotation_syntax.main(); + argument_must_be_a_constant.main(); argument_type_not_assignable.main(); argument_type_not_assignable_to_error_handler.main(); assert_in_redirecting_constructor.main(); @@ -816,6 +820,7 @@ main() { extra_positional_arguments.main(); extra_size_annotation_carray.main(); ffi_array_multi_non_positive_input_test.main(); + ffi_leaf_call_must_not_use_handle.main(); field_in_struct_with_initializer.main(); field_initialized_by_multiple_initializers.main(); final_initialized_in_declaration_and_constructor.main(); diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart index b2250cfb368..65066331057 100644 --- a/pkg/front_end/lib/src/api_unstable/vm.dart +++ b/pkg/front_end/lib/src/api_unstable/vm.dart @@ -52,11 +52,14 @@ export '../fasta/fasta_codes.dart' LocatedMessage, messageFfiExceptionalReturnNull, messageFfiExpectedConstant, + messageFfiLeafCallMustNotReturnHandle, + messageFfiLeafCallMustNotTakeHandle, messageFfiPackedAnnotationAlignment, messageNonPositiveArrayDimensions, noLength, templateFfiDartTypeMismatch, templateFfiEmptyStruct, + templateFfiExpectedConstantArg, templateFfiExpectedExceptionalReturn, templateFfiExpectedNoExceptionalReturn, templateFfiExtendsOrImplementsSealedClass, diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index 9c7263d3176..c890c30212a 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -326,6 +326,7 @@ FfiDartTypeMismatch/analyzerCode: Fail FfiEmptyStruct/analyzerCode: Fail FfiExceptionalReturnNull/analyzerCode: Fail FfiExpectedConstant/analyzerCode: Fail +FfiExpectedConstantArg/analyzerCode: Fail FfiExpectedExceptionalReturn/analyzerCode: Fail FfiExpectedNoExceptionalReturn/analyzerCode: Fail FfiExtendsOrImplementsSealedClass/analyzerCode: Fail @@ -334,6 +335,8 @@ FfiFieldCyclic/analyzerCode: Fail FfiFieldInitializer/analyzerCode: Fail FfiFieldNoAnnotation/analyzerCode: Fail FfiFieldNull/analyzerCode: Fail +FfiLeafCallMustNotReturnHandle/analyzerCode: Fail +FfiLeafCallMustNotTakeHandle/analyzerCode: Fail FfiNotStatic/analyzerCode: Fail FfiPackedAnnotation/analyzerCode: Fail FfiPackedAnnotationAlignment/analyzerCode: Fail diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 2afa94c8ef0..b00bab2df3a 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -4501,6 +4501,21 @@ FfiExceptionalReturnNull: template: "Exceptional return value must not be null." external: test/ffi_test.dart +FfiExpectedConstantArg: + # Used by dart:ffi + template: "Argument '#name' must be a constant." + external: test/ffi_test.dart + +FfiLeafCallMustNotTakeHandle: + # Used by dart:ffi + template: "FFI leaf call must not have Handle argument types." + external: test/ffi_test.dart + +FfiLeafCallMustNotReturnHandle: + # Used by dart:ffi + template: "FFI leaf call must not have Handle return type." + external: test/ffi_test.dart + SpreadTypeMismatch: template: "Unexpected type '#type' of a spread. Expected 'dynamic' or an Iterable." script: diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart index a4c6fd41c52..5225286d451 100644 --- a/pkg/vm/lib/transformations/ffi.dart +++ b/pkg/vm/lib/transformations/ffi.dart @@ -221,6 +221,7 @@ class FfiTransformer extends Transformer { final Library ffiLibrary; final Class allocatorClass; final Class nativeFunctionClass; + final Class handleClass; final Class opaqueClass; final Class arrayClass; final Class arraySizeClass; @@ -324,6 +325,7 @@ class FfiTransformer extends Transformer { ffiLibrary = index.getLibrary('dart:ffi'), allocatorClass = index.getClass('dart:ffi', 'Allocator'), nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'), + handleClass = index.getClass('dart:ffi', 'Handle'), opaqueClass = index.getClass('dart:ffi', 'Opaque'), arrayClass = index.getClass('dart:ffi', 'Array'), arraySizeClass = index.getClass('dart:ffi', '_ArraySize'), diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart index d1640a21403..6917fd1d89f 100644 --- a/pkg/vm/lib/transformations/ffi_use_sites.dart +++ b/pkg/vm/lib/transformations/ffi_use_sites.dart @@ -8,7 +8,10 @@ import 'package:front_end/src/api_unstable/vm.dart' show messageFfiExceptionalReturnNull, messageFfiExpectedConstant, + messageFfiLeafCallMustNotReturnHandle, + messageFfiLeafCallMustNotTakeHandle, templateFfiDartTypeMismatch, + templateFfiExpectedConstantArg, templateFfiExpectedExceptionalReturn, templateFfiExpectedNoExceptionalReturn, templateFfiExtendsOrImplementsSealedClass, @@ -176,12 +179,14 @@ class _FfiUseSiteTransformer extends FfiTransformer { } } } else if (target == lookupFunctionMethod) { - final DartType nativeType = InterfaceType( + final nativeType = InterfaceType( nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]); final DartType dartType = node.arguments.types[1]; _ensureNativeTypeValid(nativeType, node); _ensureNativeTypeToDartType(nativeType, dartType, node); + _ensureIsLeafIsConst(node); + _ensureLeafCallDoesNotUseHandles(nativeType, node); final replacement = _replaceLookupFunction(node); @@ -197,20 +202,27 @@ class _FfiUseSiteTransformer extends FfiTransformer { } return replacement; } else if (target == asFunctionMethod) { - final DartType dartType = node.arguments.types[1]; + final dartType = node.arguments.types[1]; final DartType nativeType = InterfaceType( nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]); _ensureNativeTypeValid(nativeType, node); _ensureNativeTypeToDartType(nativeType, dartType, node); + _ensureIsLeafIsConst(node); + _ensureLeafCallDoesNotUseHandles(nativeType, node); final DartType nativeSignature = (nativeType as InterfaceType).typeArguments[0]; + bool isLeaf = _getIsLeafBoolean(node); + if (isLeaf == null) { + isLeaf = false; + } + // Inline function body to make all type arguments instatiated. final replacement = StaticInvocation( asFunctionInternal, - Arguments([node.arguments.positional[0]], + Arguments([node.arguments.positional[0], BoolLiteral(isLeaf)], types: [dartType, nativeSignature])); if (dartType is FunctionType) { @@ -411,12 +423,12 @@ class _FfiUseSiteTransformer extends FfiTransformer { Expression _replaceLookupFunction(StaticInvocation node) { // The generated code looks like: // - // _asFunctionInternal(lookup>(symbolName)) - + // _asFunctionInternal(lookup>(symbolName), + // isLeaf) final DartType nativeSignature = node.arguments.types[0]; final DartType dartSignature = node.arguments.types[1]; - final Arguments args = Arguments([ + final Arguments lookupArgs = Arguments([ node.arguments.positional[1] ], types: [ InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeSignature]) @@ -425,11 +437,18 @@ class _FfiUseSiteTransformer extends FfiTransformer { final Expression lookupResult = MethodInvocation( node.arguments.positional[0], Name("lookup"), - args, + lookupArgs, libraryLookupMethod); - return StaticInvocation(asFunctionInternal, - Arguments([lookupResult], types: [dartSignature, nativeSignature])); + bool isLeaf = _getIsLeafBoolean(node); + if (isLeaf == null) { + isLeaf = false; + } + + return StaticInvocation( + asFunctionInternal, + Arguments([lookupResult, BoolLiteral(isLeaf)], + types: [dartSignature, nativeSignature])); } // We need to rewrite calls to 'fromFunction' into two calls, representing the @@ -818,6 +837,72 @@ class _FfiUseSiteTransformer extends FfiTransformer { throw _FfiStaticTypeError(); } } + + // Returns + // - `true` if leaf + // - `false` if not leaf + // - `null` if the expression is not valid (e.g. non-const bool, null) + bool _getIsLeafBoolean(StaticInvocation node) { + for (final named in node.arguments.named) { + if (named.name == 'isLeaf') { + final expr = named.value; + if (expr is BoolLiteral) { + return expr.value; + } else if (expr is ConstantExpression) { + final constant = expr.constant; + if (constant is BoolConstant) { + return constant.value; + } + } + // isLeaf is passed some invalid value. + return null; + } + } + // isLeaf defaults to false. + return false; + } + + void _ensureIsLeafIsConst(StaticInvocation node) { + final isLeaf = _getIsLeafBoolean(node); + if (isLeaf == null) { + diagnosticReporter.report( + templateFfiExpectedConstantArg.withArguments('isLeaf'), + node.fileOffset, + 1, + node.location.file); + // Throw so we don't get another error about not replacing + // `lookupFunction`, which will shadow the above error. + throw _FfiStaticTypeError(); + } + } + + void _ensureLeafCallDoesNotUseHandles( + InterfaceType nativeType, StaticInvocation node) { + // Handles are only disallowed for leaf calls. + final isLeaf = _getIsLeafBoolean(node); + if (isLeaf == null || isLeaf == false) { + return; + } + + // Check if return type is Handle. + final functionType = nativeType.typeArguments[0]; + if (functionType is FunctionType) { + final returnType = functionType.returnType; + if (returnType is InterfaceType) { + if (returnType.classNode == handleClass) { + diagnosticReporter.report(messageFfiLeafCallMustNotReturnHandle, + node.fileOffset, 1, node.location.file); + } + } + // Check if any of the argument types are Handle. + for (InterfaceType param in functionType.positionalParameters) { + if (param.classNode == handleClass) { + diagnosticReporter.report(messageFfiLeafCallMustNotTakeHandle, + node.fileOffset, 1, node.location.file); + } + } + } + } } /// Used internally for abnormal control flow to prevent cascading error diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect index 08c06bb5517..0e9d6a52dd9 100644 --- a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect +++ b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect @@ -67,7 +67,7 @@ static method testLookupFunctionReturn() → void { final ffi::DynamicLibrary* dylib = [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary?] ffi::DynamicLibrary::executable(); final () →* self::Struct1* function1 = block { _in::_nativeEffect(new self::Struct1::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18))); - } =>ffi::_asFunctionInternal<() →* self::Struct1*, () →* self::Struct1*>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup??] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] dylib.{ffi::DynamicLibrary::lookup}*>("function1")); + } =>ffi::_asFunctionInternal<() →* self::Struct1*, () →* self::Struct1*>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup??] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] dylib.{ffi::DynamicLibrary::lookup}*>("function1"), false); final self::Struct1* struct1 = [@vm.call-site-attributes.metadata=receiverType:#lib::Struct1* Function()*] function1(){() →* self::Struct1*}; core::print(struct1); } @@ -75,7 +75,7 @@ static method testAsFunctionReturn() → void { final ffi::Pointer*>* pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer::fromAddress*>(3735928559); final () →* self::Struct2* function2 = block { _in::_nativeEffect(new self::Struct2::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•(#C18))); - } =>ffi::_asFunctionInternal<() →* self::Struct2*, () →* self::Struct2*>(pointer); + } =>ffi::_asFunctionInternal<() →* self::Struct2*, () →* self::Struct2*>(pointer, false); final self::Struct2* struct2 = [@vm.call-site-attributes.metadata=receiverType:#lib::Struct2* Function()*] function2(){() →* self::Struct2*}; core::print(struct2); } @@ -90,12 +90,12 @@ static method testFromFunctionArgument() → void { } static method testLookupFunctionArgument() → void { final ffi::DynamicLibrary* dylib = [@vm.inferred-type.metadata=dart.ffi::DynamicLibrary?] ffi::DynamicLibrary::executable(); - final (self::Struct5*) →* void function5 = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::Struct5*) →* void, (self::Struct5*) →* ffi::Void*>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup??] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] dylib.{ffi::DynamicLibrary::lookup}*>("function5")); + final (self::Struct5*) →* void function5 = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::Struct5*) →* void, (self::Struct5*) →* ffi::Void*>([@vm.direct-call.metadata=dart.ffi::DynamicLibrary.lookup??] [@vm.inferred-type.metadata=dart.ffi::Pointer? (skip check)] dylib.{ffi::DynamicLibrary::lookup}*>("function5"), false); core::print(function5); } static method testAsFunctionArgument() → void { final ffi::Pointer*>* pointer = [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::Pointer::fromAddress*>(3735928559); - final (self::Struct6*) →* void function6 = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::Struct6*) →* void, (self::Struct6*) →* ffi::Void*>(pointer); + final (self::Struct6*) →* void function6 = [@vm.inferred-type.metadata=dart.core::_Closure?] ffi::_asFunctionInternal<(self::Struct6*) →* void, (self::Struct6*) →* ffi::Void*>(pointer, false); core::print(function6); } static method returnStruct7() → self::Struct7* { diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc index e6428191a15..87e9d662807 100644 --- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc +++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc @@ -109,6 +109,13 @@ DART_EXPORT void Regress37069(uint64_t a, Dart_ExecuteInternalCommand("gc-now", nullptr); } +DART_EXPORT uint8_t IsThreadInGenerated() { + return Dart_ExecuteInternalCommand("is-thread-in-generated", nullptr) != + nullptr + ? 1 + : 0; +} + #if !defined(HOST_OS_WINDOWS) DART_EXPORT void* UnprotectCodeOtherThread(void* isolate, std::condition_variable* var, @@ -276,6 +283,35 @@ DART_EXPORT intptr_t TestCallbackWrongIsolate(void (*fn)()) { return ExpectAbort(fn); } +DART_EXPORT intptr_t TestCallbackLeaf(void (*fn)()) { +#if defined(DEBUG) + // Calling a callback from a leaf call will crash on T->IsAtSafepoint(). + return ExpectAbort(fn); +#else + // The above will only crash in debug as ASSERTS are disabled in all other + // build modes. + return 0; +#endif +} + +void CallDebugName() { + Dart_DebugName(); +} + +DART_EXPORT intptr_t TestLeafCallApi(void (*fn)()) { + // This should be fine since it's a simple function that returns a const + // string. Though any API call should be considered unsafe from leaf calls. + Dart_VersionString(); +#if defined(DEBUG) + // This will fail because it requires running in DARTSCOPE. + return ExpectAbort(&CallDebugName); +#else + // The above will only crash in debug as ASSERTS are disabled in all other + // build modes. + return 0; +#endif +} + #endif // defined(TARGET_OS_LINUX) DART_EXPORT void IGH_MsanUnpoison(void* start, intptr_t length) { diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc index 7b1ad660f27..f9217ed0004 100644 --- a/runtime/lib/ffi.cc +++ b/runtime/lib/ffi.cc @@ -62,7 +62,7 @@ DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 3) { } // Static invocations to this method are translated directly in streaming FGB. -DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 1) { +DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 2) { UNREACHABLE(); } diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index c17daf44c0d..586c58a61e8 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -406,7 +406,7 @@ namespace dart { V(Ffi_storePointer, 3) \ V(Ffi_address, 1) \ V(Ffi_fromAddress, 1) \ - V(Ffi_asFunctionInternal, 1) \ + V(Ffi_asFunctionInternal, 2) \ V(Ffi_nativeCallbackFunction, 2) \ V(Ffi_pointerFromFunction, 1) \ V(Ffi_dl_open, 1) \ diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index 1011d8cb148..43a677c63f2 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -6435,25 +6435,31 @@ Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const { #define Z zone_ -LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, - bool is_optimizing) const { +LocationSummary* FfiCallInstr::MakeLocationSummaryInternal( + Zone* zone, + bool is_optimizing, + const Register temp) const { // The temporary register needs to be callee-saved and not an argument // register. ASSERT(((1 << CallingConventions::kFfiAnyNonAbiRegister) & CallingConventions::kArgumentRegisters) == 0); - constexpr intptr_t kNumTemps = 2; + // TODO(dartbug.com/45468): Investigate whether we can avoid spilling + // registers across ffi leaf calls by not using `kCall` here. + LocationSummary* summary = new (zone) LocationSummary( + zone, /*num_inputs=*/InputCount(), + /*num_temps=*/temp == kNoRegister ? 2 : 3, LocationSummary::kCall); - LocationSummary* summary = new (zone) - LocationSummary(zone, /*num_inputs=*/InputCount(), - /*num_temps=*/kNumTemps, LocationSummary::kCall); - - const Register temp0 = CallingConventions::kSecondNonArgumentRegister; - const Register temp1 = CallingConventions::kFfiAnyNonAbiRegister; + const Register temp0 = CallingConventions::kFfiAnyNonAbiRegister; + const Register temp1 = CallingConventions::kSecondNonArgumentRegister; ASSERT(temp0 != temp1); summary->set_temp(0, Location::RegisterLocation(temp0)); summary->set_temp(1, Location::RegisterLocation(temp1)); + if (temp != kNoRegister) { + summary->set_temp(2, Location::RegisterLocation(temp)); + } + summary->set_in(TargetAddressIndex(), Location::RegisterLocation( CallingConventions::kFirstNonArgumentRegister)); @@ -6476,14 +6482,13 @@ LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, return summary; } -void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler) { +void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler, + const Register saved_fp, + const Register temp) { if (compiler::Assembler::EmittingComments()) { __ Comment("EmitParamMoves"); } - const Register saved_fp = locs()->temp(0).reg(); - const Register temp = locs()->temp(1).reg(); - // Moves for return pointer. const auto& return_location = marshaller_.Location(compiler::ffi::kResultIndex); @@ -6591,7 +6596,9 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler) { } } -void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler) { +void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler, + const Register temp0, + const Register temp1) { __ Comment("EmitReturnMoves"); const auto& returnLocation = @@ -6611,16 +6618,17 @@ void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler) { ASSERT(returnLocation.payload_type().IsCompound()); ASSERT(marshaller_.PassTypedData()); - const Register temp0 = TMP != kNoRegister ? TMP : locs()->temp(0).reg(); - const Register temp1 = locs()->temp(1).reg(); - ASSERT(temp0 != temp1); - // Get the typed data pointer which we have pinned to a stack slot. const Location typed_data_loc = locs()->in(TypedDataIndex()); ASSERT(typed_data_loc.IsStackSlot()); ASSERT(typed_data_loc.base_reg() == FPREG); - __ LoadMemoryValue(temp0, FPREG, 0); - __ LoadMemoryValue(temp0, temp0, typed_data_loc.ToStackSlotOffset()); + // If this is a leaf call there is no extra call frame to step through. + if (is_leaf_) { + __ LoadMemoryValue(temp0, FPREG, typed_data_loc.ToStackSlotOffset()); + } else { + __ LoadMemoryValue(temp0, FPREG, 0); + __ LoadMemoryValue(temp0, temp0, typed_data_loc.ToStackSlotOffset()); + } __ LoadField( temp0, compiler::FieldAddress( diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index 12baaef86cb..e5800c4580e 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -5106,12 +5106,14 @@ class FfiCallInstr : public Definition { public: FfiCallInstr(Zone* zone, intptr_t deopt_id, - const compiler::ffi::CallMarshaller& marshaller) + const compiler::ffi::CallMarshaller& marshaller, + bool is_leaf) : Definition(deopt_id), zone_(zone), marshaller_(marshaller), inputs_(marshaller.NumDefinitions() + 1 + - (marshaller.PassTypedData() ? 1 : 0)) { + (marshaller.PassTypedData() ? 1 : 0)), + is_leaf_(is_leaf) { inputs_.FillWith( nullptr, 0, marshaller.NumDefinitions() + 1 + (marshaller.PassTypedData() ? 1 : 0)); @@ -5162,14 +5164,27 @@ class FfiCallInstr : public Definition { private: virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } - void EmitParamMoves(FlowGraphCompiler* compiler); - void EmitReturnMoves(FlowGraphCompiler* compiler); + LocationSummary* MakeLocationSummaryInternal(Zone* zone, + bool is_optimizing, + const Register temp) const; + + // Clobbers both given registers. + // `saved_fp` is used as the frame base to rebase off of. + void EmitParamMoves(FlowGraphCompiler* compiler, + const Register saved_fp, + const Register temp); + // Clobbers both given temp registers. + void EmitReturnMoves(FlowGraphCompiler* compiler, + const Register temp0, + const Register temp1); Zone* const zone_; const compiler::ffi::CallMarshaller& marshaller_; GrowableArray inputs_; + bool is_leaf_; + DISALLOW_COPY_AND_ASSIGN(FfiCallInstr); }; diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc index 6add21d34a3..2358f4e6d71 100644 --- a/runtime/vm/compiler/backend/il_arm.cc +++ b/runtime/vm/compiler/backend/il_arm.cc @@ -1402,85 +1402,114 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Drop(ArgumentCount()); // Drop the arguments. } +LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, + bool is_optimizing) const { + return MakeLocationSummaryInternal(zone, is_optimizing, R0); +} + void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - const Register saved_fp = locs()->temp(0).reg(); - const Register temp = locs()->temp(1).reg(); + // For regular calls, this holds the FP for rebasing the original locations + // during EmitParamMoves. + // For leaf calls, this holds the SP used to restore the pre-aligned SP after + // the call. + const Register saved_fp_or_sp = locs()->temp(0).reg(); + RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters & + (1 << saved_fp_or_sp)) != 0); + const Register temp1 = locs()->temp(1).reg(); + const Register temp2 = locs()->temp(2).reg(); const Register branch = locs()->in(TargetAddressIndex()).reg(); - // Save frame pointer because we're going to update it when we enter the exit - // frame. - __ mov(saved_fp, compiler::Operand(FPREG)); + // Ensure these are callee-saved register and are preserved across the call. + ASSERT((CallingConventions::kCalleeSaveCpuRegisters & + (1 << saved_fp_or_sp)) != 0); + // temp doesn't need to be preserved. - // Make a space to put the return address. - __ PushImmediate(0); + __ mov(saved_fp_or_sp, + is_leaf_ ? compiler::Operand(SPREG) : compiler::Operand(FPREG)); - // We need to create a dummy "exit frame". It will have a null code object. - __ LoadObject(CODE_REG, Object::null_object()); - __ set_constant_pool_allowed(false); - __ EnterDartFrame(0, /*load_pool_pointer=*/false); + if (!is_leaf_) { + // Make a space to put the return address. + __ PushImmediate(0); - // Reserve space for arguments and align frame before entering C++ world. + // We need to create a dummy "exit frame". It will have a null code object. + __ LoadObject(CODE_REG, Object::null_object()); + __ set_constant_pool_allowed(false); + __ EnterDartFrame(0, /*load_pool_pointer=*/false); + } + + // Reserve space for the arguments that go on the stack (if any), then align. __ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes()); - EmitParamMoves(compiler); + EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1); if (compiler::Assembler::EmittingComments()) { - __ Comment("Call"); + __ Comment(is_leaf_ ? "Leaf Call" : "Call"); } - // We need to copy the return address up into the dummy stack frame so the - // stack walker will know which safepoint to use. - __ mov(TMP, compiler::Operand(PC)); - __ str(TMP, compiler::Address(FPREG, kSavedCallerPcSlotFromFp * - compiler::target::kWordSize)); - - // For historical reasons, the PC on ARM points 8 bytes past the current - // instruction. Therefore we emit the metadata here, 8 bytes (2 instructions) - // after the original mov. - compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(), - UntaggedPcDescriptors::Kind::kOther, locs(), - env()); - - // Update information in the thread object and enter a safepoint. - if (CanExecuteGeneratedCodeInSafepoint()) { - __ LoadImmediate(temp, compiler::target::Thread::exit_through_ffi()); - __ TransitionGeneratedToNative(branch, FPREG, temp, saved_fp, - /*enter_safepoint=*/true); + if (is_leaf_) { __ blx(branch); - - // Update information in the thread object and leave the safepoint. - __ TransitionNativeToGenerated(saved_fp, temp, /*leave_safepoint=*/true); } else { - // We cannot trust that this code will be executable within a safepoint. - // Therefore we delegate the responsibility of entering/exiting the - // safepoint to a stub which in the VM isolate's heap, which will never lose - // execute permission. - __ ldr(TMP, - compiler::Address( - THR, compiler::target::Thread:: - call_native_through_safepoint_entry_point_offset())); + // We need to copy the return address up into the dummy stack frame so the + // stack walker will know which safepoint to use. + __ mov(temp1, compiler::Operand(PC)); + __ str(temp1, compiler::Address(FPREG, kSavedCallerPcSlotFromFp * + compiler::target::kWordSize)); - // Calls R8 in a safepoint and clobbers R4 and NOTFP. - ASSERT(branch == R8 && temp == R4); - static_assert((kReservedCpuRegisters & (1 << NOTFP)) != 0, - "NOTFP should be a reserved register"); - __ blx(TMP); + // For historical reasons, the PC on ARM points 8 bytes past the current + // instruction. Therefore we emit the metadata here, 8 bytes + // (2 instructions) after the original mov. + compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(), + UntaggedPcDescriptors::Kind::kOther, locs(), + env()); + + // Update information in the thread object and enter a safepoint. + if (CanExecuteGeneratedCodeInSafepoint()) { + __ LoadImmediate(temp1, compiler::target::Thread::exit_through_ffi()); + __ TransitionGeneratedToNative(branch, FPREG, temp1, saved_fp_or_sp, + /*enter_safepoint=*/true); + + __ blx(branch); + + // Update information in the thread object and leave the safepoint. + __ TransitionNativeToGenerated(saved_fp_or_sp, temp1, + /*leave_safepoint=*/true); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never + // lose execute permission. + __ ldr(temp1, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls R8 in a safepoint and clobbers R4 and NOTFP. + ASSERT(branch == R8); + static_assert((kReservedCpuRegisters & (1 << NOTFP)) != 0, + "NOTFP should be a reserved register"); + __ blx(temp1); + } + + // Restore the global object pool after returning from runtime (old space is + // moving, so the GOP could have been relocated). + if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { + __ SetupGlobalPoolAndDispatchTable(); + } } - // Restore the global object pool after returning from runtime (old space is - // moving, so the GOP could have been relocated). - if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { - __ SetupGlobalPoolAndDispatchTable(); + EmitReturnMoves(compiler, temp1, temp2); + + if (is_leaf_) { + // Restore the pre-aligned SP. + __ mov(SPREG, compiler::Operand(saved_fp_or_sp)); + } else { + // Leave dummy exit frame. + __ LeaveDartFrame(); + __ set_constant_pool_allowed(true); + + // Instead of returning to the "fake" return address, we just pop it. + __ PopRegister(temp1); } - - EmitReturnMoves(compiler); - - // Leave dummy exit frame. - __ LeaveDartFrame(); - __ set_constant_pool_allowed(true); - - // Instead of returning to the "fake" return address, we just pop it. - __ PopRegister(TMP); } // Keep in sync with NativeEntryInstr::EmitNativeCode. diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc index c6e7064e7ab..9f3bfa7f2f0 100644 --- a/runtime/vm/compiler/backend/il_arm64.cc +++ b/runtime/vm/compiler/backend/il_arm64.cc @@ -1228,46 +1228,48 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Drop(ArgumentCount()); // Drop the arguments. } +LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, + bool is_optimizing) const { + return MakeLocationSummaryInternal(zone, is_optimizing, R11); +} + void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - const Register saved_fp = locs()->temp(0).reg(); - const Register temp = locs()->temp(1).reg(); + // For regular calls, this holds the FP for rebasing the original locations + // during EmitParamMoves. + // For leaf calls, this holds the SP used to restore the pre-aligned SP after + // the call. + const Register saved_fp_or_sp = locs()->temp(0).reg(); + RELEASE_ASSERT((CallingConventions::kCalleeSaveCpuRegisters & + (1 << saved_fp_or_sp)) != 0); + const Register temp1 = locs()->temp(1).reg(); + const Register temp2 = locs()->temp(2).reg(); const Register branch = locs()->in(TargetAddressIndex()).reg(); - // Save frame pointer because we're going to update it when we enter the exit - // frame. - __ mov(saved_fp, FPREG); + // Ensure these are callee-saved register and are preserved across the call. + ASSERT((CallingConventions::kCalleeSaveCpuRegisters & + (1 << saved_fp_or_sp)) != 0); + // temps don't need to be preserved. - // We need to create a dummy "exit frame". It will share the same pool pointer - // but have a null code object. - __ LoadObject(CODE_REG, Object::null_object()); - __ set_constant_pool_allowed(false); - __ EnterDartFrame(0, PP); + __ mov(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG); - // Make space for arguments and align the frame. + if (!is_leaf_) { + // We need to create a dummy "exit frame". It will share the same pool + // pointer but have a null code object. + __ LoadObject(CODE_REG, Object::null_object()); + __ set_constant_pool_allowed(false); + __ EnterDartFrame(0, PP); + } + + // Reserve space for the arguments that go on the stack (if any), then align. __ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes()); - EmitParamMoves(compiler); + EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1); if (compiler::Assembler::EmittingComments()) { - __ Comment("Call"); + __ Comment(is_leaf_ ? "Leaf Call" : "Call"); } - // We need to copy a dummy return address up into the dummy stack frame so the - // stack walker will know which safepoint to use. - // - // ADR loads relative to itself, so add kInstrSize to point to the next - // instruction. - __ adr(temp, compiler::Immediate(Instr::kInstrSize)); - compiler->EmitCallsiteMetadata( - source(), deopt_id(), UntaggedPcDescriptors::Kind::kOther, locs(), env()); - - __ StoreToOffset(temp, FPREG, kSavedCallerPcSlotFromFp * kWordSize); - - if (CanExecuteGeneratedCodeInSafepoint()) { - // Update information in the thread object and enter a safepoint. - __ LoadImmediate(temp, compiler::target::Thread::exit_through_ffi()); - __ TransitionGeneratedToNative(branch, FPREG, temp, - /*enter_safepoint=*/true); + if (is_leaf_) { // We are entering runtime code, so the C stack pointer must be restored // from the stack limit to the top of the stack. __ mov(R25, CSP); @@ -1278,39 +1280,75 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { // Restore the Dart stack pointer. __ mov(SP, CSP); __ mov(CSP, R25); - - // Update information in the thread object and leave the safepoint. - __ TransitionNativeToGenerated(temp, /*leave_safepoint=*/true); } else { - // We cannot trust that this code will be executable within a safepoint. - // Therefore we delegate the responsibility of entering/exiting the - // safepoint to a stub which in the VM isolate's heap, which will never lose - // execute permission. - __ ldr(TMP, - compiler::Address( - THR, compiler::target::Thread:: - call_native_through_safepoint_entry_point_offset())); + // We need to copy a dummy return address up into the dummy stack frame so + // the stack walker will know which safepoint to use. + // + // ADR loads relative to itself, so add kInstrSize to point to the next + // instruction. + __ adr(temp1, compiler::Immediate(Instr::kInstrSize)); + compiler->EmitCallsiteMetadata(source(), deopt_id(), + UntaggedPcDescriptors::Kind::kOther, locs(), + env()); - // Calls R9 and clobbers R19 (along with volatile registers). - ASSERT(branch == R9 && temp == R19); - __ blr(TMP); + __ StoreToOffset(temp1, FPREG, kSavedCallerPcSlotFromFp * kWordSize); + + if (CanExecuteGeneratedCodeInSafepoint()) { + // Update information in the thread object and enter a safepoint. + __ LoadImmediate(temp1, compiler::target::Thread::exit_through_ffi()); + __ TransitionGeneratedToNative(branch, FPREG, temp1, + /*enter_safepoint=*/true); + + // We are entering runtime code, so the C stack pointer must be restored + // from the stack limit to the top of the stack. + __ mov(R25, CSP); + __ mov(CSP, SP); + + __ blr(branch); + + // Restore the Dart stack pointer. + __ mov(SP, CSP); + __ mov(CSP, R25); + + // Update information in the thread object and leave the safepoint. + __ TransitionNativeToGenerated(temp1, /*leave_safepoint=*/true); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never + // lose execute permission. + __ ldr(temp1, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls R9 and clobbers R19 (along with volatile registers). + ASSERT(branch == R9); + __ blr(temp1); + } + + // Refresh pinned registers values (inc. write barrier mask and null + // object). + __ RestorePinnedRegisters(); } - // Refresh pinned registers values (inc. write barrier mask and null object). - __ RestorePinnedRegisters(); + EmitReturnMoves(compiler, temp1, temp2); - EmitReturnMoves(compiler); + if (is_leaf_) { + // Restore the pre-aligned SP. + __ mov(SPREG, saved_fp_or_sp); + } else { + // Although PP is a callee-saved register, it may have been moved by the GC. + __ LeaveDartFrame(compiler::kRestoreCallerPP); - // Although PP is a callee-saved register, it may have been moved by the GC. - __ LeaveDartFrame(compiler::kRestoreCallerPP); + // Restore the global object pool after returning from runtime (old space is + // moving, so the GOP could have been relocated). + if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { + __ SetupGlobalPoolAndDispatchTable(); + } - // Restore the global object pool after returning from runtime (old space is - // moving, so the GOP could have been relocated). - if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { - __ SetupGlobalPoolAndDispatchTable(); + __ set_constant_pool_allowed(true); } - - __ set_constant_pool_allowed(true); } // Keep in sync with NativeEntryInstr::EmitNativeCode. diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc index 045490ac5ee..f2373092a9a 100644 --- a/runtime/vm/compiler/backend/il_ia32.cc +++ b/runtime/vm/compiler/backend/il_ia32.cc @@ -999,57 +999,88 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Drop(ArgumentCount()); // Drop the arguments. } +LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, + bool is_optimizing) const { + return MakeLocationSummaryInternal(zone, is_optimizing, kNoRegister); +} + void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - const Register saved_fp = locs()->temp(0).reg(); + // For regular calls, this holds the FP for rebasing the original locations + // during EmitParamMoves. + // For leaf calls, this holds the SP used to restore the pre-aligned SP after + // the call. + const Register saved_fp_or_sp = locs()->temp(0).reg(); const Register temp = locs()->temp(1).reg(); const Register branch = locs()->in(TargetAddressIndex()).reg(); - // Save frame pointer because we're going to update it when we enter the exit - // frame. - __ movl(saved_fp, FPREG); + // Ensure these are callee-saved register and are preserved across the call. + ASSERT((CallingConventions::kCalleeSaveCpuRegisters & + (1 << saved_fp_or_sp)) != 0); + // temp doesn't need to be preserved. - // Make a space to put the return address. - __ pushl(compiler::Immediate(0)); + __ movl(saved_fp_or_sp, is_leaf_ ? SPREG : FPREG); - // We need to create a dummy "exit frame". It will have a null code object. - __ LoadObject(CODE_REG, Object::null_object()); - __ EnterDartFrame(marshaller_.RequiredStackSpaceInBytes()); + intptr_t stack_required = marshaller_.RequiredStackSpaceInBytes(); - // Align frame before entering C++ world. - if (OS::ActivationFrameAlignment() > 1) { - __ andl(SPREG, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1))); + if (is_leaf_) { + // For leaf calls we need to leave space at the bottom for the pre-align SP. + stack_required += compiler::target::kWordSize; + } else { + // Make a space to put the return address. + __ pushl(compiler::Immediate(0)); + + // We need to create a dummy "exit frame". It will have a null code object. + __ LoadObject(CODE_REG, Object::null_object()); + __ EnterDartFrame(0); } - EmitParamMoves(compiler); + // Reserve space for the arguments that go on the stack (if any), then align. + __ ReserveAlignedFrameSpace(stack_required); + + EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp); + + if (is_leaf_) { + // We store the pre-align SP at a fixed offset from the final SP. + // Pushing before alignment would mean its placement would vary with how + // much the frame was unaligned. + __ movl(compiler::Address(SPREG, marshaller_.RequiredStackSpaceInBytes()), + saved_fp_or_sp); + } if (compiler::Assembler::EmittingComments()) { - __ Comment("Call"); + __ Comment(is_leaf_ ? "Leaf Call" : "Call"); } - // We need to copy a dummy return address up into the dummy stack frame so the - // stack walker will know which safepoint to use. Unlike X64, there's no - // PC-relative 'leaq' available, so we have do a trick with 'call'. - compiler::Label get_pc; - __ call(&get_pc); - compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(), - UntaggedPcDescriptors::Kind::kOther, locs(), - env()); - __ Bind(&get_pc); - __ popl(temp); - __ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), temp); - ASSERT(!CanExecuteGeneratedCodeInSafepoint()); - // We cannot trust that this code will be executable within a safepoint. - // Therefore we delegate the responsibility of entering/exiting the - // safepoint to a stub which in the VM isolate's heap, which will never lose - // execute permission. - __ movl(temp, - compiler::Address( - THR, compiler::target::Thread:: - call_native_through_safepoint_entry_point_offset())); + if (is_leaf_) { + __ call(branch); + } else { + // We need to copy a dummy return address up into the dummy stack frame so + // the stack walker will know which safepoint to use. Unlike X64, there's no + // PC-relative 'leaq' available, so we have do a trick with 'call'. + compiler::Label get_pc; + __ call(&get_pc); + compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(), + UntaggedPcDescriptors::Kind::kOther, locs(), + env()); + __ Bind(&get_pc); + __ popl(temp); + __ movl(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), + temp); - // Calls EAX within a safepoint and clobbers EBX. - ASSERT(temp == EBX && branch == EAX); - __ call(temp); + ASSERT(!CanExecuteGeneratedCodeInSafepoint()); + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which in the VM isolate's heap, which will never lose + // execute permission. + __ movl(temp, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls EAX within a safepoint and clobbers EBX. + ASSERT(branch == EAX); + __ call(temp); + } // Restore the stack when a struct by value is returned into memory pointed // to by a pointer that is passed into the function. @@ -1061,10 +1092,10 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ subl(SPREG, compiler::Immediate(compiler::target::kWordSize)); } - // The x86 calling convention requires floating point values to be returned on - // the "floating-point stack" (aka. register ST0). We don't use the - // floating-point stack in Dart, so we need to move the return value back into - // an XMM register. + // The x86 calling convention requires floating point values to be returned + // on the "floating-point stack" (aka. register ST0). We don't use the + // floating-point stack in Dart, so we need to move the return value back + // into an XMM register. if (representation() == kUnboxedDouble) { __ fstpl(compiler::Address(SPREG, -kDoubleSize)); __ movsd(XMM0, compiler::Address(SPREG, -kDoubleSize)); @@ -1073,13 +1104,20 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ movss(XMM0, compiler::Address(SPREG, -kFloatSize)); } - EmitReturnMoves(compiler); + // Pass both registers for use as clobbered temp registers. + EmitReturnMoves(compiler, saved_fp_or_sp, temp); - // Leave dummy exit frame. - __ LeaveFrame(); + if (is_leaf_) { + // Restore pre-align SP. Was stored right before the first stack argument. + __ movl(SPREG, + compiler::Address(SPREG, marshaller_.RequiredStackSpaceInBytes())); + } else { + // Leave dummy exit frame. + __ LeaveFrame(); - // Instead of returning to the "fake" return address, we just pop it. - __ popl(temp); + // Instead of returning to the "fake" return address, we just pop it. + __ popl(temp); + } } // Keep in sync with NativeReturnInstr::EmitNativeCode. diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc index 8e6d3e9bbfc..43930b02d0f 100644 --- a/runtime/vm/compiler/backend/il_x64.cc +++ b/runtime/vm/compiler/backend/il_x64.cc @@ -1188,82 +1188,118 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Drop(ArgumentCount()); // Drop the arguments. } +LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone, + bool is_optimizing) const { + // Use R10 as a temp register. We can't use RDI, RSI, RDX, R8, R9 as they are + // arg registers, and R11 is TMP. + return MakeLocationSummaryInternal(zone, is_optimizing, R10); +} + void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { - const Register saved_fp = locs()->temp(0).reg(); + // For leaf calls, this holds the SP used to restore the pre-aligned SP after + // the call. + // Note: R12 doubles as CODE_REG, which gets clobbered during frame setup in + // regular calls. + const Register saved_sp = locs()->temp(0).reg(); + // For regular calls, this holds the FP for rebasing the original locations + // during EmitParamMoves. + const Register saved_fp = locs()->temp(1).reg(); + const Register temp = locs()->temp(2).reg(); const Register target_address = locs()->in(TargetAddressIndex()).reg(); - // Save frame pointer because we're going to update it when we enter the exit - // frame. - __ movq(saved_fp, FPREG); + // Ensure these are callee-saved register and are preserved across the call. + ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << saved_sp)) != 0); + ASSERT((CallingConventions::kCalleeSaveCpuRegisters & (1 << saved_fp)) != 0); + // temp doesn't need to be preserved. - // Make a space to put the return address. - __ pushq(compiler::Immediate(0)); - - // We need to create a dummy "exit frame". It will share the same pool pointer - // but have a null code object. - __ LoadObject(CODE_REG, Object::null_object()); - __ set_constant_pool_allowed(false); - __ EnterDartFrame(marshaller_.RequiredStackSpaceInBytes(), PP); - - // Align frame before entering C++ world. - if (OS::ActivationFrameAlignment() > 1) { - __ andq(SPREG, compiler::Immediate(~(OS::ActivationFrameAlignment() - 1))); - } - - EmitParamMoves(compiler); - - // We need to copy a dummy return address up into the dummy stack frame so the - // stack walker will know which safepoint to use. RIP points to the *next* - // instruction, so 'AddressRIPRelative' loads the address of the following - // 'movq'. - __ leaq(TMP, compiler::Address::AddressRIPRelative(0)); - compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(), - UntaggedPcDescriptors::Kind::kOther, locs(), - env()); - __ movq(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), TMP); - - if (CanExecuteGeneratedCodeInSafepoint()) { - // Update information in the thread object and enter a safepoint. - __ movq(TMP, - compiler::Immediate(compiler::target::Thread::exit_through_ffi())); - __ TransitionGeneratedToNative(target_address, FPREG, TMP, - /*enter_safepoint=*/true); - - __ CallCFunction(target_address, /*restore_rsp=*/true); - - // Update information in the thread object and leave the safepoint. - __ TransitionNativeToGenerated(/*leave_safepoint=*/true); + if (is_leaf_) { + __ movq(saved_sp, SPREG); } else { - // We cannot trust that this code will be executable within a safepoint. - // Therefore we delegate the responsibility of entering/exiting the - // safepoint to a stub which in the VM isolate's heap, which will never lose - // execute permission. - __ movq(TMP, - compiler::Address( - THR, compiler::target::Thread:: - call_native_through_safepoint_entry_point_offset())); + __ movq(saved_fp, FPREG); + // Make a space to put the return address. + __ pushq(compiler::Immediate(0)); - // Calls RBX within a safepoint. - ASSERT(saved_fp == RBX); - __ movq(RBX, target_address); - __ call(TMP); + // We need to create a dummy "exit frame". It will share the same pool + // pointer but have a null code object. + __ LoadObject(CODE_REG, Code::null_object()); + __ set_constant_pool_allowed(false); + __ EnterDartFrame(0, PP); } - EmitReturnMoves(compiler); + // Reserve space for the arguments that go on the stack (if any), then align. + __ ReserveAlignedFrameSpace(marshaller_.RequiredStackSpaceInBytes()); - // Although PP is a callee-saved register, it may have been moved by the GC. - __ LeaveDartFrame(compiler::kRestoreCallerPP); - - // Restore the global object pool after returning from runtime (old space is - // moving, so the GOP could have been relocated). - if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { - __ movq(PP, compiler::Address(THR, Thread::global_object_pool_offset())); + if (is_leaf_) { + EmitParamMoves(compiler, FPREG, saved_fp); + } else { + EmitParamMoves(compiler, saved_fp, saved_sp); } - __ set_constant_pool_allowed(true); + if (compiler::Assembler::EmittingComments()) { + __ Comment(is_leaf_ ? "Leaf Call" : "Call"); + } - // Instead of returning to the "fake" return address, we just pop it. - __ popq(TMP); + if (is_leaf_) { + __ CallCFunction(target_address, /*restore_rsp=*/true); + } else { + // We need to copy a dummy return address up into the dummy stack frame so + // the stack walker will know which safepoint to use. RIP points to the + // *next* instruction, so 'AddressRIPRelative' loads the address of the + // following 'movq'. + __ leaq(temp, compiler::Address::AddressRIPRelative(0)); + compiler->EmitCallsiteMetadata(InstructionSource(), deopt_id(), + UntaggedPcDescriptors::Kind::kOther, locs(), + env()); + __ movq(compiler::Address(FPREG, kSavedCallerPcSlotFromFp * kWordSize), + temp); + + if (CanExecuteGeneratedCodeInSafepoint()) { + // Update information in the thread object and enter a safepoint. + __ movq(temp, compiler::Immediate( + compiler::target::Thread::exit_through_ffi())); + + __ TransitionGeneratedToNative(target_address, FPREG, temp, + /*enter_safepoint=*/true); + + __ CallCFunction(target_address, /*restore_rsp=*/true); + + // Update information in the thread object and leave the safepoint. + __ TransitionNativeToGenerated(/*leave_safepoint=*/true); + } else { + // We cannot trust that this code will be executable within a safepoint. + // Therefore we delegate the responsibility of entering/exiting the + // safepoint to a stub which is in the VM isolate's heap, which will never + // lose execute permission. + __ movq(temp, + compiler::Address( + THR, compiler::target::Thread:: + call_native_through_safepoint_entry_point_offset())); + + // Calls RBX within a safepoint. RBX and R12 are clobbered. + __ movq(RBX, target_address); + __ call(temp); + } + } + + // Pass the `saved_fp` reg. as a temp to clobber since we're done with it. + EmitReturnMoves(compiler, temp, saved_fp); + + if (is_leaf_) { + // Restore the pre-aligned SP. + __ movq(SPREG, saved_sp); + } else { + // Although PP is a callee-saved register, it may have been moved by the GC. + __ LeaveDartFrame(compiler::kRestoreCallerPP); + // Restore the global object pool after returning from runtime (old space is + // moving, so the GOP could have been relocated). + if (FLAG_precompiled_mode && FLAG_use_bare_instructions) { + __ movq(PP, compiler::Address(THR, Thread::global_object_pool_offset())); + } + __ set_constant_pool_allowed(true); + + // Instead of returning to the "fake" return address, we just pop it. + __ popq(temp); + } } // Keep in sync with NativeReturnInstr::EmitNativeCode. diff --git a/runtime/vm/compiler/ffi/call.cc b/runtime/vm/compiler/ffi/call.cc index 1735a6949fa..990e257122d 100644 --- a/runtime/vm/compiler/ffi/call.cc +++ b/runtime/vm/compiler/ffi/call.cc @@ -15,7 +15,8 @@ namespace ffi { // TODO(dartbug.com/36607): Cache the trampolines. FunctionPtr TrampolineFunction(const FunctionType& dart_signature, - const FunctionType& c_signature) { + const FunctionType& c_signature, + bool is_leaf) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); String& name = String::Handle(zone, Symbols::New(thread, "FfiTrampoline")); @@ -54,6 +55,8 @@ FunctionPtr TrampolineFunction(const FunctionType& dart_signature, signature ^= ClassFinalizer::FinalizeType(signature); function.set_signature(signature); + function.SetFfiIsLeaf(is_leaf); + return function.ptr(); } diff --git a/runtime/vm/compiler/ffi/call.h b/runtime/vm/compiler/ffi/call.h index 5a4e5f0b65b..06bccde1515 100644 --- a/runtime/vm/compiler/ffi/call.h +++ b/runtime/vm/compiler/ffi/call.h @@ -20,7 +20,8 @@ namespace compiler { namespace ffi { FunctionPtr TrampolineFunction(const FunctionType& dart_signature, - const FunctionType& c_signature); + const FunctionType& c_signature, + bool is_leaf); } // namespace ffi diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc index 52416d72961..9e3e05fe7b6 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc @@ -980,7 +980,8 @@ Fragment BaseFlowGraphBuilder::Box(Representation from) { } Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall( - const TypeArguments& signatures) { + const TypeArguments& signatures, + bool is_leaf) { ASSERT(signatures.IsInstantiated()); ASSERT(signatures.Length() == 2); @@ -990,7 +991,8 @@ Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall( ASSERT(dart_type.IsFunctionType() && native_type.IsFunctionType()); const Function& target = Function::ZoneHandle(compiler::ffi::TrampolineFunction( - FunctionType::Cast(dart_type), FunctionType::Cast(native_type))); + FunctionType::Cast(dart_type), FunctionType::Cast(native_type), + is_leaf)); Fragment code; // Store the pointer in the context, we cannot load the untagged address diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h index b2d1cfef0ae..6873a4fce83 100644 --- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h +++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h @@ -362,7 +362,8 @@ class BaseFlowGraphBuilder { // Builds the graph for an invocation of '_asFunctionInternal'. // // 'signatures' contains the pair [, ]. - Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures); + Fragment BuildFfiAsFunctionInternalCall(const TypeArguments& signatures, + bool is_leaf); Fragment AllocateObject(TokenPosition position, const Class& klass, diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index 1ff1ed39ae6..5c94964f66b 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -5667,20 +5667,32 @@ Fragment StreamingFlowGraphBuilder::BuildNativeEffect() { Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() { const intptr_t argc = ReadUInt(); // Read argument count. - ASSERT(argc == 1); // Pointer. + ASSERT(argc == 2); // Pointer, isLeaf. const intptr_t list_length = ReadListLength(); // Read types list length. - ASSERT(list_length == 2); // Dart signature, then native signature. - const TypeArguments& type_arguments = - T.BuildTypeArguments(list_length); // Read types. + ASSERT(list_length == 2); // Dart signature, then native signature + // Read types. + const TypeArguments& type_arguments = T.BuildTypeArguments(list_length); Fragment code; - const intptr_t positional_count = - ReadListLength(); // Read positional argument count. - ASSERT(positional_count == 1); + // Read positional argument count. + const intptr_t positional_count = ReadListLength(); + ASSERT(positional_count == 2); code += BuildExpression(); // Build first positional argument (pointer). - const intptr_t named_args_len = - ReadListLength(); // Skip empty named arguments list. + + // The second argument, `isLeaf`, is only used internally and dictates whether + // we can do a lightweight leaf function call. + bool is_leaf = false; + Fragment frag = BuildExpression(); + ASSERT(frag.entry->IsConstant()); + if (frag.entry->AsConstant()->value().ptr() == Object::bool_true().ptr()) { + is_leaf = true; + } + Pop(); + + // Skip (empty) named arguments list. + const intptr_t named_args_len = ReadListLength(); ASSERT(named_args_len == 0); - code += B->BuildFfiAsFunctionInternalCall(type_arguments); + + code += B->BuildFfiAsFunctionInternalCall(type_arguments, is_leaf); return code; } diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc index a12040d7d39..ff70644a1f3 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -384,7 +384,8 @@ Fragment FlowGraphBuilder::FfiCall( Fragment body; FfiCallInstr* const call = - new (Z) FfiCallInstr(Z, GetNextDeoptId(), marshaller); + new (Z) FfiCallInstr(Z, GetNextDeoptId(), marshaller, + parsed_function_->function().FfiIsLeaf()); for (intptr_t i = call->InputCount() - 1; i >= 0; --i) { call->SetInputAt(i, Pop()); diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h index a36b5d4fcfa..58d8c8b0c11 100644 --- a/runtime/vm/compiler/recognized_methods_list.h +++ b/runtime/vm/compiler/recognized_methods_list.h @@ -164,7 +164,7 @@ namespace dart { V(_WeakProperty, set:value, WeakProperty_setValue, 0x8b2bafab) \ V(::, _classRangeCheck, ClassRangeCheck, 0x00269620) \ V(::, _abi, FfiAbi, 0x7c4ab775) \ - V(::, _asFunctionInternal, FfiAsFunctionInternal, 0xbbcb235a) \ + V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x92ae104f) \ V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3ff5ae9c) \ V(::, _nativeEffect, NativeEffect, 0x61e00b59) \ V(::, _loadInt8, FfiLoadInt8, 0x0f04dfd6) \ diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h index 95c2e896aef..6862f50998e 100644 --- a/runtime/vm/compiler/runtime_offsets_extracted.h +++ b/runtime/vm/compiler/runtime_offsets_extracted.h @@ -489,7 +489,7 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize = 12; static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = - 24; + 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; @@ -1569,7 +1569,7 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize = 12; static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = - 24; + 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; @@ -3738,7 +3738,7 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize = 12; static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = - 24; + 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; @@ -4806,7 +4806,7 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word ExternalTypedData_InstanceSize = 12; static constexpr dart::compiler::target::word FfiTrampolineData_InstanceSize = - 24; + 28; static constexpr dart::compiler::target::word Field_InstanceSize = 60; static constexpr dart::compiler::target::word Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word Float64x2_InstanceSize = 24; @@ -7018,7 +7018,7 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_ExternalTypedData_InstanceSize = 12; static constexpr dart::compiler::target::word - AOT_FfiTrampolineData_InstanceSize = 24; + AOT_FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; @@ -10049,7 +10049,7 @@ static constexpr dart::compiler::target::word static constexpr dart::compiler::target::word AOT_ExternalTypedData_InstanceSize = 12; static constexpr dart::compiler::target::word - AOT_FfiTrampolineData_InstanceSize = 24; + AOT_FfiTrampolineData_InstanceSize = 28; static constexpr dart::compiler::target::word AOT_Field_InstanceSize = 48; static constexpr dart::compiler::target::word AOT_Float32x4_InstanceSize = 24; static constexpr dart::compiler::target::word AOT_Float64x2_InstanceSize = 24; diff --git a/runtime/vm/native_api_impl.cc b/runtime/vm/native_api_impl.cc index 8914904b35e..20f7748e798 100644 --- a/runtime/vm/native_api_impl.cc +++ b/runtime/vm/native_api_impl.cc @@ -246,6 +246,12 @@ DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg) { IsolateGroup::Current()->heap()->CollectAllGarbage(); return nullptr; + } else if (strcmp(command, "is-thread-in-generated") == 0) { + if (Thread::Current()->execution_state() == Thread::kThreadInGenerated) { + return reinterpret_cast(1); + } + return nullptr; + } else if (strcmp(command, "is-mutator-in-native") == 0) { Isolate* const isolate = reinterpret_cast(arg); if (isolate->mutator_thread()->execution_state_cross_thread_for_testing() == diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 7a611843427..bdef06a71c9 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -7535,6 +7535,20 @@ void Function::SetFfiCallbackId(int32_t value) const { FfiTrampolineData::Cast(obj).set_callback_id(value); } +bool Function::FfiIsLeaf() const { + ASSERT(IsFfiTrampoline()); + const Object& obj = Object::Handle(untag()->data()); + ASSERT(!obj.IsNull()); + return FfiTrampolineData::Cast(obj).is_leaf(); +} + +void Function::SetFfiIsLeaf(bool is_leaf) const { + ASSERT(IsFfiTrampoline()); + const Object& obj = Object::Handle(untag()->data()); + ASSERT(!obj.IsNull()); + FfiTrampolineData::Cast(obj).set_is_leaf(is_leaf); +} + FunctionPtr Function::FfiCallbackTarget() const { ASSERT(IsFfiTrampoline()); const Object& obj = Object::Handle(data()); @@ -10295,6 +10309,10 @@ void FfiTrampolineData::set_callback_id(int32_t callback_id) const { StoreNonPointer(&untag()->callback_id_, callback_id); } +void FfiTrampolineData::set_is_leaf(bool is_leaf) const { + StoreNonPointer(&untag()->is_leaf_, is_leaf); +} + void FfiTrampolineData::set_callback_exceptional_return( const Instance& value) const { untag()->set_callback_exceptional_return(value.ptr()); @@ -10307,6 +10325,7 @@ FfiTrampolineDataPtr FfiTrampolineData::New() { Heap::kOld, FfiTrampolineData::ContainsCompressedPointers()); FfiTrampolineDataPtr data = static_cast(raw); data->untag()->callback_id_ = 0; + data->untag()->is_leaf_ = false; return data; } diff --git a/runtime/vm/object.h b/runtime/vm/object.h index a04f9b1b3d4..846155236f7 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -2554,6 +2554,12 @@ class Function : public Object { // Can only be called on FFI trampolines. void SetFfiCallbackId(int32_t value) const; + // Can only be called on FFI trampolines. + bool FfiIsLeaf() const; + + // Can only be called on FFI trampolines. + void SetFfiIsLeaf(bool is_leaf) const; + // Can only be called on FFI trampolines. // Null for Dart -> native calls. FunctionPtr FfiCallbackTarget() const; @@ -3869,6 +3875,9 @@ class FfiTrampolineData : public Object { int32_t callback_id() const { return untag()->callback_id_; } void set_callback_id(int32_t value) const; + bool is_leaf() const { return untag()->is_leaf_; } + void set_is_leaf(bool value) const; + static FfiTrampolineDataPtr New(); FINAL_HEAP_OBJECT_IMPLEMENTATION(FfiTrampolineData, Object); diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index acff79d27f2..f567ae72a32 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -1463,6 +1463,9 @@ class UntaggedFfiTrampolineData : public UntaggedObject { // Will be 0 for non-callbacks. Check 'callback_target_' to determine if this // is a callback or not. uint32_t callback_id_; + + // Whether this is a leaf call - i.e. one that doesn't call back into Dart. + bool is_leaf_; }; class UntaggedField : public UntaggedObject { diff --git a/samples/ffi/sample_ffi_functions.dart b/samples/ffi/sample_ffi_functions.dart index 4ab9b81b8a2..51376e18fbb 100644 --- a/samples/ffi/sample_ffi_functions.dart +++ b/samples/ffi/sample_ffi_functions.dart @@ -90,6 +90,16 @@ main() { print(result.runtimeType); } + { + // As a leaf call. + BinaryOp sumPlus42Leaf = ffiTestFunctions + .lookupFunction("SumPlus42", isLeaf: true); + + final result = sumPlus42Leaf(3, 17); + print(result); + print(result.runtimeType); + } + { // Various size arguments. QuadOp intComputation = ffiTestFunctions diff --git a/samples_2/ffi/sample_ffi_functions.dart b/samples_2/ffi/sample_ffi_functions.dart index a0526fa9d74..f999ed6e305 100644 --- a/samples_2/ffi/sample_ffi_functions.dart +++ b/samples_2/ffi/sample_ffi_functions.dart @@ -92,6 +92,16 @@ main() { print(result.runtimeType); } + { + // As a leaf call. + BinaryOp sumPlus42Leaf = ffiTestFunctions + .lookupFunction("SumPlus42", isLeaf: true); + + final result = sumPlus42Leaf(3, 17); + print(result); + print(result.runtimeType); + } + { // Various size arguments. QuadOp intComputation = ffiTestFunctions diff --git a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart index 655006c9a25..5482890738b 100644 --- a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart @@ -52,7 +52,7 @@ class DynamicLibrary { extension DynamicLibraryExtension on DynamicLibrary { @patch - DS lookupFunction( - String symbolName) => + DS lookupFunction(String symbolName, + {bool isLeaf: false}) => throw UnsupportedError("The body is inlined in the frontend."); } diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart index 4290185bf63..ac745e39c27 100644 --- a/sdk/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart @@ -38,7 +38,8 @@ Pointer _fromAddress(int ptr) native "Ffi_fromAddress"; // this function. @pragma("vm:recognized", "other") DS _asFunctionInternal( - Pointer> ptr) native "Ffi_asFunctionInternal"; + Pointer> ptr, + bool isLeaf) native "Ffi_asFunctionInternal"; dynamic _asExternalTypedData(Pointer ptr, int count) native "Ffi_asExternalTypedData"; @@ -336,7 +337,7 @@ Pointer> _elementAtPointer( extension NativeFunctionPointer on Pointer> { @patch - DF asFunction() => + DF asFunction({bool isLeaf: false}) => throw UnsupportedError("The body is inlined in the frontend."); } diff --git a/sdk/lib/ffi/dynamic_library.dart b/sdk/lib/ffi/dynamic_library.dart index 16e1b0f2963..e39e53c5816 100644 --- a/sdk/lib/ffi/dynamic_library.dart +++ b/sdk/lib/ffi/dynamic_library.dart @@ -76,5 +76,6 @@ extension DynamicLibraryExtension on DynamicLibrary { /// int Function(int, int)>('add'); /// ``` external F lookupFunction( - String symbolName); + String symbolName, + {bool isLeaf: false}); } diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart index 369b38991ae..26177e801f4 100644 --- a/sdk/lib/ffi/ffi.dart +++ b/sdk/lib/ffi/ffi.dart @@ -149,7 +149,8 @@ extension NativeFunctionPointer on Pointer> { /// Convert to Dart function, automatically marshalling the arguments /// and return value. - external DF asFunction<@DartRepresentationOf("NF") DF extends Function>(); + external DF asFunction<@DartRepresentationOf("NF") DF extends Function>( + {bool isLeaf: false}); } // diff --git a/tests/ffi/callback_tests_utils.dart b/tests/ffi/callback_tests_utils.dart index e34a52b7060..3f5fed82320 100644 --- a/tests/ffi/callback_tests_utils.dart +++ b/tests/ffi/callback_tests_utils.dart @@ -17,13 +17,19 @@ class CallbackTest { final String name; final Pointer callback; final void Function() afterCallbackChecks; + final bool isLeaf; - CallbackTest(this.name, this.callback) : afterCallbackChecks = noChecks {} - CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks) {} + CallbackTest(this.name, this.callback, {this.isLeaf: false}) + : afterCallbackChecks = noChecks {} + CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks, + {this.isLeaf: false}) {} void run() { - final NativeCallbackTestFn tester = ffiTestFunctions - .lookupFunction("Test$name"); + final NativeCallbackTestFn tester = isLeaf + ? ffiTestFunctions.lookupFunction("Test$name", isLeaf: true) + : ffiTestFunctions.lookupFunction("Test$name", isLeaf: false); final int testCode = tester(callback); diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart index e83063143b7..6c970e1734b 100644 --- a/tests/ffi/function_structs_by_value_generated_test.dart +++ b/tests/ffi/function_structs_by_value_generated_test.dart @@ -133,6 +133,119 @@ void main() { testReturnStructNestedIntStructAlignmentInt32(); testReturnStructNestedIntStructAlignmentInt64(); testReturnStructNestedIrregularEvenBigger(); + testPassStruct1ByteIntx10Leaf(); + testPassStruct3BytesHomogeneousUint8x10Leaf(); + testPassStruct3BytesInt2ByteAlignedx10Leaf(); + testPassStruct4BytesHomogeneousInt16x10Leaf(); + testPassStruct7BytesHomogeneousUint8x10Leaf(); + testPassStruct7BytesInt4ByteAlignedx10Leaf(); + testPassStruct8BytesIntx10Leaf(); + testPassStruct8BytesHomogeneousFloatx10Leaf(); + testPassStruct8BytesMixedx10Leaf(); + testPassStruct9BytesHomogeneousUint8x10Leaf(); + testPassStruct9BytesInt4Or8ByteAlignedx10Leaf(); + testPassStruct12BytesHomogeneousFloatx6Leaf(); + testPassStruct16BytesHomogeneousFloatx5Leaf(); + testPassStruct16BytesMixedx10Leaf(); + testPassStruct16BytesMixed2x10Leaf(); + testPassStruct17BytesIntx10Leaf(); + testPassStruct19BytesHomogeneousUint8x10Leaf(); + testPassStruct20BytesHomogeneousInt32x10Leaf(); + testPassStruct20BytesHomogeneousFloatLeaf(); + testPassStruct32BytesHomogeneousDoublex5Leaf(); + testPassStruct40BytesHomogeneousDoubleLeaf(); + testPassStruct1024BytesHomogeneousUint64Leaf(); + testPassFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf(); + testPassFloatStruct32BytesHomogeneousDoubleFloatStructLeaf(); + testPassInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf(); + testPassDoublex6Struct16BytesMixedx4Int32Leaf(); + testPassInt32x4Struct16BytesMixedx4DoubleLeaf(); + testPassStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf(); + testPassInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf(); + testPassStructAlignmentInt16Leaf(); + testPassStructAlignmentInt32Leaf(); + testPassStructAlignmentInt64Leaf(); + testPassStruct8BytesNestedIntx10Leaf(); + testPassStruct8BytesNestedFloatx10Leaf(); + testPassStruct8BytesNestedFloat2x10Leaf(); + testPassStruct8BytesNestedMixedx10Leaf(); + testPassStruct16BytesNestedIntx2Leaf(); + testPassStruct32BytesNestedIntx2Leaf(); + testPassStructNestedIntStructAlignmentInt16Leaf(); + testPassStructNestedIntStructAlignmentInt32Leaf(); + testPassStructNestedIntStructAlignmentInt64Leaf(); + testPassStructNestedIrregularEvenBiggerx4Leaf(); + testPassStruct8BytesInlineArrayIntx4Leaf(); + testPassStructInlineArrayIrregularx4Leaf(); + testPassStructInlineArray100BytesLeaf(); + testPassStructStruct16BytesHomogeneousFloat2x5Leaf(); + testPassStructStruct32BytesHomogeneousDouble2x5Leaf(); + testPassStructStruct16BytesMixed3x10Leaf(); + testPassUint8Struct32BytesInlineArrayMultiDimensionalILeaf(); + testPassUint8Struct4BytesInlineArrayMultiDimensionalInLeaf(); + testPassStruct3BytesPackedIntx10Leaf(); + testPassStruct8BytesPackedIntx10Leaf(); + testPassStruct9BytesPackedMixedx10DoubleInt32Leaf(); + testPassStruct5BytesPackedMixedLeaf(); + testPassStructNestedAlignmentStruct5BytesPackedMixedLeaf(); + testPassStruct6BytesInlineArrayIntLeaf(); + testPassStruct15BytesInlineArrayMixedLeaf(); + testPassUnion4BytesMixedx10Leaf(); + testPassUnion8BytesNestedFloatx10Leaf(); + testPassUnion9BytesNestedIntx10Leaf(); + testPassUnion16BytesNestedInlineArrayFloatx10Leaf(); + testPassUnion16BytesNestedFloatx10Leaf(); + testReturnStruct1ByteIntLeaf(); + testReturnStruct3BytesHomogeneousUint8Leaf(); + testReturnStruct3BytesInt2ByteAlignedLeaf(); + testReturnStruct4BytesHomogeneousInt16Leaf(); + testReturnStruct7BytesHomogeneousUint8Leaf(); + testReturnStruct7BytesInt4ByteAlignedLeaf(); + testReturnStruct8BytesIntLeaf(); + testReturnStruct8BytesHomogeneousFloatLeaf(); + testReturnStruct8BytesMixedLeaf(); + testReturnStruct9BytesHomogeneousUint8Leaf(); + testReturnStruct9BytesInt4Or8ByteAlignedLeaf(); + testReturnStruct12BytesHomogeneousFloatLeaf(); + testReturnStruct16BytesHomogeneousFloatLeaf(); + testReturnStruct16BytesMixedLeaf(); + testReturnStruct16BytesMixed2Leaf(); + testReturnStruct17BytesIntLeaf(); + testReturnStruct19BytesHomogeneousUint8Leaf(); + testReturnStruct20BytesHomogeneousInt32Leaf(); + testReturnStruct20BytesHomogeneousFloatLeaf(); + testReturnStruct32BytesHomogeneousDoubleLeaf(); + testReturnStruct40BytesHomogeneousDoubleLeaf(); + testReturnStruct1024BytesHomogeneousUint64Leaf(); + testReturnStruct3BytesPackedIntLeaf(); + testReturnStruct8BytesPackedIntLeaf(); + testReturnStruct9BytesPackedMixedLeaf(); + testReturnUnion4BytesMixedLeaf(); + testReturnUnion8BytesNestedFloatLeaf(); + testReturnUnion9BytesNestedIntLeaf(); + testReturnUnion16BytesNestedFloatLeaf(); + testReturnStructArgumentStruct1ByteIntLeaf(); + testReturnStructArgumentInt32x8Struct1ByteIntLeaf(); + testReturnStructArgumentStruct8BytesHomogeneousFloatLeaf(); + testReturnStructArgumentStruct20BytesHomogeneousInt32Leaf(); + testReturnStructArgumentInt32x8Struct20BytesHomogeneouLeaf(); + testReturnStructArgumentStruct8BytesInlineArrayIntLeaf(); + testReturnStructArgumentStructStruct16BytesHomogeneousLeaf(); + testReturnStructArgumentStructStruct32BytesHomogeneousLeaf(); + testReturnStructArgumentStructStruct16BytesMixed3Leaf(); + testReturnStructAlignmentInt16Leaf(); + testReturnStructAlignmentInt32Leaf(); + testReturnStructAlignmentInt64Leaf(); + testReturnStruct8BytesNestedIntLeaf(); + testReturnStruct8BytesNestedFloatLeaf(); + testReturnStruct8BytesNestedFloat2Leaf(); + testReturnStruct8BytesNestedMixedLeaf(); + testReturnStruct16BytesNestedIntLeaf(); + testReturnStruct32BytesNestedIntLeaf(); + testReturnStructNestedIntStructAlignmentInt16Leaf(); + testReturnStructNestedIntStructAlignmentInt32Leaf(); + testReturnStructNestedIntStructAlignmentInt64Leaf(); + testReturnStructNestedIrregularEvenBiggerLeaf(); } } @@ -8770,3 +8883,7541 @@ void testReturnStructNestedIrregularEvenBigger() { calloc.free(a1Pointer); calloc.free(a2Pointer); } + +final passStruct1ByteIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt), + int Function( + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt)>("PassStruct1ByteIntx10", isLeaf: true); + +/// Smallest struct with data. +/// 10 struct arguments will exhaust available registers. +void testPassStruct1ByteIntx10Leaf() { + final a0Pointer = calloc(); + final Struct1ByteInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct1ByteInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct1ByteInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct1ByteInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct1ByteInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct1ByteInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct1ByteInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct1ByteInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct1ByteInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct1ByteInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a1.a0 = 2; + a2.a0 = -3; + a3.a0 = 4; + a4.a0 = -5; + a5.a0 = 6; + a6.a0 = -7; + a7.a0 = 8; + a8.a0 = -9; + a9.a0 = 10; + + final result = + passStruct1ByteIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(5, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct3BytesHomogeneousUint8x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8), + int Function( + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8)>( + "PassStruct3BytesHomogeneousUint8x10", + isLeaf: true); + +/// Not a multiple of word size, not a power of two. +/// 10 struct arguments will exhaust available registers. +void testPassStruct3BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a1.a0 = 4; + a1.a1 = 5; + a1.a2 = 6; + a2.a0 = 7; + a2.a1 = 8; + a2.a2 = 9; + a3.a0 = 10; + a3.a1 = 11; + a3.a2 = 12; + a4.a0 = 13; + a4.a1 = 14; + a4.a2 = 15; + a5.a0 = 16; + a5.a1 = 17; + a5.a2 = 18; + a6.a0 = 19; + a6.a1 = 20; + a6.a2 = 21; + a7.a0 = 22; + a7.a1 = 23; + a7.a2 = 24; + a8.a0 = 25; + a8.a1 = 26; + a8.a2 = 27; + a9.a0 = 28; + a9.a1 = 29; + a9.a2 = 30; + + final result = passStruct3BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(465, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct3BytesInt2ByteAlignedx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned), + int Function( + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned)>("PassStruct3BytesInt2ByteAlignedx10", + isLeaf: true); + +/// Not a multiple of word size, not a power of two. +/// With alignment rules taken into account size is 4 bytes. +/// 10 struct arguments will exhaust available registers. +void testPassStruct3BytesInt2ByteAlignedx10Leaf() { + final a0Pointer = calloc(); + final Struct3BytesInt2ByteAligned a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct3BytesInt2ByteAligned a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct3BytesInt2ByteAligned a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct3BytesInt2ByteAligned a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct3BytesInt2ByteAligned a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct3BytesInt2ByteAligned a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct3BytesInt2ByteAligned a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct3BytesInt2ByteAligned a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct3BytesInt2ByteAligned a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct3BytesInt2ByteAligned a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = passStruct3BytesInt2ByteAlignedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct4BytesHomogeneousInt16x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16), + int Function( + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16)>( + "PassStruct4BytesHomogeneousInt16x10", + isLeaf: true); + +/// Exactly word size on 32-bit architectures. +/// 10 struct arguments will exhaust available registers. +void testPassStruct4BytesHomogeneousInt16x10Leaf() { + final a0Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = passStruct4BytesHomogeneousInt16x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct7BytesHomogeneousUint8x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8), + int Function( + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8)>( + "PassStruct7BytesHomogeneousUint8x10", + isLeaf: true); + +/// Sub word size on 64 bit architectures. +/// 10 struct arguments will exhaust available registers. +void testPassStruct7BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a1.a0 = 8; + a1.a1 = 9; + a1.a2 = 10; + a1.a3 = 11; + a1.a4 = 12; + a1.a5 = 13; + a1.a6 = 14; + a2.a0 = 15; + a2.a1 = 16; + a2.a2 = 17; + a2.a3 = 18; + a2.a4 = 19; + a2.a5 = 20; + a2.a6 = 21; + a3.a0 = 22; + a3.a1 = 23; + a3.a2 = 24; + a3.a3 = 25; + a3.a4 = 26; + a3.a5 = 27; + a3.a6 = 28; + a4.a0 = 29; + a4.a1 = 30; + a4.a2 = 31; + a4.a3 = 32; + a4.a4 = 33; + a4.a5 = 34; + a4.a6 = 35; + a5.a0 = 36; + a5.a1 = 37; + a5.a2 = 38; + a5.a3 = 39; + a5.a4 = 40; + a5.a5 = 41; + a5.a6 = 42; + a6.a0 = 43; + a6.a1 = 44; + a6.a2 = 45; + a6.a3 = 46; + a6.a4 = 47; + a6.a5 = 48; + a6.a6 = 49; + a7.a0 = 50; + a7.a1 = 51; + a7.a2 = 52; + a7.a3 = 53; + a7.a4 = 54; + a7.a5 = 55; + a7.a6 = 56; + a8.a0 = 57; + a8.a1 = 58; + a8.a2 = 59; + a8.a3 = 60; + a8.a4 = 61; + a8.a5 = 62; + a8.a6 = 63; + a9.a0 = 64; + a9.a1 = 65; + a9.a2 = 66; + a9.a3 = 67; + a9.a4 = 68; + a9.a5 = 69; + a9.a6 = 70; + + final result = passStruct7BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(2485, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct7BytesInt4ByteAlignedx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned), + int Function( + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned)>("PassStruct7BytesInt4ByteAlignedx10", + isLeaf: true); + +/// Sub word size on 64 bit architectures. +/// With alignment rules taken into account size is 8 bytes. +/// 10 struct arguments will exhaust available registers. +void testPassStruct7BytesInt4ByteAlignedx10Leaf() { + final a0Pointer = calloc(); + final Struct7BytesInt4ByteAligned a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct7BytesInt4ByteAligned a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct7BytesInt4ByteAligned a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct7BytesInt4ByteAligned a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct7BytesInt4ByteAligned a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct7BytesInt4ByteAligned a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct7BytesInt4ByteAligned a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct7BytesInt4ByteAligned a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct7BytesInt4ByteAligned a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct7BytesInt4ByteAligned a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28; + a9.a1 = -29; + a9.a2 = 30; + + final result = passStruct7BytesInt4ByteAlignedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(15, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt), + int Function( + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt)>("PassStruct8BytesIntx10", isLeaf: true); + +/// Exactly word size struct on 64bit architectures. +/// 10 struct arguments will exhaust available registers. +void testPassStruct8BytesIntx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28; + a9.a1 = -29; + a9.a2 = 30; + + final result = + passStruct8BytesIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(15, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesHomogeneousFloatx10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat), + double Function( + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat)>( + "PassStruct8BytesHomogeneousFloatx10", + isLeaf: true); + +/// Arguments passed in FP registers as long as they fit. +/// 10 struct arguments will exhaust available registers. +void testPassStruct8BytesHomogeneousFloatx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesHomogeneousFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesHomogeneousFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesHomogeneousFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesHomogeneousFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesHomogeneousFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesHomogeneousFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesHomogeneousFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesHomogeneousFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesHomogeneousFloat a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a1.a0 = -3.0; + a1.a1 = 4.0; + a2.a0 = -5.0; + a2.a1 = 6.0; + a3.a0 = -7.0; + a3.a1 = 8.0; + a4.a0 = -9.0; + a4.a1 = 10.0; + a5.a0 = -11.0; + a5.a1 = 12.0; + a6.a0 = -13.0; + a6.a1 = 14.0; + a7.a0 = -15.0; + a7.a1 = 16.0; + a8.a0 = -17.0; + a8.a1 = 18.0; + a9.a0 = -19.0; + a9.a1 = 20.0; + + final result = passStruct8BytesHomogeneousFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesMixedx10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed), + double Function( + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed)>("PassStruct8BytesMixedx10", isLeaf: true); + +/// On x64, arguments go in int registers because it is not only float. +/// 10 struct arguments will exhaust available registers. +void testPassStruct8BytesMixedx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesMixed a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4.0; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7.0; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10.0; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13.0; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16.0; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19.0; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22.0; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25.0; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28.0; + a9.a1 = -29; + a9.a2 = 30; + + final result = + passStruct8BytesMixedx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(15.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct9BytesHomogeneousUint8x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8), + int Function( + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8)>( + "PassStruct9BytesHomogeneousUint8x10", + isLeaf: true); + +/// Argument is a single byte over a multiple of word size. +/// 10 struct arguments will exhaust available registers. +/// Struct only has 1-byte aligned fields to test struct alignment itself. +/// Tests upper bytes in the integer registers that are partly filled. +/// Tests stack alignment of non word size stack arguments. +void testPassStruct9BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a0.a7 = 8; + a0.a8 = 9; + a1.a0 = 10; + a1.a1 = 11; + a1.a2 = 12; + a1.a3 = 13; + a1.a4 = 14; + a1.a5 = 15; + a1.a6 = 16; + a1.a7 = 17; + a1.a8 = 18; + a2.a0 = 19; + a2.a1 = 20; + a2.a2 = 21; + a2.a3 = 22; + a2.a4 = 23; + a2.a5 = 24; + a2.a6 = 25; + a2.a7 = 26; + a2.a8 = 27; + a3.a0 = 28; + a3.a1 = 29; + a3.a2 = 30; + a3.a3 = 31; + a3.a4 = 32; + a3.a5 = 33; + a3.a6 = 34; + a3.a7 = 35; + a3.a8 = 36; + a4.a0 = 37; + a4.a1 = 38; + a4.a2 = 39; + a4.a3 = 40; + a4.a4 = 41; + a4.a5 = 42; + a4.a6 = 43; + a4.a7 = 44; + a4.a8 = 45; + a5.a0 = 46; + a5.a1 = 47; + a5.a2 = 48; + a5.a3 = 49; + a5.a4 = 50; + a5.a5 = 51; + a5.a6 = 52; + a5.a7 = 53; + a5.a8 = 54; + a6.a0 = 55; + a6.a1 = 56; + a6.a2 = 57; + a6.a3 = 58; + a6.a4 = 59; + a6.a5 = 60; + a6.a6 = 61; + a6.a7 = 62; + a6.a8 = 63; + a7.a0 = 64; + a7.a1 = 65; + a7.a2 = 66; + a7.a3 = 67; + a7.a4 = 68; + a7.a5 = 69; + a7.a6 = 70; + a7.a7 = 71; + a7.a8 = 72; + a8.a0 = 73; + a8.a1 = 74; + a8.a2 = 75; + a8.a3 = 76; + a8.a4 = 77; + a8.a5 = 78; + a8.a6 = 79; + a8.a7 = 80; + a8.a8 = 81; + a9.a0 = 82; + a9.a1 = 83; + a9.a2 = 84; + a9.a3 = 85; + a9.a4 = 86; + a9.a5 = 87; + a9.a6 = 88; + a9.a7 = 89; + a9.a8 = 90; + + final result = passStruct9BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(4095, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct9BytesInt4Or8ByteAlignedx10Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function( + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned), + int Function( + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned)>( + "PassStruct9BytesInt4Or8ByteAlignedx10", + isLeaf: true); + +/// Argument is a single byte over a multiple of word size. +/// With alignment rules taken into account size is 12 or 16 bytes. +/// 10 struct arguments will exhaust available registers. +/// +void testPassStruct9BytesInt4Or8ByteAlignedx10Leaf() { + final a0Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = passStruct9BytesInt4Or8ByteAlignedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct12BytesHomogeneousFloatx6Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat), + double Function( + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat)>( + "PassStruct12BytesHomogeneousFloatx6", + isLeaf: true); + +/// Arguments in FPU registers on arm hardfp and arm64. +/// Struct arguments will exhaust available registers, and leave some empty. +/// The last argument is to test whether arguments are backfilled. +void testPassStruct12BytesHomogeneousFloatx6Leaf() { + final a0Pointer = calloc(); + final Struct12BytesHomogeneousFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct12BytesHomogeneousFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct12BytesHomogeneousFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct12BytesHomogeneousFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct12BytesHomogeneousFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct12BytesHomogeneousFloat a5 = a5Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a1.a0 = 4.0; + a1.a1 = -5.0; + a1.a2 = 6.0; + a2.a0 = -7.0; + a2.a1 = 8.0; + a2.a2 = -9.0; + a3.a0 = 10.0; + a3.a1 = -11.0; + a3.a2 = 12.0; + a4.a0 = -13.0; + a4.a1 = 14.0; + a4.a2 = -15.0; + a5.a0 = 16.0; + a5.a1 = -17.0; + a5.a2 = 18.0; + + final result = + passStruct12BytesHomogeneousFloatx6Leaf(a0, a1, a2, a3, a4, a5); + + print("result = $result"); + + Expect.approxEquals(9.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); +} + +final passStruct16BytesHomogeneousFloatx5Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat), + double Function( + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat)>( + "PassStruct16BytesHomogeneousFloatx5", + isLeaf: true); + +/// On Linux x64 argument is transferred on stack because it is over 16 bytes. +/// Arguments in FPU registers on arm hardfp and arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStruct16BytesHomogeneousFloatx5Leaf() { + final a0Pointer = calloc(); + final Struct16BytesHomogeneousFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesHomogeneousFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct16BytesHomogeneousFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct16BytesHomogeneousFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct16BytesHomogeneousFloat a4 = a4Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a1.a0 = -5.0; + a1.a1 = 6.0; + a1.a2 = -7.0; + a1.a3 = 8.0; + a2.a0 = -9.0; + a2.a1 = 10.0; + a2.a2 = -11.0; + a2.a3 = 12.0; + a3.a0 = -13.0; + a3.a1 = 14.0; + a3.a2 = -15.0; + a3.a3 = 16.0; + a4.a0 = -17.0; + a4.a1 = 18.0; + a4.a2 = -19.0; + a4.a3 = 20.0; + + final result = passStruct16BytesHomogeneousFloatx5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStruct16BytesMixedx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed), + double Function( + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed)>("PassStruct16BytesMixedx10", isLeaf: true); + +/// On x64, arguments are split over FP and int registers. +/// On x64, it will exhaust the integer registers with the 6th argument. +/// The rest goes on the stack. +/// On arm, arguments are 8 byte aligned. +void testPassStruct16BytesMixedx10Leaf() { + final a0Pointer = calloc(); + final Struct16BytesMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct16BytesMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct16BytesMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct16BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct16BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct16BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct16BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct16BytesMixed a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2; + a1.a0 = -3.0; + a1.a1 = 4; + a2.a0 = -5.0; + a2.a1 = 6; + a3.a0 = -7.0; + a3.a1 = 8; + a4.a0 = -9.0; + a4.a1 = 10; + a5.a0 = -11.0; + a5.a1 = 12; + a6.a0 = -13.0; + a6.a1 = 14; + a7.a0 = -15.0; + a7.a1 = 16; + a8.a0 = -17.0; + a8.a1 = 18; + a9.a0 = -19.0; + a9.a1 = 20; + + final result = + passStruct16BytesMixedx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct16BytesMixed2x10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2), + double Function( + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2)>("PassStruct16BytesMixed2x10", isLeaf: true); + +/// On x64, arguments are split over FP and int registers. +/// On x64, it will exhaust the integer registers with the 6th argument. +/// The rest goes on the stack. +/// On arm, arguments are 4 byte aligned. +void testPassStruct16BytesMixed2x10Leaf() { + final a0Pointer = calloc(); + final Struct16BytesMixed2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesMixed2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct16BytesMixed2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct16BytesMixed2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct16BytesMixed2 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct16BytesMixed2 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct16BytesMixed2 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed2 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct16BytesMixed2 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct16BytesMixed2 a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4; + a1.a0 = -5.0; + a1.a1 = 6.0; + a1.a2 = -7.0; + a1.a3 = 8; + a2.a0 = -9.0; + a2.a1 = 10.0; + a2.a2 = -11.0; + a2.a3 = 12; + a3.a0 = -13.0; + a3.a1 = 14.0; + a3.a2 = -15.0; + a3.a3 = 16; + a4.a0 = -17.0; + a4.a1 = 18.0; + a4.a2 = -19.0; + a4.a3 = 20; + a5.a0 = -21.0; + a5.a1 = 22.0; + a5.a2 = -23.0; + a5.a3 = 24; + a6.a0 = -25.0; + a6.a1 = 26.0; + a6.a2 = -27.0; + a6.a3 = 28; + a7.a0 = -29.0; + a7.a1 = 30.0; + a7.a2 = -31.0; + a7.a3 = 32; + a8.a0 = -33.0; + a8.a1 = 34.0; + a8.a2 = -35.0; + a8.a3 = 36; + a9.a0 = -37.0; + a9.a1 = 38.0; + a9.a2 = -39.0; + a9.a3 = 40; + + final result = + passStruct16BytesMixed2x10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(20.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct17BytesIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt), + int Function( + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt)>("PassStruct17BytesIntx10", isLeaf: true); + +/// Arguments are passed as pointer to copy on arm64. +/// Tests that the memory allocated for copies are rounded up to word size. +void testPassStruct17BytesIntx10Leaf() { + final a0Pointer = calloc(); + final Struct17BytesInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct17BytesInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct17BytesInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct17BytesInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct17BytesInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct17BytesInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct17BytesInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct17BytesInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct17BytesInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct17BytesInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28; + a9.a1 = -29; + a9.a2 = 30; + + final result = + passStruct17BytesIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(15, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct19BytesHomogeneousUint8x10Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function( + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8), + int Function( + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8)>( + "PassStruct19BytesHomogeneousUint8x10", + isLeaf: true); + +/// The minimum alignment of this struct is only 1 byte based on its fields. +/// Test that the memory backing these structs is extended to the right size. +/// +void testPassStruct19BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a0.a7 = 8; + a0.a8 = 9; + a0.a9 = 10; + a0.a10 = 11; + a0.a11 = 12; + a0.a12 = 13; + a0.a13 = 14; + a0.a14 = 15; + a0.a15 = 16; + a0.a16 = 17; + a0.a17 = 18; + a0.a18 = 19; + a1.a0 = 20; + a1.a1 = 21; + a1.a2 = 22; + a1.a3 = 23; + a1.a4 = 24; + a1.a5 = 25; + a1.a6 = 26; + a1.a7 = 27; + a1.a8 = 28; + a1.a9 = 29; + a1.a10 = 30; + a1.a11 = 31; + a1.a12 = 32; + a1.a13 = 33; + a1.a14 = 34; + a1.a15 = 35; + a1.a16 = 36; + a1.a17 = 37; + a1.a18 = 38; + a2.a0 = 39; + a2.a1 = 40; + a2.a2 = 41; + a2.a3 = 42; + a2.a4 = 43; + a2.a5 = 44; + a2.a6 = 45; + a2.a7 = 46; + a2.a8 = 47; + a2.a9 = 48; + a2.a10 = 49; + a2.a11 = 50; + a2.a12 = 51; + a2.a13 = 52; + a2.a14 = 53; + a2.a15 = 54; + a2.a16 = 55; + a2.a17 = 56; + a2.a18 = 57; + a3.a0 = 58; + a3.a1 = 59; + a3.a2 = 60; + a3.a3 = 61; + a3.a4 = 62; + a3.a5 = 63; + a3.a6 = 64; + a3.a7 = 65; + a3.a8 = 66; + a3.a9 = 67; + a3.a10 = 68; + a3.a11 = 69; + a3.a12 = 70; + a3.a13 = 71; + a3.a14 = 72; + a3.a15 = 73; + a3.a16 = 74; + a3.a17 = 75; + a3.a18 = 76; + a4.a0 = 77; + a4.a1 = 78; + a4.a2 = 79; + a4.a3 = 80; + a4.a4 = 81; + a4.a5 = 82; + a4.a6 = 83; + a4.a7 = 84; + a4.a8 = 85; + a4.a9 = 86; + a4.a10 = 87; + a4.a11 = 88; + a4.a12 = 89; + a4.a13 = 90; + a4.a14 = 91; + a4.a15 = 92; + a4.a16 = 93; + a4.a17 = 94; + a4.a18 = 95; + a5.a0 = 96; + a5.a1 = 97; + a5.a2 = 98; + a5.a3 = 99; + a5.a4 = 100; + a5.a5 = 101; + a5.a6 = 102; + a5.a7 = 103; + a5.a8 = 104; + a5.a9 = 105; + a5.a10 = 106; + a5.a11 = 107; + a5.a12 = 108; + a5.a13 = 109; + a5.a14 = 110; + a5.a15 = 111; + a5.a16 = 112; + a5.a17 = 113; + a5.a18 = 114; + a6.a0 = 115; + a6.a1 = 116; + a6.a2 = 117; + a6.a3 = 118; + a6.a4 = 119; + a6.a5 = 120; + a6.a6 = 121; + a6.a7 = 122; + a6.a8 = 123; + a6.a9 = 124; + a6.a10 = 125; + a6.a11 = 126; + a6.a12 = 127; + a6.a13 = 128; + a6.a14 = 129; + a6.a15 = 130; + a6.a16 = 131; + a6.a17 = 132; + a6.a18 = 133; + a7.a0 = 134; + a7.a1 = 135; + a7.a2 = 136; + a7.a3 = 137; + a7.a4 = 138; + a7.a5 = 139; + a7.a6 = 140; + a7.a7 = 141; + a7.a8 = 142; + a7.a9 = 143; + a7.a10 = 144; + a7.a11 = 145; + a7.a12 = 146; + a7.a13 = 147; + a7.a14 = 148; + a7.a15 = 149; + a7.a16 = 150; + a7.a17 = 151; + a7.a18 = 152; + a8.a0 = 153; + a8.a1 = 154; + a8.a2 = 155; + a8.a3 = 156; + a8.a4 = 157; + a8.a5 = 158; + a8.a6 = 159; + a8.a7 = 160; + a8.a8 = 161; + a8.a9 = 162; + a8.a10 = 163; + a8.a11 = 164; + a8.a12 = 165; + a8.a13 = 166; + a8.a14 = 167; + a8.a15 = 168; + a8.a16 = 169; + a8.a17 = 170; + a8.a18 = 171; + a9.a0 = 172; + a9.a1 = 173; + a9.a2 = 174; + a9.a3 = 175; + a9.a4 = 176; + a9.a5 = 177; + a9.a6 = 178; + a9.a7 = 179; + a9.a8 = 180; + a9.a9 = 181; + a9.a10 = 182; + a9.a11 = 183; + a9.a12 = 184; + a9.a13 = 185; + a9.a14 = 186; + a9.a15 = 187; + a9.a16 = 188; + a9.a17 = 189; + a9.a18 = 190; + + final result = passStruct19BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(18145, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct20BytesHomogeneousInt32x10Leaf = + ffiTestFunctions.lookupFunction< + Int32 Function( + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32), + int Function( + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32)>( + "PassStruct20BytesHomogeneousInt32x10", + isLeaf: true); + +/// Argument too big to go into integer registers on arm64. +/// The arguments are passed as pointers to copies. +/// The amount of arguments exhausts the number of integer registers, such that +/// pointers to copies are also passed on the stack. +void testPassStruct20BytesHomogeneousInt32x10Leaf() { + final a0Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a0.a3 = 4; + a0.a4 = -5; + a1.a0 = 6; + a1.a1 = -7; + a1.a2 = 8; + a1.a3 = -9; + a1.a4 = 10; + a2.a0 = -11; + a2.a1 = 12; + a2.a2 = -13; + a2.a3 = 14; + a2.a4 = -15; + a3.a0 = 16; + a3.a1 = -17; + a3.a2 = 18; + a3.a3 = -19; + a3.a4 = 20; + a4.a0 = -21; + a4.a1 = 22; + a4.a2 = -23; + a4.a3 = 24; + a4.a4 = -25; + a5.a0 = 26; + a5.a1 = -27; + a5.a2 = 28; + a5.a3 = -29; + a5.a4 = 30; + a6.a0 = -31; + a6.a1 = 32; + a6.a2 = -33; + a6.a3 = 34; + a6.a4 = -35; + a7.a0 = 36; + a7.a1 = -37; + a7.a2 = 38; + a7.a3 = -39; + a7.a4 = 40; + a8.a0 = -41; + a8.a1 = 42; + a8.a2 = -43; + a8.a3 = 44; + a8.a4 = -45; + a9.a0 = 46; + a9.a1 = -47; + a9.a2 = 48; + a9.a3 = -49; + a9.a4 = 50; + + final result = passStruct20BytesHomogeneousInt32x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(25, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct20BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Float Function(Struct20BytesHomogeneousFloat), + double Function(Struct20BytesHomogeneousFloat)>( + "PassStruct20BytesHomogeneousFloat", + isLeaf: true); + +/// Argument too big to go into FPU registers in hardfp and arm64. +void testPassStruct20BytesHomogeneousFloatLeaf() { + final a0Pointer = calloc(); + final Struct20BytesHomogeneousFloat a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a0.a4 = -5.0; + + final result = passStruct20BytesHomogeneousFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(-3.0, result); + + calloc.free(a0Pointer); +} + +final passStruct32BytesHomogeneousDoublex5Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble), + double Function( + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble)>( + "PassStruct32BytesHomogeneousDoublex5", + isLeaf: true); + +/// Arguments in FPU registers on arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStruct32BytesHomogeneousDoublex5Leaf() { + final a0Pointer = calloc(); + final Struct32BytesHomogeneousDouble a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct32BytesHomogeneousDouble a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct32BytesHomogeneousDouble a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct32BytesHomogeneousDouble a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct32BytesHomogeneousDouble a4 = a4Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a1.a0 = -5.0; + a1.a1 = 6.0; + a1.a2 = -7.0; + a1.a3 = 8.0; + a2.a0 = -9.0; + a2.a1 = 10.0; + a2.a2 = -11.0; + a2.a3 = 12.0; + a3.a0 = -13.0; + a3.a1 = 14.0; + a3.a2 = -15.0; + a3.a3 = 16.0; + a4.a0 = -17.0; + a4.a1 = 18.0; + a4.a2 = -19.0; + a4.a3 = 20.0; + + final result = passStruct32BytesHomogeneousDoublex5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStruct40BytesHomogeneousDoubleLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct40BytesHomogeneousDouble), + double Function(Struct40BytesHomogeneousDouble)>( + "PassStruct40BytesHomogeneousDouble", + isLeaf: true); + +/// Argument too big to go into FPU registers in arm64. +void testPassStruct40BytesHomogeneousDoubleLeaf() { + final a0Pointer = calloc(); + final Struct40BytesHomogeneousDouble a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a0.a4 = -5.0; + + final result = passStruct40BytesHomogeneousDoubleLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(-3.0, result); + + calloc.free(a0Pointer); +} + +final passStruct1024BytesHomogeneousUint64Leaf = + ffiTestFunctions.lookupFunction< + Uint64 Function(Struct1024BytesHomogeneousUint64), + int Function(Struct1024BytesHomogeneousUint64)>( + "PassStruct1024BytesHomogeneousUint64", + isLeaf: true); + +/// Test 1kb struct. +void testPassStruct1024BytesHomogeneousUint64Leaf() { + final a0Pointer = calloc(); + final Struct1024BytesHomogeneousUint64 a0 = a0Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a0.a7 = 8; + a0.a8 = 9; + a0.a9 = 10; + a0.a10 = 11; + a0.a11 = 12; + a0.a12 = 13; + a0.a13 = 14; + a0.a14 = 15; + a0.a15 = 16; + a0.a16 = 17; + a0.a17 = 18; + a0.a18 = 19; + a0.a19 = 20; + a0.a20 = 21; + a0.a21 = 22; + a0.a22 = 23; + a0.a23 = 24; + a0.a24 = 25; + a0.a25 = 26; + a0.a26 = 27; + a0.a27 = 28; + a0.a28 = 29; + a0.a29 = 30; + a0.a30 = 31; + a0.a31 = 32; + a0.a32 = 33; + a0.a33 = 34; + a0.a34 = 35; + a0.a35 = 36; + a0.a36 = 37; + a0.a37 = 38; + a0.a38 = 39; + a0.a39 = 40; + a0.a40 = 41; + a0.a41 = 42; + a0.a42 = 43; + a0.a43 = 44; + a0.a44 = 45; + a0.a45 = 46; + a0.a46 = 47; + a0.a47 = 48; + a0.a48 = 49; + a0.a49 = 50; + a0.a50 = 51; + a0.a51 = 52; + a0.a52 = 53; + a0.a53 = 54; + a0.a54 = 55; + a0.a55 = 56; + a0.a56 = 57; + a0.a57 = 58; + a0.a58 = 59; + a0.a59 = 60; + a0.a60 = 61; + a0.a61 = 62; + a0.a62 = 63; + a0.a63 = 64; + a0.a64 = 65; + a0.a65 = 66; + a0.a66 = 67; + a0.a67 = 68; + a0.a68 = 69; + a0.a69 = 70; + a0.a70 = 71; + a0.a71 = 72; + a0.a72 = 73; + a0.a73 = 74; + a0.a74 = 75; + a0.a75 = 76; + a0.a76 = 77; + a0.a77 = 78; + a0.a78 = 79; + a0.a79 = 80; + a0.a80 = 81; + a0.a81 = 82; + a0.a82 = 83; + a0.a83 = 84; + a0.a84 = 85; + a0.a85 = 86; + a0.a86 = 87; + a0.a87 = 88; + a0.a88 = 89; + a0.a89 = 90; + a0.a90 = 91; + a0.a91 = 92; + a0.a92 = 93; + a0.a93 = 94; + a0.a94 = 95; + a0.a95 = 96; + a0.a96 = 97; + a0.a97 = 98; + a0.a98 = 99; + a0.a99 = 100; + a0.a100 = 101; + a0.a101 = 102; + a0.a102 = 103; + a0.a103 = 104; + a0.a104 = 105; + a0.a105 = 106; + a0.a106 = 107; + a0.a107 = 108; + a0.a108 = 109; + a0.a109 = 110; + a0.a110 = 111; + a0.a111 = 112; + a0.a112 = 113; + a0.a113 = 114; + a0.a114 = 115; + a0.a115 = 116; + a0.a116 = 117; + a0.a117 = 118; + a0.a118 = 119; + a0.a119 = 120; + a0.a120 = 121; + a0.a121 = 122; + a0.a122 = 123; + a0.a123 = 124; + a0.a124 = 125; + a0.a125 = 126; + a0.a126 = 127; + a0.a127 = 128; + + final result = passStruct1024BytesHomogeneousUint64Leaf(a0); + + print("result = $result"); + + Expect.equals(8256, result); + + calloc.free(a0Pointer); +} + +final passFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf = + ffiTestFunctions.lookupFunction< + Float Function( + Float, + Struct16BytesHomogeneousFloat, + Float, + Struct16BytesHomogeneousFloat, + Float, + Struct16BytesHomogeneousFloat, + Float, + Struct16BytesHomogeneousFloat, + Float), + double Function( + double, + Struct16BytesHomogeneousFloat, + double, + Struct16BytesHomogeneousFloat, + double, + Struct16BytesHomogeneousFloat, + double, + Struct16BytesHomogeneousFloat, + double)>("PassFloatStruct16BytesHomogeneousFloatFloatStruct1", + isLeaf: true); + +/// Tests the alignment of structs in FPU registers and backfilling. +void testPassFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf() { + double a0; + final a1Pointer = calloc(); + final Struct16BytesHomogeneousFloat a1 = a1Pointer.ref; + double a2; + final a3Pointer = calloc(); + final Struct16BytesHomogeneousFloat a3 = a3Pointer.ref; + double a4; + final a5Pointer = calloc(); + final Struct16BytesHomogeneousFloat a5 = a5Pointer.ref; + double a6; + final a7Pointer = calloc(); + final Struct16BytesHomogeneousFloat a7 = a7Pointer.ref; + double a8; + + a0 = -1.0; + a1.a0 = 2.0; + a1.a1 = -3.0; + a1.a2 = 4.0; + a1.a3 = -5.0; + a2 = 6.0; + a3.a0 = -7.0; + a3.a1 = 8.0; + a3.a2 = -9.0; + a3.a3 = 10.0; + a4 = -11.0; + a5.a0 = 12.0; + a5.a1 = -13.0; + a5.a2 = 14.0; + a5.a3 = -15.0; + a6 = 16.0; + a7.a0 = -17.0; + a7.a1 = 18.0; + a7.a2 = -19.0; + a7.a3 = 20.0; + a8 = -21.0; + + final result = passFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-11.0, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); + calloc.free(a7Pointer); +} + +final passFloatStruct32BytesHomogeneousDoubleFloatStructLeaf = + ffiTestFunctions.lookupFunction< + Double Function( + Float, + Struct32BytesHomogeneousDouble, + Float, + Struct32BytesHomogeneousDouble, + Float, + Struct32BytesHomogeneousDouble, + Float, + Struct32BytesHomogeneousDouble, + Float), + double Function( + double, + Struct32BytesHomogeneousDouble, + double, + Struct32BytesHomogeneousDouble, + double, + Struct32BytesHomogeneousDouble, + double, + Struct32BytesHomogeneousDouble, + double)>("PassFloatStruct32BytesHomogeneousDoubleFloatStruct", + isLeaf: true); + +/// Tests the alignment of structs in FPU registers and backfilling. +void testPassFloatStruct32BytesHomogeneousDoubleFloatStructLeaf() { + double a0; + final a1Pointer = calloc(); + final Struct32BytesHomogeneousDouble a1 = a1Pointer.ref; + double a2; + final a3Pointer = calloc(); + final Struct32BytesHomogeneousDouble a3 = a3Pointer.ref; + double a4; + final a5Pointer = calloc(); + final Struct32BytesHomogeneousDouble a5 = a5Pointer.ref; + double a6; + final a7Pointer = calloc(); + final Struct32BytesHomogeneousDouble a7 = a7Pointer.ref; + double a8; + + a0 = -1.0; + a1.a0 = 2.0; + a1.a1 = -3.0; + a1.a2 = 4.0; + a1.a3 = -5.0; + a2 = 6.0; + a3.a0 = -7.0; + a3.a1 = 8.0; + a3.a2 = -9.0; + a3.a3 = 10.0; + a4 = -11.0; + a5.a0 = 12.0; + a5.a1 = -13.0; + a5.a2 = 14.0; + a5.a3 = -15.0; + a6 = 16.0; + a7.a0 = -17.0; + a7.a1 = 18.0; + a7.a2 = -19.0; + a7.a3 = 20.0; + a8 = -21.0; + + final result = passFloatStruct32BytesHomogeneousDoubleFloatStructLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-11.0, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); + calloc.free(a7Pointer); +} + +final passInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf = + ffiTestFunctions.lookupFunction< + Double Function(Int8, Struct16BytesMixed, Int8, Struct16BytesMixed, + Int8, Struct16BytesMixed, Int8, Struct16BytesMixed, Int8), + double Function( + int, + Struct16BytesMixed, + int, + Struct16BytesMixed, + int, + Struct16BytesMixed, + int, + Struct16BytesMixed, + int)>("PassInt8Struct16BytesMixedInt8Struct16BytesMixedIn", + isLeaf: true); + +/// Tests the alignment of structs in integers registers and on the stack. +/// Arm32 aligns this struct at 8. +/// Also, arm32 allocates the second struct partially in registers, partially +/// on stack. +/// Test backfilling of integer registers. +void testPassInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf() { + int a0; + final a1Pointer = calloc(); + final Struct16BytesMixed a1 = a1Pointer.ref; + int a2; + final a3Pointer = calloc(); + final Struct16BytesMixed a3 = a3Pointer.ref; + int a4; + final a5Pointer = calloc(); + final Struct16BytesMixed a5 = a5Pointer.ref; + int a6; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + int a8; + + a0 = -1; + a1.a0 = 2.0; + a1.a1 = -3; + a2 = 4; + a3.a0 = -5.0; + a3.a1 = 6; + a4 = -7; + a5.a0 = 8.0; + a5.a1 = -9; + a6 = 10; + a7.a0 = -11.0; + a7.a1 = 12; + a8 = -13; + + final result = passInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-7.0, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); + calloc.free(a7Pointer); +} + +final passDoublex6Struct16BytesMixedx4Int32Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Double, + Double, + Double, + Double, + Double, + Double, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Int32), + double Function( + double, + double, + double, + double, + double, + double, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + int)>("PassDoublex6Struct16BytesMixedx4Int32", isLeaf: true); + +/// On Linux x64, it will exhaust xmm registers first, after 6 doubles and 2 +/// structs. The rest of the structs will go on the stack. +/// The int will be backfilled into the int register. +void testPassDoublex6Struct16BytesMixedx4Int32Leaf() { + double a0; + double a1; + double a2; + double a3; + double a4; + double a5; + final a6Pointer = calloc(); + final Struct16BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct16BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct16BytesMixed a9 = a9Pointer.ref; + int a10; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + a4 = -5.0; + a5 = 6.0; + a6.a0 = -7.0; + a6.a1 = 8; + a7.a0 = -9.0; + a7.a1 = 10; + a8.a0 = -11.0; + a8.a1 = 12; + a9.a0 = -13.0; + a9.a1 = 14; + a10 = -15; + + final result = passDoublex6Struct16BytesMixedx4Int32Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + + print("result = $result"); + + Expect.approxEquals(-8.0, result); + + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passInt32x4Struct16BytesMixedx4DoubleLeaf = + ffiTestFunctions.lookupFunction< + Double Function(Int32, Int32, Int32, Int32, Struct16BytesMixed, + Struct16BytesMixed, Struct16BytesMixed, Struct16BytesMixed, Double), + double Function( + int, + int, + int, + int, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + double)>("PassInt32x4Struct16BytesMixedx4Double", isLeaf: true); + +/// On Linux x64, it will exhaust int registers first. +/// The rest of the structs will go on the stack. +/// The double will be backfilled into the xmm register. +void testPassInt32x4Struct16BytesMixedx4DoubleLeaf() { + int a0; + int a1; + int a2; + int a3; + final a4Pointer = calloc(); + final Struct16BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct16BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct16BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + double a8; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4.a0 = -5.0; + a4.a1 = 6; + a5.a0 = -7.0; + a5.a1 = 8; + a6.a0 = -9.0; + a6.a1 = 10; + a7.a0 = -11.0; + a7.a1 = 12; + a8 = -13.0; + + final result = passInt32x4Struct16BytesMixedx4DoubleLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-7.0, result); + + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); +} + +final passStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf = + ffiTestFunctions.lookupFunction< + Double Function(Struct40BytesHomogeneousDouble, + Struct4BytesHomogeneousInt16, Struct8BytesHomogeneousFloat), + double Function(Struct40BytesHomogeneousDouble, + Struct4BytesHomogeneousInt16, Struct8BytesHomogeneousFloat)>( + "PassStruct40BytesHomogeneousDoubleStruct4BytesHomo", + isLeaf: true); + +/// On various architectures, first struct is allocated on stack. +/// Check that the other two arguments are allocated on registers. +void testPassStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf() { + final a0Pointer = calloc(); + final Struct40BytesHomogeneousDouble a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesHomogeneousFloat a2 = a2Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a0.a4 = -5.0; + a1.a0 = 6; + a1.a1 = -7; + a2.a0 = 8.0; + a2.a1 = -9.0; + + final result = + passStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.approxEquals(-5.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); +} + +final passInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf = + ffiTestFunctions.lookupFunction< + Double Function( + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Int64, + Int8, + Struct1ByteInt, + Int64, + Int8, + Struct4BytesHomogeneousInt16, + Int64, + Int8, + Struct8BytesInt, + Int64, + Int8, + Struct8BytesHomogeneousFloat, + Int64, + Int8, + Struct8BytesMixed, + Int64, + Int8, + StructAlignmentInt16, + Int64, + Int8, + StructAlignmentInt32, + Int64, + Int8, + StructAlignmentInt64), + double Function( + int, + int, + int, + int, + int, + int, + int, + int, + double, + double, + double, + double, + double, + double, + double, + double, + int, + int, + Struct1ByteInt, + int, + int, + Struct4BytesHomogeneousInt16, + int, + int, + Struct8BytesInt, + int, + int, + Struct8BytesHomogeneousFloat, + int, + int, + Struct8BytesMixed, + int, + int, + StructAlignmentInt16, + int, + int, + StructAlignmentInt32, + int, + int, + StructAlignmentInt64)>("PassInt32x8Doublex8Int64Int8Struct1ByteIntInt64Int", isLeaf: true); + +/// Test alignment and padding of 16 byte int within struct. +void testPassInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + double a8; + double a9; + double a10; + double a11; + double a12; + double a13; + double a14; + double a15; + int a16; + int a17; + final a18Pointer = calloc(); + final Struct1ByteInt a18 = a18Pointer.ref; + int a19; + int a20; + final a21Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a21 = a21Pointer.ref; + int a22; + int a23; + final a24Pointer = calloc(); + final Struct8BytesInt a24 = a24Pointer.ref; + int a25; + int a26; + final a27Pointer = calloc(); + final Struct8BytesHomogeneousFloat a27 = a27Pointer.ref; + int a28; + int a29; + final a30Pointer = calloc(); + final Struct8BytesMixed a30 = a30Pointer.ref; + int a31; + int a32; + final a33Pointer = calloc(); + final StructAlignmentInt16 a33 = a33Pointer.ref; + int a34; + int a35; + final a36Pointer = calloc(); + final StructAlignmentInt32 a36 = a36Pointer.ref; + int a37; + int a38; + final a39Pointer = calloc(); + final StructAlignmentInt64 a39 = a39Pointer.ref; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + a5 = 6; + a6 = -7; + a7 = 8; + a8 = -9.0; + a9 = 10.0; + a10 = -11.0; + a11 = 12.0; + a12 = -13.0; + a13 = 14.0; + a14 = -15.0; + a15 = 16.0; + a16 = -17; + a17 = 18; + a18.a0 = -19; + a19 = 20; + a20 = -21; + a21.a0 = 22; + a21.a1 = -23; + a22 = 24; + a23 = -25; + a24.a0 = 26; + a24.a1 = -27; + a24.a2 = 28; + a25 = -29; + a26 = 30; + a27.a0 = -31.0; + a27.a1 = 32.0; + a28 = -33; + a29 = 34; + a30.a0 = -35.0; + a30.a1 = 36; + a30.a2 = -37; + a31 = 38; + a32 = -39; + a33.a0 = 40; + a33.a1 = -41; + a33.a2 = 42; + a34 = -43; + a35 = 44; + a36.a0 = -45; + a36.a1 = 46; + a36.a2 = -47; + a37 = 48; + a38 = -49; + a39.a0 = 50; + a39.a1 = -51; + a39.a2 = 52; + + final result = passInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf( + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + a17, + a18, + a19, + a20, + a21, + a22, + a23, + a24, + a25, + a26, + a27, + a28, + a29, + a30, + a31, + a32, + a33, + a34, + a35, + a36, + a37, + a38, + a39); + + print("result = $result"); + + Expect.approxEquals(26.0, result); + + calloc.free(a18Pointer); + calloc.free(a21Pointer); + calloc.free(a24Pointer); + calloc.free(a27Pointer); + calloc.free(a30Pointer); + calloc.free(a33Pointer); + calloc.free(a36Pointer); + calloc.free(a39Pointer); +} + +final passStructAlignmentInt16Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(StructAlignmentInt16), + int Function( + StructAlignmentInt16)>("PassStructAlignmentInt16", isLeaf: true); + +/// Test alignment and padding of 16 byte int within struct. +void testPassStructAlignmentInt16Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt16 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = passStructAlignmentInt16Leaf(a0); + + print("result = $result"); + + Expect.equals(-2, result); + + calloc.free(a0Pointer); +} + +final passStructAlignmentInt32Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(StructAlignmentInt32), + int Function( + StructAlignmentInt32)>("PassStructAlignmentInt32", isLeaf: true); + +/// Test alignment and padding of 32 byte int within struct. +void testPassStructAlignmentInt32Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt32 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = passStructAlignmentInt32Leaf(a0); + + print("result = $result"); + + Expect.equals(-2, result); + + calloc.free(a0Pointer); +} + +final passStructAlignmentInt64Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(StructAlignmentInt64), + int Function( + StructAlignmentInt64)>("PassStructAlignmentInt64", isLeaf: true); + +/// Test alignment and padding of 64 byte int within struct. +void testPassStructAlignmentInt64Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt64 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = passStructAlignmentInt64Leaf(a0); + + print("result = $result"); + + Expect.equals(-2, result); + + calloc.free(a0Pointer); +} + +final passStruct8BytesNestedIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt), + int Function( + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt)>("PassStruct8BytesNestedIntx10", isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust registers on all platforms. +void testPassStruct8BytesNestedIntx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedInt a9 = a9Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a1.a0 = -3; + a0.a1.a1 = 4; + a1.a0.a0 = -5; + a1.a0.a1 = 6; + a1.a1.a0 = -7; + a1.a1.a1 = 8; + a2.a0.a0 = -9; + a2.a0.a1 = 10; + a2.a1.a0 = -11; + a2.a1.a1 = 12; + a3.a0.a0 = -13; + a3.a0.a1 = 14; + a3.a1.a0 = -15; + a3.a1.a1 = 16; + a4.a0.a0 = -17; + a4.a0.a1 = 18; + a4.a1.a0 = -19; + a4.a1.a1 = 20; + a5.a0.a0 = -21; + a5.a0.a1 = 22; + a5.a1.a0 = -23; + a5.a1.a1 = 24; + a6.a0.a0 = -25; + a6.a0.a1 = 26; + a6.a1.a0 = -27; + a6.a1.a1 = 28; + a7.a0.a0 = -29; + a7.a0.a1 = 30; + a7.a1.a0 = -31; + a7.a1.a1 = 32; + a8.a0.a0 = -33; + a8.a0.a1 = 34; + a8.a1.a0 = -35; + a8.a1.a1 = 36; + a9.a0.a0 = -37; + a9.a0.a1 = 38; + a9.a1.a0 = -39; + a9.a1.a1 = 40; + + final result = + passStruct8BytesNestedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(20, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesNestedFloatx10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat), + double Function( + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat)>("PassStruct8BytesNestedFloatx10", + isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust fpu registers on all platforms. +void testPassStruct8BytesNestedFloatx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedFloat a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1.a0 = 2.0; + a1.a0.a0 = -3.0; + a1.a1.a0 = 4.0; + a2.a0.a0 = -5.0; + a2.a1.a0 = 6.0; + a3.a0.a0 = -7.0; + a3.a1.a0 = 8.0; + a4.a0.a0 = -9.0; + a4.a1.a0 = 10.0; + a5.a0.a0 = -11.0; + a5.a1.a0 = 12.0; + a6.a0.a0 = -13.0; + a6.a1.a0 = 14.0; + a7.a0.a0 = -15.0; + a7.a1.a0 = 16.0; + a8.a0.a0 = -17.0; + a8.a1.a0 = 18.0; + a9.a0.a0 = -19.0; + a9.a1.a0 = 20.0; + + final result = passStruct8BytesNestedFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesNestedFloat2x10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2), + double Function( + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2)>("PassStruct8BytesNestedFloat2x10", + isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust fpu registers on all platforms. +/// The nesting is irregular, testing homogenous float rules on arm and arm64, +/// and the fpu register usage on x64. +void testPassStruct8BytesNestedFloat2x10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedFloat2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedFloat2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedFloat2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedFloat2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedFloat2 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedFloat2 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedFloat2 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedFloat2 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedFloat2 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedFloat2 a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1 = 2.0; + a1.a0.a0 = -3.0; + a1.a1 = 4.0; + a2.a0.a0 = -5.0; + a2.a1 = 6.0; + a3.a0.a0 = -7.0; + a3.a1 = 8.0; + a4.a0.a0 = -9.0; + a4.a1 = 10.0; + a5.a0.a0 = -11.0; + a5.a1 = 12.0; + a6.a0.a0 = -13.0; + a6.a1 = 14.0; + a7.a0.a0 = -15.0; + a7.a1 = 16.0; + a8.a0.a0 = -17.0; + a8.a1 = 18.0; + a9.a0.a0 = -19.0; + a9.a1 = 20.0; + + final result = passStruct8BytesNestedFloat2x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesNestedMixedx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed), + double Function( + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed)>("PassStruct8BytesNestedMixedx10", + isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust all registers on all platforms. +void testPassStruct8BytesNestedMixedx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedMixed a9 = a9Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a1.a0 = -3.0; + a1.a0.a0 = 4; + a1.a0.a1 = -5; + a1.a1.a0 = 6.0; + a2.a0.a0 = -7; + a2.a0.a1 = 8; + a2.a1.a0 = -9.0; + a3.a0.a0 = 10; + a3.a0.a1 = -11; + a3.a1.a0 = 12.0; + a4.a0.a0 = -13; + a4.a0.a1 = 14; + a4.a1.a0 = -15.0; + a5.a0.a0 = 16; + a5.a0.a1 = -17; + a5.a1.a0 = 18.0; + a6.a0.a0 = -19; + a6.a0.a1 = 20; + a6.a1.a0 = -21.0; + a7.a0.a0 = 22; + a7.a0.a1 = -23; + a7.a1.a0 = 24.0; + a8.a0.a0 = -25; + a8.a0.a1 = 26; + a8.a1.a0 = -27.0; + a9.a0.a0 = 28; + a9.a0.a1 = -29; + a9.a1.a0 = 30.0; + + final result = passStruct8BytesNestedMixedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(15.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct16BytesNestedIntx2Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(Struct16BytesNestedInt, Struct16BytesNestedInt), + int Function(Struct16BytesNestedInt, + Struct16BytesNestedInt)>("PassStruct16BytesNestedIntx2", isLeaf: true); + +/// Deeper nested struct to test recursive member access. +void testPassStruct16BytesNestedIntx2Leaf() { + final a0Pointer = calloc(); + final Struct16BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0.a0 = -1; + a0.a0.a0.a1 = 2; + a0.a0.a1.a0 = -3; + a0.a0.a1.a1 = 4; + a0.a1.a0.a0 = -5; + a0.a1.a0.a1 = 6; + a0.a1.a1.a0 = -7; + a0.a1.a1.a1 = 8; + a1.a0.a0.a0 = -9; + a1.a0.a0.a1 = 10; + a1.a0.a1.a0 = -11; + a1.a0.a1.a1 = 12; + a1.a1.a0.a0 = -13; + a1.a1.a0.a1 = 14; + a1.a1.a1.a0 = -15; + a1.a1.a1.a1 = 16; + + final result = passStruct16BytesNestedIntx2Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(8, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final passStruct32BytesNestedIntx2Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(Struct32BytesNestedInt, Struct32BytesNestedInt), + int Function(Struct32BytesNestedInt, + Struct32BytesNestedInt)>("PassStruct32BytesNestedIntx2", isLeaf: true); + +/// Even deeper nested struct to test recursive member access. +void testPassStruct32BytesNestedIntx2Leaf() { + final a0Pointer = calloc(); + final Struct32BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct32BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0.a0.a0 = -1; + a0.a0.a0.a0.a1 = 2; + a0.a0.a0.a1.a0 = -3; + a0.a0.a0.a1.a1 = 4; + a0.a0.a1.a0.a0 = -5; + a0.a0.a1.a0.a1 = 6; + a0.a0.a1.a1.a0 = -7; + a0.a0.a1.a1.a1 = 8; + a0.a1.a0.a0.a0 = -9; + a0.a1.a0.a0.a1 = 10; + a0.a1.a0.a1.a0 = -11; + a0.a1.a0.a1.a1 = 12; + a0.a1.a1.a0.a0 = -13; + a0.a1.a1.a0.a1 = 14; + a0.a1.a1.a1.a0 = -15; + a0.a1.a1.a1.a1 = 16; + a1.a0.a0.a0.a0 = -17; + a1.a0.a0.a0.a1 = 18; + a1.a0.a0.a1.a0 = -19; + a1.a0.a0.a1.a1 = 20; + a1.a0.a1.a0.a0 = -21; + a1.a0.a1.a0.a1 = 22; + a1.a0.a1.a1.a0 = -23; + a1.a0.a1.a1.a1 = 24; + a1.a1.a0.a0.a0 = -25; + a1.a1.a0.a0.a1 = 26; + a1.a1.a0.a1.a0 = -27; + a1.a1.a0.a1.a1 = 28; + a1.a1.a1.a0.a0 = -29; + a1.a1.a1.a0.a1 = 30; + a1.a1.a1.a1.a0 = -31; + a1.a1.a1.a1.a1 = 32; + + final result = passStruct32BytesNestedIntx2Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(16, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final passStructNestedIntStructAlignmentInt16Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function(StructNestedIntStructAlignmentInt16), + int Function(StructNestedIntStructAlignmentInt16)>( + "PassStructNestedIntStructAlignmentInt16", + isLeaf: true); + +/// Test alignment and padding of nested struct with 16 byte int. +void testPassStructNestedIntStructAlignmentInt16Leaf() { + final a0Pointer = calloc(); + final StructNestedIntStructAlignmentInt16 a0 = a0Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a0.a1.a0 = 4; + a0.a1.a1 = -5; + a0.a1.a2 = 6; + + final result = passStructNestedIntStructAlignmentInt16Leaf(a0); + + print("result = $result"); + + Expect.equals(3, result); + + calloc.free(a0Pointer); +} + +final passStructNestedIntStructAlignmentInt32Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function(StructNestedIntStructAlignmentInt32), + int Function(StructNestedIntStructAlignmentInt32)>( + "PassStructNestedIntStructAlignmentInt32", + isLeaf: true); + +/// Test alignment and padding of nested struct with 32 byte int. +void testPassStructNestedIntStructAlignmentInt32Leaf() { + final a0Pointer = calloc(); + final StructNestedIntStructAlignmentInt32 a0 = a0Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a0.a1.a0 = 4; + a0.a1.a1 = -5; + a0.a1.a2 = 6; + + final result = passStructNestedIntStructAlignmentInt32Leaf(a0); + + print("result = $result"); + + Expect.equals(3, result); + + calloc.free(a0Pointer); +} + +final passStructNestedIntStructAlignmentInt64Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function(StructNestedIntStructAlignmentInt64), + int Function(StructNestedIntStructAlignmentInt64)>( + "PassStructNestedIntStructAlignmentInt64", + isLeaf: true); + +/// Test alignment and padding of nested struct with 64 byte int. +void testPassStructNestedIntStructAlignmentInt64Leaf() { + final a0Pointer = calloc(); + final StructNestedIntStructAlignmentInt64 a0 = a0Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a0.a1.a0 = 4; + a0.a1.a1 = -5; + a0.a1.a2 = 6; + + final result = passStructNestedIntStructAlignmentInt64Leaf(a0); + + print("result = $result"); + + Expect.equals(3, result); + + calloc.free(a0Pointer); +} + +final passStructNestedIrregularEvenBiggerx4Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger), + double Function( + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger)>( + "PassStructNestedIrregularEvenBiggerx4", + isLeaf: true); + +/// Return big irregular struct as smoke test. +void testPassStructNestedIrregularEvenBiggerx4Leaf() { + final a0Pointer = calloc(); + final StructNestedIrregularEvenBigger a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructNestedIrregularEvenBigger a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructNestedIrregularEvenBigger a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructNestedIrregularEvenBigger a3 = a3Pointer.ref; + + a0.a0 = 1; + a0.a1.a0.a0 = 2; + a0.a1.a0.a1.a0.a0 = -3; + a0.a1.a0.a1.a0.a1 = 4; + a0.a1.a0.a1.a1.a0 = -5.0; + a0.a1.a0.a2 = 6; + a0.a1.a0.a3.a0.a0 = -7.0; + a0.a1.a0.a3.a1 = 8.0; + a0.a1.a0.a4 = 9; + a0.a1.a0.a5.a0.a0 = 10.0; + a0.a1.a0.a5.a1.a0 = -11.0; + a0.a1.a0.a6 = 12; + a0.a1.a1.a0.a0 = -13; + a0.a1.a1.a0.a1 = 14; + a0.a1.a1.a1.a0 = -15.0; + a0.a1.a2 = 16.0; + a0.a1.a3 = -17.0; + a0.a2.a0.a0 = 18; + a0.a2.a0.a1.a0.a0 = -19; + a0.a2.a0.a1.a0.a1 = 20; + a0.a2.a0.a1.a1.a0 = -21.0; + a0.a2.a0.a2 = 22; + a0.a2.a0.a3.a0.a0 = -23.0; + a0.a2.a0.a3.a1 = 24.0; + a0.a2.a0.a4 = 25; + a0.a2.a0.a5.a0.a0 = 26.0; + a0.a2.a0.a5.a1.a0 = -27.0; + a0.a2.a0.a6 = 28; + a0.a2.a1.a0.a0 = -29; + a0.a2.a1.a0.a1 = 30; + a0.a2.a1.a1.a0 = -31.0; + a0.a2.a2 = 32.0; + a0.a2.a3 = -33.0; + a0.a3 = 34.0; + a1.a0 = 35; + a1.a1.a0.a0 = 36; + a1.a1.a0.a1.a0.a0 = -37; + a1.a1.a0.a1.a0.a1 = 38; + a1.a1.a0.a1.a1.a0 = -39.0; + a1.a1.a0.a2 = 40; + a1.a1.a0.a3.a0.a0 = -41.0; + a1.a1.a0.a3.a1 = 42.0; + a1.a1.a0.a4 = 43; + a1.a1.a0.a5.a0.a0 = 44.0; + a1.a1.a0.a5.a1.a0 = -45.0; + a1.a1.a0.a6 = 46; + a1.a1.a1.a0.a0 = -47; + a1.a1.a1.a0.a1 = 48; + a1.a1.a1.a1.a0 = -49.0; + a1.a1.a2 = 50.0; + a1.a1.a3 = -51.0; + a1.a2.a0.a0 = 52; + a1.a2.a0.a1.a0.a0 = -53; + a1.a2.a0.a1.a0.a1 = 54; + a1.a2.a0.a1.a1.a0 = -55.0; + a1.a2.a0.a2 = 56; + a1.a2.a0.a3.a0.a0 = -57.0; + a1.a2.a0.a3.a1 = 58.0; + a1.a2.a0.a4 = 59; + a1.a2.a0.a5.a0.a0 = 60.0; + a1.a2.a0.a5.a1.a0 = -61.0; + a1.a2.a0.a6 = 62; + a1.a2.a1.a0.a0 = -63; + a1.a2.a1.a0.a1 = 64; + a1.a2.a1.a1.a0 = -65.0; + a1.a2.a2 = 66.0; + a1.a2.a3 = -67.0; + a1.a3 = 68.0; + a2.a0 = 69; + a2.a1.a0.a0 = 70; + a2.a1.a0.a1.a0.a0 = -71; + a2.a1.a0.a1.a0.a1 = 72; + a2.a1.a0.a1.a1.a0 = -73.0; + a2.a1.a0.a2 = 74; + a2.a1.a0.a3.a0.a0 = -75.0; + a2.a1.a0.a3.a1 = 76.0; + a2.a1.a0.a4 = 77; + a2.a1.a0.a5.a0.a0 = 78.0; + a2.a1.a0.a5.a1.a0 = -79.0; + a2.a1.a0.a6 = 80; + a2.a1.a1.a0.a0 = -81; + a2.a1.a1.a0.a1 = 82; + a2.a1.a1.a1.a0 = -83.0; + a2.a1.a2 = 84.0; + a2.a1.a3 = -85.0; + a2.a2.a0.a0 = 86; + a2.a2.a0.a1.a0.a0 = -87; + a2.a2.a0.a1.a0.a1 = 88; + a2.a2.a0.a1.a1.a0 = -89.0; + a2.a2.a0.a2 = 90; + a2.a2.a0.a3.a0.a0 = -91.0; + a2.a2.a0.a3.a1 = 92.0; + a2.a2.a0.a4 = 93; + a2.a2.a0.a5.a0.a0 = 94.0; + a2.a2.a0.a5.a1.a0 = -95.0; + a2.a2.a0.a6 = 96; + a2.a2.a1.a0.a0 = -97; + a2.a2.a1.a0.a1 = 98; + a2.a2.a1.a1.a0 = -99.0; + a2.a2.a2 = 100.0; + a2.a2.a3 = -101.0; + a2.a3 = 102.0; + a3.a0 = 103; + a3.a1.a0.a0 = 104; + a3.a1.a0.a1.a0.a0 = -105; + a3.a1.a0.a1.a0.a1 = 106; + a3.a1.a0.a1.a1.a0 = -107.0; + a3.a1.a0.a2 = 108; + a3.a1.a0.a3.a0.a0 = -109.0; + a3.a1.a0.a3.a1 = 110.0; + a3.a1.a0.a4 = 111; + a3.a1.a0.a5.a0.a0 = 112.0; + a3.a1.a0.a5.a1.a0 = -113.0; + a3.a1.a0.a6 = 114; + a3.a1.a1.a0.a0 = -115; + a3.a1.a1.a0.a1 = 116; + a3.a1.a1.a1.a0 = -117.0; + a3.a1.a2 = 118.0; + a3.a1.a3 = -119.0; + a3.a2.a0.a0 = 120; + a3.a2.a0.a1.a0.a0 = -121; + a3.a2.a0.a1.a0.a1 = 122; + a3.a2.a0.a1.a1.a0 = -123.0; + a3.a2.a0.a2 = 124; + a3.a2.a0.a3.a0.a0 = -125.0; + a3.a2.a0.a3.a1 = 126.0; + a3.a2.a0.a4 = 127; + a3.a2.a0.a5.a0.a0 = 128.0; + a3.a2.a0.a5.a1.a0 = -129.0; + a3.a2.a0.a6 = 130; + a3.a2.a1.a0.a0 = -131; + a3.a2.a1.a0.a1 = 132; + a3.a2.a1.a1.a0 = -133.0; + a3.a2.a2 = 134.0; + a3.a2.a3 = -135.0; + a3.a3 = 136.0; + + final result = passStructNestedIrregularEvenBiggerx4Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(1572.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); +} + +final passStruct8BytesInlineArrayIntx4Leaf = ffiTestFunctions.lookupFunction< + Int32 Function(Struct8BytesInlineArrayInt, Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt, Struct8BytesInlineArrayInt), + int Function( + Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt)>("PassStruct8BytesInlineArrayIntx4", + isLeaf: true); + +/// Simple struct with inline array. +void testPassStruct8BytesInlineArrayIntx4Leaf() { + final a0Pointer = calloc(); + final Struct8BytesInlineArrayInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesInlineArrayInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesInlineArrayInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesInlineArrayInt a3 = a3Pointer.ref; + + a0.a0[0] = 1; + a0.a0[1] = 2; + a0.a0[2] = 3; + a0.a0[3] = 4; + a0.a0[4] = 5; + a0.a0[5] = 6; + a0.a0[6] = 7; + a0.a0[7] = 8; + a1.a0[0] = 9; + a1.a0[1] = 10; + a1.a0[2] = 11; + a1.a0[3] = 12; + a1.a0[4] = 13; + a1.a0[5] = 14; + a1.a0[6] = 15; + a1.a0[7] = 16; + a2.a0[0] = 17; + a2.a0[1] = 18; + a2.a0[2] = 19; + a2.a0[3] = 20; + a2.a0[4] = 21; + a2.a0[5] = 22; + a2.a0[6] = 23; + a2.a0[7] = 24; + a3.a0[0] = 25; + a3.a0[1] = 26; + a3.a0[2] = 27; + a3.a0[3] = 28; + a3.a0[4] = 29; + a3.a0[5] = 30; + a3.a0[6] = 31; + a3.a0[7] = 32; + + final result = passStruct8BytesInlineArrayIntx4Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.equals(528, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); +} + +final passStructInlineArrayIrregularx4Leaf = ffiTestFunctions.lookupFunction< + Int32 Function(StructInlineArrayIrregular, StructInlineArrayIrregular, + StructInlineArrayIrregular, StructInlineArrayIrregular), + int Function( + StructInlineArrayIrregular, + StructInlineArrayIrregular, + StructInlineArrayIrregular, + StructInlineArrayIrregular)>("PassStructInlineArrayIrregularx4", + isLeaf: true); + +/// Irregular struct with inline array. +void testPassStructInlineArrayIrregularx4Leaf() { + final a0Pointer = calloc(); + final StructInlineArrayIrregular a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructInlineArrayIrregular a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructInlineArrayIrregular a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructInlineArrayIrregular a3 = a3Pointer.ref; + + a0.a0[0].a0 = -1; + a0.a0[0].a1 = 2; + a0.a0[1].a0 = -3; + a0.a0[1].a1 = 4; + a0.a1 = 5; + a1.a0[0].a0 = 6; + a1.a0[0].a1 = -7; + a1.a0[1].a0 = 8; + a1.a0[1].a1 = -9; + a1.a1 = 10; + a2.a0[0].a0 = -11; + a2.a0[0].a1 = 12; + a2.a0[1].a0 = -13; + a2.a0[1].a1 = 14; + a2.a1 = 15; + a3.a0[0].a0 = 16; + a3.a0[0].a1 = -17; + a3.a0[1].a0 = 18; + a3.a0[1].a1 = -19; + a3.a1 = 20; + + final result = passStructInlineArrayIrregularx4Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.equals(50, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); +} + +final passStructInlineArray100BytesLeaf = ffiTestFunctions.lookupFunction< + Int32 Function(StructInlineArray100Bytes), + int Function(StructInlineArray100Bytes)>( + "PassStructInlineArray100Bytes", + isLeaf: true); + +/// Regular larger struct with inline array. +void testPassStructInlineArray100BytesLeaf() { + final a0Pointer = calloc(); + final StructInlineArray100Bytes a0 = a0Pointer.ref; + + a0.a0[0] = 1; + a0.a0[1] = 2; + a0.a0[2] = 3; + a0.a0[3] = 4; + a0.a0[4] = 5; + a0.a0[5] = 6; + a0.a0[6] = 7; + a0.a0[7] = 8; + a0.a0[8] = 9; + a0.a0[9] = 10; + a0.a0[10] = 11; + a0.a0[11] = 12; + a0.a0[12] = 13; + a0.a0[13] = 14; + a0.a0[14] = 15; + a0.a0[15] = 16; + a0.a0[16] = 17; + a0.a0[17] = 18; + a0.a0[18] = 19; + a0.a0[19] = 20; + a0.a0[20] = 21; + a0.a0[21] = 22; + a0.a0[22] = 23; + a0.a0[23] = 24; + a0.a0[24] = 25; + a0.a0[25] = 26; + a0.a0[26] = 27; + a0.a0[27] = 28; + a0.a0[28] = 29; + a0.a0[29] = 30; + a0.a0[30] = 31; + a0.a0[31] = 32; + a0.a0[32] = 33; + a0.a0[33] = 34; + a0.a0[34] = 35; + a0.a0[35] = 36; + a0.a0[36] = 37; + a0.a0[37] = 38; + a0.a0[38] = 39; + a0.a0[39] = 40; + a0.a0[40] = 41; + a0.a0[41] = 42; + a0.a0[42] = 43; + a0.a0[43] = 44; + a0.a0[44] = 45; + a0.a0[45] = 46; + a0.a0[46] = 47; + a0.a0[47] = 48; + a0.a0[48] = 49; + a0.a0[49] = 50; + a0.a0[50] = 51; + a0.a0[51] = 52; + a0.a0[52] = 53; + a0.a0[53] = 54; + a0.a0[54] = 55; + a0.a0[55] = 56; + a0.a0[56] = 57; + a0.a0[57] = 58; + a0.a0[58] = 59; + a0.a0[59] = 60; + a0.a0[60] = 61; + a0.a0[61] = 62; + a0.a0[62] = 63; + a0.a0[63] = 64; + a0.a0[64] = 65; + a0.a0[65] = 66; + a0.a0[66] = 67; + a0.a0[67] = 68; + a0.a0[68] = 69; + a0.a0[69] = 70; + a0.a0[70] = 71; + a0.a0[71] = 72; + a0.a0[72] = 73; + a0.a0[73] = 74; + a0.a0[74] = 75; + a0.a0[75] = 76; + a0.a0[76] = 77; + a0.a0[77] = 78; + a0.a0[78] = 79; + a0.a0[79] = 80; + a0.a0[80] = 81; + a0.a0[81] = 82; + a0.a0[82] = 83; + a0.a0[83] = 84; + a0.a0[84] = 85; + a0.a0[85] = 86; + a0.a0[86] = 87; + a0.a0[87] = 88; + a0.a0[88] = 89; + a0.a0[89] = 90; + a0.a0[90] = 91; + a0.a0[91] = 92; + a0.a0[92] = 93; + a0.a0[93] = 94; + a0.a0[94] = 95; + a0.a0[95] = 96; + a0.a0[96] = 97; + a0.a0[97] = 98; + a0.a0[98] = 99; + a0.a0[99] = 100; + + final result = passStructInlineArray100BytesLeaf(a0); + + print("result = $result"); + + Expect.equals(5050, result); + + calloc.free(a0Pointer); +} + +final passStructStruct16BytesHomogeneousFloat2x5Leaf = + ffiTestFunctions.lookupFunction< + Float Function( + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2), + double Function( + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2)>( + "PassStructStruct16BytesHomogeneousFloat2x5", + isLeaf: true); + +/// Arguments in FPU registers on arm hardfp and arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStructStruct16BytesHomogeneousFloat2x5Leaf() { + final a0Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a4 = a4Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + a1.a0.a0 = -5.0; + a1.a1[0].a0 = 6.0; + a1.a1[1].a0 = -7.0; + a1.a2 = 8.0; + a2.a0.a0 = -9.0; + a2.a1[0].a0 = 10.0; + a2.a1[1].a0 = -11.0; + a2.a2 = 12.0; + a3.a0.a0 = -13.0; + a3.a1[0].a0 = 14.0; + a3.a1[1].a0 = -15.0; + a3.a2 = 16.0; + a4.a0.a0 = -17.0; + a4.a1[0].a0 = 18.0; + a4.a1[1].a0 = -19.0; + a4.a2 = 20.0; + + final result = + passStructStruct16BytesHomogeneousFloat2x5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStructStruct32BytesHomogeneousDouble2x5Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2), + double Function( + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2)>( + "PassStructStruct32BytesHomogeneousDouble2x5", + isLeaf: true); + +/// Arguments in FPU registers on arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStructStruct32BytesHomogeneousDouble2x5Leaf() { + final a0Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a4 = a4Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + a1.a0.a0 = -5.0; + a1.a1[0].a0 = 6.0; + a1.a1[1].a0 = -7.0; + a1.a2 = 8.0; + a2.a0.a0 = -9.0; + a2.a1[0].a0 = 10.0; + a2.a1[1].a0 = -11.0; + a2.a2 = 12.0; + a3.a0.a0 = -13.0; + a3.a1[0].a0 = 14.0; + a3.a1[1].a0 = -15.0; + a3.a2 = 16.0; + a4.a0.a0 = -17.0; + a4.a1[0].a0 = 18.0; + a4.a1[1].a0 = -19.0; + a4.a2 = 20.0; + + final result = + passStructStruct32BytesHomogeneousDouble2x5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStructStruct16BytesMixed3x10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3), + double Function( + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3)>("PassStructStruct16BytesMixed3x10", + isLeaf: true); + +/// On x64, arguments are split over FP and int registers. +/// On x64, it will exhaust the integer registers with the 6th argument. +/// The rest goes on the stack. +/// On arm, arguments are 4 byte aligned. +void testPassStructStruct16BytesMixed3x10Leaf() { + final a0Pointer = calloc(); + final StructStruct16BytesMixed3 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructStruct16BytesMixed3 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructStruct16BytesMixed3 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructStruct16BytesMixed3 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final StructStruct16BytesMixed3 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final StructStruct16BytesMixed3 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final StructStruct16BytesMixed3 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final StructStruct16BytesMixed3 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final StructStruct16BytesMixed3 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final StructStruct16BytesMixed3 a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[0].a1 = -3; + a0.a1[0].a2 = 4; + a0.a2[0] = -5; + a0.a2[1] = 6; + a1.a0.a0 = -7.0; + a1.a1[0].a0 = 8.0; + a1.a1[0].a1 = -9; + a1.a1[0].a2 = 10; + a1.a2[0] = -11; + a1.a2[1] = 12; + a2.a0.a0 = -13.0; + a2.a1[0].a0 = 14.0; + a2.a1[0].a1 = -15; + a2.a1[0].a2 = 16; + a2.a2[0] = -17; + a2.a2[1] = 18; + a3.a0.a0 = -19.0; + a3.a1[0].a0 = 20.0; + a3.a1[0].a1 = -21; + a3.a1[0].a2 = 22; + a3.a2[0] = -23; + a3.a2[1] = 24; + a4.a0.a0 = -25.0; + a4.a1[0].a0 = 26.0; + a4.a1[0].a1 = -27; + a4.a1[0].a2 = 28; + a4.a2[0] = -29; + a4.a2[1] = 30; + a5.a0.a0 = -31.0; + a5.a1[0].a0 = 32.0; + a5.a1[0].a1 = -33; + a5.a1[0].a2 = 34; + a5.a2[0] = -35; + a5.a2[1] = 36; + a6.a0.a0 = -37.0; + a6.a1[0].a0 = 38.0; + a6.a1[0].a1 = -39; + a6.a1[0].a2 = 40; + a6.a2[0] = -41; + a6.a2[1] = 42; + a7.a0.a0 = -43.0; + a7.a1[0].a0 = 44.0; + a7.a1[0].a1 = -45; + a7.a1[0].a2 = 46; + a7.a2[0] = -47; + a7.a2[1] = 48; + a8.a0.a0 = -49.0; + a8.a1[0].a0 = 50.0; + a8.a1[0].a1 = -51; + a8.a1[0].a2 = 52; + a8.a2[0] = -53; + a8.a2[1] = 54; + a9.a0.a0 = -55.0; + a9.a1[0].a0 = 56.0; + a9.a1[0].a1 = -57; + a9.a1[0].a2 = 58; + a9.a2[0] = -59; + a9.a2[1] = 60; + + final result = passStructStruct16BytesMixed3x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(30.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUint8Struct32BytesInlineArrayMultiDimensionalILeaf = + ffiTestFunctions.lookupFunction< + Uint32 Function( + Uint8, + Struct32BytesInlineArrayMultiDimensionalInt, + Uint8, + Struct8BytesInlineArrayMultiDimensionalInt, + Uint8, + Struct8BytesInlineArrayMultiDimensionalInt, + Uint8), + int Function( + int, + Struct32BytesInlineArrayMultiDimensionalInt, + int, + Struct8BytesInlineArrayMultiDimensionalInt, + int, + Struct8BytesInlineArrayMultiDimensionalInt, + int)>("PassUint8Struct32BytesInlineArrayMultiDimensionalI", + isLeaf: true); + +/// Test multi dimensional inline array struct as argument. +void testPassUint8Struct32BytesInlineArrayMultiDimensionalILeaf() { + int a0; + final a1Pointer = calloc(); + final Struct32BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref; + int a2; + final a3Pointer = calloc(); + final Struct8BytesInlineArrayMultiDimensionalInt a3 = a3Pointer.ref; + int a4; + final a5Pointer = calloc(); + final Struct8BytesInlineArrayMultiDimensionalInt a5 = a5Pointer.ref; + int a6; + + a0 = 1; + a1.a0[0][0][0][0][0] = 2; + a1.a0[0][0][0][0][1] = 3; + a1.a0[0][0][0][1][0] = 4; + a1.a0[0][0][0][1][1] = 5; + a1.a0[0][0][1][0][0] = 6; + a1.a0[0][0][1][0][1] = 7; + a1.a0[0][0][1][1][0] = 8; + a1.a0[0][0][1][1][1] = 9; + a1.a0[0][1][0][0][0] = 10; + a1.a0[0][1][0][0][1] = 11; + a1.a0[0][1][0][1][0] = 12; + a1.a0[0][1][0][1][1] = 13; + a1.a0[0][1][1][0][0] = 14; + a1.a0[0][1][1][0][1] = 15; + a1.a0[0][1][1][1][0] = 16; + a1.a0[0][1][1][1][1] = 17; + a1.a0[1][0][0][0][0] = 18; + a1.a0[1][0][0][0][1] = 19; + a1.a0[1][0][0][1][0] = 20; + a1.a0[1][0][0][1][1] = 21; + a1.a0[1][0][1][0][0] = 22; + a1.a0[1][0][1][0][1] = 23; + a1.a0[1][0][1][1][0] = 24; + a1.a0[1][0][1][1][1] = 25; + a1.a0[1][1][0][0][0] = 26; + a1.a0[1][1][0][0][1] = 27; + a1.a0[1][1][0][1][0] = 28; + a1.a0[1][1][0][1][1] = 29; + a1.a0[1][1][1][0][0] = 30; + a1.a0[1][1][1][0][1] = 31; + a1.a0[1][1][1][1][0] = 32; + a1.a0[1][1][1][1][1] = 33; + a2 = 34; + a3.a0[0][0][0] = 35; + a3.a0[0][0][1] = 36; + a3.a0[0][1][0] = 37; + a3.a0[0][1][1] = 38; + a3.a0[1][0][0] = 39; + a3.a0[1][0][1] = 40; + a3.a0[1][1][0] = 41; + a3.a0[1][1][1] = 42; + a4 = 43; + a5.a0[0][0][0] = 44; + a5.a0[0][0][1] = 45; + a5.a0[0][1][0] = 46; + a5.a0[0][1][1] = 47; + a5.a0[1][0][0] = 48; + a5.a0[1][0][1] = 49; + a5.a0[1][1][0] = 50; + a5.a0[1][1][1] = 51; + a6 = 52; + + final result = passUint8Struct32BytesInlineArrayMultiDimensionalILeaf( + a0, a1, a2, a3, a4, a5, a6); + + print("result = $result"); + + Expect.equals(1378, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); +} + +final passUint8Struct4BytesInlineArrayMultiDimensionalInLeaf = + ffiTestFunctions.lookupFunction< + Uint32 Function( + Uint8, Struct4BytesInlineArrayMultiDimensionalInt, Uint8), + int Function(int, Struct4BytesInlineArrayMultiDimensionalInt, int)>( + "PassUint8Struct4BytesInlineArrayMultiDimensionalIn", + isLeaf: true); + +/// Test struct in multi dimensional inline array. +void testPassUint8Struct4BytesInlineArrayMultiDimensionalInLeaf() { + int a0; + final a1Pointer = calloc(); + final Struct4BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref; + int a2; + + a0 = 1; + a1.a0[0][0].a0 = 2; + a1.a0[0][1].a0 = -3; + a1.a0[1][0].a0 = 4; + a1.a0[1][1].a0 = -5; + a2 = 6; + + final result = + passUint8Struct4BytesInlineArrayMultiDimensionalInLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(5, result); + + calloc.free(a1Pointer); +} + +final passStruct3BytesPackedIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt), + int Function( + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt)>("PassStruct3BytesPackedIntx10", isLeaf: true); + +/// Small struct with mis-aligned member. +void testPassStruct3BytesPackedIntx10Leaf() { + final a0Pointer = calloc(); + final Struct3BytesPackedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct3BytesPackedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct3BytesPackedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct3BytesPackedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct3BytesPackedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct3BytesPackedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct3BytesPackedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct3BytesPackedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct3BytesPackedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct3BytesPackedInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = + passStruct3BytesPackedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesPackedIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt), + int Function( + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt)>("PassStruct8BytesPackedIntx10", isLeaf: true); + +/// Struct with mis-aligned member. +void testPassStruct8BytesPackedIntx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesPackedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesPackedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesPackedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesPackedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesPackedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesPackedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesPackedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesPackedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesPackedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesPackedInt a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a1.a0 = 6; + a1.a1 = 7; + a1.a2 = 8; + a1.a3 = 9; + a1.a4 = 10; + a2.a0 = 11; + a2.a1 = 12; + a2.a2 = 13; + a2.a3 = 14; + a2.a4 = 15; + a3.a0 = 16; + a3.a1 = 17; + a3.a2 = 18; + a3.a3 = 19; + a3.a4 = 20; + a4.a0 = 21; + a4.a1 = 22; + a4.a2 = 23; + a4.a3 = 24; + a4.a4 = 25; + a5.a0 = 26; + a5.a1 = 27; + a5.a2 = 28; + a5.a3 = 29; + a5.a4 = 30; + a6.a0 = 31; + a6.a1 = 32; + a6.a2 = 33; + a6.a3 = 34; + a6.a4 = 35; + a7.a0 = 36; + a7.a1 = 37; + a7.a2 = 38; + a7.a3 = 39; + a7.a4 = 40; + a8.a0 = 41; + a8.a1 = 42; + a8.a2 = 43; + a8.a3 = 44; + a8.a4 = 45; + a9.a0 = 46; + a9.a1 = 47; + a9.a2 = 48; + a9.a3 = 49; + a9.a4 = 50; + + final result = + passStruct8BytesPackedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(1275, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct9BytesPackedMixedx10DoubleInt32Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Double, + Int32), + double Function( + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + double, + int)>("PassStruct9BytesPackedMixedx10DoubleInt32", isLeaf: true); + +/// Struct with mis-aligned member. +/// Tests backfilling of CPU and FPU registers. +void testPassStruct9BytesPackedMixedx10DoubleInt32Leaf() { + final a0Pointer = calloc(); + final Struct9BytesPackedMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct9BytesPackedMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct9BytesPackedMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct9BytesPackedMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct9BytesPackedMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct9BytesPackedMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct9BytesPackedMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct9BytesPackedMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct9BytesPackedMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct9BytesPackedMixed a9 = a9Pointer.ref; + double a10; + int a11; + + a0.a0 = 1; + a0.a1 = 2.0; + a1.a0 = 3; + a1.a1 = 4.0; + a2.a0 = 5; + a2.a1 = 6.0; + a3.a0 = 7; + a3.a1 = 8.0; + a4.a0 = 9; + a4.a1 = 10.0; + a5.a0 = 11; + a5.a1 = 12.0; + a6.a0 = 13; + a6.a1 = 14.0; + a7.a0 = 15; + a7.a1 = 16.0; + a8.a0 = 17; + a8.a1 = 18.0; + a9.a0 = 19; + a9.a1 = 20.0; + a10 = -21.0; + a11 = 22; + + final result = passStruct9BytesPackedMixedx10DoubleInt32Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + + print("result = $result"); + + Expect.approxEquals(211.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct5BytesPackedMixedLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct5BytesPackedMixed), + double Function( + Struct5BytesPackedMixed)>("PassStruct5BytesPackedMixed", isLeaf: true); + +/// This packed struct happens to have only aligned members. +void testPassStruct5BytesPackedMixedLeaf() { + final a0Pointer = calloc(); + final Struct5BytesPackedMixed a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2; + + final result = passStruct5BytesPackedMixedLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(1.0, result); + + calloc.free(a0Pointer); +} + +final passStructNestedAlignmentStruct5BytesPackedMixedLeaf = + ffiTestFunctions.lookupFunction< + Double Function(StructNestedAlignmentStruct5BytesPackedMixed), + double Function(StructNestedAlignmentStruct5BytesPackedMixed)>( + "PassStructNestedAlignmentStruct5BytesPackedMixed", + isLeaf: true); + +/// Check alignment of packed struct in non-packed struct. +void testPassStructNestedAlignmentStruct5BytesPackedMixedLeaf() { + final a0Pointer = calloc(); + final StructNestedAlignmentStruct5BytesPackedMixed a0 = a0Pointer.ref; + + a0.a0 = 1; + a0.a1.a0 = 2.0; + a0.a1.a1 = 3; + + final result = passStructNestedAlignmentStruct5BytesPackedMixedLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(6.0, result); + + calloc.free(a0Pointer); +} + +final passStruct6BytesInlineArrayIntLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct6BytesInlineArrayInt), + double Function(Struct6BytesInlineArrayInt)>( + "PassStruct6BytesInlineArrayInt", + isLeaf: true); + +/// Check alignment of packed struct array in non-packed struct. +void testPassStruct6BytesInlineArrayIntLeaf() { + final a0Pointer = calloc(); + final Struct6BytesInlineArrayInt a0 = a0Pointer.ref; + + a0.a0[0].a0 = -1; + a0.a0[0].a1 = 2; + a0.a0[1].a0 = -3; + a0.a0[1].a1 = 4; + + final result = passStruct6BytesInlineArrayIntLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(2.0, result); + + calloc.free(a0Pointer); +} + +final passStruct15BytesInlineArrayMixedLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct15BytesInlineArrayMixed), + double Function(Struct15BytesInlineArrayMixed)>( + "PassStruct15BytesInlineArrayMixed", + isLeaf: true); + +/// Check alignment of packed struct array in non-packed struct. +void testPassStruct15BytesInlineArrayMixedLeaf() { + final a0Pointer = calloc(); + final Struct15BytesInlineArrayMixed a0 = a0Pointer.ref; + + a0.a0[0].a0 = -1.0; + a0.a0[0].a1 = 2; + a0.a0[1].a0 = -3.0; + a0.a0[1].a1 = 4; + a0.a0[2].a0 = -5.0; + a0.a0[2].a1 = 6; + + final result = passStruct15BytesInlineArrayMixedLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(3.0, result); + + calloc.free(a0Pointer); +} + +final passUnion4BytesMixedx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed), + double Function( + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed)>("PassUnion4BytesMixedx10", isLeaf: true); + +/// Check placement of mixed integer/float union. +void testPassUnion4BytesMixedx10Leaf() { + final a0Pointer = calloc(); + final Union4BytesMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union4BytesMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union4BytesMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union4BytesMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union4BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union4BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union4BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union4BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union4BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union4BytesMixed a9 = a9Pointer.ref; + + a0.a0 = 1; + a1.a0 = 2; + a2.a0 = 3; + a3.a0 = 4; + a4.a0 = 5; + a5.a0 = 6; + a6.a0 = 7; + a7.a0 = 8; + a8.a0 = 9; + a9.a0 = 10; + + final result = + passUnion4BytesMixedx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(55.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion8BytesNestedFloatx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat), + double Function( + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat)>("PassUnion8BytesNestedFloatx10", isLeaf: true); + +/// Check placement of mixed floats union. +void testPassUnion8BytesNestedFloatx10Leaf() { + final a0Pointer = calloc(); + final Union8BytesNestedFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union8BytesNestedFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union8BytesNestedFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union8BytesNestedFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union8BytesNestedFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union8BytesNestedFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union8BytesNestedFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union8BytesNestedFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union8BytesNestedFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union8BytesNestedFloat a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a1.a0 = 2.0; + a2.a0 = -3.0; + a3.a0 = 4.0; + a4.a0 = -5.0; + a5.a0 = 6.0; + a6.a0 = -7.0; + a7.a0 = 8.0; + a8.a0 = -9.0; + a9.a0 = 10.0; + + final result = + passUnion8BytesNestedFloatx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(5.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion9BytesNestedIntx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt), + double Function( + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt)>("PassUnion9BytesNestedIntx10", isLeaf: true); + +/// Mixed-size union argument. +void testPassUnion9BytesNestedIntx10Leaf() { + final a0Pointer = calloc(); + final Union9BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union9BytesNestedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union9BytesNestedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union9BytesNestedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union9BytesNestedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union9BytesNestedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union9BytesNestedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union9BytesNestedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union9BytesNestedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union9BytesNestedInt a9 = a9Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a1.a0.a0 = 4; + a1.a0.a1 = -5; + a1.a0.a2 = 6; + a2.a0.a0 = -7; + a2.a0.a1 = 8; + a2.a0.a2 = -9; + a3.a0.a0 = 10; + a3.a0.a1 = -11; + a3.a0.a2 = 12; + a4.a0.a0 = -13; + a4.a0.a1 = 14; + a4.a0.a2 = -15; + a5.a0.a0 = 16; + a5.a0.a1 = -17; + a5.a0.a2 = 18; + a6.a0.a0 = -19; + a6.a0.a1 = 20; + a6.a0.a2 = -21; + a7.a0.a0 = 22; + a7.a0.a1 = -23; + a7.a0.a2 = 24; + a8.a0.a0 = -25; + a8.a0.a1 = 26; + a8.a0.a2 = -27; + a9.a0.a0 = 28; + a9.a0.a1 = -29; + a9.a0.a2 = 30; + + final result = + passUnion9BytesNestedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(15.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion16BytesNestedInlineArrayFloatx10Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat), + double Function( + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat)>( + "PassUnion16BytesNestedInlineArrayFloatx10", + isLeaf: true); + +/// Union with homogenous floats. +void testPassUnion16BytesNestedInlineArrayFloatx10Leaf() { + final a0Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a9 = a9Pointer.ref; + + a0.a0[0] = -1.0; + a0.a0[1] = 2.0; + a0.a0[2] = -3.0; + a0.a0[3] = 4.0; + a1.a0[0] = -5.0; + a1.a0[1] = 6.0; + a1.a0[2] = -7.0; + a1.a0[3] = 8.0; + a2.a0[0] = -9.0; + a2.a0[1] = 10.0; + a2.a0[2] = -11.0; + a2.a0[3] = 12.0; + a3.a0[0] = -13.0; + a3.a0[1] = 14.0; + a3.a0[2] = -15.0; + a3.a0[3] = 16.0; + a4.a0[0] = -17.0; + a4.a0[1] = 18.0; + a4.a0[2] = -19.0; + a4.a0[3] = 20.0; + a5.a0[0] = -21.0; + a5.a0[1] = 22.0; + a5.a0[2] = -23.0; + a5.a0[3] = 24.0; + a6.a0[0] = -25.0; + a6.a0[1] = 26.0; + a6.a0[2] = -27.0; + a6.a0[3] = 28.0; + a7.a0[0] = -29.0; + a7.a0[1] = 30.0; + a7.a0[2] = -31.0; + a7.a0[3] = 32.0; + a8.a0[0] = -33.0; + a8.a0[1] = 34.0; + a8.a0[2] = -35.0; + a8.a0[3] = 36.0; + a9.a0[0] = -37.0; + a9.a0[1] = 38.0; + a9.a0[2] = -39.0; + a9.a0[3] = 40.0; + + final result = passUnion16BytesNestedInlineArrayFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(20.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion16BytesNestedFloatx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat), + double Function( + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat)>("PassUnion16BytesNestedFloatx10", + isLeaf: true); + +/// Union with homogenous floats. +void testPassUnion16BytesNestedFloatx10Leaf() { + final a0Pointer = calloc(); + final Union16BytesNestedFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union16BytesNestedFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union16BytesNestedFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union16BytesNestedFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union16BytesNestedFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union16BytesNestedFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union16BytesNestedFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union16BytesNestedFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union16BytesNestedFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union16BytesNestedFloat a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a0.a1 = 2.0; + a1.a0.a0 = -3.0; + a1.a0.a1 = 4.0; + a2.a0.a0 = -5.0; + a2.a0.a1 = 6.0; + a3.a0.a0 = -7.0; + a3.a0.a1 = 8.0; + a4.a0.a0 = -9.0; + a4.a0.a1 = 10.0; + a5.a0.a0 = -11.0; + a5.a0.a1 = 12.0; + a6.a0.a0 = -13.0; + a6.a0.a1 = 14.0; + a7.a0.a0 = -15.0; + a7.a0.a1 = 16.0; + a8.a0.a0 = -17.0; + a8.a0.a1 = 18.0; + a9.a0.a0 = -19.0; + a9.a0.a1 = 20.0; + + final result = passUnion16BytesNestedFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final returnStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction< + Struct1ByteInt Function(Int8), + Struct1ByteInt Function(int)>("ReturnStruct1ByteInt", isLeaf: true); + +/// Smallest struct with data. +void testReturnStruct1ByteIntLeaf() { + int a0; + + a0 = -1; + + final result = returnStruct1ByteIntLeaf(a0); + + print("result = $result"); + + Expect.equals(a0, result.a0); +} + +final returnStruct3BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct3BytesHomogeneousUint8 Function(Uint8, Uint8, Uint8), + Struct3BytesHomogeneousUint8 Function( + int, int, int)>("ReturnStruct3BytesHomogeneousUint8", isLeaf: true); + +/// Smaller than word size return value on all architectures. +void testReturnStruct3BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + + a0 = 1; + a1 = 2; + a2 = 3; + + final result = returnStruct3BytesHomogeneousUint8Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct3BytesInt2ByteAlignedLeaf = ffiTestFunctions.lookupFunction< + Struct3BytesInt2ByteAligned Function(Int16, Int8), + Struct3BytesInt2ByteAligned Function( + int, int)>("ReturnStruct3BytesInt2ByteAligned", isLeaf: true); + +/// Smaller than word size return value on all architectures. +/// With alignment rules taken into account size is 4 bytes. +void testReturnStruct3BytesInt2ByteAlignedLeaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct3BytesInt2ByteAlignedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct4BytesHomogeneousInt16Leaf = ffiTestFunctions.lookupFunction< + Struct4BytesHomogeneousInt16 Function(Int16, Int16), + Struct4BytesHomogeneousInt16 Function( + int, int)>("ReturnStruct4BytesHomogeneousInt16", isLeaf: true); + +/// Word size return value on 32 bit architectures.. +void testReturnStruct4BytesHomogeneousInt16Leaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct4BytesHomogeneousInt16Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct7BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct7BytesHomogeneousUint8 Function( + Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8), + Struct7BytesHomogeneousUint8 Function(int, int, int, int, int, int, + int)>("ReturnStruct7BytesHomogeneousUint8", isLeaf: true); + +/// Non-wordsize return value. +void testReturnStruct7BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + + final result = + returnStruct7BytesHomogeneousUint8Leaf(a0, a1, a2, a3, a4, a5, a6); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); +} + +final returnStruct7BytesInt4ByteAlignedLeaf = ffiTestFunctions.lookupFunction< + Struct7BytesInt4ByteAligned Function(Int32, Int16, Int8), + Struct7BytesInt4ByteAligned Function( + int, int, int)>("ReturnStruct7BytesInt4ByteAligned", isLeaf: true); + +/// Non-wordsize return value. +/// With alignment rules taken into account size is 8 bytes. +void testReturnStruct7BytesInt4ByteAlignedLeaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStruct7BytesInt4ByteAlignedLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct8BytesIntLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesInt Function(Int16, Int16, Int32), + Struct8BytesInt Function( + int, int, int)>("ReturnStruct8BytesInt", isLeaf: true); + +/// Return value in integer registers on many architectures. +void testReturnStruct8BytesIntLeaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStruct8BytesIntLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct8BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesHomogeneousFloat Function(Float, Float), + Struct8BytesHomogeneousFloat Function( + double, double)>("ReturnStruct8BytesHomogeneousFloat", isLeaf: true); + +/// Return value in FP registers on many architectures. +void testReturnStruct8BytesHomogeneousFloatLeaf() { + double a0; + double a1; + + a0 = -1.0; + a1 = 2.0; + + final result = returnStruct8BytesHomogeneousFloatLeaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); +} + +final returnStruct8BytesMixedLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesMixed Function(Float, Int16, Int16), + Struct8BytesMixed Function( + double, int, int)>("ReturnStruct8BytesMixed", isLeaf: true); + +/// Return value split over FP and integer register in x64. +void testReturnStruct8BytesMixedLeaf() { + double a0; + int a1; + int a2; + + a0 = -1.0; + a1 = 2; + a2 = -3; + + final result = returnStruct8BytesMixedLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct9BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct9BytesHomogeneousUint8 Function( + Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8), + Struct9BytesHomogeneousUint8 Function(int, int, int, int, int, int, int, + int, int)>("ReturnStruct9BytesHomogeneousUint8", isLeaf: true); + +/// The minimum alignment of this struct is only 1 byte based on its fields. +/// Test that the memory backing these structs is the right size and that +/// dart:ffi trampolines do not write outside this size. +void testReturnStruct9BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + a7 = 8; + a8 = 9; + + final result = returnStruct9BytesHomogeneousUint8Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); + Expect.equals(a7, result.a7); + Expect.equals(a8, result.a8); +} + +final returnStruct9BytesInt4Or8ByteAlignedLeaf = + ffiTestFunctions.lookupFunction< + Struct9BytesInt4Or8ByteAligned Function(Int64, Int8), + Struct9BytesInt4Or8ByteAligned Function( + int, int)>("ReturnStruct9BytesInt4Or8ByteAligned", isLeaf: true); + +/// Return value in two integer registers on x64. +/// With alignment rules taken into account size is 12 or 16 bytes. +void testReturnStruct9BytesInt4Or8ByteAlignedLeaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct9BytesInt4Or8ByteAlignedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct12BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct12BytesHomogeneousFloat Function(Float, Float, Float), + Struct12BytesHomogeneousFloat Function(double, double, + double)>("ReturnStruct12BytesHomogeneousFloat", isLeaf: true); + +/// Return value in FPU registers, but does not use all registers on arm hardfp +/// and arm64. +void testReturnStruct12BytesHomogeneousFloatLeaf() { + double a0; + double a1; + double a2; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + + final result = returnStruct12BytesHomogeneousFloatLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); +} + +final returnStruct16BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct16BytesHomogeneousFloat Function(Float, Float, Float, Float), + Struct16BytesHomogeneousFloat Function(double, double, double, + double)>("ReturnStruct16BytesHomogeneousFloat", isLeaf: true); + +/// Return value in FPU registers on arm hardfp and arm64. +void testReturnStruct16BytesHomogeneousFloatLeaf() { + double a0; + double a1; + double a2; + double a3; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + + final result = returnStruct16BytesHomogeneousFloatLeaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); +} + +final returnStruct16BytesMixedLeaf = ffiTestFunctions.lookupFunction< + Struct16BytesMixed Function(Double, Int64), + Struct16BytesMixed Function( + double, int)>("ReturnStruct16BytesMixed", isLeaf: true); + +/// Return value split over FP and integer register in x64. +void testReturnStruct16BytesMixedLeaf() { + double a0; + int a1; + + a0 = -1.0; + a1 = 2; + + final result = returnStruct16BytesMixedLeaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct16BytesMixed2Leaf = ffiTestFunctions.lookupFunction< + Struct16BytesMixed2 Function(Float, Float, Float, Int32), + Struct16BytesMixed2 Function(double, double, double, + int)>("ReturnStruct16BytesMixed2", isLeaf: true); + +/// Return value split over FP and integer register in x64. +/// The integer register contains half float half int. +void testReturnStruct16BytesMixed2Leaf() { + double a0; + double a1; + double a2; + int a3; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4; + + final result = returnStruct16BytesMixed2Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.equals(a3, result.a3); +} + +final returnStruct17BytesIntLeaf = ffiTestFunctions.lookupFunction< + Struct17BytesInt Function(Int64, Int64, Int8), + Struct17BytesInt Function( + int, int, int)>("ReturnStruct17BytesInt", isLeaf: true); + +/// Rerturn value returned in preallocated space passed by pointer on most ABIs. +/// Is non word size on purpose, to test that structs are rounded up to word size +/// on all ABIs. +void testReturnStruct17BytesIntLeaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStruct17BytesIntLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct19BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct19BytesHomogeneousUint8 Function( + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8), + Struct19BytesHomogeneousUint8 Function( + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int)>("ReturnStruct19BytesHomogeneousUint8", isLeaf: true); + +/// The minimum alignment of this struct is only 1 byte based on its fields. +/// Test that the memory backing these structs is the right size and that +/// dart:ffi trampolines do not write outside this size. +void testReturnStruct19BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + int a9; + int a10; + int a11; + int a12; + int a13; + int a14; + int a15; + int a16; + int a17; + int a18; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + a7 = 8; + a8 = 9; + a9 = 10; + a10 = 11; + a11 = 12; + a12 = 13; + a13 = 14; + a14 = 15; + a15 = 16; + a16 = 17; + a17 = 18; + a18 = 19; + + final result = returnStruct19BytesHomogeneousUint8Leaf(a0, a1, a2, a3, a4, a5, + a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); + Expect.equals(a7, result.a7); + Expect.equals(a8, result.a8); + Expect.equals(a9, result.a9); + Expect.equals(a10, result.a10); + Expect.equals(a11, result.a11); + Expect.equals(a12, result.a12); + Expect.equals(a13, result.a13); + Expect.equals(a14, result.a14); + Expect.equals(a15, result.a15); + Expect.equals(a16, result.a16); + Expect.equals(a17, result.a17); + Expect.equals(a18, result.a18); +} + +final returnStruct20BytesHomogeneousInt32Leaf = ffiTestFunctions.lookupFunction< + Struct20BytesHomogeneousInt32 Function(Int32, Int32, Int32, Int32, Int32), + Struct20BytesHomogeneousInt32 Function(int, int, int, int, + int)>("ReturnStruct20BytesHomogeneousInt32", isLeaf: true); + +/// Return value too big to go in cpu registers on arm64. +void testReturnStruct20BytesHomogeneousInt32Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + + final result = returnStruct20BytesHomogeneousInt32Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); +} + +final returnStruct20BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct20BytesHomogeneousFloat Function(Float, Float, Float, Float, Float), + Struct20BytesHomogeneousFloat Function(double, double, double, double, + double)>("ReturnStruct20BytesHomogeneousFloat", isLeaf: true); + +/// Return value too big to go in FPU registers on x64, arm hardfp and arm64. +void testReturnStruct20BytesHomogeneousFloatLeaf() { + double a0; + double a1; + double a2; + double a3; + double a4; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + a4 = -5.0; + + final result = returnStruct20BytesHomogeneousFloatLeaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); + Expect.approxEquals(a4, result.a4); +} + +final returnStruct32BytesHomogeneousDoubleLeaf = + ffiTestFunctions.lookupFunction< + Struct32BytesHomogeneousDouble Function(Double, Double, Double, Double), + Struct32BytesHomogeneousDouble Function(double, double, double, + double)>("ReturnStruct32BytesHomogeneousDouble", isLeaf: true); + +/// Return value in FPU registers on arm64. +void testReturnStruct32BytesHomogeneousDoubleLeaf() { + double a0; + double a1; + double a2; + double a3; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + + final result = returnStruct32BytesHomogeneousDoubleLeaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); +} + +final returnStruct40BytesHomogeneousDoubleLeaf = + ffiTestFunctions.lookupFunction< + Struct40BytesHomogeneousDouble Function( + Double, Double, Double, Double, Double), + Struct40BytesHomogeneousDouble Function(double, double, double, double, + double)>("ReturnStruct40BytesHomogeneousDouble", isLeaf: true); + +/// Return value too big to go in FPU registers on arm64. +void testReturnStruct40BytesHomogeneousDoubleLeaf() { + double a0; + double a1; + double a2; + double a3; + double a4; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + a4 = -5.0; + + final result = returnStruct40BytesHomogeneousDoubleLeaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); + Expect.approxEquals(a4, result.a4); +} + +final returnStruct1024BytesHomogeneousUint64Leaf = + ffiTestFunctions.lookupFunction< + Struct1024BytesHomogeneousUint64 Function( + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64), + Struct1024BytesHomogeneousUint64 Function( + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int)>("ReturnStruct1024BytesHomogeneousUint64", isLeaf: true); + +/// Test 1kb struct. +void testReturnStruct1024BytesHomogeneousUint64Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + int a9; + int a10; + int a11; + int a12; + int a13; + int a14; + int a15; + int a16; + int a17; + int a18; + int a19; + int a20; + int a21; + int a22; + int a23; + int a24; + int a25; + int a26; + int a27; + int a28; + int a29; + int a30; + int a31; + int a32; + int a33; + int a34; + int a35; + int a36; + int a37; + int a38; + int a39; + int a40; + int a41; + int a42; + int a43; + int a44; + int a45; + int a46; + int a47; + int a48; + int a49; + int a50; + int a51; + int a52; + int a53; + int a54; + int a55; + int a56; + int a57; + int a58; + int a59; + int a60; + int a61; + int a62; + int a63; + int a64; + int a65; + int a66; + int a67; + int a68; + int a69; + int a70; + int a71; + int a72; + int a73; + int a74; + int a75; + int a76; + int a77; + int a78; + int a79; + int a80; + int a81; + int a82; + int a83; + int a84; + int a85; + int a86; + int a87; + int a88; + int a89; + int a90; + int a91; + int a92; + int a93; + int a94; + int a95; + int a96; + int a97; + int a98; + int a99; + int a100; + int a101; + int a102; + int a103; + int a104; + int a105; + int a106; + int a107; + int a108; + int a109; + int a110; + int a111; + int a112; + int a113; + int a114; + int a115; + int a116; + int a117; + int a118; + int a119; + int a120; + int a121; + int a122; + int a123; + int a124; + int a125; + int a126; + int a127; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + a7 = 8; + a8 = 9; + a9 = 10; + a10 = 11; + a11 = 12; + a12 = 13; + a13 = 14; + a14 = 15; + a15 = 16; + a16 = 17; + a17 = 18; + a18 = 19; + a19 = 20; + a20 = 21; + a21 = 22; + a22 = 23; + a23 = 24; + a24 = 25; + a25 = 26; + a26 = 27; + a27 = 28; + a28 = 29; + a29 = 30; + a30 = 31; + a31 = 32; + a32 = 33; + a33 = 34; + a34 = 35; + a35 = 36; + a36 = 37; + a37 = 38; + a38 = 39; + a39 = 40; + a40 = 41; + a41 = 42; + a42 = 43; + a43 = 44; + a44 = 45; + a45 = 46; + a46 = 47; + a47 = 48; + a48 = 49; + a49 = 50; + a50 = 51; + a51 = 52; + a52 = 53; + a53 = 54; + a54 = 55; + a55 = 56; + a56 = 57; + a57 = 58; + a58 = 59; + a59 = 60; + a60 = 61; + a61 = 62; + a62 = 63; + a63 = 64; + a64 = 65; + a65 = 66; + a66 = 67; + a67 = 68; + a68 = 69; + a69 = 70; + a70 = 71; + a71 = 72; + a72 = 73; + a73 = 74; + a74 = 75; + a75 = 76; + a76 = 77; + a77 = 78; + a78 = 79; + a79 = 80; + a80 = 81; + a81 = 82; + a82 = 83; + a83 = 84; + a84 = 85; + a85 = 86; + a86 = 87; + a87 = 88; + a88 = 89; + a89 = 90; + a90 = 91; + a91 = 92; + a92 = 93; + a93 = 94; + a94 = 95; + a95 = 96; + a96 = 97; + a97 = 98; + a98 = 99; + a99 = 100; + a100 = 101; + a101 = 102; + a102 = 103; + a103 = 104; + a104 = 105; + a105 = 106; + a106 = 107; + a107 = 108; + a108 = 109; + a109 = 110; + a110 = 111; + a111 = 112; + a112 = 113; + a113 = 114; + a114 = 115; + a115 = 116; + a116 = 117; + a117 = 118; + a118 = 119; + a119 = 120; + a120 = 121; + a121 = 122; + a122 = 123; + a123 = 124; + a124 = 125; + a125 = 126; + a126 = 127; + a127 = 128; + + final result = returnStruct1024BytesHomogeneousUint64Leaf( + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + a17, + a18, + a19, + a20, + a21, + a22, + a23, + a24, + a25, + a26, + a27, + a28, + a29, + a30, + a31, + a32, + a33, + a34, + a35, + a36, + a37, + a38, + a39, + a40, + a41, + a42, + a43, + a44, + a45, + a46, + a47, + a48, + a49, + a50, + a51, + a52, + a53, + a54, + a55, + a56, + a57, + a58, + a59, + a60, + a61, + a62, + a63, + a64, + a65, + a66, + a67, + a68, + a69, + a70, + a71, + a72, + a73, + a74, + a75, + a76, + a77, + a78, + a79, + a80, + a81, + a82, + a83, + a84, + a85, + a86, + a87, + a88, + a89, + a90, + a91, + a92, + a93, + a94, + a95, + a96, + a97, + a98, + a99, + a100, + a101, + a102, + a103, + a104, + a105, + a106, + a107, + a108, + a109, + a110, + a111, + a112, + a113, + a114, + a115, + a116, + a117, + a118, + a119, + a120, + a121, + a122, + a123, + a124, + a125, + a126, + a127); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); + Expect.equals(a7, result.a7); + Expect.equals(a8, result.a8); + Expect.equals(a9, result.a9); + Expect.equals(a10, result.a10); + Expect.equals(a11, result.a11); + Expect.equals(a12, result.a12); + Expect.equals(a13, result.a13); + Expect.equals(a14, result.a14); + Expect.equals(a15, result.a15); + Expect.equals(a16, result.a16); + Expect.equals(a17, result.a17); + Expect.equals(a18, result.a18); + Expect.equals(a19, result.a19); + Expect.equals(a20, result.a20); + Expect.equals(a21, result.a21); + Expect.equals(a22, result.a22); + Expect.equals(a23, result.a23); + Expect.equals(a24, result.a24); + Expect.equals(a25, result.a25); + Expect.equals(a26, result.a26); + Expect.equals(a27, result.a27); + Expect.equals(a28, result.a28); + Expect.equals(a29, result.a29); + Expect.equals(a30, result.a30); + Expect.equals(a31, result.a31); + Expect.equals(a32, result.a32); + Expect.equals(a33, result.a33); + Expect.equals(a34, result.a34); + Expect.equals(a35, result.a35); + Expect.equals(a36, result.a36); + Expect.equals(a37, result.a37); + Expect.equals(a38, result.a38); + Expect.equals(a39, result.a39); + Expect.equals(a40, result.a40); + Expect.equals(a41, result.a41); + Expect.equals(a42, result.a42); + Expect.equals(a43, result.a43); + Expect.equals(a44, result.a44); + Expect.equals(a45, result.a45); + Expect.equals(a46, result.a46); + Expect.equals(a47, result.a47); + Expect.equals(a48, result.a48); + Expect.equals(a49, result.a49); + Expect.equals(a50, result.a50); + Expect.equals(a51, result.a51); + Expect.equals(a52, result.a52); + Expect.equals(a53, result.a53); + Expect.equals(a54, result.a54); + Expect.equals(a55, result.a55); + Expect.equals(a56, result.a56); + Expect.equals(a57, result.a57); + Expect.equals(a58, result.a58); + Expect.equals(a59, result.a59); + Expect.equals(a60, result.a60); + Expect.equals(a61, result.a61); + Expect.equals(a62, result.a62); + Expect.equals(a63, result.a63); + Expect.equals(a64, result.a64); + Expect.equals(a65, result.a65); + Expect.equals(a66, result.a66); + Expect.equals(a67, result.a67); + Expect.equals(a68, result.a68); + Expect.equals(a69, result.a69); + Expect.equals(a70, result.a70); + Expect.equals(a71, result.a71); + Expect.equals(a72, result.a72); + Expect.equals(a73, result.a73); + Expect.equals(a74, result.a74); + Expect.equals(a75, result.a75); + Expect.equals(a76, result.a76); + Expect.equals(a77, result.a77); + Expect.equals(a78, result.a78); + Expect.equals(a79, result.a79); + Expect.equals(a80, result.a80); + Expect.equals(a81, result.a81); + Expect.equals(a82, result.a82); + Expect.equals(a83, result.a83); + Expect.equals(a84, result.a84); + Expect.equals(a85, result.a85); + Expect.equals(a86, result.a86); + Expect.equals(a87, result.a87); + Expect.equals(a88, result.a88); + Expect.equals(a89, result.a89); + Expect.equals(a90, result.a90); + Expect.equals(a91, result.a91); + Expect.equals(a92, result.a92); + Expect.equals(a93, result.a93); + Expect.equals(a94, result.a94); + Expect.equals(a95, result.a95); + Expect.equals(a96, result.a96); + Expect.equals(a97, result.a97); + Expect.equals(a98, result.a98); + Expect.equals(a99, result.a99); + Expect.equals(a100, result.a100); + Expect.equals(a101, result.a101); + Expect.equals(a102, result.a102); + Expect.equals(a103, result.a103); + Expect.equals(a104, result.a104); + Expect.equals(a105, result.a105); + Expect.equals(a106, result.a106); + Expect.equals(a107, result.a107); + Expect.equals(a108, result.a108); + Expect.equals(a109, result.a109); + Expect.equals(a110, result.a110); + Expect.equals(a111, result.a111); + Expect.equals(a112, result.a112); + Expect.equals(a113, result.a113); + Expect.equals(a114, result.a114); + Expect.equals(a115, result.a115); + Expect.equals(a116, result.a116); + Expect.equals(a117, result.a117); + Expect.equals(a118, result.a118); + Expect.equals(a119, result.a119); + Expect.equals(a120, result.a120); + Expect.equals(a121, result.a121); + Expect.equals(a122, result.a122); + Expect.equals(a123, result.a123); + Expect.equals(a124, result.a124); + Expect.equals(a125, result.a125); + Expect.equals(a126, result.a126); + Expect.equals(a127, result.a127); +} + +final returnStruct3BytesPackedIntLeaf = ffiTestFunctions.lookupFunction< + Struct3BytesPackedInt Function(Int8, Int16), + Struct3BytesPackedInt Function( + int, int)>("ReturnStruct3BytesPackedInt", isLeaf: true); + +/// Small struct with mis-aligned member. +void testReturnStruct3BytesPackedIntLeaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct3BytesPackedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct8BytesPackedIntLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesPackedInt Function(Uint8, Uint32, Uint8, Uint8, Uint8), + Struct8BytesPackedInt Function( + int, int, int, int, int)>("ReturnStruct8BytesPackedInt", isLeaf: true); + +/// Struct with mis-aligned member. +void testReturnStruct8BytesPackedIntLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + + final result = returnStruct8BytesPackedIntLeaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); +} + +final returnStruct9BytesPackedMixedLeaf = ffiTestFunctions.lookupFunction< + Struct9BytesPackedMixed Function(Uint8, Double), + Struct9BytesPackedMixed Function( + int, double)>("ReturnStruct9BytesPackedMixed", isLeaf: true); + +/// Struct with mis-aligned member. +/// Tests backfilling of CPU and FPU registers. +void testReturnStruct9BytesPackedMixedLeaf() { + int a0; + double a1; + + a0 = 1; + a1 = 2.0; + + final result = returnStruct9BytesPackedMixedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.approxEquals(a1, result.a1); +} + +final returnUnion4BytesMixedLeaf = ffiTestFunctions.lookupFunction< + Union4BytesMixed Function(Uint32), + Union4BytesMixed Function(int)>("ReturnUnion4BytesMixed", isLeaf: true); + +/// Returning a mixed integer/float union. +void testReturnUnion4BytesMixedLeaf() { + int a0; + + a0 = 1; + + final result = returnUnion4BytesMixedLeaf(a0); + + print("result = $result"); + + Expect.equals(a0, result.a0); +} + +final returnUnion8BytesNestedFloatLeaf = ffiTestFunctions.lookupFunction< + Union8BytesNestedFloat Function(Double), + Union8BytesNestedFloat Function( + double)>("ReturnUnion8BytesNestedFloat", isLeaf: true); + +/// Returning a floating point only union. +void testReturnUnion8BytesNestedFloatLeaf() { + double a0; + + a0 = -1.0; + + final result = returnUnion8BytesNestedFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); +} + +final returnUnion9BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Union9BytesNestedInt Function(Struct8BytesInt), + Union9BytesNestedInt Function( + Struct8BytesInt)>("ReturnUnion9BytesNestedInt", isLeaf: true); + +/// Returning a mixed-size union. +void testReturnUnion9BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct8BytesInt a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = returnUnion9BytesNestedIntLeaf(a0); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + + calloc.free(a0Pointer); +} + +final returnUnion16BytesNestedFloatLeaf = ffiTestFunctions.lookupFunction< + Union16BytesNestedFloat Function(Struct8BytesHomogeneousFloat), + Union16BytesNestedFloat Function(Struct8BytesHomogeneousFloat)>( + "ReturnUnion16BytesNestedFloat", + isLeaf: true); + +/// Returning union with homogenous floats. +void testReturnUnion16BytesNestedFloatLeaf() { + final a0Pointer = calloc(); + final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + + final result = returnUnion16BytesNestedFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0.a0); + Expect.approxEquals(a0.a1, result.a0.a1); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction< + Struct1ByteInt Function(Struct1ByteInt), + Struct1ByteInt Function( + Struct1ByteInt)>("ReturnStructArgumentStruct1ByteInt", isLeaf: true); + +/// Test that a struct passed in as argument can be returned. +/// Especially for ffi callbacks. +/// Struct is passed in int registers in most ABIs. +void testReturnStructArgumentStruct1ByteIntLeaf() { + final a0Pointer = calloc(); + final Struct1ByteInt a0 = a0Pointer.ref; + + a0.a0 = -1; + + final result = returnStructArgumentStruct1ByteIntLeaf(a0); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0); + + calloc.free(a0Pointer); +} + +final returnStructArgumentInt32x8Struct1ByteIntLeaf = + ffiTestFunctions + .lookupFunction< + Struct1ByteInt Function(Int32, Int32, Int32, Int32, Int32, Int32, + Int32, Int32, Struct1ByteInt), + Struct1ByteInt Function( + int, int, int, int, int, int, int, int, Struct1ByteInt)>( + "ReturnStructArgumentInt32x8Struct1ByteInt", + isLeaf: true); + +/// Test that a struct passed in as argument can be returned. +/// Especially for ffi callbacks. +/// Struct is passed on stack on all ABIs. +void testReturnStructArgumentInt32x8Struct1ByteIntLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + final a8Pointer = calloc(); + final Struct1ByteInt a8 = a8Pointer.ref; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + a5 = 6; + a6 = -7; + a7 = 8; + a8.a0 = -9; + + final result = returnStructArgumentInt32x8Struct1ByteIntLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.equals(a8.a0, result.a0); + + calloc.free(a8Pointer); +} + +final returnStructArgumentStruct8BytesHomogeneousFloatLeaf = + ffiTestFunctions.lookupFunction< + Struct8BytesHomogeneousFloat Function(Struct8BytesHomogeneousFloat), + Struct8BytesHomogeneousFloat Function( + Struct8BytesHomogeneousFloat)>( + "ReturnStructArgumentStruct8BytesHomogeneousFloat", + isLeaf: true); + +/// Test that a struct passed in as argument can be returned. +/// Especially for ffi callbacks. +/// Struct is passed in float registers in most ABIs. +void testReturnStructArgumentStruct8BytesHomogeneousFloatLeaf() { + final a0Pointer = calloc(); + final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + + final result = returnStructArgumentStruct8BytesHomogeneousFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0); + Expect.approxEquals(a0.a1, result.a1); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStruct20BytesHomogeneousInt32Leaf = + ffiTestFunctions + .lookupFunction< + Struct20BytesHomogeneousInt32 Function( + Struct20BytesHomogeneousInt32), + Struct20BytesHomogeneousInt32 Function( + Struct20BytesHomogeneousInt32)>( + "ReturnStructArgumentStruct20BytesHomogeneousInt32", + isLeaf: true); + +/// On arm64, both argument and return value are passed in by pointer. +void testReturnStructArgumentStruct20BytesHomogeneousInt32Leaf() { + final a0Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a0.a3 = 4; + a0.a4 = -5; + + final result = returnStructArgumentStruct20BytesHomogeneousInt32Leaf(a0); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0); + Expect.equals(a0.a1, result.a1); + Expect.equals(a0.a2, result.a2); + Expect.equals(a0.a3, result.a3); + Expect.equals(a0.a4, result.a4); + + calloc.free(a0Pointer); +} + +final returnStructArgumentInt32x8Struct20BytesHomogeneouLeaf = + ffiTestFunctions.lookupFunction< + Struct20BytesHomogeneousInt32 Function(Int32, Int32, Int32, Int32, + Int32, Int32, Int32, Int32, Struct20BytesHomogeneousInt32), + Struct20BytesHomogeneousInt32 Function(int, int, int, int, int, int, + int, int, Struct20BytesHomogeneousInt32)>( + "ReturnStructArgumentInt32x8Struct20BytesHomogeneou", + isLeaf: true); + +/// On arm64, both argument and return value are passed in by pointer. +/// Ints exhaust registers, so that pointer is passed on stack. +void testReturnStructArgumentInt32x8Struct20BytesHomogeneouLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + final a8Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a8 = a8Pointer.ref; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + a5 = 6; + a6 = -7; + a7 = 8; + a8.a0 = -9; + a8.a1 = 10; + a8.a2 = -11; + a8.a3 = 12; + a8.a4 = -13; + + final result = returnStructArgumentInt32x8Struct20BytesHomogeneouLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.equals(a8.a0, result.a0); + Expect.equals(a8.a1, result.a1); + Expect.equals(a8.a2, result.a2); + Expect.equals(a8.a3, result.a3); + Expect.equals(a8.a4, result.a4); + + calloc.free(a8Pointer); +} + +final returnStructArgumentStruct8BytesInlineArrayIntLeaf = + ffiTestFunctions.lookupFunction< + Struct8BytesInlineArrayInt Function(Struct8BytesInlineArrayInt), + Struct8BytesInlineArrayInt Function(Struct8BytesInlineArrayInt)>( + "ReturnStructArgumentStruct8BytesInlineArrayInt", + isLeaf: true); + +/// Test returning struct with inline array. +void testReturnStructArgumentStruct8BytesInlineArrayIntLeaf() { + final a0Pointer = calloc(); + final Struct8BytesInlineArrayInt a0 = a0Pointer.ref; + + a0.a0[0] = 1; + a0.a0[1] = 2; + a0.a0[2] = 3; + a0.a0[3] = 4; + a0.a0[4] = 5; + a0.a0[5] = 6; + a0.a0[6] = 7; + a0.a0[7] = 8; + + final result = returnStructArgumentStruct8BytesInlineArrayIntLeaf(a0); + + print("result = $result"); + + for (int i = 0; i < 8; i++) { + Expect.equals(a0.a0[i], result.a0[i]); + } + + calloc.free(a0Pointer); +} + +final returnStructArgumentStructStruct16BytesHomogeneousLeaf = + ffiTestFunctions.lookupFunction< + StructStruct16BytesHomogeneousFloat2 Function( + StructStruct16BytesHomogeneousFloat2), + StructStruct16BytesHomogeneousFloat2 Function( + StructStruct16BytesHomogeneousFloat2)>( + "ReturnStructArgumentStructStruct16BytesHomogeneous", + isLeaf: true); + +/// Return value in FPU registers on arm hardfp and arm64. +void testReturnStructArgumentStructStruct16BytesHomogeneousLeaf() { + final a0Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a0 = a0Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + + final result = returnStructArgumentStructStruct16BytesHomogeneousLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0.a0, result.a0.a0); + for (int i = 0; i < 2; i++) { + Expect.approxEquals(a0.a1[i].a0, result.a1[i].a0); + } + Expect.approxEquals(a0.a2, result.a2); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStructStruct32BytesHomogeneousLeaf = + ffiTestFunctions.lookupFunction< + StructStruct32BytesHomogeneousDouble2 Function( + StructStruct32BytesHomogeneousDouble2), + StructStruct32BytesHomogeneousDouble2 Function( + StructStruct32BytesHomogeneousDouble2)>( + "ReturnStructArgumentStructStruct32BytesHomogeneous", + isLeaf: true); + +/// Return value in FPU registers on arm64. +void testReturnStructArgumentStructStruct32BytesHomogeneousLeaf() { + final a0Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a0 = a0Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + + final result = returnStructArgumentStructStruct32BytesHomogeneousLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0.a0, result.a0.a0); + for (int i = 0; i < 2; i++) { + Expect.approxEquals(a0.a1[i].a0, result.a1[i].a0); + } + Expect.approxEquals(a0.a2, result.a2); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStructStruct16BytesMixed3Leaf = + ffiTestFunctions.lookupFunction< + StructStruct16BytesMixed3 Function(StructStruct16BytesMixed3), + StructStruct16BytesMixed3 Function(StructStruct16BytesMixed3)>( + "ReturnStructArgumentStructStruct16BytesMixed3", + isLeaf: true); + +/// On x64 Linux, return value is split over FP and int registers. +void testReturnStructArgumentStructStruct16BytesMixed3Leaf() { + final a0Pointer = calloc(); + final StructStruct16BytesMixed3 a0 = a0Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[0].a1 = -3; + a0.a1[0].a2 = 4; + a0.a2[0] = -5; + a0.a2[1] = 6; + + final result = returnStructArgumentStructStruct16BytesMixed3Leaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0.a0, result.a0.a0); + for (int i = 0; i < 1; i++) { + Expect.approxEquals(a0.a1[i].a0, result.a1[i].a0); + Expect.equals(a0.a1[i].a1, result.a1[i].a1); + Expect.equals(a0.a1[i].a2, result.a1[i].a2); + } + for (int i = 0; i < 2; i++) { + Expect.equals(a0.a2[i], result.a2[i]); + } + + calloc.free(a0Pointer); +} + +final returnStructAlignmentInt16Leaf = ffiTestFunctions.lookupFunction< + StructAlignmentInt16 Function(Int8, Int16, Int8), + StructAlignmentInt16 Function( + int, int, int)>("ReturnStructAlignmentInt16", isLeaf: true); + +/// Test alignment and padding of 16 byte int within struct. +void testReturnStructAlignmentInt16Leaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStructAlignmentInt16Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStructAlignmentInt32Leaf = ffiTestFunctions.lookupFunction< + StructAlignmentInt32 Function(Int8, Int32, Int8), + StructAlignmentInt32 Function( + int, int, int)>("ReturnStructAlignmentInt32", isLeaf: true); + +/// Test alignment and padding of 32 byte int within struct. +void testReturnStructAlignmentInt32Leaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStructAlignmentInt32Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStructAlignmentInt64Leaf = ffiTestFunctions.lookupFunction< + StructAlignmentInt64 Function(Int8, Int64, Int8), + StructAlignmentInt64 Function( + int, int, int)>("ReturnStructAlignmentInt64", isLeaf: true); + +/// Test alignment and padding of 64 byte int within struct. +void testReturnStructAlignmentInt64Leaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStructAlignmentInt64Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct8BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedInt Function( + Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16), + Struct8BytesNestedInt Function( + Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16)>( + "ReturnStruct8BytesNestedInt", + isLeaf: true); + +/// Simple nested struct. +void testReturnStruct8BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + + final result = returnStruct8BytesNestedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct8BytesNestedFloatLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedFloat Function(Struct4BytesFloat, Struct4BytesFloat), + Struct8BytesNestedFloat Function(Struct4BytesFloat, + Struct4BytesFloat)>("ReturnStruct8BytesNestedFloat", isLeaf: true); + +/// Simple nested struct with floats. +void testReturnStruct8BytesNestedFloatLeaf() { + final a0Pointer = calloc(); + final Struct4BytesFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesFloat a1 = a1Pointer.ref; + + a0.a0 = -1.0; + a1.a0 = 2.0; + + final result = returnStruct8BytesNestedFloatLeaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0.a0); + Expect.approxEquals(a1.a0, result.a1.a0); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct8BytesNestedFloat2Leaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedFloat2 Function(Struct4BytesFloat, Float), + Struct8BytesNestedFloat2 Function(Struct4BytesFloat, + double)>("ReturnStruct8BytesNestedFloat2", isLeaf: true); + +/// The nesting is irregular, testing homogenous float rules on arm and arm64, +/// and the fpu register usage on x64. +void testReturnStruct8BytesNestedFloat2Leaf() { + final a0Pointer = calloc(); + final Struct4BytesFloat a0 = a0Pointer.ref; + double a1; + + a0.a0 = -1.0; + a1 = 2.0; + + final result = returnStruct8BytesNestedFloat2Leaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0.a0); + Expect.approxEquals(a1, result.a1); + + calloc.free(a0Pointer); +} + +final returnStruct8BytesNestedMixedLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedMixed Function( + Struct4BytesHomogeneousInt16, Struct4BytesFloat), + Struct8BytesNestedMixed Function(Struct4BytesHomogeneousInt16, + Struct4BytesFloat)>("ReturnStruct8BytesNestedMixed", isLeaf: true); + +/// Simple nested struct with mixed members. +void testReturnStruct8BytesNestedMixedLeaf() { + final a0Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesFloat a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3.0; + + final result = returnStruct8BytesNestedMixedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.approxEquals(a1.a0, result.a1.a0); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct16BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Struct16BytesNestedInt Function( + Struct8BytesNestedInt, Struct8BytesNestedInt), + Struct16BytesNestedInt Function(Struct8BytesNestedInt, + Struct8BytesNestedInt)>("ReturnStruct16BytesNestedInt", isLeaf: true); + +/// Deeper nested struct to test recursive member access. +void testReturnStruct16BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a1.a0 = -3; + a0.a1.a1 = 4; + a1.a0.a0 = -5; + a1.a0.a1 = 6; + a1.a1.a0 = -7; + a1.a1.a1 = 8; + + final result = returnStruct16BytesNestedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0.a0, result.a0.a0.a0); + Expect.equals(a0.a0.a1, result.a0.a0.a1); + Expect.equals(a0.a1.a0, result.a0.a1.a0); + Expect.equals(a0.a1.a1, result.a0.a1.a1); + Expect.equals(a1.a0.a0, result.a1.a0.a0); + Expect.equals(a1.a0.a1, result.a1.a0.a1); + Expect.equals(a1.a1.a0, result.a1.a1.a0); + Expect.equals(a1.a1.a1, result.a1.a1.a1); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct32BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Struct32BytesNestedInt Function( + Struct16BytesNestedInt, Struct16BytesNestedInt), + Struct32BytesNestedInt Function(Struct16BytesNestedInt, + Struct16BytesNestedInt)>("ReturnStruct32BytesNestedInt", isLeaf: true); + +/// Even deeper nested struct to test recursive member access. +void testReturnStruct32BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct16BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0.a0 = -1; + a0.a0.a0.a1 = 2; + a0.a0.a1.a0 = -3; + a0.a0.a1.a1 = 4; + a0.a1.a0.a0 = -5; + a0.a1.a0.a1 = 6; + a0.a1.a1.a0 = -7; + a0.a1.a1.a1 = 8; + a1.a0.a0.a0 = -9; + a1.a0.a0.a1 = 10; + a1.a0.a1.a0 = -11; + a1.a0.a1.a1 = 12; + a1.a1.a0.a0 = -13; + a1.a1.a0.a1 = 14; + a1.a1.a1.a0 = -15; + a1.a1.a1.a1 = 16; + + final result = returnStruct32BytesNestedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0.a0.a0, result.a0.a0.a0.a0); + Expect.equals(a0.a0.a0.a1, result.a0.a0.a0.a1); + Expect.equals(a0.a0.a1.a0, result.a0.a0.a1.a0); + Expect.equals(a0.a0.a1.a1, result.a0.a0.a1.a1); + Expect.equals(a0.a1.a0.a0, result.a0.a1.a0.a0); + Expect.equals(a0.a1.a0.a1, result.a0.a1.a0.a1); + Expect.equals(a0.a1.a1.a0, result.a0.a1.a1.a0); + Expect.equals(a0.a1.a1.a1, result.a0.a1.a1.a1); + Expect.equals(a1.a0.a0.a0, result.a1.a0.a0.a0); + Expect.equals(a1.a0.a0.a1, result.a1.a0.a0.a1); + Expect.equals(a1.a0.a1.a0, result.a1.a0.a1.a0); + Expect.equals(a1.a0.a1.a1, result.a1.a0.a1.a1); + Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0); + Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1); + Expect.equals(a1.a1.a1.a0, result.a1.a1.a1.a0); + Expect.equals(a1.a1.a1.a1, result.a1.a1.a1.a1); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIntStructAlignmentInt16Leaf = + ffiTestFunctions.lookupFunction< + StructNestedIntStructAlignmentInt16 Function( + StructAlignmentInt16, StructAlignmentInt16), + StructNestedIntStructAlignmentInt16 Function( + StructAlignmentInt16, StructAlignmentInt16)>( + "ReturnStructNestedIntStructAlignmentInt16", + isLeaf: true); + +/// Test alignment and padding of nested struct with 16 byte int. +void testReturnStructNestedIntStructAlignmentInt16Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructAlignmentInt16 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + + final result = returnStructNestedIntStructAlignmentInt16Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + Expect.equals(a1.a2, result.a1.a2); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIntStructAlignmentInt32Leaf = + ffiTestFunctions.lookupFunction< + StructNestedIntStructAlignmentInt32 Function( + StructAlignmentInt32, StructAlignmentInt32), + StructNestedIntStructAlignmentInt32 Function( + StructAlignmentInt32, StructAlignmentInt32)>( + "ReturnStructNestedIntStructAlignmentInt32", + isLeaf: true); + +/// Test alignment and padding of nested struct with 32 byte int. +void testReturnStructNestedIntStructAlignmentInt32Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt32 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructAlignmentInt32 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + + final result = returnStructNestedIntStructAlignmentInt32Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + Expect.equals(a1.a2, result.a1.a2); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIntStructAlignmentInt64Leaf = + ffiTestFunctions.lookupFunction< + StructNestedIntStructAlignmentInt64 Function( + StructAlignmentInt64, StructAlignmentInt64), + StructNestedIntStructAlignmentInt64 Function( + StructAlignmentInt64, StructAlignmentInt64)>( + "ReturnStructNestedIntStructAlignmentInt64", + isLeaf: true); + +/// Test alignment and padding of nested struct with 64 byte int. +void testReturnStructNestedIntStructAlignmentInt64Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt64 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructAlignmentInt64 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + + final result = returnStructNestedIntStructAlignmentInt64Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + Expect.equals(a1.a2, result.a1.a2); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIrregularEvenBiggerLeaf = + ffiTestFunctions.lookupFunction< + StructNestedIrregularEvenBigger Function(Uint64, + StructNestedIrregularBigger, StructNestedIrregularBigger, Double), + StructNestedIrregularEvenBigger Function( + int, + StructNestedIrregularBigger, + StructNestedIrregularBigger, + double)>("ReturnStructNestedIrregularEvenBigger", isLeaf: true); + +/// Return big irregular struct as smoke test. +void testReturnStructNestedIrregularEvenBiggerLeaf() { + int a0; + final a1Pointer = calloc(); + final StructNestedIrregularBigger a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructNestedIrregularBigger a2 = a2Pointer.ref; + double a3; + + a0 = 1; + a1.a0.a0 = 2; + a1.a0.a1.a0.a0 = -3; + a1.a0.a1.a0.a1 = 4; + a1.a0.a1.a1.a0 = -5.0; + a1.a0.a2 = 6; + a1.a0.a3.a0.a0 = -7.0; + a1.a0.a3.a1 = 8.0; + a1.a0.a4 = 9; + a1.a0.a5.a0.a0 = 10.0; + a1.a0.a5.a1.a0 = -11.0; + a1.a0.a6 = 12; + a1.a1.a0.a0 = -13; + a1.a1.a0.a1 = 14; + a1.a1.a1.a0 = -15.0; + a1.a2 = 16.0; + a1.a3 = -17.0; + a2.a0.a0 = 18; + a2.a0.a1.a0.a0 = -19; + a2.a0.a1.a0.a1 = 20; + a2.a0.a1.a1.a0 = -21.0; + a2.a0.a2 = 22; + a2.a0.a3.a0.a0 = -23.0; + a2.a0.a3.a1 = 24.0; + a2.a0.a4 = 25; + a2.a0.a5.a0.a0 = 26.0; + a2.a0.a5.a1.a0 = -27.0; + a2.a0.a6 = 28; + a2.a1.a0.a0 = -29; + a2.a1.a0.a1 = 30; + a2.a1.a1.a0 = -31.0; + a2.a2 = 32.0; + a2.a3 = -33.0; + a3 = 34.0; + + final result = returnStructNestedIrregularEvenBiggerLeaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1.a0.a0, result.a1.a0.a0); + Expect.equals(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0); + Expect.equals(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1); + Expect.approxEquals(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0); + Expect.equals(a1.a0.a2, result.a1.a0.a2); + Expect.approxEquals(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0); + Expect.approxEquals(a1.a0.a3.a1, result.a1.a0.a3.a1); + Expect.equals(a1.a0.a4, result.a1.a0.a4); + Expect.approxEquals(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0); + Expect.approxEquals(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0); + Expect.equals(a1.a0.a6, result.a1.a0.a6); + Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0); + Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1); + Expect.approxEquals(a1.a1.a1.a0, result.a1.a1.a1.a0); + Expect.approxEquals(a1.a2, result.a1.a2); + Expect.approxEquals(a1.a3, result.a1.a3); + Expect.equals(a2.a0.a0, result.a2.a0.a0); + Expect.equals(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0); + Expect.equals(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1); + Expect.approxEquals(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0); + Expect.equals(a2.a0.a2, result.a2.a0.a2); + Expect.approxEquals(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0); + Expect.approxEquals(a2.a0.a3.a1, result.a2.a0.a3.a1); + Expect.equals(a2.a0.a4, result.a2.a0.a4); + Expect.approxEquals(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0); + Expect.approxEquals(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0); + Expect.equals(a2.a0.a6, result.a2.a0.a6); + Expect.equals(a2.a1.a0.a0, result.a2.a1.a0.a0); + Expect.equals(a2.a1.a0.a1, result.a2.a1.a0.a1); + Expect.approxEquals(a2.a1.a1.a0, result.a2.a1.a1.a0); + Expect.approxEquals(a2.a2, result.a2.a2); + Expect.approxEquals(a2.a3, result.a2.a3); + Expect.approxEquals(a3, result.a3); + + calloc.free(a1Pointer); + calloc.free(a2Pointer); +} diff --git a/tests/ffi/function_structs_test.dart b/tests/ffi/function_structs_test.dart index d81324741a5..b0f2cdb9860 100644 --- a/tests/ffi/function_structs_test.dart +++ b/tests/ffi/function_structs_test.dart @@ -19,18 +19,22 @@ import 'very_large_struct.dart'; typedef NativeCoordinateOp = Pointer Function(Pointer); void main() { - testFunctionWithStruct(); - testFunctionWithStructArray(); - testFunctionWithVeryLargeStruct(); + for (final isLeaf in [false, true]) { + testFunctionWithStruct(isLeaf: isLeaf); + testFunctionWithStructArray(isLeaf: isLeaf); + testFunctionWithVeryLargeStruct(isLeaf: isLeaf); + } } DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); /// pass a struct to a c function and get a struct as return value -void testFunctionWithStruct() { +void testFunctionWithStruct({bool isLeaf: false}) { Pointer> p1 = ffiTestFunctions.lookup("TransposeCoordinate"); - NativeCoordinateOp f1 = p1.asFunction(); + NativeCoordinateOp f1 = + (isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false)); + ; final c1 = calloc() ..ref.x = 10.0 @@ -54,10 +58,12 @@ void testFunctionWithStruct() { } /// pass an array of structs to a c funtion -void testFunctionWithStructArray() { +void testFunctionWithStructArray({bool isLeaf: false}) { Pointer> p1 = ffiTestFunctions.lookup("CoordinateElemAt1"); - NativeCoordinateOp f1 = p1.asFunction(); + NativeCoordinateOp f1 = + (isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false)); + ; final coordinateArray = calloc(3); Coordinate c1 = coordinateArray[0]; @@ -83,10 +89,12 @@ void testFunctionWithStructArray() { typedef VeryLargeStructSum = int Function(Pointer); typedef NativeVeryLargeStructSum = Int64 Function(Pointer); -void testFunctionWithVeryLargeStruct() { +void testFunctionWithVeryLargeStruct({bool isLeaf: false}) { Pointer> p1 = ffiTestFunctions.lookup("SumVeryLargeStruct"); - VeryLargeStructSum f = p1.asFunction(); + VeryLargeStructSum f = + (isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false)); + ; final vlsArray = calloc(2); VeryLargeStruct vls1 = vlsArray[0]; diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart index 0ab7739ce4a..c624d584e27 100644 --- a/tests/ffi/generator/structs_by_value_tests_generator.dart +++ b/tests/ffi/generator/structs_by_value_tests_generator.dart @@ -537,7 +537,7 @@ extension on CompositeType { } extension on FunctionType { - String get dartCallCode { + String dartCallCode({bool isLeaf: false}) { final a = ArgumentValueAssigner(); final assignValues = arguments.assignValueStatements(a); final argumentFrees = arguments.dartFreeStatements(); @@ -561,17 +561,19 @@ extension on FunctionType { break; } + final namePostfix = isLeaf ? "Leaf" : ""; return """ - final $dartName = - ffiTestFunctions.lookupFunction<$dartCType, $dartType>("$cName"); + final $dartName$namePostfix = + ffiTestFunctions.lookupFunction<$dartCType, $dartType>( + "$cName"${isLeaf ? ", isLeaf:true" : ""}); ${reason.makeDartDocComment()} - void $dartTestName() { + void $dartTestName$namePostfix() { ${arguments.dartAllocateStatements()} ${assignValues} - final result = $dartName($argumentNames); + final result = $dartName$namePostfix($argumentNames); print("result = \$result"); @@ -886,11 +888,13 @@ void writeDartCallTest() { void main() { for (int i = 0; i < 10; ++i) { ${functions.map((e) => "${e.dartTestName}();").join("\n")} + ${functions.map((e) => "${e.dartTestName}Leaf();").join("\n")} } } """); buffer.writeAll(compounds.map((e) => e.dartClass(nnbd))); - buffer.writeAll(functions.map((e) => e.dartCallCode)); + buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: false))); + buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: true))); final path = callTestPath(nnbd); File(path).writeAsStringSync(buffer.toString()); diff --git a/tests/ffi/vmspecific_leaf_call_test.dart b/tests/ffi/vmspecific_leaf_call_test.dart new file mode 100644 index 00000000000..3d051731be3 --- /dev/null +++ b/tests/ffi/vmspecific_leaf_call_test.dart @@ -0,0 +1,61 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions + +import 'dart:ffi'; +import 'dart:io'; + +import 'package:expect/expect.dart'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; +import 'callback_tests_utils.dart'; + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +testLeafCall() { + // Note: This test currently fails on Windows AOT: https://dartbug.com/40579 + // Regular calls should transition generated -> native. + final isThreadInGenerated = ffiTestFunctions.lookupFunction< + Int8 Function(), int Function()>("IsThreadInGenerated"); + Expect.equals(0, isThreadInGenerated()); + // Leaf calls should remain in generated state. + final isThreadInGeneratedLeaf = ffiTestFunctions.lookupFunction< + Int8 Function(), int Function()>("IsThreadInGenerated", isLeaf: true); + Expect.equals(1, isThreadInGeneratedLeaf()); +} + +testLeafCallApi() { + // Note: This will only crash as expected in debug build mode. In other modes + // it's effectively skip. + final f = ffiTestFunctions.lookupFunction< + Void Function(), void Function()>("TestLeafCallApi", isLeaf: true); + // Calling Dart_.. API is unsafe from leaf calls since we explicitly haven't + // made the generated -> native transition. + f(); +} + +void nop() {} + +testCallbackLeaf() { + // This should crash with "expected: T->IsAtSafepoint()", since it's unsafe to + // do callbacks from leaf calls (otherwise they wouldn't be leaf calls). + // Note: This will only crash as expected in debug build mode. In other modes + // it's effectively skip. + CallbackTest("CallbackLeaf", Pointer.fromFunction(nop), + isLeaf:true).run(); +} + +main() { + testLeafCall(); //# 01: ok + // These tests terminate the process after successful completion, so we have + // to run them separately. + // + // Since they use signal handlers they only run on Linux. + if (Platform.isLinux && !const bool.fromEnvironment("dart.vm.product")) { + testLeafCallApi(); //# 02: ok + testCallbackLeaf(); //# 03: ok + } +} diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart index a89638c14fe..9dca0e71aa1 100644 --- a/tests/ffi/vmspecific_static_checks_test.dart +++ b/tests/ffi/vmspecific_static_checks_test.dart @@ -61,6 +61,12 @@ void main() { testSizeOfHandle(); testElementAtGeneric(); testElementAtNativeType(); + testLookupFunctionIsLeafMustBeConst(); + testAsFunctionIsLeafMustBeConst(); + testLookupFunctionTakesHandle(); + testAsFunctionTakesHandle(); + testLookupFunctionReturnsHandle(); + testAsFunctionReturnsHandle(); } typedef Int8UnOp = Int8 Function(Int8); @@ -705,6 +711,44 @@ class TestStruct1405 extends Struct { external Pointer notEmpty; } +void testLookupFunctionIsLeafMustBeConst() { + bool notAConst = false; + DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library"); + l.lookupFunction("timesFour", isLeaf:notAConst); //# 1500: compile-time error +} + +void testAsFunctionIsLeafMustBeConst() { + bool notAConst = false; + Pointer> p = Pointer.fromAddress(1337); + IntUnOp f = p.asFunction(isLeaf:notAConst); //# 1501: compile-time error +} + +typedef NativeTakesHandle = Void Function(Handle); +typedef TakesHandle = void Function(Object); + +void testLookupFunctionTakesHandle() { + DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library"); + l.lookupFunction("takesHandle", isLeaf:true); //# 1502: compile-time error +} + +void testAsFunctionTakesHandle() { + Pointer> p = Pointer.fromAddress(1337); //# 1503: compile-time error + TakesHandle f = p.asFunction(isLeaf:true); //# 1503: compile-time error +} + +typedef NativeReturnsHandle = Handle Function(); +typedef ReturnsHandle = Object Function(); + +void testLookupFunctionReturnsHandle() { + DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library"); + l.lookupFunction("returnsHandle", isLeaf:true); //# 1504: compile-time error +} + +void testAsFunctionReturnsHandle() { + Pointer> p = Pointer.fromAddress(1337); //# 1505: compile-time error + ReturnsHandle f = p.asFunction(isLeaf:true); //# 1505: compile-time error +} + @Packed(1) class TestStruct1600 extends Struct { external Pointer notEmpty; diff --git a/tests/ffi_2/callback_tests_utils.dart b/tests/ffi_2/callback_tests_utils.dart index 40a2995a368..68621c0c40e 100644 --- a/tests/ffi_2/callback_tests_utils.dart +++ b/tests/ffi_2/callback_tests_utils.dart @@ -19,13 +19,19 @@ class CallbackTest { final String name; final Pointer callback; final void Function() afterCallbackChecks; + final bool isLeaf; - CallbackTest(this.name, this.callback) : afterCallbackChecks = noChecks {} - CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks) {} + CallbackTest(this.name, this.callback, {this.isLeaf: false}) + : afterCallbackChecks = noChecks {} + CallbackTest.withCheck(this.name, this.callback, this.afterCallbackChecks, + {this.isLeaf: false}) {} void run() { - final NativeCallbackTestFn tester = ffiTestFunctions - .lookupFunction("Test$name"); + final NativeCallbackTestFn tester = isLeaf + ? ffiTestFunctions.lookupFunction("Test$name", isLeaf: true) + : ffiTestFunctions.lookupFunction("Test$name", isLeaf: false); final int testCode = tester(callback); diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart index 9bf037613cc..b1e3f273733 100644 --- a/tests/ffi_2/function_structs_by_value_generated_test.dart +++ b/tests/ffi_2/function_structs_by_value_generated_test.dart @@ -135,6 +135,119 @@ void main() { testReturnStructNestedIntStructAlignmentInt32(); testReturnStructNestedIntStructAlignmentInt64(); testReturnStructNestedIrregularEvenBigger(); + testPassStruct1ByteIntx10Leaf(); + testPassStruct3BytesHomogeneousUint8x10Leaf(); + testPassStruct3BytesInt2ByteAlignedx10Leaf(); + testPassStruct4BytesHomogeneousInt16x10Leaf(); + testPassStruct7BytesHomogeneousUint8x10Leaf(); + testPassStruct7BytesInt4ByteAlignedx10Leaf(); + testPassStruct8BytesIntx10Leaf(); + testPassStruct8BytesHomogeneousFloatx10Leaf(); + testPassStruct8BytesMixedx10Leaf(); + testPassStruct9BytesHomogeneousUint8x10Leaf(); + testPassStruct9BytesInt4Or8ByteAlignedx10Leaf(); + testPassStruct12BytesHomogeneousFloatx6Leaf(); + testPassStruct16BytesHomogeneousFloatx5Leaf(); + testPassStruct16BytesMixedx10Leaf(); + testPassStruct16BytesMixed2x10Leaf(); + testPassStruct17BytesIntx10Leaf(); + testPassStruct19BytesHomogeneousUint8x10Leaf(); + testPassStruct20BytesHomogeneousInt32x10Leaf(); + testPassStruct20BytesHomogeneousFloatLeaf(); + testPassStruct32BytesHomogeneousDoublex5Leaf(); + testPassStruct40BytesHomogeneousDoubleLeaf(); + testPassStruct1024BytesHomogeneousUint64Leaf(); + testPassFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf(); + testPassFloatStruct32BytesHomogeneousDoubleFloatStructLeaf(); + testPassInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf(); + testPassDoublex6Struct16BytesMixedx4Int32Leaf(); + testPassInt32x4Struct16BytesMixedx4DoubleLeaf(); + testPassStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf(); + testPassInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf(); + testPassStructAlignmentInt16Leaf(); + testPassStructAlignmentInt32Leaf(); + testPassStructAlignmentInt64Leaf(); + testPassStruct8BytesNestedIntx10Leaf(); + testPassStruct8BytesNestedFloatx10Leaf(); + testPassStruct8BytesNestedFloat2x10Leaf(); + testPassStruct8BytesNestedMixedx10Leaf(); + testPassStruct16BytesNestedIntx2Leaf(); + testPassStruct32BytesNestedIntx2Leaf(); + testPassStructNestedIntStructAlignmentInt16Leaf(); + testPassStructNestedIntStructAlignmentInt32Leaf(); + testPassStructNestedIntStructAlignmentInt64Leaf(); + testPassStructNestedIrregularEvenBiggerx4Leaf(); + testPassStruct8BytesInlineArrayIntx4Leaf(); + testPassStructInlineArrayIrregularx4Leaf(); + testPassStructInlineArray100BytesLeaf(); + testPassStructStruct16BytesHomogeneousFloat2x5Leaf(); + testPassStructStruct32BytesHomogeneousDouble2x5Leaf(); + testPassStructStruct16BytesMixed3x10Leaf(); + testPassUint8Struct32BytesInlineArrayMultiDimensionalILeaf(); + testPassUint8Struct4BytesInlineArrayMultiDimensionalInLeaf(); + testPassStruct3BytesPackedIntx10Leaf(); + testPassStruct8BytesPackedIntx10Leaf(); + testPassStruct9BytesPackedMixedx10DoubleInt32Leaf(); + testPassStruct5BytesPackedMixedLeaf(); + testPassStructNestedAlignmentStruct5BytesPackedMixedLeaf(); + testPassStruct6BytesInlineArrayIntLeaf(); + testPassStruct15BytesInlineArrayMixedLeaf(); + testPassUnion4BytesMixedx10Leaf(); + testPassUnion8BytesNestedFloatx10Leaf(); + testPassUnion9BytesNestedIntx10Leaf(); + testPassUnion16BytesNestedInlineArrayFloatx10Leaf(); + testPassUnion16BytesNestedFloatx10Leaf(); + testReturnStruct1ByteIntLeaf(); + testReturnStruct3BytesHomogeneousUint8Leaf(); + testReturnStruct3BytesInt2ByteAlignedLeaf(); + testReturnStruct4BytesHomogeneousInt16Leaf(); + testReturnStruct7BytesHomogeneousUint8Leaf(); + testReturnStruct7BytesInt4ByteAlignedLeaf(); + testReturnStruct8BytesIntLeaf(); + testReturnStruct8BytesHomogeneousFloatLeaf(); + testReturnStruct8BytesMixedLeaf(); + testReturnStruct9BytesHomogeneousUint8Leaf(); + testReturnStruct9BytesInt4Or8ByteAlignedLeaf(); + testReturnStruct12BytesHomogeneousFloatLeaf(); + testReturnStruct16BytesHomogeneousFloatLeaf(); + testReturnStruct16BytesMixedLeaf(); + testReturnStruct16BytesMixed2Leaf(); + testReturnStruct17BytesIntLeaf(); + testReturnStruct19BytesHomogeneousUint8Leaf(); + testReturnStruct20BytesHomogeneousInt32Leaf(); + testReturnStruct20BytesHomogeneousFloatLeaf(); + testReturnStruct32BytesHomogeneousDoubleLeaf(); + testReturnStruct40BytesHomogeneousDoubleLeaf(); + testReturnStruct1024BytesHomogeneousUint64Leaf(); + testReturnStruct3BytesPackedIntLeaf(); + testReturnStruct8BytesPackedIntLeaf(); + testReturnStruct9BytesPackedMixedLeaf(); + testReturnUnion4BytesMixedLeaf(); + testReturnUnion8BytesNestedFloatLeaf(); + testReturnUnion9BytesNestedIntLeaf(); + testReturnUnion16BytesNestedFloatLeaf(); + testReturnStructArgumentStruct1ByteIntLeaf(); + testReturnStructArgumentInt32x8Struct1ByteIntLeaf(); + testReturnStructArgumentStruct8BytesHomogeneousFloatLeaf(); + testReturnStructArgumentStruct20BytesHomogeneousInt32Leaf(); + testReturnStructArgumentInt32x8Struct20BytesHomogeneouLeaf(); + testReturnStructArgumentStruct8BytesInlineArrayIntLeaf(); + testReturnStructArgumentStructStruct16BytesHomogeneousLeaf(); + testReturnStructArgumentStructStruct32BytesHomogeneousLeaf(); + testReturnStructArgumentStructStruct16BytesMixed3Leaf(); + testReturnStructAlignmentInt16Leaf(); + testReturnStructAlignmentInt32Leaf(); + testReturnStructAlignmentInt64Leaf(); + testReturnStruct8BytesNestedIntLeaf(); + testReturnStruct8BytesNestedFloatLeaf(); + testReturnStruct8BytesNestedFloat2Leaf(); + testReturnStruct8BytesNestedMixedLeaf(); + testReturnStruct16BytesNestedIntLeaf(); + testReturnStruct32BytesNestedIntLeaf(); + testReturnStructNestedIntStructAlignmentInt16Leaf(); + testReturnStructNestedIntStructAlignmentInt32Leaf(); + testReturnStructNestedIntStructAlignmentInt64Leaf(); + testReturnStructNestedIrregularEvenBiggerLeaf(); } } @@ -8772,3 +8885,7541 @@ void testReturnStructNestedIrregularEvenBigger() { calloc.free(a1Pointer); calloc.free(a2Pointer); } + +final passStruct1ByteIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt), + int Function( + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt, + Struct1ByteInt)>("PassStruct1ByteIntx10", isLeaf: true); + +/// Smallest struct with data. +/// 10 struct arguments will exhaust available registers. +void testPassStruct1ByteIntx10Leaf() { + final a0Pointer = calloc(); + final Struct1ByteInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct1ByteInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct1ByteInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct1ByteInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct1ByteInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct1ByteInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct1ByteInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct1ByteInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct1ByteInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct1ByteInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a1.a0 = 2; + a2.a0 = -3; + a3.a0 = 4; + a4.a0 = -5; + a5.a0 = 6; + a6.a0 = -7; + a7.a0 = 8; + a8.a0 = -9; + a9.a0 = 10; + + final result = + passStruct1ByteIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(5, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct3BytesHomogeneousUint8x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8), + int Function( + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8, + Struct3BytesHomogeneousUint8)>( + "PassStruct3BytesHomogeneousUint8x10", + isLeaf: true); + +/// Not a multiple of word size, not a power of two. +/// 10 struct arguments will exhaust available registers. +void testPassStruct3BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct3BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a1.a0 = 4; + a1.a1 = 5; + a1.a2 = 6; + a2.a0 = 7; + a2.a1 = 8; + a2.a2 = 9; + a3.a0 = 10; + a3.a1 = 11; + a3.a2 = 12; + a4.a0 = 13; + a4.a1 = 14; + a4.a2 = 15; + a5.a0 = 16; + a5.a1 = 17; + a5.a2 = 18; + a6.a0 = 19; + a6.a1 = 20; + a6.a2 = 21; + a7.a0 = 22; + a7.a1 = 23; + a7.a2 = 24; + a8.a0 = 25; + a8.a1 = 26; + a8.a2 = 27; + a9.a0 = 28; + a9.a1 = 29; + a9.a2 = 30; + + final result = passStruct3BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(465, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct3BytesInt2ByteAlignedx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned), + int Function( + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned, + Struct3BytesInt2ByteAligned)>("PassStruct3BytesInt2ByteAlignedx10", + isLeaf: true); + +/// Not a multiple of word size, not a power of two. +/// With alignment rules taken into account size is 4 bytes. +/// 10 struct arguments will exhaust available registers. +void testPassStruct3BytesInt2ByteAlignedx10Leaf() { + final a0Pointer = calloc(); + final Struct3BytesInt2ByteAligned a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct3BytesInt2ByteAligned a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct3BytesInt2ByteAligned a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct3BytesInt2ByteAligned a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct3BytesInt2ByteAligned a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct3BytesInt2ByteAligned a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct3BytesInt2ByteAligned a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct3BytesInt2ByteAligned a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct3BytesInt2ByteAligned a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct3BytesInt2ByteAligned a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = passStruct3BytesInt2ByteAlignedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct4BytesHomogeneousInt16x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16), + int Function( + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16, + Struct4BytesHomogeneousInt16)>( + "PassStruct4BytesHomogeneousInt16x10", + isLeaf: true); + +/// Exactly word size on 32-bit architectures. +/// 10 struct arguments will exhaust available registers. +void testPassStruct4BytesHomogeneousInt16x10Leaf() { + final a0Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = passStruct4BytesHomogeneousInt16x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct7BytesHomogeneousUint8x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8), + int Function( + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8, + Struct7BytesHomogeneousUint8)>( + "PassStruct7BytesHomogeneousUint8x10", + isLeaf: true); + +/// Sub word size on 64 bit architectures. +/// 10 struct arguments will exhaust available registers. +void testPassStruct7BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct7BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a1.a0 = 8; + a1.a1 = 9; + a1.a2 = 10; + a1.a3 = 11; + a1.a4 = 12; + a1.a5 = 13; + a1.a6 = 14; + a2.a0 = 15; + a2.a1 = 16; + a2.a2 = 17; + a2.a3 = 18; + a2.a4 = 19; + a2.a5 = 20; + a2.a6 = 21; + a3.a0 = 22; + a3.a1 = 23; + a3.a2 = 24; + a3.a3 = 25; + a3.a4 = 26; + a3.a5 = 27; + a3.a6 = 28; + a4.a0 = 29; + a4.a1 = 30; + a4.a2 = 31; + a4.a3 = 32; + a4.a4 = 33; + a4.a5 = 34; + a4.a6 = 35; + a5.a0 = 36; + a5.a1 = 37; + a5.a2 = 38; + a5.a3 = 39; + a5.a4 = 40; + a5.a5 = 41; + a5.a6 = 42; + a6.a0 = 43; + a6.a1 = 44; + a6.a2 = 45; + a6.a3 = 46; + a6.a4 = 47; + a6.a5 = 48; + a6.a6 = 49; + a7.a0 = 50; + a7.a1 = 51; + a7.a2 = 52; + a7.a3 = 53; + a7.a4 = 54; + a7.a5 = 55; + a7.a6 = 56; + a8.a0 = 57; + a8.a1 = 58; + a8.a2 = 59; + a8.a3 = 60; + a8.a4 = 61; + a8.a5 = 62; + a8.a6 = 63; + a9.a0 = 64; + a9.a1 = 65; + a9.a2 = 66; + a9.a3 = 67; + a9.a4 = 68; + a9.a5 = 69; + a9.a6 = 70; + + final result = passStruct7BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(2485, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct7BytesInt4ByteAlignedx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned), + int Function( + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned, + Struct7BytesInt4ByteAligned)>("PassStruct7BytesInt4ByteAlignedx10", + isLeaf: true); + +/// Sub word size on 64 bit architectures. +/// With alignment rules taken into account size is 8 bytes. +/// 10 struct arguments will exhaust available registers. +void testPassStruct7BytesInt4ByteAlignedx10Leaf() { + final a0Pointer = calloc(); + final Struct7BytesInt4ByteAligned a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct7BytesInt4ByteAligned a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct7BytesInt4ByteAligned a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct7BytesInt4ByteAligned a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct7BytesInt4ByteAligned a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct7BytesInt4ByteAligned a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct7BytesInt4ByteAligned a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct7BytesInt4ByteAligned a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct7BytesInt4ByteAligned a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct7BytesInt4ByteAligned a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28; + a9.a1 = -29; + a9.a2 = 30; + + final result = passStruct7BytesInt4ByteAlignedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(15, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt), + int Function( + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt, + Struct8BytesInt)>("PassStruct8BytesIntx10", isLeaf: true); + +/// Exactly word size struct on 64bit architectures. +/// 10 struct arguments will exhaust available registers. +void testPassStruct8BytesIntx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28; + a9.a1 = -29; + a9.a2 = 30; + + final result = + passStruct8BytesIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(15, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesHomogeneousFloatx10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat), + double Function( + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat, + Struct8BytesHomogeneousFloat)>( + "PassStruct8BytesHomogeneousFloatx10", + isLeaf: true); + +/// Arguments passed in FP registers as long as they fit. +/// 10 struct arguments will exhaust available registers. +void testPassStruct8BytesHomogeneousFloatx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesHomogeneousFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesHomogeneousFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesHomogeneousFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesHomogeneousFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesHomogeneousFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesHomogeneousFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesHomogeneousFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesHomogeneousFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesHomogeneousFloat a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a1.a0 = -3.0; + a1.a1 = 4.0; + a2.a0 = -5.0; + a2.a1 = 6.0; + a3.a0 = -7.0; + a3.a1 = 8.0; + a4.a0 = -9.0; + a4.a1 = 10.0; + a5.a0 = -11.0; + a5.a1 = 12.0; + a6.a0 = -13.0; + a6.a1 = 14.0; + a7.a0 = -15.0; + a7.a1 = 16.0; + a8.a0 = -17.0; + a8.a1 = 18.0; + a9.a0 = -19.0; + a9.a1 = 20.0; + + final result = passStruct8BytesHomogeneousFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesMixedx10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed), + double Function( + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed, + Struct8BytesMixed)>("PassStruct8BytesMixedx10", isLeaf: true); + +/// On x64, arguments go in int registers because it is not only float. +/// 10 struct arguments will exhaust available registers. +void testPassStruct8BytesMixedx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesMixed a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4.0; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7.0; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10.0; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13.0; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16.0; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19.0; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22.0; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25.0; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28.0; + a9.a1 = -29; + a9.a2 = 30; + + final result = + passStruct8BytesMixedx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(15.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct9BytesHomogeneousUint8x10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8), + int Function( + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8, + Struct9BytesHomogeneousUint8)>( + "PassStruct9BytesHomogeneousUint8x10", + isLeaf: true); + +/// Argument is a single byte over a multiple of word size. +/// 10 struct arguments will exhaust available registers. +/// Struct only has 1-byte aligned fields to test struct alignment itself. +/// Tests upper bytes in the integer registers that are partly filled. +/// Tests stack alignment of non word size stack arguments. +void testPassStruct9BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct9BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a0.a7 = 8; + a0.a8 = 9; + a1.a0 = 10; + a1.a1 = 11; + a1.a2 = 12; + a1.a3 = 13; + a1.a4 = 14; + a1.a5 = 15; + a1.a6 = 16; + a1.a7 = 17; + a1.a8 = 18; + a2.a0 = 19; + a2.a1 = 20; + a2.a2 = 21; + a2.a3 = 22; + a2.a4 = 23; + a2.a5 = 24; + a2.a6 = 25; + a2.a7 = 26; + a2.a8 = 27; + a3.a0 = 28; + a3.a1 = 29; + a3.a2 = 30; + a3.a3 = 31; + a3.a4 = 32; + a3.a5 = 33; + a3.a6 = 34; + a3.a7 = 35; + a3.a8 = 36; + a4.a0 = 37; + a4.a1 = 38; + a4.a2 = 39; + a4.a3 = 40; + a4.a4 = 41; + a4.a5 = 42; + a4.a6 = 43; + a4.a7 = 44; + a4.a8 = 45; + a5.a0 = 46; + a5.a1 = 47; + a5.a2 = 48; + a5.a3 = 49; + a5.a4 = 50; + a5.a5 = 51; + a5.a6 = 52; + a5.a7 = 53; + a5.a8 = 54; + a6.a0 = 55; + a6.a1 = 56; + a6.a2 = 57; + a6.a3 = 58; + a6.a4 = 59; + a6.a5 = 60; + a6.a6 = 61; + a6.a7 = 62; + a6.a8 = 63; + a7.a0 = 64; + a7.a1 = 65; + a7.a2 = 66; + a7.a3 = 67; + a7.a4 = 68; + a7.a5 = 69; + a7.a6 = 70; + a7.a7 = 71; + a7.a8 = 72; + a8.a0 = 73; + a8.a1 = 74; + a8.a2 = 75; + a8.a3 = 76; + a8.a4 = 77; + a8.a5 = 78; + a8.a6 = 79; + a8.a7 = 80; + a8.a8 = 81; + a9.a0 = 82; + a9.a1 = 83; + a9.a2 = 84; + a9.a3 = 85; + a9.a4 = 86; + a9.a5 = 87; + a9.a6 = 88; + a9.a7 = 89; + a9.a8 = 90; + + final result = passStruct9BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(4095, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct9BytesInt4Or8ByteAlignedx10Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function( + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned), + int Function( + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned, + Struct9BytesInt4Or8ByteAligned)>( + "PassStruct9BytesInt4Or8ByteAlignedx10", + isLeaf: true); + +/// Argument is a single byte over a multiple of word size. +/// With alignment rules taken into account size is 12 or 16 bytes. +/// 10 struct arguments will exhaust available registers. +/// +void testPassStruct9BytesInt4Or8ByteAlignedx10Leaf() { + final a0Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct9BytesInt4Or8ByteAligned a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = passStruct9BytesInt4Or8ByteAlignedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct12BytesHomogeneousFloatx6Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat), + double Function( + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat, + Struct12BytesHomogeneousFloat)>( + "PassStruct12BytesHomogeneousFloatx6", + isLeaf: true); + +/// Arguments in FPU registers on arm hardfp and arm64. +/// Struct arguments will exhaust available registers, and leave some empty. +/// The last argument is to test whether arguments are backfilled. +void testPassStruct12BytesHomogeneousFloatx6Leaf() { + final a0Pointer = calloc(); + final Struct12BytesHomogeneousFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct12BytesHomogeneousFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct12BytesHomogeneousFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct12BytesHomogeneousFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct12BytesHomogeneousFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct12BytesHomogeneousFloat a5 = a5Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a1.a0 = 4.0; + a1.a1 = -5.0; + a1.a2 = 6.0; + a2.a0 = -7.0; + a2.a1 = 8.0; + a2.a2 = -9.0; + a3.a0 = 10.0; + a3.a1 = -11.0; + a3.a2 = 12.0; + a4.a0 = -13.0; + a4.a1 = 14.0; + a4.a2 = -15.0; + a5.a0 = 16.0; + a5.a1 = -17.0; + a5.a2 = 18.0; + + final result = + passStruct12BytesHomogeneousFloatx6Leaf(a0, a1, a2, a3, a4, a5); + + print("result = $result"); + + Expect.approxEquals(9.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); +} + +final passStruct16BytesHomogeneousFloatx5Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat), + double Function( + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat, + Struct16BytesHomogeneousFloat)>( + "PassStruct16BytesHomogeneousFloatx5", + isLeaf: true); + +/// On Linux x64 argument is transferred on stack because it is over 16 bytes. +/// Arguments in FPU registers on arm hardfp and arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStruct16BytesHomogeneousFloatx5Leaf() { + final a0Pointer = calloc(); + final Struct16BytesHomogeneousFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesHomogeneousFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct16BytesHomogeneousFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct16BytesHomogeneousFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct16BytesHomogeneousFloat a4 = a4Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a1.a0 = -5.0; + a1.a1 = 6.0; + a1.a2 = -7.0; + a1.a3 = 8.0; + a2.a0 = -9.0; + a2.a1 = 10.0; + a2.a2 = -11.0; + a2.a3 = 12.0; + a3.a0 = -13.0; + a3.a1 = 14.0; + a3.a2 = -15.0; + a3.a3 = 16.0; + a4.a0 = -17.0; + a4.a1 = 18.0; + a4.a2 = -19.0; + a4.a3 = 20.0; + + final result = passStruct16BytesHomogeneousFloatx5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStruct16BytesMixedx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed), + double Function( + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed)>("PassStruct16BytesMixedx10", isLeaf: true); + +/// On x64, arguments are split over FP and int registers. +/// On x64, it will exhaust the integer registers with the 6th argument. +/// The rest goes on the stack. +/// On arm, arguments are 8 byte aligned. +void testPassStruct16BytesMixedx10Leaf() { + final a0Pointer = calloc(); + final Struct16BytesMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct16BytesMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct16BytesMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct16BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct16BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct16BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct16BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct16BytesMixed a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2; + a1.a0 = -3.0; + a1.a1 = 4; + a2.a0 = -5.0; + a2.a1 = 6; + a3.a0 = -7.0; + a3.a1 = 8; + a4.a0 = -9.0; + a4.a1 = 10; + a5.a0 = -11.0; + a5.a1 = 12; + a6.a0 = -13.0; + a6.a1 = 14; + a7.a0 = -15.0; + a7.a1 = 16; + a8.a0 = -17.0; + a8.a1 = 18; + a9.a0 = -19.0; + a9.a1 = 20; + + final result = + passStruct16BytesMixedx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct16BytesMixed2x10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2), + double Function( + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2, + Struct16BytesMixed2)>("PassStruct16BytesMixed2x10", isLeaf: true); + +/// On x64, arguments are split over FP and int registers. +/// On x64, it will exhaust the integer registers with the 6th argument. +/// The rest goes on the stack. +/// On arm, arguments are 4 byte aligned. +void testPassStruct16BytesMixed2x10Leaf() { + final a0Pointer = calloc(); + final Struct16BytesMixed2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesMixed2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct16BytesMixed2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct16BytesMixed2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct16BytesMixed2 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct16BytesMixed2 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct16BytesMixed2 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed2 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct16BytesMixed2 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct16BytesMixed2 a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4; + a1.a0 = -5.0; + a1.a1 = 6.0; + a1.a2 = -7.0; + a1.a3 = 8; + a2.a0 = -9.0; + a2.a1 = 10.0; + a2.a2 = -11.0; + a2.a3 = 12; + a3.a0 = -13.0; + a3.a1 = 14.0; + a3.a2 = -15.0; + a3.a3 = 16; + a4.a0 = -17.0; + a4.a1 = 18.0; + a4.a2 = -19.0; + a4.a3 = 20; + a5.a0 = -21.0; + a5.a1 = 22.0; + a5.a2 = -23.0; + a5.a3 = 24; + a6.a0 = -25.0; + a6.a1 = 26.0; + a6.a2 = -27.0; + a6.a3 = 28; + a7.a0 = -29.0; + a7.a1 = 30.0; + a7.a2 = -31.0; + a7.a3 = 32; + a8.a0 = -33.0; + a8.a1 = 34.0; + a8.a2 = -35.0; + a8.a3 = 36; + a9.a0 = -37.0; + a9.a1 = 38.0; + a9.a2 = -39.0; + a9.a3 = 40; + + final result = + passStruct16BytesMixed2x10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(20.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct17BytesIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt), + int Function( + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt, + Struct17BytesInt)>("PassStruct17BytesIntx10", isLeaf: true); + +/// Arguments are passed as pointer to copy on arm64. +/// Tests that the memory allocated for copies are rounded up to word size. +void testPassStruct17BytesIntx10Leaf() { + final a0Pointer = calloc(); + final Struct17BytesInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct17BytesInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct17BytesInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct17BytesInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct17BytesInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct17BytesInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct17BytesInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct17BytesInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct17BytesInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct17BytesInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + a2.a0 = -7; + a2.a1 = 8; + a2.a2 = -9; + a3.a0 = 10; + a3.a1 = -11; + a3.a2 = 12; + a4.a0 = -13; + a4.a1 = 14; + a4.a2 = -15; + a5.a0 = 16; + a5.a1 = -17; + a5.a2 = 18; + a6.a0 = -19; + a6.a1 = 20; + a6.a2 = -21; + a7.a0 = 22; + a7.a1 = -23; + a7.a2 = 24; + a8.a0 = -25; + a8.a1 = 26; + a8.a2 = -27; + a9.a0 = 28; + a9.a1 = -29; + a9.a2 = 30; + + final result = + passStruct17BytesIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(15, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct19BytesHomogeneousUint8x10Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function( + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8), + int Function( + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8, + Struct19BytesHomogeneousUint8)>( + "PassStruct19BytesHomogeneousUint8x10", + isLeaf: true); + +/// The minimum alignment of this struct is only 1 byte based on its fields. +/// Test that the memory backing these structs is extended to the right size. +/// +void testPassStruct19BytesHomogeneousUint8x10Leaf() { + final a0Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct19BytesHomogeneousUint8 a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a0.a7 = 8; + a0.a8 = 9; + a0.a9 = 10; + a0.a10 = 11; + a0.a11 = 12; + a0.a12 = 13; + a0.a13 = 14; + a0.a14 = 15; + a0.a15 = 16; + a0.a16 = 17; + a0.a17 = 18; + a0.a18 = 19; + a1.a0 = 20; + a1.a1 = 21; + a1.a2 = 22; + a1.a3 = 23; + a1.a4 = 24; + a1.a5 = 25; + a1.a6 = 26; + a1.a7 = 27; + a1.a8 = 28; + a1.a9 = 29; + a1.a10 = 30; + a1.a11 = 31; + a1.a12 = 32; + a1.a13 = 33; + a1.a14 = 34; + a1.a15 = 35; + a1.a16 = 36; + a1.a17 = 37; + a1.a18 = 38; + a2.a0 = 39; + a2.a1 = 40; + a2.a2 = 41; + a2.a3 = 42; + a2.a4 = 43; + a2.a5 = 44; + a2.a6 = 45; + a2.a7 = 46; + a2.a8 = 47; + a2.a9 = 48; + a2.a10 = 49; + a2.a11 = 50; + a2.a12 = 51; + a2.a13 = 52; + a2.a14 = 53; + a2.a15 = 54; + a2.a16 = 55; + a2.a17 = 56; + a2.a18 = 57; + a3.a0 = 58; + a3.a1 = 59; + a3.a2 = 60; + a3.a3 = 61; + a3.a4 = 62; + a3.a5 = 63; + a3.a6 = 64; + a3.a7 = 65; + a3.a8 = 66; + a3.a9 = 67; + a3.a10 = 68; + a3.a11 = 69; + a3.a12 = 70; + a3.a13 = 71; + a3.a14 = 72; + a3.a15 = 73; + a3.a16 = 74; + a3.a17 = 75; + a3.a18 = 76; + a4.a0 = 77; + a4.a1 = 78; + a4.a2 = 79; + a4.a3 = 80; + a4.a4 = 81; + a4.a5 = 82; + a4.a6 = 83; + a4.a7 = 84; + a4.a8 = 85; + a4.a9 = 86; + a4.a10 = 87; + a4.a11 = 88; + a4.a12 = 89; + a4.a13 = 90; + a4.a14 = 91; + a4.a15 = 92; + a4.a16 = 93; + a4.a17 = 94; + a4.a18 = 95; + a5.a0 = 96; + a5.a1 = 97; + a5.a2 = 98; + a5.a3 = 99; + a5.a4 = 100; + a5.a5 = 101; + a5.a6 = 102; + a5.a7 = 103; + a5.a8 = 104; + a5.a9 = 105; + a5.a10 = 106; + a5.a11 = 107; + a5.a12 = 108; + a5.a13 = 109; + a5.a14 = 110; + a5.a15 = 111; + a5.a16 = 112; + a5.a17 = 113; + a5.a18 = 114; + a6.a0 = 115; + a6.a1 = 116; + a6.a2 = 117; + a6.a3 = 118; + a6.a4 = 119; + a6.a5 = 120; + a6.a6 = 121; + a6.a7 = 122; + a6.a8 = 123; + a6.a9 = 124; + a6.a10 = 125; + a6.a11 = 126; + a6.a12 = 127; + a6.a13 = 128; + a6.a14 = 129; + a6.a15 = 130; + a6.a16 = 131; + a6.a17 = 132; + a6.a18 = 133; + a7.a0 = 134; + a7.a1 = 135; + a7.a2 = 136; + a7.a3 = 137; + a7.a4 = 138; + a7.a5 = 139; + a7.a6 = 140; + a7.a7 = 141; + a7.a8 = 142; + a7.a9 = 143; + a7.a10 = 144; + a7.a11 = 145; + a7.a12 = 146; + a7.a13 = 147; + a7.a14 = 148; + a7.a15 = 149; + a7.a16 = 150; + a7.a17 = 151; + a7.a18 = 152; + a8.a0 = 153; + a8.a1 = 154; + a8.a2 = 155; + a8.a3 = 156; + a8.a4 = 157; + a8.a5 = 158; + a8.a6 = 159; + a8.a7 = 160; + a8.a8 = 161; + a8.a9 = 162; + a8.a10 = 163; + a8.a11 = 164; + a8.a12 = 165; + a8.a13 = 166; + a8.a14 = 167; + a8.a15 = 168; + a8.a16 = 169; + a8.a17 = 170; + a8.a18 = 171; + a9.a0 = 172; + a9.a1 = 173; + a9.a2 = 174; + a9.a3 = 175; + a9.a4 = 176; + a9.a5 = 177; + a9.a6 = 178; + a9.a7 = 179; + a9.a8 = 180; + a9.a9 = 181; + a9.a10 = 182; + a9.a11 = 183; + a9.a12 = 184; + a9.a13 = 185; + a9.a14 = 186; + a9.a15 = 187; + a9.a16 = 188; + a9.a17 = 189; + a9.a18 = 190; + + final result = passStruct19BytesHomogeneousUint8x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(18145, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct20BytesHomogeneousInt32x10Leaf = + ffiTestFunctions.lookupFunction< + Int32 Function( + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32), + int Function( + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32, + Struct20BytesHomogeneousInt32)>( + "PassStruct20BytesHomogeneousInt32x10", + isLeaf: true); + +/// Argument too big to go into integer registers on arm64. +/// The arguments are passed as pointers to copies. +/// The amount of arguments exhausts the number of integer registers, such that +/// pointers to copies are also passed on the stack. +void testPassStruct20BytesHomogeneousInt32x10Leaf() { + final a0Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a0.a3 = 4; + a0.a4 = -5; + a1.a0 = 6; + a1.a1 = -7; + a1.a2 = 8; + a1.a3 = -9; + a1.a4 = 10; + a2.a0 = -11; + a2.a1 = 12; + a2.a2 = -13; + a2.a3 = 14; + a2.a4 = -15; + a3.a0 = 16; + a3.a1 = -17; + a3.a2 = 18; + a3.a3 = -19; + a3.a4 = 20; + a4.a0 = -21; + a4.a1 = 22; + a4.a2 = -23; + a4.a3 = 24; + a4.a4 = -25; + a5.a0 = 26; + a5.a1 = -27; + a5.a2 = 28; + a5.a3 = -29; + a5.a4 = 30; + a6.a0 = -31; + a6.a1 = 32; + a6.a2 = -33; + a6.a3 = 34; + a6.a4 = -35; + a7.a0 = 36; + a7.a1 = -37; + a7.a2 = 38; + a7.a3 = -39; + a7.a4 = 40; + a8.a0 = -41; + a8.a1 = 42; + a8.a2 = -43; + a8.a3 = 44; + a8.a4 = -45; + a9.a0 = 46; + a9.a1 = -47; + a9.a2 = 48; + a9.a3 = -49; + a9.a4 = 50; + + final result = passStruct20BytesHomogeneousInt32x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(25, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct20BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Float Function(Struct20BytesHomogeneousFloat), + double Function(Struct20BytesHomogeneousFloat)>( + "PassStruct20BytesHomogeneousFloat", + isLeaf: true); + +/// Argument too big to go into FPU registers in hardfp and arm64. +void testPassStruct20BytesHomogeneousFloatLeaf() { + final a0Pointer = calloc(); + final Struct20BytesHomogeneousFloat a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a0.a4 = -5.0; + + final result = passStruct20BytesHomogeneousFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(-3.0, result); + + calloc.free(a0Pointer); +} + +final passStruct32BytesHomogeneousDoublex5Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble), + double Function( + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble, + Struct32BytesHomogeneousDouble)>( + "PassStruct32BytesHomogeneousDoublex5", + isLeaf: true); + +/// Arguments in FPU registers on arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStruct32BytesHomogeneousDoublex5Leaf() { + final a0Pointer = calloc(); + final Struct32BytesHomogeneousDouble a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct32BytesHomogeneousDouble a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct32BytesHomogeneousDouble a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct32BytesHomogeneousDouble a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct32BytesHomogeneousDouble a4 = a4Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a1.a0 = -5.0; + a1.a1 = 6.0; + a1.a2 = -7.0; + a1.a3 = 8.0; + a2.a0 = -9.0; + a2.a1 = 10.0; + a2.a2 = -11.0; + a2.a3 = 12.0; + a3.a0 = -13.0; + a3.a1 = 14.0; + a3.a2 = -15.0; + a3.a3 = 16.0; + a4.a0 = -17.0; + a4.a1 = 18.0; + a4.a2 = -19.0; + a4.a3 = 20.0; + + final result = passStruct32BytesHomogeneousDoublex5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStruct40BytesHomogeneousDoubleLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct40BytesHomogeneousDouble), + double Function(Struct40BytesHomogeneousDouble)>( + "PassStruct40BytesHomogeneousDouble", + isLeaf: true); + +/// Argument too big to go into FPU registers in arm64. +void testPassStruct40BytesHomogeneousDoubleLeaf() { + final a0Pointer = calloc(); + final Struct40BytesHomogeneousDouble a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a0.a4 = -5.0; + + final result = passStruct40BytesHomogeneousDoubleLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(-3.0, result); + + calloc.free(a0Pointer); +} + +final passStruct1024BytesHomogeneousUint64Leaf = + ffiTestFunctions.lookupFunction< + Uint64 Function(Struct1024BytesHomogeneousUint64), + int Function(Struct1024BytesHomogeneousUint64)>( + "PassStruct1024BytesHomogeneousUint64", + isLeaf: true); + +/// Test 1kb struct. +void testPassStruct1024BytesHomogeneousUint64Leaf() { + final a0Pointer = calloc(); + final Struct1024BytesHomogeneousUint64 a0 = a0Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a0.a5 = 6; + a0.a6 = 7; + a0.a7 = 8; + a0.a8 = 9; + a0.a9 = 10; + a0.a10 = 11; + a0.a11 = 12; + a0.a12 = 13; + a0.a13 = 14; + a0.a14 = 15; + a0.a15 = 16; + a0.a16 = 17; + a0.a17 = 18; + a0.a18 = 19; + a0.a19 = 20; + a0.a20 = 21; + a0.a21 = 22; + a0.a22 = 23; + a0.a23 = 24; + a0.a24 = 25; + a0.a25 = 26; + a0.a26 = 27; + a0.a27 = 28; + a0.a28 = 29; + a0.a29 = 30; + a0.a30 = 31; + a0.a31 = 32; + a0.a32 = 33; + a0.a33 = 34; + a0.a34 = 35; + a0.a35 = 36; + a0.a36 = 37; + a0.a37 = 38; + a0.a38 = 39; + a0.a39 = 40; + a0.a40 = 41; + a0.a41 = 42; + a0.a42 = 43; + a0.a43 = 44; + a0.a44 = 45; + a0.a45 = 46; + a0.a46 = 47; + a0.a47 = 48; + a0.a48 = 49; + a0.a49 = 50; + a0.a50 = 51; + a0.a51 = 52; + a0.a52 = 53; + a0.a53 = 54; + a0.a54 = 55; + a0.a55 = 56; + a0.a56 = 57; + a0.a57 = 58; + a0.a58 = 59; + a0.a59 = 60; + a0.a60 = 61; + a0.a61 = 62; + a0.a62 = 63; + a0.a63 = 64; + a0.a64 = 65; + a0.a65 = 66; + a0.a66 = 67; + a0.a67 = 68; + a0.a68 = 69; + a0.a69 = 70; + a0.a70 = 71; + a0.a71 = 72; + a0.a72 = 73; + a0.a73 = 74; + a0.a74 = 75; + a0.a75 = 76; + a0.a76 = 77; + a0.a77 = 78; + a0.a78 = 79; + a0.a79 = 80; + a0.a80 = 81; + a0.a81 = 82; + a0.a82 = 83; + a0.a83 = 84; + a0.a84 = 85; + a0.a85 = 86; + a0.a86 = 87; + a0.a87 = 88; + a0.a88 = 89; + a0.a89 = 90; + a0.a90 = 91; + a0.a91 = 92; + a0.a92 = 93; + a0.a93 = 94; + a0.a94 = 95; + a0.a95 = 96; + a0.a96 = 97; + a0.a97 = 98; + a0.a98 = 99; + a0.a99 = 100; + a0.a100 = 101; + a0.a101 = 102; + a0.a102 = 103; + a0.a103 = 104; + a0.a104 = 105; + a0.a105 = 106; + a0.a106 = 107; + a0.a107 = 108; + a0.a108 = 109; + a0.a109 = 110; + a0.a110 = 111; + a0.a111 = 112; + a0.a112 = 113; + a0.a113 = 114; + a0.a114 = 115; + a0.a115 = 116; + a0.a116 = 117; + a0.a117 = 118; + a0.a118 = 119; + a0.a119 = 120; + a0.a120 = 121; + a0.a121 = 122; + a0.a122 = 123; + a0.a123 = 124; + a0.a124 = 125; + a0.a125 = 126; + a0.a126 = 127; + a0.a127 = 128; + + final result = passStruct1024BytesHomogeneousUint64Leaf(a0); + + print("result = $result"); + + Expect.equals(8256, result); + + calloc.free(a0Pointer); +} + +final passFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf = + ffiTestFunctions.lookupFunction< + Float Function( + Float, + Struct16BytesHomogeneousFloat, + Float, + Struct16BytesHomogeneousFloat, + Float, + Struct16BytesHomogeneousFloat, + Float, + Struct16BytesHomogeneousFloat, + Float), + double Function( + double, + Struct16BytesHomogeneousFloat, + double, + Struct16BytesHomogeneousFloat, + double, + Struct16BytesHomogeneousFloat, + double, + Struct16BytesHomogeneousFloat, + double)>("PassFloatStruct16BytesHomogeneousFloatFloatStruct1", + isLeaf: true); + +/// Tests the alignment of structs in FPU registers and backfilling. +void testPassFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf() { + double a0; + final a1Pointer = calloc(); + final Struct16BytesHomogeneousFloat a1 = a1Pointer.ref; + double a2; + final a3Pointer = calloc(); + final Struct16BytesHomogeneousFloat a3 = a3Pointer.ref; + double a4; + final a5Pointer = calloc(); + final Struct16BytesHomogeneousFloat a5 = a5Pointer.ref; + double a6; + final a7Pointer = calloc(); + final Struct16BytesHomogeneousFloat a7 = a7Pointer.ref; + double a8; + + a0 = -1.0; + a1.a0 = 2.0; + a1.a1 = -3.0; + a1.a2 = 4.0; + a1.a3 = -5.0; + a2 = 6.0; + a3.a0 = -7.0; + a3.a1 = 8.0; + a3.a2 = -9.0; + a3.a3 = 10.0; + a4 = -11.0; + a5.a0 = 12.0; + a5.a1 = -13.0; + a5.a2 = 14.0; + a5.a3 = -15.0; + a6 = 16.0; + a7.a0 = -17.0; + a7.a1 = 18.0; + a7.a2 = -19.0; + a7.a3 = 20.0; + a8 = -21.0; + + final result = passFloatStruct16BytesHomogeneousFloatFloatStruct1Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-11.0, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); + calloc.free(a7Pointer); +} + +final passFloatStruct32BytesHomogeneousDoubleFloatStructLeaf = + ffiTestFunctions.lookupFunction< + Double Function( + Float, + Struct32BytesHomogeneousDouble, + Float, + Struct32BytesHomogeneousDouble, + Float, + Struct32BytesHomogeneousDouble, + Float, + Struct32BytesHomogeneousDouble, + Float), + double Function( + double, + Struct32BytesHomogeneousDouble, + double, + Struct32BytesHomogeneousDouble, + double, + Struct32BytesHomogeneousDouble, + double, + Struct32BytesHomogeneousDouble, + double)>("PassFloatStruct32BytesHomogeneousDoubleFloatStruct", + isLeaf: true); + +/// Tests the alignment of structs in FPU registers and backfilling. +void testPassFloatStruct32BytesHomogeneousDoubleFloatStructLeaf() { + double a0; + final a1Pointer = calloc(); + final Struct32BytesHomogeneousDouble a1 = a1Pointer.ref; + double a2; + final a3Pointer = calloc(); + final Struct32BytesHomogeneousDouble a3 = a3Pointer.ref; + double a4; + final a5Pointer = calloc(); + final Struct32BytesHomogeneousDouble a5 = a5Pointer.ref; + double a6; + final a7Pointer = calloc(); + final Struct32BytesHomogeneousDouble a7 = a7Pointer.ref; + double a8; + + a0 = -1.0; + a1.a0 = 2.0; + a1.a1 = -3.0; + a1.a2 = 4.0; + a1.a3 = -5.0; + a2 = 6.0; + a3.a0 = -7.0; + a3.a1 = 8.0; + a3.a2 = -9.0; + a3.a3 = 10.0; + a4 = -11.0; + a5.a0 = 12.0; + a5.a1 = -13.0; + a5.a2 = 14.0; + a5.a3 = -15.0; + a6 = 16.0; + a7.a0 = -17.0; + a7.a1 = 18.0; + a7.a2 = -19.0; + a7.a3 = 20.0; + a8 = -21.0; + + final result = passFloatStruct32BytesHomogeneousDoubleFloatStructLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-11.0, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); + calloc.free(a7Pointer); +} + +final passInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf = + ffiTestFunctions.lookupFunction< + Double Function(Int8, Struct16BytesMixed, Int8, Struct16BytesMixed, + Int8, Struct16BytesMixed, Int8, Struct16BytesMixed, Int8), + double Function( + int, + Struct16BytesMixed, + int, + Struct16BytesMixed, + int, + Struct16BytesMixed, + int, + Struct16BytesMixed, + int)>("PassInt8Struct16BytesMixedInt8Struct16BytesMixedIn", + isLeaf: true); + +/// Tests the alignment of structs in integers registers and on the stack. +/// Arm32 aligns this struct at 8. +/// Also, arm32 allocates the second struct partially in registers, partially +/// on stack. +/// Test backfilling of integer registers. +void testPassInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf() { + int a0; + final a1Pointer = calloc(); + final Struct16BytesMixed a1 = a1Pointer.ref; + int a2; + final a3Pointer = calloc(); + final Struct16BytesMixed a3 = a3Pointer.ref; + int a4; + final a5Pointer = calloc(); + final Struct16BytesMixed a5 = a5Pointer.ref; + int a6; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + int a8; + + a0 = -1; + a1.a0 = 2.0; + a1.a1 = -3; + a2 = 4; + a3.a0 = -5.0; + a3.a1 = 6; + a4 = -7; + a5.a0 = 8.0; + a5.a1 = -9; + a6 = 10; + a7.a0 = -11.0; + a7.a1 = 12; + a8 = -13; + + final result = passInt8Struct16BytesMixedInt8Struct16BytesMixedInLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-7.0, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); + calloc.free(a7Pointer); +} + +final passDoublex6Struct16BytesMixedx4Int32Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Double, + Double, + Double, + Double, + Double, + Double, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Int32), + double Function( + double, + double, + double, + double, + double, + double, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + int)>("PassDoublex6Struct16BytesMixedx4Int32", isLeaf: true); + +/// On Linux x64, it will exhaust xmm registers first, after 6 doubles and 2 +/// structs. The rest of the structs will go on the stack. +/// The int will be backfilled into the int register. +void testPassDoublex6Struct16BytesMixedx4Int32Leaf() { + double a0; + double a1; + double a2; + double a3; + double a4; + double a5; + final a6Pointer = calloc(); + final Struct16BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct16BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct16BytesMixed a9 = a9Pointer.ref; + int a10; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + a4 = -5.0; + a5 = 6.0; + a6.a0 = -7.0; + a6.a1 = 8; + a7.a0 = -9.0; + a7.a1 = 10; + a8.a0 = -11.0; + a8.a1 = 12; + a9.a0 = -13.0; + a9.a1 = 14; + a10 = -15; + + final result = passDoublex6Struct16BytesMixedx4Int32Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); + + print("result = $result"); + + Expect.approxEquals(-8.0, result); + + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passInt32x4Struct16BytesMixedx4DoubleLeaf = + ffiTestFunctions.lookupFunction< + Double Function(Int32, Int32, Int32, Int32, Struct16BytesMixed, + Struct16BytesMixed, Struct16BytesMixed, Struct16BytesMixed, Double), + double Function( + int, + int, + int, + int, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + Struct16BytesMixed, + double)>("PassInt32x4Struct16BytesMixedx4Double", isLeaf: true); + +/// On Linux x64, it will exhaust int registers first. +/// The rest of the structs will go on the stack. +/// The double will be backfilled into the xmm register. +void testPassInt32x4Struct16BytesMixedx4DoubleLeaf() { + int a0; + int a1; + int a2; + int a3; + final a4Pointer = calloc(); + final Struct16BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct16BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct16BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct16BytesMixed a7 = a7Pointer.ref; + double a8; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4.a0 = -5.0; + a4.a1 = 6; + a5.a0 = -7.0; + a5.a1 = 8; + a6.a0 = -9.0; + a6.a1 = 10; + a7.a0 = -11.0; + a7.a1 = 12; + a8 = -13.0; + + final result = passInt32x4Struct16BytesMixedx4DoubleLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.approxEquals(-7.0, result); + + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); +} + +final passStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf = + ffiTestFunctions.lookupFunction< + Double Function(Struct40BytesHomogeneousDouble, + Struct4BytesHomogeneousInt16, Struct8BytesHomogeneousFloat), + double Function(Struct40BytesHomogeneousDouble, + Struct4BytesHomogeneousInt16, Struct8BytesHomogeneousFloat)>( + "PassStruct40BytesHomogeneousDoubleStruct4BytesHomo", + isLeaf: true); + +/// On various architectures, first struct is allocated on stack. +/// Check that the other two arguments are allocated on registers. +void testPassStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf() { + final a0Pointer = calloc(); + final Struct40BytesHomogeneousDouble a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesHomogeneousFloat a2 = a2Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + a0.a2 = -3.0; + a0.a3 = 4.0; + a0.a4 = -5.0; + a1.a0 = 6; + a1.a1 = -7; + a2.a0 = 8.0; + a2.a1 = -9.0; + + final result = + passStruct40BytesHomogeneousDoubleStruct4BytesHomoLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.approxEquals(-5.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); +} + +final passInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf = + ffiTestFunctions.lookupFunction< + Double Function( + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Int64, + Int8, + Struct1ByteInt, + Int64, + Int8, + Struct4BytesHomogeneousInt16, + Int64, + Int8, + Struct8BytesInt, + Int64, + Int8, + Struct8BytesHomogeneousFloat, + Int64, + Int8, + Struct8BytesMixed, + Int64, + Int8, + StructAlignmentInt16, + Int64, + Int8, + StructAlignmentInt32, + Int64, + Int8, + StructAlignmentInt64), + double Function( + int, + int, + int, + int, + int, + int, + int, + int, + double, + double, + double, + double, + double, + double, + double, + double, + int, + int, + Struct1ByteInt, + int, + int, + Struct4BytesHomogeneousInt16, + int, + int, + Struct8BytesInt, + int, + int, + Struct8BytesHomogeneousFloat, + int, + int, + Struct8BytesMixed, + int, + int, + StructAlignmentInt16, + int, + int, + StructAlignmentInt32, + int, + int, + StructAlignmentInt64)>("PassInt32x8Doublex8Int64Int8Struct1ByteIntInt64Int", isLeaf: true); + +/// Test alignment and padding of 16 byte int within struct. +void testPassInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + double a8; + double a9; + double a10; + double a11; + double a12; + double a13; + double a14; + double a15; + int a16; + int a17; + final a18Pointer = calloc(); + final Struct1ByteInt a18 = a18Pointer.ref; + int a19; + int a20; + final a21Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a21 = a21Pointer.ref; + int a22; + int a23; + final a24Pointer = calloc(); + final Struct8BytesInt a24 = a24Pointer.ref; + int a25; + int a26; + final a27Pointer = calloc(); + final Struct8BytesHomogeneousFloat a27 = a27Pointer.ref; + int a28; + int a29; + final a30Pointer = calloc(); + final Struct8BytesMixed a30 = a30Pointer.ref; + int a31; + int a32; + final a33Pointer = calloc(); + final StructAlignmentInt16 a33 = a33Pointer.ref; + int a34; + int a35; + final a36Pointer = calloc(); + final StructAlignmentInt32 a36 = a36Pointer.ref; + int a37; + int a38; + final a39Pointer = calloc(); + final StructAlignmentInt64 a39 = a39Pointer.ref; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + a5 = 6; + a6 = -7; + a7 = 8; + a8 = -9.0; + a9 = 10.0; + a10 = -11.0; + a11 = 12.0; + a12 = -13.0; + a13 = 14.0; + a14 = -15.0; + a15 = 16.0; + a16 = -17; + a17 = 18; + a18.a0 = -19; + a19 = 20; + a20 = -21; + a21.a0 = 22; + a21.a1 = -23; + a22 = 24; + a23 = -25; + a24.a0 = 26; + a24.a1 = -27; + a24.a2 = 28; + a25 = -29; + a26 = 30; + a27.a0 = -31.0; + a27.a1 = 32.0; + a28 = -33; + a29 = 34; + a30.a0 = -35.0; + a30.a1 = 36; + a30.a2 = -37; + a31 = 38; + a32 = -39; + a33.a0 = 40; + a33.a1 = -41; + a33.a2 = 42; + a34 = -43; + a35 = 44; + a36.a0 = -45; + a36.a1 = 46; + a36.a2 = -47; + a37 = 48; + a38 = -49; + a39.a0 = 50; + a39.a1 = -51; + a39.a2 = 52; + + final result = passInt32x8Doublex8Int64Int8Struct1ByteIntInt64IntLeaf( + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + a17, + a18, + a19, + a20, + a21, + a22, + a23, + a24, + a25, + a26, + a27, + a28, + a29, + a30, + a31, + a32, + a33, + a34, + a35, + a36, + a37, + a38, + a39); + + print("result = $result"); + + Expect.approxEquals(26.0, result); + + calloc.free(a18Pointer); + calloc.free(a21Pointer); + calloc.free(a24Pointer); + calloc.free(a27Pointer); + calloc.free(a30Pointer); + calloc.free(a33Pointer); + calloc.free(a36Pointer); + calloc.free(a39Pointer); +} + +final passStructAlignmentInt16Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(StructAlignmentInt16), + int Function( + StructAlignmentInt16)>("PassStructAlignmentInt16", isLeaf: true); + +/// Test alignment and padding of 16 byte int within struct. +void testPassStructAlignmentInt16Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt16 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = passStructAlignmentInt16Leaf(a0); + + print("result = $result"); + + Expect.equals(-2, result); + + calloc.free(a0Pointer); +} + +final passStructAlignmentInt32Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(StructAlignmentInt32), + int Function( + StructAlignmentInt32)>("PassStructAlignmentInt32", isLeaf: true); + +/// Test alignment and padding of 32 byte int within struct. +void testPassStructAlignmentInt32Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt32 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = passStructAlignmentInt32Leaf(a0); + + print("result = $result"); + + Expect.equals(-2, result); + + calloc.free(a0Pointer); +} + +final passStructAlignmentInt64Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(StructAlignmentInt64), + int Function( + StructAlignmentInt64)>("PassStructAlignmentInt64", isLeaf: true); + +/// Test alignment and padding of 64 byte int within struct. +void testPassStructAlignmentInt64Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt64 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = passStructAlignmentInt64Leaf(a0); + + print("result = $result"); + + Expect.equals(-2, result); + + calloc.free(a0Pointer); +} + +final passStruct8BytesNestedIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt), + int Function( + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt, + Struct8BytesNestedInt)>("PassStruct8BytesNestedIntx10", isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust registers on all platforms. +void testPassStruct8BytesNestedIntx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedInt a9 = a9Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a1.a0 = -3; + a0.a1.a1 = 4; + a1.a0.a0 = -5; + a1.a0.a1 = 6; + a1.a1.a0 = -7; + a1.a1.a1 = 8; + a2.a0.a0 = -9; + a2.a0.a1 = 10; + a2.a1.a0 = -11; + a2.a1.a1 = 12; + a3.a0.a0 = -13; + a3.a0.a1 = 14; + a3.a1.a0 = -15; + a3.a1.a1 = 16; + a4.a0.a0 = -17; + a4.a0.a1 = 18; + a4.a1.a0 = -19; + a4.a1.a1 = 20; + a5.a0.a0 = -21; + a5.a0.a1 = 22; + a5.a1.a0 = -23; + a5.a1.a1 = 24; + a6.a0.a0 = -25; + a6.a0.a1 = 26; + a6.a1.a0 = -27; + a6.a1.a1 = 28; + a7.a0.a0 = -29; + a7.a0.a1 = 30; + a7.a1.a0 = -31; + a7.a1.a1 = 32; + a8.a0.a0 = -33; + a8.a0.a1 = 34; + a8.a1.a0 = -35; + a8.a1.a1 = 36; + a9.a0.a0 = -37; + a9.a0.a1 = 38; + a9.a1.a0 = -39; + a9.a1.a1 = 40; + + final result = + passStruct8BytesNestedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(20, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesNestedFloatx10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat), + double Function( + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat, + Struct8BytesNestedFloat)>("PassStruct8BytesNestedFloatx10", + isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust fpu registers on all platforms. +void testPassStruct8BytesNestedFloatx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedFloat a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1.a0 = 2.0; + a1.a0.a0 = -3.0; + a1.a1.a0 = 4.0; + a2.a0.a0 = -5.0; + a2.a1.a0 = 6.0; + a3.a0.a0 = -7.0; + a3.a1.a0 = 8.0; + a4.a0.a0 = -9.0; + a4.a1.a0 = 10.0; + a5.a0.a0 = -11.0; + a5.a1.a0 = 12.0; + a6.a0.a0 = -13.0; + a6.a1.a0 = 14.0; + a7.a0.a0 = -15.0; + a7.a1.a0 = 16.0; + a8.a0.a0 = -17.0; + a8.a1.a0 = 18.0; + a9.a0.a0 = -19.0; + a9.a1.a0 = 20.0; + + final result = passStruct8BytesNestedFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesNestedFloat2x10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2), + double Function( + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2, + Struct8BytesNestedFloat2)>("PassStruct8BytesNestedFloat2x10", + isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust fpu registers on all platforms. +/// The nesting is irregular, testing homogenous float rules on arm and arm64, +/// and the fpu register usage on x64. +void testPassStruct8BytesNestedFloat2x10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedFloat2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedFloat2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedFloat2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedFloat2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedFloat2 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedFloat2 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedFloat2 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedFloat2 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedFloat2 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedFloat2 a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1 = 2.0; + a1.a0.a0 = -3.0; + a1.a1 = 4.0; + a2.a0.a0 = -5.0; + a2.a1 = 6.0; + a3.a0.a0 = -7.0; + a3.a1 = 8.0; + a4.a0.a0 = -9.0; + a4.a1 = 10.0; + a5.a0.a0 = -11.0; + a5.a1 = 12.0; + a6.a0.a0 = -13.0; + a6.a1 = 14.0; + a7.a0.a0 = -15.0; + a7.a1 = 16.0; + a8.a0.a0 = -17.0; + a8.a1 = 18.0; + a9.a0.a0 = -19.0; + a9.a1 = 20.0; + + final result = passStruct8BytesNestedFloat2x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesNestedMixedx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed), + double Function( + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed, + Struct8BytesNestedMixed)>("PassStruct8BytesNestedMixedx10", + isLeaf: true); + +/// Simple nested struct. No alignment gaps on any architectures. +/// 10 arguments exhaust all registers on all platforms. +void testPassStruct8BytesNestedMixedx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesNestedMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesNestedMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesNestedMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesNestedMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesNestedMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesNestedMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesNestedMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesNestedMixed a9 = a9Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a1.a0 = -3.0; + a1.a0.a0 = 4; + a1.a0.a1 = -5; + a1.a1.a0 = 6.0; + a2.a0.a0 = -7; + a2.a0.a1 = 8; + a2.a1.a0 = -9.0; + a3.a0.a0 = 10; + a3.a0.a1 = -11; + a3.a1.a0 = 12.0; + a4.a0.a0 = -13; + a4.a0.a1 = 14; + a4.a1.a0 = -15.0; + a5.a0.a0 = 16; + a5.a0.a1 = -17; + a5.a1.a0 = 18.0; + a6.a0.a0 = -19; + a6.a0.a1 = 20; + a6.a1.a0 = -21.0; + a7.a0.a0 = 22; + a7.a0.a1 = -23; + a7.a1.a0 = 24.0; + a8.a0.a0 = -25; + a8.a0.a1 = 26; + a8.a1.a0 = -27.0; + a9.a0.a0 = 28; + a9.a0.a1 = -29; + a9.a1.a0 = 30.0; + + final result = passStruct8BytesNestedMixedx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(15.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct16BytesNestedIntx2Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(Struct16BytesNestedInt, Struct16BytesNestedInt), + int Function(Struct16BytesNestedInt, + Struct16BytesNestedInt)>("PassStruct16BytesNestedIntx2", isLeaf: true); + +/// Deeper nested struct to test recursive member access. +void testPassStruct16BytesNestedIntx2Leaf() { + final a0Pointer = calloc(); + final Struct16BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0.a0 = -1; + a0.a0.a0.a1 = 2; + a0.a0.a1.a0 = -3; + a0.a0.a1.a1 = 4; + a0.a1.a0.a0 = -5; + a0.a1.a0.a1 = 6; + a0.a1.a1.a0 = -7; + a0.a1.a1.a1 = 8; + a1.a0.a0.a0 = -9; + a1.a0.a0.a1 = 10; + a1.a0.a1.a0 = -11; + a1.a0.a1.a1 = 12; + a1.a1.a0.a0 = -13; + a1.a1.a0.a1 = 14; + a1.a1.a1.a0 = -15; + a1.a1.a1.a1 = 16; + + final result = passStruct16BytesNestedIntx2Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(8, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final passStruct32BytesNestedIntx2Leaf = ffiTestFunctions.lookupFunction< + Int64 Function(Struct32BytesNestedInt, Struct32BytesNestedInt), + int Function(Struct32BytesNestedInt, + Struct32BytesNestedInt)>("PassStruct32BytesNestedIntx2", isLeaf: true); + +/// Even deeper nested struct to test recursive member access. +void testPassStruct32BytesNestedIntx2Leaf() { + final a0Pointer = calloc(); + final Struct32BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct32BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0.a0.a0 = -1; + a0.a0.a0.a0.a1 = 2; + a0.a0.a0.a1.a0 = -3; + a0.a0.a0.a1.a1 = 4; + a0.a0.a1.a0.a0 = -5; + a0.a0.a1.a0.a1 = 6; + a0.a0.a1.a1.a0 = -7; + a0.a0.a1.a1.a1 = 8; + a0.a1.a0.a0.a0 = -9; + a0.a1.a0.a0.a1 = 10; + a0.a1.a0.a1.a0 = -11; + a0.a1.a0.a1.a1 = 12; + a0.a1.a1.a0.a0 = -13; + a0.a1.a1.a0.a1 = 14; + a0.a1.a1.a1.a0 = -15; + a0.a1.a1.a1.a1 = 16; + a1.a0.a0.a0.a0 = -17; + a1.a0.a0.a0.a1 = 18; + a1.a0.a0.a1.a0 = -19; + a1.a0.a0.a1.a1 = 20; + a1.a0.a1.a0.a0 = -21; + a1.a0.a1.a0.a1 = 22; + a1.a0.a1.a1.a0 = -23; + a1.a0.a1.a1.a1 = 24; + a1.a1.a0.a0.a0 = -25; + a1.a1.a0.a0.a1 = 26; + a1.a1.a0.a1.a0 = -27; + a1.a1.a0.a1.a1 = 28; + a1.a1.a1.a0.a0 = -29; + a1.a1.a1.a0.a1 = 30; + a1.a1.a1.a1.a0 = -31; + a1.a1.a1.a1.a1 = 32; + + final result = passStruct32BytesNestedIntx2Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(16, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final passStructNestedIntStructAlignmentInt16Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function(StructNestedIntStructAlignmentInt16), + int Function(StructNestedIntStructAlignmentInt16)>( + "PassStructNestedIntStructAlignmentInt16", + isLeaf: true); + +/// Test alignment and padding of nested struct with 16 byte int. +void testPassStructNestedIntStructAlignmentInt16Leaf() { + final a0Pointer = calloc(); + final StructNestedIntStructAlignmentInt16 a0 = a0Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a0.a1.a0 = 4; + a0.a1.a1 = -5; + a0.a1.a2 = 6; + + final result = passStructNestedIntStructAlignmentInt16Leaf(a0); + + print("result = $result"); + + Expect.equals(3, result); + + calloc.free(a0Pointer); +} + +final passStructNestedIntStructAlignmentInt32Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function(StructNestedIntStructAlignmentInt32), + int Function(StructNestedIntStructAlignmentInt32)>( + "PassStructNestedIntStructAlignmentInt32", + isLeaf: true); + +/// Test alignment and padding of nested struct with 32 byte int. +void testPassStructNestedIntStructAlignmentInt32Leaf() { + final a0Pointer = calloc(); + final StructNestedIntStructAlignmentInt32 a0 = a0Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a0.a1.a0 = 4; + a0.a1.a1 = -5; + a0.a1.a2 = 6; + + final result = passStructNestedIntStructAlignmentInt32Leaf(a0); + + print("result = $result"); + + Expect.equals(3, result); + + calloc.free(a0Pointer); +} + +final passStructNestedIntStructAlignmentInt64Leaf = + ffiTestFunctions.lookupFunction< + Int64 Function(StructNestedIntStructAlignmentInt64), + int Function(StructNestedIntStructAlignmentInt64)>( + "PassStructNestedIntStructAlignmentInt64", + isLeaf: true); + +/// Test alignment and padding of nested struct with 64 byte int. +void testPassStructNestedIntStructAlignmentInt64Leaf() { + final a0Pointer = calloc(); + final StructNestedIntStructAlignmentInt64 a0 = a0Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a0.a1.a0 = 4; + a0.a1.a1 = -5; + a0.a1.a2 = 6; + + final result = passStructNestedIntStructAlignmentInt64Leaf(a0); + + print("result = $result"); + + Expect.equals(3, result); + + calloc.free(a0Pointer); +} + +final passStructNestedIrregularEvenBiggerx4Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger), + double Function( + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger, + StructNestedIrregularEvenBigger)>( + "PassStructNestedIrregularEvenBiggerx4", + isLeaf: true); + +/// Return big irregular struct as smoke test. +void testPassStructNestedIrregularEvenBiggerx4Leaf() { + final a0Pointer = calloc(); + final StructNestedIrregularEvenBigger a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructNestedIrregularEvenBigger a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructNestedIrregularEvenBigger a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructNestedIrregularEvenBigger a3 = a3Pointer.ref; + + a0.a0 = 1; + a0.a1.a0.a0 = 2; + a0.a1.a0.a1.a0.a0 = -3; + a0.a1.a0.a1.a0.a1 = 4; + a0.a1.a0.a1.a1.a0 = -5.0; + a0.a1.a0.a2 = 6; + a0.a1.a0.a3.a0.a0 = -7.0; + a0.a1.a0.a3.a1 = 8.0; + a0.a1.a0.a4 = 9; + a0.a1.a0.a5.a0.a0 = 10.0; + a0.a1.a0.a5.a1.a0 = -11.0; + a0.a1.a0.a6 = 12; + a0.a1.a1.a0.a0 = -13; + a0.a1.a1.a0.a1 = 14; + a0.a1.a1.a1.a0 = -15.0; + a0.a1.a2 = 16.0; + a0.a1.a3 = -17.0; + a0.a2.a0.a0 = 18; + a0.a2.a0.a1.a0.a0 = -19; + a0.a2.a0.a1.a0.a1 = 20; + a0.a2.a0.a1.a1.a0 = -21.0; + a0.a2.a0.a2 = 22; + a0.a2.a0.a3.a0.a0 = -23.0; + a0.a2.a0.a3.a1 = 24.0; + a0.a2.a0.a4 = 25; + a0.a2.a0.a5.a0.a0 = 26.0; + a0.a2.a0.a5.a1.a0 = -27.0; + a0.a2.a0.a6 = 28; + a0.a2.a1.a0.a0 = -29; + a0.a2.a1.a0.a1 = 30; + a0.a2.a1.a1.a0 = -31.0; + a0.a2.a2 = 32.0; + a0.a2.a3 = -33.0; + a0.a3 = 34.0; + a1.a0 = 35; + a1.a1.a0.a0 = 36; + a1.a1.a0.a1.a0.a0 = -37; + a1.a1.a0.a1.a0.a1 = 38; + a1.a1.a0.a1.a1.a0 = -39.0; + a1.a1.a0.a2 = 40; + a1.a1.a0.a3.a0.a0 = -41.0; + a1.a1.a0.a3.a1 = 42.0; + a1.a1.a0.a4 = 43; + a1.a1.a0.a5.a0.a0 = 44.0; + a1.a1.a0.a5.a1.a0 = -45.0; + a1.a1.a0.a6 = 46; + a1.a1.a1.a0.a0 = -47; + a1.a1.a1.a0.a1 = 48; + a1.a1.a1.a1.a0 = -49.0; + a1.a1.a2 = 50.0; + a1.a1.a3 = -51.0; + a1.a2.a0.a0 = 52; + a1.a2.a0.a1.a0.a0 = -53; + a1.a2.a0.a1.a0.a1 = 54; + a1.a2.a0.a1.a1.a0 = -55.0; + a1.a2.a0.a2 = 56; + a1.a2.a0.a3.a0.a0 = -57.0; + a1.a2.a0.a3.a1 = 58.0; + a1.a2.a0.a4 = 59; + a1.a2.a0.a5.a0.a0 = 60.0; + a1.a2.a0.a5.a1.a0 = -61.0; + a1.a2.a0.a6 = 62; + a1.a2.a1.a0.a0 = -63; + a1.a2.a1.a0.a1 = 64; + a1.a2.a1.a1.a0 = -65.0; + a1.a2.a2 = 66.0; + a1.a2.a3 = -67.0; + a1.a3 = 68.0; + a2.a0 = 69; + a2.a1.a0.a0 = 70; + a2.a1.a0.a1.a0.a0 = -71; + a2.a1.a0.a1.a0.a1 = 72; + a2.a1.a0.a1.a1.a0 = -73.0; + a2.a1.a0.a2 = 74; + a2.a1.a0.a3.a0.a0 = -75.0; + a2.a1.a0.a3.a1 = 76.0; + a2.a1.a0.a4 = 77; + a2.a1.a0.a5.a0.a0 = 78.0; + a2.a1.a0.a5.a1.a0 = -79.0; + a2.a1.a0.a6 = 80; + a2.a1.a1.a0.a0 = -81; + a2.a1.a1.a0.a1 = 82; + a2.a1.a1.a1.a0 = -83.0; + a2.a1.a2 = 84.0; + a2.a1.a3 = -85.0; + a2.a2.a0.a0 = 86; + a2.a2.a0.a1.a0.a0 = -87; + a2.a2.a0.a1.a0.a1 = 88; + a2.a2.a0.a1.a1.a0 = -89.0; + a2.a2.a0.a2 = 90; + a2.a2.a0.a3.a0.a0 = -91.0; + a2.a2.a0.a3.a1 = 92.0; + a2.a2.a0.a4 = 93; + a2.a2.a0.a5.a0.a0 = 94.0; + a2.a2.a0.a5.a1.a0 = -95.0; + a2.a2.a0.a6 = 96; + a2.a2.a1.a0.a0 = -97; + a2.a2.a1.a0.a1 = 98; + a2.a2.a1.a1.a0 = -99.0; + a2.a2.a2 = 100.0; + a2.a2.a3 = -101.0; + a2.a3 = 102.0; + a3.a0 = 103; + a3.a1.a0.a0 = 104; + a3.a1.a0.a1.a0.a0 = -105; + a3.a1.a0.a1.a0.a1 = 106; + a3.a1.a0.a1.a1.a0 = -107.0; + a3.a1.a0.a2 = 108; + a3.a1.a0.a3.a0.a0 = -109.0; + a3.a1.a0.a3.a1 = 110.0; + a3.a1.a0.a4 = 111; + a3.a1.a0.a5.a0.a0 = 112.0; + a3.a1.a0.a5.a1.a0 = -113.0; + a3.a1.a0.a6 = 114; + a3.a1.a1.a0.a0 = -115; + a3.a1.a1.a0.a1 = 116; + a3.a1.a1.a1.a0 = -117.0; + a3.a1.a2 = 118.0; + a3.a1.a3 = -119.0; + a3.a2.a0.a0 = 120; + a3.a2.a0.a1.a0.a0 = -121; + a3.a2.a0.a1.a0.a1 = 122; + a3.a2.a0.a1.a1.a0 = -123.0; + a3.a2.a0.a2 = 124; + a3.a2.a0.a3.a0.a0 = -125.0; + a3.a2.a0.a3.a1 = 126.0; + a3.a2.a0.a4 = 127; + a3.a2.a0.a5.a0.a0 = 128.0; + a3.a2.a0.a5.a1.a0 = -129.0; + a3.a2.a0.a6 = 130; + a3.a2.a1.a0.a0 = -131; + a3.a2.a1.a0.a1 = 132; + a3.a2.a1.a1.a0 = -133.0; + a3.a2.a2 = 134.0; + a3.a2.a3 = -135.0; + a3.a3 = 136.0; + + final result = passStructNestedIrregularEvenBiggerx4Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(1572.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); +} + +final passStruct8BytesInlineArrayIntx4Leaf = ffiTestFunctions.lookupFunction< + Int32 Function(Struct8BytesInlineArrayInt, Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt, Struct8BytesInlineArrayInt), + int Function( + Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt, + Struct8BytesInlineArrayInt)>("PassStruct8BytesInlineArrayIntx4", + isLeaf: true); + +/// Simple struct with inline array. +void testPassStruct8BytesInlineArrayIntx4Leaf() { + final a0Pointer = calloc(); + final Struct8BytesInlineArrayInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesInlineArrayInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesInlineArrayInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesInlineArrayInt a3 = a3Pointer.ref; + + a0.a0[0] = 1; + a0.a0[1] = 2; + a0.a0[2] = 3; + a0.a0[3] = 4; + a0.a0[4] = 5; + a0.a0[5] = 6; + a0.a0[6] = 7; + a0.a0[7] = 8; + a1.a0[0] = 9; + a1.a0[1] = 10; + a1.a0[2] = 11; + a1.a0[3] = 12; + a1.a0[4] = 13; + a1.a0[5] = 14; + a1.a0[6] = 15; + a1.a0[7] = 16; + a2.a0[0] = 17; + a2.a0[1] = 18; + a2.a0[2] = 19; + a2.a0[3] = 20; + a2.a0[4] = 21; + a2.a0[5] = 22; + a2.a0[6] = 23; + a2.a0[7] = 24; + a3.a0[0] = 25; + a3.a0[1] = 26; + a3.a0[2] = 27; + a3.a0[3] = 28; + a3.a0[4] = 29; + a3.a0[5] = 30; + a3.a0[6] = 31; + a3.a0[7] = 32; + + final result = passStruct8BytesInlineArrayIntx4Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.equals(528, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); +} + +final passStructInlineArrayIrregularx4Leaf = ffiTestFunctions.lookupFunction< + Int32 Function(StructInlineArrayIrregular, StructInlineArrayIrregular, + StructInlineArrayIrregular, StructInlineArrayIrregular), + int Function( + StructInlineArrayIrregular, + StructInlineArrayIrregular, + StructInlineArrayIrregular, + StructInlineArrayIrregular)>("PassStructInlineArrayIrregularx4", + isLeaf: true); + +/// Irregular struct with inline array. +void testPassStructInlineArrayIrregularx4Leaf() { + final a0Pointer = calloc(); + final StructInlineArrayIrregular a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructInlineArrayIrregular a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructInlineArrayIrregular a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructInlineArrayIrregular a3 = a3Pointer.ref; + + a0.a0[0].a0 = -1; + a0.a0[0].a1 = 2; + a0.a0[1].a0 = -3; + a0.a0[1].a1 = 4; + a0.a1 = 5; + a1.a0[0].a0 = 6; + a1.a0[0].a1 = -7; + a1.a0[1].a0 = 8; + a1.a0[1].a1 = -9; + a1.a1 = 10; + a2.a0[0].a0 = -11; + a2.a0[0].a1 = 12; + a2.a0[1].a0 = -13; + a2.a0[1].a1 = 14; + a2.a1 = 15; + a3.a0[0].a0 = 16; + a3.a0[0].a1 = -17; + a3.a0[1].a0 = 18; + a3.a0[1].a1 = -19; + a3.a1 = 20; + + final result = passStructInlineArrayIrregularx4Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.equals(50, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); +} + +final passStructInlineArray100BytesLeaf = ffiTestFunctions.lookupFunction< + Int32 Function(StructInlineArray100Bytes), + int Function(StructInlineArray100Bytes)>( + "PassStructInlineArray100Bytes", + isLeaf: true); + +/// Regular larger struct with inline array. +void testPassStructInlineArray100BytesLeaf() { + final a0Pointer = calloc(); + final StructInlineArray100Bytes a0 = a0Pointer.ref; + + a0.a0[0] = 1; + a0.a0[1] = 2; + a0.a0[2] = 3; + a0.a0[3] = 4; + a0.a0[4] = 5; + a0.a0[5] = 6; + a0.a0[6] = 7; + a0.a0[7] = 8; + a0.a0[8] = 9; + a0.a0[9] = 10; + a0.a0[10] = 11; + a0.a0[11] = 12; + a0.a0[12] = 13; + a0.a0[13] = 14; + a0.a0[14] = 15; + a0.a0[15] = 16; + a0.a0[16] = 17; + a0.a0[17] = 18; + a0.a0[18] = 19; + a0.a0[19] = 20; + a0.a0[20] = 21; + a0.a0[21] = 22; + a0.a0[22] = 23; + a0.a0[23] = 24; + a0.a0[24] = 25; + a0.a0[25] = 26; + a0.a0[26] = 27; + a0.a0[27] = 28; + a0.a0[28] = 29; + a0.a0[29] = 30; + a0.a0[30] = 31; + a0.a0[31] = 32; + a0.a0[32] = 33; + a0.a0[33] = 34; + a0.a0[34] = 35; + a0.a0[35] = 36; + a0.a0[36] = 37; + a0.a0[37] = 38; + a0.a0[38] = 39; + a0.a0[39] = 40; + a0.a0[40] = 41; + a0.a0[41] = 42; + a0.a0[42] = 43; + a0.a0[43] = 44; + a0.a0[44] = 45; + a0.a0[45] = 46; + a0.a0[46] = 47; + a0.a0[47] = 48; + a0.a0[48] = 49; + a0.a0[49] = 50; + a0.a0[50] = 51; + a0.a0[51] = 52; + a0.a0[52] = 53; + a0.a0[53] = 54; + a0.a0[54] = 55; + a0.a0[55] = 56; + a0.a0[56] = 57; + a0.a0[57] = 58; + a0.a0[58] = 59; + a0.a0[59] = 60; + a0.a0[60] = 61; + a0.a0[61] = 62; + a0.a0[62] = 63; + a0.a0[63] = 64; + a0.a0[64] = 65; + a0.a0[65] = 66; + a0.a0[66] = 67; + a0.a0[67] = 68; + a0.a0[68] = 69; + a0.a0[69] = 70; + a0.a0[70] = 71; + a0.a0[71] = 72; + a0.a0[72] = 73; + a0.a0[73] = 74; + a0.a0[74] = 75; + a0.a0[75] = 76; + a0.a0[76] = 77; + a0.a0[77] = 78; + a0.a0[78] = 79; + a0.a0[79] = 80; + a0.a0[80] = 81; + a0.a0[81] = 82; + a0.a0[82] = 83; + a0.a0[83] = 84; + a0.a0[84] = 85; + a0.a0[85] = 86; + a0.a0[86] = 87; + a0.a0[87] = 88; + a0.a0[88] = 89; + a0.a0[89] = 90; + a0.a0[90] = 91; + a0.a0[91] = 92; + a0.a0[92] = 93; + a0.a0[93] = 94; + a0.a0[94] = 95; + a0.a0[95] = 96; + a0.a0[96] = 97; + a0.a0[97] = 98; + a0.a0[98] = 99; + a0.a0[99] = 100; + + final result = passStructInlineArray100BytesLeaf(a0); + + print("result = $result"); + + Expect.equals(5050, result); + + calloc.free(a0Pointer); +} + +final passStructStruct16BytesHomogeneousFloat2x5Leaf = + ffiTestFunctions.lookupFunction< + Float Function( + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2), + double Function( + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2, + StructStruct16BytesHomogeneousFloat2)>( + "PassStructStruct16BytesHomogeneousFloat2x5", + isLeaf: true); + +/// Arguments in FPU registers on arm hardfp and arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStructStruct16BytesHomogeneousFloat2x5Leaf() { + final a0Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a4 = a4Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + a1.a0.a0 = -5.0; + a1.a1[0].a0 = 6.0; + a1.a1[1].a0 = -7.0; + a1.a2 = 8.0; + a2.a0.a0 = -9.0; + a2.a1[0].a0 = 10.0; + a2.a1[1].a0 = -11.0; + a2.a2 = 12.0; + a3.a0.a0 = -13.0; + a3.a1[0].a0 = 14.0; + a3.a1[1].a0 = -15.0; + a3.a2 = 16.0; + a4.a0.a0 = -17.0; + a4.a1[0].a0 = 18.0; + a4.a1[1].a0 = -19.0; + a4.a2 = 20.0; + + final result = + passStructStruct16BytesHomogeneousFloat2x5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStructStruct32BytesHomogeneousDouble2x5Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2), + double Function( + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2, + StructStruct32BytesHomogeneousDouble2)>( + "PassStructStruct32BytesHomogeneousDouble2x5", + isLeaf: true); + +/// Arguments in FPU registers on arm64. +/// 5 struct arguments will exhaust available registers. +void testPassStructStruct32BytesHomogeneousDouble2x5Leaf() { + final a0Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a4 = a4Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + a1.a0.a0 = -5.0; + a1.a1[0].a0 = 6.0; + a1.a1[1].a0 = -7.0; + a1.a2 = 8.0; + a2.a0.a0 = -9.0; + a2.a1[0].a0 = 10.0; + a2.a1[1].a0 = -11.0; + a2.a2 = 12.0; + a3.a0.a0 = -13.0; + a3.a1[0].a0 = 14.0; + a3.a1[1].a0 = -15.0; + a3.a2 = 16.0; + a4.a0.a0 = -17.0; + a4.a1[0].a0 = 18.0; + a4.a1[1].a0 = -19.0; + a4.a2 = 20.0; + + final result = + passStructStruct32BytesHomogeneousDouble2x5Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); +} + +final passStructStruct16BytesMixed3x10Leaf = ffiTestFunctions.lookupFunction< + Float Function( + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3), + double Function( + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3, + StructStruct16BytesMixed3)>("PassStructStruct16BytesMixed3x10", + isLeaf: true); + +/// On x64, arguments are split over FP and int registers. +/// On x64, it will exhaust the integer registers with the 6th argument. +/// The rest goes on the stack. +/// On arm, arguments are 4 byte aligned. +void testPassStructStruct16BytesMixed3x10Leaf() { + final a0Pointer = calloc(); + final StructStruct16BytesMixed3 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructStruct16BytesMixed3 a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructStruct16BytesMixed3 a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final StructStruct16BytesMixed3 a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final StructStruct16BytesMixed3 a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final StructStruct16BytesMixed3 a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final StructStruct16BytesMixed3 a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final StructStruct16BytesMixed3 a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final StructStruct16BytesMixed3 a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final StructStruct16BytesMixed3 a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[0].a1 = -3; + a0.a1[0].a2 = 4; + a0.a2[0] = -5; + a0.a2[1] = 6; + a1.a0.a0 = -7.0; + a1.a1[0].a0 = 8.0; + a1.a1[0].a1 = -9; + a1.a1[0].a2 = 10; + a1.a2[0] = -11; + a1.a2[1] = 12; + a2.a0.a0 = -13.0; + a2.a1[0].a0 = 14.0; + a2.a1[0].a1 = -15; + a2.a1[0].a2 = 16; + a2.a2[0] = -17; + a2.a2[1] = 18; + a3.a0.a0 = -19.0; + a3.a1[0].a0 = 20.0; + a3.a1[0].a1 = -21; + a3.a1[0].a2 = 22; + a3.a2[0] = -23; + a3.a2[1] = 24; + a4.a0.a0 = -25.0; + a4.a1[0].a0 = 26.0; + a4.a1[0].a1 = -27; + a4.a1[0].a2 = 28; + a4.a2[0] = -29; + a4.a2[1] = 30; + a5.a0.a0 = -31.0; + a5.a1[0].a0 = 32.0; + a5.a1[0].a1 = -33; + a5.a1[0].a2 = 34; + a5.a2[0] = -35; + a5.a2[1] = 36; + a6.a0.a0 = -37.0; + a6.a1[0].a0 = 38.0; + a6.a1[0].a1 = -39; + a6.a1[0].a2 = 40; + a6.a2[0] = -41; + a6.a2[1] = 42; + a7.a0.a0 = -43.0; + a7.a1[0].a0 = 44.0; + a7.a1[0].a1 = -45; + a7.a1[0].a2 = 46; + a7.a2[0] = -47; + a7.a2[1] = 48; + a8.a0.a0 = -49.0; + a8.a1[0].a0 = 50.0; + a8.a1[0].a1 = -51; + a8.a1[0].a2 = 52; + a8.a2[0] = -53; + a8.a2[1] = 54; + a9.a0.a0 = -55.0; + a9.a1[0].a0 = 56.0; + a9.a1[0].a1 = -57; + a9.a1[0].a2 = 58; + a9.a2[0] = -59; + a9.a2[1] = 60; + + final result = passStructStruct16BytesMixed3x10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(30.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUint8Struct32BytesInlineArrayMultiDimensionalILeaf = + ffiTestFunctions.lookupFunction< + Uint32 Function( + Uint8, + Struct32BytesInlineArrayMultiDimensionalInt, + Uint8, + Struct8BytesInlineArrayMultiDimensionalInt, + Uint8, + Struct8BytesInlineArrayMultiDimensionalInt, + Uint8), + int Function( + int, + Struct32BytesInlineArrayMultiDimensionalInt, + int, + Struct8BytesInlineArrayMultiDimensionalInt, + int, + Struct8BytesInlineArrayMultiDimensionalInt, + int)>("PassUint8Struct32BytesInlineArrayMultiDimensionalI", + isLeaf: true); + +/// Test multi dimensional inline array struct as argument. +void testPassUint8Struct32BytesInlineArrayMultiDimensionalILeaf() { + int a0; + final a1Pointer = calloc(); + final Struct32BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref; + int a2; + final a3Pointer = calloc(); + final Struct8BytesInlineArrayMultiDimensionalInt a3 = a3Pointer.ref; + int a4; + final a5Pointer = calloc(); + final Struct8BytesInlineArrayMultiDimensionalInt a5 = a5Pointer.ref; + int a6; + + a0 = 1; + a1.a0[0][0][0][0][0] = 2; + a1.a0[0][0][0][0][1] = 3; + a1.a0[0][0][0][1][0] = 4; + a1.a0[0][0][0][1][1] = 5; + a1.a0[0][0][1][0][0] = 6; + a1.a0[0][0][1][0][1] = 7; + a1.a0[0][0][1][1][0] = 8; + a1.a0[0][0][1][1][1] = 9; + a1.a0[0][1][0][0][0] = 10; + a1.a0[0][1][0][0][1] = 11; + a1.a0[0][1][0][1][0] = 12; + a1.a0[0][1][0][1][1] = 13; + a1.a0[0][1][1][0][0] = 14; + a1.a0[0][1][1][0][1] = 15; + a1.a0[0][1][1][1][0] = 16; + a1.a0[0][1][1][1][1] = 17; + a1.a0[1][0][0][0][0] = 18; + a1.a0[1][0][0][0][1] = 19; + a1.a0[1][0][0][1][0] = 20; + a1.a0[1][0][0][1][1] = 21; + a1.a0[1][0][1][0][0] = 22; + a1.a0[1][0][1][0][1] = 23; + a1.a0[1][0][1][1][0] = 24; + a1.a0[1][0][1][1][1] = 25; + a1.a0[1][1][0][0][0] = 26; + a1.a0[1][1][0][0][1] = 27; + a1.a0[1][1][0][1][0] = 28; + a1.a0[1][1][0][1][1] = 29; + a1.a0[1][1][1][0][0] = 30; + a1.a0[1][1][1][0][1] = 31; + a1.a0[1][1][1][1][0] = 32; + a1.a0[1][1][1][1][1] = 33; + a2 = 34; + a3.a0[0][0][0] = 35; + a3.a0[0][0][1] = 36; + a3.a0[0][1][0] = 37; + a3.a0[0][1][1] = 38; + a3.a0[1][0][0] = 39; + a3.a0[1][0][1] = 40; + a3.a0[1][1][0] = 41; + a3.a0[1][1][1] = 42; + a4 = 43; + a5.a0[0][0][0] = 44; + a5.a0[0][0][1] = 45; + a5.a0[0][1][0] = 46; + a5.a0[0][1][1] = 47; + a5.a0[1][0][0] = 48; + a5.a0[1][0][1] = 49; + a5.a0[1][1][0] = 50; + a5.a0[1][1][1] = 51; + a6 = 52; + + final result = passUint8Struct32BytesInlineArrayMultiDimensionalILeaf( + a0, a1, a2, a3, a4, a5, a6); + + print("result = $result"); + + Expect.equals(1378, result); + + calloc.free(a1Pointer); + calloc.free(a3Pointer); + calloc.free(a5Pointer); +} + +final passUint8Struct4BytesInlineArrayMultiDimensionalInLeaf = + ffiTestFunctions.lookupFunction< + Uint32 Function( + Uint8, Struct4BytesInlineArrayMultiDimensionalInt, Uint8), + int Function(int, Struct4BytesInlineArrayMultiDimensionalInt, int)>( + "PassUint8Struct4BytesInlineArrayMultiDimensionalIn", + isLeaf: true); + +/// Test struct in multi dimensional inline array. +void testPassUint8Struct4BytesInlineArrayMultiDimensionalInLeaf() { + int a0; + final a1Pointer = calloc(); + final Struct4BytesInlineArrayMultiDimensionalInt a1 = a1Pointer.ref; + int a2; + + a0 = 1; + a1.a0[0][0].a0 = 2; + a1.a0[0][1].a0 = -3; + a1.a0[1][0].a0 = 4; + a1.a0[1][1].a0 = -5; + a2 = 6; + + final result = + passUint8Struct4BytesInlineArrayMultiDimensionalInLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(5, result); + + calloc.free(a1Pointer); +} + +final passStruct3BytesPackedIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt), + int Function( + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt, + Struct3BytesPackedInt)>("PassStruct3BytesPackedIntx10", isLeaf: true); + +/// Small struct with mis-aligned member. +void testPassStruct3BytesPackedIntx10Leaf() { + final a0Pointer = calloc(); + final Struct3BytesPackedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct3BytesPackedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct3BytesPackedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct3BytesPackedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct3BytesPackedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct3BytesPackedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct3BytesPackedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct3BytesPackedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct3BytesPackedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct3BytesPackedInt a9 = a9Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + a2.a0 = -5; + a2.a1 = 6; + a3.a0 = -7; + a3.a1 = 8; + a4.a0 = -9; + a4.a1 = 10; + a5.a0 = -11; + a5.a1 = 12; + a6.a0 = -13; + a6.a1 = 14; + a7.a0 = -15; + a7.a1 = 16; + a8.a0 = -17; + a8.a1 = 18; + a9.a0 = -19; + a9.a1 = 20; + + final result = + passStruct3BytesPackedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(10, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct8BytesPackedIntx10Leaf = ffiTestFunctions.lookupFunction< + Int64 Function( + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt), + int Function( + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt, + Struct8BytesPackedInt)>("PassStruct8BytesPackedIntx10", isLeaf: true); + +/// Struct with mis-aligned member. +void testPassStruct8BytesPackedIntx10Leaf() { + final a0Pointer = calloc(); + final Struct8BytesPackedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesPackedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct8BytesPackedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct8BytesPackedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct8BytesPackedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct8BytesPackedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct8BytesPackedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct8BytesPackedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct8BytesPackedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct8BytesPackedInt a9 = a9Pointer.ref; + + a0.a0 = 1; + a0.a1 = 2; + a0.a2 = 3; + a0.a3 = 4; + a0.a4 = 5; + a1.a0 = 6; + a1.a1 = 7; + a1.a2 = 8; + a1.a3 = 9; + a1.a4 = 10; + a2.a0 = 11; + a2.a1 = 12; + a2.a2 = 13; + a2.a3 = 14; + a2.a4 = 15; + a3.a0 = 16; + a3.a1 = 17; + a3.a2 = 18; + a3.a3 = 19; + a3.a4 = 20; + a4.a0 = 21; + a4.a1 = 22; + a4.a2 = 23; + a4.a3 = 24; + a4.a4 = 25; + a5.a0 = 26; + a5.a1 = 27; + a5.a2 = 28; + a5.a3 = 29; + a5.a4 = 30; + a6.a0 = 31; + a6.a1 = 32; + a6.a2 = 33; + a6.a3 = 34; + a6.a4 = 35; + a7.a0 = 36; + a7.a1 = 37; + a7.a2 = 38; + a7.a3 = 39; + a7.a4 = 40; + a8.a0 = 41; + a8.a1 = 42; + a8.a2 = 43; + a8.a3 = 44; + a8.a4 = 45; + a9.a0 = 46; + a9.a1 = 47; + a9.a2 = 48; + a9.a3 = 49; + a9.a4 = 50; + + final result = + passStruct8BytesPackedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.equals(1275, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct9BytesPackedMixedx10DoubleInt32Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Double, + Int32), + double Function( + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + Struct9BytesPackedMixed, + double, + int)>("PassStruct9BytesPackedMixedx10DoubleInt32", isLeaf: true); + +/// Struct with mis-aligned member. +/// Tests backfilling of CPU and FPU registers. +void testPassStruct9BytesPackedMixedx10DoubleInt32Leaf() { + final a0Pointer = calloc(); + final Struct9BytesPackedMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct9BytesPackedMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Struct9BytesPackedMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Struct9BytesPackedMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Struct9BytesPackedMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Struct9BytesPackedMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Struct9BytesPackedMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Struct9BytesPackedMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Struct9BytesPackedMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Struct9BytesPackedMixed a9 = a9Pointer.ref; + double a10; + int a11; + + a0.a0 = 1; + a0.a1 = 2.0; + a1.a0 = 3; + a1.a1 = 4.0; + a2.a0 = 5; + a2.a1 = 6.0; + a3.a0 = 7; + a3.a1 = 8.0; + a4.a0 = 9; + a4.a1 = 10.0; + a5.a0 = 11; + a5.a1 = 12.0; + a6.a0 = 13; + a6.a1 = 14.0; + a7.a0 = 15; + a7.a1 = 16.0; + a8.a0 = 17; + a8.a1 = 18.0; + a9.a0 = 19; + a9.a1 = 20.0; + a10 = -21.0; + a11 = 22; + + final result = passStruct9BytesPackedMixedx10DoubleInt32Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); + + print("result = $result"); + + Expect.approxEquals(211.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passStruct5BytesPackedMixedLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct5BytesPackedMixed), + double Function( + Struct5BytesPackedMixed)>("PassStruct5BytesPackedMixed", isLeaf: true); + +/// This packed struct happens to have only aligned members. +void testPassStruct5BytesPackedMixedLeaf() { + final a0Pointer = calloc(); + final Struct5BytesPackedMixed a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2; + + final result = passStruct5BytesPackedMixedLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(1.0, result); + + calloc.free(a0Pointer); +} + +final passStructNestedAlignmentStruct5BytesPackedMixedLeaf = + ffiTestFunctions.lookupFunction< + Double Function(StructNestedAlignmentStruct5BytesPackedMixed), + double Function(StructNestedAlignmentStruct5BytesPackedMixed)>( + "PassStructNestedAlignmentStruct5BytesPackedMixed", + isLeaf: true); + +/// Check alignment of packed struct in non-packed struct. +void testPassStructNestedAlignmentStruct5BytesPackedMixedLeaf() { + final a0Pointer = calloc(); + final StructNestedAlignmentStruct5BytesPackedMixed a0 = a0Pointer.ref; + + a0.a0 = 1; + a0.a1.a0 = 2.0; + a0.a1.a1 = 3; + + final result = passStructNestedAlignmentStruct5BytesPackedMixedLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(6.0, result); + + calloc.free(a0Pointer); +} + +final passStruct6BytesInlineArrayIntLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct6BytesInlineArrayInt), + double Function(Struct6BytesInlineArrayInt)>( + "PassStruct6BytesInlineArrayInt", + isLeaf: true); + +/// Check alignment of packed struct array in non-packed struct. +void testPassStruct6BytesInlineArrayIntLeaf() { + final a0Pointer = calloc(); + final Struct6BytesInlineArrayInt a0 = a0Pointer.ref; + + a0.a0[0].a0 = -1; + a0.a0[0].a1 = 2; + a0.a0[1].a0 = -3; + a0.a0[1].a1 = 4; + + final result = passStruct6BytesInlineArrayIntLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(2.0, result); + + calloc.free(a0Pointer); +} + +final passStruct15BytesInlineArrayMixedLeaf = ffiTestFunctions.lookupFunction< + Double Function(Struct15BytesInlineArrayMixed), + double Function(Struct15BytesInlineArrayMixed)>( + "PassStruct15BytesInlineArrayMixed", + isLeaf: true); + +/// Check alignment of packed struct array in non-packed struct. +void testPassStruct15BytesInlineArrayMixedLeaf() { + final a0Pointer = calloc(); + final Struct15BytesInlineArrayMixed a0 = a0Pointer.ref; + + a0.a0[0].a0 = -1.0; + a0.a0[0].a1 = 2; + a0.a0[1].a0 = -3.0; + a0.a0[1].a1 = 4; + a0.a0[2].a0 = -5.0; + a0.a0[2].a1 = 6; + + final result = passStruct15BytesInlineArrayMixedLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(3.0, result); + + calloc.free(a0Pointer); +} + +final passUnion4BytesMixedx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed), + double Function( + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed, + Union4BytesMixed)>("PassUnion4BytesMixedx10", isLeaf: true); + +/// Check placement of mixed integer/float union. +void testPassUnion4BytesMixedx10Leaf() { + final a0Pointer = calloc(); + final Union4BytesMixed a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union4BytesMixed a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union4BytesMixed a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union4BytesMixed a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union4BytesMixed a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union4BytesMixed a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union4BytesMixed a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union4BytesMixed a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union4BytesMixed a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union4BytesMixed a9 = a9Pointer.ref; + + a0.a0 = 1; + a1.a0 = 2; + a2.a0 = 3; + a3.a0 = 4; + a4.a0 = 5; + a5.a0 = 6; + a6.a0 = 7; + a7.a0 = 8; + a8.a0 = 9; + a9.a0 = 10; + + final result = + passUnion4BytesMixedx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(55.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion8BytesNestedFloatx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat), + double Function( + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat, + Union8BytesNestedFloat)>("PassUnion8BytesNestedFloatx10", isLeaf: true); + +/// Check placement of mixed floats union. +void testPassUnion8BytesNestedFloatx10Leaf() { + final a0Pointer = calloc(); + final Union8BytesNestedFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union8BytesNestedFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union8BytesNestedFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union8BytesNestedFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union8BytesNestedFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union8BytesNestedFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union8BytesNestedFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union8BytesNestedFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union8BytesNestedFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union8BytesNestedFloat a9 = a9Pointer.ref; + + a0.a0 = -1.0; + a1.a0 = 2.0; + a2.a0 = -3.0; + a3.a0 = 4.0; + a4.a0 = -5.0; + a5.a0 = 6.0; + a6.a0 = -7.0; + a7.a0 = 8.0; + a8.a0 = -9.0; + a9.a0 = 10.0; + + final result = + passUnion8BytesNestedFloatx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(5.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion9BytesNestedIntx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt), + double Function( + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt, + Union9BytesNestedInt)>("PassUnion9BytesNestedIntx10", isLeaf: true); + +/// Mixed-size union argument. +void testPassUnion9BytesNestedIntx10Leaf() { + final a0Pointer = calloc(); + final Union9BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union9BytesNestedInt a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union9BytesNestedInt a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union9BytesNestedInt a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union9BytesNestedInt a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union9BytesNestedInt a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union9BytesNestedInt a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union9BytesNestedInt a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union9BytesNestedInt a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union9BytesNestedInt a9 = a9Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a0.a2 = -3; + a1.a0.a0 = 4; + a1.a0.a1 = -5; + a1.a0.a2 = 6; + a2.a0.a0 = -7; + a2.a0.a1 = 8; + a2.a0.a2 = -9; + a3.a0.a0 = 10; + a3.a0.a1 = -11; + a3.a0.a2 = 12; + a4.a0.a0 = -13; + a4.a0.a1 = 14; + a4.a0.a2 = -15; + a5.a0.a0 = 16; + a5.a0.a1 = -17; + a5.a0.a2 = 18; + a6.a0.a0 = -19; + a6.a0.a1 = 20; + a6.a0.a2 = -21; + a7.a0.a0 = 22; + a7.a0.a1 = -23; + a7.a0.a2 = 24; + a8.a0.a0 = -25; + a8.a0.a1 = 26; + a8.a0.a2 = -27; + a9.a0.a0 = 28; + a9.a0.a1 = -29; + a9.a0.a2 = 30; + + final result = + passUnion9BytesNestedIntx10Leaf(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(15.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion16BytesNestedInlineArrayFloatx10Leaf = + ffiTestFunctions.lookupFunction< + Double Function( + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat), + double Function( + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat, + Union16BytesNestedInlineArrayFloat)>( + "PassUnion16BytesNestedInlineArrayFloatx10", + isLeaf: true); + +/// Union with homogenous floats. +void testPassUnion16BytesNestedInlineArrayFloatx10Leaf() { + final a0Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union16BytesNestedInlineArrayFloat a9 = a9Pointer.ref; + + a0.a0[0] = -1.0; + a0.a0[1] = 2.0; + a0.a0[2] = -3.0; + a0.a0[3] = 4.0; + a1.a0[0] = -5.0; + a1.a0[1] = 6.0; + a1.a0[2] = -7.0; + a1.a0[3] = 8.0; + a2.a0[0] = -9.0; + a2.a0[1] = 10.0; + a2.a0[2] = -11.0; + a2.a0[3] = 12.0; + a3.a0[0] = -13.0; + a3.a0[1] = 14.0; + a3.a0[2] = -15.0; + a3.a0[3] = 16.0; + a4.a0[0] = -17.0; + a4.a0[1] = 18.0; + a4.a0[2] = -19.0; + a4.a0[3] = 20.0; + a5.a0[0] = -21.0; + a5.a0[1] = 22.0; + a5.a0[2] = -23.0; + a5.a0[3] = 24.0; + a6.a0[0] = -25.0; + a6.a0[1] = 26.0; + a6.a0[2] = -27.0; + a6.a0[3] = 28.0; + a7.a0[0] = -29.0; + a7.a0[1] = 30.0; + a7.a0[2] = -31.0; + a7.a0[3] = 32.0; + a8.a0[0] = -33.0; + a8.a0[1] = 34.0; + a8.a0[2] = -35.0; + a8.a0[3] = 36.0; + a9.a0[0] = -37.0; + a9.a0[1] = 38.0; + a9.a0[2] = -39.0; + a9.a0[3] = 40.0; + + final result = passUnion16BytesNestedInlineArrayFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(20.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final passUnion16BytesNestedFloatx10Leaf = ffiTestFunctions.lookupFunction< + Double Function( + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat), + double Function( + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat, + Union16BytesNestedFloat)>("PassUnion16BytesNestedFloatx10", + isLeaf: true); + +/// Union with homogenous floats. +void testPassUnion16BytesNestedFloatx10Leaf() { + final a0Pointer = calloc(); + final Union16BytesNestedFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Union16BytesNestedFloat a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final Union16BytesNestedFloat a2 = a2Pointer.ref; + final a3Pointer = calloc(); + final Union16BytesNestedFloat a3 = a3Pointer.ref; + final a4Pointer = calloc(); + final Union16BytesNestedFloat a4 = a4Pointer.ref; + final a5Pointer = calloc(); + final Union16BytesNestedFloat a5 = a5Pointer.ref; + final a6Pointer = calloc(); + final Union16BytesNestedFloat a6 = a6Pointer.ref; + final a7Pointer = calloc(); + final Union16BytesNestedFloat a7 = a7Pointer.ref; + final a8Pointer = calloc(); + final Union16BytesNestedFloat a8 = a8Pointer.ref; + final a9Pointer = calloc(); + final Union16BytesNestedFloat a9 = a9Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a0.a1 = 2.0; + a1.a0.a0 = -3.0; + a1.a0.a1 = 4.0; + a2.a0.a0 = -5.0; + a2.a0.a1 = 6.0; + a3.a0.a0 = -7.0; + a3.a0.a1 = 8.0; + a4.a0.a0 = -9.0; + a4.a0.a1 = 10.0; + a5.a0.a0 = -11.0; + a5.a0.a1 = 12.0; + a6.a0.a0 = -13.0; + a6.a0.a1 = 14.0; + a7.a0.a0 = -15.0; + a7.a0.a1 = 16.0; + a8.a0.a0 = -17.0; + a8.a0.a1 = 18.0; + a9.a0.a0 = -19.0; + a9.a0.a1 = 20.0; + + final result = passUnion16BytesNestedFloatx10Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + + print("result = $result"); + + Expect.approxEquals(10.0, result); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); + calloc.free(a2Pointer); + calloc.free(a3Pointer); + calloc.free(a4Pointer); + calloc.free(a5Pointer); + calloc.free(a6Pointer); + calloc.free(a7Pointer); + calloc.free(a8Pointer); + calloc.free(a9Pointer); +} + +final returnStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction< + Struct1ByteInt Function(Int8), + Struct1ByteInt Function(int)>("ReturnStruct1ByteInt", isLeaf: true); + +/// Smallest struct with data. +void testReturnStruct1ByteIntLeaf() { + int a0; + + a0 = -1; + + final result = returnStruct1ByteIntLeaf(a0); + + print("result = $result"); + + Expect.equals(a0, result.a0); +} + +final returnStruct3BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct3BytesHomogeneousUint8 Function(Uint8, Uint8, Uint8), + Struct3BytesHomogeneousUint8 Function( + int, int, int)>("ReturnStruct3BytesHomogeneousUint8", isLeaf: true); + +/// Smaller than word size return value on all architectures. +void testReturnStruct3BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + + a0 = 1; + a1 = 2; + a2 = 3; + + final result = returnStruct3BytesHomogeneousUint8Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct3BytesInt2ByteAlignedLeaf = ffiTestFunctions.lookupFunction< + Struct3BytesInt2ByteAligned Function(Int16, Int8), + Struct3BytesInt2ByteAligned Function( + int, int)>("ReturnStruct3BytesInt2ByteAligned", isLeaf: true); + +/// Smaller than word size return value on all architectures. +/// With alignment rules taken into account size is 4 bytes. +void testReturnStruct3BytesInt2ByteAlignedLeaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct3BytesInt2ByteAlignedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct4BytesHomogeneousInt16Leaf = ffiTestFunctions.lookupFunction< + Struct4BytesHomogeneousInt16 Function(Int16, Int16), + Struct4BytesHomogeneousInt16 Function( + int, int)>("ReturnStruct4BytesHomogeneousInt16", isLeaf: true); + +/// Word size return value on 32 bit architectures.. +void testReturnStruct4BytesHomogeneousInt16Leaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct4BytesHomogeneousInt16Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct7BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct7BytesHomogeneousUint8 Function( + Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8), + Struct7BytesHomogeneousUint8 Function(int, int, int, int, int, int, + int)>("ReturnStruct7BytesHomogeneousUint8", isLeaf: true); + +/// Non-wordsize return value. +void testReturnStruct7BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + + final result = + returnStruct7BytesHomogeneousUint8Leaf(a0, a1, a2, a3, a4, a5, a6); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); +} + +final returnStruct7BytesInt4ByteAlignedLeaf = ffiTestFunctions.lookupFunction< + Struct7BytesInt4ByteAligned Function(Int32, Int16, Int8), + Struct7BytesInt4ByteAligned Function( + int, int, int)>("ReturnStruct7BytesInt4ByteAligned", isLeaf: true); + +/// Non-wordsize return value. +/// With alignment rules taken into account size is 8 bytes. +void testReturnStruct7BytesInt4ByteAlignedLeaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStruct7BytesInt4ByteAlignedLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct8BytesIntLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesInt Function(Int16, Int16, Int32), + Struct8BytesInt Function( + int, int, int)>("ReturnStruct8BytesInt", isLeaf: true); + +/// Return value in integer registers on many architectures. +void testReturnStruct8BytesIntLeaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStruct8BytesIntLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct8BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesHomogeneousFloat Function(Float, Float), + Struct8BytesHomogeneousFloat Function( + double, double)>("ReturnStruct8BytesHomogeneousFloat", isLeaf: true); + +/// Return value in FP registers on many architectures. +void testReturnStruct8BytesHomogeneousFloatLeaf() { + double a0; + double a1; + + a0 = -1.0; + a1 = 2.0; + + final result = returnStruct8BytesHomogeneousFloatLeaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); +} + +final returnStruct8BytesMixedLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesMixed Function(Float, Int16, Int16), + Struct8BytesMixed Function( + double, int, int)>("ReturnStruct8BytesMixed", isLeaf: true); + +/// Return value split over FP and integer register in x64. +void testReturnStruct8BytesMixedLeaf() { + double a0; + int a1; + int a2; + + a0 = -1.0; + a1 = 2; + a2 = -3; + + final result = returnStruct8BytesMixedLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct9BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct9BytesHomogeneousUint8 Function( + Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8), + Struct9BytesHomogeneousUint8 Function(int, int, int, int, int, int, int, + int, int)>("ReturnStruct9BytesHomogeneousUint8", isLeaf: true); + +/// The minimum alignment of this struct is only 1 byte based on its fields. +/// Test that the memory backing these structs is the right size and that +/// dart:ffi trampolines do not write outside this size. +void testReturnStruct9BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + a7 = 8; + a8 = 9; + + final result = returnStruct9BytesHomogeneousUint8Leaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); + Expect.equals(a7, result.a7); + Expect.equals(a8, result.a8); +} + +final returnStruct9BytesInt4Or8ByteAlignedLeaf = + ffiTestFunctions.lookupFunction< + Struct9BytesInt4Or8ByteAligned Function(Int64, Int8), + Struct9BytesInt4Or8ByteAligned Function( + int, int)>("ReturnStruct9BytesInt4Or8ByteAligned", isLeaf: true); + +/// Return value in two integer registers on x64. +/// With alignment rules taken into account size is 12 or 16 bytes. +void testReturnStruct9BytesInt4Or8ByteAlignedLeaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct9BytesInt4Or8ByteAlignedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct12BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct12BytesHomogeneousFloat Function(Float, Float, Float), + Struct12BytesHomogeneousFloat Function(double, double, + double)>("ReturnStruct12BytesHomogeneousFloat", isLeaf: true); + +/// Return value in FPU registers, but does not use all registers on arm hardfp +/// and arm64. +void testReturnStruct12BytesHomogeneousFloatLeaf() { + double a0; + double a1; + double a2; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + + final result = returnStruct12BytesHomogeneousFloatLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); +} + +final returnStruct16BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct16BytesHomogeneousFloat Function(Float, Float, Float, Float), + Struct16BytesHomogeneousFloat Function(double, double, double, + double)>("ReturnStruct16BytesHomogeneousFloat", isLeaf: true); + +/// Return value in FPU registers on arm hardfp and arm64. +void testReturnStruct16BytesHomogeneousFloatLeaf() { + double a0; + double a1; + double a2; + double a3; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + + final result = returnStruct16BytesHomogeneousFloatLeaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); +} + +final returnStruct16BytesMixedLeaf = ffiTestFunctions.lookupFunction< + Struct16BytesMixed Function(Double, Int64), + Struct16BytesMixed Function( + double, int)>("ReturnStruct16BytesMixed", isLeaf: true); + +/// Return value split over FP and integer register in x64. +void testReturnStruct16BytesMixedLeaf() { + double a0; + int a1; + + a0 = -1.0; + a1 = 2; + + final result = returnStruct16BytesMixedLeaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct16BytesMixed2Leaf = ffiTestFunctions.lookupFunction< + Struct16BytesMixed2 Function(Float, Float, Float, Int32), + Struct16BytesMixed2 Function(double, double, double, + int)>("ReturnStruct16BytesMixed2", isLeaf: true); + +/// Return value split over FP and integer register in x64. +/// The integer register contains half float half int. +void testReturnStruct16BytesMixed2Leaf() { + double a0; + double a1; + double a2; + int a3; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4; + + final result = returnStruct16BytesMixed2Leaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.equals(a3, result.a3); +} + +final returnStruct17BytesIntLeaf = ffiTestFunctions.lookupFunction< + Struct17BytesInt Function(Int64, Int64, Int8), + Struct17BytesInt Function( + int, int, int)>("ReturnStruct17BytesInt", isLeaf: true); + +/// Rerturn value returned in preallocated space passed by pointer on most ABIs. +/// Is non word size on purpose, to test that structs are rounded up to word size +/// on all ABIs. +void testReturnStruct17BytesIntLeaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStruct17BytesIntLeaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct19BytesHomogeneousUint8Leaf = ffiTestFunctions.lookupFunction< + Struct19BytesHomogeneousUint8 Function( + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8, + Uint8), + Struct19BytesHomogeneousUint8 Function( + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int)>("ReturnStruct19BytesHomogeneousUint8", isLeaf: true); + +/// The minimum alignment of this struct is only 1 byte based on its fields. +/// Test that the memory backing these structs is the right size and that +/// dart:ffi trampolines do not write outside this size. +void testReturnStruct19BytesHomogeneousUint8Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + int a9; + int a10; + int a11; + int a12; + int a13; + int a14; + int a15; + int a16; + int a17; + int a18; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + a7 = 8; + a8 = 9; + a9 = 10; + a10 = 11; + a11 = 12; + a12 = 13; + a13 = 14; + a14 = 15; + a15 = 16; + a16 = 17; + a17 = 18; + a18 = 19; + + final result = returnStruct19BytesHomogeneousUint8Leaf(a0, a1, a2, a3, a4, a5, + a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); + Expect.equals(a7, result.a7); + Expect.equals(a8, result.a8); + Expect.equals(a9, result.a9); + Expect.equals(a10, result.a10); + Expect.equals(a11, result.a11); + Expect.equals(a12, result.a12); + Expect.equals(a13, result.a13); + Expect.equals(a14, result.a14); + Expect.equals(a15, result.a15); + Expect.equals(a16, result.a16); + Expect.equals(a17, result.a17); + Expect.equals(a18, result.a18); +} + +final returnStruct20BytesHomogeneousInt32Leaf = ffiTestFunctions.lookupFunction< + Struct20BytesHomogeneousInt32 Function(Int32, Int32, Int32, Int32, Int32), + Struct20BytesHomogeneousInt32 Function(int, int, int, int, + int)>("ReturnStruct20BytesHomogeneousInt32", isLeaf: true); + +/// Return value too big to go in cpu registers on arm64. +void testReturnStruct20BytesHomogeneousInt32Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + + final result = returnStruct20BytesHomogeneousInt32Leaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); +} + +final returnStruct20BytesHomogeneousFloatLeaf = ffiTestFunctions.lookupFunction< + Struct20BytesHomogeneousFloat Function(Float, Float, Float, Float, Float), + Struct20BytesHomogeneousFloat Function(double, double, double, double, + double)>("ReturnStruct20BytesHomogeneousFloat", isLeaf: true); + +/// Return value too big to go in FPU registers on x64, arm hardfp and arm64. +void testReturnStruct20BytesHomogeneousFloatLeaf() { + double a0; + double a1; + double a2; + double a3; + double a4; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + a4 = -5.0; + + final result = returnStruct20BytesHomogeneousFloatLeaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); + Expect.approxEquals(a4, result.a4); +} + +final returnStruct32BytesHomogeneousDoubleLeaf = + ffiTestFunctions.lookupFunction< + Struct32BytesHomogeneousDouble Function(Double, Double, Double, Double), + Struct32BytesHomogeneousDouble Function(double, double, double, + double)>("ReturnStruct32BytesHomogeneousDouble", isLeaf: true); + +/// Return value in FPU registers on arm64. +void testReturnStruct32BytesHomogeneousDoubleLeaf() { + double a0; + double a1; + double a2; + double a3; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + + final result = returnStruct32BytesHomogeneousDoubleLeaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); +} + +final returnStruct40BytesHomogeneousDoubleLeaf = + ffiTestFunctions.lookupFunction< + Struct40BytesHomogeneousDouble Function( + Double, Double, Double, Double, Double), + Struct40BytesHomogeneousDouble Function(double, double, double, double, + double)>("ReturnStruct40BytesHomogeneousDouble", isLeaf: true); + +/// Return value too big to go in FPU registers on arm64. +void testReturnStruct40BytesHomogeneousDoubleLeaf() { + double a0; + double a1; + double a2; + double a3; + double a4; + + a0 = -1.0; + a1 = 2.0; + a2 = -3.0; + a3 = 4.0; + a4 = -5.0; + + final result = returnStruct40BytesHomogeneousDoubleLeaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); + Expect.approxEquals(a1, result.a1); + Expect.approxEquals(a2, result.a2); + Expect.approxEquals(a3, result.a3); + Expect.approxEquals(a4, result.a4); +} + +final returnStruct1024BytesHomogeneousUint64Leaf = + ffiTestFunctions.lookupFunction< + Struct1024BytesHomogeneousUint64 Function( + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64, + Uint64), + Struct1024BytesHomogeneousUint64 Function( + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int, + int)>("ReturnStruct1024BytesHomogeneousUint64", isLeaf: true); + +/// Test 1kb struct. +void testReturnStruct1024BytesHomogeneousUint64Leaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + int a8; + int a9; + int a10; + int a11; + int a12; + int a13; + int a14; + int a15; + int a16; + int a17; + int a18; + int a19; + int a20; + int a21; + int a22; + int a23; + int a24; + int a25; + int a26; + int a27; + int a28; + int a29; + int a30; + int a31; + int a32; + int a33; + int a34; + int a35; + int a36; + int a37; + int a38; + int a39; + int a40; + int a41; + int a42; + int a43; + int a44; + int a45; + int a46; + int a47; + int a48; + int a49; + int a50; + int a51; + int a52; + int a53; + int a54; + int a55; + int a56; + int a57; + int a58; + int a59; + int a60; + int a61; + int a62; + int a63; + int a64; + int a65; + int a66; + int a67; + int a68; + int a69; + int a70; + int a71; + int a72; + int a73; + int a74; + int a75; + int a76; + int a77; + int a78; + int a79; + int a80; + int a81; + int a82; + int a83; + int a84; + int a85; + int a86; + int a87; + int a88; + int a89; + int a90; + int a91; + int a92; + int a93; + int a94; + int a95; + int a96; + int a97; + int a98; + int a99; + int a100; + int a101; + int a102; + int a103; + int a104; + int a105; + int a106; + int a107; + int a108; + int a109; + int a110; + int a111; + int a112; + int a113; + int a114; + int a115; + int a116; + int a117; + int a118; + int a119; + int a120; + int a121; + int a122; + int a123; + int a124; + int a125; + int a126; + int a127; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + a5 = 6; + a6 = 7; + a7 = 8; + a8 = 9; + a9 = 10; + a10 = 11; + a11 = 12; + a12 = 13; + a13 = 14; + a14 = 15; + a15 = 16; + a16 = 17; + a17 = 18; + a18 = 19; + a19 = 20; + a20 = 21; + a21 = 22; + a22 = 23; + a23 = 24; + a24 = 25; + a25 = 26; + a26 = 27; + a27 = 28; + a28 = 29; + a29 = 30; + a30 = 31; + a31 = 32; + a32 = 33; + a33 = 34; + a34 = 35; + a35 = 36; + a36 = 37; + a37 = 38; + a38 = 39; + a39 = 40; + a40 = 41; + a41 = 42; + a42 = 43; + a43 = 44; + a44 = 45; + a45 = 46; + a46 = 47; + a47 = 48; + a48 = 49; + a49 = 50; + a50 = 51; + a51 = 52; + a52 = 53; + a53 = 54; + a54 = 55; + a55 = 56; + a56 = 57; + a57 = 58; + a58 = 59; + a59 = 60; + a60 = 61; + a61 = 62; + a62 = 63; + a63 = 64; + a64 = 65; + a65 = 66; + a66 = 67; + a67 = 68; + a68 = 69; + a69 = 70; + a70 = 71; + a71 = 72; + a72 = 73; + a73 = 74; + a74 = 75; + a75 = 76; + a76 = 77; + a77 = 78; + a78 = 79; + a79 = 80; + a80 = 81; + a81 = 82; + a82 = 83; + a83 = 84; + a84 = 85; + a85 = 86; + a86 = 87; + a87 = 88; + a88 = 89; + a89 = 90; + a90 = 91; + a91 = 92; + a92 = 93; + a93 = 94; + a94 = 95; + a95 = 96; + a96 = 97; + a97 = 98; + a98 = 99; + a99 = 100; + a100 = 101; + a101 = 102; + a102 = 103; + a103 = 104; + a104 = 105; + a105 = 106; + a106 = 107; + a107 = 108; + a108 = 109; + a109 = 110; + a110 = 111; + a111 = 112; + a112 = 113; + a113 = 114; + a114 = 115; + a115 = 116; + a116 = 117; + a117 = 118; + a118 = 119; + a119 = 120; + a120 = 121; + a121 = 122; + a122 = 123; + a123 = 124; + a124 = 125; + a125 = 126; + a126 = 127; + a127 = 128; + + final result = returnStruct1024BytesHomogeneousUint64Leaf( + a0, + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + a17, + a18, + a19, + a20, + a21, + a22, + a23, + a24, + a25, + a26, + a27, + a28, + a29, + a30, + a31, + a32, + a33, + a34, + a35, + a36, + a37, + a38, + a39, + a40, + a41, + a42, + a43, + a44, + a45, + a46, + a47, + a48, + a49, + a50, + a51, + a52, + a53, + a54, + a55, + a56, + a57, + a58, + a59, + a60, + a61, + a62, + a63, + a64, + a65, + a66, + a67, + a68, + a69, + a70, + a71, + a72, + a73, + a74, + a75, + a76, + a77, + a78, + a79, + a80, + a81, + a82, + a83, + a84, + a85, + a86, + a87, + a88, + a89, + a90, + a91, + a92, + a93, + a94, + a95, + a96, + a97, + a98, + a99, + a100, + a101, + a102, + a103, + a104, + a105, + a106, + a107, + a108, + a109, + a110, + a111, + a112, + a113, + a114, + a115, + a116, + a117, + a118, + a119, + a120, + a121, + a122, + a123, + a124, + a125, + a126, + a127); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); + Expect.equals(a5, result.a5); + Expect.equals(a6, result.a6); + Expect.equals(a7, result.a7); + Expect.equals(a8, result.a8); + Expect.equals(a9, result.a9); + Expect.equals(a10, result.a10); + Expect.equals(a11, result.a11); + Expect.equals(a12, result.a12); + Expect.equals(a13, result.a13); + Expect.equals(a14, result.a14); + Expect.equals(a15, result.a15); + Expect.equals(a16, result.a16); + Expect.equals(a17, result.a17); + Expect.equals(a18, result.a18); + Expect.equals(a19, result.a19); + Expect.equals(a20, result.a20); + Expect.equals(a21, result.a21); + Expect.equals(a22, result.a22); + Expect.equals(a23, result.a23); + Expect.equals(a24, result.a24); + Expect.equals(a25, result.a25); + Expect.equals(a26, result.a26); + Expect.equals(a27, result.a27); + Expect.equals(a28, result.a28); + Expect.equals(a29, result.a29); + Expect.equals(a30, result.a30); + Expect.equals(a31, result.a31); + Expect.equals(a32, result.a32); + Expect.equals(a33, result.a33); + Expect.equals(a34, result.a34); + Expect.equals(a35, result.a35); + Expect.equals(a36, result.a36); + Expect.equals(a37, result.a37); + Expect.equals(a38, result.a38); + Expect.equals(a39, result.a39); + Expect.equals(a40, result.a40); + Expect.equals(a41, result.a41); + Expect.equals(a42, result.a42); + Expect.equals(a43, result.a43); + Expect.equals(a44, result.a44); + Expect.equals(a45, result.a45); + Expect.equals(a46, result.a46); + Expect.equals(a47, result.a47); + Expect.equals(a48, result.a48); + Expect.equals(a49, result.a49); + Expect.equals(a50, result.a50); + Expect.equals(a51, result.a51); + Expect.equals(a52, result.a52); + Expect.equals(a53, result.a53); + Expect.equals(a54, result.a54); + Expect.equals(a55, result.a55); + Expect.equals(a56, result.a56); + Expect.equals(a57, result.a57); + Expect.equals(a58, result.a58); + Expect.equals(a59, result.a59); + Expect.equals(a60, result.a60); + Expect.equals(a61, result.a61); + Expect.equals(a62, result.a62); + Expect.equals(a63, result.a63); + Expect.equals(a64, result.a64); + Expect.equals(a65, result.a65); + Expect.equals(a66, result.a66); + Expect.equals(a67, result.a67); + Expect.equals(a68, result.a68); + Expect.equals(a69, result.a69); + Expect.equals(a70, result.a70); + Expect.equals(a71, result.a71); + Expect.equals(a72, result.a72); + Expect.equals(a73, result.a73); + Expect.equals(a74, result.a74); + Expect.equals(a75, result.a75); + Expect.equals(a76, result.a76); + Expect.equals(a77, result.a77); + Expect.equals(a78, result.a78); + Expect.equals(a79, result.a79); + Expect.equals(a80, result.a80); + Expect.equals(a81, result.a81); + Expect.equals(a82, result.a82); + Expect.equals(a83, result.a83); + Expect.equals(a84, result.a84); + Expect.equals(a85, result.a85); + Expect.equals(a86, result.a86); + Expect.equals(a87, result.a87); + Expect.equals(a88, result.a88); + Expect.equals(a89, result.a89); + Expect.equals(a90, result.a90); + Expect.equals(a91, result.a91); + Expect.equals(a92, result.a92); + Expect.equals(a93, result.a93); + Expect.equals(a94, result.a94); + Expect.equals(a95, result.a95); + Expect.equals(a96, result.a96); + Expect.equals(a97, result.a97); + Expect.equals(a98, result.a98); + Expect.equals(a99, result.a99); + Expect.equals(a100, result.a100); + Expect.equals(a101, result.a101); + Expect.equals(a102, result.a102); + Expect.equals(a103, result.a103); + Expect.equals(a104, result.a104); + Expect.equals(a105, result.a105); + Expect.equals(a106, result.a106); + Expect.equals(a107, result.a107); + Expect.equals(a108, result.a108); + Expect.equals(a109, result.a109); + Expect.equals(a110, result.a110); + Expect.equals(a111, result.a111); + Expect.equals(a112, result.a112); + Expect.equals(a113, result.a113); + Expect.equals(a114, result.a114); + Expect.equals(a115, result.a115); + Expect.equals(a116, result.a116); + Expect.equals(a117, result.a117); + Expect.equals(a118, result.a118); + Expect.equals(a119, result.a119); + Expect.equals(a120, result.a120); + Expect.equals(a121, result.a121); + Expect.equals(a122, result.a122); + Expect.equals(a123, result.a123); + Expect.equals(a124, result.a124); + Expect.equals(a125, result.a125); + Expect.equals(a126, result.a126); + Expect.equals(a127, result.a127); +} + +final returnStruct3BytesPackedIntLeaf = ffiTestFunctions.lookupFunction< + Struct3BytesPackedInt Function(Int8, Int16), + Struct3BytesPackedInt Function( + int, int)>("ReturnStruct3BytesPackedInt", isLeaf: true); + +/// Small struct with mis-aligned member. +void testReturnStruct3BytesPackedIntLeaf() { + int a0; + int a1; + + a0 = -1; + a1 = 2; + + final result = returnStruct3BytesPackedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); +} + +final returnStruct8BytesPackedIntLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesPackedInt Function(Uint8, Uint32, Uint8, Uint8, Uint8), + Struct8BytesPackedInt Function( + int, int, int, int, int)>("ReturnStruct8BytesPackedInt", isLeaf: true); + +/// Struct with mis-aligned member. +void testReturnStruct8BytesPackedIntLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + + a0 = 1; + a1 = 2; + a2 = 3; + a3 = 4; + a4 = 5; + + final result = returnStruct8BytesPackedIntLeaf(a0, a1, a2, a3, a4); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); + Expect.equals(a3, result.a3); + Expect.equals(a4, result.a4); +} + +final returnStruct9BytesPackedMixedLeaf = ffiTestFunctions.lookupFunction< + Struct9BytesPackedMixed Function(Uint8, Double), + Struct9BytesPackedMixed Function( + int, double)>("ReturnStruct9BytesPackedMixed", isLeaf: true); + +/// Struct with mis-aligned member. +/// Tests backfilling of CPU and FPU registers. +void testReturnStruct9BytesPackedMixedLeaf() { + int a0; + double a1; + + a0 = 1; + a1 = 2.0; + + final result = returnStruct9BytesPackedMixedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.approxEquals(a1, result.a1); +} + +final returnUnion4BytesMixedLeaf = ffiTestFunctions.lookupFunction< + Union4BytesMixed Function(Uint32), + Union4BytesMixed Function(int)>("ReturnUnion4BytesMixed", isLeaf: true); + +/// Returning a mixed integer/float union. +void testReturnUnion4BytesMixedLeaf() { + int a0; + + a0 = 1; + + final result = returnUnion4BytesMixedLeaf(a0); + + print("result = $result"); + + Expect.equals(a0, result.a0); +} + +final returnUnion8BytesNestedFloatLeaf = ffiTestFunctions.lookupFunction< + Union8BytesNestedFloat Function(Double), + Union8BytesNestedFloat Function( + double)>("ReturnUnion8BytesNestedFloat", isLeaf: true); + +/// Returning a floating point only union. +void testReturnUnion8BytesNestedFloatLeaf() { + double a0; + + a0 = -1.0; + + final result = returnUnion8BytesNestedFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0, result.a0); +} + +final returnUnion9BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Union9BytesNestedInt Function(Struct8BytesInt), + Union9BytesNestedInt Function( + Struct8BytesInt)>("ReturnUnion9BytesNestedInt", isLeaf: true); + +/// Returning a mixed-size union. +void testReturnUnion9BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct8BytesInt a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + + final result = returnUnion9BytesNestedIntLeaf(a0); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + + calloc.free(a0Pointer); +} + +final returnUnion16BytesNestedFloatLeaf = ffiTestFunctions.lookupFunction< + Union16BytesNestedFloat Function(Struct8BytesHomogeneousFloat), + Union16BytesNestedFloat Function(Struct8BytesHomogeneousFloat)>( + "ReturnUnion16BytesNestedFloat", + isLeaf: true); + +/// Returning union with homogenous floats. +void testReturnUnion16BytesNestedFloatLeaf() { + final a0Pointer = calloc(); + final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + + final result = returnUnion16BytesNestedFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0.a0); + Expect.approxEquals(a0.a1, result.a0.a1); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction< + Struct1ByteInt Function(Struct1ByteInt), + Struct1ByteInt Function( + Struct1ByteInt)>("ReturnStructArgumentStruct1ByteInt", isLeaf: true); + +/// Test that a struct passed in as argument can be returned. +/// Especially for ffi callbacks. +/// Struct is passed in int registers in most ABIs. +void testReturnStructArgumentStruct1ByteIntLeaf() { + final a0Pointer = calloc(); + final Struct1ByteInt a0 = a0Pointer.ref; + + a0.a0 = -1; + + final result = returnStructArgumentStruct1ByteIntLeaf(a0); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0); + + calloc.free(a0Pointer); +} + +final returnStructArgumentInt32x8Struct1ByteIntLeaf = + ffiTestFunctions + .lookupFunction< + Struct1ByteInt Function(Int32, Int32, Int32, Int32, Int32, Int32, + Int32, Int32, Struct1ByteInt), + Struct1ByteInt Function( + int, int, int, int, int, int, int, int, Struct1ByteInt)>( + "ReturnStructArgumentInt32x8Struct1ByteInt", + isLeaf: true); + +/// Test that a struct passed in as argument can be returned. +/// Especially for ffi callbacks. +/// Struct is passed on stack on all ABIs. +void testReturnStructArgumentInt32x8Struct1ByteIntLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + final a8Pointer = calloc(); + final Struct1ByteInt a8 = a8Pointer.ref; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + a5 = 6; + a6 = -7; + a7 = 8; + a8.a0 = -9; + + final result = returnStructArgumentInt32x8Struct1ByteIntLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.equals(a8.a0, result.a0); + + calloc.free(a8Pointer); +} + +final returnStructArgumentStruct8BytesHomogeneousFloatLeaf = + ffiTestFunctions.lookupFunction< + Struct8BytesHomogeneousFloat Function(Struct8BytesHomogeneousFloat), + Struct8BytesHomogeneousFloat Function( + Struct8BytesHomogeneousFloat)>( + "ReturnStructArgumentStruct8BytesHomogeneousFloat", + isLeaf: true); + +/// Test that a struct passed in as argument can be returned. +/// Especially for ffi callbacks. +/// Struct is passed in float registers in most ABIs. +void testReturnStructArgumentStruct8BytesHomogeneousFloatLeaf() { + final a0Pointer = calloc(); + final Struct8BytesHomogeneousFloat a0 = a0Pointer.ref; + + a0.a0 = -1.0; + a0.a1 = 2.0; + + final result = returnStructArgumentStruct8BytesHomogeneousFloatLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0); + Expect.approxEquals(a0.a1, result.a1); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStruct20BytesHomogeneousInt32Leaf = + ffiTestFunctions + .lookupFunction< + Struct20BytesHomogeneousInt32 Function( + Struct20BytesHomogeneousInt32), + Struct20BytesHomogeneousInt32 Function( + Struct20BytesHomogeneousInt32)>( + "ReturnStructArgumentStruct20BytesHomogeneousInt32", + isLeaf: true); + +/// On arm64, both argument and return value are passed in by pointer. +void testReturnStructArgumentStruct20BytesHomogeneousInt32Leaf() { + final a0Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a0 = a0Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a0.a3 = 4; + a0.a4 = -5; + + final result = returnStructArgumentStruct20BytesHomogeneousInt32Leaf(a0); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0); + Expect.equals(a0.a1, result.a1); + Expect.equals(a0.a2, result.a2); + Expect.equals(a0.a3, result.a3); + Expect.equals(a0.a4, result.a4); + + calloc.free(a0Pointer); +} + +final returnStructArgumentInt32x8Struct20BytesHomogeneouLeaf = + ffiTestFunctions.lookupFunction< + Struct20BytesHomogeneousInt32 Function(Int32, Int32, Int32, Int32, + Int32, Int32, Int32, Int32, Struct20BytesHomogeneousInt32), + Struct20BytesHomogeneousInt32 Function(int, int, int, int, int, int, + int, int, Struct20BytesHomogeneousInt32)>( + "ReturnStructArgumentInt32x8Struct20BytesHomogeneou", + isLeaf: true); + +/// On arm64, both argument and return value are passed in by pointer. +/// Ints exhaust registers, so that pointer is passed on stack. +void testReturnStructArgumentInt32x8Struct20BytesHomogeneouLeaf() { + int a0; + int a1; + int a2; + int a3; + int a4; + int a5; + int a6; + int a7; + final a8Pointer = calloc(); + final Struct20BytesHomogeneousInt32 a8 = a8Pointer.ref; + + a0 = -1; + a1 = 2; + a2 = -3; + a3 = 4; + a4 = -5; + a5 = 6; + a6 = -7; + a7 = 8; + a8.a0 = -9; + a8.a1 = 10; + a8.a2 = -11; + a8.a3 = 12; + a8.a4 = -13; + + final result = returnStructArgumentInt32x8Struct20BytesHomogeneouLeaf( + a0, a1, a2, a3, a4, a5, a6, a7, a8); + + print("result = $result"); + + Expect.equals(a8.a0, result.a0); + Expect.equals(a8.a1, result.a1); + Expect.equals(a8.a2, result.a2); + Expect.equals(a8.a3, result.a3); + Expect.equals(a8.a4, result.a4); + + calloc.free(a8Pointer); +} + +final returnStructArgumentStruct8BytesInlineArrayIntLeaf = + ffiTestFunctions.lookupFunction< + Struct8BytesInlineArrayInt Function(Struct8BytesInlineArrayInt), + Struct8BytesInlineArrayInt Function(Struct8BytesInlineArrayInt)>( + "ReturnStructArgumentStruct8BytesInlineArrayInt", + isLeaf: true); + +/// Test returning struct with inline array. +void testReturnStructArgumentStruct8BytesInlineArrayIntLeaf() { + final a0Pointer = calloc(); + final Struct8BytesInlineArrayInt a0 = a0Pointer.ref; + + a0.a0[0] = 1; + a0.a0[1] = 2; + a0.a0[2] = 3; + a0.a0[3] = 4; + a0.a0[4] = 5; + a0.a0[5] = 6; + a0.a0[6] = 7; + a0.a0[7] = 8; + + final result = returnStructArgumentStruct8BytesInlineArrayIntLeaf(a0); + + print("result = $result"); + + for (int i = 0; i < 8; i++) { + Expect.equals(a0.a0[i], result.a0[i]); + } + + calloc.free(a0Pointer); +} + +final returnStructArgumentStructStruct16BytesHomogeneousLeaf = + ffiTestFunctions.lookupFunction< + StructStruct16BytesHomogeneousFloat2 Function( + StructStruct16BytesHomogeneousFloat2), + StructStruct16BytesHomogeneousFloat2 Function( + StructStruct16BytesHomogeneousFloat2)>( + "ReturnStructArgumentStructStruct16BytesHomogeneous", + isLeaf: true); + +/// Return value in FPU registers on arm hardfp and arm64. +void testReturnStructArgumentStructStruct16BytesHomogeneousLeaf() { + final a0Pointer = calloc(); + final StructStruct16BytesHomogeneousFloat2 a0 = a0Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + + final result = returnStructArgumentStructStruct16BytesHomogeneousLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0.a0, result.a0.a0); + for (int i = 0; i < 2; i++) { + Expect.approxEquals(a0.a1[i].a0, result.a1[i].a0); + } + Expect.approxEquals(a0.a2, result.a2); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStructStruct32BytesHomogeneousLeaf = + ffiTestFunctions.lookupFunction< + StructStruct32BytesHomogeneousDouble2 Function( + StructStruct32BytesHomogeneousDouble2), + StructStruct32BytesHomogeneousDouble2 Function( + StructStruct32BytesHomogeneousDouble2)>( + "ReturnStructArgumentStructStruct32BytesHomogeneous", + isLeaf: true); + +/// Return value in FPU registers on arm64. +void testReturnStructArgumentStructStruct32BytesHomogeneousLeaf() { + final a0Pointer = calloc(); + final StructStruct32BytesHomogeneousDouble2 a0 = a0Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[1].a0 = -3.0; + a0.a2 = 4.0; + + final result = returnStructArgumentStructStruct32BytesHomogeneousLeaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0.a0, result.a0.a0); + for (int i = 0; i < 2; i++) { + Expect.approxEquals(a0.a1[i].a0, result.a1[i].a0); + } + Expect.approxEquals(a0.a2, result.a2); + + calloc.free(a0Pointer); +} + +final returnStructArgumentStructStruct16BytesMixed3Leaf = + ffiTestFunctions.lookupFunction< + StructStruct16BytesMixed3 Function(StructStruct16BytesMixed3), + StructStruct16BytesMixed3 Function(StructStruct16BytesMixed3)>( + "ReturnStructArgumentStructStruct16BytesMixed3", + isLeaf: true); + +/// On x64 Linux, return value is split over FP and int registers. +void testReturnStructArgumentStructStruct16BytesMixed3Leaf() { + final a0Pointer = calloc(); + final StructStruct16BytesMixed3 a0 = a0Pointer.ref; + + a0.a0.a0 = -1.0; + a0.a1[0].a0 = 2.0; + a0.a1[0].a1 = -3; + a0.a1[0].a2 = 4; + a0.a2[0] = -5; + a0.a2[1] = 6; + + final result = returnStructArgumentStructStruct16BytesMixed3Leaf(a0); + + print("result = $result"); + + Expect.approxEquals(a0.a0.a0, result.a0.a0); + for (int i = 0; i < 1; i++) { + Expect.approxEquals(a0.a1[i].a0, result.a1[i].a0); + Expect.equals(a0.a1[i].a1, result.a1[i].a1); + Expect.equals(a0.a1[i].a2, result.a1[i].a2); + } + for (int i = 0; i < 2; i++) { + Expect.equals(a0.a2[i], result.a2[i]); + } + + calloc.free(a0Pointer); +} + +final returnStructAlignmentInt16Leaf = ffiTestFunctions.lookupFunction< + StructAlignmentInt16 Function(Int8, Int16, Int8), + StructAlignmentInt16 Function( + int, int, int)>("ReturnStructAlignmentInt16", isLeaf: true); + +/// Test alignment and padding of 16 byte int within struct. +void testReturnStructAlignmentInt16Leaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStructAlignmentInt16Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStructAlignmentInt32Leaf = ffiTestFunctions.lookupFunction< + StructAlignmentInt32 Function(Int8, Int32, Int8), + StructAlignmentInt32 Function( + int, int, int)>("ReturnStructAlignmentInt32", isLeaf: true); + +/// Test alignment and padding of 32 byte int within struct. +void testReturnStructAlignmentInt32Leaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStructAlignmentInt32Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStructAlignmentInt64Leaf = ffiTestFunctions.lookupFunction< + StructAlignmentInt64 Function(Int8, Int64, Int8), + StructAlignmentInt64 Function( + int, int, int)>("ReturnStructAlignmentInt64", isLeaf: true); + +/// Test alignment and padding of 64 byte int within struct. +void testReturnStructAlignmentInt64Leaf() { + int a0; + int a1; + int a2; + + a0 = -1; + a1 = 2; + a2 = -3; + + final result = returnStructAlignmentInt64Leaf(a0, a1, a2); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1, result.a1); + Expect.equals(a2, result.a2); +} + +final returnStruct8BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedInt Function( + Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16), + Struct8BytesNestedInt Function( + Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16)>( + "ReturnStruct8BytesNestedInt", + isLeaf: true); + +/// Simple nested struct. +void testReturnStruct8BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3; + a1.a1 = 4; + + final result = returnStruct8BytesNestedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct8BytesNestedFloatLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedFloat Function(Struct4BytesFloat, Struct4BytesFloat), + Struct8BytesNestedFloat Function(Struct4BytesFloat, + Struct4BytesFloat)>("ReturnStruct8BytesNestedFloat", isLeaf: true); + +/// Simple nested struct with floats. +void testReturnStruct8BytesNestedFloatLeaf() { + final a0Pointer = calloc(); + final Struct4BytesFloat a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesFloat a1 = a1Pointer.ref; + + a0.a0 = -1.0; + a1.a0 = 2.0; + + final result = returnStruct8BytesNestedFloatLeaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0.a0); + Expect.approxEquals(a1.a0, result.a1.a0); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct8BytesNestedFloat2Leaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedFloat2 Function(Struct4BytesFloat, Float), + Struct8BytesNestedFloat2 Function(Struct4BytesFloat, + double)>("ReturnStruct8BytesNestedFloat2", isLeaf: true); + +/// The nesting is irregular, testing homogenous float rules on arm and arm64, +/// and the fpu register usage on x64. +void testReturnStruct8BytesNestedFloat2Leaf() { + final a0Pointer = calloc(); + final Struct4BytesFloat a0 = a0Pointer.ref; + double a1; + + a0.a0 = -1.0; + a1 = 2.0; + + final result = returnStruct8BytesNestedFloat2Leaf(a0, a1); + + print("result = $result"); + + Expect.approxEquals(a0.a0, result.a0.a0); + Expect.approxEquals(a1, result.a1); + + calloc.free(a0Pointer); +} + +final returnStruct8BytesNestedMixedLeaf = ffiTestFunctions.lookupFunction< + Struct8BytesNestedMixed Function( + Struct4BytesHomogeneousInt16, Struct4BytesFloat), + Struct8BytesNestedMixed Function(Struct4BytesHomogeneousInt16, + Struct4BytesFloat)>("ReturnStruct8BytesNestedMixed", isLeaf: true); + +/// Simple nested struct with mixed members. +void testReturnStruct8BytesNestedMixedLeaf() { + final a0Pointer = calloc(); + final Struct4BytesHomogeneousInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct4BytesFloat a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a1.a0 = -3.0; + + final result = returnStruct8BytesNestedMixedLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.approxEquals(a1.a0, result.a1.a0); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct16BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Struct16BytesNestedInt Function( + Struct8BytesNestedInt, Struct8BytesNestedInt), + Struct16BytesNestedInt Function(Struct8BytesNestedInt, + Struct8BytesNestedInt)>("ReturnStruct16BytesNestedInt", isLeaf: true); + +/// Deeper nested struct to test recursive member access. +void testReturnStruct16BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct8BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct8BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0 = -1; + a0.a0.a1 = 2; + a0.a1.a0 = -3; + a0.a1.a1 = 4; + a1.a0.a0 = -5; + a1.a0.a1 = 6; + a1.a1.a0 = -7; + a1.a1.a1 = 8; + + final result = returnStruct16BytesNestedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0.a0, result.a0.a0.a0); + Expect.equals(a0.a0.a1, result.a0.a0.a1); + Expect.equals(a0.a1.a0, result.a0.a1.a0); + Expect.equals(a0.a1.a1, result.a0.a1.a1); + Expect.equals(a1.a0.a0, result.a1.a0.a0); + Expect.equals(a1.a0.a1, result.a1.a0.a1); + Expect.equals(a1.a1.a0, result.a1.a1.a0); + Expect.equals(a1.a1.a1, result.a1.a1.a1); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStruct32BytesNestedIntLeaf = ffiTestFunctions.lookupFunction< + Struct32BytesNestedInt Function( + Struct16BytesNestedInt, Struct16BytesNestedInt), + Struct32BytesNestedInt Function(Struct16BytesNestedInt, + Struct16BytesNestedInt)>("ReturnStruct32BytesNestedInt", isLeaf: true); + +/// Even deeper nested struct to test recursive member access. +void testReturnStruct32BytesNestedIntLeaf() { + final a0Pointer = calloc(); + final Struct16BytesNestedInt a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final Struct16BytesNestedInt a1 = a1Pointer.ref; + + a0.a0.a0.a0 = -1; + a0.a0.a0.a1 = 2; + a0.a0.a1.a0 = -3; + a0.a0.a1.a1 = 4; + a0.a1.a0.a0 = -5; + a0.a1.a0.a1 = 6; + a0.a1.a1.a0 = -7; + a0.a1.a1.a1 = 8; + a1.a0.a0.a0 = -9; + a1.a0.a0.a1 = 10; + a1.a0.a1.a0 = -11; + a1.a0.a1.a1 = 12; + a1.a1.a0.a0 = -13; + a1.a1.a0.a1 = 14; + a1.a1.a1.a0 = -15; + a1.a1.a1.a1 = 16; + + final result = returnStruct32BytesNestedIntLeaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0.a0.a0, result.a0.a0.a0.a0); + Expect.equals(a0.a0.a0.a1, result.a0.a0.a0.a1); + Expect.equals(a0.a0.a1.a0, result.a0.a0.a1.a0); + Expect.equals(a0.a0.a1.a1, result.a0.a0.a1.a1); + Expect.equals(a0.a1.a0.a0, result.a0.a1.a0.a0); + Expect.equals(a0.a1.a0.a1, result.a0.a1.a0.a1); + Expect.equals(a0.a1.a1.a0, result.a0.a1.a1.a0); + Expect.equals(a0.a1.a1.a1, result.a0.a1.a1.a1); + Expect.equals(a1.a0.a0.a0, result.a1.a0.a0.a0); + Expect.equals(a1.a0.a0.a1, result.a1.a0.a0.a1); + Expect.equals(a1.a0.a1.a0, result.a1.a0.a1.a0); + Expect.equals(a1.a0.a1.a1, result.a1.a0.a1.a1); + Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0); + Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1); + Expect.equals(a1.a1.a1.a0, result.a1.a1.a1.a0); + Expect.equals(a1.a1.a1.a1, result.a1.a1.a1.a1); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIntStructAlignmentInt16Leaf = + ffiTestFunctions.lookupFunction< + StructNestedIntStructAlignmentInt16 Function( + StructAlignmentInt16, StructAlignmentInt16), + StructNestedIntStructAlignmentInt16 Function( + StructAlignmentInt16, StructAlignmentInt16)>( + "ReturnStructNestedIntStructAlignmentInt16", + isLeaf: true); + +/// Test alignment and padding of nested struct with 16 byte int. +void testReturnStructNestedIntStructAlignmentInt16Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt16 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructAlignmentInt16 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + + final result = returnStructNestedIntStructAlignmentInt16Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + Expect.equals(a1.a2, result.a1.a2); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIntStructAlignmentInt32Leaf = + ffiTestFunctions.lookupFunction< + StructNestedIntStructAlignmentInt32 Function( + StructAlignmentInt32, StructAlignmentInt32), + StructNestedIntStructAlignmentInt32 Function( + StructAlignmentInt32, StructAlignmentInt32)>( + "ReturnStructNestedIntStructAlignmentInt32", + isLeaf: true); + +/// Test alignment and padding of nested struct with 32 byte int. +void testReturnStructNestedIntStructAlignmentInt32Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt32 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructAlignmentInt32 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + + final result = returnStructNestedIntStructAlignmentInt32Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + Expect.equals(a1.a2, result.a1.a2); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIntStructAlignmentInt64Leaf = + ffiTestFunctions.lookupFunction< + StructNestedIntStructAlignmentInt64 Function( + StructAlignmentInt64, StructAlignmentInt64), + StructNestedIntStructAlignmentInt64 Function( + StructAlignmentInt64, StructAlignmentInt64)>( + "ReturnStructNestedIntStructAlignmentInt64", + isLeaf: true); + +/// Test alignment and padding of nested struct with 64 byte int. +void testReturnStructNestedIntStructAlignmentInt64Leaf() { + final a0Pointer = calloc(); + final StructAlignmentInt64 a0 = a0Pointer.ref; + final a1Pointer = calloc(); + final StructAlignmentInt64 a1 = a1Pointer.ref; + + a0.a0 = -1; + a0.a1 = 2; + a0.a2 = -3; + a1.a0 = 4; + a1.a1 = -5; + a1.a2 = 6; + + final result = returnStructNestedIntStructAlignmentInt64Leaf(a0, a1); + + print("result = $result"); + + Expect.equals(a0.a0, result.a0.a0); + Expect.equals(a0.a1, result.a0.a1); + Expect.equals(a0.a2, result.a0.a2); + Expect.equals(a1.a0, result.a1.a0); + Expect.equals(a1.a1, result.a1.a1); + Expect.equals(a1.a2, result.a1.a2); + + calloc.free(a0Pointer); + calloc.free(a1Pointer); +} + +final returnStructNestedIrregularEvenBiggerLeaf = + ffiTestFunctions.lookupFunction< + StructNestedIrregularEvenBigger Function(Uint64, + StructNestedIrregularBigger, StructNestedIrregularBigger, Double), + StructNestedIrregularEvenBigger Function( + int, + StructNestedIrregularBigger, + StructNestedIrregularBigger, + double)>("ReturnStructNestedIrregularEvenBigger", isLeaf: true); + +/// Return big irregular struct as smoke test. +void testReturnStructNestedIrregularEvenBiggerLeaf() { + int a0; + final a1Pointer = calloc(); + final StructNestedIrregularBigger a1 = a1Pointer.ref; + final a2Pointer = calloc(); + final StructNestedIrregularBigger a2 = a2Pointer.ref; + double a3; + + a0 = 1; + a1.a0.a0 = 2; + a1.a0.a1.a0.a0 = -3; + a1.a0.a1.a0.a1 = 4; + a1.a0.a1.a1.a0 = -5.0; + a1.a0.a2 = 6; + a1.a0.a3.a0.a0 = -7.0; + a1.a0.a3.a1 = 8.0; + a1.a0.a4 = 9; + a1.a0.a5.a0.a0 = 10.0; + a1.a0.a5.a1.a0 = -11.0; + a1.a0.a6 = 12; + a1.a1.a0.a0 = -13; + a1.a1.a0.a1 = 14; + a1.a1.a1.a0 = -15.0; + a1.a2 = 16.0; + a1.a3 = -17.0; + a2.a0.a0 = 18; + a2.a0.a1.a0.a0 = -19; + a2.a0.a1.a0.a1 = 20; + a2.a0.a1.a1.a0 = -21.0; + a2.a0.a2 = 22; + a2.a0.a3.a0.a0 = -23.0; + a2.a0.a3.a1 = 24.0; + a2.a0.a4 = 25; + a2.a0.a5.a0.a0 = 26.0; + a2.a0.a5.a1.a0 = -27.0; + a2.a0.a6 = 28; + a2.a1.a0.a0 = -29; + a2.a1.a0.a1 = 30; + a2.a1.a1.a0 = -31.0; + a2.a2 = 32.0; + a2.a3 = -33.0; + a3 = 34.0; + + final result = returnStructNestedIrregularEvenBiggerLeaf(a0, a1, a2, a3); + + print("result = $result"); + + Expect.equals(a0, result.a0); + Expect.equals(a1.a0.a0, result.a1.a0.a0); + Expect.equals(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0); + Expect.equals(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1); + Expect.approxEquals(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0); + Expect.equals(a1.a0.a2, result.a1.a0.a2); + Expect.approxEquals(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0); + Expect.approxEquals(a1.a0.a3.a1, result.a1.a0.a3.a1); + Expect.equals(a1.a0.a4, result.a1.a0.a4); + Expect.approxEquals(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0); + Expect.approxEquals(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0); + Expect.equals(a1.a0.a6, result.a1.a0.a6); + Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0); + Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1); + Expect.approxEquals(a1.a1.a1.a0, result.a1.a1.a1.a0); + Expect.approxEquals(a1.a2, result.a1.a2); + Expect.approxEquals(a1.a3, result.a1.a3); + Expect.equals(a2.a0.a0, result.a2.a0.a0); + Expect.equals(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0); + Expect.equals(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1); + Expect.approxEquals(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0); + Expect.equals(a2.a0.a2, result.a2.a0.a2); + Expect.approxEquals(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0); + Expect.approxEquals(a2.a0.a3.a1, result.a2.a0.a3.a1); + Expect.equals(a2.a0.a4, result.a2.a0.a4); + Expect.approxEquals(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0); + Expect.approxEquals(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0); + Expect.equals(a2.a0.a6, result.a2.a0.a6); + Expect.equals(a2.a1.a0.a0, result.a2.a1.a0.a0); + Expect.equals(a2.a1.a0.a1, result.a2.a1.a0.a1); + Expect.approxEquals(a2.a1.a1.a0, result.a2.a1.a1.a0); + Expect.approxEquals(a2.a2, result.a2.a2); + Expect.approxEquals(a2.a3, result.a2.a3); + Expect.approxEquals(a3, result.a3); + + calloc.free(a1Pointer); + calloc.free(a2Pointer); +} diff --git a/tests/ffi_2/function_structs_test.dart b/tests/ffi_2/function_structs_test.dart index c2594356dab..86f49dea189 100644 --- a/tests/ffi_2/function_structs_test.dart +++ b/tests/ffi_2/function_structs_test.dart @@ -21,18 +21,21 @@ import 'very_large_struct.dart'; typedef NativeCoordinateOp = Pointer Function(Pointer); void main() { - testFunctionWithStruct(); - testFunctionWithStructArray(); - testFunctionWithVeryLargeStruct(); + for (final isLeaf in [false, true]) { + testFunctionWithStruct(isLeaf: isLeaf); + testFunctionWithStructArray(isLeaf: isLeaf); + testFunctionWithVeryLargeStruct(isLeaf: isLeaf); + } } DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); /// pass a struct to a c function and get a struct as return value -void testFunctionWithStruct() { +void testFunctionWithStruct({bool isLeaf: false}) { Pointer> p1 = ffiTestFunctions.lookup("TransposeCoordinate"); - NativeCoordinateOp f1 = p1.asFunction(); + NativeCoordinateOp f1 = + (isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false)); final c1 = calloc() ..ref.x = 10.0 @@ -56,10 +59,11 @@ void testFunctionWithStruct() { } /// pass an array of structs to a c funtion -void testFunctionWithStructArray() { +void testFunctionWithStructArray({bool isLeaf: false}) { Pointer> p1 = ffiTestFunctions.lookup("CoordinateElemAt1"); - NativeCoordinateOp f1 = p1.asFunction(); + NativeCoordinateOp f1 = + (isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false)); final coordinateArray = calloc(3); Coordinate c1 = coordinateArray[0]; @@ -85,10 +89,11 @@ void testFunctionWithStructArray() { typedef VeryLargeStructSum = int Function(Pointer); typedef NativeVeryLargeStructSum = Int64 Function(Pointer); -void testFunctionWithVeryLargeStruct() { +void testFunctionWithVeryLargeStruct({bool isLeaf: false}) { Pointer> p1 = ffiTestFunctions.lookup("SumVeryLargeStruct"); - VeryLargeStructSum f = p1.asFunction(); + VeryLargeStructSum f = + (isLeaf ? p1.asFunction(isLeaf: true) : p1.asFunction(isLeaf: false)); final vlsArray = calloc(2); VeryLargeStruct vls1 = vlsArray[0]; diff --git a/tests/ffi_2/generator/structs_by_value_tests_generator.dart b/tests/ffi_2/generator/structs_by_value_tests_generator.dart index bbd315819ff..39d8f7f19c5 100644 --- a/tests/ffi_2/generator/structs_by_value_tests_generator.dart +++ b/tests/ffi_2/generator/structs_by_value_tests_generator.dart @@ -539,7 +539,7 @@ extension on CompositeType { } extension on FunctionType { - String get dartCallCode { + String dartCallCode({bool isLeaf: false}) { final a = ArgumentValueAssigner(); final assignValues = arguments.assignValueStatements(a); final argumentFrees = arguments.dartFreeStatements(); @@ -563,17 +563,19 @@ extension on FunctionType { break; } + final namePostfix = isLeaf ? "Leaf" : ""; return """ - final $dartName = - ffiTestFunctions.lookupFunction<$dartCType, $dartType>("$cName"); + final ${dartName}$namePostfix = + ffiTestFunctions.lookupFunction<$dartCType, $dartType>( + "$cName"${isLeaf ? ", isLeaf:true" : ""}); ${reason.makeDartDocComment()} - void $dartTestName() { + void ${dartTestName}$namePostfix() { ${arguments.dartAllocateStatements()} ${assignValues} - final result = $dartName($argumentNames); + final result = ${dartName}$namePostfix($argumentNames); print("result = \$result"); @@ -888,11 +890,13 @@ void writeDartCallTest() { void main() { for (int i = 0; i < 10; ++i) { ${functions.map((e) => "${e.dartTestName}();").join("\n")} + ${functions.map((e) => "${e.dartTestName}Leaf();").join("\n")} } } """); buffer.writeAll(compounds.map((e) => e.dartClass(nnbd))); - buffer.writeAll(functions.map((e) => e.dartCallCode)); + buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: false))); + buffer.writeAll(functions.map((e) => e.dartCallCode(isLeaf: true))); final path = callTestPath(nnbd); File(path).writeAsStringSync(buffer.toString()); diff --git a/tests/ffi_2/vmspecific_leaf_call_test.dart b/tests/ffi_2/vmspecific_leaf_call_test.dart new file mode 100644 index 00000000000..f4c0445fa71 --- /dev/null +++ b/tests/ffi_2/vmspecific_leaf_call_test.dart @@ -0,0 +1,60 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// SharedObjects=ffi_test_functions + +import 'dart:ffi'; +import 'dart:io'; + +import 'package:expect/expect.dart'; + +import 'dylib_utils.dart'; +import 'ffi_test_helpers.dart'; +import 'callback_tests_utils.dart'; + +DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); + +testLeafCall() { + // Regular calls should transition generated -> native. + final isThreadInGenerated = ffiTestFunctions.lookupFunction< + Int8 Function(), int Function()>("IsThreadInGenerated"); + Expect.equals(0, isThreadInGenerated()); + // Leaf calls should remain in generated state. + final isThreadInGeneratedLeaf = ffiTestFunctions.lookupFunction< + Int8 Function(), int Function()>("IsThreadInGenerated", isLeaf: true); + Expect.equals(1, isThreadInGeneratedLeaf()); +} + +testLeafCallApi() { + // Note: This will only crash as expected in debug build mode. In other modes + // it's effectively skip. + final f = ffiTestFunctions.lookupFunction< + Void Function(), void Function()>("TestLeafCallApi", isLeaf: true); + // Calling Dart_.. API is unsafe from leaf calls since we explicitly haven't + // made the generated -> native transition. + f(); +} + +void nop() {} + +testCallbackLeaf() { + // This should crash with "expected: T->IsAtSafepoint()", since it's unsafe to + // do callbacks from leaf calls (otherwise they wouldn't be leaf calls). + // Note: This will only crash as expected in debug build mode. In other modes + // it's effectively skip. + CallbackTest("CallbackLeaf", Pointer.fromFunction(nop), + isLeaf:true).run(); +} + +main() { + testLeafCall(); //# 01: ok + // These tests terminate the process after successful completion, so we have + // to run them separately. + // + // Since they use signal handlers they only run on Linux. + if (Platform.isLinux && !const bool.fromEnvironment("dart.vm.product")) { + testLeafCallApi(); //# 02: ok + testCallbackLeaf(); //# 03: ok + } +} diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart index ae19a0d75ea..112455c40a3 100644 --- a/tests/ffi_2/vmspecific_static_checks_test.dart +++ b/tests/ffi_2/vmspecific_static_checks_test.dart @@ -63,6 +63,12 @@ void main() { testSizeOfHandle(); testElementAtGeneric(); testElementAtNativeType(); + testLookupFunctionIsLeafMustBeConst(); + testAsFunctionIsLeafMustBeConst(); + testLookupFunctionTakesHandle(); + testAsFunctionTakesHandle(); + testLookupFunctionReturnsHandle(); + testAsFunctionReturnsHandle(); } typedef Int8UnOp = Int8 Function(Int8); @@ -705,6 +711,44 @@ class TestStruct1405 extends Struct { Pointer notEmpty; } +void testLookupFunctionIsLeafMustBeConst() { + bool notAConst = false; + DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library"); + l.lookupFunction("timesFour", isLeaf:notAConst); //# 1500: compile-time error +} + +void testAsFunctionIsLeafMustBeConst() { + bool notAConst = false; + Pointer> p = Pointer.fromAddress(1337); + IntUnOp f = p.asFunction(isLeaf:notAConst); //# 1501: compile-time error +} + +typedef NativeTakesHandle = Void Function(Handle); +typedef TakesHandle = void Function(Object); + +void testLookupFunctionTakesHandle() { + DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library"); + l.lookupFunction("takesHandle", isLeaf:true); //# 1502: compile-time error +} + +void testAsFunctionTakesHandle() { + Pointer> p = Pointer.fromAddress(1337); //# 1503: compile-time error + TakesHandle f = p.asFunction(isLeaf:true); //# 1503: compile-time error +} + +typedef NativeReturnsHandle = Handle Function(); +typedef ReturnsHandle = Object Function(); + +void testLookupFunctionReturnsHandle() { + DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library"); + l.lookupFunction("returnsHandle", isLeaf:true); //# 1504: compile-time error +} + +void testAsFunctionReturnsHandle() { + Pointer> p = Pointer.fromAddress(1337); //# 1505: compile-time error + ReturnsHandle f = p.asFunction(isLeaf:true); //# 1505: compile-time error +} + @Packed(1) class TestStruct1600 extends Struct { Pointer notEmpty;