Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(467)

Unified Diff: runtime/vm/jit_optimizer.cc

Issue 2809583002: Use off-heap data for type feedback in PolymorphicInstanceCallInstr (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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());
}

Powered by Google App Engine
This is Rietveld 408576698