37 max_exhaustive_polymorphic_checks,
39 "If a call receiver is known to be of at most this many classes, "
40 "generate exhaustive class tests instead of a megamorphic call");
43#define IG (isolate_group())
46#ifdef DART_PRECOMPILER
54static void GetUniqueDynamicTarget(IsolateGroup* isolate_group,
58 isolate_group->object_store()->unique_dynamic_targets());
60 *
function = functions_map.GetOrNull(fname);
61 ASSERT(functions_map.Release().ptr() ==
62 isolate_group->object_store()->unique_dynamic_targets());
66 Precompiler* precompiler,
67 FlowGraph* flow_graph,
68 SpeculativeInliningPolicy* speculative_policy)
69 : CallSpecializer(flow_graph,
72 precompiler_(precompiler),
73 has_unique_no_such_method_(
false) {
74 Function& target_function = Function::Handle();
75 if (isolate_group()->object_store()->unique_dynamic_targets() !=
77 GetUniqueDynamicTarget(isolate_group(), Symbols::NoSuchMethod(),
79 has_unique_no_such_method_ = !target_function.IsNull();
83bool AotCallSpecializer::TryCreateICDataForUniqueTarget(
84 InstanceCallInstr*
call) {
85 if (isolate_group()->object_store()->unique_dynamic_targets() ==
91 Function& target_function = Function::Handle(
Z);
92 GetUniqueDynamicTarget(isolate_group(),
call->function_name(),
95 if (target_function.IsNull()) {
103 if (target_function.HasOptionalNamedParameters() ||
104 target_function.IsGeneric() ||
105 !target_function.AreValidArgumentCounts(
106 call->type_args_len(),
call->ArgumentCountWithoutTypeArgs(),
107 call->argument_names().IsNull() ? 0 :
call->argument_names().Length(),
112 const Class& cls = Class::Handle(
Z, target_function.Owner());
114 if (!CHA::HasSingleConcreteImplementation(cls, &implementor_cid)) {
119 CallTargets::CreateMonomorphic(
Z, implementor_cid, target_function));
124 if (has_unique_no_such_method_) {
125 call->set_has_unique_selector(
true);
129 const intptr_t receiver_index =
call->FirstArgIndex();
130 RedefinitionInstr* redefinition =
new (
Z)
131 RedefinitionInstr(
new (
Z)
Value(
call->ArgumentAt(receiver_index)));
132 flow_graph()->AllocateSSAIndex(redefinition);
133 redefinition->InsertAfter(
call);
135 FlowGraph::RenameDominatedUses(
call->ArgumentAt(receiver_index),
136 redefinition, redefinition);
137 if (!redefinition->HasUses()) {
138 redefinition->RemoveFromGraph();
145bool AotCallSpecializer::TryCreateICData(InstanceCallInstr*
call) {
146 if (TryCreateICDataForUniqueTarget(
call)) {
150 return CallSpecializer::TryCreateICData(
call);
153bool AotCallSpecializer::RecognizeRuntimeTypeGetter(InstanceCallInstr*
call) {
154 if ((precompiler_ ==
nullptr) ||
155 !precompiler_->get_runtime_type_is_unique()) {
159 if (
call->function_name().ptr() != Symbols::GetRuntimeType().ptr()) {
165 const Class& cls = Class::Handle(
Z,
IG->object_store()->object_class());
167 Function::Handle(
Z,
call->ResolveForReceiverClass(cls));
170 StaticCallInstr* static_call =
173 call->ReplaceWith(static_call, current_iterator());
177static bool IsGetRuntimeType(Definition* defn) {
178 StaticCallInstr*
call = defn->AsStaticCall();
179 return (
call !=
nullptr) && (
call->function().recognized_kind() ==
180 MethodRecognizer::kObjectRuntimeType);
186bool AotCallSpecializer::TryReplaceWithHaveSameRuntimeType(
187 TemplateDartCall<0>*
call) {
189 (
call->AsInstanceCall()->ic_data()->NumArgsTested() == 2)) ||
190 call->IsStaticCall());
194 Definition*
left =
call->ArgumentAt(0);
197 if (IsGetRuntimeType(
left) &&
left->input_use_list()->IsSingleUse() &&
198 IsGetRuntimeType(
right) &&
right->input_use_list()->IsSingleUse()) {
199 const Class& cls = Class::Handle(
Z,
IG->object_store()->object_class());
200 const Function& have_same_runtime_type = Function::ZoneHandle(
202 cls.LookupStaticFunctionAllowPrivate(Symbols::HaveSameRuntimeType()));
203 ASSERT(!have_same_runtime_type.IsNull());
206 args.Add(
left->ArgumentValueAt(0)->CopyWithType(
Z));
207 args.Add(
right->ArgumentValueAt(0)->CopyWithType(
Z));
208 const intptr_t kTypeArgsLen = 0;
209 StaticCallInstr* static_call =
new (
Z)
210 StaticCallInstr(
call->source(), have_same_runtime_type, kTypeArgsLen,
211 Object::null_array(),
214 static_call->SetResultType(
Z, CompileType::FromCid(kBoolCid));
215 ReplaceCall(
call, static_call);
218 Environment*
env = static_call->env();
219 env->ValueAt(
env->Length() - 2)
220 ->BindToEnvironment(static_call->ArgumentAt(0));
221 env->ValueAt(
env->Length() - 1)
222 ->BindToEnvironment(static_call->ArgumentAt(1));
229bool AotCallSpecializer::TryInlineFieldAccess(InstanceCallInstr*
call) {
231 if ((op_kind == Token::kGET) && TryInlineInstanceGetter(
call)) {
234 if ((op_kind == Token::kSET) && TryInlineInstanceSetter(
call)) {
240bool AotCallSpecializer::TryInlineFieldAccess(StaticCallInstr*
call) {
241 if (
call->function().IsImplicitGetterFunction()) {
242 Field& field = Field::ZoneHandle(
call->function().accessor_field());
243 if (field.is_late()) {
247 if (should_clone_fields_) {
248 field = field.CloneFromOriginal();
250 InlineImplicitInstanceGetter(
call, field);
257bool AotCallSpecializer::IsSupportedIntOperandForStaticDoubleOp(
258 CompileType* operand_type) {
259 if (operand_type->IsNullableInt()) {
260 if (operand_type->ToNullableCid() == kSmiCid) {
272Value* AotCallSpecializer::PrepareStaticOpInput(
Value* input,
277 if (input->Type()->is_nullable()) {
279 (
call->IsInstanceCall()
280 ?
call->AsInstanceCall()->function_name()
281 : String::ZoneHandle(
Z,
call->AsStaticCall()->function().name()));
285 input = input->CopyWithType(
Z);
287 if (
cid == kDoubleCid && input->Type()->IsNullableInt()) {
288 Definition* conversion =
nullptr;
290 if (input->Type()->ToNullableCid() == kSmiCid) {
291 conversion =
new (
Z) SmiToDoubleInstr(input,
call->source());
294 Instruction::kNotSpeculative);
299 if (FLAG_trace_strong_mode_types) {
300 THR_Print(
"[Strong mode] Inserted %s\n", conversion->ToCString());
302 InsertBefore(
call, conversion,
nullptr, FlowGraph::kValue);
303 return new (
Z)
Value(conversion);
309CompileType AotCallSpecializer::BuildStrengthenedReceiverType(
Value* input,
311 CompileType* old_type = input->Type();
312 CompileType* refined_type = old_type;
314 CompileType
type = CompileType::None();
315 if (
cid == kSmiCid) {
316 type = CompileType::NullableSmi();
317 refined_type = CompileType::ComputeRefinedType(old_type, &
type);
318 }
else if (
cid == kMintCid) {
319 type = CompileType::NullableMint();
320 refined_type = CompileType::ComputeRefinedType(old_type, &
type);
321 }
else if (
cid == kIntegerCid && !input->Type()->IsNullableInt()) {
322 type = CompileType::NullableInt();
323 refined_type = CompileType::ComputeRefinedType(old_type, &
type);
324 }
else if (
cid == kDoubleCid && !input->Type()->IsNullableDouble()) {
325 type = CompileType::NullableDouble();
326 refined_type = CompileType::ComputeRefinedType(old_type, &
type);
329 if (refined_type != old_type) {
330 return *refined_type;
332 return CompileType::None();
339 CompileType* new_type = instr->Type();
340 for (Value::Iterator it(instr->input_use_list()); !it.Done(); it.Advance()) {
341 it.Current()->RefineReachingType(new_type);
345bool AotCallSpecializer::TryOptimizeInstanceCallUsingStaticTypes(
346 InstanceCallInstr* instr) {
348 return TryOptimizeIntegerOperation(instr, op_kind) ||
349 TryOptimizeDoubleOperation(instr, op_kind);
352bool AotCallSpecializer::TryOptimizeStaticCallUsingStaticTypes(
353 StaticCallInstr* instr) {
354 const String&
name = String::Handle(
Z, instr->function().name());
355 const Token::Kind op_kind = MethodTokenRecognizer::RecognizeTokenKind(
name);
357 if (op_kind == Token::kEQ && TryReplaceWithHaveSameRuntimeType(instr)) {
362 const auto&
target = instr->function();
363 if (!
target.IsDynamicFunction()) {
370 const Class& owner = Class::Handle(
Z,
target.Owner());
371 const intptr_t
cid = owner.id();
372 if (
cid == kSmiCid ||
cid == kMintCid ||
cid == kIntegerCid ||
378 const intptr_t receiver_index = instr->FirstArgIndex();
379 const intptr_t
argument_count = instr->ArgumentCountWithoutTypeArgs();
381 auto receiver_value = instr->ArgumentValueAt(receiver_index);
382 auto receiver = receiver_value->definition();
383 auto type = BuildStrengthenedReceiverType(receiver_value,
cid);
384 if (!
type.IsNone()) {
386 flow_graph()->EnsureRedefinition(instr->previous(), receiver,
type);
387 if (redefinition !=
nullptr) {
394 return TryOptimizeIntegerOperation(instr, op_kind) ||
395 TryOptimizeDoubleOperation(instr, op_kind);
398Definition* AotCallSpecializer::TryOptimizeDivisionOperation(
399 TemplateDartCall<0>* instr,
402 Value* right_value) {
403 auto unboxed_constant = [&](int64_t
value) -> Definition* {
405#if defined(TARGET_ARCH_IS_32_BIT)
406 Definition*
const const_def =
new (
Z) UnboxedConstantInstr(
407 Smi::ZoneHandle(
Z, Smi::New(
value)), kUnboxedInt32);
408 InsertBefore(instr, const_def,
nullptr, FlowGraph::kValue);
409 return new (
Z) IntConverterInstr(kUnboxedInt32, kUnboxedInt64,
412 return new (
Z) UnboxedConstantInstr(Smi::ZoneHandle(
Z, Smi::New(
value)),
417 if (!right_value->BindsToConstant()) {
421 const Object& rhs = right_value->BoundConstant();
422 const int64_t
value = Integer::Cast(rhs).AsInt64Value();
437 if (op_kind == Token::kMOD) {
440 left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
442 Definition* right_definition = unboxed_constant(
magnitude - 1);
443 if (
magnitude == 1)
return right_definition;
444 InsertBefore(instr, right_definition,
nullptr, FlowGraph::kValue);
445 right_value =
new (
Z)
Value(right_definition);
447 BinaryInt64OpInstr(Token::kBIT_AND, left_value, right_value,
451#if !defined(TARGET_ARCH_IS_32_BIT)
458 const bool negate =
value < 0;
459 Definition*
result =
nullptr;
461 left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
476 auto*
const sign_bit_position = unboxed_constant(63);
477 InsertBefore(instr, sign_bit_position,
nullptr,
479 auto*
const sign_bit_extended =
new (
Z)
480 ShiftInt64OpInstr(Token::kSHR, left_value,
482 InsertBefore(instr, sign_bit_extended,
nullptr,
484 auto* rounding_adjustment = unboxed_constant(
magnitude - 1);
485 InsertBefore(instr, rounding_adjustment,
nullptr,
487 rounding_adjustment =
new (
Z)
488 BinaryInt64OpInstr(Token::kBIT_AND,
new (
Z)
Value(sign_bit_extended),
490 Instruction::kNotSpeculative);
491 InsertBefore(instr, rounding_adjustment,
nullptr,
493 auto*
const left_definition =
new (
Z)
494 BinaryInt64OpInstr(Token::kADD, left_value->CopyWithType(
Z),
496 Instruction::kNotSpeculative);
497 InsertBefore(instr, left_definition,
nullptr, FlowGraph::kValue);
498 left_value =
new (
Z)
Value(left_definition);
499 auto*
const right_definition =
500 unboxed_constant(Utils::ShiftForPowerOfTwo(
magnitude));
501 InsertBefore(instr, right_definition,
nullptr, FlowGraph::kValue);
502 right_value =
new (
Z)
Value(right_definition);
503 result =
new (
Z) ShiftInt64OpInstr(Token::kSHR, left_value, right_value,
508 result =
new (
Z) RedefinitionInstr(left_value);
511 InsertBefore(instr,
result,
nullptr, FlowGraph::kValue);
519bool AotCallSpecializer::TryOptimizeIntegerOperation(TemplateDartCall<0>* instr,
521 if (instr->type_args_len() != 0) {
526 Definition* replacement =
nullptr;
527 if (instr->ArgumentCount() == 2) {
528 Value* left_value = instr->ArgumentValueAt(0);
529 Value* right_value = instr->ArgumentValueAt(1);
530 CompileType* left_type = left_value->Type();
531 CompileType* right_type = right_value->Type();
533 bool has_nullable_int_args =
534 left_type->IsNullableInt() && right_type->IsNullableInt();
536 if (
auto*
call = instr->AsInstanceCall()) {
537 if (!
call->CanReceiverBeSmiBasedOnInterfaceTarget(
Z)) {
538 has_nullable_int_args =
false;
544 if (!has_nullable_int_args) {
551 const bool either_can_be_null =
552 left_type->is_nullable() || right_type->is_nullable();
553 replacement =
new (
Z) EqualityCompareInstr(
554 instr->source(), op_kind, left_value->CopyWithType(
Z),
556 either_can_be_null, Instruction::kNotSpeculative);
563 left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
564 right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
565 replacement =
new (
Z) RelationalOpInstr(
566 instr->source(), op_kind, left_value, right_value, kMintCid,
570 case Token::kTRUNCDIV:
571 replacement = TryOptimizeDivisionOperation(instr, op_kind, left_value,
573 if (replacement !=
nullptr)
break;
574#if defined(TARGET_ARCH_IS_32_BIT)
590 case Token::kBIT_XOR:
592 case Token::kBIT_AND:
599 if (op_kind == Token::kSHL || op_kind == Token::kSHR ||
600 op_kind == Token::kUSHR) {
601 left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
602 right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
603 replacement =
new (
Z) ShiftInt64OpInstr(op_kind, left_value,
606 left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
607 right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
608 replacement =
new (
Z)
609 BinaryInt64OpInstr(op_kind, left_value, right_value,
618 }
else if (instr->ArgumentCount() == 1) {
619 Value* left_value = instr->ArgumentValueAt(0);
620 CompileType* left_type = left_value->Type();
623 if (!left_type->IsNullableInt()) {
627 if (op_kind == Token::kNEGATE || op_kind == Token::kBIT_NOT) {
628 left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
629 replacement =
new (
Z) UnaryInt64OpInstr(
630 op_kind, left_value,
DeoptId::kNone, Instruction::kNotSpeculative);
634 if (replacement !=
nullptr && !replacement->ComputeCanDeoptimize()) {
635 if (FLAG_trace_strong_mode_types) {
636 THR_Print(
"[Strong mode] Optimization: replacing %s with %s\n",
637 instr->ToCString(), replacement->ToCString());
639 ReplaceCall(instr, replacement);
647bool AotCallSpecializer::TryOptimizeDoubleOperation(TemplateDartCall<0>* instr,
649 if (instr->type_args_len() != 0) {
654 Definition* replacement =
nullptr;
656 if (instr->ArgumentCount() == 2) {
657 Value* left_value = instr->ArgumentValueAt(0);
658 Value* right_value = instr->ArgumentValueAt(1);
659 CompileType* left_type = left_value->Type();
660 CompileType* right_type = right_value->Type();
662 if (!left_type->IsNullableDouble() &&
663 !IsSupportedIntOperandForStaticDoubleOp(left_type)) {
666 if (!right_type->IsNullableDouble() &&
667 !IsSupportedIntOperandForStaticDoubleOp(right_type)) {
677 if (!left_type->is_nullable() && !right_type->is_nullable()) {
678 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
679 right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
680 replacement =
new (
Z) EqualityCompareInstr(
681 instr->source(), op_kind, left_value, right_value, kDoubleCid,
683 Instruction::kNotSpeculative);
695 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
696 right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
697 replacement =
new (
Z) RelationalOpInstr(
698 instr->source(), op_kind, left_value, right_value, kDoubleCid,
709 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
710 right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
711 replacement =
new (
Z) BinaryDoubleOpInstr(
712 op_kind, left_value, right_value,
DeoptId::kNone, instr->source(),
713 Instruction::kNotSpeculative);
719 case Token::kBIT_XOR:
721 case Token::kBIT_AND:
725 case Token::kTRUNCDIV:
730 }
else if (instr->ArgumentCount() == 1) {
731 Value* left_value = instr->ArgumentValueAt(0);
732 CompileType* left_type = left_value->Type();
735 if (!left_type->IsNullableDouble()) {
739 if (op_kind == Token::kNEGATE) {
740 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
741 replacement =
new (
Z)
742 UnaryDoubleOpInstr(Token::kNEGATE, left_value, instr->deopt_id(),
743 Instruction::kNotSpeculative);
747 if (replacement !=
nullptr && !replacement->ComputeCanDeoptimize()) {
748 if (FLAG_trace_strong_mode_types) {
749 THR_Print(
"[Strong mode] Optimization: replacing %s with %s\n",
750 instr->ToCString(), replacement->ToCString());
752 ReplaceCall(instr, replacement);
764void AotCallSpecializer::VisitInstanceCall(InstanceCallInstr* instr) {
767 if (Token::IsTypeTestOperator(op_kind)) {
768 ReplaceWithInstanceOf(instr);
772 if (TryInlineFieldAccess(instr)) {
776 if (RecognizeRuntimeTypeGetter(instr)) {
780 if ((op_kind == Token::kEQ) && TryReplaceWithHaveSameRuntimeType(instr)) {
784 const CallTargets& targets = instr->Targets();
785 const intptr_t receiver_idx = instr->FirstArgIndex();
787 if (TryOptimizeInstanceCallUsingStaticTypes(instr)) {
791 bool has_one_target = targets.HasSingleTarget();
792 if (has_one_target) {
795 const Function&
target = targets.FirstTarget();
797 !
target.is_polymorphic_target() && !
target.IsDynamicallyOverridden();
800 if (has_one_target) {
801 const Function&
target = targets.FirstTarget();
803 if (flow_graph()->CheckForInstanceCall(instr, function_kind) ==
804 FlowGraph::ToCheck::kNoCheck) {
805 StaticCallInstr*
call = StaticCallInstr::FromCall(
806 Z, instr,
target, targets.AggregateCallCount());
807 instr->ReplaceWith(
call, current_iterator());
813 const intptr_t receiver_cid =
814 instr->ArgumentValueAt(receiver_idx)->Type()->ToCid();
815 if (receiver_cid !=
kDynamicCid && receiver_cid != kSentinelCid) {
816 const Class& receiver_class =
817 Class::Handle(
Z, isolate_group()->class_table()->At(receiver_cid));
819 Function::Handle(
Z, instr->ResolveForReceiverClass(receiver_class));
822 StaticCallInstr*
call =
823 StaticCallInstr::FromCall(
Z, instr,
target, instr->CallCount());
824 instr->ReplaceWith(
call, current_iterator());
831 if (instr->token_kind() == Token::kEQ || instr->token_kind() == Token::kNE) {
832 GrowableArray<intptr_t> class_ids(6);
833 if (instr->ArgumentValueAt(receiver_idx)->Type()->Specialize(&class_ids)) {
834 bool is_object_eq =
true;
835 for (intptr_t
i = 0;
i < class_ids.length();
i++) {
836 const intptr_t
cid = class_ids[
i];
839 if (
cid == kSentinelCid)
continue;
841 Class::Handle(
Z, isolate_group()->class_table()->At(
cid));
843 Function::Handle(
Z, instr->ResolveForReceiverClass(cls));
844 if (
target.recognized_kind() != MethodRecognizer::kObjectEquals) {
845 is_object_eq =
false;
850 auto* replacement =
new (
Z) StrictCompareInstr(
852 (instr->token_kind() == Token::kEQ) ? Token::kEQ_STRICT
854 instr->ArgumentValueAt(0)->CopyWithType(
Z),
855 instr->ArgumentValueAt(1)->CopyWithType(
Z),
857 ReplaceCall(instr, replacement);
864 Definition* callee_receiver = instr->ArgumentAt(receiver_idx);
865 const Function&
function = flow_graph()->function();
866 Class& receiver_class = Class::Handle(
Z);
869 flow_graph()->IsReceiver(callee_receiver)) {
874 CompileType*
type = instr->ArgumentAt(receiver_idx)->Type();
875 if (
type->ToAbstractType()->IsType() &&
876 !
type->ToAbstractType()->IsDynamicType() && !
type->is_nullable()) {
877 receiver_class =
type->ToAbstractType()->type_class();
878 if (receiver_class.is_implemented()) {
879 receiver_class = Class::null();
883 if (!receiver_class.IsNull()) {
884 GrowableArray<intptr_t> class_ids(6);
885 if (thread()->compiler_state().cha().ConcreteSubclasses(receiver_class,
892 Function& single_target = Function::Handle(
Z);
893 ICData& ic_data = ICData::Handle(
Z);
894 const Array& args_desc_array =
895 Array::Handle(
Z, instr->GetArgumentsDescriptor());
896 Function&
target = Function::Handle(
Z);
897 Class& cls = Class::Handle(
Z);
898 for (intptr_t
i = 0;
i < class_ids.length();
i++) {
899 const intptr_t
cid = class_ids[
i];
900 cls = isolate_group()->class_table()->At(
cid);
901 target = instr->ResolveForReceiverClass(cls);
904 single_target = Function::null();
905 ic_data = ICData::null();
907 }
else if (ic_data.IsNull()) {
909 if (single_target.IsNull()) {
911 single_target =
target.ptr();
913 }
else if (single_target.ptr() ==
target.ptr()) {
919 if (class_ids.length() > FLAG_max_exhaustive_polymorphic_checks) {
920 single_target = Function::null();
926 ic_data = ICData::New(
function, instr->function_name(),
928 1, ICData::kOptimized);
929 for (intptr_t j = 0; j <
i; j++) {
930 ic_data.AddReceiverCheck(class_ids[j], single_target);
933 single_target = Function::null();
936 ASSERT(ic_data.ptr() != ICData::null());
937 ASSERT(single_target.ptr() == Function::null());
941 if (single_target.ptr() != Function::null()) {
944 if ((op_kind == Token::kGET) || (op_kind == Token::kSET)) {
946 const ICData& ic_data = ICData::Handle(
947 ICData::New(flow_graph()->
function(), instr->function_name(),
949 1, ICData::kOptimized));
950 cls = single_target.Owner();
951 ic_data.AddReceiverCheck(cls.id(), single_target);
952 instr->set_ic_data(&ic_data);
954 if (TryInlineFieldAccess(instr)) {
961 const Function&
target = Function::ZoneHandle(
Z, single_target.ptr());
962 StaticCallInstr*
call =
963 StaticCallInstr::FromCall(
Z, instr,
target, instr->CallCount());
964 instr->ReplaceWith(
call, current_iterator());
966 }
else if ((ic_data.ptr() != ICData::null()) &&
967 !ic_data.NumberOfChecksIs(0)) {
969 ASSERT(!targets->is_empty());
970 PolymorphicInstanceCallInstr*
call =
971 PolymorphicInstanceCallInstr::FromCall(
Z, instr, *targets,
973 instr->ReplaceWith(
call, current_iterator());
980 if (TryExpandCallThroughGetter(receiver_class, instr)) {
987 if (targets.length() > 0) {
988 ASSERT(!FLAG_polymorphic_with_deopt);
991 PolymorphicInstanceCallInstr*
call =
992 PolymorphicInstanceCallInstr::FromCall(
Z, instr, targets,
994 instr->ReplaceWith(
call, current_iterator());
999void AotCallSpecializer::VisitStaticCall(StaticCallInstr* instr) {
1000 if (TryInlineFieldAccess(instr)) {
1003 CallSpecializer::VisitStaticCall(instr);
1006bool AotCallSpecializer::TryExpandCallThroughGetter(
const Class& receiver_class,
1007 InstanceCallInstr*
call) {
1009 if (
call->token_kind() == Token::kGET ||
call->token_kind() == Token::kSET) {
1019 Function&
target = Function::Handle(
Z);
1021 const String& getter_name =
1022 String::ZoneHandle(
Z, Symbols::FromGet(thread(),
call->function_name()));
1024 const Array& args_desc_array = Array::Handle(
1026 ArgumentsDescriptor::NewBoxed(0, 1));
1027 ArgumentsDescriptor args_desc(args_desc_array);
1028 target = Resolver::ResolveDynamicForReceiverClass(
1029 receiver_class, getter_name, args_desc,
false);
1030 if (
target.ptr() == Function::null() ||
target.IsMethodExtractor()) {
1039 const intptr_t receiver_idx =
call->type_args_len() > 0 ? 1 : 0;
1042 get_arguments.Add(
call->ArgumentValueAt(receiver_idx)->CopyWithType(
Z));
1043 InstanceCallInstr* invoke_get =
new (
Z) InstanceCallInstr(
1044 call->source(), getter_name, Token::kGET, std::move(get_arguments),
1046 Object::empty_array(),
1048 thread()->compiler_state().GetNextDeoptId());
1054 if (
call->type_args_len() > 0) {
1055 call_arguments.Add(
call->ArgumentValueAt(0)->CopyWithType(
Z));
1057 call_arguments.Add(
new (
Z)
Value(invoke_get));
1058 for (intptr_t
i = receiver_idx + 1;
i <
call->ArgumentCount();
i++) {
1059 call_arguments.Add(
call->ArgumentValueAt(
i)->CopyWithType(
Z));
1062 InstanceCallInstr* invoke_call =
new (
Z) InstanceCallInstr(
1064 std::move(call_arguments),
call->type_args_len(),
call->argument_names(),
1066 thread()->compiler_state().GetNextDeoptId());
1069 Environment* get_env =
1070 call->env()->DeepCopy(
Z,
call->env()->Length() -
call->ArgumentCount());
1071 for (intptr_t
i = 0, n = invoke_get->ArgumentCount();
i < n;
i++) {
1072 get_env->PushValue(
new (
Z)
Value(invoke_get->ArgumentAt(
i)));
1074 InsertBefore(
call, invoke_get, get_env, FlowGraph::kValue);
1077 call->ReplaceWith(invoke_call, current_iterator());
1081 Environment* invoke_env = invoke_call->env();
1083 ->ValueAt(invoke_env->Length() - invoke_call->ArgumentCount() +
1085 ->BindToEnvironment(invoke_get);
1088 invoke_get->EnsureICData(flow_graph());
1089 invoke_call->EnsureICData(flow_graph());
1092 TryCreateICData(invoke_get);
1093 VisitInstanceCall(invoke_get);
1094 TryCreateICData(invoke_call);
1095 VisitInstanceCall(invoke_call);
1101void AotCallSpecializer::VisitPolymorphicInstanceCall(
1102 PolymorphicInstanceCallInstr*
call) {
1103 const intptr_t receiver_idx =
call->type_args_len() > 0 ? 1 : 0;
1104 const intptr_t receiver_cid =
1105 call->ArgumentValueAt(receiver_idx)->Type()->ToCid();
1106 if (receiver_cid !=
kDynamicCid && receiver_cid != kSentinelCid) {
1107 const Class& receiver_class =
1108 Class::Handle(
Z, isolate_group()->class_table()->At(receiver_cid));
1110 Function::ZoneHandle(
Z,
call->ResolveForReceiverClass(receiver_class));
1113 StaticCallInstr* new_call =
1115 call->ReplaceWith(new_call, current_iterator());
1120bool AotCallSpecializer::TryReplaceInstanceOfWithRangeCheck(
1121 InstanceCallInstr*
call,
1122 const AbstractType&
type) {
1123 HierarchyInfo* hi = thread()->hierarchy_info();
1124 if (hi ==
nullptr) {
1128 intptr_t lower_limit, upper_limit;
1129 if (!hi->InstanceOfHasClassRange(
type, &lower_limit, &upper_limit)) {
1133 Definition*
left =
call->ArgumentAt(0);
1134 LoadClassIdInstr* load_cid =
1136 InsertBefore(
call, load_cid,
nullptr, FlowGraph::kValue);
1138 ComparisonInstr* check_range;
1139 if (lower_limit == upper_limit) {
1140 ConstantInstr* cid_constant = flow_graph()->GetConstant(
1142 check_range =
new (
Z) EqualityCompareInstr(
1143 call->source(), Token::kEQ,
new Value(load_cid),
1145 Instruction::kNotSpeculative);
1148 new (
Z) TestRangeInstr(
call->source(),
new (
Z)
Value(load_cid),
1151 ReplaceCall(
call, check_range);
1156void AotCallSpecializer::ReplaceInstanceCallsWithDispatchTableCalls() {
1157 ASSERT(current_iterator_ ==
nullptr);
1158 const intptr_t max_block_id = flow_graph()->max_block_id();
1159 for (BlockIterator block_it = flow_graph()->reverse_postorder_iterator();
1160 !block_it.Done(); block_it.Advance()) {
1161 ForwardInstructionIterator it(block_it.Current());
1162 current_iterator_ = ⁢
1163 while (!it.Done()) {
1164 Instruction* instr = it.Current();
1168 if (!it.Done()) it.Advance();
1170 if (
auto call = instr->AsInstanceCall()) {
1171 TryReplaceWithDispatchTableCall(
call);
1172 }
else if (
auto call = instr->AsPolymorphicInstanceCall()) {
1173 TryReplaceWithDispatchTableCall(
call);
1176 current_iterator_ =
nullptr;
1178 if (flow_graph()->max_block_id() != max_block_id) {
1179 flow_graph()->DiscoverBlocks();
1183const Function& AotCallSpecializer::InterfaceTargetForTableDispatch(
1184 InstanceCallBaseInstr*
call) {
1185 const Function& interface_target =
call->interface_target();
1186 if (!interface_target.IsNull()) {
1187 return interface_target;
1191 const Function& tearoff_interface_target =
call->tearoff_interface_target();
1192 if (!tearoff_interface_target.IsNull()) {
1194 return Function::ZoneHandle(
1195 Z, tearoff_interface_target.GetMethodExtractor(
call->function_name()));
1199 return Function::null_function();
1202void AotCallSpecializer::TryReplaceWithDispatchTableCall(
1203 InstanceCallBaseInstr*
call) {
1204 const Function& interface_target = InterfaceTargetForTableDispatch(
call);
1205 if (interface_target.IsNull()) {
1210 Value* receiver =
call->ArgumentValueAt(
call->FirstArgIndex());
1211 const compiler::TableSelector* selector =
1212 precompiler_->selector_map()->GetSelector(interface_target);
1214 if (selector ==
nullptr) {
1216 if (!interface_target.IsDynamicallyOverridden()) {
1219 AddCheckNull(receiver->CopyWithType(
Z),
call->function_name(),
1221 StopInstr* stop =
new (
Z) StopInstr(
"Dead instance call executed.");
1222 InsertBefore(
call, stop,
call->env(), FlowGraph::kEffect);
1228 const bool receiver_can_be_smi =
1229 call->CanReceiverBeSmiBasedOnInterfaceTarget(
Z);
1230 auto load_cid =
new (
Z) LoadClassIdInstr(receiver->CopyWithType(
Z),
1232 InsertBefore(
call, load_cid,
call->env(), FlowGraph::kValue);
1234 const auto& cls = Class::Handle(
Z, interface_target.Owner());
1235 if (cls.has_dynamically_extendable_subtypes()) {
1236 ReplaceWithConditionalDispatchTableCall(
call, load_cid, interface_target,
1241 auto dispatch_table_call = DispatchTableCallInstr::FromCall(
1242 Z,
call,
new (
Z)
Value(load_cid), interface_target, selector);
1243 call->ReplaceWith(dispatch_table_call, current_iterator());
1246static void InheritDeoptTargetIfNeeded(Zone* zone,
1248 Instruction* from) {
1249 if (from->env() !=
nullptr) {
1250 instr->InheritDeoptTarget(zone, from);
1254void AotCallSpecializer::ReplaceWithConditionalDispatchTableCall(
1255 InstanceCallBaseInstr*
call,
1256 LoadClassIdInstr* load_cid,
1257 const Function& interface_target,
1258 const compiler::TableSelector* selector) {
1259 BlockEntryInstr* current_block =
call->GetBlock();
1260 const bool has_uses =
call->HasUses();
1262 const intptr_t num_cids = isolate_group()->class_table()->NumCids();
1263 auto*
compare =
new (
Z) TestRangeInstr(
1267 InheritDeoptTargetIfNeeded(
Z, branch,
call);
1269 TargetEntryInstr* true_target =
1270 new (
Z) TargetEntryInstr(flow_graph()->allocate_block_id(),
1272 InheritDeoptTargetIfNeeded(
Z, true_target,
call);
1273 *branch->true_successor_address() = true_target;
1275 TargetEntryInstr* false_target =
1276 new (
Z) TargetEntryInstr(flow_graph()->allocate_block_id(),
1278 InheritDeoptTargetIfNeeded(
Z, false_target,
call);
1279 *branch->false_successor_address() = false_target;
1281 JoinEntryInstr*
join =
1282 new (
Z) JoinEntryInstr(flow_graph()->allocate_block_id(),
1284 InheritDeoptTargetIfNeeded(
Z,
join,
call);
1286 current_block->ReplaceAsPredecessorWith(
join);
1288 for (intptr_t
i = 0, n = current_block->dominated_blocks().length();
i < n;
1290 BlockEntryInstr* block = current_block->dominated_blocks()[
i];
1291 join->AddDominatedBlock(block);
1293 current_block->ClearDominatedBlocks();
1294 current_block->AddDominatedBlock(
join);
1295 current_block->AddDominatedBlock(true_target);
1296 current_block->AddDominatedBlock(false_target);
1298 PhiInstr* phi =
nullptr;
1300 phi =
new (
Z) PhiInstr(
join, 2);
1302 flow_graph()->AllocateSSAIndex(phi);
1303 join->InsertPhi(phi);
1304 phi->UpdateType(*
call->Type());
1305 phi->set_representation(
call->representation());
1306 call->ReplaceUsesWith(phi);
1310 InheritDeoptTargetIfNeeded(
Z, true_goto,
call);
1311 true_target->LinkTo(true_goto);
1312 true_target->set_last_instruction(true_goto);
1315 InheritDeoptTargetIfNeeded(
Z, false_goto,
call);
1316 false_target->LinkTo(false_goto);
1317 false_target->set_last_instruction(false_goto);
1319 auto dispatch_table_call = DispatchTableCallInstr::FromCall(
1320 Z,
call,
new (
Z)
Value(load_cid), interface_target, selector);
1321 ASSERT(dispatch_table_call->representation() ==
call->representation());
1322 InsertBefore(true_goto, dispatch_table_call,
call->env(),
1323 has_uses ? FlowGraph::kValue : FlowGraph::kEffect);
1325 call->previous()->AppendInstruction(branch);
1326 call->set_previous(
nullptr);
1328 call->set_next(
nullptr);
1329 call->UnuseAllInputs();
1330 call->InsertBefore(false_goto);
1331 InheritDeoptTargetIfNeeded(
Z,
call,
call);
1334 phi->SetInputAt(0,
new (
Z)
Value(dispatch_table_call));
1335 dispatch_table_call->AddInputUse(phi->InputAt(0));
1337 call->AddInputUse(phi->InputAt(1));
static sk_sp< Effect > Create()
static double magnitude(double a)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
#define ASSERT_EQUAL(expected, actual)
AotCallSpecializer(Precompiler *precompiler, FlowGraph *flow_graph, SpeculativeInliningPolicy *speculative_policy)
#define THR_Print(format,...)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
Dart_NativeFunction function
static bool CanConvertInt64ToDouble()
constexpr int64_t kMinInt64
static constexpr Representation kUnboxedUword
static void RefineUseTypes(Definition *instr)
GrowableArray< Value * > InputsArray
UnorderedHashMap< FunctionsTraits > UniqueFunctionsMap
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
const char *const function_name
DEF_SWITCHES_START aot vmservice shared library name
int compare(const void *untyped_lhs, const void *untyped_rhs)
static SkString join(const CommandLineFlags::StringArray &)