Version 1.4.92

Cherry pick: Extend member pool collection to library types
CL: https://r8-review.googlesource.com/c/r8/+/36640

Cherry pick: Fix missing edge in member pool collection
CL: https://r8-review.googlesource.com/c/r8/+/36700

Cherry pick: Allow archiving when triggering cls
CL: https://r8-review.googlesource.com/c/r8/+/37522

Bug: 129794426, 65810338
Change-Id: If63127dde393c5f7cef4051e0e989be7dbe156fc
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4b23ef3..0182e69 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -484,14 +484,14 @@
           if (options.enableUninstantiatedTypeOptimization) {
             timing.begin("UninstantiatedTypeOptimization");
             appView.setGraphLense(
-                new UninstantiatedTypeOptimization(appViewWithLiveness, options)
-                    .run(new MethodPoolCollection(application), executorService, timing));
+                new UninstantiatedTypeOptimization(appViewWithLiveness)
+                    .run(new MethodPoolCollection(appView), executorService, timing));
             application = application.asDirect().rewrittenWithLense(appView.graphLense());
-            timing.end();
             appViewWithLiveness.setAppInfo(
                 appViewWithLiveness
                     .appInfo()
                     .rewrittenWithLense(application.asDirect(), appView.graphLense()));
+            timing.end();
           }
         }
 
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index bb835e0..4b0f4b8 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.4.91";
+  public static final String LABEL = "1.4.92";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 084ad80..f6851fd 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.shaking.VerticalClassMerger.VerticallyMergedClasses;
 import com.android.tools.r8.utils.InternalOptions;
 
