From 7a4d1c118f3ede3b6301ad3c33e40edc249005cd Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Wed, 10 Feb 2021 11:01:18 +0000 Subject: [PATCH] [vm/ffi] Support inline arrays in `Struct`s backend The various ABIs lay out structs with inline arrays the same way as they do with only nested structs and primitive values. Most notably, homogenous structs (arm and arm64) and structs spread over a CPU and FPU register in x64 Linux/MacOS will even be laid out this way if irregular size nested arrays are involved. These cases are covered in the unit tests. This CL introduces the ByteRange to ease the ContainsOnlyFloats calculation for x64. Bug: https://github.com/dart-lang/sdk/issues/35763 tools/build.py run_ffi_unit_tests && tools/test.py ffi_unit TEST=runtime/vm/compiler/ffi/native_calling_convention_test.cc TEST=runtime/vm/compiler/ffi/native_type_test.cc tools/test.py ffi ffi_2 TEST=tests/ffi(_2)/(.*)by_value_(*.)_test.dart Change-Id: I4bbcbffd47eb8901a87db64e62aa5cbe67d03e18 Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-mac-debug-x64-try,vm-ffi-android-debug-arm64-try,vm-ffi-android-debug-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/183682 Commit-Queue: Daco Harkes Reviewed-by: Clement Skau Reviewed-by: Martin Kustermann --- .../compiler/ffi/native_calling_convention.cc | 10 +- .../ffi/native_calling_convention_test.cc | 103 +++++++++++++ runtime/vm/compiler/ffi/native_type.cc | 138 ++++++++++++++---- runtime/vm/compiler/ffi/native_type.h | 74 ++++++++-- runtime/vm/compiler/ffi/native_type_test.cc | 49 ++++++- runtime/vm/compiler/ffi/range.h | 103 +++++++++++++ .../struct16bytesMixedx10_3/x64_ios.expect | 13 ++ .../struct16bytesMixedx10_3/x64_linux.expect | 13 ++ .../struct16bytesMixedx10_3/x64_macos.expect | 13 ++ .../struct16bytesMixedx10_3/x64_win.expect | 13 ++ .../struct_floatarray/arm64_android.expect | 3 + .../struct_floatarray/arm64_ios.expect | 3 + .../struct_floatarray/arm64_linux.expect | 3 + .../struct_floatarray/arm64_macos.expect | 3 + .../struct_floatarray/arm_android.expect | 3 + .../struct_floatarray/arm_ios.expect | 3 + .../struct_floatarray/arm_linux.expect | 3 + .../struct_floatarray/ia32_android.expect | 3 + .../struct_floatarray/ia32_linux.expect | 3 + .../struct_floatarray/ia32_win.expect | 3 + .../struct_floatarray/x64_ios.expect | 3 + .../struct_floatarray/x64_linux.expect | 3 + .../struct_floatarray/x64_macos.expect | 3 + .../struct_floatarray/x64_win.expect | 3 + .../struct_int8array/arm64_android.expect | 3 + .../struct_int8array/arm64_ios.expect | 3 + .../struct_int8array/arm64_linux.expect | 3 + .../struct_int8array/arm64_macos.expect | 3 + .../struct_int8array/arm_android.expect | 3 + .../struct_int8array/arm_ios.expect | 3 + .../struct_int8array/arm_linux.expect | 3 + .../struct_int8array/ia32_android.expect | 3 + .../struct_int8array/ia32_linux.expect | 3 + .../struct_int8array/ia32_win.expect | 3 + .../struct_int8array/x64_ios.expect | 3 + .../struct_int8array/x64_linux.expect | 3 + .../struct_int8array/x64_macos.expect | 3 + .../struct_int8array/x64_win.expect | 3 + 38 files changed, 567 insertions(+), 46 deletions(-) create mode 100644 runtime/vm/compiler/ffi/range.h create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_macos.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_win.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_android.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_macos.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_android.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_android.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_win.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_macos.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_win.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_android.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_macos.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_android.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_android.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_win.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_ios.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_linux.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_macos.expect create mode 100644 runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_win.expect diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc index 86b2ebaa2f0d..a0c1f30cc82c 100644 --- a/runtime/vm/compiler/ffi/native_calling_convention.cc +++ b/runtime/vm/compiler/ffi/native_calling_convention.cc @@ -158,8 +158,8 @@ class ArgumentAllocator : public ValueObject { zone_, required_regs + required_xmm_regs); for (intptr_t offset = 0; offset < size; offset += compiler::target::kWordSize) { - if (payload_type.ContainsOnlyFloats( - offset, Utils::Minimum(size - offset, 8))) { + if (payload_type.ContainsOnlyFloats(Range::StartAndEnd( + offset, Utils::Minimum(size, offset + 8)))) { const intptr_t reg_index = FirstFreeFpuRegisterIndex(kQuadFpuReg); AllocateFpuRegisterAtIndex(kQuadFpuReg, reg_index); const auto& type = *new (zone_) NativePrimitiveType(kDouble); @@ -527,8 +527,8 @@ static const NativeLocation& CompoundResultLocation( const auto& double_type = *new (zone) NativePrimitiveType(kDouble); const auto& int64_type = *new (zone) NativePrimitiveType(kInt64); - const bool first_half_in_xmm = - payload_type.ContainsOnlyFloats(0, Utils::Minimum(size, 8)); + const bool first_half_in_xmm = payload_type.ContainsOnlyFloats( + Range::StartAndEnd(0, Utils::Minimum(size, 8))); if (first_half_in_xmm) { multiple_locations.Add(new (zone) NativeFpuRegistersLocation( double_type, double_type, kQuadFpuReg, @@ -541,7 +541,7 @@ static const NativeLocation& CompoundResultLocation( } if (size > 8) { const bool second_half_in_xmm = payload_type.ContainsOnlyFloats( - 8, Utils::Minimum(size - 8, 8)); + Range::StartAndEnd(8, Utils::Minimum(size, 16))); if (second_half_in_xmm) { const FpuRegister reg = used_xmm_regs == 0 ? CallingConventions::kReturnFpuReg diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc index 8b277608aa70..82db265f4627 100644 --- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc +++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc @@ -159,6 +159,50 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesHomogenousx10) { RunSignatureTest(Z, "struct16bytesHomogenousx10", arguments, struct_type); } +// Test with homogenous struct (2). +// +// This time with nested structs and inline arrays. +// +// See the *.expect in ./unit_tests for this behavior. +UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesHomogenousx10_2) { + const auto& float_type = *new (Z) NativePrimitiveType(kFloat); + const auto& int8type = *new (Z) NativePrimitiveType(kInt8); + + const auto& float_1_array_type = *new (Z) NativeArrayType(float_type, 1); + + const auto& float_2_array_type = *new (Z) NativeArrayType(float_type, 2); + auto& full_float_member_types = *new (Z) NativeTypes(Z, 1); + full_float_member_types.Add(&float_2_array_type); + const auto& float_array_struct_type = + NativeCompoundType::FromNativeTypes(Z, full_float_member_types); + + auto& member_types = *new (Z) NativeTypes(Z, 3); + member_types.Add(&float_1_array_type); + member_types.Add(&float_array_struct_type); + member_types.Add(&float_type); + const auto& struct_type = + NativeCompoundType::FromNativeTypes(Z, member_types); + + auto& arguments = *new (Z) NativeTypes(Z, 13); + arguments.Add(&struct_type); + arguments.Add(&float_type); // Claim a single FPU register. + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&float_type); // Check float register back filling, if any. + arguments.Add(&int8type); // Check integer register back filling, if any. + arguments.Add(&struct_type); // Check stack alignment of struct. + + // Identical expectation files as previous test, struct contains the same + // members, but nested in arrays and nested structs. + RunSignatureTest(Z, "struct16bytesHomogenousx10", arguments, struct_type); +} + // A fairly big struct. // // On arm, split up in 8-byte chunks. The first chunk goes into two registers, @@ -269,6 +313,65 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10_2) { RunSignatureTest(Z, "struct16bytesMixedx10_2", arguments, struct_type); } + +// On x64 non-Windows a struct can be spread over an FPU and int register. +// +// This behavior also happens with nested structs and inline arrays. +// +// typedef struct { +// int32_t a0; +// float a1; +// } HalfFloat; +// +// typedef struct { +// float a1[1]; +// } FullFloat; +// +// typedef struct { +// int32_t a0; +// HalfFloat a1; +// FullFloat a2; +// } HalfFloat2; +// +// See the *.expect in ./unit_tests for this behavior. +UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_struct16bytesMixedx10_3) { + const auto& float_type = *new (Z) NativePrimitiveType(kFloat); + const auto& int32_type = *new (Z) NativePrimitiveType(kInt32); + + auto& half_float_member_types = *new (Z) NativeTypes(Z, 2); + half_float_member_types.Add(&int32_type); + half_float_member_types.Add(&float_type); + const auto& half_float_type = + NativeCompoundType::FromNativeTypes(Z, half_float_member_types); + + const auto& float_array_type = *new (Z) NativeArrayType(float_type, 1); + auto& full_float_member_types = *new (Z) NativeTypes(Z, 1); + full_float_member_types.Add(&float_array_type); + const auto& full_float_type = + NativeCompoundType::FromNativeTypes(Z, full_float_member_types); + + auto& member_types = *new (Z) NativeTypes(Z, 3); + member_types.Add(&int32_type); + member_types.Add(&half_float_type); + member_types.Add(&full_float_type); + const auto& struct_type = + NativeCompoundType::FromNativeTypes(Z, member_types); + + auto& arguments = *new (Z) NativeTypes(Z, 11); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); // Integer registers exhausted, on stack. + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&struct_type); + arguments.Add(&float_type); // Use remaining FPU register. + + RunSignatureTest(Z, "struct16bytesMixedx10_3", arguments, struct_type); +} #endif // defined(TARGET_ARCH_X64) // On ia32 Windows a struct can be returned in registers, on non-Windows not. diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc index 837c17f36209..7e1ade525061 100644 --- a/runtime/vm/compiler/ffi/native_type.cc +++ b/runtime/vm/compiler/ffi/native_type.cc @@ -46,6 +46,11 @@ const NativePrimitiveType& NativeType::AsPrimitive() const { return static_cast(*this); } +const NativeArrayType& NativeType::AsArray() const { + ASSERT(IsArray()); + return static_cast(*this); +} + const NativeCompoundType& NativeType::AsCompound() const { ASSERT(IsCompound()); return static_cast(*this); @@ -256,6 +261,14 @@ bool NativePrimitiveType::Equals(const NativeType& other) const { return other.AsPrimitive().representation_ == representation_; } +bool NativeArrayType::Equals(const NativeType& other) const { + if (!other.IsArray()) { + return false; + } + return other.AsArray().length_ == length_ && + other.AsArray().element_type_.Equals(element_type_); +} + bool NativeCompoundType::Equals(const NativeType& other) const { if (!other.IsCompound()) { return false; @@ -462,6 +475,16 @@ const char* NativeFunctionType::ToCString(Zone* zone) const { return textBuffer.buffer(); } +void NativeArrayType::PrintTo(BaseTextBuffer* f, + bool multi_line, + bool verbose) const { + f->AddString("Array("); + f->Printf("element type: "); + element_type_.PrintTo(f, /*multi_line*/ false, verbose); + f->Printf(", length: %" Pd "", length_); + f->AddString(")"); +} + void NativeCompoundType::PrintTo(BaseTextBuffer* f, bool multi_line, bool verbose) const { @@ -518,6 +541,10 @@ intptr_t NativePrimitiveType::NumPrimitiveMembersRecursive() const { return 1; } +intptr_t NativeArrayType::NumPrimitiveMembersRecursive() const { + return element_type_.NumPrimitiveMembersRecursive() * length_; +} + intptr_t NativeCompoundType::NumPrimitiveMembersRecursive() const { intptr_t count = 0; for (intptr_t i = 0; i < members_.length(); i++) { @@ -530,6 +557,10 @@ const NativePrimitiveType& NativePrimitiveType::FirstPrimitiveMember() const { return *this; } +const NativePrimitiveType& NativeArrayType::FirstPrimitiveMember() const { + return element_type_.FirstPrimitiveMember(); +} + const NativePrimitiveType& NativeCompoundType::FirstPrimitiveMember() const { ASSERT(NumPrimitiveMembersRecursive() >= 1); for (intptr_t i = 0; i < members().length(); i++) { @@ -540,30 +571,77 @@ const NativePrimitiveType& NativeCompoundType::FirstPrimitiveMember() const { UNREACHABLE(); } -bool NativeCompoundType::ContainsOnlyFloats(intptr_t offset_in_bytes, - intptr_t size_in_bytes) const { - ASSERT(size_in_bytes >= 0); - const intptr_t first_byte = offset_in_bytes; - const intptr_t last_byte = offset_in_bytes + size_in_bytes - 1; +#if !defined(DART_PRECOMPILED_RUNTIME) +bool NativePrimitiveType::ContainsOnlyFloats(Range range) const { + const auto this_range = Range::StartAndEnd(0, SizeInBytes()); + ASSERT(this_range.Contains(range)); + + return IsFloat(); +} + +bool NativeArrayType::ContainsOnlyFloats(Range range) const { + const auto this_range = Range::StartAndEnd(0, SizeInBytes()); + ASSERT(this_range.Contains(range)); + + const intptr_t element_size_in_bytes = element_type_.SizeInBytes(); + + // Assess how many elements are (partially) covered by the range. + const intptr_t first_element_start = range.start() / element_size_in_bytes; + const intptr_t last_element_index = + range.end_inclusive() / element_size_in_bytes; + const intptr_t num_elements = last_element_index - first_element_start + 1; + ASSERT(num_elements >= 1); + + if (num_elements > 2) { + // At least one full element covered. + return element_type_.ContainsOnlyFloats( + Range::StartAndLength(0, element_size_in_bytes)); + } + + // Check first element, which falls (partially) in range. + const intptr_t first_start = first_element_start * element_size_in_bytes; + const auto first_range = + Range::StartAndLength(first_start, element_size_in_bytes); + const auto first_range_clipped = range.Intersect(first_range); + const auto range_in_first = first_range_clipped.Translate(-first_start); + if (!element_type_.ContainsOnlyFloats(range_in_first)) { + // First element contains not only floats in specified range. + return false; + } + + if (num_elements == 2) { + // Check the second (and last) element, which falls (partially) in range. + const intptr_t second_element_index = first_element_start + 1; + const intptr_t second_start = second_element_index * element_size_in_bytes; + const auto second_range = + Range::StartAndLength(second_start, element_size_in_bytes); + const auto second_range_clipped = range.Intersect(second_range); + const auto range_in_second = second_range_clipped.Translate(-second_start); + return element_type_.ContainsOnlyFloats(range_in_second); + } + + return true; +} + +bool NativeCompoundType::ContainsOnlyFloats(Range range) const { + const auto this_range = Range::StartAndEnd(0, SizeInBytes()); + ASSERT(this_range.Contains(range)); + for (intptr_t i = 0; i < members_.length(); i++) { - const intptr_t member_first_byte = member_offsets_[i]; - const intptr_t member_last_byte = - member_first_byte + members_[i]->SizeInBytes() - 1; - if ((first_byte <= member_first_byte && member_first_byte <= last_byte) || - (first_byte <= member_last_byte && member_last_byte <= last_byte)) { - if (members_[i]->IsPrimitive() && !members_[i]->IsFloat()) { + const auto& member = *members_[i]; + const intptr_t member_offset = member_offsets_[i]; + const intptr_t member_size = member.SizeInBytes(); + const auto member_range = Range::StartAndLength(member_offset, member_size); + if (range.Overlaps(member_range)) { + const auto member_range_clipped = member_range.Intersect(range); + const auto range_in_member = + member_range_clipped.Translate(-member_offset); + if (!member.ContainsOnlyFloats(range_in_member)) { + // Member contains not only floats in specified range. return false; } - if (members_[i]->IsCompound()) { - const auto& nested = members_[i]->AsCompound(); - const bool nested_only_floats = nested.ContainsOnlyFloats( - offset_in_bytes - member_first_byte, size_in_bytes); - if (!nested_only_floats) { - return false; - } - } } - if (member_first_byte > last_byte) { + if (member_range.After(range)) { // None of the remaining members fits the range. break; } @@ -574,13 +652,14 @@ bool NativeCompoundType::ContainsOnlyFloats(intptr_t offset_in_bytes, intptr_t NativeCompoundType::NumberOfWordSizeChunksOnlyFloat() const { // O(n^2) implementation, but only invoked for small structs. ASSERT(SizeInBytes() <= 16); + const auto this_range = Range::StartAndEnd(0, SizeInBytes()); const intptr_t size = SizeInBytes(); intptr_t float_only_chunks = 0; for (intptr_t offset = 0; offset < size; offset += compiler::target::kWordSize) { - if (ContainsOnlyFloats( - offset, Utils::Minimum(size - offset, - compiler::target::kWordSize))) { + const auto chunk_range = + Range::StartAndLength(offset, compiler::target::kWordSize); + if (ContainsOnlyFloats(chunk_range.Intersect(this_range))) { float_only_chunks++; } } @@ -593,19 +672,22 @@ intptr_t NativeCompoundType::NumberOfWordSizeChunksNotOnlyFloat() const { compiler::target::kWordSize; return total_chunks - NumberOfWordSizeChunksOnlyFloat(); } +#endif // !defined(DART_PRECOMPILED_RUNTIME) static void ContainsHomogenuousFloatsRecursive(const NativeTypes& types, bool* only_float, bool* only_double) { for (intptr_t i = 0; i < types.length(); i++) { - const auto& member_type = types.At(i); - if (member_type->IsPrimitive()) { - PrimitiveType type = member_type->AsPrimitive().representation(); + const auto& type = *types.At(i); + const auto& member_type = + type.IsArray() ? type.AsArray().element_type() : type; + if (member_type.IsPrimitive()) { + PrimitiveType type = member_type.AsPrimitive().representation(); *only_float = *only_float && (type == kFloat); *only_double = *only_double && (type == kDouble); } - if (member_type->IsCompound()) { - ContainsHomogenuousFloatsRecursive(member_type->AsCompound().members(), + if (member_type.IsCompound()) { + ContainsHomogenuousFloatsRecursive(member_type.AsCompound().members(), only_float, only_double); } } diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h index 73d691572866..9b3df3f8407c 100644 --- a/runtime/vm/compiler/ffi/native_type.h +++ b/runtime/vm/compiler/ffi/native_type.h @@ -10,6 +10,9 @@ #include "vm/allocation.h" #include "vm/growable_array.h" +#if !defined(DART_PRECOMPILED_RUNTIME) +#include "vm/compiler/ffi/range.h" +#endif #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS) #include "vm/compiler/backend/locations.h" #endif @@ -26,6 +29,7 @@ namespace compiler { namespace ffi { class NativePrimitiveType; +class NativeArrayType; class NativeCompoundType; // NativeTypes are the types used in calling convention specifications: @@ -64,6 +68,8 @@ class NativeType : public ZoneAllocated { virtual bool IsPrimitive() const { return false; } const NativePrimitiveType& AsPrimitive() const; + virtual bool IsArray() const { return false; } + const NativeArrayType& AsArray() const; virtual bool IsCompound() const { return false; } const NativeCompoundType& AsCompound() const; @@ -84,6 +90,13 @@ class NativeType : public ZoneAllocated { // The alignment in bytes of this representation as member of a composite. virtual intptr_t AlignmentInBytesField() const = 0; +#if !defined(DART_PRECOMPILED_RUNTIME) + // Returns true iff a range within this type contains only floats. + // + // Useful for determining whether struct is passed in FP registers on x64. + virtual bool ContainsOnlyFloats(Range range) const = 0; +#endif // !defined(DART_PRECOMPILED_RUNTIME) + #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS) // NativeTypes which are available as unboxed Representations. virtual bool IsExpressibleAsRepresentation() const { return false; } @@ -117,10 +130,8 @@ class NativeType : public ZoneAllocated { const char* ToCString() const; #endif - virtual intptr_t NumPrimitiveMembersRecursive() const { UNREACHABLE(); } - virtual const NativePrimitiveType& FirstPrimitiveMember() const { - UNREACHABLE(); - } + virtual intptr_t NumPrimitiveMembersRecursive() const = 0; + virtual const NativePrimitiveType& FirstPrimitiveMember() const = 0; virtual ~NativeType() {} @@ -170,6 +181,10 @@ class NativePrimitiveType : public NativeType { virtual intptr_t AlignmentInBytesStack() const; virtual intptr_t AlignmentInBytesField() const; +#if !defined(DART_PRECOMPILED_RUNTIME) + virtual bool ContainsOnlyFloats(Range range) const; +#endif // !defined(DART_PRECOMPILED_RUNTIME) + #if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS) virtual bool IsExpressibleAsRepresentation() const; virtual Representation AsRepresentation() const; @@ -191,12 +206,53 @@ class NativePrimitiveType : public NativeType { const PrimitiveType representation_; }; +// Fixed-length arrays. +class NativeArrayType : public NativeType { + public: + NativeArrayType(const NativeType& element_type, intptr_t length) + : element_type_(element_type), length_(length) { + ASSERT(!element_type.IsArray()); + ASSERT(length > 0); + } + + const NativeType& element_type() const { return element_type_; } + intptr_t length() const { return length_; } + + virtual bool IsArray() const { return true; } + + virtual intptr_t SizeInBytes() const { + return element_type_.SizeInBytes() * length_; + } + virtual intptr_t AlignmentInBytesField() const { + return element_type_.AlignmentInBytesField(); + } + virtual intptr_t AlignmentInBytesStack() const { + return element_type_.AlignmentInBytesStack(); + } + +#if !defined(DART_PRECOMPILED_RUNTIME) + virtual bool ContainsOnlyFloats(Range range) const; +#endif // !defined(DART_PRECOMPILED_RUNTIME) + + virtual bool Equals(const NativeType& other) const; + + virtual void PrintTo(BaseTextBuffer* f, + bool multi_line = false, + bool verbose = true) const; + + virtual intptr_t NumPrimitiveMembersRecursive() const; + virtual const NativePrimitiveType& FirstPrimitiveMember() const; + + private: + const NativeType& element_type_; + const intptr_t length_; +}; + using NativeTypes = ZoneGrowableArray; // Struct // // TODO(dartbug.com/38491): Support unions. -// TODO(dartbug.com/35763): Support inline fixed-length arrays. class NativeCompoundType : public NativeType { public: static NativeCompoundType& FromNativeTypes(Zone* zone, @@ -219,11 +275,8 @@ class NativeCompoundType : public NativeType { bool multi_line = false, bool verbose = true) const; - // Whether a range within a struct contains only floats. - // - // Useful for determining whether struct is passed in FP registers on x64. - bool ContainsOnlyFloats(intptr_t offset_in_bytes, - intptr_t size_in_bytes) const; +#if !defined(DART_PRECOMPILED_RUNTIME) + virtual bool ContainsOnlyFloats(Range range) const; // Returns how many word-sized chuncks _only_ contain floats. // @@ -234,6 +287,7 @@ class NativeCompoundType : public NativeType { // // Useful for determining whether struct is passed in FP registers on x64. intptr_t NumberOfWordSizeChunksNotOnlyFloat() const; +#endif // !defined(DART_PRECOMPILED_RUNTIME) // Whether this type has only same-size floating point members. // diff --git a/runtime/vm/compiler/ffi/native_type_test.cc b/runtime/vm/compiler/ffi/native_type_test.cc index 66fa987fb2cd..d71e0e365d1b 100644 --- a/runtime/vm/compiler/ffi/native_type_test.cc +++ b/runtime/vm/compiler/ffi/native_type_test.cc @@ -70,7 +70,7 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_int8x10) { const auto& struct_type = RunStructTest(Z, "struct_int8x10", members); EXPECT(!struct_type.ContainsHomogenuousFloats()); - EXPECT(!struct_type.ContainsOnlyFloats(0, 8)); + EXPECT(!struct_type.ContainsOnlyFloats(Range::StartAndEnd(0, 8))); EXPECT_EQ(0, struct_type.NumberOfWordSizeChunksOnlyFloat()); EXPECT_EQ( Utils::RoundUp(struct_type.SizeInBytes(), compiler::target::kWordSize) / @@ -97,8 +97,8 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_floatx4) { // On x64, 8-byte parts of the chunks contain only floats and will be passed // in FPU registers. - EXPECT(struct_type.ContainsOnlyFloats(0, 8)); - EXPECT(struct_type.ContainsOnlyFloats(8, 8)); + EXPECT(struct_type.ContainsOnlyFloats(Range::StartAndEnd(0, 8))); + EXPECT(struct_type.ContainsOnlyFloats(Range::StartAndEnd(8, 16))); EXPECT_EQ(struct_type.SizeInBytes() / compiler::target::kWordSize, struct_type.NumberOfWordSizeChunksOnlyFloat()); EXPECT_EQ(0, struct_type.NumberOfWordSizeChunksNotOnlyFloat()); @@ -153,6 +153,49 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_VeryLargeStruct) { RunStructTest(Z, "struct_VeryLargeStruct", members); } +UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_int8array) { + const auto& int8type = *new (Z) NativePrimitiveType(kInt8); + const auto& array_type = *new (Z) NativeArrayType(int8type, 8); + + auto& members = *new (Z) NativeTypes(Z, 1); + members.Add(&array_type); + + const auto& struct_type = RunStructTest(Z, "struct_int8array", members); + + EXPECT_EQ(8, struct_type.SizeInBytes()); + EXPECT(!struct_type.ContainsHomogenuousFloats()); + EXPECT(!struct_type.ContainsOnlyFloats(Range::StartAndEnd(0, 8))); + EXPECT_EQ(0, struct_type.NumberOfWordSizeChunksOnlyFloat()); + EXPECT_EQ( + Utils::RoundUp(struct_type.SizeInBytes(), compiler::target::kWordSize) / + compiler::target::kWordSize, + struct_type.NumberOfWordSizeChunksNotOnlyFloat()); +} + +UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_floatarray) { + const auto& float_type = *new (Z) NativePrimitiveType(kFloat); + + const auto& inner_array_type = *new (Z) NativeArrayType(float_type, 2); + + auto& inner_struct_members = *new (Z) NativeTypes(Z, 1); + inner_struct_members.Add(&inner_array_type); + const auto& inner_struct = + NativeCompoundType::FromNativeTypes(Z, inner_struct_members); + + const auto& array_type = *new (Z) NativeArrayType(inner_struct, 2); + + auto& members = *new (Z) NativeTypes(Z, 1); + members.Add(&array_type); + + const auto& struct_type = RunStructTest(Z, "struct_floatarray", members); + + EXPECT_EQ(16, struct_type.SizeInBytes()); + EXPECT(struct_type.ContainsHomogenuousFloats()); + EXPECT(struct_type.ContainsOnlyFloats(Range::StartAndEnd(0, 8))); + EXPECT_EQ(16 / compiler::target::kWordSize, + struct_type.NumberOfWordSizeChunksOnlyFloat()); +} + } // namespace ffi } // namespace compiler } // namespace dart diff --git a/runtime/vm/compiler/ffi/range.h b/runtime/vm/compiler/ffi/range.h new file mode 100644 index 000000000000..0848875175ac --- /dev/null +++ b/runtime/vm/compiler/ffi/range.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef RUNTIME_VM_COMPILER_FFI_RANGE_H_ +#define RUNTIME_VM_COMPILER_FFI_RANGE_H_ + +#include "platform/assert.h" +#include "platform/utils.h" +#include "vm/allocation.h" + +namespace dart { + +namespace compiler { + +namespace ffi { + +// A non-empty range. +// +// Ranges are positive and non-empty. +// +// The end is exclusive. +class Range : public ValueObject { + public: + // Constructs a Range from start (inclusive) and length. + // + // The resulting range is `[start_inclusive, start_inclusive + length)`. + static Range StartAndLength(intptr_t start_inclusive, intptr_t length) { + return Range(start_inclusive, start_inclusive + length); + } + + // Constructs a Range from start (inclusive) and end (exclusive). + // + // The resulting range is `[start_inclusive, end_exclusive)`. + static Range StartAndEnd(intptr_t start_inclusive, intptr_t end_exclusive) { + return Range(start_inclusive, end_exclusive); + } + + Range(const Range& other) + : start_(other.start_), end_exclusive_(other.end_exclusive_) {} + + intptr_t start() const { return start_; } + intptr_t end_exclusive() const { return end_exclusive_; } + intptr_t end_inclusive() const { return end_exclusive_ - 1; } + + intptr_t Length() const { return end_exclusive_ - start_; } + + // Returs true iff number is in this range. + bool Contains(intptr_t number) const { + return start_ <= number && number < end_exclusive_; + } + + // Returns true iff [this] contains [other] completely. + bool Contains(const Range& other) const { + return Contains(other.start_) && Contains(other.end_inclusive()); + } + + // Returns true iff [this] is completey after [other]. + bool After(const Range& other) const { + return other.end_exclusive_ <= start_; + } + + // Returns true iff [this] contains some numbers of [other]. + bool Overlaps(const Range& other) const { + return !this->After(other) && !other.After(*this); + } + + // Returns the intersection of [this] with [other]. + // + // Requires [this] and [other] to overlap. + const Range Intersect(const Range& other) const { + ASSERT(Overlaps(other)); + return Range(Utils::Maximum(start_, other.start_), + Utils::Minimum(end_exclusive_, other.end_exclusive_)); + } + + // Returns a range moved by [delta]. + // + // `this.start() - delta` must be positive. + const Range Translate(intptr_t delta) const { + return Range(start_ + delta, end_exclusive_ + delta); + } + + private: + Range(intptr_t start_inclusive, intptr_t end_exclusive) + : start_(start_inclusive), end_exclusive_(end_exclusive) { + if (!(start_ >= 0 && end_exclusive_ > start_)) { + ASSERT(start_ >= 0); + ASSERT(end_exclusive_ > start_); + } + } + + const intptr_t start_; + const intptr_t end_exclusive_; +}; + +} // namespace ffi + +} // namespace compiler + +} // namespace dart + +#endif // RUNTIME_VM_COMPILER_FFI_RANGE_H_ diff --git a/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_ios.expect new file mode 100644 index 000000000000..a9ed4cdf2020 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_ios.expect @@ -0,0 +1,13 @@ +M(rdi int64, xmm0 double) Compound(size: 16) +M(rsi int64, xmm1 double) Compound(size: 16) +M(rdx int64, xmm2 double) Compound(size: 16) +M(rcx int64, xmm3 double) Compound(size: 16) +M(r8 int64, xmm4 double) Compound(size: 16) +M(r9 int64, xmm5 double) Compound(size: 16) +S+0 Compound(size: 16) +S+16 Compound(size: 16) +S+32 Compound(size: 16) +S+48 Compound(size: 16) +xmm6 float +=> +M(rax int64, xmm0 double) Compound(size: 16) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_linux.expect new file mode 100644 index 000000000000..a9ed4cdf2020 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_linux.expect @@ -0,0 +1,13 @@ +M(rdi int64, xmm0 double) Compound(size: 16) +M(rsi int64, xmm1 double) Compound(size: 16) +M(rdx int64, xmm2 double) Compound(size: 16) +M(rcx int64, xmm3 double) Compound(size: 16) +M(r8 int64, xmm4 double) Compound(size: 16) +M(r9 int64, xmm5 double) Compound(size: 16) +S+0 Compound(size: 16) +S+16 Compound(size: 16) +S+32 Compound(size: 16) +S+48 Compound(size: 16) +xmm6 float +=> +M(rax int64, xmm0 double) Compound(size: 16) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_macos.expect new file mode 100644 index 000000000000..a9ed4cdf2020 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_macos.expect @@ -0,0 +1,13 @@ +M(rdi int64, xmm0 double) Compound(size: 16) +M(rsi int64, xmm1 double) Compound(size: 16) +M(rdx int64, xmm2 double) Compound(size: 16) +M(rcx int64, xmm3 double) Compound(size: 16) +M(r8 int64, xmm4 double) Compound(size: 16) +M(r9 int64, xmm5 double) Compound(size: 16) +S+0 Compound(size: 16) +S+16 Compound(size: 16) +S+32 Compound(size: 16) +S+48 Compound(size: 16) +xmm6 float +=> +M(rax int64, xmm0 double) Compound(size: 16) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_win.expect new file mode 100644 index 000000000000..d21ee2b11e5c --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct16bytesMixedx10_3/x64_win.expect @@ -0,0 +1,13 @@ +P(rdx int64) Compound(size: 16) +P(r8 int64) Compound(size: 16) +P(r9 int64) Compound(size: 16) +P(S+0 int64) Compound(size: 16) +P(S+8 int64) Compound(size: 16) +P(S+16 int64) Compound(size: 16) +P(S+24 int64) Compound(size: 16) +P(S+32 int64) Compound(size: 16) +P(S+40 int64) Compound(size: 16) +P(S+48 int64) Compound(size: 16) +S+56 float +=> +P(rcx int64, ret:rax int64) Compound(size: 16) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_android.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_android.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_ios.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_ios.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_linux.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_macos.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm64_macos.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_android.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_android.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_ios.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_ios.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_linux.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/arm_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_android.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_android.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_linux.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_win.expect new file mode 100644 index 000000000000..71313bc62c6e --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/ia32_win.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 4, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 4, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_ios.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_ios.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_linux.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_macos.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_macos.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_win.expect new file mode 100644 index 000000000000..025106d7aaa0 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_floatarray/x64_win.expect @@ -0,0 +1,3 @@ +Compound(size: 16, field alignment: 4, stack alignment: 8, members: { + 0: Array(element type: Compound(size: 8, field alignment: 4, stack alignment: 8, members: {0: Array(element type: float, length: 2)}), length: 2) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_android.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_android.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_ios.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_ios.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_linux.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_macos.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm64_macos.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_android.expect new file mode 100644 index 000000000000..879e00ff0a8f --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_android.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 4, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_ios.expect new file mode 100644 index 000000000000..879e00ff0a8f --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_ios.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 4, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_linux.expect new file mode 100644 index 000000000000..879e00ff0a8f --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/arm_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 4, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_android.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_android.expect new file mode 100644 index 000000000000..879e00ff0a8f --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_android.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 4, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_linux.expect new file mode 100644 index 000000000000..879e00ff0a8f --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 4, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_win.expect new file mode 100644 index 000000000000..879e00ff0a8f --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/ia32_win.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 4, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_ios.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_ios.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_ios.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_linux.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_linux.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_linux.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_macos.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_macos.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_macos.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +}) diff --git a/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_win.expect b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_win.expect new file mode 100644 index 000000000000..a44e66bdbcc9 --- /dev/null +++ b/runtime/vm/compiler/ffi/unit_tests/struct_int8array/x64_win.expect @@ -0,0 +1,3 @@ +Compound(size: 8, field alignment: 1, stack alignment: 8, members: { + 0: Array(element type: int8, length: 8) +})