[vm/aot/tfa] Use RTA to improve AOT compilation speed

This change introduces Rapid Type Analysis (RTA) and uses it to
calculate initial set of allocated classes for Type Flow Analysis (TFA).
As a result, TFA converges much faster and AOT compilation time
improves.

RTA is less precise than TFA, so the set of allocated classes is
larger compared to the one calculated by TFA. However, it has only
marginal effect on the size of resulting AOT snapshots.

Time of AOT compilation step 2 on a large Flutter application
118.652s -> 59.907s (-49.5% / improved by a factor of 1.98x)
Snapshot size on armv8: -0.13%

Flutter gallery snapshot size in release and release-sizeopt modes
armv7 +0.19%, armv8 +0.2%

Just in case, RTA can be disabled using --no-rta option.

TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/42442
Issue: b/154155290

Change-Id: Iffbdabe7d486cad2e138f7592bffcb70474ddc34
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/222500
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 706448e..7563736 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -55,6 +55,9 @@
       help:
           'Enable global type flow analysis and related transformations in AOT mode.',
       defaultsTo: false)
+  ..addFlag('rta',
+      help: 'Use rapid type analysis for faster compilation in AOT mode.',
+      defaultsTo: true)
   ..addFlag('tree-shake-write-only-fields',
       help: 'Enable tree shaking of fields which are only written in AOT mode.',
       defaultsTo: true)
@@ -555,6 +558,7 @@
           deleteToStringPackageUris: options['delete-tostring-package-uri'],
           aot: options['aot'],
           useGlobalTypeFlowAnalysis: options['tfa'],
+          useRapidTypeAnalysis: options['rta'],
           environmentDefines: environmentDefines,
           enableAsserts: options['enable-asserts'],
           useProtobufTreeShakerV2: options['protobuf-tree-shaker-v2'],
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 51ed004..871ee7d 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -101,6 +101,9 @@
       help:
           'Enable global type flow analysis and related transformations in AOT mode.',
       defaultsTo: true);
+  args.addFlag('rta',
+      help: 'Use rapid type analysis for faster compilation in AOT mode.',
+      defaultsTo: true);
   args.addFlag('tree-shake-write-only-fields',
       help: 'Enable tree shaking of fields which are only written in AOT mode.',
       defaultsTo: true);
@@ -181,6 +184,7 @@
   final List<String>? fileSystemRoots = options['filesystem-root'];
   final bool aot = options['aot'];
   final bool tfa = options['tfa'];
+  final bool rta = options['rta'];
   final bool linkPlatform = options['link-platform'];
   final bool embedSources = options['embed-sources'];
   final bool enableAsserts = options['enable-asserts'];
@@ -269,6 +273,7 @@
       deleteToStringPackageUris: options['delete-tostring-package-uri'],
       aot: aot,
       useGlobalTypeFlowAnalysis: tfa,
+      useRapidTypeAnalysis: rta,
       environmentDefines: environmentDefines,
       enableAsserts: enableAsserts,
       useProtobufTreeShakerV2: useProtobufTreeShakerV2,
@@ -337,6 +342,7 @@
     List<String> deleteToStringPackageUris: const <String>[],
     bool aot: false,
     bool useGlobalTypeFlowAnalysis: false,
+    bool useRapidTypeAnalysis: true,
     required Map<String, String> environmentDefines,
     bool enableAsserts: true,
     bool useProtobufTreeShakerV2: false,
