blob: aa3b00cce2253a69c1b2b7e60d0f59a6b58fbd2d [file] [log] [blame]
// Copyright (c) 2020, 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.
#include "vm/compiler/ffi/unit_test.h"
#include "platform/syslog.h"
#include "vm/compiler/ffi/native_type.h"
#include "vm/compiler/runtime_api.h"
namespace dart {
namespace compiler {
namespace ffi {
// TODO(https://github.com/dart-lang/sdk/issues/48164)
#if !defined(TARGET_ARCH_RISCV32) && !defined(TARGET_ARCH_RISCV64)
const NativeCompoundType& RunStructTest(dart::Zone* zone,
const char* name,
const NativeTypes& member_types,
intptr_t packing = kMaxInt32) {
const auto& struct_type =
NativeStructType::FromNativeTypes(zone, member_types, packing);
const char* test_result = struct_type.ToCString(zone, /*multi_line=*/true);
const int kFilePathLength = 100;
char expectation_file_path[kFilePathLength];
Utils::SNPrint(expectation_file_path, kFilePathLength,
"runtime/vm/compiler/ffi/unit_tests/%s/%s_%s.expect", name,
kArch, kOs);
if (TestCaseBase::update_expectations) {
Syslog::Print("Updating %s\n", expectation_file_path);
WriteToFile(expectation_file_path, test_result);
}
char* expectation_file_contents = nullptr;
ReadFromFile(expectation_file_path, &expectation_file_contents);
EXPECT_NOTNULL(expectation_file_contents);
if (expectation_file_contents != nullptr) {
EXPECT_STREQ(expectation_file_contents, test_result);
free(expectation_file_contents);
}
return struct_type;
}
UNIT_TEST_CASE_WITH_ZONE(NativeType) {
const auto& native_type = *new (Z) NativePrimitiveType(kInt8);
EXPECT_EQ(1, native_type.SizeInBytes());
EXPECT(native_type.IsInt());
EXPECT(native_type.IsPrimitive());
EXPECT_STREQ("int8", native_type.ToCString(Z));
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_int8x10) {
const auto& int8type = *new (Z) NativePrimitiveType(kInt8);
auto& members = *new (Z) NativeTypes(Z, 10);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
members.Add(&int8type);
const auto& struct_type = RunStructTest(Z, "struct_int8x10", members);
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_floatx4) {
const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
auto& members = *new (Z) NativeTypes(Z, 4);
members.Add(&float_type);
members.Add(&float_type);
members.Add(&float_type);
members.Add(&float_type);
const auto& struct_type = RunStructTest(Z, "struct_floatx4", members);
// This is a homogenous float in the arm and arm64 ABIs.
//
// On Arm64 iOS stack alignment of homogenous floats is not word size see
// runtime/vm/compiler/ffi/unit_tests/struct_floatx4/arm64_ios.expect.
EXPECT(struct_type.ContainsHomogenuousFloats());
// On x64, 8-byte parts of the chunks contain only floats and will be passed
// in FPU registers.
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());
}
// A struct designed to exercise all kinds of alignment rules.
// Note that offset32A (System V ia32, iOS arm) aligns doubles on 4 bytes while
// offset32B (Arm 32 bit and MSVC ia32) aligns on 8 bytes.
// TODO(37271): Support nested structs.
// TODO(37470): Add uncommon primitive data types when we want to support them.
struct VeryLargeStruct {
// size32 size64 offset32A offset32B offset64
int8_t a; // 1 0 0 0
int16_t b; // 2 2 2 2
int32_t c; // 4 4 4 4
int64_t d; // 8 8 8 8
uint8_t e; // 1 16 16 16
uint16_t f; // 2 18 18 18
uint32_t g; // 4 20 20 20
uint64_t h; // 8 24 24 24
intptr_t i; // 4 8 32 32 32
double j; // 8 36 40 40
float k; // 4 44 48 48
VeryLargeStruct* parent; // 4 8 48 52 56
intptr_t numChildren; // 4 8 52 56 64
VeryLargeStruct* children; // 4 8 56 60 72
int8_t smallLastField; // 1 60 64 80
// sizeof 64 72 88
};
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_VeryLargeStruct) {
const auto& intptr_type = *new (Z) NativePrimitiveType(
compiler::target::kWordSize == 4 ? kInt32 : kInt64);
auto& members = *new (Z) NativeTypes(Z, 15);
members.Add(new (Z) NativePrimitiveType(kInt8));
members.Add(new (Z) NativePrimitiveType(kInt16));
members.Add(new (Z) NativePrimitiveType(kInt32));
members.Add(new (Z) NativePrimitiveType(kInt64));
members.Add(new (Z) NativePrimitiveType(kUint8));
members.Add(new (Z) NativePrimitiveType(kUint16));
members.Add(new (Z) NativePrimitiveType(kUint32));
members.Add(new (Z) NativePrimitiveType(kUint64));
members.Add(&intptr_type);
members.Add(new (Z) NativePrimitiveType(kDouble));
members.Add(new (Z) NativePrimitiveType(kFloat));
members.Add(&intptr_type);
members.Add(&intptr_type);
members.Add(&intptr_type);
members.Add(new (Z) NativePrimitiveType(kInt8));
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 =
NativeStructType::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());
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed) {
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
const auto& uint16_type = *new (Z) NativePrimitiveType(kUint16);
auto& members = *new (Z) NativeTypes(Z, 2);
members.Add(&uint8_type);
members.Add(&uint16_type);
const intptr_t packing = 1;
const auto& struct_type =
NativeStructType::FromNativeTypes(Z, members, packing);
// Should be 3 bytes on every platform.
EXPECT_EQ(3, struct_type.SizeInBytes());
EXPECT(struct_type.ContainsUnalignedMembers());
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_array) {
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
const auto& uint16_type = *new (Z) NativePrimitiveType(kUint16);
auto& inner_members = *new (Z) NativeTypes(Z, 2);
inner_members.Add(&uint16_type);
inner_members.Add(&uint8_type);
const intptr_t packing = 1;
const auto& inner_struct_type =
NativeStructType::FromNativeTypes(Z, inner_members, packing);
EXPECT_EQ(3, inner_struct_type.SizeInBytes());
// Non-windows x64 considers this struct as all members aligned, even though
// its size is not a multiple of its individual member alignment.
EXPECT(!inner_struct_type.ContainsUnalignedMembers());
const auto& array_type = *new (Z) NativeArrayType(inner_struct_type, 2);
auto& members = *new (Z) NativeTypes(Z, 1);
members.Add(&array_type);
const auto& struct_type = NativeStructType::FromNativeTypes(Z, members);
EXPECT_EQ(6, struct_type.SizeInBytes());
// Non-windows x64 passes this as a struct with unaligned members, because
// the second element of the array contains unaligned members.
EXPECT(struct_type.ContainsUnalignedMembers());
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_nested) {
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
auto& inner_members = *new (Z) NativeTypes(Z, 2);
inner_members.Add(&uint32_type);
inner_members.Add(&uint8_type);
const intptr_t packing = 1;
const auto& inner_struct_type =
NativeStructType::FromNativeTypes(Z, inner_members, packing);
EXPECT_EQ(5, inner_struct_type.SizeInBytes());
// Non-windows x64 considers this struct as all members aligned, even though
// its size is not a multiple of its individual member alignment.
EXPECT(!inner_struct_type.ContainsUnalignedMembers());
auto& members = *new (Z) NativeTypes(Z, 2);
members.Add(&uint8_type);
members.Add(&inner_struct_type);
const auto& struct_type = NativeStructType::FromNativeTypes(Z, members);
EXPECT_EQ(6, struct_type.SizeInBytes());
// Non-windows x64 passes this as a struct with unaligned members, even
// though the nested struct itself has all its members aligned in isolation.
EXPECT(struct_type.ContainsUnalignedMembers());
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_union_size) {
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
const auto& array8_bytes = *new (Z) NativeArrayType(uint32_type, 2);
const auto& array9_bytes = *new (Z) NativeArrayType(uint8_type, 9);
auto& members = *new (Z) NativeTypes(Z, 2);
members.Add(&array8_bytes);
members.Add(&array9_bytes);
const auto& union_type = NativeUnionType::FromNativeTypes(Z, members);
// The alignment requirements of the first member and the size of the second
// member force the size to be rounded up to 12.
EXPECT_EQ(12, union_type.SizeInBytes());
EXPECT(!union_type.ContainsUnalignedMembers());
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_union_primitive_members) {
const auto& float_type = *new (Z) NativePrimitiveType(kFloat);
const auto& float_array_type = *new (Z) NativeArrayType(float_type, 3);
auto& struct_member_types = *new (Z) NativeTypes(Z, 4);
struct_member_types.Add(&float_type);
struct_member_types.Add(&float_type);
struct_member_types.Add(&float_type);
struct_member_types.Add(&float_type);
const auto& struct_type =
NativeStructType::FromNativeTypes(Z, struct_member_types);
auto& member_types = *new (Z) NativeTypes(Z, 2);
member_types.Add(&float_array_type);
member_types.Add(&struct_type);
const auto& union_type = NativeUnionType::FromNativeTypes(Z, member_types);
EXPECT_EQ(16, union_type.SizeInBytes());
EXPECT_EQ(4, union_type.NumPrimitiveMembersRecursive());
EXPECT(union_type.FirstPrimitiveMember().Equals(float_type));
EXPECT(union_type.ContainsHomogenuousFloats());
EXPECT(!union_type.ContainsUnalignedMembers());
}
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_union_unaligned) {
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
auto& inner_members = *new (Z) NativeTypes(Z, 2);
inner_members.Add(&uint8_type);
inner_members.Add(&uint32_type);
const intptr_t packing = 1;
const auto& struct_type =
NativeStructType::FromNativeTypes(Z, inner_members, packing);
const auto& array_type = *new (Z) NativeArrayType(uint8_type, 5);
auto& member_types = *new (Z) NativeTypes(Z, 2);
member_types.Add(&array_type);
member_types.Add(&struct_type);
const auto& union_type = NativeUnionType::FromNativeTypes(Z, member_types);
EXPECT_EQ(5, union_type.SizeInBytes());
EXPECT_EQ(1, union_type.AlignmentInBytesField());
EXPECT(union_type.ContainsUnalignedMembers());
}
#endif // !defined(TARGET_ARCH_RISCV32) && !defined(TARGET_ARCH_RISCV64)
} // namespace ffi
} // namespace compiler
} // namespace dart