Merge "Trim <clinit> some more"
diff --git a/src/main/java/com/android/tools/r8/BSPatch.java b/src/main/java/com/android/tools/r8/BSPatch.java
index eda5f23..8c1d208 100644
--- a/src/main/java/com/android/tools/r8/BSPatch.java
+++ b/src/main/java/com/android/tools/r8/BSPatch.java
@@ -202,7 +202,6 @@
 
     @Override
     public void copyOld(int blockSize) throws IOException {
-      assert mergeBuffer != null;
       assert mergeBuffer.length == blockSize;
       byte[] data = new byte[blockSize];
       oldInput.get(data);
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 4c8cdd5..ed992a3 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -55,7 +55,7 @@
  */
 public final class D8 {
 
-  private static final String kVersion = "v0.0.1";
+  private static final String VERSION = "v0.1.0";
   private static final int STATUS_ERROR = 1;
 
   private D8() {}
@@ -110,7 +110,7 @@
       return;
     }
     if (command.isPrintVersion()) {
-      System.out.println("D8 " + kVersion);
+      System.out.println("D8 " + VERSION);
       return;
     }
     run(command);
@@ -161,7 +161,7 @@
       return options.getMarker();
     }
     return new Marker(Tool.D8)
-        .put("version", kVersion)
+        .put("version", VERSION)
         .put("min-api", options.minApiLevel);
   }
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index acbbe0a..f30ee0a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -71,7 +71,7 @@
 
 public class R8 {
 
-  private static final String kVersion = "v0.0.1";
+  private static final String VERSION = "v0.1.0";
   private final Timing timing = new Timing("R8");
   private final InternalOptions options;
 
@@ -86,7 +86,7 @@
       return options.getMarker();
     }
     return new Marker(Tool.R8)
-        .put("version", kVersion)
+        .put("version", VERSION)
         .put("min-api", options.minApiLevel);
   }
 
