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..3fbeb86ece5f866050f9c32d51969abdfe0c183b 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,84 +1792,70 @@ 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 |
- } |
- |
- 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; |
- } |
- |
- return ic_data; |
-} |
- |
- |
-intptr_t FlowGraphCompiler::ComputeGoodBiasForCidComparison( |
- const GrowableArray<CidRangeTarget>& sorted, |
- intptr_t max_immediate) { |
- // Sometimes a bias can be useful so we can emit more compact compare |
- // instructions. |
- intptr_t min_cid = 1000000; |
- intptr_t max_cid = -1; |
- |
- const intptr_t sorted_len = sorted.length(); |
- |
- for (intptr_t i = 0; i < sorted_len + 1; i++) { |
- bool done = (i == sorted_len); |
- intptr_t start = done ? 0 : sorted[i].cid_start; |
- intptr_t end = done ? 0 : sorted[i].cid_end; |
- bool is_range = start != end; |
- bool spread_too_big = start - min_cid > max_immediate; |
- if (done || is_range || spread_too_big) { |
- if (i >= 2 && max_cid - min_cid <= max_immediate && |
- max_cid > max_immediate) { |
- return min_cid; |
- } else { |
- return 0; |
- } |
- } |
- min_cid = Utils::Minimum(min_cid, start); |
- max_cid = Utils::Maximum(max_cid, end); |
- } |
- UNREACHABLE(); |
- return 0; |
+ |
+ ArgumentsDescriptor args_desc(args_desc_array); |
+ |
+ Function& fn = Function::ZoneHandle(zone); |
+ if (!LookupMethodFor(cid, selector, args_desc, &fn)) return NULL; |
+ |
+ CallTargets* targets = new (zone) CallTargets(); |
+ targets->Add(CidRangeTarget(cid, cid, &fn, /* count = */ 1)); |
+ |
+ return targets; |
+} |
+ |
+ |
+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 +1863,141 @@ 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; |
+ EmitTestAndCallSmiBranch(non_smi_length == 0 ? failed : &after_smi_test, |
+ /* jump_if_smi= */ 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) { |
+ __ Jump(match_found); |
+ } |
+ __ Bind(&after_smi_test); |
+ } else { |
+ if (!complete) { |
+ // Smi is not a valid class. |
+ EmitTestAndCallSmiBranch(failed, /* jump_if_smi = */ true); |
+ } |
+ } |
+ |
+ 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; |
+ int bias = 0; |
+ |
+ // Value is not Smi. |
+ EmitTestAndCallLoadCid(); |
+ |
+ 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) { |
+ __ Jump(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) |