Index: runtime/vm/aot_optimizer.cc |
diff --git a/runtime/vm/aot_optimizer.cc b/runtime/vm/aot_optimizer.cc |
index 25074516d074f0c4042effded046301a806dbc92..63a62534eb28675731d897b186275c10dfdb6524 100644 |
--- a/runtime/vm/aot_optimizer.cc |
+++ b/runtime/vm/aot_optimizer.cc |
@@ -2465,58 +2465,104 @@ void AotOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
flow_graph_->IsReceiver(callee_receiver)) { |
// Call receiver is method receiver. |
Class& receiver_class = Class::Handle(Z, function.Owner()); |
+ |
GrowableArray<intptr_t> class_ids(6); |
if (thread()->cha()->ConcreteSubclasses(receiver_class, &class_ids)) { |
- if (class_ids.length() <= FLAG_max_exhaustive_polymorphic_checks) { |
- if (FLAG_trace_cha) { |
- THR_Print(" **(CHA) Only %" Pd " concrete subclasses of %s for %s\n", |
- class_ids.length(), |
- receiver_class.ToCString(), |
- instr->function_name().ToCString()); |
- } |
- |
- const Array& args_desc_array = Array::Handle(Z, |
- ArgumentsDescriptor::New(instr->ArgumentCount(), |
- instr->argument_names())); |
- ArgumentsDescriptor args_desc(args_desc_array); |
+ // First check if all subclasses end up calling the same method. |
+ // If this is the case we will replace instance call with a direct |
+ // static call. |
+ // Otherwise we will try to create ICData that contains all possible |
+ // targets with appropriate checks. |
+ Function& single_target = Function::Handle(Z); |
+ ICData& ic_data = ICData::Handle(Z); |
+ |
+ const Array& args_desc_array = Array::Handle(Z, |
+ ArgumentsDescriptor::New(instr->ArgumentCount(), |
+ instr->argument_names())); |
+ ArgumentsDescriptor args_desc(args_desc_array); |
+ |
+ Function& target = Function::Handle(Z); |
+ Class& cls = Class::Handle(Z); |
+ for (intptr_t i = 0; i < class_ids.length(); i++) { |
+ const intptr_t cid = class_ids[i]; |
+ cls = isolate()->class_table()->At(cid); |
+ target = Resolver::ResolveDynamicForReceiverClass( |
+ cls, |
+ instr->function_name(), |
+ args_desc); |
+ |
+ if (target.IsNull()) { |
+ // Can't resolve the target. It might be a noSuchMethod, |
+ // call through getter or closurization. |
+ single_target = Function::null(); |
+ ic_data = ICData::null(); |
+ break; |
+ } else if (ic_data.IsNull()) { |
+ // First we are trying to compute a single target for all subclasses. |
+ if (single_target.IsNull()) { |
+ ASSERT(i == 0); |
+ single_target = target.raw(); |
+ continue; |
+ } else if (single_target.raw() == target.raw()) { |
+ continue; |
+ } |
- const ICData& ic_data = ICData::Handle( |
- ICData::New(function, |
- instr->function_name(), |
- args_desc_array, |
- Thread::kNoDeoptId, |
- /* args_tested = */ 1)); |
+ // The call does not resolve to a single target within the hierarchy. |
+ // If we have too many subclasses abort the optimization. |
+ if (class_ids.length() > FLAG_max_exhaustive_polymorphic_checks) { |
+ single_target = Function::null(); |
+ break; |
+ } |
- Function& target = Function::Handle(Z); |
- Class& cls = Class::Handle(Z); |
- bool includes_dispatcher_case = false; |
- for (intptr_t i = 0; i < class_ids.length(); i++) { |
- intptr_t cid = class_ids[i]; |
- cls = isolate()->class_table()->At(cid); |
- target = Resolver::ResolveDynamicForReceiverClass( |
- cls, |
- instr->function_name(), |
- args_desc); |
- if (target.IsNull()) { |
- // noSuchMethod, call through getter or closurization |
- includes_dispatcher_case = true; |
- } else { |
- ic_data.AddReceiverCheck(cid, target); |
+ // Create an ICData and map all previously seen classes (< i) to |
+ // the computed single_target. |
+ ic_data = ICData::New(function, |
+ instr->function_name(), |
+ args_desc_array, |
+ Thread::kNoDeoptId, |
+ /* args_tested = */ 1); |
+ for (intptr_t j = 0; j < i; j++) { |
+ ic_data.AddReceiverCheck(class_ids[j], single_target); |
} |
+ |
+ single_target = Function::null(); |
} |
- if (!includes_dispatcher_case && (ic_data.NumberOfChecks() > 0)) { |
- PolymorphicInstanceCallInstr* call = |
- new(Z) PolymorphicInstanceCallInstr(instr, ic_data, |
- /* with_checks = */ true, |
- /* complete = */ true); |
- instr->ReplaceWith(call, current_iterator()); |
- return; |
+ |
+ ASSERT(ic_data.raw() != ICData::null()); |
+ ASSERT(single_target.raw() == Function::null()); |
+ ic_data.AddReceiverCheck(cid, target); |
+ } |
+ |
+ if (single_target.raw() != Function::null()) { |
+ // We have computed that there is only a single target for this call |
+ // within the whole hierarchy. Replace InstanceCall with StaticCall. |
+ ZoneGrowableArray<PushArgumentInstr*>* args = |
+ new (Z) ZoneGrowableArray<PushArgumentInstr*>( |
+ instr->ArgumentCount()); |
+ for (intptr_t i = 0; i < instr->ArgumentCount(); i++) { |
+ args->Add(instr->PushArgumentAt(i)); |
} |
+ StaticCallInstr* call = new (Z) StaticCallInstr( |
Florian Schneider
2016/06/10 15:30:18
Alternatively, maybe you can combine this with the
Vyacheslav Egorov (Google)
2016/06/10 16:05:06
I considered combining it - but then I need to cre
|
+ instr->token_pos(), |
+ Function::ZoneHandle(Z, single_target.raw()), |
+ instr->argument_names(), |
+ args, |
+ instr->deopt_id()); |
+ instr->ReplaceWith(call, current_iterator()); |
+ return; |
+ } else if ((ic_data.raw() != ICData::null()) && |
+ (ic_data.NumberOfChecks() > 0)) { |
+ PolymorphicInstanceCallInstr* call = |
+ new(Z) PolymorphicInstanceCallInstr(instr, ic_data, |
+ /* with_checks = */ true, |
+ /* complete = */ true); |
+ instr->ReplaceWith(call, current_iterator()); |
+ return; |
} |
} |
} |
- // More than one targets. Generate generic polymorphic call without |
+ // More than one target. Generate generic polymorphic call without |
// deoptimization. |
if (instr->ic_data()->NumberOfUsedChecks() > 0) { |
ASSERT(!FLAG_polymorphic_with_deopt); |