500 {
501 HierarchyInfo hi(thread);
503 Type::Handle(IsolateGroup::Current()->object_store()->object_type());
504 const bool is_nullable = Instance::NullIsAssignableTo(
type);
506 const auto& cls = Class::Handle(
type.type_class());
507
508 ClassTable* const class_table = thread->isolate_group()->class_table();
509 const intptr_t num_cids = class_table->NumCids();
510 auto& to_check = Class::Handle(thread->zone());
511 auto& rare_type = AbstractType::Handle(thread->zone());
512
513 GrowableArray<intptr_t> expected_concrete_cids;
514 GrowableArray<intptr_t> expected_abstract_cids;
515 for (intptr_t cid = kInstanceCid;
cid < num_cids;
cid++) {
516 if (!class_table->HasValidClassAt(cid)) continue;
517 if (cid == kNullCid) continue;
518 if (cid == kNeverCid) continue;
519 if (cid == kDynamicCid && !is_nullable) continue;
520 if (cid == kVoidCid && !is_nullable) continue;
521 to_check = class_table->At(cid);
522
523 if (to_check.is_abstract()) {
524 expected_abstract_cids.Add(cid);
525 } else {
526 expected_concrete_cids.Add(cid);
527 }
528 if (cid != kTypeArgumentsCid) {
529 rare_type = to_check.RareType();
530 EXPECT(rare_type.IsSubtypeOf(
type, Heap::kNew));
531 }
532 }
533
535 cls, false, !is_nullable);
537
538 GrowableArray<intptr_t> expected_cids;
539 expected_cids.AddArray(expected_concrete_cids);
540 expected_cids.AddArray(expected_abstract_cids);
542 cls, true, !is_nullable);
544}
545
547 HierarchyInfo hi(thread);
549 Type::Handle(IsolateGroup::Current()->object_store()->function_type());
551 const auto& cls = Class::Handle(
type.type_class());
552
553 GrowableArray<intptr_t> expected_concrete_cids;
554 expected_concrete_cids.Add(kClosureCid);
555
556 GrowableArray<intptr_t> expected_abstract_cids;
557 expected_abstract_cids.Add(
type.type_class_id());
558
560 cls, false, true);
562
563 GrowableArray<intptr_t> expected_cids;
564 expected_cids.AddArray(expected_concrete_cids);
565 expected_cids.AddArray(expected_abstract_cids);
567 cls, true, true);
569}
570
572 HierarchyInfo hi(thread);
573 const auto& num_type = Type::Handle(Type::Number());
574 const auto& int_type = Type::Handle(Type::IntType());
575 const auto& double_type = Type::Handle(Type::Double());
576 EXPECT(hi.CanUseSubtypeRangeCheckFor(num_type));
577 const auto& cls = Class::Handle(num_type.type_class());
578
579 GrowableArray<intptr_t> expected_concrete_cids;
580 expected_concrete_cids.Add(kSmiCid);
581 expected_concrete_cids.Add(kMintCid);
582 expected_concrete_cids.Add(kDoubleCid);
583
584 GrowableArray<intptr_t> expected_abstract_cids;
585 expected_abstract_cids.Add(num_type.type_class_id());
586 expected_abstract_cids.Add(int_type.type_class_id());
587 expected_abstract_cids.Add(double_type.type_class_id());
588
590 cls, false, true);
592
593 GrowableArray<intptr_t> expected_cids;
594 expected_cids.AddArray(expected_concrete_cids);
595 expected_cids.AddArray(expected_abstract_cids);
597 cls, true, true);
599}
600
602 HierarchyInfo hi(thread);
603 const auto&
type = Type::Handle(Type::IntType());
605 const auto& cls = Class::Handle(
type.type_class());
606
607 GrowableArray<intptr_t> expected_concrete_cids;
608 expected_concrete_cids.Add(kSmiCid);
609 expected_concrete_cids.Add(kMintCid);
610
611 GrowableArray<intptr_t> expected_abstract_cids;
612 expected_abstract_cids.Add(
type.type_class_id());
613
615 cls, false, true);
617
618 GrowableArray<intptr_t> expected_cids;
619 expected_cids.AddArray(expected_concrete_cids);
620 expected_cids.AddArray(expected_abstract_cids);
622 cls, true, true);
624}
625
627 HierarchyInfo hi(thread);
628 const auto&
type = Type::Handle(Type::StringType());
630 const auto& cls = Class::Handle(
type.type_class());
631
632 GrowableArray<intptr_t> expected_concrete_cids;
633 expected_concrete_cids.Add(kOneByteStringCid);
634 expected_concrete_cids.Add(kTwoByteStringCid);
635
636 GrowableArray<intptr_t> expected_abstract_cids;
637 expected_abstract_cids.Add(
type.type_class_id());
638
640 cls, false, true);
641 THR_Print(
"Checking concrete subtype ranges for String\n");
643
644 GrowableArray<intptr_t> expected_cids;
645 expected_cids.AddArray(expected_concrete_cids);
646 expected_cids.AddArray(expected_abstract_cids);
648 cls, true, true);
649 THR_Print(
"Checking concrete and abstract subtype ranges for String\n");
651}
652
653
654
655
657 const char* kScript = R"(
658 bool foo(double x) => (x + 0.5) == 0;
659 main() {
660 foo(-0.5);
661 }
662 )";
663
664 const auto& root_library = Library::Handle(
LoadTestScript(kScript));
665 const auto&
function = Function::Handle(
GetFunction(root_library,
"foo"));
666
667 TestPipeline pipeline(function, CompilerPass::kAOT);
668 FlowGraph* flow_graph = pipeline.RunPasses({});
669
670 auto entry = flow_graph->graph_entry()->normal_entry();
671 ILMatcher cursor(flow_graph, entry, true,
672 ParallelMovesHandling::kSkip);
673
675 kMoveGlob,
676 kMatchAndMoveBinaryDoubleOp,
677 kMatchAndMoveEqualityCompare,
678 kMatchDartReturn,
679 }));
680}
681
683
684 auto kScript = R"(
685 import 'dart:ffi';
686
687 int myFunction() {
688 return 100;
689 }
690
691 void anotherFunction() {}
692 )";
693
694
695 const auto& root_library = Library::Handle(
LoadTestScript(kScript));
696 Zone* const zone = Thread::Current()->zone();
697 auto& invoke_result = Instance::Handle(zone);
698 invoke_result ^=
Invoke(root_library,
"myFunction");
699 EXPECT_EQ(Smi::New(100), invoke_result.ptr());
700
701 const auto& my_function =
702 Function::Handle(
GetFunction(root_library,
"myFunction"));
703
704 TestPipeline pipeline(my_function, CompilerPass::kJIT);
705 FlowGraph* flow_graph = pipeline.RunPasses({
706 CompilerPass::kComputeSSA,
707 });
708
709 DartReturnInstr* return_instr = nullptr;
710 {
711 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
712
714 kMoveGlob,
715 {kMatchDartReturn, &return_instr},
716 }));
717 }
718
719 auto* const load_thread_instr = new (zone) LoadThreadInstr();
720 flow_graph->InsertBefore(return_instr, load_thread_instr, nullptr,
721 FlowGraph::kValue);
722 auto load_thread_value =
Value(load_thread_instr);
723
724 auto* const convert_instr = new (zone) IntConverterInstr(
725 kUntagged, kUnboxedAddress, &load_thread_value, DeoptId::kNone);
726 flow_graph->InsertBefore(return_instr, convert_instr, nullptr,
727 FlowGraph::kValue);
728 auto convert_value =
Value(convert_instr);
729
730 auto* const box_instr = BoxInstr::Create(kUnboxedAddress, &convert_value);
731 flow_graph->InsertBefore(return_instr, box_instr, nullptr, FlowGraph::kValue);
732
733 return_instr->InputAt(0)->definition()->ReplaceUsesWith(box_instr);
734
735 {
736
737 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
739 kMoveGlob,
740 kMatchAndMoveLoadThread,
741 kMatchAndMoveIntConverter,
742 kMatchAndMoveBox,
743 kMatchDartReturn,
744 }));
745 }
746
747 pipeline.RunForcedOptimizedAfterSSAPasses();
748
749 {
750#if !defined(PRODUCT) && !defined(USING_THREAD_SANITIZER)
751 SetFlagScope<bool> sfs(&FLAG_disassemble_optimized, true);
752#endif
753 pipeline.CompileGraphAndAttachFunction();
754 }
755
756
757 invoke_result ^=
Invoke(root_library,
"myFunction");
758 intptr_t result_int = Integer::Cast(invoke_result).AsInt64Value();
759 EXPECT_EQ(reinterpret_cast<intptr_t>(thread), result_int);
760}
761
762#if !defined(TARGET_ARCH_IA32)
764
765 auto kScript = Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
766 int globalCounter = 0;
767
768 int increment() => ++globalCounter;
769
770 int cachedIncrement() {
771 // We will replace this call with a cacheable call,
772 // which will lead to the counter no longer being incremented.
773 // Make sure to return the value, so we can see that the boxing and
774 // unboxing works as expected.
775 return increment();
776 }
777
778 int multipleIncrement() {
779 int returnValue = 0;
780 for(int i = 0; i < 10; i++) {
781 // Save the last returned value.
782 returnValue = cachedIncrement();
783 }
784 return returnValue;
785 }
786 )"), std::free);
787
788
789 const auto& root_library = Library::Handle(
LoadTestScript(kScript.get()));
790 const auto& first_result =
791 Object::Handle(
Invoke(root_library,
"multipleIncrement"));
792 EXPECT(first_result.IsSmi());
793 if (first_result.IsSmi()) {
794 const intptr_t int_value = Smi::Cast(first_result).Value();
795 EXPECT_EQ(10, int_value);
796 }
797
798 const auto& cached_increment_function =
799 Function::Handle(
GetFunction(root_library,
"cachedIncrement"));
800
801 const auto& increment_function =
802 Function::ZoneHandle(
GetFunction(root_library,
"increment"));
803
804 TestPipeline pipeline(cached_increment_function, CompilerPass::kJIT);
805 FlowGraph* flow_graph = pipeline.RunPasses({
806 CompilerPass::kComputeSSA,
807 });
808
809 StaticCallInstr* static_call = nullptr;
810 {
811 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
812
814 kMoveGlob,
815 {kMatchAndMoveStaticCall, &static_call},
816 kMoveGlob,
817 kMatchDartReturn,
818 }));
819 }
820
822 CachableIdempotentCallInstr*
call =
new CachableIdempotentCallInstr(
823 InstructionSource(), kUnboxedAddress, increment_function,
824 static_call->type_args_len(), Array::empty_array(), std::move(
args),
825 DeoptId::kNone);
826 static_call->ReplaceWith(call, nullptr);
827
828 pipeline.RunForcedOptimizedAfterSSAPasses();
829
830 {
831 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
832
834 kMoveGlob,
835 kMatchAndMoveCachableIdempotentCall,
836 kMoveGlob,
837
838
839 kMatchBox,
840 kMoveGlob,
841 kMatchDartReturn,
842 }));
843 }
844
845 {
846#if !defined(PRODUCT)
847 SetFlagScope<bool> sfs(&FLAG_disassemble_optimized, true);
848#endif
849 pipeline.CompileGraphAndAttachFunction();
850 }
851
852 const auto& second_result =
853 Object::Handle(
Invoke(root_library,
"multipleIncrement"));
854 EXPECT(second_result.IsSmi());
855 if (second_result.IsSmi()) {
856 const intptr_t int_value = Smi::Cast(second_result).Value();
857 EXPECT_EQ(11, int_value);
858 }
859}
860#endif
861
862
864 const compiler::ffi::CallMarshaller& marshaller,
865 uword native_entry,
866 bool is_leaf) {
867 FlowGraph* flow_graph = pipeline->RunPasses({CompilerPass::kComputeSSA});
868
869 {
870
871 StaticCallInstr* static_call = nullptr;
872 {
873 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry(),
874 false);
875 cursor.TryMatch({
kMoveGlob, {kMatchStaticCall, &static_call}});
876 }
878
879
880
881 Zone*
const Z = flow_graph->zone();
882 auto*
const load_entry_point =
new (
Z) IntConverterInstr(
883 kUnboxedIntPtr, kUntagged,
884 new (
Z)
Value(flow_graph->GetConstant(
885 Integer::Handle(
Z, Integer::NewCanonical(native_entry)),
886 kUnboxedIntPtr)),
887 DeoptId::kNone);
888 flow_graph->InsertBefore(static_call, load_entry_point, nullptr,
889 FlowGraph::kValue);
890
891
892 const intptr_t num_arguments =
893 FfiCallInstr::InputCountForMarshaller(marshaller);
896 arguments.Add(
new (
Z)
Value(load_entry_point));
897 auto*
const ffi_call =
new (
Z)
898 FfiCallInstr(DeoptId::kNone, marshaller, is_leaf, std::move(arguments));
900 ffi_call->InputAt(ffi_call->TargetAddressIndex())->definition() ==
901 load_entry_point);
902 flow_graph->InsertBefore(static_call, ffi_call, nullptr,
903 FlowGraph::kEffect);
904
905
906 static_call->RemoveFromGraph(false);
907 }
908
909
910 pipeline->RunAdditionalPasses({
911 CompilerPass::kApplyICData,
912 CompilerPass::kTryOptimizePatterns,
913 CompilerPass::kSetOuterInliningId,
914 CompilerPass::kTypePropagation,
915
916 CompilerPass::kWidenSmiToInt32,
917 CompilerPass::kSelectRepresentations,
918
919 CompilerPass::kTypePropagation,
920 CompilerPass::kRangeAnalysis,
921
922 CompilerPass::kFinalizeGraph,
923 CompilerPass::kCanonicalize,
924 CompilerPass::kAllocateRegisters,
925 CompilerPass::kReorderBlocks,
926 });
927
928 return flow_graph;
929}
930
931
932
933
934
936 const char* kScript = R"(
937 import 'dart:ffi';
938
939 // This is purely a placeholder and is never called.
940 void placeholder() {}
941
942 // Will call the "doFfiCall" and exercise its code.
943 bool invokeDoFfiCall() {
944 final double result = doFfiCall(1, 2, 3, 1.0, 2.0, 3.0);
945 if (result != (2 + 3 + 4 + 2.0 + 3.0 + 4.0)) {
946 throw 'Failed. Result was $result.';
947 }
948 return true;
949 }
950
951 // Will perform a "C" call while having live values in registers
952 // across the FfiCall.
953 double doFfiCall(int a, int b, int c, double x, double y, double z) {
954 // Ensure there is at least one live value in a register.
955 a += 1;
956 b += 1;
957 c += 1;
958 x += 1.0;
959 y += 1.0;
960 z += 1.0;
961 // We'll replace this StaticCall with an FfiCall.
962 placeholder();
963 // Use the live value.
964 return (a + b + c + x + y + z);
965 }
966
967 // FFI trampoline function.
968 typedef NT = Void Function();
969 typedef DT = void Function();
970 Pointer<NativeFunction<NT>> ptr = Pointer.fromAddress(0);
971 DT getFfiTrampolineClosure() => ptr.asFunction(isLeaf:true);
972 )";
973
974 const auto& root_library = Library::Handle(
LoadTestScript(kScript));
975
976
977 auto& c_function = Instructions::Handle(
979
980
982 if ((kAbiVolatileFpuRegs & (1 << reg)) != 0) {
983#if defined(TARGET_ARCH_ARM)
984
985 assembler->LoadDImmediate(
static_cast<DRegister>(reg), 0.0, R3);
986#else
987 assembler->LoadDImmediate(
static_cast<FpuRegister>(reg), 0.0);
988#endif
989 }
990 }
992 if ((kDartVolatileCpuRegs & (1 << reg)) != 0) {
993 assembler->LoadImmediate(
static_cast<Register>(reg), 0xDEADBEEF);
994 }
995 }
996 assembler->Ret();
997 }));
998 uword native_entry = c_function.EntryPoint();
999
1000
1001 Invoke(root_library,
"invokeDoFfiCall");
1002
1003 const Function& do_ffi_call =
1004 Function::Handle(
GetFunction(root_library,
"doFfiCall"));
1006
1007 const auto&
value = Closure::Handle(
1008 Closure::RawCast(
Invoke(root_library,
"getFfiTrampolineClosure")));
1010 const auto& ffi_trampoline =
1011 Function::ZoneHandle(Closure::Cast(value).function());
1013
1014
1015
1016 const char*
error =
nullptr;
1017 auto* const zone = thread->zone();
1018 const auto& c_signature =
1019 FunctionType::ZoneHandle(zone, ffi_trampoline.FfiCSignature());
1020 const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
1021 zone, ffi_trampoline, 1, c_signature,
1025 const auto& marshaller = *marshaller_ptr;
1026
1027 const auto& compile_and_run =
1028 [&](bool is_leaf, std::function<void(ParallelMoveInstr*)> verify) {
1029
1030 TestPipeline pipeline(do_ffi_call, CompilerPass::kJIT);
1031 FlowGraph* flow_graph =
1033
1034 {
1035 ParallelMoveInstr* parallel_move = nullptr;
1036 ILMatcher cursor(flow_graph,
1037 flow_graph->graph_entry()->normal_entry(),
1038 false);
1039 while (cursor.TryMatch(
1040 {kMoveGlob, {kMatchAndMoveParallelMove, ¶llel_move}})) {
1041 verify(parallel_move);
1042 }
1043 }
1044
1045
1046 pipeline.CompileGraphAndAttachFunction();
1047
1048
1049 auto&
result = Object::Handle(
Invoke(root_library,
"invokeDoFfiCall"));
1052 };
1053
1054 intptr_t num_cpu_reg_to_stack_nonleaf = 0;
1055 intptr_t num_cpu_reg_to_stack_leaf = 0;
1056 intptr_t num_fpu_reg_to_stack_nonleaf = 0;
1057 intptr_t num_fpu_reg_to_stack_leaf = 0;
1058
1059
1060 compile_and_run(false, [&](ParallelMoveInstr* parallel_move) {
1061
1062 for (int i = 0; i < parallel_move->NumMoves(); i++) {
1063 auto move = parallel_move->moves()[i];
1064 if (move->src_slot()->IsRegister() && move->dest_slot()->IsStackSlot()) {
1065 num_cpu_reg_to_stack_nonleaf++;
1066 }
else if (move->src_slot()->IsFpuRegister() &&
1067 move->dest_slot()->IsDoubleStackSlot()) {
1068 num_fpu_reg_to_stack_nonleaf++;
1069 }
1070 }
1071 });
1072
1073
1074 compile_and_run(true, [&](ParallelMoveInstr* parallel_move) {
1075
1076
1077 for (int i = 0; i < parallel_move->NumMoves(); i++) {
1078 auto move = parallel_move->moves()[i];
1079 if (move->src_slot()->IsRegister() && move->dest_slot()->IsStackSlot()) {
1080 num_cpu_reg_to_stack_leaf++;
1081 }
else if (move->src_slot()->IsFpuRegister() &&
1082 move->dest_slot()->IsDoubleStackSlot()) {
1083 num_fpu_reg_to_stack_leaf++;
1084 }
1085 }
1086 });
1087
1088
1089 EXPECT_LT(num_cpu_reg_to_stack_leaf, num_cpu_reg_to_stack_nonleaf);
1090
1091 const bool has_callee_save_fpu_regs =
1092 Utils::CountOneBitsWord(kAbiVolatileFpuRegs) <
1093 Utils::CountOneBitsWord(kAllFpuRegistersList);
1094 EXPECT(!has_callee_save_fpu_regs ||
1095 num_fpu_reg_to_stack_leaf < num_fpu_reg_to_stack_nonleaf);
1096}
1097
1099 const char* function_name,
1100 CompilerPass::PipelineMode mode,
1102 const auto& function =
1103 Function::Handle(
GetFunction(root_library, function_name));
1104
1105 TestPipeline pipeline(function, mode);
1106 FlowGraph* flow_graph = pipeline.RunPasses({});
1107
1108 auto entry = flow_graph->graph_entry()->normal_entry();
1109 EXPECT(entry !=
nullptr);
1110
1111 DartReturnInstr* ret = nullptr;
1112
1113 ILMatcher cursor(flow_graph, entry, true, ParallelMovesHandling::kSkip);
1115 kMoveGlob,
1116 {kMatchDartReturn, &ret},
1117 }));
1118
1119 ConstantInstr* constant = ret->value()->definition()->AsConstant();
1120 EXPECT(constant !=
nullptr);
1121 if (constant != nullptr) {
1122 const Object&
value = constant->value();
1124 if (
value.IsSmi()) {
1125 const intptr_t int_value = Smi::Cast(value).Value();
1127 }
1128 }
1129}
1130
1132
1133 auto kScript = R"(
1134 b0() => 0. bitLength; // 0...00000
1135 b1() => 1. bitLength; // 0...00001
1136 b100() => 100. bitLength;
1137 b200() => 200. bitLength;
1138 bffff() => 0xffff. bitLength;
1139 m1() => (-1).bitLength; // 1...11111
1140 m2() => (-2).bitLength; // 1...11110
1141
1142 main() {
1143 b0();
1144 b1();
1145 b100();
1146 b200();
1147 bffff();
1148 m1();
1149 m2();
1150 }
1151 )";
1152
1153
1154 const auto& root_library = Library::Handle(
LoadTestScript(kScript));
1155 Invoke(root_library,
"main");
1156
1157 auto test = [&](
const char*
function, intptr_t expected) {
1160 };
1161
1169}
1170
1172 Thread* thread,
1173 bool allow_representation_change) {
1174 using compiler::BlockBuilder;
1175
1176 const auto& lib = Library::Handle(Library::CoreLibrary());
1177 const Class& list_class =
1178 Class::Handle(lib.LookupClassAllowPrivate(Symbols::_List()));
1179 EXPECT(!list_class.IsNull());
1180 const Error& err = Error::Handle(list_class.EnsureIsFinalized(thread));
1182 const Function& list_filled = Function::ZoneHandle(
1183 list_class.LookupFactoryAllowPrivate(Symbols::_ListFilledFactory()));
1184 EXPECT(!list_filled.IsNull());
1185
1186 CompilerState
S(thread,
true,
true);
1187
1188 FlowGraphBuilderHelper
H(1);
1189 H.AddVariable(
"param", AbstractType::ZoneHandle(Type::IntType()));
1190
1191 auto normal_entry =
H.flow_graph()->graph_entry()->normal_entry();
1192
1193 Definition* param = nullptr;
1194 LoadFieldInstr*
load =
nullptr;
1195 UnboxInstr* unbox = nullptr;
1196 Definition* add = nullptr;
1197 {
1198 BlockBuilder
builder(
H.flow_graph(), normal_entry);
1199 param =
builder.AddParameter(0, kUnboxedInt64);
1200
1205 StaticCallInstr* array =
builder.AddDefinition(
new StaticCallInstr(
1206 InstructionSource(), list_filled, 1, Array::empty_array(),
1207 std::move(
args), DeoptId::kNone, 0, ICData::kNoRebind));
1208 array->UpdateType(CompileType::FromCid(kArrayCid));
1209 array->SetResultType(thread->zone(), CompileType::FromCid(kArrayCid));
1210 array->set_is_known_list_constructor(true);
1211
1213 new Value(array), Slot::Array_length(), InstructionSource()));
1214
1215 unbox =
builder.AddDefinition(
new UnboxInt64Instr(
1216 new Value(
load), DeoptId::kNone, Instruction::kNotSpeculative));
1217
1218 add =
builder.AddDefinition(
new BinaryInt64OpInstr(
1219 Token::kADD,
new Value(unbox),
new Value(
H.IntConstant(1)),
1220 S.GetNextDeoptId(), Instruction::kNotSpeculative));
1221
1222 Definition* box =
builder.AddDefinition(
new BoxInt64Instr(
new Value(add)));
1223
1225 }
1226
1228
1229 if (!allow_representation_change) {
1230 H.flow_graph()->disallow_unmatched_representations();
1231 }
1232
1233 H.flow_graph()->Canonicalize();
1234
1235 if (allow_representation_change) {
1236 EXPECT(add->InputAt(0)->definition() == param);
1237 } else {
1238 EXPECT(add->InputAt(0)->definition() == unbox);
1239 EXPECT(unbox->value()->definition() ==
load);
1240 }
1241}
1242
1246}
1247
1252};
1253
1255 Thread* thread,
1256 TypeDataField field_kind) {
1257 const auto& typed_data_lib = Library::Handle(Library::TypedDataLibrary());
1258 const auto& view_cls = Class::Handle(
1259 typed_data_lib.LookupClassAllowPrivate(Symbols::_Float32ArrayView()));
1260 const Error& err = Error::Handle(view_cls.EnsureIsFinalized(thread));
1262 const auto& factory =
1263 Function::ZoneHandle(view_cls.LookupFactoryAllowPrivate(String::Handle(
1264 String::Concat(Symbols::_Float32ArrayView(), Symbols::DotUnder()))));
1265 EXPECT(!factory.IsNull());
1266
1267 using compiler::BlockBuilder;
1268 CompilerState
S(thread,
false,
true);
1269 FlowGraphBuilderHelper
H;
1270
1271 const Slot* field =
nullptr;
1272 switch (field_kind) {
1274 field = &Slot::TypedDataBase_length();
1275 break;
1277 field = &Slot::TypedDataView_offset_in_bytes();
1278 break;
1280 field = &Slot::TypedDataView_typed_data();
1281 break;
1282 }
1283
1284 auto b1 =
H.flow_graph()->graph_entry()->normal_entry();
1285
1286 const auto constant_4 =
H.IntConstant(4);
1287 const auto constant_1 =
H.IntConstant(1);
1288
1289 Definition* array;
1291 DartReturnInstr* ret;
1292
1293 {
1294 BlockBuilder
builder(
H.flow_graph(), b1);
1295
1296 array =
builder.AddDefinition(
new AllocateTypedDataInstr(
1297 InstructionSource(), kTypedDataFloat64ArrayCid,
new Value(constant_1),
1298 DeoptId::kNone));
1299
1300 const auto view =
builder.AddDefinition(
new StaticCallInstr(
1301 InstructionSource(), factory, 1, Array::empty_array(),
1302 {
new Value(
H.flow_graph()->constant_null()),
new Value(array),
1303 new Value(constant_4),
new Value(constant_1)},
1304 DeoptId::kNone, 1, ICData::RebindRule::kStatic));
1305
1307 new LoadFieldInstr(
new Value(view), *field, InstructionSource()));
1308
1310 }
1312 H.flow_graph()->Canonicalize();
1313
1314 switch (field_kind) {
1317 break;
1320 break;
1323 break;
1324 }
1325}
1326
1330 TypedDataView_offset_in_bytes);
1332 TypedDataView_typed_data);
1333}
1334
1335
1336
1338 const auto& typed_data_lib = Library::Handle(Library::TypedDataLibrary());
1339 const auto& view_cls = Class::Handle(typed_data_lib.LookupClassAllowPrivate(
1340 String::Handle(Symbols::New(thread, "_TypedListBase"))));
1341 const Error& err = Error::Handle(view_cls.EnsureIsFinalized(thread));
1343 const auto& getter = Function::Handle(
1344 view_cls.LookupFunctionAllowPrivate(Symbols::GetLength()));
1345 EXPECT(!getter.IsNull());
1346
1347 using compiler::BlockBuilder;
1348 CompilerState
S(thread,
true,
true);
1349 FlowGraphBuilderHelper
H;
1350
1351 auto b1 =
H.flow_graph()->graph_entry()->normal_entry();
1352
1353 InstanceCallInstr* length_call;
1354 DartReturnInstr* ret;
1355
1356 {
1357 BlockBuilder
builder(
H.flow_graph(), b1);
1358
1359 const auto array =
builder.AddDefinition(
new AllocateTypedDataInstr(
1360 InstructionSource(), kTypedDataFloat64ArrayCid,
1361 new Value(
H.IntConstant(1)), DeoptId::kNone));
1362
1363 length_call =
builder.AddDefinition(
new InstanceCallInstr(
1364 InstructionSource(), Symbols::GetLength(), Token::kGET,
1365 {
new Value(array)}, 0, Array::empty_array(), 1,
1366 42));
1367 length_call->EnsureICData(
H.flow_graph());
1368
1370 }
1372 H.flow_graph()->Canonicalize();
1373
1377 it.function().ptr() == getter.ptr());
1378}
1379
1381 uword lower,
1382 uword upper,
1384 using compiler::BlockBuilder;
1385 CompilerState
S(Thread::Current(),
true,
true);
1386 FlowGraphBuilderHelper
H(1);
1387 H.AddVariable(
"v0",
type);
1388
1389 auto normal_entry =
H.flow_graph()->graph_entry()->normal_entry();
1390
1391 DartReturnInstr* ret;
1392 {
1393 BlockBuilder
builder(
H.flow_graph(), normal_entry);
1394 Definition* param =
builder.AddParameter(0, kTagged);
1395 Definition* load_cid =
1396 builder.AddDefinition(
new LoadClassIdInstr(
new Value(param)));
1398 InstructionSource(),
new Value(load_cid), lower, upper, kTagged));
1400 }
1402 H.flow_graph()->Canonicalize();
1403
1406 it.value()->BoundConstant().ptr() == Bool::Get(
result).ptr());
1407}
1408
1410 HierarchyInfo hierarchy_info(thread);
1412 kOneByteStringCid, kTwoByteStringCid, false);
1414 kMintCid, true);
1416 kMintCid, false);
1418 kMintCid, false);
1420 kClassIdTagMax, true);
1421}
1422
1424 const Class& test_cls,
1425 const Field& field,
1426 intptr_t num_stores,
1427 bool expected_to_forward) {
1429
1430 using compiler::BlockBuilder;
1431 CompilerState
S(thread,
false,
true);
1432 FlowGraphBuilderHelper
H;
1433
1434 auto b1 =
H.flow_graph()->graph_entry()->normal_entry();
1435
1436 const auto constant_42 =
H.IntConstant(42);
1437 const auto constant_24 =
H.IntConstant(24);
1439 DartReturnInstr* ret;
1440
1441 {
1442 BlockBuilder
builder(
H.flow_graph(), b1);
1443
1444 const auto obj =
builder.AddDefinition(
1445 new AllocateObjectInstr(InstructionSource(), test_cls, DeoptId::kNone));
1446
1447 if (num_stores >= 1) {
1448
1449 builder.AddInstruction(
new StoreFieldInstr(
1450 field,
new Value(obj),
new Value(constant_42),
1451 StoreBarrierType::kNoStoreBarrier, InstructionSource(),
1452 &
H.flow_graph()->parsed_function(),
1453 StoreFieldInstr::Kind::kInitializing));
1454 }
1455
1456 if (num_stores >= 2) {
1457
1458 builder.AddInstruction(
new StoreFieldInstr(
1459 field,
new Value(obj),
new Value(constant_24),
1460 StoreBarrierType::kNoStoreBarrier, InstructionSource(),
1461 &
H.flow_graph()->parsed_function()));
1462 }
1463
1464
1466 new Value(obj), Slot::Get(field, &
H.flow_graph()->parsed_function()),
1467 InstructionSource()));
1468
1469
1471 }
1473 H.flow_graph()->Canonicalize();
1474
1475 if (expected_to_forward) {
1477 } else {
1479 }
1480}
1481
1483 const char* script_chars = R"(
1484 import 'dart:typed_data';
1485
1486 class TestClass {
1487 final dynamic finalField;
1488 late final dynamic lateFinalField;
1489 dynamic normalField;
1490
1491 TestClass(this.finalField, this.lateFinalField, this.normalField);
1492 }
1493 )";
1495
1496 const auto& test_cls = Class::ZoneHandle(
1497 lib.LookupClass(String::Handle(Symbols::New(thread, "TestClass"))));
1498 const auto& err = Error::Handle(test_cls.EnsureIsFinalized(thread));
1500
1501 const auto lookup_field = [&](
const char*
name) ->
const Field& {
1502 const auto& original_field = Field::Handle(
1503 test_cls.LookupField(String::Handle(Symbols::New(thread,
name))));
1504 EXPECT(!original_field.IsNull());
1505 return Field::Handle(original_field.CloneFromOriginal());
1506 };
1507
1508 const auto& final_field = lookup_field("finalField");
1509 const auto& late_final_field = lookup_field("lateFinalField");
1510 const auto& normal_field = lookup_field("normalField");
1511
1513 false);
1515 true);
1517 false);
1518
1520 0, false);
1522 1, false);
1524 2, false);
1525
1527 false);
1529 false);
1531 false);
1532}
1533
1534}
static void test_range(skiatest::Reporter *reporter)
#define RELEASE_ASSERT(cond)
#define THR_Print(format,...)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const char * expected_value
const uint8_t uint32_t uint32_t GError ** error
#define RANGES_CONTAIN_EXPECTED_CIDS(ranges, cids)
#define EXPECT_PROPERTY(entity, property)
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
@ TypedDataView_offset_in_bytes
@ TypedDataView_typed_data
static void TestTestRangeCanonicalize(const AbstractType &type, uword lower, uword upper, bool result)
static void TestRepresentationChangeDuringCanonicalization(Thread *thread, bool allow_representation_change)
GrowableArray< Value * > InputsArray
static void TestCanonicalizationOfTypedDataViewFieldLoads(Thread *thread, TypeDataField field_kind)
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
MallocGrowableArray< CidRangeValue > CidRangeVector
const int kNumberOfFpuRegisters
static void TestConstantFoldToSmi(const Library &root_library, const char *function_name, CompilerPass::PipelineMode mode, intptr_t expected_value)
void TestStaticFieldForwarding(Thread *thread, const Class &test_cls, const Field &field, intptr_t num_stores, bool expected_to_forward)
InstructionsPtr BuildInstructions(std::function< void(compiler::Assembler *assembler)> fun)
FlowGraph * SetupFfiFlowgraph(TestPipeline *pipeline, const compiler::ffi::CallMarshaller &marshaller, uword native_entry, bool is_leaf)
#define ISOLATE_UNIT_TEST_CASE(name)