[vm/compiler] Merge cid ranges when generating receiver check
This change adds merging of cid ranges into Cids::Create if there are
gaps with the same target function. Previously, this optimization was
performed only when calculating targets via CallTargets::CreateAndExpand.
However, Cids::Create is widely used to check receiver type if there is
only one targets (e.g. in CallSpecializer::AddReceiverCheck /
CallSpecializer::AddChecksForArgNr).
This optimization is needed to shrink huge code for CheckClass instructions
in Flutter RenderObject.layout() method in JIT mode.
Issue: https://github.com/dart-lang/sdk/issues/36428
Change-Id: I97051b4213066b252ac3238df55ab4ed5c98f412
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103402
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index c2aeb5e..7edc129 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -649,24 +649,75 @@
return cids;
}
-Cids* Cids::Create(Zone* zone, const ICData& ic_data, int argument_number) {
+Cids* Cids::CreateAndExpand(Zone* zone,
+ const ICData& ic_data,
+ int argument_number) {
Cids* cids = new (zone) Cids(zone);
cids->CreateHelper(zone, ic_data, argument_number,
/* include_targets = */ false);
cids->Sort(OrderById);
// Merge adjacent class id ranges.
- int dest = 0;
- for (int src = 1; src < cids->length(); src++) {
- if (cids->cid_ranges_[dest]->cid_end + 1 >=
- cids->cid_ranges_[src]->cid_start) {
- cids->cid_ranges_[dest]->cid_end = cids->cid_ranges_[src]->cid_end;
- } else {
- dest++;
- if (src != dest) cids->cid_ranges_[dest] = cids->cid_ranges_[src];
+ {
+ int dest = 0;
+ for (int src = 1; src < cids->length(); src++) {
+ if (cids->cid_ranges_[dest]->cid_end + 1 >=
+ cids->cid_ranges_[src]->cid_start) {
+ cids->cid_ranges_[dest]->cid_end = cids->cid_ranges_[src]->cid_end;
+ } else {
+ dest++;
+ if (src != dest) cids->cid_ranges_[dest] = cids->cid_ranges_[src];
+ }
+ }
+ cids->SetLength(dest + 1);
+ }
+
+ // Merging/extending cid ranges is also done in CallTargets::CreateAndExpand.
+ // If changing this code, consider also adjusting CallTargets code.
+
+ if (cids->length() > 1 && argument_number == 0 && ic_data.HasOneTarget()) {
+ // Try harder to merge ranges if method lookups in the gaps result in the
+ // same target method.
+ const Function& target = Function::Handle(zone, ic_data.GetTargetAt(0));
+ if (!MethodRecognizer::PolymorphicTarget(target)) {
+ const auto& args_desc_array =
+ Array::Handle(zone, ic_data.arguments_descriptor());
+ ArgumentsDescriptor args_desc(args_desc_array);
+ const auto& name = String::Handle(zone, ic_data.target_name());
+ auto& fn = Function::Handle(zone);
+
+ intptr_t dest = 0;
+ for (intptr_t src = 1; src < cids->length(); src++) {
+ // Inspect all cids in the gap and see if they all resolve to the same
+ // target.
+ bool can_merge = true;
+ for (intptr_t cid = cids->cid_ranges_[dest]->cid_end + 1,
+ end = cids->cid_ranges_[src]->cid_start;
+ cid < end; ++cid) {
+ bool class_is_abstract = false;
+ if (FlowGraphCompiler::LookupMethodFor(cid, name, args_desc, &fn,
+ &class_is_abstract)) {
+ if (fn.raw() == target.raw()) {
+ continue;
+ }
+ if (class_is_abstract) {
+ continue;
+ }
+ }
+ can_merge = false;
+ break;
+ }
+
+ if (can_merge) {
+ cids->cid_ranges_[dest]->cid_end = cids->cid_ranges_[src]->cid_end;
+ } else {
+ dest++;
+ if (src != dest) cids->cid_ranges_[dest] = cids->cid_ranges_[src];
+ }
+ }
+ cids->SetLength(dest + 1);
}
}
- cids->SetLength(dest + 1);
return cids;
}
@@ -3651,6 +3702,9 @@
intptr_t length = targets.length();
+ // Merging/extending cid ranges is also done in Cids::CreateAndExpand.
+ // If changing this code, consider also adjusting Cids code.
+
// Spread class-ids to preceding classes where a lookup yields the same
// method. A polymorphic target is not really the same method since its
// behaviour depends on the receiver class-id, so we don't spread the
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 3e91b50..f2be34a 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -555,7 +555,11 @@
explicit Cids(Zone* zone) : zone_(zone) {}
// Creates the off-heap Cids object that reflects the contents
// of the on-VM-heap IC data.
- static Cids* Create(Zone* zone, const ICData& ic_data, int argument_number);
+ // Ranges of Cids are merged if there is only one target function and
+ // it is used for all cids in the gaps between ranges.
+ static Cids* CreateAndExpand(Zone* zone,
+ const ICData& ic_data,
+ int argument_number);
static Cids* CreateMonomorphic(Zone* zone, intptr_t cid);
bool Equals(const Cids& other) const;
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index d3b03a9..7be5d0e 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -3322,10 +3322,10 @@
// Insert receiver class or null check if needed.
switch (check) {
case FlowGraph::ToCheck::kCheckCid: {
- Instruction* check_class =
- flow_graph->CreateCheckClass(call->Receiver()->definition(),
- *Cids::Create(Z, *call->ic_data(), 0),
- call->deopt_id(), call->token_pos());
+ Instruction* check_class = flow_graph->CreateCheckClass(
+ call->Receiver()->definition(),
+ *Cids::CreateAndExpand(Z, *call->ic_data(), 0), call->deopt_id(),
+ call->token_pos());
flow_graph->InsertBefore(call, check_class, call->env(),
FlowGraph::kEffect);
break;
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index 5f12916..1c405f9 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -366,7 +366,8 @@
void CallSpecializer::AddChecksForArgNr(InstanceCallInstr* call,
Definition* instr,
int argument_number) {
- const Cids* cids = Cids::Create(Z, *call->ic_data(), argument_number);
+ const Cids* cids =
+ Cids::CreateAndExpand(Z, *call->ic_data(), argument_number);
AddCheckClass(instr, *cids, call->deopt_id(), call->env(), call);
}
@@ -1502,7 +1503,7 @@
new (Z) Value(call->ArgumentAt(1)), call->deopt_id(),
result_cid);
const Cids* cids =
- Cids::Create(Z, ic_data, /* argument_number =*/0);
+ Cids::CreateAndExpand(Z, ic_data, /* argument_number =*/0);
AddCheckClass(min_max->left()->definition(), *cids,
call->deopt_id(), call->env(), call);
AddCheckClass(min_max->right()->definition(), *cids,