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 =
171 StaticCallInstr::FromCall(
Z, call,
target,
call->CallCount());
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) {
230 const Token::Kind op_kind =
call->token_kind();
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) {
264 if (FlowGraphCompiler::CanConvertInt64ToDouble()) {
272Value* AotCallSpecializer::PrepareStaticOpInput(
Value* input,
275 ASSERT((cid == kDoubleCid) || (cid == kMintCid));
277 if (input->Type()->is_nullable()) {
279 (
call->IsInstanceCall()
280 ?
call->AsInstanceCall()->function_name()
281 : String::ZoneHandle(
Z,
call->AsStaticCall()->function().name()));
282 AddCheckNull(input, function_name,
call->deopt_id(),
call->env(), call);
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());
292 }
else if (FlowGraphCompiler::CanConvertInt64ToDouble()) {
293 conversion =
new (
Z) Int64ToDoubleInstr(input, DeoptId::kNone,
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();
338static void RefineUseTypes(Definition* instr) {
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) {
347 const Token::Kind op_kind = instr->token_kind();
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) {
388 RefineUseTypes(redefinition);
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* {
404 ASSERT(compiler::target::IsSmi(value));
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,
410 new (
Z)
Value(const_def), DeoptId::kNone);
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();
424 if (value == kMinInt64) {
428 const int64_t
magnitude = Utils::Abs(value);
433 !compiler::target::IsSmi(
magnitude - 1)) {
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,
448 DeoptId::kNone, Instruction::kNotSpeculative);
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,
481 new (
Z)
Value(sign_bit_position), DeoptId::kNone);
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),
489 new (
Z)
Value(rounding_adjustment), DeoptId::kNone,
490 Instruction::kNotSpeculative);
491 InsertBefore(instr, rounding_adjustment,
nullptr,
493 auto*
const left_definition =
new (
Z)
494 BinaryInt64OpInstr(Token::kADD, left_value->CopyWithType(
Z),
495 new (
Z)
Value(rounding_adjustment), DeoptId::kNone,
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,
520 Token::Kind op_kind) {
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(zone())) {
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),
555 right_value->CopyWithType(
Z), kMintCid, DeoptId::kNone,
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,
567 DeoptId::kNone, Instruction::kNotSpeculative);
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,
604 right_value, DeoptId::kNone);
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,
610 DeoptId::kNone, Instruction::kNotSpeculative);
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);
640 RefineUseTypes(replacement);
647bool AotCallSpecializer::TryOptimizeDoubleOperation(TemplateDartCall<0>* instr,
648 Token::Kind op_kind) {
649 if (instr->type_args_len() != 0) {
654 if (!FlowGraphCompiler::SupportsUnboxedDoubles()) {
658 Definition* replacement =
nullptr;
660 if (instr->ArgumentCount() == 2) {
661 Value* left_value = instr->ArgumentValueAt(0);
662 Value* right_value = instr->ArgumentValueAt(1);
663 CompileType* left_type = left_value->Type();
664 CompileType* right_type = right_value->Type();
666 if (!left_type->IsNullableDouble() &&
667 !IsSupportedIntOperandForStaticDoubleOp(left_type)) {
670 if (!right_type->IsNullableDouble() &&
671 !IsSupportedIntOperandForStaticDoubleOp(right_type)) {
681 if (!left_type->is_nullable() && !right_type->is_nullable()) {
682 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
683 right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
684 replacement =
new (
Z) EqualityCompareInstr(
685 instr->source(), op_kind, left_value, right_value, kDoubleCid,
686 DeoptId::kNone,
false,
687 Instruction::kNotSpeculative);
699 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
700 right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
701 replacement =
new (
Z) RelationalOpInstr(
702 instr->source(), op_kind, left_value, right_value, kDoubleCid,
703 DeoptId::kNone, Instruction::kNotSpeculative);
713 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
714 right_value = PrepareStaticOpInput(right_value, kDoubleCid, instr);
715 replacement =
new (
Z) BinaryDoubleOpInstr(
716 op_kind, left_value, right_value, DeoptId::kNone, instr->source(),
717 Instruction::kNotSpeculative);
723 case Token::kBIT_XOR:
725 case Token::kBIT_AND:
729 case Token::kTRUNCDIV:
734 }
else if (instr->ArgumentCount() == 1) {
735 Value* left_value = instr->ArgumentValueAt(0);
736 CompileType* left_type = left_value->Type();
739 if (!left_type->IsNullableDouble()) {
743 if (op_kind == Token::kNEGATE) {
744 left_value = PrepareStaticOpInput(left_value, kDoubleCid, instr);
745 replacement =
new (
Z)
746 UnaryDoubleOpInstr(Token::kNEGATE, left_value, instr->deopt_id(),
747 Instruction::kNotSpeculative);
751 if (replacement !=
nullptr && !replacement->ComputeCanDeoptimize()) {
752 if (FLAG_trace_strong_mode_types) {
753 THR_Print(
"[Strong mode] Optimization: replacing %s with %s\n",
754 instr->ToCString(), replacement->ToCString());
756 ReplaceCall(instr, replacement);
757 RefineUseTypes(replacement);
768void AotCallSpecializer::VisitInstanceCall(InstanceCallInstr* instr) {
770 const Token::Kind op_kind = instr->token_kind();
771 if (Token::IsTypeTestOperator(op_kind)) {
772 ReplaceWithInstanceOf(instr);
776 if (TryInlineFieldAccess(instr)) {
780 if (RecognizeRuntimeTypeGetter(instr)) {
784 if ((op_kind == Token::kEQ) && TryReplaceWithHaveSameRuntimeType(instr)) {
788 const CallTargets& targets = instr->Targets();
789 const intptr_t receiver_idx = instr->FirstArgIndex();
791 if (TryOptimizeInstanceCallUsingStaticTypes(instr)) {
795 bool has_one_target = targets.HasSingleTarget();
796 if (has_one_target) {
799 const Function&
target = targets.FirstTarget();
800 has_one_target = !
target.is_polymorphic_target();
803 if (has_one_target) {
804 const Function&
target = targets.FirstTarget();
805 UntaggedFunction::Kind function_kind =
target.kind();
806 if (flow_graph()->CheckForInstanceCall(instr, function_kind) ==
807 FlowGraph::ToCheck::kNoCheck) {
808 StaticCallInstr*
call = StaticCallInstr::FromCall(
809 Z, instr,
target, targets.AggregateCallCount());
810 instr->ReplaceWith(call, current_iterator());
816 const intptr_t receiver_cid =
817 instr->ArgumentValueAt(receiver_idx)->Type()->ToCid();
818 if (receiver_cid != kDynamicCid && receiver_cid != kSentinelCid) {
819 const Class& receiver_class =
820 Class::Handle(
Z, isolate_group()->class_table()->At(receiver_cid));
822 Function::Handle(
Z, instr->ResolveForReceiverClass(receiver_class));
825 StaticCallInstr*
call =
826 StaticCallInstr::FromCall(
Z, instr,
target, instr->CallCount());
827 instr->ReplaceWith(call, current_iterator());
834 if (instr->token_kind() == Token::kEQ || instr->token_kind() == Token::kNE) {
835 GrowableArray<intptr_t> class_ids(6);
836 if (instr->ArgumentValueAt(receiver_idx)->Type()->Specialize(&class_ids)) {
837 bool is_object_eq =
true;
838 for (intptr_t i = 0; i < class_ids.length(); i++) {
839 const intptr_t
cid = class_ids[i];
842 if (cid == kSentinelCid)
continue;
844 Class::Handle(
Z, isolate_group()->class_table()->At(cid));
846 Function::Handle(
Z, instr->ResolveForReceiverClass(cls));
847 if (
target.recognized_kind() != MethodRecognizer::kObjectEquals) {
848 is_object_eq =
false;
853 auto* replacement =
new (
Z) StrictCompareInstr(
855 (instr->token_kind() == Token::kEQ) ? Token::kEQ_STRICT
857 instr->ArgumentValueAt(0)->CopyWithType(
Z),
858 instr->ArgumentValueAt(1)->CopyWithType(
Z),
860 ReplaceCall(instr, replacement);
861 RefineUseTypes(replacement);
867 Definition* callee_receiver = instr->ArgumentAt(receiver_idx);
868 const Function&
function = flow_graph()->function();
869 Class& receiver_class = Class::Handle(
Z);
872 flow_graph()->IsReceiver(callee_receiver)) {
877 CompileType*
type = instr->ArgumentAt(receiver_idx)->Type();
878 if (
type->ToAbstractType()->IsType() &&
879 !
type->ToAbstractType()->IsDynamicType() && !
type->is_nullable()) {
880 receiver_class =
type->ToAbstractType()->type_class();
881 if (receiver_class.is_implemented()) {
882 receiver_class = Class::null();
886 if (!receiver_class.IsNull()) {
887 GrowableArray<intptr_t> class_ids(6);
888 if (thread()->compiler_state().cha().ConcreteSubclasses(receiver_class,
895 Function& single_target = Function::Handle(
Z);
896 ICData& ic_data = ICData::Handle(
Z);
897 const Array& args_desc_array =
898 Array::Handle(
Z, instr->GetArgumentsDescriptor());
899 Function&
target = Function::Handle(
Z);
900 Class& cls = Class::Handle(
Z);
901 for (intptr_t i = 0; i < class_ids.length(); i++) {
902 const intptr_t
cid = class_ids[i];
903 cls = isolate_group()->class_table()->At(cid);
904 target = instr->ResolveForReceiverClass(cls);
907 single_target = Function::null();
908 ic_data = ICData::null();
910 }
else if (ic_data.IsNull()) {
912 if (single_target.IsNull()) {
914 single_target =
target.ptr();
916 }
else if (single_target.ptr() ==
target.ptr()) {
922 if (class_ids.length() > FLAG_max_exhaustive_polymorphic_checks) {
923 single_target = Function::null();
929 ic_data = ICData::New(
function, instr->function_name(),
930 args_desc_array, DeoptId::kNone,
931 1, ICData::kOptimized);
932 for (intptr_t j = 0; j < i; j++) {
933 ic_data.AddReceiverCheck(class_ids[j], single_target);
936 single_target = Function::null();
939 ASSERT(ic_data.ptr() != ICData::null());
940 ASSERT(single_target.ptr() == Function::null());
941 ic_data.AddReceiverCheck(cid,
target);
944 if (single_target.ptr() != Function::null()) {
947 if ((op_kind == Token::kGET) || (op_kind == Token::kSET)) {
949 const ICData& ic_data = ICData::Handle(
950 ICData::New(flow_graph()->
function(), instr->function_name(),
951 args_desc_array, DeoptId::kNone,
952 1, ICData::kOptimized));
953 cls = single_target.Owner();
954 ic_data.AddReceiverCheck(cls.id(), single_target);
955 instr->set_ic_data(&ic_data);
957 if (TryInlineFieldAccess(instr)) {
964 const Function&
target = Function::ZoneHandle(
Z, single_target.ptr());
965 StaticCallInstr*
call =
966 StaticCallInstr::FromCall(
Z, instr,
target, instr->CallCount());
967 instr->ReplaceWith(call, current_iterator());
969 }
else if ((ic_data.ptr() != ICData::null()) &&
970 !ic_data.NumberOfChecksIs(0)) {
971 const CallTargets* targets = CallTargets::Create(
Z, ic_data);
972 ASSERT(!targets->is_empty());
973 PolymorphicInstanceCallInstr*
call =
974 PolymorphicInstanceCallInstr::FromCall(
Z, instr, *targets,
976 instr->ReplaceWith(call, current_iterator());
983 if (TryExpandCallThroughGetter(receiver_class, instr)) {
990 if (targets.length() > 0) {
991 ASSERT(!FLAG_polymorphic_with_deopt);
994 PolymorphicInstanceCallInstr*
call =
995 PolymorphicInstanceCallInstr::FromCall(
Z, instr, targets,
997 instr->ReplaceWith(call, current_iterator());
1002void AotCallSpecializer::VisitStaticCall(StaticCallInstr* instr) {
1003 if (TryInlineFieldAccess(instr)) {
1006 CallSpecializer::VisitStaticCall(instr);
1009bool AotCallSpecializer::TryExpandCallThroughGetter(
const Class& receiver_class,
1010 InstanceCallInstr* call) {
1012 if (
call->token_kind() == Token::kGET ||
call->token_kind() == Token::kSET) {
1018 if (
call->function_name().ptr() == Symbols::call().ptr()) {
1022 Function&
target = Function::Handle(
Z);
1024 const String& getter_name =
1025 String::ZoneHandle(
Z, Symbols::FromGet(thread(),
call->function_name()));
1027 const Array& args_desc_array = Array::Handle(
1029 ArgumentsDescriptor::NewBoxed(0, 1));
1030 ArgumentsDescriptor args_desc(args_desc_array);
1031 target = Resolver::ResolveDynamicForReceiverClass(
1032 receiver_class, getter_name, args_desc,
false);
1033 if (
target.ptr() == Function::null() ||
target.IsMethodExtractor()) {
1042 const intptr_t receiver_idx =
call->type_args_len() > 0 ? 1 : 0;
1045 get_arguments.Add(
call->ArgumentValueAt(receiver_idx)->CopyWithType(
Z));
1046 InstanceCallInstr* invoke_get =
new (
Z) InstanceCallInstr(
1047 call->source(), getter_name, Token::kGET, std::move(get_arguments),
1049 Object::empty_array(),
1051 thread()->compiler_state().GetNextDeoptId());
1057 if (
call->type_args_len() > 0) {
1058 call_arguments.Add(
call->ArgumentValueAt(0)->CopyWithType(
Z));
1060 call_arguments.Add(
new (
Z)
Value(invoke_get));
1061 for (intptr_t i = receiver_idx + 1; i <
call->ArgumentCount(); i++) {
1062 call_arguments.Add(
call->ArgumentValueAt(i)->CopyWithType(
Z));
1065 InstanceCallInstr* invoke_call =
new (
Z) InstanceCallInstr(
1066 call->source(), Symbols::call(), Token::kILLEGAL,
1067 std::move(call_arguments),
call->type_args_len(),
call->argument_names(),
1069 thread()->compiler_state().GetNextDeoptId());
1072 Environment* get_env =
1073 call->env()->DeepCopy(
Z,
call->env()->Length() -
call->ArgumentCount());
1074 for (intptr_t i = 0, n = invoke_get->ArgumentCount(); i < n; i++) {
1075 get_env->PushValue(
new (
Z)
Value(invoke_get->ArgumentAt(i)));
1077 InsertBefore(call, invoke_get, get_env, FlowGraph::kValue);
1080 call->ReplaceWith(invoke_call, current_iterator());
1084 Environment* invoke_env = invoke_call->env();
1086 ->ValueAt(invoke_env->Length() - invoke_call->ArgumentCount() +
1088 ->BindToEnvironment(invoke_get);
1091 invoke_get->EnsureICData(flow_graph());
1092 invoke_call->EnsureICData(flow_graph());
1095 TryCreateICData(invoke_get);
1096 VisitInstanceCall(invoke_get);
1097 TryCreateICData(invoke_call);
1098 VisitInstanceCall(invoke_call);
1104void AotCallSpecializer::VisitPolymorphicInstanceCall(
1105 PolymorphicInstanceCallInstr* call) {
1106 const intptr_t receiver_idx =
call->type_args_len() > 0 ? 1 : 0;
1107 const intptr_t receiver_cid =
1108 call->ArgumentValueAt(receiver_idx)->Type()->ToCid();
1109 if (receiver_cid != kDynamicCid && receiver_cid != kSentinelCid) {
1110 const Class& receiver_class =
1111 Class::Handle(
Z, isolate_group()->class_table()->At(receiver_cid));
1113 Function::ZoneHandle(
Z,
call->ResolveForReceiverClass(receiver_class));
1116 StaticCallInstr* new_call =
1117 StaticCallInstr::FromCall(
Z, call,
function,
call->CallCount());
1118 call->ReplaceWith(new_call, current_iterator());
1123bool AotCallSpecializer::TryReplaceInstanceOfWithRangeCheck(
1124 InstanceCallInstr* call,
1125 const AbstractType&
type) {
1126 HierarchyInfo* hi = thread()->hierarchy_info();
1127 if (hi ==
nullptr) {
1131 intptr_t lower_limit, upper_limit;
1132 if (!hi->InstanceOfHasClassRange(
type, &lower_limit, &upper_limit)) {
1136 Definition*
left =
call->ArgumentAt(0);
1137 LoadClassIdInstr* load_cid =
1138 new (
Z) LoadClassIdInstr(
new (
Z)
Value(
left), kUnboxedUword);
1139 InsertBefore(call, load_cid,
nullptr, FlowGraph::kValue);
1141 ComparisonInstr* check_range;
1142 if (lower_limit == upper_limit) {
1143 ConstantInstr* cid_constant = flow_graph()->GetConstant(
1144 Smi::Handle(
Z, Smi::New(lower_limit)), kUnboxedUword);
1145 check_range =
new (
Z) EqualityCompareInstr(
1146 call->source(), Token::kEQ,
new Value(load_cid),
1147 new Value(cid_constant), kIntegerCid, DeoptId::kNone,
false,
1148 Instruction::kNotSpeculative);
1151 new (
Z) TestRangeInstr(
call->source(),
new (
Z)
Value(load_cid),
1152 lower_limit, upper_limit, kUnboxedUword);
1154 ReplaceCall(call, check_range);
1159void AotCallSpecializer::ReplaceInstanceCallsWithDispatchTableCalls() {
1160 ASSERT(current_iterator_ ==
nullptr);
1161 for (BlockIterator block_it = flow_graph()->reverse_postorder_iterator();
1162 !block_it.Done(); block_it.Advance()) {
1163 ForwardInstructionIterator it(block_it.Current());
1164 current_iterator_ = ⁢
1165 for (; !it.Done(); it.Advance()) {
1166 Instruction* instr = it.Current();
1167 if (
auto call = instr->AsInstanceCall()) {
1168 TryReplaceWithDispatchTableCall(call);
1169 }
else if (
auto call = instr->AsPolymorphicInstanceCall()) {
1170 TryReplaceWithDispatchTableCall(call);
1173 current_iterator_ =
nullptr;
1177const Function& AotCallSpecializer::InterfaceTargetForTableDispatch(
1178 InstanceCallBaseInstr* call) {
1179 const Function& interface_target =
call->interface_target();
1180 if (!interface_target.IsNull()) {
1181 return interface_target;
1185 const Function& tearoff_interface_target =
call->tearoff_interface_target();
1186 if (!tearoff_interface_target.IsNull()) {
1188 return Function::ZoneHandle(
1189 Z, tearoff_interface_target.GetMethodExtractor(
call->function_name()));
1193 return Function::null_function();
1196void AotCallSpecializer::TryReplaceWithDispatchTableCall(
1197 InstanceCallBaseInstr* call) {
1198 const Function& interface_target = InterfaceTargetForTableDispatch(call);
1199 if (interface_target.IsNull()) {
1204 Value* receiver =
call->ArgumentValueAt(
call->FirstArgIndex());
1205 const compiler::TableSelector* selector =
1206 precompiler_->selector_map()->GetSelector(interface_target);
1208 if (selector ==
nullptr) {
1212 AddCheckNull(receiver->CopyWithType(
Z),
call->function_name(),
1213 DeoptId::kNone,
call->env(), call);
1214 StopInstr* stop =
new (
Z) StopInstr(
"Dead instance call executed.");
1215 InsertBefore(call, stop,
call->env(), FlowGraph::kEffect);
1220 const bool receiver_can_be_smi =
1221 call->CanReceiverBeSmiBasedOnInterfaceTarget(zone());
1222 auto load_cid =
new (
Z) LoadClassIdInstr(receiver->CopyWithType(
Z),
1223 kUnboxedUword, receiver_can_be_smi);
1224 InsertBefore(call, load_cid,
call->env(), FlowGraph::kValue);
1226 auto dispatch_table_call = DispatchTableCallInstr::FromCall(
1227 Z, call,
new (
Z)
Value(load_cid), interface_target, selector);
1228 call->ReplaceWith(dispatch_table_call, current_iterator());
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
#define DEFINE_FLAG(type, name, default_value, comment)
Dart_NativeFunction function
GrowableArray< Value * > InputsArray
UnorderedHashMap< FunctionsTraits > UniqueFunctionsMap
const char *const function_name