From ec9b1c385a4e7824cf133cffb834bb38d65fe1fc Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Fri, 10 Jun 2016 18:12:49 +0200 Subject: [PATCH] VM: Improve specialization of calls on receiver in AOT mode. When encountering call this.m(...) we can check if m resolves to the same method in all concrete subclasses of the receiver class. If it does then we don't need any checks and can just use a StaticCall instead of InstanceCall. This is allows us to improve code quality for cases like: class Base { var _field; foo() { _field = true; } } class A01 extends Base { } ... class A16 extends Base { } Previously AOT would generate InstanceCall(get:_field) in the method foo. However with this change we generate StaticCall(...) which subsequently gets handled by the inliner. R=fschneider@google.com BUG= Review URL: https://codereview.chromium.org/2055263002 . --- runtime/vm/aot_optimizer.cc | 126 ++++++++++++++++++++--------- runtime/vm/intermediate_language.h | 19 +++++ 2 files changed, 105 insertions(+), 40 deletions(-) diff --git a/runtime/vm/aot_optimizer.cc b/runtime/vm/aot_optimizer.cc index 25074516d07..63a62534eb2 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 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()); - } + // 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); + const Array& args_desc_array = Array::Handle(Z, + ArgumentsDescriptor::New(instr->ArgumentCount(), + instr->argument_names())); + ArgumentsDescriptor args_desc(args_desc_array); - const ICData& ic_data = ICData::Handle( - ICData::New(function, - instr->function_name(), - args_desc_array, - Thread::kNoDeoptId, - /* args_tested = */ 1)); + 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); - 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); + 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; } + + // 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; + } + + // 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* args = + new (Z) ZoneGrowableArray( + instr->ArgumentCount()); + for (intptr_t i = 0; i < instr->ArgumentCount(); i++) { + args->Add(instr->PushArgumentAt(i)); } + StaticCallInstr* call = new (Z) StaticCallInstr( + 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); diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h index 27fe9a10743..d048c0e93d9 100644 --- a/runtime/vm/intermediate_language.h +++ b/runtime/vm/intermediate_language.h @@ -3267,6 +3267,25 @@ class StaticCallInstr : public TemplateDefinition<0, Throws> { ASSERT(argument_names.IsZoneHandle() || argument_names.InVMHeap()); } + StaticCallInstr(TokenPosition token_pos, + const Function& function, + const Array& argument_names, + ZoneGrowableArray* arguments, + intptr_t deopt_id) + : TemplateDefinition(deopt_id), + ic_data_(NULL), + token_pos_(token_pos), + function_(function), + argument_names_(argument_names), + arguments_(arguments), + result_cid_(kDynamicCid), + is_known_list_constructor_(false), + is_native_list_factory_(false), + identity_(AliasIdentity::Unknown()) { + ASSERT(function.IsZoneHandle()); + ASSERT(argument_names.IsZoneHandle() || argument_names.InVMHeap()); + } + // ICData for static calls carries call count. const ICData* ic_data() const { return ic_data_; } bool HasICData() const {