14
14
#include " vm/compiler/backend/locations.h"
15
15
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
16
16
17
+ #if !defined(FFI_UNIT_TESTS)
18
+ #include " vm/symbols.h"
19
+ #endif
20
+
17
21
namespace dart {
18
22
19
23
namespace compiler {
@@ -25,6 +29,11 @@ const NativePrimitiveType& NativeType::AsPrimitive() const {
25
29
return static_cast <const NativePrimitiveType&>(*this );
26
30
}
27
31
32
+ const NativeCompoundType& NativeType::AsCompound () const {
33
+ ASSERT (IsCompound ());
34
+ return static_cast <const NativeCompoundType&>(*this );
35
+ }
36
+
28
37
bool NativePrimitiveType::IsInt () const {
29
38
switch (representation_) {
30
39
case kInt8 :
@@ -127,6 +136,57 @@ intptr_t NativePrimitiveType::AlignmentInBytesField() const {
127
136
}
128
137
}
129
138
139
+ static bool ContainsHomogenuousFloatsInternal (const NativeTypes& types);
140
+
141
+ // Keep consistent with
142
+ // pkg/vm/lib/transformations/ffi_definitions.dart:_calculateSizeAndOffsets.
143
+ NativeCompoundType& NativeCompoundType::FromNativeTypes (
144
+ Zone* zone,
145
+ const NativeTypes& members) {
146
+ intptr_t offset = 0 ;
147
+
148
+ const intptr_t kAtLeast1ByteAligned = 1 ;
149
+ // If this struct is nested in another struct, it should be aligned to the
150
+ // largest alignment of its members.
151
+ intptr_t alignment_field = kAtLeast1ByteAligned ;
152
+ // If this struct is passed on the stack, it should be aligned to the largest
153
+ // alignment of its members when passing those members on the stack.
154
+ intptr_t alignment_stack = kAtLeast1ByteAligned ;
155
+ #if defined(TARGET_OS_MACOS_IOS) && defined(TARGET_ARCH_ARM64)
156
+ // On iOS64 stack values can be less aligned than wordSize, which deviates
157
+ // from the arm64 ABI.
158
+ ASSERT (CallingConventions::kArgumentStackAlignment == kAlignedToValueSize );
159
+ // Because the arm64 ABI aligns primitives to word size on the stack, every
160
+ // struct will be automatically aligned to word size. iOS64 does not align
161
+ // the primitives to word size, so we set structs to align to word size for
162
+ // iOS64.
163
+ // However, homogenous structs are treated differently. They are aligned to
164
+ // their member alignment. (Which is 4 in case of a homogenous float).
165
+ // Source: manual testing.
166
+ if (!ContainsHomogenuousFloatsInternal (members)) {
167
+ alignment_stack = compiler::target::kWordSize ;
168
+ }
169
+ #endif
170
+
171
+ auto & member_offsets =
172
+ *new (zone) ZoneGrowableArray<intptr_t >(zone, members.length ());
173
+ for (intptr_t i = 0 ; i < members.length (); i++) {
174
+ const NativeType& member = *members[i];
175
+ const intptr_t member_size = member.SizeInBytes ();
176
+ const intptr_t member_align_field = member.AlignmentInBytesField ();
177
+ const intptr_t member_align_stack = member.AlignmentInBytesStack ();
178
+ offset = Utils::RoundUp (offset, member_align_field);
179
+ member_offsets.Add (offset);
180
+ offset += member_size;
181
+ alignment_field = Utils::Maximum (alignment_field, member_align_field);
182
+ alignment_stack = Utils::Maximum (alignment_stack, member_align_stack);
183
+ }
184
+ const intptr_t size = Utils::RoundUp (offset, alignment_field);
185
+
186
+ return *new (zone) NativeCompoundType (members, member_offsets, size,
187
+ alignment_field, alignment_stack);
188
+ }
189
+
130
190
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
131
191
bool NativePrimitiveType::IsExpressibleAsRepresentation () const {
132
192
switch (representation_) {
@@ -139,7 +199,7 @@ bool NativePrimitiveType::IsExpressibleAsRepresentation() const {
139
199
case kInt32 :
140
200
case kUint32 :
141
201
case kInt64 :
142
- case kUint64 :
202
+ case kUint64 : // We don't actually have a kUnboxedUint64.
143
203
case kFloat :
144
204
case kDouble :
145
205
return true ;
@@ -179,6 +239,23 @@ bool NativePrimitiveType::Equals(const NativeType& other) const {
179
239
return other.AsPrimitive ().representation_ == representation_;
180
240
}
181
241
242
+ bool NativeCompoundType::Equals (const NativeType& other) const {
243
+ if (!other.IsCompound ()) {
244
+ return false ;
245
+ }
246
+ const auto & other_compound = other.AsCompound ();
247
+ const auto & other_members = other_compound.members_ ;
248
+ if (other_members.length () != members_.length ()) {
249
+ return false ;
250
+ }
251
+ for (intptr_t i = 0 ; i < members_.length (); i++) {
252
+ if (!members_[i]->Equals (*other_members[i])) {
253
+ return false ;
254
+ }
255
+ }
256
+ return true ;
257
+ }
258
+
182
259
static PrimitiveType split_fundamental (PrimitiveType in) {
183
260
switch (in) {
184
261
case kInt16 :
@@ -243,16 +320,52 @@ static PrimitiveType TypeRepresentation(classid_t class_id) {
243
320
}
244
321
}
245
322
323
+ static bool IsPredefinedFfiCid (classid_t class_id) {
324
+ switch (class_id) {
325
+ #define CASE_FFI_CID_TRUE (name ) \
326
+ case kFfi ##name##Cid: \
327
+ return true ;
328
+ CLASS_LIST_FFI (CASE_FFI_CID_TRUE)
329
+ default :
330
+ return false ;
331
+ }
332
+ UNREACHABLE ();
333
+ }
334
+
246
335
NativeType& NativeType::FromTypedDataClassId (Zone* zone, classid_t class_id) {
247
- // TODO(36730): Support composites.
336
+ ASSERT ( IsPredefinedFfiCid (class_id));
248
337
const auto fundamental_rep = TypeRepresentation (class_id);
249
338
return *new (zone) NativePrimitiveType (fundamental_rep);
250
339
}
251
340
252
341
#if !defined(FFI_UNIT_TESTS)
253
342
NativeType& NativeType::FromAbstractType (Zone* zone, const AbstractType& type) {
254
- // TODO(36730): Support composites.
255
- return NativeType::FromTypedDataClassId (zone, type.type_class_id ());
343
+ const classid_t class_id = type.type_class_id ();
344
+ if (IsPredefinedFfiCid (class_id)) {
345
+ return NativeType::FromTypedDataClassId (zone, class_id);
346
+ }
347
+
348
+ // User-defined structs.
349
+ const auto & cls = Class::Handle (zone, type.type_class ());
350
+
351
+ auto & options = Object::Handle (zone);
352
+ Library::FindPragma (dart::Thread::Current (), /* only_core=*/ false , cls,
353
+ Symbols::vm_ffi_struct_fields (), &options);
354
+ ASSERT (!options.IsNull ());
355
+ ASSERT (options.IsArray ());
356
+
357
+ const auto & field_types = Array::Cast (options);
358
+ auto & field_type = AbstractType::Handle (zone);
359
+ auto & field_native_types = *new (zone) ZoneGrowableArray<const NativeType*>(
360
+ zone, field_types.Length ());
361
+ for (intptr_t i = 0 ; i < field_types.Length (); i++) {
362
+ field_type ^= field_types.At (i);
363
+ const NativeType& field_native_type =
364
+ NativeType::FromAbstractType (zone, field_type);
365
+ field_native_types.Add (&field_native_type);
366
+ }
367
+
368
+ return NativeCompoundType::FromNativeTypes (zone, field_native_types);
256
369
}
257
370
#endif
258
371
@@ -281,15 +394,15 @@ NativePrimitiveType& NativeType::FromUnboxedRepresentation(Zone* zone,
281
394
}
282
395
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
283
396
284
- const char * NativeType::ToCString (Zone* zone) const {
397
+ const char * NativeType::ToCString (Zone* zone, bool multi_line ) const {
285
398
ZoneTextBuffer textBuffer (zone);
286
- PrintTo (&textBuffer);
399
+ PrintTo (&textBuffer, multi_line );
287
400
return textBuffer.buffer ();
288
401
}
289
402
290
403
#if !defined(FFI_UNIT_TESTS)
291
- const char * NativeType::ToCString () const {
292
- return ToCString (Thread::Current ()->zone ());
404
+ const char * NativeType::ToCString (bool multi_line ) const {
405
+ return ToCString (Thread::Current ()->zone (), multi_line );
293
406
}
294
407
#endif
295
408
@@ -324,11 +437,11 @@ static const char* PrimitiveTypeToCString(PrimitiveType rep) {
324
437
}
325
438
}
326
439
327
- void NativeType::PrintTo (BaseTextBuffer* f) const {
440
+ void NativeType::PrintTo (BaseTextBuffer* f, bool multi_line ) const {
328
441
f->AddString (" I" );
329
442
}
330
443
331
- void NativePrimitiveType::PrintTo (BaseTextBuffer* f) const {
444
+ void NativePrimitiveType::PrintTo (BaseTextBuffer* f, bool multi_line ) const {
332
445
f->Printf (" %s" , PrimitiveTypeToCString (representation_));
333
446
}
334
447
@@ -338,6 +451,35 @@ const char* NativeFunctionType::ToCString(Zone* zone) const {
338
451
return textBuffer.buffer ();
339
452
}
340
453
454
+ void NativeCompoundType::PrintTo (BaseTextBuffer* f, bool multi_line) const {
455
+ f->AddString (" Compound(" );
456
+ f->Printf (" size: %" Pd " , " , SizeInBytes ());
457
+ f->Printf (" field alignment: %" Pd " , " , AlignmentInBytesField ());
458
+ f->Printf (" stack alignment: %" Pd " , " , AlignmentInBytesStack ());
459
+ f->AddString (" members: {" );
460
+ if (multi_line) {
461
+ f->AddString (" \n " );
462
+ }
463
+ for (intptr_t i = 0 ; i < members_.length (); i++) {
464
+ if (i > 0 ) {
465
+ if (multi_line) {
466
+ f->AddString (" ,\n " );
467
+ } else {
468
+ f->AddString (" , " );
469
+ }
470
+ }
471
+ f->Printf (" %" Pd " : " , member_offsets_[i]);
472
+ members_[i]->PrintTo (f);
473
+ }
474
+ if (multi_line) {
475
+ f->AddString (" \n " );
476
+ }
477
+ f->AddString (" })" );
478
+ if (multi_line) {
479
+ f->AddString (" \n " );
480
+ }
481
+ }
482
+
341
483
#if !defined(FFI_UNIT_TESTS)
342
484
const char * NativeFunctionType::ToCString () const {
343
485
return ToCString (Thread::Current ()->zone ());
@@ -356,6 +498,88 @@ void NativeFunctionType::PrintTo(BaseTextBuffer* f) const {
356
498
return_type_.PrintTo (f);
357
499
}
358
500
501
+ bool NativeCompoundType::ContainsOnlyFloats (intptr_t offset_in_bytes,
502
+ intptr_t size_in_bytes) const {
503
+ ASSERT (size_in_bytes >= 0 );
504
+ const intptr_t first_byte = offset_in_bytes;
505
+ const intptr_t last_byte = offset_in_bytes + size_in_bytes - 1 ;
506
+ for (intptr_t i = 0 ; i < members_.length (); i++) {
507
+ const intptr_t member_first_byte = member_offsets_[i];
508
+ const intptr_t member_last_byte =
509
+ member_first_byte + members_[i]->SizeInBytes () - 1 ;
510
+ if ((first_byte <= member_first_byte && member_first_byte <= last_byte) ||
511
+ (first_byte <= member_last_byte && member_last_byte <= last_byte)) {
512
+ if (members_[i]->IsPrimitive () && !members_[i]->IsFloat ()) {
513
+ return false ;
514
+ }
515
+ if (members_[i]->IsCompound ()) {
516
+ const auto & nested = members_[i]->AsCompound ();
517
+ const bool nested_only_floats = nested.ContainsOnlyFloats (
518
+ offset_in_bytes - member_first_byte, size_in_bytes);
519
+ if (!nested_only_floats) {
520
+ return false ;
521
+ }
522
+ }
523
+ }
524
+ if (member_first_byte > last_byte) {
525
+ // None of the remaining members fits the range.
526
+ break ;
527
+ }
528
+ }
529
+ return true ;
530
+ }
531
+
532
+ intptr_t NativeCompoundType::NumberOfWordSizeChunksOnlyFloat () const {
533
+ // O(n^2) implementation, but only invoked for small structs.
534
+ ASSERT (SizeInBytes () <= 16 );
535
+ const intptr_t size = SizeInBytes ();
536
+ intptr_t float_only_chunks = 0 ;
537
+ for (intptr_t offset = 0 ; offset < size;
538
+ offset += compiler::target::kWordSize ) {
539
+ if (ContainsOnlyFloats (
540
+ offset, Utils::Minimum<intptr_t >(size - offset,
541
+ compiler::target::kWordSize ))) {
542
+ float_only_chunks++;
543
+ }
544
+ }
545
+ return float_only_chunks;
546
+ }
547
+
548
+ intptr_t NativeCompoundType::NumberOfWordSizeChunksNotOnlyFloat () const {
549
+ const intptr_t total_chunks =
550
+ Utils::RoundUp (SizeInBytes (), compiler::target::kWordSize ) /
551
+ compiler::target::kWordSize ;
552
+ return total_chunks - NumberOfWordSizeChunksOnlyFloat ();
553
+ }
554
+
555
+ static void ContainsHomogenuousFloatsRecursive (const NativeTypes& types,
556
+ bool * only_float,
557
+ bool * only_double) {
558
+ for (intptr_t i = 0 ; i < types.length (); i++) {
559
+ const auto & member_type = types.At (i);
560
+ if (member_type->IsPrimitive ()) {
561
+ PrimitiveType type = member_type->AsPrimitive ().representation ();
562
+ *only_float = *only_float && (type == kFloat );
563
+ *only_double = *only_double && (type == kDouble );
564
+ }
565
+ if (member_type->IsCompound ()) {
566
+ ContainsHomogenuousFloatsRecursive (member_type->AsCompound ().members (),
567
+ only_float, only_double);
568
+ }
569
+ }
570
+ }
571
+
572
+ static bool ContainsHomogenuousFloatsInternal (const NativeTypes& types) {
573
+ bool only_float = true ;
574
+ bool only_double = true ;
575
+ ContainsHomogenuousFloatsRecursive (types, &only_float, &only_double);
576
+ return (only_double || only_float) && types.length () > 0 ;
577
+ }
578
+
579
+ bool NativeCompoundType::ContainsHomogenuousFloats () const {
580
+ return ContainsHomogenuousFloatsInternal (this ->members ());
581
+ }
582
+
359
583
const NativeType& NativeType::WidenTo4Bytes (Zone* zone) const {
360
584
if (IsInt () && SizeInBytes () <= 2 ) {
361
585
if (IsSigned ()) {
0 commit comments