[vm/aot] Ignore artificial methods when computing single target.

When AOT call specializer computes single target for a method
invocation it needs to ignore artificial methods like field
dispatchers and method extractors that are lazily injected
by the precompiler as compilation of the whole application
progresses.

Lazy injections means that we don't have complete knowledge
when we compile a single method.

Bug:
Change-Id: I249ca397bb3da89f512a1ace49b198a0ffc9bf97
Reviewed-on: https://dart-review.googlesource.com/30520
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Vyacheslav Egorov 2017-12-20 12:35:42 +00:00 committed by commit-bot@chromium.org
parent f150c47ac1
commit c41e92de84
2 changed files with 60 additions and 3 deletions

View file

@ -0,0 +1,49 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "package:expect/expect.dart";
// Note: below we are using tear-offs instead of calling methods directly
// to guarantee very specific compilation order:
//
// - compile main, add get:methodM and get:callMethodM to selector set.
// - compile A.callMethodM and add get:createB to selector set.
// - compile A.createB and mark B as allocated.
//
// Class B is not marked as allocated until A.createB is compiled, which means
// that when A.callMethodM is compiled only class A has get:methodM method
// extractor injected.
//
// This test is verifying that optimizing compiler does not treat this method
// extractor as a single target for this.get:methodM call.
main() {
// This adds selector 'get:methodM' into the sent selector set and
// marks class A as allocated.
new A().methodM;
// This adds get:callMethodM to the sent selector set.
final callMethodMOnA = new A().callMethodM;
final b = callMethodMOnA("A");
final callMethodMOnB = b.callMethodM;
callMethodMOnB("B");
}
class A {
B callMethodM(String expected) {
final f = methodM;
Expect.equals(expected, f());
final newB = createB;
return newB();
}
B createB() => new B();
String methodM() => 'A';
}
class B extends A {
@override
String methodM() => 'B';
}

View file

@ -719,10 +719,18 @@ void AotCallSpecializer::VisitInstanceCall(InstanceCallInstr* instr) {
for (intptr_t i = 0; i < class_ids.length(); i++) {
const intptr_t cid = class_ids[i];
cls = isolate()->class_table()->At(cid);
// Even if we are resolving get:M on a class that has method M
// ResolveForReceiverClass would not inject a method extractor into
// a class becuase FLAG_lazy_dispatchers is set to false during AOT
// compilation. Precompiler however does inject method extractors
// (see Precompiler::CheckForNewDynamicFunctions). This means that that
// lookup for get:m might overlook a method M in subclass and return
// get:m (method extractor for M) from a superclass.
// For this reason we abort optimization if lookup returns any
// artificial functions that can be inserted lazily.
target = instr->ResolveForReceiverClass(cls);
if (target.IsNull()) {
// Can't resolve the target. It might be a noSuchMethod,
// call through getter or closurization.
if (target.IsNull() || target.IsMethodExtractor() ||
target.IsInvokeFieldDispatcher()) {
single_target = Function::null();
ic_data = ICData::null();
break;