blob: d71e0e365d1b6719dff06ce80773f32c7ea5fa4d [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 {
const NativeCompoundType& RunStructTest(dart::Zone* zone,
const char* name,
const NativeTypes& member_types) {
const auto& struct_type =
NativeCompoundType::FromNativeTypes(zone, member_types);
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 =
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