Index: runtime/vm/flow_graph_compiler_arm64.cc |
diff --git a/runtime/vm/flow_graph_compiler_arm64.cc b/runtime/vm/flow_graph_compiler_arm64.cc |
index 6f02236c483f9568f99b6bb6f4168a35dad99857..445e9357eb09aea3b57d78a22b4bf98684bd44da 100644 |
--- a/runtime/vm/flow_graph_compiler_arm64.cc |
+++ b/runtime/vm/flow_graph_compiler_arm64.cc |
@@ -1202,16 +1202,14 @@ void FlowGraphCompiler::EmitInstanceCall(const StubEntry& stub_entry, |
void FlowGraphCompiler::EmitMegamorphicInstanceCall( |
- const ICData& ic_data, |
+ const String& name, |
+ const Array& arguments_descriptor, |
intptr_t argument_count, |
intptr_t deopt_id, |
TokenPosition token_pos, |
LocationSummary* locs, |
intptr_t try_index, |
intptr_t slow_path_argument_count) { |
- const String& name = String::Handle(zone(), ic_data.target_name()); |
- const Array& arguments_descriptor = |
- Array::ZoneHandle(zone(), ic_data.arguments_descriptor()); |
ASSERT(!arguments_descriptor.IsNull() && (arguments_descriptor.Length() > 0)); |
const MegamorphicCache& cache = MegamorphicCache::ZoneHandle( |
zone(), |
@@ -1444,7 +1442,8 @@ void FlowGraphCompiler::ClobberDeadTempRegisters(LocationSummary* locs) { |
#endif |
-void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data, |
+void FlowGraphCompiler::EmitTestAndCall(const PolymorphicTargets& targets, |
+ const String& function_name, |
intptr_t argument_count, |
const Array& argument_names, |
Label* failed, |
@@ -1455,57 +1454,68 @@ void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data, |
bool complete, |
intptr_t total_ic_calls) { |
ASSERT(is_optimizing()); |
+ |
__ Comment("EmitTestAndCall"); |
const Array& arguments_descriptor = Array::ZoneHandle( |
zone(), ArgumentsDescriptor::New(argument_count, argument_names)); |
- |
// Load receiver into R0. |
__ LoadFromOffset(R0, SP, (argument_count - 1) * kWordSize); |
__ LoadObject(R4, arguments_descriptor); |
- const bool kFirstCheckIsSmi = ic_data.GetReceiverClassIdAt(0) == kSmiCid; |
- const intptr_t num_checks = ic_data.NumberOfChecks(); |
- |
- ASSERT(!ic_data.IsNull() && (num_checks > 0)); |
+ const int kNoCase = -1; |
+ int smi_case = kNoCase; |
+ int which_case_to_skip = kNoCase; |
+ |
+ const int length = targets.length(); |
+ int non_smi_length = length; |
+ |
+ // Find out if one of the classes in one of the cases is the Smi class. We |
+ // will be handling that specially. |
+ for (int i = 0; i < length; i++) { |
+ const intptr_t start = targets[i].cid_start; |
+ if (start > kSmiCid) continue; |
+ const intptr_t end = targets[i].cid_end; |
+ if (end >= kSmiCid) { |
+ smi_case = i; |
+ if (start == kSmiCid && end == kSmiCid) { |
+ // If this case has only the Smi class then we won't need to emit it at |
+ // all later. |
+ which_case_to_skip = i; |
+ non_smi_length--; |
+ } |
+ break; |
+ } |
+ } |
- Label after_smi_test; |
- if (kFirstCheckIsSmi) { |
+ if (smi_case != kNoCase) { |
+ Label after_smi_test; |
__ tsti(R0, Immediate(kSmiTagMask)); |
// Jump if receiver is not Smi. |
- if (num_checks == 1) { |
- __ b(failed, NE); |
- } else { |
- __ b(&after_smi_test, NE); |
- } |
+ __ b(non_smi_length == 0 ? failed : &after_smi_test, NE); |
// Do not use the code from the function, but let the code be patched so |
// that we can record the outgoing edges to other code. |
- const Function& function = |
- Function::ZoneHandle(zone(), ic_data.GetTargetAt(0)); |
+ const Function& function = *targets[smi_case].target; |
GenerateStaticDartCall(deopt_id, token_index, |
*StubCode::CallStaticFunction_entry(), |
RawPcDescriptors::kOther, locs, function); |
__ Drop(argument_count); |
- if (num_checks > 1) { |
+ if (non_smi_length > 0) { |
__ b(match_found); |
} |
+ __ Bind(&after_smi_test); |
} else { |
// Receiver is Smi, but Smi is not a valid class therefore fail. |
- // (Smi class must be first in the list). |
if (!complete) { |
__ tsti(R0, Immediate(kSmiTagMask)); |
__ b(failed, EQ); |
} |
} |
- __ Bind(&after_smi_test); |
- ASSERT(!ic_data.IsNull() && (num_checks > 0)); |
- GrowableArray<CidRangeTarget> sorted(num_checks); |
- SortICDataByCount(ic_data, &sorted, /* drop_smi = */ true); |
+ ASSERT(length > 0); |
- const intptr_t sorted_len = sorted.length(); |
- // If sorted_len is 0 then only a Smi check was needed; the Smi check above |
- // will fail if there was only one check and receiver is not Smi. |
- if (sorted_len == 0) return; |
+ // If non_smi_length is 0 then only a Smi check was needed; the Smi check |
+ // above will fail if there was only one check and receiver is not Smi. |
+ if (non_smi_length == 0) return; |
// Value is not Smi, |
__ LoadClassId(R2, R0); |
@@ -1513,21 +1523,25 @@ void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data, |
bool add_megamorphic_call = false; |
const int kMaxImmediateInInstruction = 256; |
int bias = |
- ComputeGoodBiasForCidComparison(sorted, kMaxImmediateInInstruction); |
+ ComputeGoodBiasForCidComparison(targets, kMaxImmediateInInstruction); |
if (bias != 0) __ AddImmediate(R2, R2, -bias); |
- for (intptr_t i = 0; i < sorted_len; i++) { |
- const bool is_last_check = (i == (sorted_len - 1)); |
- int cid_start = sorted[i].cid_start; |
- int cid_end = sorted[i].cid_end; |
- int count = sorted[i].count; |
+ int last_check = which_case_to_skip == length - 1 ? length - 2 : length - 1; |
+ |
+ for (intptr_t i = 0; i < length; i++) { |
+ if (i == which_case_to_skip) continue; |
+ const bool is_last_check = (i == last_check); |
+ const int cid_start = targets[i].cid_start; |
+ const int cid_end = targets[i].cid_end; |
+ const int count = targets[i].count; |
if (!is_last_check && !complete && count < (total_ic_calls >> 5)) { |
// This case is hit too rarely to be worth writing class-id checks inline |
- // for. |
+ // for. Note that we can't do this for calls with only one target because |
+ // the type propagator may have made use of that and expects a deopt if |
+ // a new class is seen at this calls site. See HasSingleRecognizedCid. |
add_megamorphic_call = true; |
break; |
} |
- ASSERT(cid_start > kSmiCid || cid_end < kSmiCid); |
Label next_test; |
if (!complete || !is_last_check) { |
Label* next_label = is_last_check ? failed : &next_test; |
@@ -1543,20 +1557,21 @@ void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data, |
} |
// Do not use the code from the function, but let the code be patched so |
// that we can record the outgoing edges to other code. |
- const Function& function = *sorted[i].target; |
+ const Function& function = *targets[i].target; |
GenerateStaticDartCall(deopt_id, token_index, |
*StubCode::CallStaticFunction_entry(), |
RawPcDescriptors::kOther, locs, function); |
__ Drop(argument_count); |
- if (!is_last_check) { |
+ if (!is_last_check || add_megamorphic_call) { |
__ b(match_found); |
} |
__ Bind(&next_test); |
} |
if (add_megamorphic_call) { |
int try_index = CatchClauseNode::kInvalidTryIndex; |
- EmitMegamorphicInstanceCall(ic_data, argument_count, deopt_id, token_index, |
- locs, try_index, argument_count); |
+ EmitMegamorphicInstanceCall(function_name, arguments_descriptor, |
+ argument_count, deopt_id, token_index, locs, |
+ try_index); |
} |
} |