Merge "Do not shared too much constants in dominator block of usages"
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 85c430a..aa0e396 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -22,6 +22,7 @@
   private final OutputMode outputMode;
   private final CompilationMode mode;
   private final int minApiLevel;
+  private final boolean overwriteOutputs;
 
   BaseCommand(boolean printHelp, boolean printVersion) {
     this.printHelp = printHelp;
@@ -32,10 +33,11 @@
     this.outputMode = OutputMode.Indexed;
     this.mode = null;
     this.minApiLevel = 0;
+    this.overwriteOutputs = true;
   }
 
   BaseCommand(AndroidApp app, Path outputPath,
-      OutputMode outputMode, CompilationMode mode, int minApiLevel) {
+      OutputMode outputMode, CompilationMode mode, int minApiLevel, boolean overwriteOutputs) {
     assert app != null;
     assert mode != null;
     assert minApiLevel > 0;
@@ -44,6 +46,7 @@
     this.outputMode = outputMode;
     this.mode = mode;
     this.minApiLevel = minApiLevel;
+    this.overwriteOutputs = overwriteOutputs;
     // Print options are not set.
     printHelp = false;
     printVersion = false;
@@ -81,6 +84,10 @@
     return outputMode;
   }
 
+  public boolean isOverwriteOutputs() {
+    return overwriteOutputs;
+  }
+
   abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
 
     private boolean printHelp = false;
@@ -90,6 +97,7 @@
     private OutputMode outputMode = OutputMode.Indexed;
     private CompilationMode mode;
     private int minApiLevel = Constants.DEFAULT_ANDROID_API;
+    private boolean overwriteOutputs = true;
 
     protected Builder(CompilationMode mode) {
       this(AndroidApp.builder(), mode);
@@ -197,9 +205,9 @@
       return outputMode;
     }
 
-    /** Set an output path. Must be an existing directory or a non-existent zip file. */
-    public B setOutputPath(Path outputPath) throws CompilationException {
-      this.outputPath = FileUtils.validateOutputFile(outputPath);
+    /** Set an output path. Must be an existing directory or a zip file. */
+    public B setOutputPath(Path outputPath) {
+      this.outputPath = outputPath;
       return self();
     }
 
@@ -248,5 +256,17 @@
       this.printVersion = printVersion;
       return self();
     }
+
+    protected boolean isOverwriteOutputs() {
+      return overwriteOutputs;
+    }
+
+    protected void setOverwriteOutputs(boolean overwriteOutputs) {
+      this.overwriteOutputs = overwriteOutputs;
+    }
+
+    protected void validate() throws CompilationException {
+      FileUtils.validateOutputFile(outputPath, overwriteOutputs);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index fb19e9b..f15a124 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -162,7 +162,7 @@
       Timing timing = new Timing("DX timer");
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
       AppInfo appInfo = new AppInfo(app);
-      app = optimize(app, appInfo, options, executor);
+      app = optimize(app, appInfo, options, timing, executor);
 
       // If a method filter is present don't produce output since the application is likely partial.
       if (options.hasMethodsFilter()) {
@@ -191,11 +191,11 @@
 
   private static DexApplication optimize(
       DexApplication application, AppInfo appInfo, InternalOptions options,
-      ExecutorService executor)
+      Timing timing, ExecutorService executor)
       throws IOException, ExecutionException {
     final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
-    IRConverter converter = new IRConverter(application, appInfo, options, printer);
+    IRConverter converter = new IRConverter(timing, application, appInfo, options, printer);
     application = converter.convertToDex(executor);
 
     if (options.printCfg) {
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 62cb5a5..0ef9728 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -101,8 +101,10 @@
       if (isPrintHelp() || isPrintVersion()) {
         return new D8Command(isPrintHelp(), isPrintVersion());
       }
+
+      validate();
       return new D8Command(getAppBuilder().build(),
-          getOutputPath(), getOutputMode(), getMode(), getMinApiLevel());
+          getOutputPath(), getOutputMode(), getMode(), getMinApiLevel(), isOverwriteOutputs());
     }
   }
 
