Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
native_type.cc
Go to the documentation of this file.
1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
6
7#include "platform/assert.h"
8#include "platform/globals.h"
9#include "vm/class_id.h"
10#include "vm/compiler/ffi/abi.h"
12#include "vm/constants.h"
13#include "vm/zone_text_buffer.h"
14
15#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
17#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
18
19#if !defined(FFI_UNIT_TESTS)
20#include "vm/symbols.h"
21#endif
22
23namespace dart {
24
25namespace compiler {
26
27namespace ffi {
28
30 ASSERT(size <= 8);
31 ASSERT(size > 0);
32 switch (size) {
33 case 1:
34 return kUint8;
35 case 2:
36 return kUint16;
37 case 4:
38 return kUint32;
39 case 8:
40 // Dart unboxed Representation for unsigned and signed is equal.
41 return kInt64;
42 }
44}
45
48 return static_cast<const NativePrimitiveType&>(*this);
49}
50
52 ASSERT(IsArray());
53 return static_cast<const NativeArrayType&>(*this);
54}
55
58 return static_cast<const NativeCompoundType&>(*this);
59}
60
63 return static_cast<const NativeStructType&>(*this);
64}
65
67 switch (representation_) {
68 case kInt8:
69 case kUint8:
70 case kInt16:
71 case kUint16:
72 case kInt24:
73 case kUint24:
74 case kInt32:
75 case kUint32:
76 case kInt40:
77 case kUint40:
78 case kInt48:
79 case kUint48:
80 case kInt56:
81 case kUint56:
82 case kInt64:
83 case kUint64:
84 return true;
85 default:
86 return false;
87 }
88}
89
91 return representation_ == kFloat || representation_ == kDouble ||
92 representation_ == kHalfDouble;
93}
94
96 return representation_ == kVoid;
97}
98
100 ASSERT(IsInt() || IsFloat());
101 switch (representation_) {
102 case kInt8:
103 case kInt16:
104 case kInt24:
105 case kInt32:
106 case kInt40:
107 case kInt48:
108 case kInt56:
109 case kInt64:
110 case kFloat:
111 case kDouble:
112 case kHalfDouble:
113 return true;
114 case kUint8:
115 case kUint16:
116 case kUint24:
117 case kUint32:
118 case kUint40:
119 case kUint48:
120 case kUint56:
121 case kUint64:
122 default:
123 return false;
124 }
125}
126
127static const intptr_t fundamental_size_in_bytes[kVoid + 1] = {
128 1, // kInt8,
129 1, // kUint8,
130 2, // kInt16,
131 2, // kUint16,
132 4, // kInt32,
133 4, // kUint32,
134 8, // kInt64,
135 8, // kUint64,
136 4, // kFloat,
137 8, // kDouble,
138 4, // kHalfDouble
139 3, // kInt24,
140 3, // kUint24,
141 5, // kInt40,
142 5, // kUint40,
143 6, // kInt48,
144 6, // kUint48,
145 7, // kInt56,
146 7, // kUint56,
147 0, // kVoid,
148};
149
151 return fundamental_size_in_bytes[representation_];
152}
153
154intptr_t NativePrimitiveType::AlignmentInBytesStack(bool is_vararg) const {
155 const auto alignment =
158 switch (alignment) {
160 // The default is to align stack arguments to word size.
161 return compiler::target::kWordSize;
163 // However, arm32+riscv32 align to the greater of word size or value size.
165 static_cast<intptr_t>(compiler::target::kWordSize));
167 // iOS on arm64 only aligns to size.
168 return SizeInBytes();
169 default:
171 }
172}
173
177 // The default is to align fields to their own size.
178 return SizeInBytes();
180 // However, on some 32-bit architectures, 8-byte fields are only aligned
181 // to 4 bytes.
182 if (SizeInBytes() == 8) {
183 return 4;
184 }
185 return SizeInBytes();
186 }
187 default:
189 }
190}
191
192static bool ContainsHomogeneousFloatsInternal(const NativeTypes& types);
193
194// Keep consistent with
195// pkg/vm/lib/transformations/ffi_definitions.dart:StructLayout:_calculateLayout.
197 const NativeTypes& members,
198 intptr_t member_packing) {
199 intptr_t offset = 0;
200
201 const intptr_t kAtLeast1ByteAligned = 1;
202 // If this struct is nested in another struct, it should be aligned to the
203 // largest alignment of its members.
204 intptr_t alignment_field = kAtLeast1ByteAligned;
205 // If this struct is passed on the stack, it should be aligned to the largest
206 // alignment of its members when passing those members on the stack.
207 intptr_t alignment_stack = kAtLeast1ByteAligned;
208 intptr_t alignment_stack_vararg = kAtLeast1ByteAligned;
209#if (defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS)) && \
210 defined(TARGET_ARCH_ARM64)
211 // On iOS64 and MacOS arm64 stack values can be less aligned than wordSize,
212 // which deviates from the arm64 ABI.
214 // Because the arm64 ABI aligns primitives to word size on the stack, every
215 // struct will be automatically aligned to word size. iOS64 does not align
216 // the primitives to word size, so we set structs to align to word size for
217 // iOS64.
218 // However, homogenous structs are treated differently. They are aligned to
219 // their member alignment. (Which is 4 in case of a homogenous float).
220 // Source: manual testing.
222 alignment_stack = compiler::target::kWordSize;
223 }
224 alignment_stack_vararg = compiler::target::kWordSize;
225#endif
226
227 auto& member_offsets =
228 *new (zone) ZoneGrowableArray<intptr_t>(zone, members.length());
229 for (intptr_t i = 0; i < members.length(); i++) {
230 const NativeType& member = *members[i];
231 const intptr_t member_size = member.SizeInBytes();
232 const intptr_t member_align_field =
233 Utils::Minimum(member.AlignmentInBytesField(), member_packing);
234 intptr_t member_align_stack = member.AlignmentInBytesStack();
235 if (member_align_stack > member_packing &&
236 member_packing < compiler::target::kWordSize) {
237 member_align_stack = compiler::target::kWordSize;
238 }
239 offset = Utils::RoundUp(offset, member_align_field);
241 offset += member_size;
242 alignment_field = Utils::Maximum(alignment_field, member_align_field);
243 alignment_stack = Utils::Maximum(alignment_stack, member_align_stack);
244 alignment_stack_vararg =
245 Utils::Maximum(alignment_stack_vararg, member_align_stack);
246 }
247 const intptr_t size = Utils::RoundUp(offset, alignment_field);
248
249 return *new (zone)
250 NativeStructType(members, member_offsets, size, alignment_field,
251 alignment_stack, alignment_stack_vararg);
252}
253
254// Keep consistent with
255// pkg/vm/lib/transformations/ffi_definitions.dart:StructLayout:_calculateLayout.
257 const NativeTypes& members) {
258 intptr_t size = 0;
259
260 const intptr_t kAtLeast1ByteAligned = 1;
261 // If this union is nested in a struct, it should be aligned to the
262 // largest alignment of its members.
263 intptr_t alignment_field = kAtLeast1ByteAligned;
264 // If this union is passed on the stack, it should be aligned to the largest
265 // alignment of its members when passing those members on the stack.
266 intptr_t alignment_stack = kAtLeast1ByteAligned;
267
268 for (intptr_t i = 0; i < members.length(); i++) {
269 const NativeType& member = *members[i];
270 const intptr_t member_size = member.SizeInBytes();
271 const intptr_t member_align_field = member.AlignmentInBytesField();
272 const intptr_t member_align_stack = member.AlignmentInBytesStack();
273 size = Utils::Maximum(size, member_size);
274 alignment_field = Utils::Maximum(alignment_field, member_align_field);
275 alignment_stack = Utils::Maximum(alignment_stack, member_align_stack);
276 }
277 size = Utils::RoundUp(size, alignment_field);
278
279 return *new (zone)
280 NativeUnionType(members, size, alignment_field, alignment_stack);
281}
282
283#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
285 switch (representation_) {
286 case kInt24:
287 case kUint24:
288 case kInt40:
289 case kUint40:
290 case kInt48:
291 case kUint48:
292 case kInt56:
293 case kUint56:
294 case kHalfDouble:
295 return false;
296 case kInt8:
297 case kUint8:
298 case kInt16:
299 case kUint16:
300 case kInt32:
301 case kUint32:
302 case kInt64:
303 case kUint64: // We don't actually have a kUnboxedUint64.
304 case kFloat:
305 case kDouble:
306 case kVoid:
307 return true;
308 default:
309 UNREACHABLE_THIS(); // Make MSVC happy.
310 }
311}
312
315 switch (representation_) {
316 case kInt8:
317 return kUnboxedInt8;
318 case kUint8:
319 return kUnboxedUint8;
320 case kInt16:
321 return kUnboxedInt16;
322 case kUint16:
323 return kUnboxedUint16;
324 case kInt32:
325 return kUnboxedInt32;
326 case kUint32:
327 return kUnboxedUint32;
328 case kInt64:
329 case kUint64:
330 return kUnboxedInt64;
331 case kFloat:
332 return kUnboxedFloat;
333 case kDouble:
334 return kUnboxedDouble;
335 case kVoid:
336 return kUnboxedIntPtr;
337 default:
339 }
340}
341#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
342
343bool NativePrimitiveType::Equals(const NativeType& other) const {
344 if (!other.IsPrimitive()) {
345 return false;
346 }
347 return other.AsPrimitive().representation_ == representation_;
348}
349
350bool NativeArrayType::Equals(const NativeType& other) const {
351 if (!other.IsArray()) {
352 return false;
353 }
354 return other.AsArray().length_ == length_ &&
355 other.AsArray().element_type_.Equals(element_type_);
356}
357
358bool NativeCompoundType::Equals(const NativeType& other) const {
359 if (!other.IsCompound()) {
360 return false;
361 }
362 const auto& other_compound = other.AsCompound();
363 const auto& other_members = other_compound.members_;
364 if (other_members.length() != members_.length()) {
365 return false;
366 }
367 for (intptr_t i = 0; i < members_.length(); i++) {
368 if (!members_[i]->Equals(*other_members[i])) {
369 return false;
370 }
371 }
372 return true;
373}
374
376 switch (in) {
377 case kInt16:
378 return kInt8;
379 case kInt32:
380 return kInt16;
381 case kInt64:
382 return kInt32;
383 case kUint16:
384 return kUint8;
385 case kUint32:
386 return kUint16;
387 case kUint64:
388 return kUint32;
389 case kDouble:
390 return kHalfDouble;
391 default:
392 UNREACHABLE();
393 }
394}
395
397 intptr_t index) const {
398 ASSERT(index == 0 || index == 1);
399 auto new_rep = split_fundamental(representation());
400 return *new (zone) NativePrimitiveType(new_rep);
401}
402
404 switch (class_id) {
405 case kFfiInt8Cid:
406 return kInt8;
407 case kFfiInt16Cid:
408 return kInt16;
409 case kFfiInt32Cid:
410 return kInt32;
411 case kFfiBoolCid:
412 case kFfiUint8Cid:
413 return kUint8;
414 case kFfiUint16Cid:
415 return kUint16;
416 case kFfiUint32Cid:
417 return kUint32;
418 case kFfiInt64Cid:
419 case kFfiUint64Cid:
420 return kInt64;
421 case kFfiFloatCid:
422 return kFloat;
423 case kFfiDoubleCid:
424 return kDouble;
425 case kPointerCid:
426 case kFfiHandleCid:
427 return kAddress;
428 case kFfiVoidCid:
429 return kVoid;
430 default:
431 UNREACHABLE();
432 }
433}
434
436 classid_t class_id) {
438 const auto fundamental_rep = TypeRepresentation(class_id);
439 return *new (zone) NativePrimitiveType(fundamental_rep);
440}
441
442#if !defined(FFI_UNIT_TESTS)
444 const Instance& pragma,
445 bool is_struct,
446 const char** error) {
447 const auto& struct_layout = pragma;
448 const auto& clazz = Class::Handle(zone, struct_layout.clazz());
449 ASSERT(String::Handle(zone, clazz.UserVisibleName())
450 .Equals(Symbols::FfiStructLayout()));
451 const auto& struct_layout_fields = Array::Handle(zone, clazz.fields());
452 ASSERT(struct_layout_fields.Length() == 2);
453 const auto& types_field =
454 Field::Handle(zone, Field::RawCast(struct_layout_fields.At(0)));
455 ASSERT(String::Handle(zone, types_field.name())
456 .Equals(Symbols::FfiFieldTypes()));
457 const auto& field_types =
458 Array::Handle(zone, Array::RawCast(struct_layout.GetField(types_field)));
459 const auto& packed_field =
460 Field::Handle(zone, Field::RawCast(struct_layout_fields.At(1)));
461 ASSERT(String::Handle(zone, packed_field.name())
462 .Equals(Symbols::FfiFieldPacking()));
463 const auto& packed_value = Integer::Handle(
464 zone, Integer::RawCast(struct_layout.GetField(packed_field)));
465 const intptr_t member_packing =
466 packed_value.IsNull() ? kMaxInt32 : packed_value.AsInt64Value();
467
468 auto& field_instance = Instance::Handle(zone);
469 auto& field_type = AbstractType::Handle(zone);
470 auto& field_native_types = *new (zone) ZoneGrowableArray<const NativeType*>(
471 zone, field_types.Length());
472 for (intptr_t i = 0; i < field_types.Length(); i++) {
473 field_instance ^= field_types.At(i);
474 if (field_instance.IsAbstractType()) {
475 // Subtype of NativeType: Struct, native integer or native float.
476 field_type ^= field_types.At(i);
477 const auto& field_native_type =
478 NativeType::FromAbstractType(zone, field_type, error);
479 if (*error != nullptr) {
480 return nullptr;
481 }
482 field_native_types.Add(field_native_type);
483 } else {
484 // Inline array.
485 const auto& struct_layout_array_class =
486 Class::Handle(zone, field_instance.clazz());
487 ASSERT(String::Handle(zone, struct_layout_array_class.UserVisibleName())
488 .Equals(Symbols::FfiStructLayoutArray()));
489 const auto& struct_layout_array_fields =
490 Array::Handle(zone, struct_layout_array_class.fields());
491 ASSERT(struct_layout_array_fields.Length() == 2);
492 const auto& element_type_field =
493 Field::Handle(zone, Field::RawCast(struct_layout_array_fields.At(0)));
494 ASSERT(String::Handle(zone, element_type_field.UserVisibleName())
495 .Equals(Symbols::FfiElementType()));
496 field_type ^= field_instance.GetField(element_type_field);
497 const auto& length_field =
498 Field::Handle(zone, Field::RawCast(struct_layout_array_fields.At(1)));
499 ASSERT(String::Handle(zone, length_field.UserVisibleName())
500 .Equals(Symbols::Length()));
501 const auto& length = Smi::Handle(
502 zone, Smi::RawCast(field_instance.GetField(length_field)));
503 const auto element_type =
504 NativeType::FromAbstractType(zone, field_type, error);
505 if (*error != nullptr) {
506 return nullptr;
507 }
508 const auto field_native_type =
509 new (zone) NativeArrayType(*element_type, length.AsInt64Value());
510 field_native_types.Add(field_native_type);
511 }
512 }
513
514 if (is_struct) {
515 return &NativeStructType::FromNativeTypes(zone, field_native_types,
516 member_packing);
517 } else {
518 return &NativeUnionType::FromNativeTypes(zone, field_native_types);
519 }
520}
521
523 const Instance& pragma,
524 const Class& abi_specific_int,
525 const char** error) {
526 const auto& clazz = Class::Handle(zone, pragma.clazz());
527 const auto& fields = Array::Handle(zone, clazz.fields());
528 ASSERT(fields.Length() == 1);
529 const auto& native_types_field =
530 Field::Handle(zone, Field::RawCast(fields.At(0)));
531 ASSERT(String::Handle(zone, native_types_field.name())
532 .Equals(Symbols::FfiNativeTypes()));
533 const auto& native_types =
534 Array::Handle(zone, Array::RawCast(pragma.GetField(native_types_field)));
535
536 ASSERT(native_types.Length() == num_abis);
537 const int64_t abi_index = static_cast<int64_t>(TargetAbi());
538 const auto& abi_abstract_type = AbstractType::Handle(
539 zone, AbstractType::RawCast(native_types.At(abi_index)));
540 if (abi_abstract_type.IsNull()) {
541 *error = zone->PrintToString(
542 "AbiSpecificInteger '%s' is missing mapping for '%s'.",
543 abi_specific_int.UserVisibleNameCString(), target_abi_name);
544 return nullptr;
545 }
546 return NativeType::FromAbstractType(zone, abi_abstract_type, error);
547}
548
550 const AbstractType& type,
551 const char** error) {
552 const classid_t class_id = type.type_class_id();
553 if (IsFfiPredefinedClassId(class_id)) {
554 return &NativeType::FromTypedDataClassId(zone, class_id);
555 }
556
557 // User-defined structs, unions, or Abi-specific integers.
558 const auto& cls = Class::Handle(zone, type.type_class());
559 const auto& superClass = Class::Handle(zone, cls.SuperClass());
560 const bool is_struct = String::Handle(zone, superClass.UserVisibleName())
561 .Equals(Symbols::Struct());
562 const bool is_union = String::Handle(zone, superClass.UserVisibleName())
563 .Equals(Symbols::Union());
564 const bool is_abi_specific_int =
565 String::Handle(zone, superClass.UserVisibleName())
566 .Equals(Symbols::AbiSpecificInteger());
567 RELEASE_ASSERT(is_struct || is_union || is_abi_specific_int);
568
569 auto& pragmas = Object::Handle(zone);
570 String& pragma_name = String::Handle(zone);
571 if (is_struct || is_union) {
572 pragma_name = Symbols::vm_ffi_struct_fields().ptr();
573 } else {
574 ASSERT(is_abi_specific_int);
575 pragma_name = Symbols::vm_ffi_abi_specific_mapping().ptr();
576 }
577 Library::FindPragma(dart::Thread::Current(), /*only_core=*/false, cls,
578 pragma_name, /*multiple=*/true, &pragmas);
579 ASSERT(!pragmas.IsNull());
580 ASSERT(pragmas.IsGrowableObjectArray());
581 const auto& pragmas_array = GrowableObjectArray::Cast(pragmas);
582 auto& pragma = Instance::Handle(zone);
583 auto& clazz = Class::Handle(zone);
584 auto& library = Library::Handle(zone);
585 String& class_symbol = String::Handle(zone);
586 if (is_struct || is_union) {
587 class_symbol = Symbols::FfiStructLayout().ptr();
588 } else {
589 ASSERT(is_abi_specific_int);
590 class_symbol = Symbols::FfiAbiSpecificMapping().ptr();
591 }
592 for (intptr_t i = 0; i < pragmas_array.Length(); i++) {
593 pragma ^= pragmas_array.At(i);
594 clazz ^= pragma.clazz();
595 library ^= clazz.library();
596 if (String::Handle(zone, clazz.UserVisibleName()).Equals(class_symbol) &&
597 String::Handle(zone, library.url()).Equals(Symbols::DartFfi())) {
598 break;
599 }
600 }
601
602 if (is_struct || is_union) {
603 return CompoundFromPragma(zone, pragma, is_struct, error);
604 }
605 ASSERT(is_abi_specific_int);
606 return AbiSpecificFromPragma(zone, pragma, cls, error);
607}
608#endif
609
610#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
612 switch (rep) {
613 case kUnboxedDouble:
614 return kDouble;
615 case kUnboxedFloat:
616 return kFloat;
617 case kUnboxedInt32:
618 return kInt32;
619 case kUnboxedUint32:
620 return kUint32;
621 case kUnboxedInt64:
622 return kInt64;
623 case kUntagged:
624 case kTagged:
625 return TypeRepresentation(kPointerCid);
626 default:
627 break;
628 }
629 FATAL("Unhandled representation %u", rep);
630}
631
636
638 Zone* zone,
639 Representation return_representation,
640 const ZoneGrowableArray<Representation>& argument_representations) {
641 const auto& return_type =
642 NativePrimitiveType::FromRepresentation(zone, return_representation);
643 auto& argument_types =
645 zone, argument_representations.length());
646 for (intptr_t i = 0; i < argument_representations.length(); i++) {
648 zone, argument_representations.At(i)));
649 }
651}
652#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
653
654const char* NativeType::ToCString(Zone* zone,
655 bool multi_line,
656 bool verbose) const {
657 ZoneTextBuffer textBuffer(zone);
658 PrintTo(&textBuffer, multi_line, verbose);
659 return textBuffer.buffer();
660}
661
662#if !defined(FFI_UNIT_TESTS)
663const char* NativeType::ToCString() const {
664 return ToCString(Thread::Current()->zone());
665}
666#endif
667
668static const char* PrimitiveTypeToCString(PrimitiveType rep) {
669 switch (rep) {
670 case kInt8:
671 return "int8";
672 case kUint8:
673 return "uint8";
674 case kInt16:
675 return "int16";
676 case kUint16:
677 return "uint16";
678 case kInt32:
679 return "int32";
680 case kUint32:
681 return "uint32";
682 case kInt64:
683 return "int64";
684 case kUint64:
685 return "uint64";
686 case kFloat:
687 return "float";
688 case kDouble:
689 return "double";
690 case kHalfDouble:
691 return "half-double";
692 case kInt24:
693 return "int24";
694 case kUint24:
695 return "uint24";
696 case kInt40:
697 return "int40";
698 case kUint40:
699 return "uint40";
700 case kInt48:
701 return "int48";
702 case kUint48:
703 return "uint48";
704 case kInt56:
705 return "int56";
706 case kUint56:
707 return "uint56";
708 case kVoid:
709 return "void";
710 default:
711 UNREACHABLE();
712 }
713}
714
716 bool multi_line,
717 bool verbose) const {
718 f->AddString("I");
719}
720
722 bool multi_line,
723 bool verbose) const {
724 f->Printf("%s", PrimitiveTypeToCString(representation_));
725}
726
727const char* NativeFunctionType::ToCString(Zone* zone) const {
728 ZoneTextBuffer textBuffer(zone);
729 PrintTo(&textBuffer);
730 return textBuffer.buffer();
731}
732
734 bool multi_line,
735 bool verbose) const {
736 f->AddString("Array(");
737 f->Printf("element type: ");
738 element_type_.PrintTo(f, /*multi_line*/ false, verbose);
739 f->Printf(", length: %" Pd "", length_);
740 f->AddString(")");
741}
742
744 bool multi_line,
745 bool verbose) const {
747 f->AddString("(");
748 f->Printf("size: %" Pd "", SizeInBytes());
749 if (verbose) {
750 f->Printf(", field alignment: %" Pd ", ", AlignmentInBytesField());
751 f->Printf("stack alignment: %" Pd ", ", AlignmentInBytesStack());
752 f->AddString("members: {");
753 if (multi_line) {
754 f->AddString("\n ");
755 }
756 for (intptr_t i = 0; i < members_.length(); i++) {
757 if (i > 0) {
758 if (multi_line) {
759 f->AddString(",\n ");
760 } else {
761 f->AddString(", ");
762 }
763 }
764 PrintMemberOffset(f, i);
765 members_[i]->PrintTo(f);
766 }
767 if (multi_line) {
768 f->AddString("\n");
769 }
770 f->AddString("}");
771 }
772 f->AddString(")");
773 if (multi_line) {
774 f->AddString("\n");
775 }
776}
777
779 f->AddString("Struct");
780}
781
783 f->AddString("Union");
784}
785
787 intptr_t member_index) const {
788 f->Printf("%" Pd ": ", member_offsets_[member_index]);
789}
790
791#if !defined(FFI_UNIT_TESTS)
792const char* NativeFunctionType::ToCString() const {
793 return ToCString(Thread::Current()->zone());
794}
795#endif
796
798 f->AddString("(");
799 for (intptr_t i = 0; i < argument_types_.length(); i++) {
800 if (i > 0) {
801 f->AddString(", ");
802 }
803 if (i == variadic_arguments_index_) {
804 f->AddString("varargs: ");
805 }
806 argument_types_[i]->PrintTo(f);
807 }
808 f->AddString(") => ");
809 return_type_.PrintTo(f);
810}
811
813 return 1;
814}
815
817 return element_type_.NumPrimitiveMembersRecursive() * length_;
818}
819
821 intptr_t count = 0;
822 for (intptr_t i = 0; i < members_.length(); i++) {
823 count += members_[i]->NumPrimitiveMembersRecursive();
824 }
825 return count;
826}
827
829 intptr_t count = 0;
830 for (intptr_t i = 0; i < members_.length(); i++) {
832 }
833 return count;
834}
835
839
843
846 for (intptr_t i = 0; i < members().length(); i++) {
847 if (members_[i]->NumPrimitiveMembersRecursive() >= 1) {
848 return members_[i]->FirstPrimitiveMember();
849 }
850 }
852}
853
855 const NativePrimitiveType** first,
856 const NativePrimitiveType** second,
857 intptr_t offset_in_members) const {
858 if (offset_in_members == 0) *first = this;
859 if (offset_in_members == 1) *second = this;
860 return offset_in_members + 1;
861}
862
864 const NativePrimitiveType** first,
865 const NativePrimitiveType** second,
866 intptr_t offset_in_members) const {
867 for (intptr_t i = 0; i < length_; i++) {
868 offset_in_members =
869 element_type_.PrimitivePairMembers(first, second, offset_in_members);
870 }
871 return offset_in_members;
872}
873
875 const NativePrimitiveType** first,
876 const NativePrimitiveType** second,
877 intptr_t offset_in_members) const {
878 for (intptr_t i = 0; i < members().length(); i++) {
879 offset_in_members =
880 members_[i]->PrimitivePairMembers(first, second, offset_in_members);
881 }
882 return offset_in_members;
883}
884
885#if !defined(DART_PRECOMPILED_RUNTIME)
887 const auto this_range = Range::StartAndEnd(0, SizeInBytes());
888 ASSERT(this_range.Contains(range));
889
890 return IsFloat();
891}
892
894 const auto this_range = Range::StartAndEnd(0, SizeInBytes());
895 ASSERT(this_range.Contains(range));
896
897 const intptr_t element_size_in_bytes = element_type_.SizeInBytes();
898
899 // Assess how many elements are (partially) covered by the range.
900 const intptr_t first_element_start = range.start() / element_size_in_bytes;
901 const intptr_t last_element_index =
902 range.end_inclusive() / element_size_in_bytes;
903 const intptr_t num_elements = last_element_index - first_element_start + 1;
904 ASSERT(num_elements >= 1);
905
906 if (num_elements > 2) {
907 // At least one full element covered.
908 return element_type_.ContainsOnlyFloats(
909 Range::StartAndLength(0, element_size_in_bytes));
910 }
911
912 // Check first element, which falls (partially) in range.
913 const intptr_t first_start = first_element_start * element_size_in_bytes;
914 const auto first_range =
915 Range::StartAndLength(first_start, element_size_in_bytes);
916 const auto first_range_clipped = range.Intersect(first_range);
917 const auto range_in_first = first_range_clipped.Translate(-first_start);
918 if (!element_type_.ContainsOnlyFloats(range_in_first)) {
919 // First element contains not only floats in specified range.
920 return false;
921 }
922
923 if (num_elements == 2) {
924 // Check the second (and last) element, which falls (partially) in range.
925 const intptr_t second_element_index = first_element_start + 1;
926 const intptr_t second_start = second_element_index * element_size_in_bytes;
927 const auto second_range =
928 Range::StartAndLength(second_start, element_size_in_bytes);
929 const auto second_range_clipped = range.Intersect(second_range);
930 const auto range_in_second = second_range_clipped.Translate(-second_start);
931 return element_type_.ContainsOnlyFloats(range_in_second);
932 }
933
934 return true;
935}
936
938 const auto this_range = Range::StartAndEnd(0, SizeInBytes());
939 ASSERT(this_range.Contains(range));
940
941 for (intptr_t i = 0; i < members_.length(); i++) {
942 const auto& member = *members_[i];
943 const intptr_t member_offset = member_offsets_[i];
944 const intptr_t member_size = member.SizeInBytes();
945 const auto member_range = Range::StartAndLength(member_offset, member_size);
946 if (range.Overlaps(member_range)) {
947 const auto member_range_clipped = member_range.Intersect(range);
948 const auto range_in_member =
949 member_range_clipped.Translate(-member_offset);
950 if (!member.ContainsOnlyFloats(range_in_member)) {
951 // Member contains not only floats in specified range.
952 return false;
953 }
954 }
955 if (member_range.After(range)) {
956 // None of the remaining members fits the range.
957 break;
958 }
959 }
960 return true;
961}
962
964 for (intptr_t i = 0; i < members_.length(); i++) {
965 const auto& member = *members_[i];
966 const intptr_t member_size = member.SizeInBytes();
967 const auto member_range = Range::StartAndLength(0, member_size);
968 if (member_range.Overlaps(range)) {
969 const auto member_range_clipped = member_range.Intersect(range);
970 if (!member.ContainsOnlyFloats(member_range_clipped)) {
971 return false;
972 }
973 }
974 }
975 return true;
976}
977
979 // O(n^2) implementation, but only invoked for small structs.
980 ASSERT(SizeInBytes() <= 16);
981 const auto this_range = Range::StartAndEnd(0, SizeInBytes());
982 const intptr_t size = SizeInBytes();
983 intptr_t float_only_chunks = 0;
984 for (intptr_t offset = 0; offset < size;
985 offset += compiler::target::kWordSize) {
986 const auto chunk_range =
987 Range::StartAndLength(offset, compiler::target::kWordSize);
988 if (ContainsOnlyFloats(chunk_range.Intersect(this_range))) {
989 float_only_chunks++;
990 }
991 }
992 return float_only_chunks;
993}
994
996 const intptr_t total_chunks =
997 Utils::RoundUp(SizeInBytes(), compiler::target::kWordSize) /
998 compiler::target::kWordSize;
999 return total_chunks - NumberOfWordSizeChunksOnlyFloat();
1000}
1001#endif // !defined(DART_PRECOMPILED_RUNTIME)
1002
1006
1008 const intptr_t element_size = element_type_.SizeInBytes();
1009 // We're only checking the first two elements of the array.
1010 //
1011 // If the element size is divisible by the alignment of the largest type
1012 // contained within the element type, the alignment of all elements is the
1013 // same. If not, either the first or the second element is unaligned.
1014 const intptr_t max_check = 2;
1015 for (intptr_t i = 0; i < Utils::Minimum(length_, max_check); i++) {
1016 const intptr_t element_offset = i * element_size;
1017 if (element_type_.ContainsUnalignedMembers(offset + element_offset)) {
1018 return true;
1019 }
1020 }
1021 return false;
1022}
1023
1025 for (intptr_t i = 0; i < members_.length(); i++) {
1026 const auto& member = *members_.At(i);
1027 const intptr_t member_offset = member_offsets_.At(i);
1028 if (member.ContainsUnalignedMembers(offset + member_offset)) {
1029 return true;
1030 }
1031 }
1032 return false;
1033}
1034
1036 for (intptr_t i = 0; i < members_.length(); i++) {
1037 const auto& member = *members_.At(i);
1038 if (member.ContainsUnalignedMembers(offset)) {
1039 return true;
1040 }
1041 }
1042 return false;
1043}
1044
1046 bool* only_float,
1047 bool* only_double) {
1048 for (intptr_t i = 0; i < types.length(); i++) {
1049 const auto& type = *types.At(i);
1050 const auto& member_type =
1051 type.IsArray() ? type.AsArray().element_type() : type;
1052 if (member_type.IsPrimitive()) {
1053 PrimitiveType type = member_type.AsPrimitive().representation();
1054 *only_float = *only_float && (type == kFloat);
1055 *only_double = *only_double && (type == kDouble);
1056 }
1057 if (member_type.IsCompound()) {
1058 ContainsHomogeneousFloatsRecursive(member_type.AsCompound().members(),
1059 only_float, only_double);
1060 }
1061 }
1062}
1063
1065 bool only_float = true;
1066 bool only_double = true;
1067 ContainsHomogeneousFloatsRecursive(types, &only_float, &only_double);
1068 return (only_double || only_float) && types.length() > 0;
1069}
1070
1074
1075#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
1077 if (IsPrimitive()) {
1078 switch (AsPrimitive().representation()) {
1079 case kInt24:
1080 return kUnboxedInt32;
1081 case kUint24:
1082 return kUnboxedUint32;
1083 case kInt40:
1084 case kUint40:
1085 case kInt48:
1086 case kUint48:
1087 case kInt56:
1088 case kUint56:
1089 return kUnboxedInt64;
1090 default:
1091 break;
1092 }
1093 }
1094 const auto& widened = WidenTo4Bytes(zone_);
1095 return widened.AsRepresentation();
1096}
1097#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
1098
1100 if (IsInt() && SizeInBytes() <= 2) {
1101 if (IsSigned()) {
1102 return *new (zone) NativePrimitiveType(kInt32);
1103 } else {
1104 return *new (zone) NativePrimitiveType(kUint32);
1105 }
1106 }
1107 return *this;
1108}
1109
1111 if (IsInt() && SizeInBytes() <= 4) {
1112 if (IsSigned()) {
1113 return *new (zone) NativePrimitiveType(kInt64);
1114 } else {
1115 return *new (zone) NativePrimitiveType(kUint64);
1116 }
1117 }
1118 return *this;
1119}
1120
1121} // namespace ffi
1122
1123} // namespace compiler
1124
1125} // namespace dart
int count
static size_t element_size(Layout layout, SkSLType type)
#define UNREACHABLE()
Definition assert.h:248
#define RELEASE_ASSERT(cond)
Definition assert.h:327
void Add(const T &value)
const T & At(intptr_t index) const
intptr_t length() const
char * buffer() const
Definition text_buffer.h:35
static constexpr AlignmentStrategy kFieldAlignment
static constexpr AlignmentStrategy kArgumentStackAlignmentVarArgs
static constexpr AlignmentStrategy kArgumentStackAlignment
const char * UserVisibleNameCString() const
Definition object.cc:3059
ObjectPtr GetField(const Field &field) const
Definition object.cc:20516
static bool FindPragma(Thread *T, bool only_core, const Object &object, const String &pragma_name, bool multiple=false, Object *options=nullptr)
Definition object.cc:4201
ObjectPtr ptr() const
Definition object.h:332
static Object & Handle()
Definition object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition object.h:325
ClassPtr clazz() const
Definition object.h:13192
static Thread * Current()
Definition thread.h:361
static constexpr T Maximum(T x, T y)
Definition utils.h:26
static T Minimum(T x, T y)
Definition utils.h:21
static constexpr T RoundUp(T x, uintptr_t alignment, uintptr_t offset=0)
Definition utils.h:105
char * PrintToString(const char *format,...) PRINTF_ATTRIBUTE(2
Definition zone.cc:313
virtual void PrintTo(BaseTextBuffer *f, bool multi_line=false, bool verbose=true) const
virtual intptr_t SizeInBytes() const
virtual bool Equals(const NativeType &other) const
virtual bool ContainsOnlyFloats(Range range) const
virtual bool ContainsUnalignedMembers(intptr_t offset=0) const
virtual intptr_t PrimitivePairMembers(const NativePrimitiveType **first, const NativePrimitiveType **second, intptr_t offset_in_members=0) const
virtual const NativePrimitiveType & FirstPrimitiveMember() const
virtual intptr_t NumPrimitiveMembersRecursive() const
virtual intptr_t NumPrimitiveMembersRecursive() const =0
const NativeTypes & members() const
virtual bool Equals(const NativeType &other) const
virtual intptr_t SizeInBytes() const
virtual intptr_t AlignmentInBytesStack(bool is_vararg=false) const
virtual intptr_t PrimitivePairMembers(const NativePrimitiveType **first, const NativePrimitiveType **second, intptr_t offset_in_members=0) const
virtual const NativePrimitiveType & FirstPrimitiveMember() const
virtual bool ContainsOnlyFloats(Range range) const =0
intptr_t NumberOfWordSizeChunksNotOnlyFloat() const
virtual void PrintMemberOffset(BaseTextBuffer *f, intptr_t member_index) const
virtual intptr_t AlignmentInBytesField() const
virtual void PrintTo(BaseTextBuffer *f, bool multi_line=false, bool verbose=true) const
virtual void PrintCompoundType(BaseTextBuffer *f) const =0
static const NativeFunctionType * FromRepresentations(Zone *zone, Representation return_representation, const ZoneGrowableArray< Representation > &argument_representations)
const NativeTypes & argument_types() const
void PrintTo(BaseTextBuffer *f) const
const NativeType & return_type() const
virtual Representation AsRepresentation() const
virtual const NativePrimitiveType & FirstPrimitiveMember() const
virtual NativePrimitiveType & Split(Zone *zone, intptr_t part) const
virtual bool ContainsOnlyFloats(Range range) const
virtual bool Equals(const NativeType &other) const
virtual bool ContainsUnalignedMembers(intptr_t offset=0) const
virtual intptr_t PrimitivePairMembers(const NativePrimitiveType **first, const NativePrimitiveType **second, intptr_t offset_in_members=0) const
virtual bool IsExpressibleAsRepresentation() const
virtual intptr_t NumPrimitiveMembersRecursive() const
virtual intptr_t AlignmentInBytesStack(bool is_vararg=false) const
virtual intptr_t AlignmentInBytesField() const
virtual void PrintTo(BaseTextBuffer *f, bool multi_line=false, bool verbose=true) const
virtual bool ContainsOnlyFloats(Range range) const
static NativeStructType & FromNativeTypes(Zone *zone, const NativeTypes &members, intptr_t member_packing=kMaxInt32)
virtual bool ContainsUnalignedMembers(intptr_t offset=0) const
virtual void PrintMemberOffset(BaseTextBuffer *f, intptr_t member_index) const
virtual intptr_t NumPrimitiveMembersRecursive() const
virtual void PrintCompoundType(BaseTextBuffer *f) const
const ZoneGrowableArray< intptr_t > & member_offsets() const
const NativeCompoundType & AsCompound() const
Representation AsRepresentationOverApprox(Zone *zone_) const
virtual bool IsInt() const
Definition native_type.h:89
static const NativeType * FromAbstractType(Zone *zone, const AbstractType &type, const char **error)
virtual bool IsCompound() const
Definition native_type.h:84
virtual void PrintTo(BaseTextBuffer *f, bool multi_line=false, bool verbose=true) const
virtual intptr_t PrimitivePairMembers(const NativePrimitiveType **first, const NativePrimitiveType **second, intptr_t offset_in_members=0) const =0
virtual bool IsSigned() const
Definition native_type.h:93
const NativeArrayType & AsArray() const
const NativePrimitiveType & AsPrimitive() const
const char * ToCString() const
virtual intptr_t AlignmentInBytesStack(bool is_vararg=false) const =0
virtual intptr_t SizeInBytes() const =0
virtual const NativePrimitiveType & FirstPrimitiveMember() const =0
virtual bool IsArray() const
Definition native_type.h:82
const NativeType & WidenTo4Bytes(Zone *zone) const
virtual bool ContainsOnlyFloats(Range range) const =0
virtual bool IsPrimitive() const
Definition native_type.h:80
const NativeType & WidenTo8Bytes(Zone *zone) const
static const NativeType & FromTypedDataClassId(Zone *zone, classid_t class_id)
static NativePrimitiveType & FromRepresentation(Zone *zone, Representation rep)
virtual intptr_t AlignmentInBytesField() const =0
virtual bool IsStruct() const
Definition native_type.h:86
virtual bool ContainsUnalignedMembers(intptr_t offset=0) const =0
virtual bool Equals(const NativeType &other) const
const NativeStructType & AsStruct() const
virtual intptr_t NumPrimitiveMembersRecursive() const =0
static NativeUnionType & FromNativeTypes(Zone *zone, const NativeTypes &members)
virtual bool ContainsUnalignedMembers(intptr_t offset=0) const
virtual intptr_t NumPrimitiveMembersRecursive() const
virtual void PrintCompoundType(BaseTextBuffer *f) const
virtual bool ContainsOnlyFloats(Range range) const
static Range StartAndLength(intptr_t start_inclusive, intptr_t length)
Definition range.h:28
intptr_t start() const
Definition range.h:39
const Range Intersect(const Range &other) const
Definition range.h:68
bool Overlaps(const Range &other) const
Definition range.h:61
static Range StartAndEnd(intptr_t start_inclusive, intptr_t end_exclusive)
Definition range.h:35
intptr_t end_inclusive() const
Definition range.h:41
const Range Translate(intptr_t delta) const
Definition range.h:77
#define ASSERT(E)
#define FATAL(error)
const uint8_t uint32_t uint32_t GError ** error
size_t length
PrimitiveType PrimitiveTypeFromSizeInBytes(intptr_t size)
static const char * PrimitiveTypeToCString(PrimitiveType rep)
static const intptr_t fundamental_size_in_bytes[kVoid+1]
static PrimitiveType split_fundamental(PrimitiveType in)
static const NativeType * CompoundFromPragma(Zone *zone, const Instance &pragma, bool is_struct, const char **error)
const int64_t num_abis
Definition abi.h:47
static bool ContainsHomogeneousFloatsInternal(const NativeTypes &types)
const char * target_abi_name
Definition abi.cc:92
static PrimitiveType TypeRepresentation(classid_t class_id)
static PrimitiveType fundamental_rep(Representation rep)
Abi TargetAbi()
Definition abi.cc:88
constexpr PrimitiveType kAddress
static const NativeType * AbiSpecificFromPragma(Zone *zone, const Instance &pragma, const Class &abi_specific_int, const char **error)
static void ContainsHomogeneousFloatsRecursive(const NativeTypes &types, bool *only_float, bool *only_double)
bool IsFfiPredefinedClassId(classid_t class_id)
Definition class_id.h:527
int32_t classid_t
Definition globals.h:524
Representation
Definition locations.h:66
constexpr int32_t kMaxInt32
Definition globals.h:483
static constexpr Representation kUnboxedIntPtr
Definition locations.h:176
@ kAlignedToWordSizeAndValueSize
@ kAlignedToValueSizeBut8AlignedTo4
@ kAlignedToValueSize
@ kAlignedToWordSize
#define UNREACHABLE_THIS()
Definition native_type.h:25
#define Pd
Definition globals.h:408
Point offset