Chromium Code Reviews| Index: runtime/vm/jit_optimizer.cc |
| diff --git a/runtime/vm/jit_optimizer.cc b/runtime/vm/jit_optimizer.cc |
| index f85d9c46fb3b212ad580ed0ee36a5428469b0e57..acefb32a150e2ee14a0c69c481f622139fdc9a9f 100644 |
| --- a/runtime/vm/jit_optimizer.cc |
| +++ b/runtime/vm/jit_optimizer.cc |
| @@ -209,9 +209,13 @@ void JitOptimizer::SpecializePolymorphicInstanceCall( |
| return; // No information about receiver was infered. |
| } |
| - const ICData& ic_data = FlowGraphCompiler::TrySpecializeICDataByReceiverCid( |
| - call->ic_data(), receiver_cid); |
| - if (ic_data.raw() == call->ic_data().raw()) { |
| + const ICData& ic_data = *call->instance_call()->ic_data(); |
|
Vyacheslav Egorov (Google)
2017/04/10 10:59:28
disregarding ic_data has an interesting effect on
erikcorry
2017/04/19 15:06:40
I'm not sure where this is happening. If it's in
|
| + |
| + const PolymorphicTargets* targets = |
| + FlowGraphCompiler::TrySpecializeByReceiverCid( |
| + Array::Handle(zone(), ic_data.arguments_descriptor()), |
| + String::Handle(zone(), ic_data.target_name()), receiver_cid); |
| + if (targets == NULL) { |
| // No specialization. |
| return; |
| } |
| @@ -219,7 +223,7 @@ void JitOptimizer::SpecializePolymorphicInstanceCall( |
| const bool with_checks = false; |
| const bool complete = false; |
| PolymorphicInstanceCallInstr* specialized = |
| - new (Z) PolymorphicInstanceCallInstr(call->instance_call(), ic_data, |
| + new (Z) PolymorphicInstanceCallInstr(call->instance_call(), *targets, |
| with_checks, complete); |
| call->ReplaceWith(specialized, current_iterator()); |
| } |
| @@ -1444,81 +1448,103 @@ void JitOptimizer::ReplaceWithTypeCast(InstanceCallInstr* call) { |
| } |
| -bool JitOptimizer::LookupMethodFor(int class_id, |
| - const ArgumentsDescriptor& args_desc, |
| - const String& name, |
| - Function* fn_return) { |
| - if (class_id < 0) return false; |
| - if (class_id >= I->class_table()->NumCids()) return false; |
| - |
| - RawClass* raw_class = I->class_table()->At(class_id); |
| - if (raw_class == NULL) return false; |
| - Class& cls = Class::Handle(Z, raw_class); |
| - if (cls.IsNull()) return false; |
| - if (!cls.is_finalized()) return false; |
| - if (Array::Handle(cls.functions()).IsNull()) return false; |
| - |
| - Function& target_function = Function::Handle( |
| - Z, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc)); |
| - if (target_function.IsNull()) return false; |
| - *fn_return ^= target_function.raw(); |
| - return true; |
| +static int OrderById(const CidRangeTarget* a, const CidRangeTarget* b) { |
| + // Negative if 'a' should sort before 'b'. |
| + ASSERT(a->cid_start == a->cid_end); |
| + ASSERT(b->cid_start == b->cid_end); |
| + return a->cid_start - b->cid_start; |
| } |
| -static int OrderById(const intptr_t* a, const intptr_t* b) { |
| +static int OrderByFrequency(const CidRangeTarget* a, const CidRangeTarget* b) { |
| // Negative if 'a' should sort before 'b'. |
| - return *a - *b; |
| + return b->count - a->count; |
| } |
| -void JitOptimizer::TryExpandClassesInICData(const ICData& ic_data) { |
| - if (ic_data.NumberOfChecks() == 0) return; |
| +PolymorphicTargets* JitOptimizer::CreatePolymorphicTargets( |
|
Vyacheslav Egorov (Google)
2017/04/10 10:59:28
Given that this method is used by both Aot and Jit
erikcorry
2017/04/19 15:06:40
Moved to static CallTargets::Create(...)
|
| + Zone* zone, |
| + const ICData& ic_data) { |
| + PolymorphicTargets* targets = new (zone) PolymorphicTargets(zone); |
| + if (ic_data.NumberOfChecks() == 0) return targets; |
| - Function& dummy = Function::Handle(Z); |
| + Function& dummy = Function::Handle(zone); |
| - GrowableArray<intptr_t> ids; |
| - for (int i = 0; i < ic_data.NumberOfChecks(); i++) { |
| - // The API works for multi dispatch ICs that check more than one argument, |
| - // but we know we only check one arg here, so only the 0th element of id |
| - // will be used. |
| - GrowableArray<intptr_t> id; |
| - ic_data.GetCheckAt(i, &id, &dummy); |
| - ids.Add(id[0]); |
| + bool check_one_arg = ic_data.NumArgsTested() == 1; |
| + |
| + int checks = ic_data.NumberOfChecks(); |
| + for (int i = 0; i < checks; i++) { |
| + intptr_t id = 0; |
| + if (check_one_arg) { |
| + ic_data.GetOneClassCheckAt(i, &id, &dummy); |
| + } else { |
| + // The API works for multi dispatch ICs that check more than one |
| + // argument, but we know we will only check one arg here, so only the 0th |
| + // element of id will be used. |
| + GrowableArray<intptr_t> arg_ids; |
| + ic_data.GetCheckAt(i, &arg_ids, &dummy); |
| + id = arg_ids[0]; |
| + } |
| + Function& function = Function::ZoneHandle(zone, ic_data.GetTargetAt(i)); |
| + targets->Add(CidRangeTarget(id, id, &function, ic_data.GetCountAt(i))); |
| } |
| - ids.Sort(OrderById); |
| - Array& args_desc_array = Array::Handle(Z, ic_data.arguments_descriptor()); |
| + targets->Sort(OrderById); |
| + |
| + Array& args_desc_array = Array::Handle(zone, ic_data.arguments_descriptor()); |
| ArgumentsDescriptor args_desc(args_desc_array); |
| - String& name = String::Handle(Z, ic_data.target_name()); |
| - |
| - Function& fn = Function::Handle(Z); |
| - Function& fn_high = Function::Handle(Z); |
| - Function& possible_match = Function::Handle(Z); |
| - |
| - for (int cid_index = 0; cid_index < ids.length() - 1; cid_index++) { |
| - int low_cid = ids[cid_index]; |
| - int high_cid = ids[cid_index + 1]; |
| - if (low_cid + 1 == high_cid) continue; |
| - if (LookupMethodFor(low_cid, args_desc, name, &fn) && |
| - LookupMethodFor(high_cid, args_desc, name, &fn_high) && |
| - fn.raw() == fn_high.raw()) { |
| - // Try to fill in the IC table by going downwards from a known class-id. |
| - bool can_fill_in = true; |
| - for (int i = low_cid + 1; i < high_cid; i++) { |
| - if (!LookupMethodFor(i, args_desc, name, &possible_match) || |
| - possible_match.raw() != fn.raw()) { |
| - can_fill_in = false; |
| - break; |
| - } |
| + String& name = String::Handle(zone, ic_data.target_name()); |
| + |
| + Function& fn = Function::Handle(zone); |
| + |
| + intptr_t length = targets->length(); |
| + |
| + // Spread class-ids to preceding classes where a lookup yields the same |
| + // method. |
| + for (int idx = 0; idx < length; idx++) { |
| + int lower_limit_cid = (idx == 0) ? -1 : targets->At(idx - 1).cid_end; |
| + const Function& target = *targets->At(idx).target; |
| + for (int i = targets->At(idx).cid_start - 1; i > lower_limit_cid; i--) { |
| + if (FlowGraphCompiler::LookupMethodFor(i, args_desc, name, &fn) && |
| + fn.raw() == target.raw()) { |
| + CidRangeTarget t = targets->At(idx); |
| + t.cid_start = i; |
| + (*targets)[idx] = t; |
| + } else { |
| + break; |
| } |
| - if (can_fill_in) { |
| - for (int i = low_cid + 1; i < high_cid; i++) { |
| - ic_data.AddReceiverCheck(i, fn, 0); |
| - } |
| + } |
| + } |
| + // Spread class-ids to following classes where a lookup yields the same |
| + // method. |
| + for (int idx = 0; idx < length; idx++) { |
| + int upper_limit_cid = |
| + (idx == length - 1) ? 1000000000 : targets->At(idx + 1).cid_start; |
| + const Function& target = *targets->At(idx).target; |
| + for (int i = targets->At(idx).cid_end + 1; i < upper_limit_cid; i++) { |
| + if (FlowGraphCompiler::LookupMethodFor(i, args_desc, name, &fn) && |
| + fn.raw() == target.raw()) { |
| + (*targets)[idx].cid_end = i; |
| + } else { |
| + break; |
| } |
| } |
| } |
| + // Merge adjacent class id ranges. |
| + int dest = 0; |
| + for (int src = 1; src < length; src++) { |
| + if (targets->At(dest).cid_end + 1 == targets->At(src).cid_start && |
| + targets->At(dest).target->raw() == targets->At(src).target->raw()) { |
| + (*targets)[dest].cid_end = targets->At(src).cid_end; |
| + (*targets)[dest].count += targets->At(src).count; |
| + } else { |
| + dest++; |
| + if (src != dest) (*targets)[dest] = targets->At(src); |
| + } |
| + } |
| + targets->SetLength(dest + 1); |
| + targets->Sort(OrderByFrequency); |
| + return targets; |
| } |
| // Tries to optimize instance call by replacing it with a faster instruction |
| @@ -1578,11 +1604,9 @@ void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| return; |
| } |
| - // Now we are done trying the inlining options that benefit from only having |
| - // 1 entry in the IC table. |
| - TryExpandClassesInICData(unary_checks); |
| + PolymorphicTargets* targets = CreatePolymorphicTargets(Z, unary_checks); |
| - bool has_one_target = unary_checks.HasOneTarget(); |
| + bool has_one_target = targets->HasSingleTarget(); |
| if (has_one_target) { |
| // Check if the single target is a polymorphic target, if it is, |
| @@ -1590,7 +1614,7 @@ void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
| if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) { |
| has_one_target = PolymorphicInstanceCallInstr::ComputeRuntimeType( |
| - unary_checks) != Type::null(); |
| + *targets) != Type::null(); |
| } else { |
| const bool polymorphic_target = |
| MethodRecognizer::PolymorphicTarget(target); |
| @@ -1603,7 +1627,7 @@ void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| const RawFunction::Kind function_kind = target.kind(); |
| if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
| PolymorphicInstanceCallInstr* call = |
| - new (Z) PolymorphicInstanceCallInstr(instr, unary_checks, |
| + new (Z) PolymorphicInstanceCallInstr(instr, *targets, |
| /* call_with_checks = */ false, |
| /* complete = */ false); |
| instr->ReplaceWith(call, current_iterator()); |
| @@ -1614,15 +1638,17 @@ void JitOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| bool call_with_checks; |
| if (has_one_target && FLAG_polymorphic_with_deopt) { |
| // Type propagation has not run yet, we cannot eliminate the check. |
| + // TODO(erikcorry): The receiver check should use the off-heap targets |
| + // array, not the IC array. |
| AddReceiverCheck(instr); |
| // Call can still deoptimize, do not detach environment from instr. |
| call_with_checks = false; |
| } else { |
| call_with_checks = true; |
| } |
| - PolymorphicInstanceCallInstr* call = new (Z) |
| - PolymorphicInstanceCallInstr(instr, unary_checks, call_with_checks, |
| - /* complete = */ false); |
| + PolymorphicInstanceCallInstr* call = |
| + new (Z) PolymorphicInstanceCallInstr(instr, *targets, call_with_checks, |
| + /* complete = */ false); |
| instr->ReplaceWith(call, current_iterator()); |
| } |