@@ -180,8 +182,8 @@
   }
 
   private D8Command(AndroidApp inputApp, Path outputPath,
-      OutputMode outputMode, CompilationMode mode, int minApiLevel) {
-    super(inputApp, outputPath, outputMode, mode, minApiLevel);
+      OutputMode outputMode, CompilationMode mode, int minApiLevel, boolean overwriteOutputs) {
+    super(inputApp, outputPath, outputMode, mode, minApiLevel, overwriteOutputs);
   }
 
   private D8Command(boolean printHelp, boolean printVersion) {
@@ -194,7 +196,7 @@
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
     internal.minApiLevel = getMinApiLevel();
-    internal.overwriteOutputs = true;
+    internal.overwriteOutputs = isOverwriteOutputs();
     // Assert and fixup defaults.
     assert !internal.skipMinification;
     internal.skipMinification = true;
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
index 501fecc..09eb21e 100644
--- a/src/main/java/com/android/tools/r8/DexSegments.java
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -41,12 +41,14 @@
         if (isPrintHelp()) {
           return new Command(isPrintHelp());
         }
+        validate();
         return new Command(
             getAppBuilder().build(),
             getOutputPath(),
             getOutputMode(),
             getMode(),
-            getMinApiLevel());
+            getMinApiLevel(),
+            isOverwriteOutputs());
       }
     }
 
@@ -89,8 +91,9 @@
         Path outputPath,
         OutputMode outputMode,
         CompilationMode mode,
-        int minApiLevel) {
-      super(inputApp, outputPath, outputMode, mode, minApiLevel);
+        int minApiLevel,
+        boolean isOverwrite) {
+      super(inputApp, outputPath, outputMode, mode, minApiLevel, isOverwrite);
     }
 
     private Command(boolean printHelp) {
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
index b183ecc..7d73895 100644
--- a/src/main/java/com/android/tools/r8/Disassemble.java
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -46,13 +46,15 @@
           return new DisassembleCommand(isPrintHelp(), isPrintVersion());
         }
 
+        validate();
         return new DisassembleCommand(
             getAppBuilder().build(),
             getOutputPath(),
             getOutputMode(),
             getMode(),
             getMinApiLevel(),
-            useSmali);
+            useSmali,
+            isOverwriteOutputs());
       }
     }
 
@@ -108,8 +110,9 @@
         OutputMode outputMode,
         CompilationMode mode,
         int minApiLevel,
-        boolean useSmali) {
-      super(inputApp, outputPath, outputMode, mode, minApiLevel);
+        boolean useSmali,
+        boolean isOverwrite) {
+      super(inputApp, outputPath, outputMode, mode, minApiLevel, isOverwrite);
       //assert getOutputMode() == OutputMode.Indexed : "Only regular mode is supported in R8";
       this.useSmali = useSmali;
     }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 7d01462..7fd1c6f 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -136,6 +136,7 @@
         return new R8Command(isPrintHelp(), isPrintVersion());
       }
 
+      validate();
       DexItemFactory factory = new DexItemFactory();
       ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
       if (this.mainDexRules.isEmpty()) {
@@ -178,7 +179,8 @@
           getMinApiLevel(),
           useTreeShaking,
           useMinification,
-          ignoreMissingClasses);
+          ignoreMissingClasses,
+          isOverwriteOutputs());
     }
   }
 
@@ -321,8 +323,9 @@
       int minApiLevel,
       boolean useTreeShaking,
       boolean useMinification,
-      boolean ignoreMissingClasses) {
-    super(inputApp, outputPath, outputMode, mode, minApiLevel);
+      boolean ignoreMissingClasses,
+      boolean overwriteOutputs) {
+    super(inputApp, outputPath, outputMode, mode, minApiLevel, overwriteOutputs);
     assert proguardConfiguration != null;
     assert mainDexKeepRules != null;
     assert getOutputMode() == OutputMode.Indexed : "Only regular mode is supported in R8";
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 f24f4de..6d6f858 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -176,7 +176,7 @@
       return this;
     }
     // At least one method needs a jumbo string.
