Chromium Code Reviews| Index: runtime/vm/flow_graph_compiler.cc |
| diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc |
| index 866cab4de2588fd832ce9d9a245ef424aee606ec..fd9f649f5f31b6b0d8fae349de4a52b2e304b02a 100644 |
| --- a/runtime/vm/flow_graph_compiler.cc |
| +++ b/runtime/vm/flow_graph_compiler.cc |
| @@ -23,6 +23,7 @@ |
| #include "vm/object_store.h" |
| #include "vm/parser.h" |
| #include "vm/raw_object.h" |
| +#include "vm/resolver.h" |
| #include "vm/stack_frame.h" |
| #include "vm/stub_code.h" |
| #include "vm/symbols.h" |
| @@ -1169,8 +1170,12 @@ void FlowGraphCompiler::GenerateInstanceCall(intptr_t deopt_id, |
| } |
| if (is_optimizing()) { |
| - EmitMegamorphicInstanceCall(ic_data_in, argument_count, deopt_id, token_pos, |
| - locs, CatchClauseNode::kInvalidTryIndex); |
| + String& name = String::Handle(ic_data_in.target_name()); |
| + Array& arguments_descriptor = |
| + Array::Handle(ic_data_in.arguments_descriptor()); |
| + EmitMegamorphicInstanceCall(name, arguments_descriptor, argument_count, |
| + deopt_id, token_pos, locs, |
| + CatchClauseNode::kInvalidTryIndex); |
| return; |
| } |
| @@ -1787,38 +1792,26 @@ void FlowGraphCompiler::EndCodeSourceRange(TokenPosition token_pos) { |
| } |
| -const ICData& FlowGraphCompiler::TrySpecializeICDataByReceiverCid( |
| - const ICData& ic_data, |
| - intptr_t cid) { |
| +const CallTargets* FlowGraphCompiler::ResolveCallTargetsForReceiverCid( |
| + intptr_t cid, |
| + const String& selector, |
| + const Array& args_desc_array) { |
| Zone* zone = Thread::Current()->zone(); |
| - if (ic_data.NumArgsTested() != 1) return ic_data; |
| - if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { |
| - return ic_data; // Nothing to do |
| - } |
| + ArgumentsDescriptor args_desc(args_desc_array); |
| - intptr_t count = 1; |
| - const Function& function = |
| - Function::Handle(zone, ic_data.GetTargetForReceiverClassId(cid, &count)); |
| - // TODO(fschneider): Try looking up the function on the class if it is |
| - // not found in the ICData. |
| - if (!function.IsNull()) { |
| - const ICData& new_ic_data = ICData::ZoneHandle( |
| - zone, ICData::New(Function::Handle(zone, ic_data.Owner()), |
| - String::Handle(zone, ic_data.target_name()), |
| - Object::empty_array(), // Dummy argument descriptor. |
| - ic_data.deopt_id(), ic_data.NumArgsTested(), false)); |
| - new_ic_data.SetDeoptReasons(ic_data.DeoptReasons()); |
| - new_ic_data.AddReceiverCheck(cid, function, count); |
| - return new_ic_data; |
| - } |
| + Function& fn = Function::ZoneHandle(zone); |
| + if (!LookupMethodFor(cid, selector, args_desc, &fn)) return NULL; |
| - return ic_data; |
| + CallTargets* targets = new (zone) CallTargets(); |
| + targets->Add(CidRangeTarget(cid, cid, &fn, /* count = */ 1)); |
| + |
| + return targets; |
| } |
| intptr_t FlowGraphCompiler::ComputeGoodBiasForCidComparison( |
| - const GrowableArray<CidRangeTarget>& sorted, |
| + const CallTargets& sorted, |
| intptr_t max_immediate) { |
| // Sometimes a bias can be useful so we can emit more compact compare |
| // instructions. |
| @@ -1849,22 +1842,52 @@ intptr_t FlowGraphCompiler::ComputeGoodBiasForCidComparison( |
| } |
| +bool FlowGraphCompiler::LookupMethodFor(int class_id, |
| + const String& name, |
| + const ArgumentsDescriptor& args_desc, |
| + Function* fn_return) { |
| + Thread* thread = Thread::Current(); |
| + Isolate* isolate = thread->isolate(); |
| + Zone* zone = thread->zone(); |
| + if (class_id < 0) return false; |
| + if (class_id >= isolate->class_table()->NumCids()) return false; |
| + |
| + RawClass* raw_class = isolate->class_table()->At(class_id); |
| + if (raw_class == NULL) return false; |
| + Class& cls = Class::Handle(zone, raw_class); |
| + if (cls.IsNull()) return false; |
| + if (!cls.is_finalized()) return false; |
| + if (Array::Handle(cls.functions()).IsNull()) return false; |
| + |
| + const bool allow_add = false; |
| + Function& target_function = |
| + Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass( |
| + cls, name, args_desc, allow_add)); |
| + if (target_function.IsNull()) return false; |
| + *fn_return ^= target_function.raw(); |
| + return true; |
| +} |
| + |
| + |
| #if !defined(TARGET_ARCH_DBC) |
| // DBC emits calls very differently from other architectures due to its |
| // interpreted nature. |
| -void FlowGraphCompiler::EmitPolymorphicInstanceCall(const ICData& ic_data, |
| - intptr_t argument_count, |
| - const Array& argument_names, |
| - intptr_t deopt_id, |
| - TokenPosition token_pos, |
| - LocationSummary* locs, |
| - bool complete, |
| - intptr_t total_ic_calls) { |
| +void FlowGraphCompiler::EmitPolymorphicInstanceCall( |
| + const CallTargets& targets, |
| + const InstanceCallInstr& original_call, |
| + intptr_t argument_count, |
| + const Array& argument_names, |
| + intptr_t deopt_id, |
| + TokenPosition token_pos, |
| + LocationSummary* locs, |
| + bool complete, |
| + intptr_t total_ic_calls) { |
| if (FLAG_polymorphic_with_deopt) { |
| Label* deopt = |
| AddDeoptStub(deopt_id, ICData::kDeoptPolymorphicInstanceCallTestFail); |
| Label ok; |
| - EmitTestAndCall(ic_data, argument_count, argument_names, |
| + EmitTestAndCall(targets, original_call.function_name(), argument_count, |
| + argument_names, |
| deopt, // No cid match. |
| &ok, // Found cid. |
| deopt_id, token_pos, locs, complete, total_ic_calls); |
| @@ -1872,17 +1895,144 @@ void FlowGraphCompiler::EmitPolymorphicInstanceCall(const ICData& ic_data, |
| } else { |
| if (complete) { |
| Label ok; |
| - EmitTestAndCall(ic_data, argument_count, argument_names, |
| + EmitTestAndCall(targets, original_call.function_name(), argument_count, |
| + argument_names, |
| NULL, // No cid match. |
| &ok, // Found cid. |
| deopt_id, token_pos, locs, true, total_ic_calls); |
| assembler()->Bind(&ok); |
| } else { |
| - EmitSwitchableInstanceCall(ic_data, argument_count, deopt_id, token_pos, |
| - locs); |
| + const ICData& unary_checks = ICData::ZoneHandle( |
| + zone(), original_call.ic_data()->AsUnaryClassChecks()); |
| + EmitSwitchableInstanceCall(unary_checks, argument_count, deopt_id, |
| + token_pos, locs); |
| + } |
| + } |
| +} |
| + |
| + |
| +#define __ assembler()-> |
| +void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets, |
| + const String& function_name, |
| + 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()); |
| + |
| + const Array& arguments_descriptor = Array::ZoneHandle( |
| + zone(), ArgumentsDescriptor::New(argument_count, argument_names)); |
| + |
| + EmitTestAndCallLoadReceiver(argument_count, arguments_descriptor); |
| + |
| + static const int kNoCase = -1; |
| + int smi_case = kNoCase; |
| + int which_case_to_skip = kNoCase; |
| + |
| + const int length = targets.length(); |
| + ASSERT(length > 0); |
| + 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; |
| } |
| } |
| + |
| + if (smi_case != kNoCase) { |
| + Label after_smi_test; |
| + // Jump if receiver is not Smi. |
| + EmitTestAndCallSmiBranch(non_smi_length == 0 ? failed : &after_smi_test, |
| + false); |
| + |
| + // 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 = *targets[smi_case].target; |
| + GenerateStaticDartCall(deopt_id, token_index, |
| + *StubCode::CallStaticFunction_entry(), |
| + RawPcDescriptors::kOther, locs, function); |
| + __ Drop(argument_count); |
| + if (match_found != NULL) { |
| + EmitTestAndCallGotoMatchFound(match_found); |
| + } |
| + __ Bind(&after_smi_test); |
| + } else { |
| + if (!complete) { |
| + // Smi is not a valid class so jump to fail if it's a Smi. |
| + EmitTestAndCallSmiBranch(failed, true); |
|
Vyacheslav Egorov (Google)
2017/04/20 16:45:33
please add a comment what true/false means in this
erikcorry
2017/04/21 13:04:43
Done.
|
| + } |
| + } |
| + |
| + if (non_smi_length == 0) { |
| + // 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. |
| + return; |
| + } |
| + |
| + bool add_megamorphic_call = false; |
| + const int kMaxImmediateInInstruction = 256; |
|
Vyacheslav Egorov (Google)
2017/04/20 16:45:33
Maybe add a note that this works on all architectu
erikcorry
2017/04/21 13:04:43
This wasn't a very noticable win (microscopic code
|
| + int bias = |
| + ComputeGoodBiasForCidComparison(targets, kMaxImmediateInInstruction); |
| + |
| + // Value is not Smi. |
| + EmitTestAndCallLoadCid(bias); |
| + |
| + 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 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. 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 IsMonomorphic. |
| + add_megamorphic_call = true; |
| + break; |
| + } |
| + Label next_test; |
| + if (!complete || !is_last_check) { |
| + bias = EmitTestAndCallCheckCid(is_last_check ? failed : &next_test, |
| + targets[i], bias); |
| + } |
| + // 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 = *targets[i].target; |
| + GenerateStaticDartCall(deopt_id, token_index, |
| + *StubCode::CallStaticFunction_entry(), |
| + RawPcDescriptors::kOther, locs, function); |
| + __ Drop(argument_count); |
| + if (!is_last_check || add_megamorphic_call) { |
| + EmitTestAndCallGotoMatchFound(match_found); |
| + } |
| + __ Bind(&next_test); |
| + } |
| + if (add_megamorphic_call) { |
| + int try_index = CatchClauseNode::kInvalidTryIndex; |
| + EmitMegamorphicInstanceCall(function_name, arguments_descriptor, |
| + argument_count, deopt_id, token_index, locs, |
| + try_index); |
| + } |
| } |
| +#undef __ |
| #endif |
| #if defined(DEBUG) && !defined(TARGET_ARCH_DBC) |