[vm/kernel/aot] Tree shaking based on results of TFA, take 2.

This is a re-landing of

06ebf884db

after the fix was submitted separately at

e3b9c2860e

Original review: https://dart-review.googlesource.com/c/sdk/+/46942

Issue: https://github.com/dart-lang/sdk/issues/30480
Change-Id: I2d3a10acb9b5af0184b3d1504acb2f4ebebfff8a
Reviewed-on: https://dart-review.googlesource.com/48440
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2018-03-27 23:39:24 +00:00 committed by commit-bot@chromium.org
parent b217e21907
commit c60199cf86
22 changed files with 827 additions and 149 deletions

View file

@ -1087,6 +1087,8 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
}
}
bool isClassAllocated(Class c) => hierarchyCache.allocatedClasses.contains(c);
Call callSite(TreeNode node) => summaryCollector.callSites[node];
Type fieldType(Field field) => _fieldValues[field]?.value;

View file

@ -6,6 +6,18 @@
"name": "noSuchMethod",
"action": "call"
},
{
"library": "dart:core",
"class": "Object",
"name": "_haveSameRuntimeType",
"action": "call"
},
{
"library": "dart:core",
"class": "Object",
"name": "_instanceOf",
"action": "call"
},
{
"library": "dart:core",
"class": "List",
@ -28,6 +40,12 @@
"class": "_ImmutableMap",
"action": "create-instance"
},
{
"library": "dart:core",
"class": "_ImmutableMap",
"name": "_create",
"action": "call"
},
{
"library": "dart:core",
"class": "_StringBase",
@ -50,11 +68,6 @@
"name": "_classIdEqualsAssert",
"action": "call"
},
{
"library": "dart:_internal",
"name": "_classRangeCheck",
"action": "call"
},
{
"library": "dart:core",
"class": "_AssertionError",
@ -67,6 +80,94 @@
"name": "_throwNew",
"action": "call"
},
{
"library": "dart:core",
"class": "_Closure",
"name": "_instantiator_type_arguments",
"action": "get"
},
{
"library": "dart:core",
"class": "_Closure",
"name": "_function_type_arguments",
"action": "get"
},
{
"library": "dart:core",
"class": "_Closure",
"name": "_function",
"action": "get"
},
{
"library": "dart:core",
"class": "_Closure",
"name": "_context",
"action": "get"
},
{
"library": "dart:core",
"class": "_Closure",
"name": "_hash",
"action": "get"
},
{
"library": "dart:core",
"class": "_CompileTimeError",
"action": "create-instance"
},
{
"library": "dart:core",
"class": "Object",
"name": "_simpleInstanceOf",
"action": "call"
},
{
"library": "dart:core",
"class": "Object",
"name": "_simpleInstanceOfTrue",
"action": "call"
},
{
"library": "dart:core",
"class": "Object",
"name": "_simpleInstanceOfFalse",
"action": "call"
},
{
"library": "dart:_internal",
"name": "_classRangeCheck",
"action": "call"
},
{
"library": "dart:_internal",
"name": "_prependTypeArguments",
"action": "call"
},
{
"library": "dart:async",
"name": "_setAsyncThreadStackTrace",
"action": "call"
},
{
"library": "dart:async",
"name": "_clearAsyncThreadStackTrace",
"action": "call"
},
{
"library": "dart:async",
"name": "_asyncStarMoveNextHelper",
"action": "call"
},
{
"library": "dart:async",
"name": "_completeOnAsyncReturn",
"action": "call"
},
{
"library": "dart:async",
"class": "_AsyncStarStreamController",
"action": "create-instance"
},
{
"library": "dart:math",
"class": "_Random",

View file

@ -33,9 +33,13 @@ class NativeCodeOracle {
final Map<String, List<Map<String, dynamic>>> _nativeMethods =
<String, List<Map<String, dynamic>>>{};
final LibraryIndex _libraryIndex;
final Set<Member> _membersReferencedFromNativeCode = new Set<Member>();
NativeCodeOracle(this._libraryIndex);
bool isMemberReferencedFromNativeCode(Member member) =>
_membersReferencedFromNativeCode.contains(member);
/// Simulate the execution of a native method by adding its entry points
/// using [entryPointsListener]. Returns result type of the native method.
Type handleNativeProcedure(
@ -171,6 +175,8 @@ class NativeCodeOracle {
entryPointsListener.addRawCall(selector);
}
_membersReferencedFromNativeCode.add(member);
} else {
throw 'Bad entry point: unrecognized action "$action" in $rootDesc';
}

View file

@ -959,9 +959,11 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
@override
TypeExpr visitAssertStatement(AssertStatement node) {
_visit(node.condition);
if (node.message != null) {
_visit(node.message);
if (!kRemoveAsserts) {
_visit(node.condition);
if (node.message != null) {
_visit(node.message);
}
}
return null;
}
@ -974,7 +976,9 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
@override
TypeExpr visitAssertBlock(AssertBlock node) {
node.statements.forEach(_visit);
if (!kRemoveAsserts) {
node.statements.forEach(_visit);
}
return null;
}
@ -1146,7 +1150,9 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
@override
visitAssertInitializer(AssertInitializer node) {
_visit(node.statement);
if (!kRemoveAsserts) {
_visit(node.statement);
}
}
@override

View file

@ -67,7 +67,7 @@ Component transformComponent(
final transformsStopWatch = new Stopwatch()..start();
new DropMethodBodiesVisitor(typeFlowAnalysis).visitComponent(component);
new TreeShaker(component, typeFlowAnalysis).transformComponent(component);
new TFADevirtualization(component, typeFlowAnalysis)
.visitComponent(component);
@ -107,32 +107,6 @@ class TFADevirtualization extends Devirtualization {
}
}
/// Drop method bodies using results of type flow analysis.
class DropMethodBodiesVisitor extends RecursiveVisitor<Null> {
final TypeFlowAnalysis _typeFlowAnalysis;
DropMethodBodiesVisitor(this._typeFlowAnalysis);
@override
defaultMember(Member m) {
if (!m.isAbstract && !_typeFlowAnalysis.isMemberUsed(m)) {
if (m.function != null && m.function.body != null) {
m.function.body = new ExpressionStatement(
new Throw(new StringLiteral("TFA Error: $m")))
..parent = m.function;
debugPrint("Dropped $m");
} else if ((m is Field) &&
(m.initializer != null) &&
(m.initializer is! NullLiteral)) {
m.isConst = false;
m.initializer = new Throw(new StringLiteral("TFA Error: $m"))
..parent = m;
debugPrint("Dropped $m");
}
}
}
}
/// Annotates kernel AST with metadata using results of type flow analysis.
class AnnotateKernel extends RecursiveVisitor<Null> {
final TypeFlowAnalysis _typeFlowAnalysis;
@ -298,3 +272,610 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
super.visitStaticGet(node);
}
}
/// Tree shaking based on results of type flow analysis (TFA).
///
/// TFA provides information about allocated classes and reachable member
/// bodies. However, it is not enough to perform tree shaking in one pass:
/// we need to figure out which classes, members and typedefs are used
/// in types, interface targets and annotations.
///
/// So, tree shaking is performed in 2 passes:
///
/// * Pass 1 visits declarations of classes and members, and dives deep into
/// bodies of reachable members. It collects sets of used classes, members
/// and typedefs. Also, while visiting bodies of reachable members, it
/// transforms unreachable calls into 'throw' expressions.
///
/// * Pass 2 removes unused classes and members, and replaces bodies of
/// used but unreachable members.
///
class TreeShaker {
final TypeFlowAnalysis typeFlowAnalysis;
final Set<Class> _usedClasses = new Set<Class>();
final Set<Class> _classesUsedInType = new Set<Class>();
final Set<Member> _usedMembers = new Set<Member>();
final Set<Typedef> _usedTypedefs = new Set<Typedef>();
_TreeShakerTypeVisitor typeVisitor;
_TreeShakerPass1 _pass1;
_TreeShakerPass2 _pass2;
TreeShaker(Component component, this.typeFlowAnalysis) {
typeVisitor = new _TreeShakerTypeVisitor(this);
_pass1 = new _TreeShakerPass1(this);
_pass2 = new _TreeShakerPass2(this);
}
transformComponent(Component component) {
_pass1.transform(component);
_pass2.transform(component);
}
bool isClassUsed(Class c) => _usedClasses.contains(c);
bool isClassUsedInType(Class c) => _classesUsedInType.contains(c);
bool isClassAllocated(Class c) => typeFlowAnalysis.isClassAllocated(c);
bool isMemberUsed(Member m) => _usedMembers.contains(m);
bool isMemberBodyReachable(Member m) => typeFlowAnalysis.isMemberUsed(m);
bool isMemberReferencedFromNativeCode(Member m) =>
typeFlowAnalysis.nativeCodeOracle.isMemberReferencedFromNativeCode(m);
bool isTypedefUsed(Typedef t) => _usedTypedefs.contains(t);
void addClassUsedInType(Class c) {
if (_classesUsedInType.add(c)) {
if (kPrintDebug) {
debugPrint('Class ${c.name} used in type');
}
_usedClasses.add(c);
visitIterable(c.supers, typeVisitor);
visitList(c.typeParameters, typeVisitor);
transformList(c.annotations, _pass1, c);
// Preserve NSM forwarders. They are overlooked by TFA / tree shaker
// as they are abstract and don't have a body.
for (Procedure p in c.procedures) {
if (p.isAbstract && p.isNoSuchMethodForwarder) {
addUsedMember(p);
}
}
}
}
void addUsedMember(Member m) {
if (_usedMembers.add(m)) {
final enclosingClass = m.enclosingClass;
if (enclosingClass != null) {
if (kPrintDebug) {
debugPrint('Member $m from class ${enclosingClass.name} is used');
}
_usedClasses.add(enclosingClass);
}
FunctionNode func = null;
if (m is Field) {
m.type.accept(typeVisitor);
} else if (m is Procedure) {
func = m.function;
if (m.forwardingStubSuperTarget != null) {
addUsedMember(m.forwardingStubSuperTarget);
}
if (m.forwardingStubInterfaceTarget != null) {
addUsedMember(m.forwardingStubInterfaceTarget);
}
} else if (m is Constructor) {
func = m.function;
} else {
throw 'Unexpected member ${m.runtimeType}: $m';
}
if (func != null) {
visitList(func.typeParameters, typeVisitor);
visitList(func.positionalParameters, typeVisitor);
visitList(func.namedParameters, typeVisitor);
func.returnType.accept(typeVisitor);
}
transformList(m.annotations, _pass1, m);
}
}
void addUsedTypedef(Typedef typedef) {
if (_usedTypedefs.add(typedef)) {
typedef.visitChildren(typeVisitor);
}
}
}
/// Visits Dart types and collects all classes and typedefs used in types.
/// This visitor is used during pass 1 of tree shaking. It is a separate
/// visitor because [Transformer] does not provide a way to traverse types.
class _TreeShakerTypeVisitor extends RecursiveVisitor<Null> {
final TreeShaker shaker;
_TreeShakerTypeVisitor(this.shaker);
@override
visitInterfaceType(InterfaceType node) {
shaker.addClassUsedInType(node.classNode);
node.visitChildren(this);
}
@override
visitSupertype(Supertype node) {
shaker.addClassUsedInType(node.classNode);
node.visitChildren(this);
}
@override
visitTypedefType(TypedefType node) {
shaker.addUsedTypedef(node.typedefNode);
}
@override
visitFunctionType(FunctionType node) {
node.visitChildren(this);
final typedef = node.typedef;
if (typedef != null) {
shaker.addUsedTypedef(typedef);
}
}
}
/// The first pass of [TreeShaker].
/// Visits all classes, members and bodies of reachable members.
/// Collects all used classes, members and types, and
/// transforms unreachable calls into 'throw' expressions.
class _TreeShakerPass1 extends Transformer {
final TreeShaker shaker;
_TreeShakerPass1(this.shaker);
void transform(Component component) {
component.transformChildren(this);
}
bool _isUnreachable(TreeNode node) {
final callSite = shaker.typeFlowAnalysis.callSite(node);
return (callSite != null) && !callSite.isReachable;
}
List<Expression> _flattenArguments(Arguments arguments,
{Expression receiver}) {
final args = <Expression>[];
if (receiver != null) {
args.add(receiver);
}
args.addAll(arguments.positional);
args.addAll(arguments.named.map((a) => a.value));
return args;
}
bool _isThrowExpression(Expression expr) {
while (expr is Let) {
expr = (expr as Let).body;
}
return expr is Throw;
}
TreeNode _makeUnreachableCall(List<Expression> args) {
TreeNode node;
final int last = args.indexWhere(_isThrowExpression);
if (last >= 0) {
// One of the arguments is a Throw expression.
// Ignore the rest of the arguments.
node = args[last];
args = args.sublist(0, last);
Statistics.throwExpressionsPruned++;
} else {
node = new Throw(new StringLiteral(
'Attempt to execute code removed by Dart AOT compiler (TFA)'));
}
for (var arg in args.reversed) {
node = new Let(new VariableDeclaration(null, initializer: arg), node);
}
Statistics.callsDropped++;
return node;
}
TreeNode _makeUnreachableInitializer(List<Expression> args) {
return new LocalInitializer(
new VariableDeclaration(null, initializer: _makeUnreachableCall(args)));
}
TreeNode _visitAssertNode(TreeNode node) {
if (kRemoveAsserts) {
return null;
} else {
node.transformChildren(this);
return node;
}
}
@override
DartType visitDartType(DartType node) {
node.accept(shaker.typeVisitor);
return node;
}
@override
Supertype visitSupertype(Supertype node) {
node.accept(shaker.typeVisitor);
return node;
}
@override
TreeNode visitTypedef(Typedef node) {
return node; // Do not go deeper.
}
@override
TreeNode visitClass(Class node) {
if (shaker.isClassAllocated(node)) {
shaker.addClassUsedInType(node);
}
transformList(node.constructors, this, node);
transformList(node.procedures, this, node);
transformList(node.fields, this, node);
transformList(node.redirectingFactoryConstructors, this, node);
return node;
}
@override
TreeNode defaultMember(Member node) {
if (shaker.isMemberBodyReachable(node)) {
if (kPrintTrace) {
tracePrint("Visiting $node");
}
shaker.addUsedMember(node);
node.transformChildren(this);
} else if (shaker.isMemberReferencedFromNativeCode(node)) {
// Preserve members referenced from native code to satisfy lookups, even
// if they are not reachable. An instance member could be added via
// native code entry point but still unreachable if no instances of
// its enclosing class are allocated.
shaker.addUsedMember(node);
}
return node;
}
@override
TreeNode visitMethodInvocation(MethodInvocation node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall(
_flattenArguments(node.arguments, receiver: node.receiver));
} else {
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
return node;
}
}
@override
TreeNode visitPropertyGet(PropertyGet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver]);
} else {
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
return node;
}
}
@override
TreeNode visitPropertySet(PropertySet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver, node.value]);
} else {
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
return node;
}
}
@override
TreeNode visitSuperMethodInvocation(SuperMethodInvocation node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall(_flattenArguments(node.arguments));
} else {
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
return node;
}
}
@override
TreeNode visitSuperPropertyGet(SuperPropertyGet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([]);
} else {
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
return node;
}
}
@override
TreeNode visitSuperPropertySet(SuperPropertySet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.value]);
} else {
if (node.interfaceTarget != null) {
shaker.addUsedMember(node.interfaceTarget);
}
return node;
}
}
@override
TreeNode visitStaticInvocation(StaticInvocation node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall(_flattenArguments(node.arguments));
} else {
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
return node;
}
}
@override
TreeNode visitStaticGet(StaticGet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([]);
} else {
if (!shaker.isMemberBodyReachable(node.target)) {
// Annotations could contain references to constant fields.
assertx((node.target is Field) && (node.target as Field).isConst);
shaker.addUsedMember(node.target);
}
return node;
}
}
@override
TreeNode visitStaticSet(StaticSet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.value]);
} else {
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
return node;
}
}
@override
TreeNode visitDirectMethodInvocation(DirectMethodInvocation node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall(
_flattenArguments(node.arguments, receiver: node.receiver));
} else {
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
return node;
}
}
@override
TreeNode visitDirectPropertyGet(DirectPropertyGet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver]);
} else {
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
return node;
}
}
@override
TreeNode visitDirectPropertySet(DirectPropertySet node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall([node.receiver, node.value]);
} else {
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
return node;
}
}
@override
TreeNode visitConstructorInvocation(ConstructorInvocation node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableCall(_flattenArguments(node.arguments));
} else {
if (!shaker.isMemberBodyReachable(node.target)) {
// Annotations could contain references to const constructors.
assertx(node.isConst);
shaker.addUsedMember(node.target);
}
return node;
}
}
@override
TreeNode visitRedirectingInitializer(RedirectingInitializer node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableInitializer(_flattenArguments(node.arguments));
} else {
assertx(shaker.isMemberBodyReachable(node.target), details: node.target);
return node;
}
}
@override
TreeNode visitSuperInitializer(SuperInitializer node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableInitializer(_flattenArguments(node.arguments));
} else {
// Can't assert that node.target is used due to partial mixin resolution.
return node;
}
}
@override
visitFieldInitializer(FieldInitializer node) {
node.transformChildren(this);
if (_isUnreachable(node)) {
return _makeUnreachableInitializer([node.value]);
} else {
assertx(shaker.isMemberBodyReachable(node.field), details: node.field);
return node;
}
}
@override
TreeNode visitAssertStatement(AssertStatement node) {
return _visitAssertNode(node);
}
@override
TreeNode visitAssertBlock(AssertBlock node) {
return _visitAssertNode(node);
}
@override
TreeNode visitAssertInitializer(AssertInitializer node) {
return _visitAssertNode(node);
}
}
/// The second pass of [TreeShaker]. It is called after set of used
/// classes, members and typedefs is determined during the first pass.
/// This pass visits classes and members and removes unused classes and member.
/// Bodies of unreachable but used members are replaced with 'throw'
/// expressions. This pass does not dive deeper than member level.
class _TreeShakerPass2 extends Transformer {
final TreeShaker shaker;
_TreeShakerPass2(this.shaker);
void transform(Component component) {
component.transformChildren(this);
}
@override
TreeNode visitLibrary(Library node) {
node.transformChildren(this);
// The transformer API does not iterate over `Library.additionalExports`,
// so we manually delete the references to shaken nodes.
node.additionalExports.removeWhere((Reference reference) {
final node = reference.node;
if (node is Class) {
return !shaker.isClassUsed(node);
} else if (node is Typedef) {
return !shaker.isTypedefUsed(node);
} else {
return !shaker.isMemberUsed(node as Member);
}
});
return node;
}
@override
Typedef visitTypedef(Typedef node) {
return shaker.isTypedefUsed(node) ? node : null;
}
@override
Class visitClass(Class node) {
if (!shaker.isClassUsed(node)) {
debugPrint('Dropped class ${node.name}');
node.canonicalName?.unbind();
Statistics.classesDropped++;
return null; // Remove the class.
}
if (!shaker.isClassUsedInType(node)) {
debugPrint('Dropped supers from class ${node.name}');
// The class is only a namespace for static members. Remove its
// hierarchy information. This is mandatory, since these references
// might otherwise become dangling.
node.supertype = shaker
.typeFlowAnalysis.environment.coreTypes.objectClass.asRawSupertype;
node.implementedTypes.clear();
node.typeParameters.clear();
node.isAbstract = true;
// Mixin applications cannot have static members.
assertx(node.mixedInType == null);
node.annotations = const <Expression>[];
}
if (!shaker.isClassAllocated(node)) {
debugPrint('Class ${node.name} converted to abstract');
node.isAbstract = true;
}
node.transformChildren(this);
return node;
}
/// Preserve instance fields of enums as VM relies on their existence.
bool _preserveSpecialMember(Member node) =>
node is Field &&
!node.isStatic &&
node.enclosingClass != null &&
node.enclosingClass.isEnum;
@override
Member defaultMember(Member node) {
if (!shaker.isMemberUsed(node) && !_preserveSpecialMember(node)) {
node.canonicalName?.unbind();
Statistics.membersDropped++;
return null;
}
if (!shaker.isMemberBodyReachable(node)) {
if (node is Procedure) {
// Remove body of unused member.
if (!node.isStatic && node.enclosingClass.isAbstract) {
node.isAbstract = true;
node.function.body = null;
} else {
// If the enclosing class is not abstract, the method should still
// have a body even if it can never be called.
_makeUnreachableBody(node.function);
}
node.function.asyncMarker = AsyncMarker.Sync;
node.forwardingStubSuperTargetReference = null;
node.forwardingStubInterfaceTargetReference = null;
Statistics.methodBodiesDropped++;
} else if (node is Field) {
node.initializer = null;
Statistics.fieldInitializersDropped++;
} else if (node is Constructor) {
_makeUnreachableBody(node.function);
node.initializers = const <Initializer>[];
Statistics.constructorBodiesDropped++;
} else {
throw 'Unexpected member ${node.runtimeType}: $node';
}
}
return node;
}
void _makeUnreachableBody(FunctionNode function) {
if (function.body != null) {
function.body = new ExpressionStatement(new Throw(new StringLiteral(
"Attempt to execute method removed by Dart AOT compiler (TFA)")))
..parent = function;
}
}
@override
TreeNode defaultTreeNode(TreeNode node) {
return node; // Do not traverse into other nodes.
}
}