-public class AppView<T extends AppInfo> {
+public class AppView<T extends AppInfo> implements DexDefinitionSupplier {
 
   private T appInfo;
   private AppServices appServices;
@@ -40,6 +40,27 @@
     this.appServices = appServices;
   }
 
+  @Override
+  public final DexDefinition definitionFor(DexReference reference) {
+    return appInfo().definitionFor(reference);
+  }
+
+  @Override
+  public final DexEncodedField definitionFor(DexField field) {
+    return appInfo().definitionFor(field);
+  }
+
+  @Override
+  public final DexEncodedMethod definitionFor(DexMethod method) {
+    return appInfo().definitionFor(method);
+  }
+
+  @Override
+  public final DexClass definitionFor(DexType type) {
+    return appInfo().definitionFor(type);
+  }
+
+  @Override
   public DexItemFactory dexItemFactory() {
     return dexItemFactory;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
new file mode 100644
index 0000000..7d9888e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, the R8 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.
+
+package com.android.tools.r8.graph;
+
+public interface DexDefinitionSupplier {
+
+  DexDefinition definitionFor(DexReference reference);
+
+  DexEncodedField definitionFor(DexField field);
+
+  DexEncodedMethod definitionFor(DexMethod method);
+
+  DexClass definitionFor(DexType type);
+
+  DexItemFactory dexItemFactory();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
index e8d6a4f..14c98e4 100644
--- a/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
@@ -11,59 +11,93 @@
 import java.util.Set;
 import java.util.function.Consumer;
 
-public class TopDownClassHierarchyTraversal {
+public class TopDownClassHierarchyTraversal<T extends DexClass> {
 
-  public static void visit(
-      AppView<? extends AppInfo> appView,
-      Iterable<DexProgramClass> classes,
-      Consumer<DexProgramClass> visitor) {
-    Deque<DexProgramClass> worklist = new ArrayDeque<>();
-    Set<DexProgramClass> visited = new HashSet<>();
+  private enum Scope {
+    ALL_CLASSES,
+    ONLY_PROGRAM_CLASSES
+  }
 
-    Iterator<DexProgramClass> classIterator = classes.iterator();
+  private final DexDefinitionSupplier definitions;
+  private final Scope scope;
+
+  private final Set<T> visited = new HashSet<>();
+  private final Deque<T> worklist = new ArrayDeque<>();
+
+  private TopDownClassHierarchyTraversal(DexDefinitionSupplier definitions, Scope scope) {
+    this.definitions = definitions;
+    this.scope = scope;
+  }
+
+  /**
+   * Returns a visitor that can be used to visit all the classes (including class path and library
+   * classes) that are reachable from a given set of sources.
+   */
+  public static TopDownClassHierarchyTraversal<DexClass> forAllClasses(
+      DexDefinitionSupplier definitions) {
+    return new TopDownClassHierarchyTraversal<>(definitions, Scope.ALL_CLASSES);
+  }
+
+  /**
+   * Returns a visitor that can be used to visit all the program classes that are reachable from a
+   * given set of sources.
+   */
+  public static TopDownClassHierarchyTraversal<DexProgramClass> forProgramClasses(
+      DexDefinitionSupplier definitions) {
+    return new TopDownClassHierarchyTraversal<>(definitions, Scope.ONLY_PROGRAM_CLASSES);
+  }
+
+  public void visit(Iterable<DexProgramClass> sources, Consumer<T> visitor) {
+    Iterator<DexProgramClass> sourceIterator = sources.iterator();
 
     // Visit the program classes in a top-down order according to the class hierarchy.
-    while (classIterator.hasNext() || !worklist.isEmpty()) {
+    while (sourceIterator.hasNext() || !worklist.isEmpty()) {
       if (worklist.isEmpty()) {
-        // Add the ancestors of this class (including the class itself) to the worklist in such a
-        // way that all super types of the class come before the class itself.
-        addAncestorsToWorklist(classIterator.next(), worklist, visited, appView);
+        // Add the ancestors of the next source (including the source itself) to the worklist in
+        // such a way that all super types of the source class come before the class itself.
+        addAncestorsToWorklist(sourceIterator.next());
         if (worklist.isEmpty()) {
           continue;
         }
       }
 
-      DexProgramClass clazz = worklist.removeFirst();
+      T clazz = worklist.removeFirst();
       if (visited.add(clazz)) {
+        assert scope != Scope.ONLY_PROGRAM_CLASSES || clazz.isProgramClass();
         visitor.accept(clazz);
       }
     }
+
+    visited.clear();
   }
 
-  private static void addAncestorsToWorklist(
-      DexProgramClass clazz,
-      Deque<DexProgramClass> worklist,
-      Set<DexProgramClass> visited,
-      AppView<? extends AppInfo> appView) {
-    if (visited.contains(clazz)) {
+  private void addAncestorsToWorklist(DexClass clazz) {
+    @SuppressWarnings("unchecked")
+    T clazzWithTypeT = (T) clazz;
+
+    if (visited.contains(clazzWithTypeT)) {
       return;
     }
 
-    worklist.addFirst(clazz);
+    worklist.addFirst(clazzWithTypeT);
 
     // Add super classes to worklist.
     if (clazz.superType != null) {
-      DexClass definition = appView.appInfo().definitionFor(clazz.superType);
-      if (definition != null && definition.isProgramClass()) {
-        addAncestorsToWorklist(definition.asProgramClass(), worklist, visited, appView);
+      DexClass definition = definitions.definitionFor(clazz.superType);
+      if (definition != null) {
+        if (scope != Scope.ONLY_PROGRAM_CLASSES || definition.isProgramClass()) {
+          addAncestorsToWorklist(definition);
+        }
       }
     }
 
     // Add super interfaces to worklist.
     for (DexType interfaceType : clazz.interfaces.values) {
-      DexClass definition = appView.appInfo().definitionFor(interfaceType);
-      if (definition != null && definition.isProgramClass()) {
-        addAncestorsToWorklist(definition.asProgramClass(), worklist, visited, appView);
+      DexClass definition = definitions.definitionFor(interfaceType);
+      if (definition != null) {
+        if (scope != Scope.ONLY_PROGRAM_CLASSES || definition.isProgramClass()) {
+          addAncestorsToWorklist(definition);
+        }
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 32fc687..2adc697 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -194,7 +194,7 @@
     if (enableWholeProgramOptimizations) {
       assert appInfo.hasLiveness();
       AppInfoWithLiveness appInfoWithLiveness = appInfo.withLiveness();
-      AppView<? extends AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       assert rootSet != null;
       this.nonNullTracker = new NonNullTracker(
           appInfoWithLiveness, libraryMethodsReturningNonNull(appInfo.dexItemFactory));
@@ -213,7 +213,7 @@
           options.enableDevirtualization ? new Devirtualizer(appViewWithLiveness) : null;
       this.uninstantiatedTypeOptimization =
           options.enableUninstantiatedTypeOptimization
-              ? new UninstantiatedTypeOptimization(appViewWithLiveness, options)
+              ? new UninstantiatedTypeOptimization(appViewWithLiveness)
               : null;
       this.typeChecker = new TypeChecker(appView);
     } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
index 26dee92..52de259 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -4,7 +4,8 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -34,19 +35,18 @@
 
   private static final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
 
-  private final DexApplication application;
+  private final AppView<? extends AppInfo> appView;
   private final Map<DexClass, MethodPool> methodPools = new ConcurrentHashMap<>();
 
-  public MethodPoolCollection(DexApplication application) {
-    this.application = application;
+  public MethodPoolCollection(AppView<? extends AppInfo> appView) {
+    this.appView = appView;
   }
 
   public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
     timing.begin("Building method pool collection");
     try {
       List<Future<?>> futures = new ArrayList<>();
-      @SuppressWarnings("unchecked")
-      List<DexClass> classes = (List) application.classes();
+      Iterable<? extends DexClass> classes = appView.appInfo().classes();
       submitAll(classes, futures, executorService);
       ThreadUtils.awaitFutures(futures);
     } finally {
@@ -85,7 +85,9 @@
   }
 
   private void submitAll(
-      Iterable<DexClass> classes, List<Future<?>> futures, ExecutorService executorService) {
+      Iterable<? extends DexClass> classes,
+      List<Future<?>> futures,
+      ExecutorService executorService) {
     for (DexClass clazz : classes) {
       futures.add(executorService.submit(computeMethodPoolPerClass(clazz)));
     }
@@ -102,7 +104,7 @@
             }
           });
       if (clazz.superType != null) {
-        DexClass superClazz = application.definitionFor(clazz.superType);
+        DexClass superClazz = appView.definitionFor(clazz.superType);
         if (superClazz != null) {
           MethodPool superPool = methodPools.computeIfAbsent(superClazz, k -> new MethodPool());
           superPool.linkSubtype(methodPool);
@@ -111,9 +113,10 @@
       }
       if (clazz.isInterface()) {
         for (DexType subtype : clazz.type.allImmediateSubtypes()) {
-          DexClass subClazz = application.definitionFor(subtype);
+          DexClass subClazz = appView.definitionFor(subtype);
           if (subClazz != null) {
             MethodPool childPool = methodPools.computeIfAbsent(subClazz, k -> new MethodPool());
+            methodPool.linkSubtype(childPool);
             childPool.linkInterface(methodPool);
           }
         }
@@ -133,10 +136,10 @@
       }
       if (superTypes.add(clazz)) {
         if (clazz.superType != null) {
-          addNonNull(worklist, application.definitionFor(clazz.superType));
+          addNonNull(worklist, appView.definitionFor(clazz.superType));
         }
         for (DexType interfaceType : clazz.interfaces.values) {
-          addNonNull(worklist, application.definitionFor(interfaceType));
+          addNonNull(worklist, appView.definitionFor(interfaceType));
         }
       }
     }
@@ -147,20 +150,18 @@
       DexClass subject, Predicate<DexClass> stoppingCriterion) {
     Set<DexClass> subTypes = new HashSet<>();
     Deque<DexClass> worklist = new ArrayDeque<>();
-    subject.type.forAllExtendsSubtypes(
-        type -> addNonNull(worklist, application.definitionFor(type)));
+    subject.type.forAllExtendsSubtypes(type -> addNonNull(worklist, appView.definitionFor(type)));
     subject.type.forAllImplementsSubtypes(
-        type -> addNonNull(worklist, application.definitionFor(type)));
+        type -> addNonNull(worklist, appView.definitionFor(type)));
     while (!worklist.isEmpty()) {
       DexClass clazz = worklist.pop();
       if (stoppingCriterion.test(clazz)) {
         continue;
       }
       if (subTypes.add(clazz)) {
-        clazz.type.forAllExtendsSubtypes(
-            type -> addNonNull(worklist, application.definitionFor(type)));
+        clazz.type.forAllExtendsSubtypes(type -> addNonNull(worklist, appView.definitionFor(type)));
         clazz.type.forAllImplementsSubtypes(
-            type -> addNonNull(worklist, application.definitionFor(type)));
+            type -> addNonNull(worklist, appView.definitionFor(type)));
       }
     }
     return subTypes;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 7385237..c0d0266 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
@@ -39,7 +40,6 @@
 import com.android.tools.r8.ir.optimize.MethodPoolCollection.MethodPool;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence.Wrapper;
@@ -104,29 +104,20 @@
     }
   }
 
+  private static final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+
   private final AppView<? extends AppInfoWithLiveness> appView;
-  private final DexItemFactory dexItemFactory;
-  private final InternalOptions options;
 
   private int numberOfInstanceGetOrInstancePutWithNullReceiver = 0;
   private int numberOfInvokesWithNullArgument = 0;
   private int numberOfInvokesWithNullReceiver = 0;
 
-  public UninstantiatedTypeOptimization(
-      AppView<? extends AppInfoWithLiveness> appView, InternalOptions options) {
+  public UninstantiatedTypeOptimization(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
-    this.dexItemFactory = appView.dexItemFactory();
-    this.options = options;
   }
 
   public GraphLense run(
       MethodPoolCollection methodPoolCollection, ExecutorService executorService, Timing timing) {
-    BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
-    Map<DexMethod, RemovedArgumentsInfo> removedArgumentsInfoPerMethod = new IdentityHashMap<>();
-
-    MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-
-    Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods = new HashMap<>();
 
     try {
       methodPoolCollection.buildAll(executorService, timing);
@@ -134,135 +125,21 @@
       throw new RuntimeException(e);
     }
 
-    TopDownClassHierarchyTraversal.visit(
-        appView,
-        appView.appInfo().classes(),
-        clazz -> {
-          MethodPool methodPool = methodPoolCollection.get(clazz);
+    Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods = new HashMap<>();
+    BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
+    Map<DexMethod, RemovedArgumentsInfo> removedArgumentsInfoPerMethod = new IdentityHashMap<>();
 
-          if (clazz.isInterface()) {
-            // Do not allow changing the prototype of methods that override an interface method.
-            // This achieved by faking that there is already a method with the given signature.
-            for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
-              RewrittenPrototypeDescription prototypeChanges =
-                  new RewrittenPrototypeDescription(
-                      isAlwaysNull(virtualMethod.method.proto.returnType),
-                      getRemovedArgumentsInfo(virtualMethod, ALLOW_ARGUMENT_REMOVAL));
-              if (!prototypeChanges.isEmpty()) {
-                DexMethod newMethod = getNewMethodSignature(virtualMethod, prototypeChanges);
-                Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
-                if (!methodPool.hasSeenDirectly(wrapper)) {
-                  methodPool.seen(wrapper);
-                }
-              }
-            }
-            return;
-          }
+    TopDownClassHierarchyTraversal.forProgramClasses(appView)
+        .visit(
+            appView.appInfo().classes(),
+            clazz ->
+                processClass(
+                    clazz,
+                    changedVirtualMethods,
+                    methodMapping,
+                    methodPoolCollection,
+                    removedArgumentsInfoPerMethod));
 
-          Map<DexEncodedMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
-              new IdentityHashMap<>();
-          for (DexEncodedMethod directMethod : clazz.directMethods()) {
-            RewrittenPrototypeDescription prototypeChanges =
-                getPrototypeChanges(directMethod, ALLOW_ARGUMENT_REMOVAL);
-            if (!prototypeChanges.isEmpty()) {
-              prototypeChangesPerMethod.put(directMethod, prototypeChanges);
-            }
-          }
-
-          // Reserve all signatures which are known to not be touched below.
-          Set<Wrapper<DexMethod>> usedSignatures = new HashSet<>();
-          for (DexEncodedMethod method : clazz.methods()) {
-            if (!prototypeChangesPerMethod.containsKey(method)) {
-              usedSignatures.add(equivalence.wrap(method.method));
-            }
-          }
-
-          // Change the return type of direct methods that return an uninstantiated type to void.
-          List<DexEncodedMethod> directMethods = clazz.directMethods();
-          for (int i = 0; i < directMethods.size(); ++i) {
-            DexEncodedMethod encodedMethod = directMethods.get(i);
-            DexMethod method = encodedMethod.method;
-            RewrittenPrototypeDescription prototypeChanges =
-                prototypeChangesPerMethod.getOrDefault(
-                    encodedMethod, RewrittenPrototypeDescription.none());
-            RemovedArgumentsInfo removedArgumentsInfo = prototypeChanges.getRemovedArgumentsInfo();
-            DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
-            if (newMethod != method) {
-              Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
-
-              // TODO(b/110806787): Can be extended to handle collisions by renaming the given
-              // method.
-              if (usedSignatures.add(wrapper)) {
-                clazz.setDirectMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
-                methodMapping.put(method, newMethod);
-                if (removedArgumentsInfo.hasRemovedArguments()) {
-                  removedArgumentsInfoPerMethod.put(newMethod, removedArgumentsInfo);
-                }
-              }
-            }
-          }
-
-          // Change the return type of virtual methods that return an uninstantiated type to void.
-          // This is done in two steps. First we change the return type of all methods that override
-          // a method whose return type has already been changed to void previously. Note that
-          // all supertypes of the current class are always visited prior to the current class.
-          // This is important to ensure that a method that used to override a method in its super
-          // class will continue to do so after this optimization.
-          List<DexEncodedMethod> virtualMethods = clazz.virtualMethods();
-          for (int i = 0; i < virtualMethods.size(); ++i) {
-            DexEncodedMethod encodedMethod = virtualMethods.get(i);
-            DexMethod method = encodedMethod.method;
-            RewrittenPrototypeDescription prototypeChanges =
-                getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
-            DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
-            if (newMethod != method) {
-              Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
-
-              boolean isOverrideOfPreviouslyChangedMethodInSuperClass =
-                  changedVirtualMethods.getOrDefault(equivalence.wrap(method), ImmutableSet.of())
-                      .stream()
-                      .anyMatch(other -> clazz.type.isSubtypeOf(other, appView.appInfo()));
-              if (isOverrideOfPreviouslyChangedMethodInSuperClass) {
-                assert methodPool.hasSeen(wrapper);
-
-                boolean signatureIsAvailable = usedSignatures.add(wrapper);
-                assert signatureIsAvailable;
-
-                clazz.setVirtualMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
-                methodMapping.put(method, newMethod);
-              }
-            }
-          }
-          for (int i = 0; i < virtualMethods.size(); ++i) {
-            DexEncodedMethod encodedMethod = virtualMethods.get(i);
-            DexMethod method = encodedMethod.method;
-            RewrittenPrototypeDescription prototypeChanges =
-                getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
-            DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
-            if (newMethod != method) {
-              Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
-
-              // TODO(b/110806787): Can be extended to handle collisions by renaming the given
-              // method. Note that this also requires renaming all of the methods that override this
-              // method, though.
-              if (!methodPool.hasSeen(wrapper) && usedSignatures.add(wrapper)) {
-                methodPool.seen(wrapper);
-
-                clazz.setVirtualMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
-                methodMapping.put(method, newMethod);
-
-                boolean added =
-                    changedVirtualMethods
-                        .computeIfAbsent(equivalence.wrap(method), key -> Sets.newIdentityHashSet())
-                        .add(clazz.type);
-                assert added;
-              }
-            }
-          }
-        });
-
-    // TODO(christofferqa): There is no need to do anything at the call site when the graph lense
-    // is unchanged!
     if (!methodMapping.isEmpty()) {
       return new UninstantiatedTypeOptimizationGraphLense(
           methodMapping, removedArgumentsInfoPerMethod, appView);
@@ -270,6 +147,134 @@
     return appView.graphLense();
   }
 
+  private void processClass(
+      DexProgramClass clazz,
+      Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods,
+      BiMap<DexMethod, DexMethod> methodMapping,
+      MethodPoolCollection methodPoolCollection,
+      Map<DexMethod, RemovedArgumentsInfo> removedArgumentsInfoPerMethod) {
+    MethodPool methodPool = methodPoolCollection.get(clazz);
+
+    if (clazz.isInterface()) {
+      // Do not allow changing the prototype of methods that override an interface method.
+      // This achieved by faking that there is already a method with the given signature.
+      for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
+        RewrittenPrototypeDescription prototypeChanges =
+            new RewrittenPrototypeDescription(
+                isAlwaysNull(virtualMethod.method.proto.returnType),
+                getRemovedArgumentsInfo(virtualMethod, ALLOW_ARGUMENT_REMOVAL));
+        if (!prototypeChanges.isEmpty()) {
+          DexMethod newMethod = getNewMethodSignature(virtualMethod, prototypeChanges);
+          Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+          if (!methodPool.hasSeenDirectly(wrapper)) {
+            methodPool.seen(wrapper);
+          }
+        }
+      }
+      return;
+    }
+
+    Map<DexEncodedMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
+        new IdentityHashMap<>();
+    for (DexEncodedMethod directMethod : clazz.directMethods()) {
+      RewrittenPrototypeDescription prototypeChanges =
+          getPrototypeChanges(directMethod, ALLOW_ARGUMENT_REMOVAL);
+      if (!prototypeChanges.isEmpty()) {
+        prototypeChangesPerMethod.put(directMethod, prototypeChanges);
+      }
+    }
+
+    // Reserve all signatures which are known to not be touched below.
+    Set<Wrapper<DexMethod>> usedSignatures = new HashSet<>();
+    for (DexEncodedMethod method : clazz.methods()) {
+      if (!prototypeChangesPerMethod.containsKey(method)) {
+        usedSignatures.add(equivalence.wrap(method.method));
+      }
+    }
+
+    // Change the return type of direct methods that return an uninstantiated type to void.
+    List<DexEncodedMethod> directMethods = clazz.directMethods();
+    for (int i = 0; i < directMethods.size(); ++i) {
+      DexEncodedMethod encodedMethod = directMethods.get(i);
+      DexMethod method = encodedMethod.method;
+      RewrittenPrototypeDescription prototypeChanges =
+          prototypeChangesPerMethod.getOrDefault(
+              encodedMethod, RewrittenPrototypeDescription.none());
+      RemovedArgumentsInfo removedArgumentsInfo = prototypeChanges.getRemovedArgumentsInfo();
+      DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
+      if (newMethod != method) {
+        Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+
+        // TODO(b/110806787): Can be extended to handle collisions by renaming the given
+        // method.
+        if (usedSignatures.add(wrapper)) {
+          clazz.setDirectMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
+          methodMapping.put(method, newMethod);
+          if (removedArgumentsInfo.hasRemovedArguments()) {
+            removedArgumentsInfoPerMethod.put(newMethod, removedArgumentsInfo);
+          }
+        }
+      }
+    }
+
+    // Change the return type of virtual methods that return an uninstantiated type to void.
+    // This is done in two steps. First we change the return type of all methods that override
+    // a method whose return type has already been changed to void previously. Note that
+    // all supertypes of the current class are always visited prior to the current class.
+    // This is important to ensure that a method that used to override a method in its super
+    // class will continue to do so after this optimization.
+    List<DexEncodedMethod> virtualMethods = clazz.virtualMethods();
+    for (int i = 0; i < virtualMethods.size(); ++i) {
+      DexEncodedMethod encodedMethod = virtualMethods.get(i);
+      DexMethod method = encodedMethod.method;
+      RewrittenPrototypeDescription prototypeChanges =
+          getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
+      DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
+      if (newMethod != method) {
+        Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+
+        boolean isOverrideOfPreviouslyChangedMethodInSuperClass =
+            changedVirtualMethods.getOrDefault(equivalence.wrap(method), ImmutableSet.of()).stream()
+                .anyMatch(other -> clazz.type.isSubtypeOf(other, appView.appInfo()));
+        if (isOverrideOfPreviouslyChangedMethodInSuperClass) {
+          assert methodPool.hasSeen(wrapper);
+
+          boolean signatureIsAvailable = usedSignatures.add(wrapper);
+          assert signatureIsAvailable;
+
+          clazz.setVirtualMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
+          methodMapping.put(method, newMethod);
+        }
+      }
+    }
+    for (int i = 0; i < virtualMethods.size(); ++i) {
+      DexEncodedMethod encodedMethod = virtualMethods.get(i);
+      DexMethod method = encodedMethod.method;
+      RewrittenPrototypeDescription prototypeChanges =
+          getPrototypeChanges(encodedMethod, DISALLOW_ARGUMENT_REMOVAL);
+      DexMethod newMethod = getNewMethodSignature(encodedMethod, prototypeChanges);
+      if (newMethod != method) {
+        Wrapper<DexMethod> wrapper = equivalence.wrap(newMethod);
+
+        // TODO(b/110806787): Can be extended to handle collisions by renaming the given
+        //  method. Note that this also requires renaming all of the methods that override this
+        //  method, though.
+        if (!methodPool.hasSeen(wrapper) && usedSignatures.add(wrapper)) {
+          methodPool.seen(wrapper);
+
+          clazz.setVirtualMethod(i, encodedMethod.toTypeSubstitutedMethod(newMethod));
+          methodMapping.put(method, newMethod);
+
+          boolean added =
+              changedVirtualMethods
+                  .computeIfAbsent(equivalence.wrap(method), key -> Sets.newIdentityHashSet())
+                  .add(clazz.type);
+          assert added;
+        }
+      }
+    }
+  }
+
   private RewrittenPrototypeDescription getPrototypeChanges(
       DexEncodedMethod encodedMethod, Strategy strategy) {
     if (ArgumentRemovalUtils.isPinned(encodedMethod, appView)
@@ -311,6 +316,8 @@
 
   private DexMethod getNewMethodSignature(
       DexEncodedMethod encodedMethod, RewrittenPrototypeDescription prototypeChanges) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+
     DexMethod method = encodedMethod.method;
     RemovedArgumentsInfo removedArgumentsInfo = prototypeChanges.getRemovedArgumentsInfo();
 
@@ -477,7 +484,7 @@
             TypeLatticeElement.fromDexType(fieldType, true, appView.appInfo());
         if (!value.getTypeLattice().lessThanOrEqual(fieldLatticeType, appView.appInfo())) {
           // Broken type hierarchy. See FieldTypeTest#test_brokenTypeHierarchy.
-          assert options.testing.allowTypeErrors;
+          assert appView.options().testing.allowTypeErrors;
           return;
         }
 
@@ -554,7 +561,8 @@
     Value nullValue = new Value(code.valueNumberGenerator.next(), TypeLatticeElement.NULL, null);
     ConstNumber constNumberInstruction = new ConstNumber(nullValue, 0);
     // Note that we only keep position info for throwing instructions in release mode.
-    constNumberInstruction.setPosition(options.debug ? instruction.getPosition() : Position.none());
+    constNumberInstruction.setPosition(
+        appView.options().debug ? instruction.getPosition() : Position.none());
     instructionIterator.add(constNumberInstruction);
     instructionIterator.next();
 
@@ -580,8 +588,8 @@
               // target.
               return;
             }
-            if (guard != dexItemFactory.catchAllType
-                && !dexItemFactory.npeType.isSubtypeOf(guard, appView.appInfo())) {
+            if (guard != DexItemFactory.catchAllType
+                && !appView.dexItemFactory().npeType.isSubtypeOf(guard, appView.appInfo())) {
               // TODO(christofferqa): Consider updating previous dominator tree instead of
               // rebuilding it from scratch.
               DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 3b3c125..a08f5d9 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -35,7 +35,7 @@
       RootSet rootSet) {
     this.application = application;
     this.appView = appView;
-    this.methodPoolCollection = new MethodPoolCollection(application);
+    this.methodPoolCollection = new MethodPoolCollection(appView);
     this.rootSet = rootSet;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 965d563..116a9e3 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -202,9 +202,7 @@
   private final AppInfoWithLiveness appInfo;
   private final AppView<? extends AppInfoWithLiveness> appView;
   private final ExecutorService executorService;
-  private final GraphLense graphLense;
   private final MethodPoolCollection methodPoolCollection;
-  private final InternalOptions options;
   private final Timing timing;
   private Collection<DexMethod> invokes;
 
@@ -239,9 +237,7 @@
     this.appInfo = appView.appInfo();
     this.appView = appView;
     this.executorService = executorService;
-    this.graphLense = appView.graphLense();
-    this.methodPoolCollection = new MethodPoolCollection(application);
-    this.options = options;
+    this.methodPoolCollection = new MethodPoolCollection(appView);
     this.renamedMembersLense = new VerticalClassMergerGraphLense.Builder();
     this.timing = timing;
     this.mainDexClasses = mainDexClasses;
@@ -607,7 +603,7 @@
 
   public GraphLense run() {
     timing.begin("merge");
-    GraphLense mergingGraphLense = mergeClasses(graphLense);
+    GraphLense mergingGraphLense = mergeClasses();
     timing.end();
     timing.begin("fixup");
     GraphLense result = new TreeFixer().fixupTypeReferences(mergingGraphLense);
@@ -683,13 +679,14 @@
     return true;
   }
 
-  private GraphLense mergeClasses(GraphLense graphLense) {
+  private GraphLense mergeClasses() {
     // Visit the program classes in a top-down order according to the class hierarchy.
-    TopDownClassHierarchyTraversal.visit(appView, mergeCandidates, this::mergeClassIfPossible);
+    TopDownClassHierarchyTraversal.forProgramClasses(appView)
+        .visit(mergeCandidates, this::mergeClassIfPossible);
     if (Log.ENABLED) {
       Log.debug(getClass(), "Merged %d classes.", mergedClasses.size());
     }
-    return renamedMembersLense.build(graphLense, mergedClasses, appInfo);
+    return renamedMembersLense.build(appView.graphLense(), mergedClasses, appInfo);
   }
 
   private boolean methodResolutionMayChange(DexClass source, DexClass target) {
@@ -1189,7 +1186,7 @@
       SynthesizedBridgeCode code =
           new SynthesizedBridgeCode(
               newMethod,
-              graphLense.getOriginalMethodSignature(method.method),
+              appView.graphLense().getOriginalMethodSignature(method.method),
               invocationTarget.method,
               invocationTarget.isPrivateMethod() ? DIRECT : STATIC);
 
@@ -1625,7 +1622,7 @@
   }
 
   private boolean disallowInlining(DexEncodedMethod method, DexType invocationContext) {
-    if (options.enableInlining) {
+    if (appView.options().enableInlining) {
       if (method.getCode().isJarCode()) {
         JarCode jarCode = method.getCode().asJarCode();
         ConstraintWithTarget constraint =
@@ -1686,7 +1683,7 @@
     public GraphLenseLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
       // First look up the method using the existing graph lense (for example, the type will have
       // changed if the method was publicized by ClassAndMemberPublicizer).
-      GraphLenseLookupResult lookup = graphLense.lookupMethod(method, context, type);
+      GraphLenseLookupResult lookup = appView.graphLense().lookupMethod(method, context, type);
       DexMethod previousMethod = lookup.getMethod();
       Type previousType = lookup.getType();
       // Then check if there is a renaming due to the vertical class merger.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
new file mode 100644
index 0000000..28c5b2f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2019, the R8 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.
+
+package com.android.tools.r8.ir.optimize.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CollisionWithLibraryMethodsTest extends TestBase {
+
+  private final Backend backend;
+
+  @Parameters(name = "Backend: {0}")
+  public static Backend[] params() {
+    return Backend.values();
+  }
+
+  public CollisionWithLibraryMethodsTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("Hello world!");
+    testForR8(backend)
+        .addInnerClasses(CollisionWithLibraryMethodsTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .compile()
+        .inspect(this::verify)
+        .run(TestClass.class)
+        .assertSuccessWithOutput(expectedOutput);
+  }
+
+  private void verify(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(Greeting.class);
+    assertThat(classSubject, isPresent());
+
+    MethodSubject methodSubject = classSubject.uniqueMethodWithName("toString");
+    assertThat(methodSubject, isPresent());
+    assertEquals("java.lang.Object", methodSubject.getMethod().method.proto.parameters.toString());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(new Greeting().toString(null));
+    }
+  }
+
+  @NeverClassInline
+  static class Greeting {
+
+    @NeverInline
+    public String toString(Object unused) {
+      return "Hello world!";
+    }
+  }
+}
diff --git a/tools/archive.py b/tools/archive.py
index c37b304..e9233c7 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -47,6 +47,10 @@
 def IsMaster(version):
   branches = subprocess.check_output(['git', 'branch', '-r', '--contains',
                                       'HEAD'])
+  # CL runs from gerrit does not have a branch, we always treat them as master
+  # commits to archive these to the hash based location
+  if len(branches) == 0:
+    return True
   if not version.endswith('-dev'):
     # Sanity check, we don't want to archive on top of release builds EVER
     # Note that even though we branch, we never push the bots to build the same