Skip to content

Commit e7acc69

Browse files
committed
[vm/ffi] Support packed Structs backend - part 2
With packed structs, the x64 non-Windows ABI only put structs in CPU/FPU registers when all it fields happen to be aligned. The previous CL did not check the alignment of nested structs and structs in inline arrays based on their offset in an outer struct. Follow up of: https://dart-review.googlesource.com/c/sdk/+/186142. Split off: https://dart-review.googlesource.com/c/sdk/+/186143. Bug: #38158 tools/build.py run_ffi_unit_tests && tools/test.py ffi_unit TEST=runtime/vm/compiler/ffi/native_type_test.cc These tests are exercised on vm-precomp-ffi-qemu-linux-release-arm-try. Change-Id: Ic64bdef2c13ac737dbf58864911f043fc7a3d831 Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/191720 Reviewed-by: Aske Simon Christensen <[email protected]>
1 parent 6d1ea68 commit e7acc69

File tree

3 files changed

+79
-15
lines changed

3 files changed

+79
-15
lines changed

runtime/vm/compiler/ffi/native_type.cc

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -736,23 +736,32 @@ intptr_t NativeCompoundType::NumberOfWordSizeChunksNotOnlyFloat() const {
736736
}
737737
#endif // !defined(DART_PRECOMPILED_RUNTIME)
738738

739-
bool NativePrimitiveType::ContainsUnalignedMembers() const {
739+
bool NativePrimitiveType::ContainsUnalignedMembers(intptr_t offset) const {
740+
return offset % AlignmentInBytesField() != 0;
741+
}
742+
743+
bool NativeArrayType::ContainsUnalignedMembers(intptr_t offset) const {
744+
const intptr_t element_size = element_type_.SizeInBytes();
745+
// We're only checking the first two elements of the array.
746+
//
747+
// If the element size is divisible by the alignment of the largest type
748+
// contained within the element type, the alignment of all elements is the
749+
// same. If not, either the first or the second element is unaligned.
750+
const intptr_t max_check = 2;
751+
for (intptr_t i = 0; i < Utils::Minimum(length_, max_check); i++) {
752+
const intptr_t element_offset = i * element_size;
753+
if (element_type_.ContainsUnalignedMembers(offset + element_offset)) {
754+
return true;
755+
}
756+
}
740757
return false;
741758
}
742759

743-
bool NativeArrayType::ContainsUnalignedMembers() const {
744-
return element_type_.ContainsUnalignedMembers();
745-
}
746-
747-
bool NativeCompoundType::ContainsUnalignedMembers() const {
760+
bool NativeCompoundType::ContainsUnalignedMembers(intptr_t offset) const {
748761
for (intptr_t i = 0; i < members_.length(); i++) {
749762
const auto& member = *members_.At(i);
750763
const intptr_t member_offset = member_offsets_.At(i);
751-
const intptr_t member_alignment = member.AlignmentInBytesField();
752-
if (member_offset % member_alignment != 0) {
753-
return true;
754-
}
755-
if (member.ContainsUnalignedMembers()) {
764+
if (member.ContainsUnalignedMembers(offset + member_offset)) {
756765
return true;
757766
}
758767
}

runtime/vm/compiler/ffi/native_type.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class NativeType : public ZoneAllocated {
9898
#endif // !defined(DART_PRECOMPILED_RUNTIME)
9999

100100
// True iff any members are misaligned recursively due to packing.
101-
virtual bool ContainsUnalignedMembers() const = 0;
101+
virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const = 0;
102102

103103
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
104104
// NativeTypes which are available as unboxed Representations.
@@ -188,7 +188,7 @@ class NativePrimitiveType : public NativeType {
188188
virtual bool ContainsOnlyFloats(Range range) const;
189189
#endif // !defined(DART_PRECOMPILED_RUNTIME)
190190

191-
virtual bool ContainsUnalignedMembers() const;
191+
virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
192192

193193
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
194194
virtual bool IsExpressibleAsRepresentation() const;
@@ -239,7 +239,7 @@ class NativeArrayType : public NativeType {
239239
virtual bool ContainsOnlyFloats(Range range) const;
240240
#endif // !defined(DART_PRECOMPILED_RUNTIME)
241241

242-
virtual bool ContainsUnalignedMembers() const;
242+
virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
243243

244244
virtual bool Equals(const NativeType& other) const;
245245

@@ -298,7 +298,7 @@ class NativeCompoundType : public NativeType {
298298
intptr_t NumberOfWordSizeChunksNotOnlyFloat() const;
299299
#endif // !defined(DART_PRECOMPILED_RUNTIME)
300300

301-
virtual bool ContainsUnalignedMembers() const;
301+
virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
302302

303303
// Whether this type has only same-size floating point members.
304304
//

runtime/vm/compiler/ffi/native_type_test.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,61 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed) {
214214
EXPECT(struct_type.ContainsUnalignedMembers());
215215
}
216216

217+
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_array) {
218+
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
219+
const auto& uint16_type = *new (Z) NativePrimitiveType(kUint16);
220+
221+
auto& inner_members = *new (Z) NativeTypes(Z, 2);
222+
inner_members.Add(&uint16_type);
223+
inner_members.Add(&uint8_type);
224+
const intptr_t packing = 1;
225+
const auto& inner_struct_type =
226+
NativeCompoundType::FromNativeTypes(Z, inner_members, packing);
227+
228+
EXPECT_EQ(3, inner_struct_type.SizeInBytes());
229+
// Non-windows x64 considers this struct as all members aligned, even though
230+
// its size is not a multiple of its individual member alignment.
231+
EXPECT(!inner_struct_type.ContainsUnalignedMembers());
232+
233+
const auto& array_type = *new (Z) NativeArrayType(inner_struct_type, 2);
234+
235+
auto& members = *new (Z) NativeTypes(Z, 1);
236+
members.Add(&array_type);
237+
const auto& struct_type = NativeCompoundType::FromNativeTypes(Z, members);
238+
239+
EXPECT_EQ(6, struct_type.SizeInBytes());
240+
// Non-windows x64 passes this as a struct with unaligned members, because
241+
// the second element of the array contains unaligned members.
242+
EXPECT(struct_type.ContainsUnalignedMembers());
243+
}
244+
245+
UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_nested) {
246+
const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
247+
const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
248+
249+
auto& inner_members = *new (Z) NativeTypes(Z, 2);
250+
inner_members.Add(&uint32_type);
251+
inner_members.Add(&uint8_type);
252+
const intptr_t packing = 1;
253+
const auto& inner_struct_type =
254+
NativeCompoundType::FromNativeTypes(Z, inner_members, packing);
255+
256+
EXPECT_EQ(5, inner_struct_type.SizeInBytes());
257+
// Non-windows x64 considers this struct as all members aligned, even though
258+
// its size is not a multiple of its individual member alignment.
259+
EXPECT(!inner_struct_type.ContainsUnalignedMembers());
260+
261+
auto& members = *new (Z) NativeTypes(Z, 2);
262+
members.Add(&uint8_type);
263+
members.Add(&inner_struct_type);
264+
const auto& struct_type = NativeCompoundType::FromNativeTypes(Z, members);
265+
266+
EXPECT_EQ(6, struct_type.SizeInBytes());
267+
// Non-windows x64 passes this as a struct with unaligned members, even
268+
// though the nested struct itself has all its members aligned in isolation.
269+
EXPECT(struct_type.ContainsUnalignedMembers());
270+
}
271+
217272
} // namespace ffi
218273
} // namespace compiler
219274
} // namespace dart

0 commit comments

Comments
 (0)