[vm/kernel/tfa] Fix invalidation of invocations during processing

This CL fixes the case when an invocation is invalidated in the
middle of processing. In such case, its result should not be retained
and it should be re-processed again.

Also:

* _WorkList.pending Set is replaced with LinkedList (reduces analysis time).

* ClassHierarchy is configured to ignore ambiguous supertypes.

* _AssertionError._throwNew entry point is added (as it is implicitly
  inserted by flow graph builder).

Change-Id: Ie2d5d29352a5463fbe602910f9ae7f6f77a917d7
Reviewed-on: https://dart-review.googlesource.com/37126
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Alexander Markov 2018-01-30 17:32:19 +00:00 committed by commit-bot@chromium.org
parent d0f105de1a
commit 30a12f927b
5 changed files with 111 additions and 13 deletions

View file

@ -5,6 +5,7 @@
/// Global type flow analysis.
library kernel.transformations.analysis;
import 'dart:collection';
import 'dart:core' hide Type;
import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
@ -67,7 +68,8 @@ class _DependencyTracker {
/// This is the basic unit of processing in type flow analysis.
/// Call sites calling the same method with the same argument types
/// may reuse results of the analysis through the same _Invocation instance.
abstract class _Invocation extends _DependencyTracker {
abstract class _Invocation extends _DependencyTracker
with LinkedListEntry<_Invocation> {
final Selector selector;
final Args<Type> args;
@ -803,19 +805,22 @@ class _ClassHierarchyCache implements TypeHierarchy {
class _WorkList {
final TypeFlowAnalysis _typeFlowAnalysis;
final Set<_Invocation> pending = new Set<_Invocation>();
final LinkedList<_Invocation> pending = new LinkedList<_Invocation>();
final Set<_Invocation> processing = new Set<_Invocation>();
final List<_Invocation> callStack = <_Invocation>[];
_WorkList(this._typeFlowAnalysis);
bool _isPending(_Invocation invocation) => invocation.list != null;
void enqueueInvocation(_Invocation invocation) {
assertx(invocation.result == null);
if (!pending.add(invocation)) {
if (_isPending(invocation)) {
// Re-add the invocation to the tail of the pending queue.
pending.remove(invocation);
pending.add(invocation);
assertx(!_isPending(invocation));
}
pending.add(invocation);
}
void invalidateInvocation(_Invocation invocation) {
@ -830,14 +835,7 @@ class _WorkList {
void process() {
while (pending.isNotEmpty) {
assertx(callStack.isEmpty && processing.isEmpty);
_Invocation invocation = pending.first;
// Remove from pending before processing, as the invocation
// could be invalidated and re-added to pending while processing.
pending.remove(invocation);
processInvocation(invocation);
assertx(invocation.result != null);
processInvocation(pending.first);
}
}
@ -855,6 +853,7 @@ class _WorkList {
if (processing.add(invocation)) {
callStack.add(invocation);
pending.remove(invocation);
final Type result = invocation.process(_typeFlowAnalysis);
@ -868,6 +867,13 @@ class _WorkList {
invocation.invalidatedResult = null;
}
// Invocation is still pending - it was invalidated while being processed.
// Move result to invalidatedResult.
if (_isPending(invocation)) {
invocation.invalidatedResult = result;
invocation.result = null;
}
final last = callStack.removeLast();
assertx(identical(last, invocation));

View file

@ -61,6 +61,12 @@
"name": "_evaluateAssertion",
"action": "call"
},
{
"library": "dart:core",
"class": "_AssertionError",
"name": "_throwNew",
"action": "call"
},
{
"library": "dart:_builtin",
"name": "_resolveScriptUri",

View file

@ -33,7 +33,9 @@ Program transformProgram(CoreTypes coreTypes, Program program,
'pkg/vm/lib/transformations/type_flow/entry_points.json',
'pkg/vm/lib/transformations/type_flow/entry_points_extra.json',
]}) {
final hierarchy = new ClassHierarchy(program);
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
final hierarchy = new ClassHierarchy(program,
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
final types = new TypeEnvironment(coreTypes, hierarchy, strongMode: true);
final libraryIndex = new LibraryIndex.all(program);

View file

@ -0,0 +1,41 @@
// Copyright (c) 2018, 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.
abstract class I {
void foo();
}
class T1 implements I {
void foo() {}
}
class T2 implements I {
void foo() {}
}
class Point {
final I x;
const Point(I x) : this.x = x;
Point newPoint1() => new Point(x);
Point newPoint2() => new Point(x);
}
getX(var point) {
point.x;
}
main() {
var a = new Point(new T1());
print(a.x);
var c = new Point(new T2());
c.x.foo();
getX(a.newPoint1());
getX(a.newPoint2());
}

View file

@ -0,0 +1,43 @@
library #lib;
import self as self;
import "dart:core" as core;
abstract class I extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::I::";
abstract method foo() → void;
}
class T1 extends core::Object implements self::I {
synthetic constructor •() → void
: super core::Object::•()
;
method foo() → void {}
}
class T2 extends core::Object implements self::I {
synthetic constructor •() → void
: super core::Object::•()
;
method foo() → void {}
}
class Point extends core::Object {
final field self::I x;
const constructor •(self::I x) → void
: self::Point::x = x, super core::Object::•()
;
method newPoint1() → self::Point
return new self::Point::•([@vm.direct-call.metadata=#lib::Point::x] [@vm.inferred-type.metadata=!] this.{self::Point::x});
method newPoint2() → self::Point
return new self::Point::•([@vm.direct-call.metadata=#lib::Point::x] [@vm.inferred-type.metadata=!] this.{self::Point::x});
}
static method getX(dynamic point) → dynamic {
point.x;
}
static method main() → dynamic {
self::Point a = new self::Point::•(new self::T1::•());
core::print([@vm.direct-call.metadata=#lib::Point::x] [@vm.inferred-type.metadata=!] a.{self::Point::x});
self::Point c = new self::Point::•(new self::T2::•());
[@vm.direct-call.metadata=#lib::Point::x] [@vm.inferred-type.metadata=!] c.{self::Point::x}.{self::I::foo}();
self::getX([@vm.direct-call.metadata=#lib::Point::newPoint1] [@vm.inferred-type.metadata=#lib::Point] a.{self::Point::newPoint1}());
self::getX([@vm.direct-call.metadata=#lib::Point::newPoint2] [@vm.inferred-type.metadata=#lib::Point] a.{self::Point::newPoint2}());
}