@@ -502,7 +502,7 @@
       return;
     }
     if (command.isPrintVersion()) {
-      System.out.println("R8 " + kVersion);
+      System.out.println("R8 " + VERSION);
       return;
     }
     run(command);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 928b28f..0895ed8 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -37,14 +37,10 @@
 
     private Builder() {
       super(CompilationMode.RELEASE);
-      // TODO(b/62048823): Minifier should not depend on -allowaccessmodification.
-      proguardConfigurationConsumer = builder -> builder.setAllowAccessModification(true);
     }
 
     private Builder(AndroidApp app) {
       super(app, CompilationMode.RELEASE);
-      // TODO(b/62048823): Minifier should not depend on -allowaccessmodification.
-      proguardConfigurationConsumer = builder -> builder.setAllowAccessModification(true);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index a8f56a7..f1162a9 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -253,10 +253,10 @@
 
   private void sortClassData(Collection<DexProgramClass> classesWithData) {
     for (DexProgramClass clazz : classesWithData) {
-      sortEncodedFields(clazz.instanceFields);
-      sortEncodedFields(clazz.staticFields);
-      sortEncodedMethods(clazz.directMethods);
-      sortEncodedMethods(clazz.virtualMethods);
+      sortEncodedFields(clazz.instanceFields());
+      sortEncodedFields(clazz.staticFields());
+      sortEncodedMethods(clazz.directMethods());
+      sortEncodedMethods(clazz.virtualMethods());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java b/src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java
similarity index 88%
rename from src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java
rename to src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java
index 7872801..8f110d5 100644
--- a/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/CachedHashValueDexItem.java
@@ -4,9 +4,9 @@
 package com.android.tools.r8.graph;
 
 /**
- * DexItems of this kind have to be canonicalized for the whole application.
+ * DexItems of this kind have cached hash values and quick equals check.
  */
-public abstract class CanonicalizedDexItem extends DexItem {
+public abstract class CachedHashValueDexItem extends DexItem {
 
   private static final int NOT_COMPUTED_HASH_VALUE = -1;
   private static final int SENTINEL_HASH_VALUE = 0;
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 54f9470..3845507 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.InternalOptions;
 
-public abstract class Code extends CanonicalizedDexItem {
+public abstract class Code extends CachedHashValueDexItem {
 
   public abstract IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options);
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index 4277324..db55cd6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -17,9 +17,11 @@
   private final List<DexEncodedMethod> methodAnnotations;
   private final List<DexEncodedMethod> parameterAnnotations;
   private final List<DexEncodedField> fieldAnnotations;
+  private final boolean classHasOnlyInternalizableAnnotations;
 
   public DexAnnotationDirectory(DexProgramClass clazz) {
     this.clazz = clazz;
+    this.classHasOnlyInternalizableAnnotations = clazz.hasOnlyInternalizableAnnotations();
     assert isSorted(clazz.directMethods());
     assert isSorted(clazz.virtualMethods());
     OrderedMergingIterator<DexEncodedMethod, DexMethod> methods =
@@ -77,7 +79,7 @@
     if (!(obj instanceof DexAnnotationDirectory)) {
       return false;
     }
-    if (clazz.hasOnlyInternalizableAnnotations()) {
+    if (classHasOnlyInternalizableAnnotations) {
       DexAnnotationDirectory other = (DexAnnotationDirectory) obj;
       if (!other.clazz.hasOnlyInternalizableAnnotations()) {
         return false;
@@ -89,7 +91,7 @@
 
   @Override
   public final int hashCode() {
-    if (clazz.hasOnlyInternalizableAnnotations()) {
+    if (classHasOnlyInternalizableAnnotations) {
       return clazz.annotations.hashCode();
     }
     return super.hashCode();
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index e91e1f0..00b99fa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import java.util.Arrays;
 
-public class DexAnnotationSet extends DexItem {
+public class DexAnnotationSet extends CachedHashValueDexItem {
 
   private static final int UNSORTED = 0;
   private static final DexAnnotationSet THE_EMPTY_ANNOTATIONS_SET =
@@ -25,15 +25,12 @@
   }
 
   @Override
-  public int hashCode() {
+  public int computeHashCode() {
     return Arrays.hashCode(annotations);
   }
 
   @Override
-  public boolean equals(Object other) {
-    if (this == other) {
-      return true;
-    }
+  public boolean computeEquals(Object other) {
     if (other instanceof DexAnnotationSet) {
       DexAnnotationSet o = (DexAnnotationSet) other;
       return Arrays.equals(annotations, o.annotations);
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 0e64e9d..3f492b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -22,10 +22,10 @@
   public DexType superType;
   public DexTypeList interfaces;
   public final DexString sourceFile;
-  public DexEncodedField[] staticFields;
-  public DexEncodedField[] instanceFields;
-  public DexEncodedMethod[] directMethods;
-  public DexEncodedMethod[] virtualMethods;
+  protected DexEncodedField[] staticFields;
+  protected DexEncodedField[] instanceFields;
+  protected DexEncodedMethod[] directMethods;
+  protected DexEncodedMethod[] virtualMethods;
   public DexAnnotationSet annotations;
 
   public DexClass(
@@ -39,10 +39,10 @@
     this.accessFlags = accessFlags;
     this.superType = superType;
     this.type = type;
-    this.staticFields = staticFields;
-    this.instanceFields = instanceFields;
-    this.directMethods = directMethods;
-    this.virtualMethods = virtualMethods;
+    setStaticFields(staticFields);
+    setInstanceFields(instanceFields);
+    setDirectMethods(directMethods);
+    setVirtualMethods(virtualMethods);
     this.annotations = annotations;
     if (type == superType) {
       throw new CompilationError("Class " + type.toString() + " cannot extend itself");
@@ -66,13 +66,22 @@
   }
 
   public DexEncodedMethod[] directMethods() {
-    return MoreObjects.firstNonNull(directMethods, NO_METHODS);
+    return directMethods;
+  }
+
+  public void setDirectMethods(DexEncodedMethod[] values) {
+    directMethods = MoreObjects.firstNonNull(values, NO_METHODS);
   }
 
   public DexEncodedMethod[] virtualMethods() {
-    return MoreObjects.firstNonNull(virtualMethods, NO_METHODS);
+    return virtualMethods;
   }
 
+  public void setVirtualMethods(DexEncodedMethod[] values) {
+    virtualMethods = MoreObjects.firstNonNull(values, NO_METHODS);
+  }
+
+
   public void forEachMethod(Consumer<DexEncodedMethod> consumer) {
     for (DexEncodedMethod method : directMethods()) {
       consumer.accept(method);
@@ -103,11 +112,19 @@
   }
 
   public DexEncodedField[] staticFields() {
-    return MoreObjects.firstNonNull(staticFields, NO_FIELDS);
+    return staticFields;
+  }
+
+  public void setStaticFields(DexEncodedField[] values) {
+    staticFields = MoreObjects.firstNonNull(values, NO_FIELDS);
   }
 
   public DexEncodedField[] instanceFields() {
-    return MoreObjects.firstNonNull(instanceFields, NO_FIELDS);
+    return instanceFields;
+  }
+
+  public void setInstanceFields(DexEncodedField[] values) {
+    instanceFields = MoreObjects.firstNonNull(values, NO_FIELDS);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index f82fe2a..d2c9b3b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -8,7 +8,7 @@
 import java.util.Arrays;
 import java.util.List;
 
-public class DexDebugInfo extends CanonicalizedDexItem {
+public class DexDebugInfo extends CachedHashValueDexItem {
 
   public final int startLine;
   public final DexString[] parameters;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index c41113d..13a4d9d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -29,7 +29,7 @@
 public class DexItemFactory {
 
   private final Map<DexString, DexString> strings = new HashMap<>();
-  private final Map<DexType, DexType> types = new HashMap<>();
+  private final Map<DexString, DexType> types = new HashMap<>();
   private final Map<DexField, DexField> fields = new HashMap<>();
   private final Map<DexProto, DexProto> protos = new HashMap<>();
   private final Map<DexMethod, DexMethod> methods = new HashMap<>();
@@ -309,10 +309,16 @@
     return null;
   }
 
-  public DexType createType(DexString descriptor) {
+  synchronized public DexType createType(DexString descriptor) {
     assert !sorted;
-    DexType type = new DexType(descriptor);
-    return canonicalize(types, type);
+    assert descriptor != null;
+    DexType result = types.get(descriptor);
+    if (result == null) {
+      result = new DexType(descriptor);
+      assert !internalSentinels.contains(result);
+      types.put(descriptor, result);
+    }
+    return result;
   }
 
   public DexType createType(String descriptor) {
diff --git a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
index d3d36e5..5c8f439 100644
--- a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
+++ b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
@@ -10,7 +10,7 @@
 /**
  * Subset of dex items that are referenced by some table index.
  */
-public abstract class IndexedDexItem extends CanonicalizedDexItem implements Presorted {
+public abstract class IndexedDexItem extends CachedHashValueDexItem implements Presorted {
 
   private static final int SORTED_INDEX_UNKNOWN = -1;
   private int sortedIndex = SORTED_INDEX_UNKNOWN; // assigned globally after reading.
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 165a014..fad378e 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
@@ -38,8 +38,8 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -89,8 +89,7 @@
     this.graphLense = graphLense != null ? graphLense : GraphLense.getIdentityLense();
     this.options = options;
     this.printer = printer;
-    Set<DexType> libraryClassesWithOptimizationInfo = markLibraryMethodsReturningReceiver();
-    this.codeRewriter = new CodeRewriter(appInfo, libraryClassesWithOptimizationInfo);
+    this.codeRewriter = new CodeRewriter(appInfo, libraryMethodsReturningReceiver());
     this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null;
     this.interfaceMethodRewriter =
         (enableDesugaring && enableInterfaceMethodDesugaring())
@@ -183,18 +182,12 @@
     throw new Unreachable();
   }
 
-  private Set<DexType> markLibraryMethodsReturningReceiver() {
+  private Set<DexMethod> libraryMethodsReturningReceiver() {
+    Set<DexMethod> methods = new HashSet<>();
     DexItemFactory dexItemFactory = appInfo.dexItemFactory;
-    dexItemFactory.stringBuilderMethods.forEachAppendMethod(this::markReturnsReceiver);
-    dexItemFactory.stringBufferMethods.forEachAppendMethod(this::markReturnsReceiver);
-    return ImmutableSet.of(dexItemFactory.stringBuilderType, dexItemFactory.stringBufferType);
-  }
-
-  private void markReturnsReceiver(DexMethod method) {
-    DexEncodedMethod definition = appInfo.definitionFor(method);
-    if (definition != null) {
-      definition.markReturnsArgument(0);
-    }
+    dexItemFactory.stringBufferMethods.forEachAppendMethod(methods::add);
+    dexItemFactory.stringBuilderMethods.forEachAppendMethod(methods::add);
+    return methods;
   }
 
   private void removeLambdaDeserializationMethods() {
@@ -529,7 +522,7 @@
     }
     printMethod(code, "Final IR (non-SSA)");
 
-    // After all the optimizations have take place, we compute whether method should be inlined.
+    // After all the optimizations have take place, we compute whether method should be inlinedex.
     Constraint state;
     if (!options.inlineAccessors || inliner == null) {
       state = Constraint.NEVER;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 236c9e5..b7036f5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -80,15 +80,15 @@
     }
 
     // Add the methods.
-    DexEncodedMethod[] existing = clazz.virtualMethods;
-    clazz.virtualMethods = new DexEncodedMethod[existing.length + methodsToImplement.size()];
-    System.arraycopy(existing, 0, clazz.virtualMethods, 0, existing.length);
+    DexEncodedMethod[] existing = clazz.virtualMethods();
+    clazz.setVirtualMethods(new DexEncodedMethod[existing.length + methodsToImplement.size()]);
+    System.arraycopy(existing, 0, clazz.virtualMethods(), 0, existing.length);
 
     for (int i = 0; i < methodsToImplement.size(); i++) {
       DexEncodedMethod method = methodsToImplement.get(i);
       assert method.accessFlags.isPublic() && !method.accessFlags.isAbstract();
       DexEncodedMethod newMethod = addForwardingMethod(method, clazz);
-      clazz.virtualMethods[existing.length + i] = newMethod;
+      clazz.virtualMethods()[existing.length + i] = newMethod;
       createdMethods.put(newMethod, method);
     }
   }
@@ -142,7 +142,7 @@
     current = clazz;
     while (true) {
       // Hide candidates by virtual method of the class.
-      hideCandidates(current.virtualMethods, candidates, toBeImplemented);
+      hideCandidates(current.virtualMethods(), candidates, toBeImplemented);
       if (candidates.isEmpty()) {
         return toBeImplemented;
       }
@@ -218,12 +218,12 @@
     }
 
     // Hide by virtual methods of this interface.
-    for (DexEncodedMethod virtual : clazz.virtualMethods) {
+    for (DexEncodedMethod virtual : clazz.virtualMethods()) {
       helper.hideMatches(virtual.method);
     }
 
     // Add all default methods of this interface.
-    for (DexEncodedMethod encoded : clazz.virtualMethods) {
+    for (DexEncodedMethod encoded : clazz.virtualMethods()) {
       if (rewriter.isDefaultMethod(encoded)) {
         helper.addDefaultMethod(encoded);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index a84e082..2d06abe 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -44,7 +44,7 @@
 
     // Process virtual interface methods first.
     List<DexEncodedMethod> remainingMethods = new ArrayList<>();
-    for (DexEncodedMethod virtual : iface.virtualMethods) {
+    for (DexEncodedMethod virtual : iface.virtualMethods()) {
       if (rewriter.isDefaultMethod(virtual)) {
         // Create a new method in a companion class to represent default method implementation.
         DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(virtual.method);
@@ -79,14 +79,14 @@
     }
 
     // If at least one bridge methods was removed update the table.
-    if (remainingMethods.size() < iface.virtualMethods.length) {
-      iface.virtualMethods = remainingMethods.toArray(
-          new DexEncodedMethod[remainingMethods.size()]);
+    if (remainingMethods.size() < iface.virtualMethods().length) {
+      iface.setVirtualMethods(remainingMethods.toArray(
+          new DexEncodedMethod[remainingMethods.size()]));
     }
     remainingMethods.clear();
 
     // Process static methods, move them into companion class as well.
-    for (DexEncodedMethod direct : iface.directMethods) {
+    for (DexEncodedMethod direct : iface.directMethods()) {
       if (direct.accessFlags.isPrivate()) {
         // We only expect to see private methods which are lambda$ methods,
         // and they are supposed to be relaxed to package private static methods
@@ -105,9 +105,9 @@
         remainingMethods.add(direct);
       }
     }
-    if (remainingMethods.size() < iface.directMethods.length) {
-      iface.directMethods = remainingMethods.toArray(
-          new DexEncodedMethod[remainingMethods.size()]);
+    if (remainingMethods.size() < iface.directMethods().length) {
+      iface.setDirectMethods(remainingMethods.toArray(
+          new DexEncodedMethod[remainingMethods.size()]));
     }
 
     if (companionMethods.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 0a6ea7e..be6252a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -444,7 +444,7 @@
       DexMethod implMethod = descriptor.implHandle.asMethod();
       DexClass implMethodHolder = definitionFor(implMethod.holder);
 
-      DexEncodedMethod[] directMethods = implMethodHolder.directMethods;
+      DexEncodedMethod[] directMethods = implMethodHolder.directMethods();
       for (int i = 0; i < directMethods.length; i++) {
         DexEncodedMethod encodedMethod = directMethods[i];
         if (implMethod.match(encodedMethod)) {
@@ -491,8 +491,8 @@
       DexEncodedMethod accessorEncodedMethod = new DexEncodedMethod(
           callTarget, accessorFlags, DexAnnotationSet.empty(), DexAnnotationSetRefList.empty(),
           new SynthesizedCode(new AccessorMethodSourceCode(LambdaClass.this)));
-      accessorClass.directMethods = appendMethod(
-          accessorClass.directMethods, accessorEncodedMethod);
+      accessorClass.setDirectMethods(appendMethod(
+          accessorClass.directMethods(), accessorEncodedMethod));
       rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod);
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index af32cb7..f6f8f2c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -162,7 +162,7 @@
   public void removeLambdaDeserializationMethods(Iterable<DexProgramClass> classes) {
     for (DexProgramClass clazz : classes) {
       // Search for a lambda deserialization method and remove it if found.
-      DexEncodedMethod[] directMethods = clazz.directMethods;
+      DexEncodedMethod[] directMethods = clazz.directMethods();
       if (directMethods != null) {
         int methodCount = directMethods.length;
         for (int i = 0; i < methodCount; i++) {
@@ -177,7 +177,7 @@
             DexEncodedMethod[] newMethods = new DexEncodedMethod[methodCount - 1];
             System.arraycopy(directMethods, 0, newMethods, 0, i);
             System.arraycopy(directMethods, i + 1, newMethods, i, methodCount - i - 1);
-            clazz.directMethods = newMethods;
+            clazz.setDirectMethods(newMethods);
 
             // We assume there is only one such method in the class.
             break;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 92e7580..78d8369 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -96,12 +97,12 @@
 
   private final AppInfo appInfo;
   private final DexItemFactory dexItemFactory;
-  private final Set<DexType> libraryClassesWithOptimizationInfo;
+  private final Set<DexMethod> libraryMethodsReturningReceiver;
 
-  public CodeRewriter(AppInfo appInfo, Set<DexType> libraryClassesWithOptimizationInfo) {
+  public CodeRewriter(AppInfo appInfo, Set<DexMethod> libraryMethodsReturningReceiver) {
     this.appInfo = appInfo;
     this.dexItemFactory = appInfo.dexItemFactory;
-    this.libraryClassesWithOptimizationInfo = libraryClassesWithOptimizationInfo;
+    this.libraryMethodsReturningReceiver = libraryMethodsReturningReceiver;
   }
 
   /**
@@ -517,37 +518,39 @@
 
   // Replace result uses for methods where something is known about what is returned.
   public void rewriteMoveResult(IRCode code) {
-    if (!appInfo.hasSubtyping()) {
-      return;
-    }
+    AppInfoWithSubtyping appInfoWithSubtyping = appInfo.withSubtyping();
     InstructionIterator iterator = code.instructionIterator();
     while (iterator.hasNext()) {
       Instruction current = iterator.next();
       if (current.isInvokeMethod()) {
         InvokeMethod invoke = current.asInvokeMethod();
         if (invoke.outValue() != null && invoke.outValue().getLocalInfo() == null) {
-          DexEncodedMethod target = invoke.computeSingleTarget(appInfo.withSubtyping());
-          // We have a set of library classes with optimization information - consider those
-          // as well.
-          if ((target == null) &&
-              libraryClassesWithOptimizationInfo.contains(invoke.getInvokedMethod().getHolder())) {
-            target = appInfo.definitionFor(invoke.getInvokedMethod());
-          }
-          if (target != null) {
-            DexMethod invokedMethod = target.method;
-            // Check if the invoked method is known to return one of its arguments.
-            DexEncodedMethod definition = appInfo.definitionFor(invokedMethod);
-            if (definition != null && definition.getOptimizationInfo().returnsArgument()) {
-              int argumentIndex = definition.getOptimizationInfo().getReturnedArgument();
-              // Replace the out value of the invoke with the argument and ignore the out value.
-              if (argumentIndex != -1 && checkArgumentType(invoke, target.method, argumentIndex)) {
-                Value argument = invoke.arguments().get(argumentIndex);
-                assert (invoke.outType() == argument.outType()) ||
-                    (invoke.outType() == MoveType.OBJECT
-                        && argument.outType() == MoveType.SINGLE
-                        && argument.getConstInstruction().asConstNumber().isZero());
-                invoke.outValue().replaceUsers(argument);
-                invoke.setOutValue(null);
+          boolean isLibraryMethodReturningReceiver =
+              libraryMethodsReturningReceiver.contains(invoke.getInvokedMethod());
+          if (isLibraryMethodReturningReceiver) {
+            if (checkArgumentType(invoke, invoke.getInvokedMethod(), 0)) {
+              invoke.outValue().replaceUsers(invoke.arguments().get(0));
+              invoke.setOutValue(null);
+            }
+          } else if (appInfoWithSubtyping != null) {
+            DexEncodedMethod target = invoke.computeSingleTarget(appInfoWithSubtyping);
+            if (target != null) {
+              DexMethod invokedMethod = target.method;
+              // Check if the invoked method is known to return one of its arguments.
+              DexEncodedMethod definition = appInfo.definitionFor(invokedMethod);
+              if (definition != null && definition.getOptimizationInfo().returnsArgument()) {
+                int argumentIndex = definition.getOptimizationInfo().getReturnedArgument();
+                // Replace the out value of the invoke with the argument and ignore the out value.
+                if (argumentIndex != -1 && checkArgumentType(invoke, target.method,
+                    argumentIndex)) {
+                  Value argument = invoke.arguments().get(argumentIndex);
+                  assert (invoke.outType() == argument.outType()) ||
+                      (invoke.outType() == MoveType.OBJECT
+                          && argument.outType() == MoveType.SINGLE
+                          && argument.getConstInstruction().asConstNumber().isZero());
+                  invoke.outValue().replaceUsers(argument);
+                  invoke.setOutValue(null);
+                }
               }
             }
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
index 0d93561..a228b20 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import java.util.HashSet;
@@ -30,21 +31,32 @@
             allocator.getRegisterForValue(activeMove.src(), activeMove.getNumber());
         int activeMoveDstRegister =
             allocator.getRegisterForValue(activeMove.dest(), activeMove.getNumber());
-        if (activeMoveSrcRegister == moveSrcRegister
-            && activeMoveDstRegister == moveDstRegister) {
+        if (activeMoveSrcRegister == moveSrcRegister && activeMoveDstRegister == moveDstRegister) {
           return true;
         }
+        if (activeMoveDstRegister == moveSrcRegister && activeMoveSrcRegister == moveDstRegister) {
+          if (move.outType() != MoveType.WIDE) {
+            return true;
+          }
+          // If the move is wide make sure the register pair is non-overlapping.
+          if (moveSrcRegister != moveDstRegister + 1 && moveSrcRegister + 1 != moveDstRegister) {
+            return true;
+          }
+        }
       }
     }
     if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
       Value defined = instruction.outValue();
       int definedRegister = allocator.getRegisterForValue(defined, instruction.getNumber());
       activeMoves.removeIf((m) -> {
-        int moveSrcRegister = allocator.getRegisterForValue(m.inValues().get(0), m.getNumber());
-        int moveDstRegister = allocator.getRegisterForValue(m.outValue(), m.getNumber());
+        int moveSrcRegister = allocator.getRegisterForValue(m.src(), m.getNumber());
+        int moveDstRegister = allocator.getRegisterForValue(m.dest(), m.getNumber());
         for (int i = 0; i < defined.requiredRegisters(); i++) {
-          if (definedRegister + i == moveDstRegister || definedRegister + i == moveSrcRegister) {
-            return true;
+          for (int j = 0; j < m.outValue().requiredRegisters(); j++) {
+            if (definedRegister + i == moveDstRegister + j
+                || definedRegister + i == moveSrcRegister + j) {
+              return true;
+            }
           }
         }
         return false;
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index f42af78..2011530 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.graph.CanonicalizedDexItem;
+import com.android.tools.r8.graph.CachedHashValueDexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.utils.StringUtils;
@@ -16,14 +16,14 @@
 import java.util.Map;
 import java.util.Set;
 
-class NamingState<T extends CanonicalizedDexItem> {
+class NamingState<T extends CachedHashValueDexItem> {
 
   private final NamingState<T> parent;
   private final Map<T, InternalState> usedNames = new IdentityHashMap<>();
   private final DexItemFactory itemFactory;
   private final ImmutableList<String> dictionary;
 
-  static <T extends CanonicalizedDexItem> NamingState<T> createRoot(
+  static <T extends CachedHashValueDexItem> NamingState<T> createRoot(
       DexItemFactory itemFactory, ImmutableList<String> dictionary) {
     return new NamingState<>(null, itemFactory, dictionary);
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index 31c7f1f..300f18e 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -60,7 +60,7 @@
         .collect(Collectors.toSet());
     for (DexType type : classes) {
       DexClass clazz = appInfo.definitionFor(type);
-      clazz.virtualMethods = removeMethods(clazz.virtualMethods, unneededVisibilityBridges);
+      clazz.setVirtualMethods(removeMethods(clazz.virtualMethods(), unneededVisibilityBridges));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
index 2850790..3fd5470 100644
--- a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -35,7 +35,7 @@
     DexClass holder = appInfo.definitionFor(type);
     scope = new ScopedDexItemSet(scope);
     if (holder != null) {
-      holder.virtualMethods = processMethods(holder.virtualMethods);
+      holder.setVirtualMethods(processMethods(holder.virtualMethods()));
     }
     type.forAllExtendsSubtypes(this::processClass);
     scope = scope.getParent();
@@ -74,10 +74,6 @@
     private final ScopedDexItemSet parent;
     private final Set<Wrapper<DexMethod>> items = new HashSet<>();
 
-    private ScopedDexItemSet() {
-      this(null);
-    }
-
     private ScopedDexItemSet(ScopedDexItemSet parent) {
       this.parent = parent;
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
index d3306d9..c04b29a 100644
--- a/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/SimpleClassMerger.java
@@ -257,20 +257,20 @@
           ? DexTypeList.empty()
           : new DexTypeList(interfaces.toArray(new DexType[interfaces.size()]));
       // Step 2: replace fields and methods.
-      target.directMethods = mergedDirectMethods
-          .toArray(new DexEncodedMethod[mergedDirectMethods.size()]);
-      target.virtualMethods = mergedVirtualMethods
-          .toArray(new DexEncodedMethod[mergedVirtualMethods.size()]);
-      target.staticFields = mergedStaticFields
-          .toArray(new DexEncodedField[mergedStaticFields.size()]);
-      target.instanceFields = mergedInstanceFields
-          .toArray(new DexEncodedField[mergedInstanceFields.size()]);
+      target.setDirectMethods(mergedDirectMethods
+          .toArray(new DexEncodedMethod[mergedDirectMethods.size()]));
+      target.setVirtualMethods(mergedVirtualMethods
+          .toArray(new DexEncodedMethod[mergedVirtualMethods.size()]));
+      target.setStaticFields(mergedStaticFields
+          .toArray(new DexEncodedField[mergedStaticFields.size()]));
+      target.setInstanceFields(mergedInstanceFields
+          .toArray(new DexEncodedField[mergedInstanceFields.size()]));
       // Step 3: Unlink old class to ease tree shaking.
       source.superType = application.dexItemFactory.objectType;
-      source.directMethods = null;
-      source.virtualMethods = null;
-      source.instanceFields = null;
-      source.staticFields = null;
+      source.setDirectMethods(null);
+      source.setVirtualMethods(null);
+      source.setInstanceFields(null);
+      source.setStaticFields(null);
       source.interfaces = DexTypeList.empty();
       // Step 4: Record merging.
       mergedClasses.put(source.type, target.type);
@@ -427,11 +427,11 @@
     private GraphLense fixupTypeReferences(GraphLense graphLense) {
       // Globally substitute merged class types in protos and holders.
       for (DexProgramClass clazz : appInfo.classes()) {
-        clazz.directMethods = substituteTypesIn(clazz.directMethods);
-        clazz.virtualMethods = substituteTypesIn(clazz.virtualMethods);
-        clazz.virtualMethods = removeDupes(clazz.virtualMethods);
-        clazz.staticFields = substituteTypesIn(clazz.staticFields);
-        clazz.instanceFields = substituteTypesIn(clazz.instanceFields);
+        clazz.setDirectMethods(substituteTypesIn(clazz.directMethods()));
+        clazz.setVirtualMethods(substituteTypesIn(clazz.virtualMethods()));
+        clazz.setVirtualMethods(removeDupes(clazz.virtualMethods()));
+        clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
+        clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
       }
       // Record type renamings so instanceof and checkcast checks are also fixed.
       for (DexType type : mergedClasses.keySet()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 58646a8..64294bd 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -84,10 +84,10 @@
         // The class is used and must be kept. Remove the unused fields and methods from
         // the class.
         usagePrinter.visiting(clazz);
-        clazz.directMethods = reachableMethods(clazz.directMethods(), clazz);
-        clazz.virtualMethods = reachableMethods(clazz.virtualMethods(), clazz);
-        clazz.instanceFields = reachableFields(clazz.instanceFields());
-        clazz.staticFields = reachableFields(clazz.staticFields());
+        clazz.setDirectMethods(reachableMethods(clazz.directMethods(), clazz));
+        clazz.setVirtualMethods(reachableMethods(clazz.virtualMethods(), clazz));
+        clazz.setInstanceFields(reachableFields(clazz.instanceFields()));
+        clazz.setStaticFields(reachableFields(clazz.staticFields()));
         usagePrinter.visited();
       }
     }
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index ea81b10..8712b7a 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -109,7 +109,7 @@
     return join(collection, separator, BraceType.NONE);
   }
 
-  public static <T> String join(String separator, T... strings) {
+  public static String join(String separator, String... strings) {
     return join(Arrays.asList(strings), separator, BraceType.NONE);
   }
 
@@ -124,15 +124,15 @@
     return builder.toString();
   }
 
-  public static <T> String lines(T... lines) {
+  public static String lines(String... lines) {
     StringBuilder builder = new StringBuilder();
-    for (T line : lines) {
+    for (String line : lines) {
       builder.append(line).append(LINE_SEPARATOR);
     }
     return builder.toString();
   }
 
-  public static <T> String joinLines(T... lines) {
+  public static String joinLines(String... lines) {
     return join(LINE_SEPARATOR, lines);
   }
 
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 063d5fc..beba10e 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -4656,14 +4656,6 @@
           // 1) t04
           // java.lang.AssertionError
 
-          .put("lang.reflect.Field.getLjava_lang_Object.Field_get_A04", match(R8_AFTER_D8_COMPILER))
-          // 1) t02
-          // java.lang.AssertionError: expected:<9223372036854775807> but was:<72057594037927935>
-
-          .put("lang.reflect.Field.getLongLjava_lang_Object.Field_getLong_A04", match(R8_AFTER_D8_COMPILER))
-          // 1)
-          // java.lang.AssertionError: expected:<9223372036854775807> but was:<72057594037927935>
-
           .build(); // end of failuresToTriage
 
   public static final Multimap<String, TestCondition> flakyWithArt =
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index 6ee0b66..a71291d 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -205,7 +205,7 @@
       String pkg = "org.apache.harmony.jpda.tests.jdwp";
       command = Arrays.asList(
           ToolHelper.getJavaExecutable(),
-          "-cp", System.getProperty("java.class.path") + ":" + lib,
+          "-cp", System.getProperty("java.class.path") + File.pathSeparator + lib,
           run, pkg + "." + test);
     } else {
       command = Arrays.asList(
diff --git a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
index 79ef0b9..1d256e0 100644
--- a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
+++ b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
@@ -53,6 +53,9 @@
     );
 
     InternalOptions options = new InternalOptions();
+    // (b/64777953) Disable outliner optimization for this test because it increases time
+    // from 1 minute to 7 minutes.
+    options.outline.enabled = false;
     DexApplication originalApplication = buildApplication(smaliBuilder, options);
     DexApplication processedApplication = processApplication(originalApplication, options);
     String result = runArt(processedApplication, options);
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 4656cd6..c726aad 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -828,13 +828,13 @@
     DexInspector inspector = new DexInspector(processedApplication);
     ClassSubject clazz = inspector.clazz(options.outline.className);
     assertTrue(clazz.isPresent());
-    assertEquals(3, clazz.getDexClass().directMethods.length);
+    assertEquals(3, clazz.getDexClass().directMethods().length);
     // Collect the return types of the putlines for the body of method1 and method2.
     List<DexType> r = new ArrayList<>();
-    for (int i = 0; i < clazz.getDexClass().directMethods.length; i++) {
-      if (clazz.getDexClass().directMethods[i].getCode().asDexCode().instructions[0]
+    for (int i = 0; i < clazz.getDexClass().directMethods().length; i++) {
+      if (clazz.getDexClass().directMethods()[i].getCode().asDexCode().instructions[0]
           instanceof InvokeVirtual) {
-        r.add(clazz.getDexClass().directMethods[i].method.proto.returnType);
+        r.add(clazz.getDexClass().directMethods()[i].method.proto.returnType);
       }
     }
     assert r.size() == 2;
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 32f0bc7..9e5f63f 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -69,6 +69,7 @@
       tool_args = ['--dex', '--output=' + temp_dir, '--multi-dex',
           '--min-sdk-version=' + MIN_SDK_VERSION]
 
+    xmx = None
     if args.tool.startswith('goyt'):
       tool_file = GOYT_EXE
       tool_args = ['--num-threads=8'] + tool_args
@@ -76,12 +77,13 @@
         tool_args.append('--no-locals')
     elif args.tool == 'dx':
       tool_file = DX_JAR
+      xmx = '-Xmx1600m'
     else:
       tool_file = D8_JAR
       tool_args = ['--output', temp_dir, '--min-api', MIN_SDK_VERSION]
       if args.tool == 'd8-release':
         tool_args.append('--release')
-
+      xmx = '-Xmx600m'
 
     cmd = []
 
@@ -91,7 +93,8 @@
       cmd.extend(['tools/track_memory.sh', track_memory_file])
 
     if tool_file.endswith('.jar'):
-      cmd.extend(['java', '-jar'])
+      assert xmx is not None
+      cmd.extend(['java', xmx, '-jar'])
 
     cmd.extend([tool_file] + tool_args + [FRAMEWORK_JAR])