| Index: runtime/vm/intermediate_language.cc
|
| diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
|
| index beb86543b041187151719ddecf36d59f18185386..45c3129456b4000811218ef925cba7a56065d198 100644
|
| --- a/runtime/vm/intermediate_language.cc
|
| +++ b/runtime/vm/intermediate_language.cc
|
| @@ -2741,6 +2741,104 @@ bool UnboxInstr::CanConvertSmi() const {
|
| }
|
|
|
|
|
| +static int OrderById(const CidRangeTarget* a, const CidRangeTarget* b) {
|
| + // Negative if 'a' should sort before 'b'.
|
| + ASSERT(a->cid_start == a->cid_end);
|
| + ASSERT(b->cid_start == b->cid_end);
|
| + return a->cid_start - b->cid_start;
|
| +}
|
| +
|
| +
|
| +static int OrderByFrequency(const CidRangeTarget* a, const CidRangeTarget* b) {
|
| + // Negative if 'a' should sort before 'b'.
|
| + return b->count - a->count;
|
| +}
|
| +
|
| +
|
| +CallTargets* CallTargets::Create(Zone* zone, const ICData& ic_data) {
|
| + CallTargets* targets = new (zone) CallTargets();
|
| + if (ic_data.NumberOfChecks() == 0) return targets;
|
| +
|
| + Function& dummy = Function::Handle(zone);
|
| +
|
| + bool check_one_arg = ic_data.NumArgsTested() == 1;
|
| +
|
| + int checks = ic_data.NumberOfChecks();
|
| + for (int i = 0; i < checks; i++) {
|
| + intptr_t id = 0;
|
| + if (check_one_arg) {
|
| + ic_data.GetOneClassCheckAt(i, &id, &dummy);
|
| + } else {
|
| + // The API works for multi dispatch ICs that check more than one
|
| + // argument, but we know we will only check one arg here, so only the 0th
|
| + // element of id will be used.
|
| + GrowableArray<intptr_t> arg_ids;
|
| + ic_data.GetCheckAt(i, &arg_ids, &dummy);
|
| + id = arg_ids[0];
|
| + }
|
| + Function& function = Function::ZoneHandle(zone, ic_data.GetTargetAt(i));
|
| + targets->Add(CidRangeTarget(id, id, &function, ic_data.GetCountAt(i)));
|
| + }
|
| +
|
| + targets->Sort(OrderById);
|
| +
|
| + Array& args_desc_array = Array::Handle(zone, ic_data.arguments_descriptor());
|
| + ArgumentsDescriptor args_desc(args_desc_array);
|
| + String& name = String::Handle(zone, ic_data.target_name());
|
| +
|
| + Function& fn = Function::Handle(zone);
|
| +
|
| + intptr_t length = targets->length();
|
| +
|
| + // Spread class-ids to preceding classes where a lookup yields the same
|
| + // method.
|
| + for (int idx = 0; idx < length; idx++) {
|
| + int lower_limit_cid = (idx == 0) ? -1 : targets->At(idx - 1).cid_end;
|
| + const Function& target = *targets->At(idx).target;
|
| + for (int i = targets->At(idx).cid_start - 1; i > lower_limit_cid; i--) {
|
| + if (FlowGraphCompiler::LookupMethodFor(i, name, args_desc, &fn) &&
|
| + fn.raw() == target.raw()) {
|
| + CidRangeTarget t = targets->At(idx);
|
| + t.cid_start = i;
|
| + (*targets)[idx] = t;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + // Spread class-ids to following classes where a lookup yields the same
|
| + // method.
|
| + for (int idx = 0; idx < length; idx++) {
|
| + int upper_limit_cid =
|
| + (idx == length - 1) ? 1000000000 : targets->At(idx + 1).cid_start;
|
| + const Function& target = *targets->At(idx).target;
|
| + for (int i = targets->At(idx).cid_end + 1; i < upper_limit_cid; i++) {
|
| + if (FlowGraphCompiler::LookupMethodFor(i, name, args_desc, &fn) &&
|
| + fn.raw() == target.raw()) {
|
| + (*targets)[idx].cid_end = i;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + // Merge adjacent class id ranges.
|
| + int dest = 0;
|
| + for (int src = 1; src < length; src++) {
|
| + if (targets->At(dest).cid_end + 1 == targets->At(src).cid_start &&
|
| + targets->At(dest).target->raw() == targets->At(src).target->raw()) {
|
| + (*targets)[dest].cid_end = targets->At(src).cid_end;
|
| + (*targets)[dest].count += targets->At(src).count;
|
| + } else {
|
| + dest++;
|
| + if (src != dest) (*targets)[dest] = targets->At(src);
|
| + }
|
| + }
|
| + targets->SetLength(dest + 1);
|
| + targets->Sort(OrderByFrequency);
|
| + return targets;
|
| +}
|
| +
|
| +
|
| // Shared code generation methods (EmitNativeCode and
|
| // MakeLocationSummary). Only assembly code that can be shared across all
|
| // architectures can be used. Machine specific register allocation and code
|
| @@ -3169,12 +3267,77 @@ bool InstanceCallInstr::MatchesCoreName(const String& name) {
|
| }
|
|
|
|
|
| -bool PolymorphicInstanceCallInstr::HasSingleRecognizedTarget() const {
|
| - if (FLAG_precompiled_mode && with_checks()) return false;
|
| +bool CallTargets::HasSingleRecognizedTarget() const {
|
| + if (!HasSingleTarget()) return false;
|
| + return MethodRecognizer::RecognizeKind(FirstTarget()) !=
|
| + MethodRecognizer::kUnknown;
|
| +}
|
|
|
| - return ic_data().HasOneTarget() &&
|
| - (MethodRecognizer::RecognizeKind(Function::Handle(
|
| - ic_data().GetTargetAt(0))) != MethodRecognizer::kUnknown);
|
| +
|
| +bool CallTargets::HasSingleTarget() const {
|
| + ASSERT(length() != 0);
|
| + for (int i = 0; i < length(); i++) {
|
| + if (cid_ranges_[i].target->raw() != cid_ranges_[0].target->raw())
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool CallTargets::IsMonomorphic() const {
|
| + if (length() != 1) return false;
|
| + return cid_ranges_[0].cid_start == cid_ranges_[0].cid_end;
|
| +}
|
| +
|
| +
|
| +intptr_t CallTargets::MonomorphicReceiverCid() const {
|
| + ASSERT(IsMonomorphic());
|
| + return cid_ranges_[0].cid_start;
|
| +}
|
| +
|
| +
|
| +Function& CallTargets::FirstTarget() const {
|
| + ASSERT(length() != 0);
|
| + ASSERT(cid_ranges_[0].target->IsZoneHandle());
|
| + return *cid_ranges_[0].target;
|
| +}
|
| +
|
| +
|
| +Function& CallTargets::MostPopularTarget() const {
|
| + ASSERT(length() != 0);
|
| + ASSERT(cid_ranges_[0].target->IsZoneHandle());
|
| + for (int i = 1; i < length(); i++) {
|
| + ASSERT(cid_ranges_[i].count <= cid_ranges_[0].count);
|
| + }
|
| + return *cid_ranges_[0].target;
|
| +}
|
| +
|
| +
|
| +intptr_t CallTargets::AggregateCallCount() const {
|
| + intptr_t sum = 0;
|
| + for (int i = 0; i < length(); i++) {
|
| + sum += cid_ranges_[i].count;
|
| + }
|
| + return sum;
|
| +}
|
| +
|
| +
|
| +bool PolymorphicInstanceCallInstr::HasOnlyDispatcherOrImplicitAccessorTargets()
|
| + const {
|
| + const intptr_t len = targets_.length();
|
| + Function& target = Function::Handle();
|
| + for (intptr_t i = 0; i < len; i++) {
|
| + target ^= targets_[i].target->raw();
|
| + if (!target.IsDispatcherOrImplicitAccessor()) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +intptr_t PolymorphicInstanceCallInstr::CallCount() const {
|
| + return targets().AggregateCallCount();
|
| }
|
|
|
|
|
| @@ -3182,10 +3345,9 @@ bool PolymorphicInstanceCallInstr::HasSingleRecognizedTarget() const {
|
| // PolymorphicInstanceCallInstr.
|
| #if !defined(TARGET_ARCH_DBC)
|
| void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
| - ASSERT(ic_data().NumArgsTested() == 1);
|
| if (!with_checks()) {
|
| - ASSERT(ic_data().HasOneTarget());
|
| - const Function& target = Function::ZoneHandle(ic_data().GetTargetAt(0));
|
| + ASSERT(targets().HasSingleTarget());
|
| + const Function& target = targets().FirstTarget();
|
| compiler->GenerateStaticCall(deopt_id(), instance_call()->token_pos(),
|
| target, instance_call()->ArgumentCount(),
|
| instance_call()->argument_names(), locs(),
|
| @@ -3194,7 +3356,7 @@ void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
| }
|
|
|
| compiler->EmitPolymorphicInstanceCall(
|
| - ic_data(), instance_call()->ArgumentCount(),
|
| + targets_, *instance_call(), instance_call()->ArgumentCount(),
|
| instance_call()->argument_names(), deopt_id(),
|
| instance_call()->token_pos(), locs(), complete(), total_call_count());
|
| }
|
| @@ -3202,22 +3364,29 @@ void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|
|
|
|
| RawType* PolymorphicInstanceCallInstr::ComputeRuntimeType(
|
| - const ICData& ic_data) {
|
| + const CallTargets& targets) {
|
| bool is_string = true;
|
| bool is_integer = true;
|
| bool is_double = true;
|
|
|
| - const intptr_t num_checks = ic_data.NumberOfChecks();
|
| + const intptr_t num_checks = targets.length();
|
| for (intptr_t i = 0; i < num_checks; i++) {
|
| - const intptr_t cid = ic_data.GetReceiverClassIdAt(i);
|
| - is_string = is_string && RawObject::IsStringClassId(cid);
|
| - is_integer = is_integer && RawObject::IsIntegerClassId(cid);
|
| - is_double = is_double && (cid == kDoubleCid);
|
| + ASSERT(targets[i].target->raw() == targets[0].target->raw());
|
| + const intptr_t start = targets[i].cid_start;
|
| + const intptr_t end = targets[i].cid_end;
|
| + for (intptr_t cid = start; cid <= end; cid++) {
|
| + is_string = is_string && RawObject::IsStringClassId(cid);
|
| + is_integer = is_integer && RawObject::IsIntegerClassId(cid);
|
| + is_double = is_double && (cid == kDoubleCid);
|
| + }
|
| }
|
|
|
| if (is_string) {
|
| + ASSERT(!is_integer);
|
| + ASSERT(!is_double);
|
| return Type::StringType();
|
| } else if (is_integer) {
|
| + ASSERT(!is_double);
|
| return Type::IntType();
|
| } else if (is_double) {
|
| return Type::Double();
|
| @@ -3230,12 +3399,18 @@ RawType* PolymorphicInstanceCallInstr::ComputeRuntimeType(
|
| Definition* InstanceCallInstr::Canonicalize(FlowGraph* flow_graph) {
|
| const intptr_t receiver_cid = PushArgumentAt(0)->value()->Type()->ToCid();
|
|
|
| - if (!HasICData()) return this;
|
| -
|
| - const ICData& new_ic_data =
|
| - FlowGraphCompiler::TrySpecializeICDataByReceiverCid(*ic_data(),
|
| - receiver_cid);
|
| - if (new_ic_data.raw() == ic_data()->raw()) {
|
| + // TODO(erikcorry): Even for cold call sites we could still try to look up
|
| + // methods when we know the receiver cid. We don't currently do this because
|
| + // it turns the InstanceCall into a PolymorphicInstanceCall which doesn't get
|
| + // recognized or inlined when it is cold.
|
| + if (ic_data()->NumberOfUsedChecks() == 0) return this;
|
| +
|
| + const CallTargets* new_target =
|
| + FlowGraphCompiler::ResolveCallTargetsForReceiverCid(
|
| + receiver_cid,
|
| + String::Handle(flow_graph->zone(), ic_data()->target_name()),
|
| + Array::Handle(flow_graph->zone(), ic_data()->arguments_descriptor()));
|
| + if (new_target == NULL) {
|
| // No specialization.
|
| return this;
|
| }
|
| @@ -3243,21 +3418,21 @@ Definition* InstanceCallInstr::Canonicalize(FlowGraph* flow_graph) {
|
| const bool with_checks = false;
|
| const bool complete = false;
|
| PolymorphicInstanceCallInstr* specialized = new PolymorphicInstanceCallInstr(
|
| - this, new_ic_data, with_checks, complete);
|
| + this, *new_target, with_checks, complete);
|
| flow_graph->InsertBefore(this, specialized, env(), FlowGraph::kValue);
|
| return specialized;
|
| }
|
|
|
|
|
| Definition* PolymorphicInstanceCallInstr::Canonicalize(FlowGraph* flow_graph) {
|
| - if (!HasSingleRecognizedTarget() || with_checks()) {
|
| + if (!IsSureToCallSingleRecognizedTarget()) {
|
| return this;
|
| }
|
|
|
| - const Function& target = Function::Handle(ic_data().GetTargetAt(0));
|
| + const Function& target = targets().FirstTarget();
|
| if (target.recognized_kind() == MethodRecognizer::kObjectRuntimeType) {
|
| const AbstractType& type =
|
| - AbstractType::Handle(ComputeRuntimeType(ic_data()));
|
| + AbstractType::Handle(ComputeRuntimeType(targets_));
|
| if (!type.IsNull()) {
|
| return flow_graph->GetConstant(type);
|
| }
|
| @@ -3267,6 +3442,12 @@ Definition* PolymorphicInstanceCallInstr::Canonicalize(FlowGraph* flow_graph) {
|
| }
|
|
|
|
|
| +bool PolymorphicInstanceCallInstr::IsSureToCallSingleRecognizedTarget() const {
|
| + if (FLAG_precompiled_mode && with_checks()) return false;
|
| + return targets_.HasSingleRecognizedTarget();
|
| +}
|
| +
|
| +
|
| Definition* StaticCallInstr::Canonicalize(FlowGraph* flow_graph) {
|
| if (!FLAG_precompiled_mode) {
|
| return this;
|
|
|