-    IRConverter converter = new IRConverter(application, appInfo, options, null, false);
+    IRConverter converter = new IRConverter(application, appInfo, options, false);
     for (DexProgramClass clazz : classes) {
       rewriteCodeWithJumboStrings(converter, clazz.directMethods());
       rewriteCodeWithJumboStrings(converter, clazz.virtualMethods());
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 9e3e118..503bc89 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -138,16 +138,15 @@
    */
   public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method) {
     assert method != null;
+    DexClass holder = definitionFor(method.holder);
+    if ((holder == null) || holder.isLibraryClass()) {
+      return null;
+    }
     if (method.isSingleVirtualMethodCached()) {
       return method.getSingleVirtualMethodCache();
     }
     DexEncodedMethod result = null;
     // First add the target for receiver type method.type.
-    DexClass root = definitionFor(method.holder);
-    if (root == null) {
-      // type specified in method does not have a materialized class.
-      return null;
-    }
     DexEncodedMethod topMethod = lookupVirtualTarget(method.holder, method);
     // The top method might be absent if this is an abstract class.
     if (topMethod != null) {
@@ -203,6 +202,10 @@
 
   public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method) {
     assert method != null;
+    DexClass holder = definitionFor(method.holder);
+    if ((holder == null) || holder.isLibraryClass()) {
+      return null;
+    }
     DexEncodedMethod result = null;
     Set<DexType> set = subtypes(method.holder);
     if (set != null) {
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 9ea50d2..984215b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -100,6 +100,7 @@
   public DexString thisName = createString("this");
 
   private DexString charArrayDescriptor = createString("[C");
+  private DexType charArrayType = createType(charArrayDescriptor);
   public DexString throwableArrayDescriptor = createString("[Ljava/lang/Throwable;");
 
   public DexType booleanType = createType(booleanDescriptor);
@@ -128,10 +129,11 @@
   public DexType annotationType = createType(annotationDescriptor);
   public DexType throwableType = createType(throwableDescriptor);
 
-  public StringBuildingMethods stringBuilderMethods =
-      new StringBuildingMethods(createString("Ljava/lang/StringBuilder;"));
-  public StringBuildingMethods stringBufferMethods =
-      new StringBuildingMethods(createString("Ljava/lang/StringBuffer;"));
+  public DexType stringBuilderType = createType(createString("Ljava/lang/StringBuilder;"));
+  public DexType stringBufferType = createType(createString("Ljava/lang/StringBuffer;"));
+
+  public StringBuildingMethods stringBuilderMethods = new StringBuildingMethods(stringBuilderType);
+  public StringBuildingMethods stringBufferMethods = new StringBuildingMethods(stringBufferType);
   public ObjectsMethods objectsMethods = new ObjectsMethods();
   public ObjectMethods objectMethods = new ObjectMethods();
   public LongMethods longMethods = new LongMethods();
@@ -213,34 +215,42 @@
     public DexMethod appendStringBuffer;
     public DexMethod toString;
 
-    private StringBuildingMethods(DexString receiver) {
-      DexString sbuf = createString("Ljava/lang/StringBuffer;");
-      DexString charSequence = createString("Ljava/lang/CharSequence;");
+    private StringBuildingMethods(DexType receiver) {
+      DexType sbufType = createType(createString("Ljava/lang/StringBuffer;"));
+      DexType charSequenceType = createType(createString("Ljava/lang/CharSequence;"));
       DexString append = createString("append");
       DexString toStringMethodName = createString("toString");
 
-      appendBoolean = createMethod(receiver, append, receiver, new DexString[]{booleanDescriptor});
-      appendChar = createMethod(receiver, append, receiver, new DexString[]{charDescriptor});
-      appendCharArray = createMethod(receiver, append, receiver, new DexString[]{
-          charArrayDescriptor});
-      appendSubCharArray = createMethod(receiver, append, receiver,
-          new DexString[]{charArrayDescriptor, intDescriptor, intDescriptor});
-      appendCharSequence = createMethod(receiver, append, receiver,
-          new DexString[]{charSequence});
-      appendSubCharSequence = createMethod(receiver, append, receiver,
-          new DexString[]{charSequence, intDescriptor, intDescriptor});
-      appendInt = createMethod(receiver, append, receiver, new DexString[]{intDescriptor});
-      appendDouble = createMethod(receiver, append, receiver, new DexString[]{doubleDescriptor});
-      appendFloat = createMethod(receiver, append, receiver, new DexString[]{floatDescriptor});
-      appendLong = createMethod(receiver, append, receiver, new DexString[]{longDescriptor});
-      appendObject = createMethod(receiver, append, receiver, new DexString[]{objectDescriptor});
-      appendString = createMethod(receiver, append, receiver, new DexString[]{stringDescriptor});
-      appendStringBuffer = createMethod(receiver, append, receiver, new DexString[]{sbuf});
-      toString = createMethod(receiver, toStringMethodName, stringDescriptor,
-          DexString.EMPTY_ARRAY);
+
+      appendBoolean =
+          createMethod(receiver, createProto(receiver, new DexType[]{booleanType}), append);
+      appendChar = createMethod(receiver, createProto(receiver, new DexType[]{charType}), append);
+      appendCharArray =
+          createMethod(receiver, createProto(receiver, new DexType[]{charArrayType}), append);
+      appendSubCharArray =
+          createMethod(receiver,
+              createProto(receiver, new DexType[]{charArrayType, intType, intType}), append);
+      appendCharSequence =
+          createMethod(receiver, createProto(receiver, new DexType[]{charSequenceType}), append);
+      appendSubCharSequence =
+          createMethod(receiver,
+              createProto(receiver, new DexType[]{charSequenceType, intType, intType}), append);
+      appendInt = createMethod(receiver, createProto(receiver, new DexType[]{intType}), append);
+      appendDouble =
+          createMethod(receiver, createProto(receiver, new DexType[]{doubleType}), append);
+      appendFloat = createMethod(receiver, createProto(receiver, new DexType[]{floatType}), append);
+      appendLong = createMethod(receiver, createProto(receiver, new DexType[]{longType}), append);
+      appendObject =
+          createMethod(receiver, createProto(receiver, new DexType[]{objectType}), append);
+      appendString =
+          createMethod(receiver, createProto(receiver, new DexType[]{stringType}), append);
+      appendStringBuffer =
+          createMethod(receiver, createProto(receiver, new DexType[]{sbufType}), append);
+      toString =
+          createMethod(receiver, createProto(stringType, DexType.EMPTY_ARRAY), toStringMethodName);
     }
 
-    public void forEeachAppendMethod(Consumer<DexMethod> consumer) {
+    public void forEachAppendMethod(Consumer<DexMethod> consumer) {
       consumer.accept(appendBoolean);
       consumer.accept(appendChar);
       consumer.accept(appendCharArray);
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 234773f..4d3d8cb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -19,6 +19,8 @@
 
 public class DexType extends IndexedDexItem implements PresortedComparable<DexType> {
 
+  public static final DexType[] EMPTY_ARRAY = new DexType[]{};
+
   private final static int ROOT_LEVEL = 0;
   private final static int UNKNOWN_LEVEL = -1;
   private final static int INTERFACE_LEVEL = -2;
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 aa3dc10..1586496 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,9 +38,11 @@
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -69,35 +71,82 @@
 
   private DexString highestSortingString;
 
-  public IRConverter(
+  private IRConverter(
+      Timing timing,
       DexApplication application,
       AppInfo appInfo,
+      GraphLense graphLense,
       InternalOptions options,
       CfgPrinter printer,
-      boolean enableDesugaring) {
-    this.timing = new Timing("Testing");
+      boolean enableDesugaring,
+      boolean enableWholeProgramOptimizations) {
+    assert application != null;
+    assert appInfo != null;
+    assert options != null;
+    this.timing = timing != null ? timing : new Timing("internal");
     this.application = application;
     this.appInfo = appInfo;
+    this.graphLense = graphLense != null ? graphLense : GraphLense.getIdentityLense();
     this.options = options;
     this.printer = printer;
-    this.graphLense = GraphLense.getIdentityLense();
-    this.inliner = null;
-    this.outliner = null;
-    this.codeRewriter = new CodeRewriter(appInfo);
-    this.memberValuePropagation = null;
+    Set<DexType> libraryClassesWithOptimizationInfo = markLibraryMethodsReturningReceiver();
+    this.codeRewriter = new CodeRewriter(appInfo, libraryClassesWithOptimizationInfo);
     this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null;
     this.interfaceMethodRewriter =
         (enableDesugaring && enableInterfaceMethodDesugaring())
             ? new InterfaceMethodRewriter(this) : null;
-    lensCodeRewriter = null;
-    markLibraryMethodsReturningReceiver();
+    if (enableWholeProgramOptimizations) {
+      assert appInfo.withSubtyping() != null;
+      this.inliner = new Inliner(appInfo.withSubtyping(), graphLense, options);
+      this.outliner = new Outliner(appInfo, options);
+      this.memberValuePropagation = new MemberValuePropagation(appInfo);
+      this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping());
+    } else {
+      this.inliner = null;
+      this.outliner = null;
+      this.memberValuePropagation = null;
+      this.lensCodeRewriter = null;
+    }
   }
 
+  /**
+   * Create an IR converter for processing methods with full program optimization disabled.
+   */
   public IRConverter(
-      DexApplication application, AppInfo appInfo, InternalOptions options, CfgPrinter printer) {
-    this(application, appInfo, options, printer, true);
+      DexApplication application,
+      AppInfo appInfo,
+      InternalOptions options) {
+    this(null, application, appInfo, null, options, null, true, false);
   }
 
+  /**
+   * Create an IR converter for processing methods without full program optimization enabled.
+   *
+   * The argument <code>enableDesugaring</code> if desugaing is enabled.
+   */
+  public IRConverter(
+      DexApplication application,
+      AppInfo appInfo,
+      InternalOptions options,
+      boolean enableDesugaring) {
+    this(null, application, appInfo, null, options, null, enableDesugaring, false);
+  }
+
+  /**
+   * Create an IR converter for processing methods with full program optimization disabled.
+   */
+  public IRConverter(
+      Timing timing,
+      DexApplication application,
+      AppInfo appInfo,
+      InternalOptions options,
+      CfgPrinter printer) {
+    this(timing, application, appInfo, null, options, printer, true, false);
+  }
+
+  /**
+   * Create an IR converter for processing methods with full program optimization enabled.
+   */
   public IRConverter(
       Timing timing,
       DexApplication application,
@@ -105,21 +154,7 @@
       InternalOptions options,
       CfgPrinter printer,
       GraphLense graphLense) {
-    this.timing = timing;
-    this.application = application;
-    this.appInfo = appInfo;
-    this.options = options;
-    this.printer = printer;
-    this.graphLense = graphLense;
-    this.codeRewriter = new CodeRewriter(appInfo);
-    this.outliner = new Outliner(appInfo, options);
-    this.memberValuePropagation = new MemberValuePropagation(appInfo);
-    this.inliner = new Inliner(appInfo, graphLense, options);
-    this.lambdaRewriter = new LambdaRewriter(this);
-    this.interfaceMethodRewriter = enableInterfaceMethodDesugaring()
-        ? new InterfaceMethodRewriter(this) : null;
-    lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo);
-    markLibraryMethodsReturningReceiver();
+    this(timing, application, appInfo, graphLense, options, printer, true, true);
   }
 
   private boolean enableInterfaceMethodDesugaring() {
@@ -142,10 +177,11 @@
     throw new Unreachable();
   }
 
-  private void markLibraryMethodsReturningReceiver() {
+  private Set<DexType> markLibraryMethodsReturningReceiver() {
     DexItemFactory dexItemFactory = appInfo.dexItemFactory;
-    dexItemFactory.stringBuilderMethods.forEeachAppendMethod(this::markReturnsReceiver);
-    dexItemFactory.stringBufferMethods.forEeachAppendMethod(this::markReturnsReceiver);
+    dexItemFactory.stringBuilderMethods.forEachAppendMethod(this::markReturnsReceiver);
+    dexItemFactory.stringBufferMethods.forEachAppendMethod(this::markReturnsReceiver);
+    return ImmutableSet.of(dexItemFactory.stringBuilderType, dexItemFactory.stringBufferType);
   }
 
   private void markReturnsReceiver(DexMethod method) {
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 2d06dfc..764beb5 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
@@ -87,10 +87,12 @@
 
   private final AppInfo appInfo;
   private final DexItemFactory dexItemFactory;
+  private final Set<DexType> libraryClassesWithOptimizationInfo;
 
-  public CodeRewriter(AppInfo appInfo) {
+  public CodeRewriter(AppInfo appInfo, Set<DexType> libraryClassesWithOptimizationInfo) {
     this.appInfo = appInfo;
     this.dexItemFactory = appInfo.dexItemFactory;
+    this.libraryClassesWithOptimizationInfo = libraryClassesWithOptimizationInfo;
   }
 
   /**
@@ -694,6 +696,12 @@
         InvokeMethod invoke = current.asInvokeMethod();
         if (invoke.outValue() != 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.
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 631f8b2..eb0aa77 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -1808,7 +1808,7 @@
               addLiveRange(use, block, number);
             }
             LiveIntervals useIntervals = use.getLiveIntervals();
-            useIntervals.addUse(new LiveIntervalsUse(number, Constants.U16BIT_MAX));
+            useIntervals.addUse(new LiveIntervalsUse(number, Constants.U16BIT_MAX, true));
           }
           Value use = instruction.getPreviousLocalValue();
           if (use != null) {
@@ -1818,7 +1818,7 @@
               addLiveRange(use, block, number);
             }
             LiveIntervals useIntervals = use.getLiveIntervals();
-            useIntervals.addUse(new LiveIntervalsUse(number, Constants.U16BIT_MAX));
+            useIntervals.addUse(new LiveIntervalsUse(number, Constants.U16BIT_MAX, true));
           }
         }
       }
@@ -1850,27 +1850,14 @@
       Value previous = null;
       for (int i = 0; i < arguments.size(); i++) {
         Value argument = arguments.get(i);
-        Value newArgument = argument;
-        // In debug mode, we have debug instructions that are also moves. Do not generate another
-        // move if there already is a move instruction that we can use. We generate moves if:
-        //
-        // 1. the argument is not defined by a move,
-        //
-        // 2. the argument is already linked or would cause a cycle if linked, or
-        //
-        // 3. the argument has a register constraint (the argument moves are there to make the
-        //    input value to a ranged invoke unconstrained.)
-        if (argument.definition == null ||
-            !argument.definition.isMove() ||
-            argument.isLinked() ||
-            argument == previous ||
-            argument.hasRegisterConstraint()) {
-          newArgument = createValue(argument.outType());
-          Move move = new Move(newArgument, argument);
-          move.setBlock(invoke.getBlock());
-          replaceArgument(invoke, i, newArgument);
-          insertAt.add(move);
-        }
+        // TODO(ager): Conditionally create a new argument if it is not already a move.
+        // Reverted optimization from CL: https://r8-review.googlesource.com/c/1985/
+        // Not considering debug-uses causes a unlinked/non-consecutive register in some cases.
+        Value newArgument = createValue(argument.outType());
+        Move move = new Move(newArgument, argument);
+        move.setBlock(invoke.getBlock());
+        replaceArgument(invoke, i, newArgument);
+        insertAt.add(move);
         if (previous != null) {
           previous.linkTo(newArgument);
         }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index 06b33f4..9c28724 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -319,7 +319,7 @@
 
   public int firstUseAfter(int unhandledStart) {
     for (LiveIntervalsUse use : uses) {
-      if (use.getPosition() >= unhandledStart) {
+      if (use.getPosition() >= unhandledStart && !use.isDebugUse()) {
         return use.getPosition();
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
index a602134..edf876d 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
@@ -6,12 +6,18 @@
 import static com.android.tools.r8.dex.Constants.U16BIT_MAX;
 
 public class LiveIntervalsUse implements Comparable<LiveIntervalsUse> {
-  private int position;
-  private int limit;
+  private final int position;
+  private final int limit;
+  private final boolean debugUse;
 
   public LiveIntervalsUse(int position, int limit) {
+    this(position, limit, false);
+  }
+
+  public LiveIntervalsUse(int position, int limit, boolean debugUse) {
     this.position = position;
     this.limit = limit;
+    this.debugUse = debugUse;
   }
 
   public int getPosition() {
@@ -47,4 +53,8 @@
   public boolean hasConstraint() {
     return limit < U16BIT_MAX;
   }
+
+  public boolean isDebugUse() {
+    return debugUse;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index 3f69c2e..6a70845 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -73,10 +73,10 @@
     Files.write(file, Arrays.asList(lines));
   }
 
-  public static Path validateOutputFile(Path path) throws CompilationException {
+  public static Path validateOutputFile(Path path, boolean allowOverwrite) throws CompilationException {
     if (path != null) {
       if (isZipFile(path)) {
-        if (Files.exists(path)) {
+        if ((!allowOverwrite) && Files.exists(path)) {
           throw new CompilationException("Cannot write to existing output file: " + path);
         }
       } else if (!(Files.exists(path) && Files.isDirectory(path))) {
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index 77eb5eb0..66853cb 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -36,8 +36,7 @@
     DexApplication application =
         new ApplicationReader(input, new InternalOptions(), timing).read(executorService);
     IRConverter converter =
-        new IRConverter(
-            application, new AppInfoWithSubtyping(application), new InternalOptions(), null);
+        new IRConverter(application, new AppInfoWithSubtyping(application), new InternalOptions());
     converter.optimize();
     DexProgramClass clazz = application.classes().iterator().next();
     assertEquals(4, clazz.directMethods().length);
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index e74eb2c..e039e9e 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -725,4 +725,10 @@
   public static AndroidApp getApp(BaseCommand command) {
     return command.getInputApp();
   }
+
+  public static <T extends BaseCommand, U extends BaseCommand.Builder<T, U>>
+      U setOverwrite(U commandBuilder, boolean overwrite) {
+    commandBuilder.setOverwriteOutputs(overwrite);
+    return commandBuilder;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index d5e66b8..4b2ee49 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -48,6 +48,11 @@
         compiler, mode, referenceApk, pgMap, pgConf, Arrays.asList(inputs));
   }
 
+  public AndroidApp runAndCheckVerification(D8Command command, String referenceApk)
+      throws IOException, ExecutionException {
+    return checkVerification(ToolHelper.runD8(command), referenceApk);
+  }
+
   public AndroidApp runAndCheckVerification(
       CompilerUnderTest compiler,
       CompilationMode mode,
@@ -83,6 +88,11 @@
                   .setMinApiLevel(Constants.ANDROID_L_API)
                   .build());
     }
+    return checkVerification(outputApp, referenceApk);
+  }
+
+  public AndroidApp checkVerification(AndroidApp outputApp, String referenceApk)
+      throws IOException, ExecutionException {
     Path out = temp.getRoot().toPath().resolve("all.zip");
     Path oatFile = temp.getRoot().toPath().resolve("all.oat");
     outputApp.writeToZip(out, OutputMode.Indexed);
diff --git a/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java
new file mode 100644
index 0000000..a4f33d5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8FrameworkVerificationTest.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8FrameworkVerificationTest extends CompilationTestBase {
+  private static final int MIN_SDK = 24;
+  private static final String JAR = "third_party/framework/framework_160115954.jar";
+
+  @Test
+  public void verifyDebugBuild()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(
+        D8Command.builder()
+            .addProgramFiles(Paths.get(JAR))
+            .setMode(CompilationMode.DEBUG)
+            .setMinApiLevel(MIN_SDK)
+            .build(),
+        JAR);
+  }
+
+  @Test
+  public void verifyReleaseBuild()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(
+        D8Command.builder()
+            .addProgramFiles(Paths.get(JAR))
+            .setMode(CompilationMode.RELEASE)
+            .setMinApiLevel(MIN_SDK)
+            .build(),
+        JAR);
+  }
+}
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 8890df8..6ee0b66 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -72,6 +72,16 @@
     return dexVm == DexVm.ART_DEFAULT;
   }
 
+  static final Map<String, TestPredicate> FLAKY_TESTS =
+      ImmutableMap.<String, TestPredicate>builder()
+          // Build bot is failing with ART segmentation faults on the following tests. b/63317743
+          .put("StackFrame.GetValues002Test", RunJdwpTests::isAndroidMOrAbove)
+          .put("ObjectReference.ReferringObjectsTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("VirtualMachine.InstanceCountsTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("ReferenceType.InstancesTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("EventModifiers.InstanceOnlyModifierTest", RunJdwpTests::isAndroidMOrAbove)
+          .build();
+
   static final Map<String, TestPredicate> FAILING_TESTS =
       ImmutableMap.<String, TestPredicate>builder()
           .put("ArrayReference.SetValues003Test", RunJdwpTests::isAndroidNOrAbove)
@@ -175,6 +185,8 @@
     if (!RUN_ALL_TESTS) {
       Assume.assumeTrue("Skipping non-smoke test " + test, SMOKE_TESTS.contains(test));
     }
+    Assume.assumeTrue("Skipping flaky test " + test,
+        !FLAKY_TESTS.containsKey(test) || FLAKY_TESTS.get(test).test(getDexVm(), tool));
     if (tool != Tool.JAVAC) {
       // Can we run the test on the current ART runtime ?
       Assume.assumeTrue("Skipping test " + test + " because ART is not supported",
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 6b377f4..8ce4f40 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -343,7 +343,7 @@
 
     public String run() {
       AppInfo appInfo = new AppInfo(application);
-      IRConverter converter = new IRConverter(application, appInfo, options, null);
+      IRConverter converter = new IRConverter(application, appInfo, options);
       converter.replaceCodeForTesting(method, code);
       return runArt(application, options);
     }
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
index be72756..b086910 100644
--- a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -58,7 +58,7 @@
   @Test
   public void defaultOutIsCwd() throws IOException, InterruptedException {
     Path working = temp.getRoot().toPath();
-    Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+    Path input = Paths.get(EXAMPLES_BUILD_DIR + "/arithmetic.jar").toAbsolutePath();
     Path output = working.resolve("classes.dex");
     assertFalse(Files.exists(output));
     assertEquals(0, ToolHelper.forkD8(working, input.toString()).exitCode);
@@ -91,9 +91,15 @@
   }
 
   @Test
-  public void existingOutputZip() throws Throwable {
+  public void existingOutputZipNoOverwrite() throws Throwable {
     thrown.expect(CompilationException.class);
     Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+    ToolHelper.setOverwrite(D8Command.builder().setOutputPath(existingZip), false).build();
+  }
+
+  @Test
+  public void existingOutputZip() throws Throwable {
+    Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
     D8Command.builder().setOutputPath(existingZip).build();
   }
 
@@ -113,7 +119,6 @@
 
   @Test
   public void existingOutputZipParse() throws Throwable {
-    thrown.expect(CompilationException.class);
     Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
     parse("--output", existingZip.toString());
   }
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
index 935275a..15df84f 100644
--- a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -98,12 +98,18 @@
 
   @Test
   public void existingOutputZip() throws Throwable {
-    thrown.expect(CompilationException.class);
     Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
     R8Command.builder().setOutputPath(existingZip).build();
   }
 
   @Test
+  public void existingOutputZipNoOverwrite() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+    ToolHelper.setOverwrite(R8Command.builder().setOutputPath(existingZip), false).build();
+  }
+
+  @Test
   public void invalidOutputFileType() throws Throwable {
     thrown.expect(CompilationException.class);
     Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
@@ -119,7 +125,6 @@
 
   @Test
   public void existingOutputZipParse() throws Throwable {
-    thrown.expect(CompilationException.class);
     Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
     parse("--output", existingZip.toString());
   }
diff --git a/tools/run_proguard_dx_on_gmscore.py b/tools/run_proguard_dx_on_gmscore.py
index a67868b..ca0b5e9 100755
--- a/tools/run_proguard_dx_on_gmscore.py
+++ b/tools/run_proguard_dx_on_gmscore.py
@@ -3,7 +3,7 @@
 # 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.
 
-# Run ProGuard and the DX tool on GmsCore V10.
+# Run ProGuard and the DX or CompatDX (= D8) tool on GmsCore V10.
 
 from __future__ import print_function
 from os import makedirs
@@ -20,13 +20,14 @@
 import utils
 
 BLAZE_BUILD_DIR = join(gmscore_data.V10_BASE,
-    'blaze-out/intel-linux-android-4.8-libcxx-x86-opt/bin/java/com/google/'
-    'android/gmscore/integ')
+    'blaze-out', 'intel-linux-android-4.8-libcxx-x86-opt', 'bin', 'java',
+    'com', 'google', 'android', 'gmscore', 'integ')
 PROGUARDED_OUTPUT = join(BLAZE_BUILD_DIR,
     'GmsCore_prod_alldpi_release_all_locales_proguard.jar')
 GMSCORE_SEEDS_FILE = join(BLAZE_BUILD_DIR,
     'GmsCore_prod_alldpi_release_all_locales_proguard.seeds')
-DX_EXECUTABLE = join(utils.REPO_ROOT, 'tools/linux/dx/bin/dx')
+DX_JAR = join(utils.REPO_ROOT, 'tools', 'linux', 'dx', 'framework', 'dx.jar')
+COMPATDX_JAR = join(utils.REPO_ROOT, 'build', 'libs', 'compatdx.jar')
 
 def parse_arguments():
   parser = argparse.ArgumentParser(
@@ -39,6 +40,10 @@
       help = 'Prints the line \'<BENCHMARKNAME>(RunTimeRaw): <elapsed>' +
              ' ms\' at the end where <elapsed> is the elapsed time in' +
              ' milliseconds.')
+  parser.add_argument('--compatdx',
+      help = 'Use CompatDx (D8) instead of DX.',
+      default = False,
+      action = 'store_true')
   return parser.parse_args()
 
 def Main():
@@ -70,8 +75,15 @@
   proguard.run(args)
 
   # run dex on the result
-  check_call([DX_EXECUTABLE, '-JXmx5256M', '--multi-dex', '--output=' + outdir,
-      '--dex', PROGUARDED_OUTPUT])
+  if options.compatdx:
+    jar = COMPATDX_JAR
+  else:
+    jar = DX_JAR
+
+  cmd = ['java', '-jar', jar, '--min-sdk-version=26', '--multi-dex',
+      '--output=' + outdir, '--dex', PROGUARDED_OUTPUT];
+  utils.PrintCmd(cmd);
+  check_call(cmd)
 
   if options.print_runtimeraw:
     print('{}(RunTimeRaw): {} ms'