| Index: runtime/vm/flow_graph_compiler_x64.cc
|
| diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc
|
| index 13af4ded99552b80b93ba4ba10646a6ddfd83bdc..7e92a96a2fef7bd04a8865137dc861517cd628e1 100644
|
| --- a/runtime/vm/flow_graph_compiler_x64.cc
|
| +++ b/runtime/vm/flow_graph_compiler_x64.cc
|
| @@ -1250,14 +1250,16 @@ void FlowGraphCompiler::EmitInstanceCall(const StubEntry& stub_entry,
|
|
|
|
|
| void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
| - const String& name,
|
| - const Array& arguments_descriptor,
|
| + const ICData& ic_data,
|
| 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,43 +1446,121 @@ void FlowGraphCompiler::ClobberDeadTempRegisters(LocationSummary* locs) {
|
| #endif
|
|
|
|
|
| -void FlowGraphCompiler::EmitTestAndCallLoadReceiver(
|
| - intptr_t argument_count,
|
| - const Array& arguments_descriptor) {
|
| +void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data,
|
| + intptr_t argument_count,
|
| + const Array& argument_names,
|
| + Label* failed,
|
| + Label* match_found,
|
| + intptr_t deopt_id,
|
| + TokenPosition token_index,
|
| + LocationSummary* locs,
|
| + 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 RAX.
|
| __ movq(RAX, Address(RSP, (argument_count - 1) * kWordSize));
|
| __ LoadObject(R10, arguments_descriptor);
|
| -}
|
|
|
| + const bool kFirstCheckIsSmi = ic_data.GetReceiverClassIdAt(0) == kSmiCid;
|
| + const intptr_t num_checks = ic_data.NumberOfChecks();
|
|
|
| -void FlowGraphCompiler::EmitTestAndCallSmiBranch(Label* label, bool if_smi) {
|
| - __ testq(RAX, Immediate(kSmiTagMask));
|
| - // Jump if receiver is (not) Smi.
|
| - __ j(if_smi ? ZERO : NOT_ZERO, label);
|
| -}
|
| + ASSERT(!ic_data.IsNull() && (num_checks > 0));
|
|
|
| + Label after_smi_test;
|
| + if (kFirstCheckIsSmi) {
|
| + __ testq(RAX, Immediate(kSmiTagMask));
|
| + // Jump if receiver is not Smi.
|
| + if (num_checks == 1) {
|
| + __ j(NOT_ZERO, failed);
|
| + } else {
|
| + __ j(NOT_ZERO, &after_smi_test);
|
| + }
|
| + // 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));
|
| + GenerateStaticDartCall(deopt_id, token_index,
|
| + *StubCode::CallStaticFunction_entry(),
|
| + RawPcDescriptors::kOther, locs, function);
|
| + __ Drop(argument_count, RCX);
|
| + if (num_checks > 1) {
|
| + __ jmp(match_found);
|
| + }
|
| + } else {
|
| + // Receiver is Smi, but Smi is not a valid class therefore fail.
|
| + // (Smi class must be first in the list).
|
| + if (!complete) {
|
| + __ testq(RAX, Immediate(kSmiTagMask));
|
| + __ j(ZERO, failed);
|
| + }
|
| + }
|
| + __ Bind(&after_smi_test);
|
|
|
| -void FlowGraphCompiler::EmitTestAndCallLoadCid() {
|
| - __ LoadClassId(RDI, RAX);
|
| -}
|
| + ASSERT(!ic_data.IsNull() && (num_checks > 0));
|
| + GrowableArray<CidRangeTarget> sorted(num_checks);
|
| + SortICDataByCount(ic_data, &sorted, /* drop_smi = */ true);
|
|
|
| + 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;
|
|
|
| -int FlowGraphCompiler::EmitTestAndCallCheckCid(Label* next_label,
|
| - const CidRangeTarget& target,
|
| - int bias) {
|
| - intptr_t cid_start = target.cid_start;
|
| - intptr_t cid_end = target.cid_end;
|
| - if (cid_start == cid_end) {
|
| - __ cmpl(RDI, Immediate(cid_start - bias));
|
| - __ j(NOT_EQUAL, next_label);
|
| - } else {
|
| - __ addl(RDI, Immediate(bias - cid_start));
|
| - bias = cid_start;
|
| - __ cmpl(RDI, Immediate(cid_end - cid_start));
|
| - __ j(ABOVE, next_label); // Unsigned higher.
|
| + // Value is not Smi,
|
| + __ LoadClassId(RDI, RAX);
|
| +
|
| + bool add_megamorphic_call = false;
|
| + const int kMaxImmediateInInstruction = 127;
|
| + int bias =
|
| + ComputeGoodBiasForCidComparison(sorted, kMaxImmediateInInstruction);
|
| + if (bias != 0) __ addl(RDI, Immediate(-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;
|
| + 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.
|
| + 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;
|
| + bool near = is_last_check ? Assembler::kFarJump : Assembler::kNearJump;
|
| + if (cid_start == cid_end) {
|
| + __ cmpl(RDI, Immediate(cid_start - bias));
|
| + __ j(NOT_EQUAL, next_label, near);
|
| + } else {
|
| + __ addl(RDI, Immediate(bias - cid_start));
|
| + bias = cid_start;
|
| + __ cmpl(RDI, Immediate(cid_end - cid_start));
|
| + __ j(ABOVE, next_label, near); // Unsigned higher.
|
| + }
|
| + }
|
| + // 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;
|
| + GenerateStaticDartCall(deopt_id, token_index,
|
| + *StubCode::CallStaticFunction_entry(),
|
| + RawPcDescriptors::kOther, locs, function);
|
| + __ Drop(argument_count, RCX);
|
| + if (!is_last_check) {
|
| + __ jmp(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);
|
| }
|
| - return bias;
|
| }
|
|
|
|
|
|
|