| 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);
|
| }
|
| }
|
|
|
|
|