View file

@ -18,6 +18,9 @@ const bool kPrintDebug =
const bool kPrintStats =
const bool.fromEnvironment('global.type.flow.print.stats');
const bool kRemoveAsserts =
const bool.fromEnvironment('global.type.flow.remove.asserts');
/// Extended 'assert': always checks condition.
assertx(bool cond, {details}) {
if (!cond) {
@ -74,6 +77,13 @@ class Statistics {
static int invocationsInvalidated = 0;
static int recursiveInvocationsApproximated = 0;
static int typeConeSpecializations = 0;
static int classesDropped = 0;
static int membersDropped = 0;
static int methodBodiesDropped = 0;
static int fieldInitializersDropped = 0;
static int constructorBodiesDropped = 0;
static int callsDropped = 0;
static int throwExpressionsPruned = 0;
/// Resets statistic counters.
static void reset() {
@ -84,6 +94,14 @@ class Statistics {
usedCachedResultsOfInvocations = 0;
invocationsInvalidated = 0;
recursiveInvocationsApproximated = 0;
typeConeSpecializations = 0;
classesDropped = 0;
membersDropped = 0;
methodBodiesDropped = 0;
fieldInitializersDropped = 0;
constructorBodiesDropped = 0;
callsDropped = 0;
throwExpressionsPruned = 0;
}
static void print(String caption) {
@ -96,6 +114,13 @@ class Statistics {
${invocationsInvalidated} invocations invalidated
${recursiveInvocationsApproximated} recursive invocations approximated
${typeConeSpecializations} type cones specialized
${classesDropped} classes dropped
${membersDropped} members dropped
${methodBodiesDropped} method bodies dropped
${fieldInitializersDropped} field initializers dropped
${constructorBodiesDropped} constructor bodies dropped
${callsDropped} calls dropped
${throwExpressionsPruned} throw expressions pruned
""");
}
}

View file

@ -10,13 +10,10 @@ class _Vector extends core::Object {
constructor •([@vm.inferred-type.metadata=!] core::int size) → void
: self::_Vector::_offset = 0, self::_Vector::_length = size, self::_Vector::_elements = [@vm.inferred-type.metadata=dart.typed_data::_Float64List] typ::Float64List::•(size), super core::Object::•()
;
[@vm.unreachable.metadata=] constructor fromVOL(core::List<core::double> values, core::int offset, core::int length) → void
: self::_Vector::_offset = offset, self::_Vector::_length = length, self::_Vector::_elements = values, super core::Object::•()
throw "TFA Error: #lib::_Vector::fromVOL";
operator [](core::int i) → core::double
return [@vm.direct-call.metadata=dart.typed_data::_Float64List::[]] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]}(i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=!] this.{self::_Vector::_offset}));
operator []=([@vm.inferred-type.metadata=!] core::int i, core::double value) → void {
[@vm.unreachable.metadata=] [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]=}([@vm.unreachable.metadata=] i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=!] this.{self::_Vector::_offset}), value);
let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = i in let dynamic #t3 = [@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=!] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
}
operator *([@vm.inferred-type.metadata=#lib::_Vector?] self::_Vector a) → core::double {
core::double result = 0.0;
@ -28,7 +25,7 @@ class _Vector extends core::Object {
[@vm.inferred-type.metadata=#lib::_Vector?]static field self::_Vector v = new self::_Vector::•(10);
[@vm.inferred-type.metadata=dart.core::_Double?]static field core::double x = 0.0;
static method main(core::List<core::String> args) → dynamic {
core::Stopwatch timer = let final core::Stopwatch #t1 = new core::Stopwatch::•() in let final dynamic #t2 = [@vm.direct-call.metadata=dart.core::Stopwatch::start] #t1.{core::Stopwatch::start}() in #t1;
core::Stopwatch timer = let final core::Stopwatch #t4 = new core::Stopwatch::•() in let final dynamic #t5 = [@vm.direct-call.metadata=dart.core::Stopwatch::start] #t4.{core::Stopwatch::start}() in #t4;
for (core::int i = 0; i.{core::num::<}(100000000); i = i.{core::num::+}(1)) {
self::x = [@vm.direct-call.metadata=dart.core::_Double::+??] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.inferred-type.metadata=dart.core::_Double?] self::x.{core::double::+}([@vm.direct-call.metadata=#lib::_Vector::*??] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.inferred-type.metadata=#lib::_Vector?] self::v.{self::_Vector::*}([@vm.inferred-type.metadata=#lib::_Vector?] self::v));
}

View file

@ -3,7 +3,7 @@ import self as self;
import "dart:core" as core;
import "dart:async" as asy;
class A extends core::Object {
abstract class A extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;

View file

@ -13,9 +13,6 @@ class T2 extends core::Object {
;
}
abstract class A extends core::Object {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::A::";
abstract method foo() → core::Object;
}
class B extends core::Object implements self::A {

View file

@ -7,11 +7,6 @@ class T1 extends core::Object {
: super core::Object::•()
;
}
class T2 extends core::Object {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::T2::";
}
abstract class A extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
@ -25,13 +20,6 @@ class B extends self::A {
method foo() → dynamic
return new self::T1::•();
}
class C extends core::Object implements self::B {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::C::";
[@vm.unreachable.metadata=] method foo() → dynamic
throw "TFA Error: #lib::C::foo";
}
class Intermediate extends core::Object {
synthetic constructor •() → void
: super core::Object::•()

View file

@ -26,11 +26,8 @@ class B extends self::A {
return new self::T1::•();
}
abstract class C extends core::Object implements self::B {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::C::";
}
class D extends core::Object {
abstract class D extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;

View file

@ -3,9 +3,6 @@ import self as self;
import "dart:core" as core;
abstract class I extends core::Object {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::I::";
abstract method foo() → void;
}
class T1 extends core::Object implements self::I {

View file

@ -12,11 +12,6 @@ class T2 extends core::Object {
: super core::Object::•()
;
}
class T3 extends core::Object {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::T3::";
}
class T4 extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
@ -56,7 +51,7 @@ class B extends self::A {
abstract no-such-method-forwarder method foo() → dynamic;
abstract no-such-method-forwarder method bazz(dynamic a1, dynamic a2, dynamic a3, [dynamic a4, dynamic a5]) → dynamic;
}
class C extends core::Object {
abstract class C extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;
@ -76,8 +71,6 @@ class E extends core::Object implements self::A {
synthetic constructor •() → void
: super core::Object::•()
;
[@vm.unreachable.metadata=] method foo() → dynamic
throw "TFA Error: #lib::E::foo";
method noSuchMethod(core::Invocation invocation) → dynamic {
return new self::T4::•();
}
@ -88,8 +81,6 @@ class F extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;
[@vm.unreachable.metadata=] method twoArg(dynamic a1, dynamic a2) → dynamic
throw "TFA Error: #lib::F::twoArg";
method noSuchMethod(core::Invocation invocation) → dynamic {
return new self::T2::•();
}

View file

@ -8,13 +8,6 @@ abstract class T0 extends core::Object {
;
abstract method foo() → void;
}
class T1 extends self::T0 {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super self::T0::•()
throw "TFA Error: #lib::T1::";
[@vm.unreachable.metadata=] method foo() → void
throw "TFA Error: #lib::T1::foo";
}
class T2 extends self::T0 {
synthetic constructor •() → void
: super self::T0::•()
@ -30,9 +23,6 @@ class A extends core::Object {
}
}
abstract class B extends core::Object {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::B::";
abstract method method2(covariant dynamic arg) → void;
}
class C extends core::Object implements self::B {

View file

@ -6,7 +6,6 @@ abstract class A extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;
abstract method foo() → core::int;
}
class B extends self::A {
synthetic constructor •() → void
@ -15,13 +14,6 @@ class B extends self::A {
method foo() → core::int
return 1.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
}
class C extends core::Object implements self::A {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::C::";
[@vm.unreachable.metadata=] method foo() → core::int
throw "TFA Error: #lib::C::foo";
}
class TearOffDynamicMethod extends core::Object {
field dynamic bazz;
constructor •(dynamic arg) → void
@ -29,7 +21,6 @@ class TearOffDynamicMethod extends core::Object {
this.{self::TearOffDynamicMethod::bazz}();
}
}
[@vm.unreachable.metadata=]static field self::A aa = throw "TFA Error: #lib::aa";
static method knownResult() → dynamic
return new self::B::•();
static method main(core::List<core::String> args) → dynamic {

View file

@ -15,20 +15,12 @@ class B extends self::A {
method foo() → core::int
return 1.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
}
class C extends core::Object implements self::A {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::C::";
[@vm.unreachable.metadata=] method foo() → core::int
throw "TFA Error: #lib::C::foo";
}
class TearOffInterfaceMethod extends core::Object {
field dynamic bazz;
constructor •([@vm.inferred-type.metadata=#lib::B] self::A arg) → void
: self::TearOffInterfaceMethod::bazz = arg.{self::A::foo}, super core::Object::•()
;
}
[@vm.unreachable.metadata=]static field self::A aa = throw "TFA Error: #lib::aa";
static method knownResult() → dynamic
return new self::B::•();
static method main(core::List<core::String> args) → dynamic {

View file

@ -15,14 +15,7 @@ class B extends self::A {
method foo() → core::int
return 1.{core::num::+}([@vm.inferred-type.metadata=#lib::B] self::knownResult().foo() as{TypeError} core::num) as{TypeError} core::int;
}
class C extends core::Object implements self::A {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::C::";
[@vm.unreachable.metadata=] method foo() → core::int
throw "TFA Error: #lib::C::foo";
}
class Base extends core::Object {
abstract class Base extends core::Object {
synthetic constructor •() → void
: super core::Object::•()
;
@ -35,8 +28,6 @@ class TearOffSuperMethod extends self::Base {
synthetic constructor •() → void
: super self::Base::•()
;
[@vm.unreachable.metadata=] method foo() → core::int
throw "TFA Error: #lib::TearOffSuperMethod::foo";
method bar() → core::int
return [@vm.direct-call.metadata=#lib::Base::doCall] this.{self::Base::doCall}(super.{self::Base::foo});
}

View file

@ -3,29 +3,18 @@ import self as self;
import "dart:core" as core;
abstract class I extends core::Object {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::I::";
abstract method foo(dynamic x) → void;
}
class A extends core::Object implements self::I {
[@vm.unreachable.metadata=] synthetic constructor •() → void
: super core::Object::•()
throw "TFA Error: #lib::A::";
[@vm.unreachable.metadata=] method foo(dynamic x) → void
throw "TFA Error: #lib::A::foo";
abstract class A extends core::Object implements self::I {
}
class B extends core::Object implements self::I {
synthetic constructor •() → void
: super core::Object::•()
;
[@vm.unreachable.metadata=] method foo(dynamic x) → void
throw "TFA Error: #lib::B::foo";
}
[@vm.inferred-type.metadata=#lib::B?]static field self::I ii = new self::B::•();
static method bar([@vm.inferred-type.metadata=#lib::B?] self::I i) → void {
if(i is self::A) {
[@vm.unreachable.metadata=] i{self::A}.{self::A::foo}(42);
let dynamic #t1 = i{self::A} in let dynamic #t2 = 42 in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
}
}
static method main(core::List<core::String> args) → dynamic {

View file

@ -2692,8 +2692,9 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
// Every class should have at least a constructor, unless it is a top level
// class or a typedef class. The Kernel frontend does not create an implicit
// constructor for abstract classes.
ASSERT(cls.IsTopLevel() || cls.IsTypedefClass() || cls.is_abstract() ||
(Array::Handle(cls.functions()).Length() > 0));
// Moreover, Dart 2 precompiler (TFA) can tree shake all members if unused.
ASSERT(FLAG_precompiled_mode || cls.IsTopLevel() || cls.IsTypedefClass() ||
cls.is_abstract() || (Array::Handle(cls.functions()).Length() > 0));
// Resolve and finalize all member types.
ResolveAndFinalizeMemberTypes(cls);
// Run additional checks after all types are finalized.

View file

@ -71,30 +71,34 @@ void Intrinsifier::InitializeState() {
Error& error = Error::Handle(zone);
#define SETUP_FUNCTION(class_name, function_name, destination, type, fp) \
func = Function::null(); \
if (strcmp(#class_name, "::") == 0) { \
str = String::New(#function_name); \
func = lib.LookupFunctionAllowPrivate(str); \
} else { \
str = String::New(#class_name); \
cls = lib.LookupClassAllowPrivate(str); \
ASSERT(!cls.IsNull()); \
error = cls.EnsureIsFinalized(thread); \
if (!error.IsNull()) { \
OS::PrintErr("%s\n", error.ToErrorCString()); \
ASSERT(FLAG_precompiled_mode || !cls.IsNull()); \
if (!cls.IsNull()) { \
error = cls.EnsureIsFinalized(thread); \
if (!error.IsNull()) { \
OS::PrintErr("%s\n", error.ToErrorCString()); \
} \
ASSERT(error.IsNull()); \
if (#function_name[0] == '.') { \
str = String::New(#class_name #function_name); \
} else { \
str = String::New(#function_name); \
} \
func = cls.LookupFunctionAllowPrivate(str); \
} \
ASSERT(error.IsNull()); \
if (#function_name[0] == '.') { \
str = String::New(#class_name #function_name); \
} else { \
str = String::New(#function_name); \
} \
func = cls.LookupFunctionAllowPrivate(str); \
} \
if (func.IsNull()) { \
if (!func.IsNull()) { \
func.set_is_intrinsic(true); \
} else if (!FLAG_precompiled_mode) { \
FATAL2("Intrinsifier failed to find method %s in class %s\n", \
#function_name, #class_name); \
} \
func.set_is_intrinsic(true);
}
// Set up all core lib functions that can be intrinsified.
lib = Library::CoreLibrary();

View file

@ -136,23 +136,25 @@ void MethodRecognizer::InitializeState() {
#define SET_RECOGNIZED_KIND(class_name, function_name, enum_name, type, fp) \
func = Library::GetFunction(libs, #class_name, #function_name); \
if (func.IsNull()) { \
if (!func.IsNull()) { \
CHECK_FINGERPRINT3(func, class_name, function_name, enum_name, fp); \
func.set_recognized_kind(k##enum_name); \
} else if (!FLAG_precompiled_mode) { \
OS::PrintErr("Missing %s::%s\n", #class_name, #function_name); \
UNREACHABLE(); \
} \
CHECK_FINGERPRINT3(func, class_name, function_name, enum_name, fp); \
func.set_recognized_kind(k##enum_name);
}
RECOGNIZED_LIST(SET_RECOGNIZED_KIND);
#define SET_FUNCTION_BIT(class_name, function_name, dest, fp, setter, value) \
func = Library::GetFunction(libs, #class_name, #function_name); \
if (func.IsNull()) { \
if (!func.IsNull()) { \
CHECK_FINGERPRINT3(func, class_name, function_name, dest, fp); \
func.setter(value); \
} else if (!FLAG_precompiled_mode) { \
OS::PrintErr("Missing %s::%s\n", #class_name, #function_name); \
UNREACHABLE(); \
} \
CHECK_FINGERPRINT3(func, class_name, function_name, dest, fp); \
func.setter(value);
}
#define SET_IS_ALWAYS_INLINE(class_name, function_name, dest, fp) \
SET_FUNCTION_BIT(class_name, function_name, dest, fp, set_always_inline, true)

View file

@ -925,10 +925,14 @@ assertion_initializer_const_error_test/01: Pass
async_star_cancel_while_paused_test: RuntimeError
async_star_pause_test: Fail, OK
async_star_test/02: RuntimeError, CompileTimeError # Issue 31402 (Invocation arguments)
bad_override_test/03: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
bad_override_test/04: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
bad_override_test/05: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
built_in_identifier_prefix_test: CompileTimeError
call_method_must_not_be_field_test/03: RuntimeError # Issue 32265
call_method_must_not_be_getter_test/03: RuntimeError # Issue 32265
call_with_no_such_method_test: RuntimeError
check_member_static_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
checked_setter3_test/01: MissingCompileTimeError
checked_setter3_test/02: MissingCompileTimeError
checked_setter3_test/03: MissingCompileTimeError
@ -983,8 +987,15 @@ external_test/10: MissingRuntimeError # KernelVM bug: Unbound external.
external_test/13: MissingRuntimeError # KernelVM bug: Unbound external.
external_test/20: MissingRuntimeError # KernelVM bug: Unbound external.
factory3_test/01: Pass
fauxverride_test/03: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
fauxverride_test/05: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
field_increment_bailout_test: SkipByDesign
field_initialization_order_test: Fail, OK
field_override3_test/00: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
field_override3_test/01: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
field_override3_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
field_override3_test/03: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
field_override4_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
flatten_test/05: MissingRuntimeError
flatten_test/08: MissingRuntimeError
flatten_test/09: MissingRuntimeError
@ -1007,6 +1018,10 @@ generic_methods_reuse_type_variables_test: Pass
generic_no_such_method_dispatcher_simple_test: CompileTimeError # Issue 31533
generic_no_such_method_dispatcher_test: CompileTimeError # Issue 31533
generic_tearoff_test: CompileTimeError
getter_override2_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
getter_override_test/00: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
getter_override_test/01: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
getter_override_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
hello_dart_test: Skip # Incompatible flag: --compile_all
implicit_closure_test: Skip # Incompatible flag: --use_slow_path
implicit_creation/implicit_new_constructor_generic_test: Pass
@ -1064,6 +1079,10 @@ method_override5_test/03: MissingCompileTimeError
method_override6_test/01: MissingCompileTimeError
method_override6_test/02: MissingCompileTimeError
method_override6_test/03: MissingCompileTimeError
method_override7_test/00: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
method_override7_test/01: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
method_override7_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
method_override8_test/01: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
method_override_test: CompileTimeError # Issue 31616
mixin_illegal_super_use_test: Skip # Issues 24478 and 23773
mixin_illegal_superclass_test: Skip # Issues 24478 and 23773
@ -1077,6 +1096,10 @@ null_no_such_method_test: CompileTimeError # Issue 31533
null_test/mirrors: Skip # Uses mirrors.
null_test/none: SkipByDesign
overridden_no_such_method_test: SkipByDesign
override_field_method2_negative_test: Fail # Issue 32613: override check is missing in CFE.
override_field_method4_negative_test: Fail # Issue 32613: override check is missing in CFE.
override_field_method5_negative_test: Fail # Issue 32613: override check is missing in CFE.
override_field_test/01: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
override_inheritance_field_test/04: CompileTimeError # Issue 31616
override_inheritance_field_test/06: CompileTimeError # Issue 31616
override_inheritance_field_test/26: CompileTimeError # Issue 31616
@ -1084,6 +1107,11 @@ override_inheritance_field_test/29: CompileTimeError # Issue 31616
override_inheritance_generic_test/02: CompileTimeError
override_inheritance_method_test/28: CompileTimeError
override_inheritance_method_test/29: CompileTimeError
override_inheritance_mixed_test/01: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
override_inheritance_mixed_test/02: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
override_inheritance_mixed_test/03: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
override_inheritance_mixed_test/04: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
override_inheritance_mixed_test/08: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
override_inheritance_mixed_test/08: Pass # Correctly passes.
parser_quirks_test: CompileTimeError # Issue 31533
redirecting_factory_infinite_steps_test/01: MissingCompileTimeError
@ -1101,6 +1129,8 @@ regress_29025_test: CompileTimeError # Issue 31402 (Variable declaration)
regress_29405_test: CompileTimeError # Issue 31402 (Invocation arguments)
regress_30339_test: CompileTimeError # Issue 31402 (Variable declaration)
setter_no_getter_test/01: Pass, CompileTimeError # Issue 31533 (started passing after switching to batch-mode)
setter_override_test/00: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
setter_override_test/03: MissingCompileTimeError # Issue 32613: override check is missing in CFE.
stacktrace_demangle_ctors_test: RuntimeError
string_interpolate_test: CompileTimeError # Issue 31533
string_interpolation_and_buffer_test: RuntimeError # Issue 31402 (Return and yield statements)