@@ -376,7 +382,8 @@
     await runGlobalTransformations(target, component, useGlobalTypeFlowAnalysis,
         enableAsserts, useProtobufTreeShakerV2, errorDetector,
         minimalKernel: minimalKernel,
-        treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
+        treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
+        useRapidTypeAnalysis: useRapidTypeAnalysis);
 
     if (minimalKernel) {
       // compiledSources is component.uriToSource.keys.
@@ -433,7 +440,8 @@
     bool useProtobufTreeShakerV2,
     ErrorDetector errorDetector,
     {bool minimalKernel: false,
-    bool treeShakeWriteOnlyFields: false}) async {
+    bool treeShakeWriteOnlyFields: false,
+    bool useRapidTypeAnalysis: true}) async {
   if (errorDetector.hasCompilationErrors) return;
 
   final coreTypes = new CoreTypes(component);
@@ -454,7 +462,8 @@
     globalTypeFlow.transformComponent(target, coreTypes, component,
         treeShakeSignatures: !minimalKernel,
         treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
-        treeShakeProtobufs: useProtobufTreeShakerV2);
+        treeShakeProtobufs: useProtobufTreeShakerV2,
+        useRapidTypeAnalysis: useRapidTypeAnalysis);
   } else {
     devirtualization.transformComponent(coreTypes, component);
     no_dynamic_invocations_annotator.transformComponent(component);
diff --git a/pkg/vm/lib/transformations/type_flow/rta.dart b/pkg/vm/lib/transformations/type_flow/rta.dart
new file mode 100644
index 0000000..df987e1
--- /dev/null
+++ b/pkg/vm/lib/transformations/type_flow/rta.dart
@@ -0,0 +1,528 @@
+// Copyright (c) 2021, 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.
+
+/// Rapid type analysis on kernel AST.
+
+import 'dart:core' hide Type;
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
+import 'package:kernel/library_index.dart' show LibraryIndex;
+import 'package:kernel/core_types.dart' show CoreTypes;
+
+import 'calls.dart' as calls
+    show Selector, DirectSelector, InterfaceSelector, VirtualSelector;
+import 'native_code.dart'
+    show EntryPointsListener, NativeCodeOracle, PragmaEntryPointsVisitor;
+import 'protobuf_handler.dart' show ProtobufHandler;
+import 'types.dart' show TFClass, Type, ConcreteType;
+import '../pragma.dart' show ConstantPragmaAnnotationParser;
+
+class Selector {
+  final Name name;
+  final bool setter;
+
+  Selector(this.name, this.setter);
+
+  @override
+  int get hashCode => name.hashCode ^ setter.hashCode;
+
+  @override
+  bool operator ==(Object other) =>
+      other is Selector &&
+      this.name == other.name &&
+      this.setter == other.setter;
+}
+
+class ClassInfo extends TFClass {
+  final ClassInfo? superclass;
+  final Set<ClassInfo> supertypes; // All super-types including this.
+  final Set<ClassInfo> subclasses = Set<ClassInfo>();
+  final Set<ClassInfo> subtypes = Set<ClassInfo>();
+
+  final Set<Selector>
+      calledDynamicSelectors; // Selectors called with dynamic and interface calls.
+  final Set<Selector> calledVirtualSelectors;
+
+  bool isAllocated = false;
+
+  late final Map<Name, Member> _dispatchTargetsSetters =
+      _initDispatchTargets(true);
+  late final Map<Name, Member> _dispatchTargetsNonSetters =
+      _initDispatchTargets(false);
+
+  ClassInfo(int id, Class classNode, this.superclass, this.supertypes,
+      this.calledDynamicSelectors, this.calledVirtualSelectors)
+      : super(id, classNode) {
+    supertypes.add(this);
+    for (var sup in supertypes) {
+      sup.subtypes.add(this);
+    }
+    for (ClassInfo? sup = this; sup != null; sup = sup.superclass) {
+      sup.subclasses.add(this);
+    }
+  }
+
+  late final ConcreteType concreteType = ConcreteType(this, null);
+
+  Map<Name, Member> _initDispatchTargets(bool setters) {
+    Map<Name, Member> targets;
+    final superclass = this.superclass;
+    if (superclass != null) {
+      targets = Map.from(setters
+          ? superclass._dispatchTargetsSetters
+          : superclass._dispatchTargetsNonSetters);
+    } else {
+      targets = {};
+    }
+    for (Field f in classNode.fields) {
+      if (!f.isStatic && !f.isAbstract) {
+        if (!setters || f.hasSetter) {
+          targets[f.name] = f;
+        }
+      }
+    }
+    for (Procedure p in classNode.procedures) {
+      if (!p.isStatic && !p.isAbstract) {
+        if (p.isSetter == setters) {
+          targets[p.name] = p;
+        }
+      }
+    }
+    return targets;
+  }
+
+  Member? getDispatchTarget(Selector selector) {
+    return (selector.setter
+        ? _dispatchTargetsSetters
+        : _dispatchTargetsNonSetters)[selector.name];
+  }
+}
+
+class _ClassHierarchyCache {
+  final Map<Class, ClassInfo> classes = <Class, ClassInfo>{};
+  int _classIdCounter = 0;
+
+  _ClassHierarchyCache();
+
+  ClassInfo getClassInfo(Class c) {
+    return classes[c] ??= _createClassInfo(c);
+  }
+
+  ClassInfo _createClassInfo(Class c) {
+    final supertypes = Set<ClassInfo>();
+    final dynSel = Set<Selector>();
+    for (var sup in c.supers) {
+      final supInfo = getClassInfo(sup.classNode);
+      supertypes.addAll(supInfo.supertypes);
+      dynSel.addAll(supInfo.calledDynamicSelectors);
+    }
+    Class? superclassNode = c.superclass;
+    ClassInfo? superclass;
+    final virtSel = Set<Selector>();
+    if (superclassNode != null) {
+      superclass = getClassInfo(superclassNode);
+      virtSel.addAll(superclass.calledVirtualSelectors);
+    }
+    return ClassInfo(
+        ++_classIdCounter, c, superclass, supertypes, dynSel, virtSel);
+  }
+
+  ConcreteType addAllocatedClass(Class cl, RapidTypeAnalysis rta) {
+    assert(!cl.isAbstract);
+    final ClassInfo classInfo = getClassInfo(cl);
+    if (!classInfo.isAllocated) {
+      classInfo.isAllocated = true;
+      for (var sel in classInfo.calledDynamicSelectors) {
+        final member = classInfo.getDispatchTarget(sel);
+        if (member != null) {
+          rta.addMember(member);
+        }
+      }
+      for (var sel in classInfo.calledVirtualSelectors) {
+        final member = classInfo.getDispatchTarget(sel);
+        if (member != null) {
+          rta.addMember(member);
+        }
+      }
+    }
+    return classInfo.concreteType;
+  }
+
+  void addDynamicCall(Selector selector, Class cl, RapidTypeAnalysis rta) {
+    final ClassInfo classInfo = getClassInfo(cl);
+    for (var sub in classInfo.subtypes) {
+      if (sub.calledDynamicSelectors.add(selector) && sub.isAllocated) {
+        final member = sub.getDispatchTarget(selector);
+        if (member != null) {
+          rta.addMember(member);
+        }
+      }
+    }
+  }
+
+  void addVirtualCall(Selector selector, Class cl, RapidTypeAnalysis rta) {
+    final ClassInfo classInfo = getClassInfo(cl);
+    for (var sub in classInfo.subclasses) {
+      if (sub.calledVirtualSelectors.add(selector) && sub.isAllocated) {
+        final member = sub.getDispatchTarget(selector);
+        if (member != null) {
+          rta.addMember(member);
+        }
+      }
+    }
+  }
+}
+
+class RapidTypeAnalysis {
+  final CoreTypes coreTypes;
+  final ClassHierarchy hierarchy;
+  final _ClassHierarchyCache hierarchyCache = _ClassHierarchyCache();
+  final ProtobufHandler? protobufHandler;
+
+  final Set<Member> visited = {};
+  final List<Member> workList = [];
+
+  RapidTypeAnalysis(Component component, this.coreTypes, this.hierarchy,
+      LibraryIndex libraryIndex, this.protobufHandler) {
+    Procedure? main = component.mainMethod;
+    if (main != null) {
+      addMember(main);
+    }
+    final annotationMatcher = ConstantPragmaAnnotationParser(coreTypes);
+    final nativeCodeOracle = NativeCodeOracle(libraryIndex, annotationMatcher);
+    component.accept(PragmaEntryPointsVisitor(
+        _EntryPointsListenerImpl(this), nativeCodeOracle, annotationMatcher));
+    run();
+  }
+
+  List<Class> get allocatedClasses {
+    return <Class>[
+      for (var entry in hierarchyCache.classes.entries)
+        if (entry.value.isAllocated) entry.key
+    ];
+  }
+
+  bool isAllocatedClass(Class cl) =>
+      hierarchyCache.classes[cl]?.isAllocated ?? false;
+
+  ConcreteType addAllocatedClass(Class cl) =>
+      hierarchyCache.addAllocatedClass(cl, this);
+
+  void addMember(Member member) {
+    if (visited.add(member)) {
+      workList.add(member);
+    }
+  }
+
+  void addCall(Class? currentClass, Member? interfaceTarget, Name name,
+      bool isVirtual, bool isSetter) {
+    final Class cl = isVirtual
+        ? currentClass!
+        : (interfaceTarget != null
+            ? interfaceTarget.enclosingClass!
+            : coreTypes.objectClass);
+    final Selector selector = Selector(name, isSetter);
+    if (isVirtual) {
+      hierarchyCache.addVirtualCall(selector, cl, this);
+    } else {
+      hierarchyCache.addDynamicCall(selector, cl, this);
+    }
+  }
+
+  void run() {
+    final memberVisitor = _MemberVisitor(this);
+    while (workList.isNotEmpty || invalidateProtobufFields()) {
+      final member = workList.removeLast();
+      protobufHandler?.beforeSummaryCreation(member);
+      member.accept(memberVisitor);
+    }
+  }
+
+  bool invalidateProtobufFields() {
+    final protobufHandler = this.protobufHandler;
+    if (protobufHandler == null) {
+      return false;
+    }
+    final fields = protobufHandler.getInvalidatedFields();
+    if (fields.isEmpty) {
+      return false;
+    }
+    // Protobuf handler replaced contents of static field initializers.
+    bool invalidated = false;
+    for (var field in fields) {
+      assert(field.isStatic);
+      if (visited.contains(field)) {
+        workList.add(field);
+        invalidated = true;
+      }
+    }
+    return invalidated;
+  }
+}
+
+class _MemberVisitor extends RecursiveVisitor {
+  final RapidTypeAnalysis rta;
+  final _ConstantVisitor _constantVisitor;
+
+  Class? _currentClass;
+  ClassInfo? _superclassInfo;
+
+  _MemberVisitor(this.rta) : _constantVisitor = _ConstantVisitor(rta);
+
+  ClassInfo get superclassInfo => _superclassInfo ??=
+      rta.hierarchyCache.getClassInfo(_currentClass!.superclass!);
+
+  @override
+  void defaultMember(Member node) {
+    _superclassInfo = null;
+    _currentClass = node.enclosingClass;
+    node.visitChildren(this);
+    if (node is Constructor) {
+      // Make sure instance field initializers are visited.
+      for (var f in _currentClass!.members) {
+        if (f is Field && !f.isStatic) {
+          f.initializer?.accept(this);
+        }
+      }
+    }
+    _superclassInfo = null;
+    _currentClass = null;
+  }
+
+  @override
+  void visitConstructorInvocation(ConstructorInvocation node) {
+    rta.addAllocatedClass(node.constructedType.classNode);
+    rta.addMember(node.target);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitInstanceInvocation(InstanceInvocation node) {
+    rta.addCall(_currentClass, node.interfaceTarget, node.name,
+        node.receiver is ThisExpression, false);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitDynamicInvocation(DynamicInvocation node) {
+    rta.addCall(null, null, node.name, false, false);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitEqualsCall(EqualsCall node) {
+    rta.addCall(_currentClass, node.interfaceTarget, node.interfaceTarget.name,
+        node.left is ThisExpression, false);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitInstanceGet(InstanceGet node) {
+    rta.addCall(_currentClass, node.interfaceTarget, node.name,
+        node.receiver is ThisExpression, false);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitInstanceTearOff(InstanceTearOff node) {
+    rta.addCall(_currentClass, node.interfaceTarget, node.name,
+        node.receiver is ThisExpression, false);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitDynamicGet(DynamicGet node) {
+    rta.addCall(null, null, node.name, false, false);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitInstanceSet(InstanceSet node) {
+    rta.addCall(_currentClass, node.interfaceTarget, node.name,
+        node.receiver is ThisExpression, true);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitDynamicSet(DynamicSet node) {
+    rta.addCall(null, null, node.name, false, true);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitSuperMethodInvocation(SuperMethodInvocation node) {
+    final target = superclassInfo.getDispatchTarget(Selector(node.name, false));
+    if (target != null) {
+      rta.addMember(target);
+    }
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitSuperPropertyGet(SuperPropertyGet node) {
+    final target = superclassInfo.getDispatchTarget(Selector(node.name, false));
+    if (target != null) {
+      rta.addMember(target);
+    }
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitSuperPropertySet(SuperPropertySet node) {
+    final target = superclassInfo.getDispatchTarget(Selector(node.name, true));
+    if (target != null) {
+      rta.addMember(target);
+    }
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitStaticGet(StaticGet node) {
+    rta.addMember(node.target);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitStaticInvocation(StaticInvocation node) {
+    rta.addMember(node.target);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitStaticSet(StaticSet node) {
+    rta.addMember(node.target);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitRedirectingInitializer(RedirectingInitializer node) {
+    rta.addMember(node.target);
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitSuperInitializer(SuperInitializer node) {
+    // Re-resolve target due to partial mixin resolution.
+    for (var replacement in _currentClass!.superclass!.constructors) {
+      if (node.target.name == replacement.name) {
+        rta.addMember(replacement);
+        break;
+      }
+    }
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitConstantExpression(ConstantExpression node) {
+    _constantVisitor.visit(node.constant);
+  }
+}
+
+class _ConstantVisitor extends ConstantVisitor<void> {
+  final RapidTypeAnalysis rta;
+  final Set<Constant> visited = {};
+
+  _ConstantVisitor(this.rta);
+
+  void visit(Constant constant) {
+    if (visited.add(constant)) {
+      constant.accept(this);
+    }
+  }
+
+  @override
+  void defaultConstant(Constant node) {}
+
+  @override
+  void visitListConstant(ListConstant constant) {
+    for (final entry in constant.entries) {
+      visit(entry);
+    }
+  }
+
+  @override
+  void visitMapConstant(MapConstant constant) {
+    for (final entry in constant.entries) {
+      visit(entry.key);
+      visit(entry.value);
+    }
+  }
+
+  @override
+  void visitSetConstant(SetConstant constant) {
+    for (final entry in constant.entries) {
+      visit(entry);
+    }
+  }
+
+  @override
+  void visitInstanceConstant(InstanceConstant constant) {
+    rta.addAllocatedClass(constant.classNode);
+    for (var value in constant.fieldValues.values) {
+      visit(value);
+    }
+  }
+
+  void _visitTearOffConstant(TearOffConstant constant) {
+    final Member member = constant.target;
+    rta.addMember(member);
+    if (member is Constructor) {
+      rta.addAllocatedClass(member.enclosingClass);
+    }
+  }
+
+  @override
+  void visitStaticTearOffConstant(StaticTearOffConstant constant) =>
+      _visitTearOffConstant(constant);
+
+  @override
+  void visitConstructorTearOffConstant(ConstructorTearOffConstant constant) =>
+      _visitTearOffConstant(constant);
+
+  @override
+  void visitRedirectingFactoryTearOffConstant(
+          RedirectingFactoryTearOffConstant constant) =>
+      _visitTearOffConstant(constant);
+
+  @override
+  void visitInstantiationConstant(InstantiationConstant constant) {
+    visit(constant.tearOffConstant);
+  }
+}
+
+class _EntryPointsListenerImpl implements EntryPointsListener {
+  final RapidTypeAnalysis rta;
+
+  _EntryPointsListenerImpl(this.rta);
+
+  @override
+  void addFieldUsedInConstant(Field field, Type instance, Type value) {}
+
+  @override
+  void addRawCall(calls.Selector selector) {
+    if (selector is calls.DirectSelector) {
+      rta.addMember(selector.member);
+    } else if (selector is calls.InterfaceSelector) {
+      rta.addCall(selector.member.enclosingClass!, selector.member,
+          selector.name, selector is calls.VirtualSelector, selector.isSetter);
+    } else {
+      throw 'Unexpected selector ${selector.runtimeType} $selector';
+    }
+  }
+
+  @override
+  ConcreteType addAllocatedClass(Class c) => rta.addAllocatedClass(c);
+
+  @override
+  void recordMemberCalledViaInterfaceSelector(Member target) =>
+      throw 'Unsupported operation';
+
+  @override
+  void recordMemberCalledViaThis(Member target) =>
+      throw 'Unsupported operation';
+
+  @override
+  void recordTearOff(Member target) => throw 'Unsupported operation';
+}
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 169abef..d82925a 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -20,6 +20,7 @@
 import 'calls.dart';
 import 'signature_shaking.dart';
 import 'protobuf_handler.dart' show ProtobufHandler;
+import 'rta.dart' show RapidTypeAnalysis;
 import 'summary.dart';
 import 'table_selector_assigner.dart';
 import 'types.dart';
@@ -44,7 +45,8 @@
     {PragmaAnnotationParser? matcher,
     bool treeShakeSignatures: true,
     bool treeShakeWriteOnlyFields: true,
-    bool treeShakeProtobufs: false}) {
+    bool treeShakeProtobufs: false,
+    bool useRapidTypeAnalysis: true}) {
   void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
   final hierarchy = new ClassHierarchy(component, coreTypes,
           onAmbiguousSupertypes: ignoreAmbiguousSupertypes)
@@ -58,6 +60,25 @@
       : null;
 
   Statistics.reset();
+
+  CleanupAnnotations(coreTypes, libraryIndex, protobufHandler)
+      .visitComponent(component);
+
+  Stopwatch? rtaStopWatch;
+  RapidTypeAnalysis? rta;
+  if (useRapidTypeAnalysis) {
+    // Rapid type analysis (RTA) is used to quickly calculate
+    // the set of allocated classes to make the subsequent
+    // type flow analysis converge much faster.
+    rtaStopWatch = new Stopwatch()..start();
+    final protobufHandlerRta = treeShakeProtobufs
+        ? ProtobufHandler.forComponent(component, coreTypes)
+        : null;
+    rta = RapidTypeAnalysis(
+        component, coreTypes, hierarchy, libraryIndex, protobufHandlerRta);
+    rtaStopWatch.stop();
+  }
+
   final analysisStopWatch = new Stopwatch()..start();
 
   MoveFieldInitializers().transformComponent(component);
@@ -81,8 +102,11 @@
     typeFlowAnalysis.addRawCall(mainSelector);
   }
 
-  CleanupAnnotations(coreTypes, libraryIndex, protobufHandler)
-      .visitComponent(component);
+  if (useRapidTypeAnalysis) {
+    for (Class c in rta!.allocatedClasses) {
+      typeFlowAnalysis.addAllocatedClass(c);
+    }
+  }
 
   typeFlowAnalysis.process();
 
@@ -119,6 +143,9 @@
 
   transformsStopWatch.stop();
 
+  if (useRapidTypeAnalysis) {
+    statPrint("RTA took ${rtaStopWatch!.elapsedMilliseconds}ms");
+  }
   statPrint("TF analysis took ${analysisStopWatch.elapsedMilliseconds}ms");
   statPrint("TF transforms took ${transformsStopWatch.elapsedMilliseconds}ms");
 
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect
index db77666..8964972 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect
@@ -22,6 +22,6 @@
   synthetic constructor •() → self::Class
     : super core::Object::•()
     ;
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3254,getterSelectorId:3255]  method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::Enum e) → core::int
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3305,getterSelectorId:3306]  method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::Enum e) → core::int
     return [@vm.inferred-type.metadata=!] e.{core::_Enum::index}{core::int};
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
index 1212909..f5fb218 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
@@ -13,6 +13,8 @@
     : super core::Object::•()
     ;
 }
+class T3 extends core::Object {
+}
 class T4 extends core::Object {
   synthetic constructor •() → self::T4
     : super core::Object::•()
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
index 0e44dd5..86ad485 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tear_off_super_method.dart.expect
@@ -16,6 +16,8 @@
 [@vm.procedure-attributes.metadata=getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=()->i]  method foo() → core::int
     return _in::unsafeCast<core::int>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] 1.{core::num::+}(_in::unsafeCast<core::num>([@vm.direct-call.metadata=#lib::B.foo] [@vm.inferred-type.metadata=int? (receiver not int)] [@vm.inferred-type.metadata=#lib::B] self::knownResult(){dynamic}.foo())){(core::num) → core::num});
 }
+class C extends core::Object implements self::A {
+}
 abstract class Base extends core::Object {
   synthetic constructor •() → self::Base
     : super core::Object::•()
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect
index 3aa20dc..7251a55 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect
@@ -51,6 +51,6 @@
   synthetic constructor •() → self::ConstClass
     : super core::Object::•()
     ;
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3258,getterSelectorId:3259]  method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::ConstEnum e) → core::int
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3309,getterSelectorId:3310]  method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::ConstEnum e) → core::int
     return [@vm.inferred-type.metadata=!] e.{core::_Enum::index}{core::int};
 }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect
index e544ddf..1d30a71 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/write_only_field.dart.expect
@@ -2,6 +2,8 @@
 import self as self;
 import "dart:core" as core;
 
+class A extends core::Object {
+}
 class B extends core::Object {
   constructor •() → self::B
     : super core::Object::•() {