Merge "Util to find all interfaces implemented in the current type."
diff --git a/build.gradle b/build.gradle
index cd831cc..f379bbf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -111,7 +111,12 @@
     }
     apiUsageSample {
         java {
-            srcDirs = ['src/test/apiUsageSample']
+            srcDirs = ['src/test/apiUsageSample', 'src/main/java']
+            include 'com/android/tools/apiusagesample/*.java'
+            include 'com/android/tools/r8/BaseCompilerCommandParser.java'
+            include 'com/android/tools/r8/D8CommandParser.java'
+            include 'com/android/tools/r8/R8CommandParser.java'
+            include 'com/android/tools/r8/utils/FlagFile.java'
         }
     }
     debugTestResources {
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index e17acd0..b3d59fb 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -35,6 +35,7 @@
  * <p>The descriptor index is built eagerly upon creating the provider and subsequent requests for
  * resources in the descriptor set will then force the read of zip entry contents.
  */
+@Keep
 public class ArchiveClassFileProvider implements ClassFileResourceProvider, Closeable {
   private final Origin origin;
   private final ZipFile zipFile;
diff --git a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
index b3a4bfb..baac9bc 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
@@ -29,8 +29,10 @@
 import java.util.zip.ZipFile;
 
 /** Provider for archives of program resources. */
+@KeepForSubclassing
 public class ArchiveProgramResourceProvider implements ProgramResourceProvider {
 
+  @KeepForSubclassing
   public interface ZipFileSupplier {
     ZipFile open() throws IOException;
   }
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index fbc6ca3..8828248 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -28,6 +28,7 @@
  * <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
  * R8Command.Builder}.
  */
+@Keep
 public abstract class BaseCommand {
 
   private final boolean printHelp;
@@ -101,6 +102,7 @@
    * @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
    *     D8Command.Builder}.
    */
+  @Keep
   public abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
 
     private final Reporter reporter;
@@ -304,6 +306,20 @@
       return self();
     }
 
+    /** Signal an error. */
+    public void error(Diagnostic diagnostic) {
+      reporter.error(diagnostic);
+    }
+
+    /**
+     * Signal an error and throw {@link AbortException}.
+     *
+     * @throws AbortException always.
+     */
+    public RuntimeException fatalError(Diagnostic diagnostic) {
+      return reporter.fatalError(diagnostic);
+    }
+
     // Internal helper for compat tools to make them ignore DEX code in input archives.
     void setIgnoreDexInArchive(boolean value) {
       guard(() -> app.setIgnoreDexInArchive(value));
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index e393f32..5980a0c 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -4,13 +4,11 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
@@ -23,6 +21,7 @@
  * <p>For concrete builders, see for example {@link D8Command.Builder} and {@link
  * R8Command.Builder}.
  */
+@Keep
 public abstract class BaseCompilerCommand extends BaseCommand {
 
   private final CompilationMode mode;
@@ -37,7 +36,7 @@
     programConsumer = null;
     mode = null;
     minApiLevel = 0;
-    reporter = new Reporter(new DefaultDiagnosticsHandler());
+    reporter = new Reporter(new DefaultDiagnosticsHandler(), this);
     enableDesugaring = true;
     optimizeMultidexForLinearAlloc = false;
   }
@@ -106,6 +105,7 @@
    * @param <B> Concrete builder extending this base, e.g., {@link R8Command.Builder} or {@link
    *     D8Command.Builder}.
    */
+  @Keep
   public abstract static class Builder<C extends BaseCompilerCommand, B extends Builder<C, B>>
       extends BaseCommand.Builder<C, B> {
 
@@ -341,31 +341,4 @@
     }
   }
 
-  static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
-  boolean parseMinApi(
-      BaseCompilerCommand.Builder<C, B> builder,
-      String minApiString,
-      boolean hasDefinedApiLevel,
-      Origin origin) {
-    if (hasDefinedApiLevel) {
-      builder.getReporter().error(new StringDiagnostic(
-          "Cannot set multiple --min-api options", origin));
-      return false;
-    }
-    int minApi;
-    try {
-      minApi = Integer.valueOf(minApiString);
-    } catch (NumberFormatException e) {
-      builder.getReporter().error(new StringDiagnostic(
-          "Invalid argument to --min-api: " + minApiString, origin));
-      return false;
-    }
-    if (minApi < 1) {
-      builder.getReporter().error(new StringDiagnostic(
-          "Invalid argument to --min-api: " + minApiString, origin));
-      return false;
-    }
-    builder.setMinApiLevel(minApi);
-    return true;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
new file mode 100644
index 0000000..054db58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringDiagnostic;
+
+public class BaseCompilerCommandParser {
+
+  static void parseMinApi(BaseCompilerCommand.Builder builder, String minApiString, Origin origin) {
+    int minApi;
+    try {
+      minApi = Integer.valueOf(minApiString);
+    } catch (NumberFormatException e) {
+      builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+      return;
+    }
+    if (minApi < 1) {
+      builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+      return;
+    }
+    builder.setMinApiLevel(minApi);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 6c9a286..3bab0ab 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -26,6 +26,7 @@
  *
  * <p>This consumer can only be provided to R8.
  */
+@KeepForSubclassing
 public interface ClassFileConsumer extends ProgramConsumer {
 
   /**
@@ -49,6 +50,7 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
+  @Keep
   class ForwardingConsumer implements ClassFileConsumer {
 
     private static final ClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -80,6 +82,7 @@
   }
 
   /** Consumer to write program resources to an output. */
+  @Keep
   class ArchiveConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
@@ -163,6 +166,7 @@
   }
 
   /** Directory consumer to write program resources to a directory. */
+  @Keep
   class DirectoryConsumer extends ForwardingConsumer implements InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
     protected final boolean consumeDataResouces;
diff --git a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
index 4c71d88..db0a9f8 100644
--- a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
@@ -12,6 +12,7 @@
  * only created on-demand when they are needed by the compiler. If never needed, the resource will
  * never be loaded.
  */
+@KeepForSubclassing
 public interface ClassFileResourceProvider {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java
index 627f417..f540ed0 100644
--- a/src/main/java/com/android/tools/r8/CompatDxHelper.java
+++ b/src/main/java/com/android/tools/r8/CompatDxHelper.java
@@ -9,8 +9,7 @@
 import java.io.IOException;
 
 public class CompatDxHelper {
-  public static void run(D8Command command, Boolean minimalMainDex)
-      throws IOException, CompilationException {
+  public static void run(D8Command command, Boolean minimalMainDex) throws IOException {
     AndroidApp app = command.getInputApp();
     InternalOptions options = command.getInternalOptions();
     // DX does not desugar.
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index a6923ba..89a33c1 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -6,6 +6,8 @@
 
 import java.nio.file.Path;
 
+// This class is used by the Android Studio Gradle plugin and is thus part of the R8 API.
+@Keep
 public class CompatProguardCommandBuilder extends R8Command.Builder {
   public CompatProguardCommandBuilder() {
     this(true);
diff --git a/src/main/java/com/android/tools/r8/CompilationException.java b/src/main/java/com/android/tools/r8/CompilationException.java
deleted file mode 100644
index 8e1e56c..0000000
--- a/src/main/java/com/android/tools/r8/CompilationException.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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;
-
-/**
- * Exception to signal an compilation error.
- *
- * This is always an expected error and considered a user input issue.
- * A user-understandable message must be provided.
- */
-public class CompilationException extends Exception {
-  private static final long serialVersionUID = 1L;
-
-  /**
-   * Construct the exception with a {@link String} message.
-   * @param message the message
-   */
-  public CompilationException(String message) {
-    super(message);
-  }
-
-  /**
-   * Construct the exception with a {@link String} message and a {@link Throwable} cause.
-   * @param message the message
-   * @param cause the cause
-   */
-  public CompilationException(String message, Throwable cause) {
-    super(message, cause);
-  }
-
-  /**
-   * Construct the exception with a {@link Throwable} cause.
-   * @param cause the cause
-   */
-  public CompilationException(Throwable cause) {
-    super(cause.getMessage(), cause);
-  }
-
-  protected CompilationException() {
-    super();
-  }
-
-  public String getMessageForD8() {
-    return super.getMessage();
-  }
-
-  public String getMessageForR8() {
-    return super.getMessage();
-  }
-}
-
diff --git a/src/main/java/com/android/tools/r8/CompilationFailedException.java b/src/main/java/com/android/tools/r8/CompilationFailedException.java
index 561ad0c..bcc4ad1 100644
--- a/src/main/java/com/android/tools/r8/CompilationFailedException.java
+++ b/src/main/java/com/android/tools/r8/CompilationFailedException.java
@@ -7,6 +7,7 @@
  * Exception thrown when compilation failed to complete because of errors previously reported
  * through {@link com.android.tools.r8.DiagnosticsHandler}.
  */
+@Keep
 public class CompilationFailedException extends Exception {
 
   public CompilationFailedException() {
diff --git a/src/main/java/com/android/tools/r8/CompilationMode.java b/src/main/java/com/android/tools/r8/CompilationMode.java
index f7aa4f0..fb02504 100644
--- a/src/main/java/com/android/tools/r8/CompilationMode.java
+++ b/src/main/java/com/android/tools/r8/CompilationMode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 /** Compilation mode. */
+@Keep
 public enum CompilationMode {
   /** Preserves debugging information during compilation, eg, line-numbers and locals. */
   DEBUG,
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 1db187e..f4d378a 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -52,6 +52,7 @@
  * them to DEX bytecode (compiling from Java bytecode for such inputs and merging for DEX inputs),
  * and then writes the result to the directory or zip archive specified by {@code outputPath}.
  */
+@Keep
 public final class D8 {
 
   private D8() {}
@@ -121,8 +122,7 @@
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
 
-  static void runForTesting(AndroidApp inputApp, InternalOptions options)
-      throws IOException, CompilationException {
+  static void runForTesting(AndroidApp inputApp, InternalOptions options) throws IOException {
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
       run(inputApp, options, executor);
@@ -149,7 +149,7 @@
   }
 
   private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException {
     Timing timing = new Timing("D8");
     try {
       // Disable global optimizations.
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6f956db..8c6c02b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,18 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collection;
 
@@ -31,7 +26,8 @@
  *     .build();
  * </pre>
  */
-public class D8Command extends BaseCompilerCommand {
+@Keep
+public final class D8Command extends BaseCompilerCommand {
 
   private static class ClasspathInputOrigin extends InputFileOrigin {
 
@@ -45,6 +41,7 @@
    *
    * <p>A builder is obtained by calling {@link D8Command#builder}.
    */
+  @Keep
   public static class Builder extends BaseCompilerCommand.Builder<D8Command, Builder> {
 
     private boolean intermediate = false;
@@ -150,24 +147,7 @@
     }
   }
 
-  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
-      "Usage: d8 [options] <input-files>",
-      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-      " and options are:",
-      "  --debug                 # Compile with debugging information (default).",
-      "  --release               # Compile without debugging information.",
-      "  --output <file>         # Output result in <outfile>.",
-      "                          # <file> must be an existing directory or a zip file.",
-      "  --lib <file>            # Add <file> as a library resource.",
-      "  --classpath <file>      # Add <file> as a classpath resource.",
-      "  --min-api               # Minimum Android API level compatibility",
-      "  --intermediate          # Compile an intermediate result intended for later",
-      "                          # merging.",
-      "  --file-per-class        # Produce a separate dex file per input class",
-      "  --no-desugaring         # Force disable desugaring.",
-      "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
-      "  --version               # Print the version of d8.",
-      "  --help                  # Print this message."));
+  static final String USAGE_MESSAGE = D8CommandParser.USAGE_MESSAGE;
 
   private boolean intermediate = false;
 
@@ -194,7 +174,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, builder());
+    return D8CommandParser.parse(args, origin);
   }
 
   /**
@@ -208,87 +188,7 @@
    * @return D8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, builder(handler));
-  }
-
-  private static Builder parse(String[] args, Origin origin, Builder builder) {
-    CompilationMode compilationMode = null;
-    Path outputPath = null;
-    OutputMode outputMode = null;
-    boolean hasDefinedApiLevel = false;
-    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
-    try {
-      for (int i = 0; i < expandedArgs.length; i++) {
-        String arg = expandedArgs[i].trim();
-        if (arg.length() == 0) {
-          continue;
-        } else if (arg.equals("--help")) {
-          builder.setPrintHelp(true);
-        } else if (arg.equals("--version")) {
-          builder.setPrintVersion(true);
-        } else if (arg.equals("--debug")) {
-          if (compilationMode == CompilationMode.RELEASE) {
-            builder.getReporter().error(new StringDiagnostic(
-                "Cannot compile in both --debug and --release mode.",
-                origin));
-            continue;
-          }
-          compilationMode = CompilationMode.DEBUG;
-        } else if (arg.equals("--release")) {
-          if (compilationMode == CompilationMode.DEBUG) {
-            builder.getReporter().error(new StringDiagnostic(
-                "Cannot compile in both --debug and --release mode.",
-                origin));
-            continue;
-          }
-          compilationMode = CompilationMode.RELEASE;
-        } else if (arg.equals("--file-per-class")) {
-          outputMode = OutputMode.DexFilePerClassFile;
-        } else if (arg.equals("--output")) {
-          String output = expandedArgs[++i];
-          if (outputPath != null) {
-            builder.getReporter().error(new StringDiagnostic(
-                "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
-                origin));
-            continue;
-          }
-          outputPath = Paths.get(output);
-        } else if (arg.equals("--lib")) {
-          builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
-        } else if (arg.equals("--classpath")) {
-          builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
-        } else if (arg.equals("--main-dex-list")) {
-          builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
-        } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
-          builder.setOptimizeMultidexForLinearAlloc(true);
-        } else if (arg.equals("--min-api")) {
-          hasDefinedApiLevel = parseMinApi(builder, expandedArgs[++i], hasDefinedApiLevel, origin);
-        } else if (arg.equals("--intermediate")) {
-          builder.setIntermediate(true);
-        } else if (arg.equals("--no-desugaring")) {
-          builder.setDisableDesugaring(true);
-        } else {
-          if (arg.startsWith("--")) {
-            builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg,
-                origin));
-            continue;
-          }
-          builder.addProgramFiles(Paths.get(arg));
-        }
-      }
-      if (compilationMode != null) {
-        builder.setMode(compilationMode);
-      }
-      if (outputMode == null) {
-        outputMode = OutputMode.DexIndexed;
-      }
-      if (outputPath == null) {
-        outputPath = Paths.get(".");
-      }
-      return builder.setOutput(outputPath, outputMode);
-    } catch (CompilationError e) {
-      throw builder.getReporter().fatalError(e);
-    }
+    return D8CommandParser.parse(args, origin, handler);
   }
 
   private D8Command(
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
new file mode 100644
index 0000000..9e5d36a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2018, 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;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class D8CommandParser extends BaseCompilerCommandParser {
+
+  public static void main(String[] args) throws CompilationFailedException {
+    D8Command command = parse(args, Origin.root()).build();
+    if (command.isPrintHelp()) {
+      System.out.println(USAGE_MESSAGE);
+      System.exit(1);
+    }
+    D8.run(command);
+  }
+
+  static final String USAGE_MESSAGE =
+      String.join(
+          "\n",
+          Arrays.asList(
+              "Usage: d8 [options] <input-files>",
+              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+              " and options are:",
+              "  --debug                 # Compile with debugging information (default).",
+              "  --release               # Compile without debugging information.",
+              "  --output <file>         # Output result in <outfile>.",
+              "                          # <file> must be an existing directory or a zip file.",
+              "  --lib <file>            # Add <file> as a library resource.",
+              "  --classpath <file>      # Add <file> as a classpath resource.",
+              "  --min-api               # Minimum Android API level compatibility",
+              "  --intermediate          # Compile an intermediate result intended for later",
+              "                          # merging.",
+              "  --file-per-class        # Produce a separate dex file per input class",
+              "  --no-desugaring         # Force disable desugaring.",
+              "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+              "  --version               # Print the version of d8.",
+              "  --help                  # Print this message."));
+
+  /**
+   * Parse the D8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @return D8 command builder with state set up according to parsed command line.
+   */
+  public static D8Command.Builder parse(String[] args, Origin origin) {
+    return parse(args, origin, D8Command.builder());
+  }
+
+  /**
+   * Parse the D8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @param handler Custom defined diagnostics handler.
+   * @return D8 command builder with state set up according to parsed command line.
+   */
+  public static D8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+    return parse(args, origin, D8Command.builder(handler));
+  }
+
+  private static D8Command.Builder parse(String[] args, Origin origin, D8Command.Builder builder) {
+    CompilationMode compilationMode = null;
+    Path outputPath = null;
+    OutputMode outputMode = null;
+    boolean hasDefinedApiLevel = false;
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+    try {
+      for (int i = 0; i < expandedArgs.length; i++) {
+        String arg = expandedArgs[i].trim();
+        if (arg.length() == 0) {
+          continue;
+        } else if (arg.equals("--help")) {
+          builder.setPrintHelp(true);
+        } else if (arg.equals("--version")) {
+          builder.setPrintVersion(true);
+        } else if (arg.equals("--debug")) {
+          if (compilationMode == CompilationMode.RELEASE) {
+            builder.error(
+                new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+            continue;
+          }
+          compilationMode = CompilationMode.DEBUG;
+        } else if (arg.equals("--release")) {
+          if (compilationMode == CompilationMode.DEBUG) {
+            builder.error(
+                new StringDiagnostic("Cannot compile in both --debug and --release mode.", origin));
+            continue;
+          }
+          compilationMode = CompilationMode.RELEASE;
+        } else if (arg.equals("--file-per-class")) {
+          outputMode = OutputMode.DexFilePerClassFile;
+        } else if (arg.equals("--output")) {
+          String output = expandedArgs[++i];
+          if (outputPath != null) {
+            builder.error(
+                new StringDiagnostic(
+                    "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
+                    origin));
+            continue;
+          }
+          outputPath = Paths.get(output);
+        } else if (arg.equals("--lib")) {
+          builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+        } else if (arg.equals("--classpath")) {
+          builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
+        } else if (arg.equals("--main-dex-list")) {
+          builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+        } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+          builder.setOptimizeMultidexForLinearAlloc(true);
+        } else if (arg.equals("--min-api")) {
+          String minApiString = expandedArgs[++i];
+          if (hasDefinedApiLevel) {
+            builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+          } else {
+            parseMinApi(builder, minApiString, origin);
+            hasDefinedApiLevel = true;
+          }
+        } else if (arg.equals("--intermediate")) {
+          builder.setIntermediate(true);
+        } else if (arg.equals("--no-desugaring")) {
+          builder.setDisableDesugaring(true);
+        } else {
+          if (arg.startsWith("--")) {
+            builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
+            continue;
+          }
+          builder.addProgramFiles(Paths.get(arg));
+        }
+      }
+      if (compilationMode != null) {
+        builder.setMode(compilationMode);
+      }
+      if (outputMode == null) {
+        outputMode = OutputMode.DexIndexed;
+      }
+      if (outputPath == null) {
+        outputPath = Paths.get(".");
+      }
+      return builder.setOutput(outputPath, outputMode);
+    } catch (CompilationError e) {
+      throw builder.fatalError(e);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/DataDirectoryResource.java b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
index 2f421f7..3e860e3 100644
--- a/src/main/java/com/android/tools/r8/DataDirectoryResource.java
+++ b/src/main/java/com/android/tools/r8/DataDirectoryResource.java
@@ -12,6 +12,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+@Keep
 public interface DataDirectoryResource extends DataResource {
 
   static DataDirectoryResource fromFile(Path dir, Path file) {
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
index 189d5eb..aa9c73f 100644
--- a/src/main/java/com/android/tools/r8/DataEntryResource.java
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -15,6 +15,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+@Keep
 public interface DataEntryResource extends DataResource {
 
   /** Get the bytes of the data entry resource. */
diff --git a/src/main/java/com/android/tools/r8/DataResource.java b/src/main/java/com/android/tools/r8/DataResource.java
index 4609c97..eac8b6c 100644
--- a/src/main/java/com/android/tools/r8/DataResource.java
+++ b/src/main/java/com/android/tools/r8/DataResource.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+@Keep
 public interface DataResource extends Resource {
   char SEPARATOR = '/';
 
diff --git a/src/main/java/com/android/tools/r8/DataResourceConsumer.java b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
index 215ca59..e306b9b 100644
--- a/src/main/java/com/android/tools/r8/DataResourceConsumer.java
+++ b/src/main/java/com/android/tools/r8/DataResourceConsumer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+@KeepForSubclassing
 public interface DataResourceConsumer {
 
   void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler);
diff --git a/src/main/java/com/android/tools/r8/DataResourceProvider.java b/src/main/java/com/android/tools/r8/DataResourceProvider.java
index e851f0f..5e0a24f 100644
--- a/src/main/java/com/android/tools/r8/DataResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/DataResourceProvider.java
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+@KeepForSubclassing
 public interface DataResourceProvider {
 
+  @KeepForSubclassing
   interface Visitor {
     void visit(DataDirectoryResource directory);
     void visit(DataEntryResource file);
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 9c76484..5987b99 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -67,7 +67,7 @@
       InternalOptions options,
       Boolean minimalMainDex,
       Map<String, Integer> inputOrdering)
-      throws IOException, CompilationException {
+      throws IOException {
     options.enableDesugaring = false;
     options.enableMainDexListCheck = false;
     options.minimalMainDex = minimalMainDex;
@@ -106,7 +106,7 @@
   }
 
   public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
-      throws IOException, CompilationException {
+      throws IOException {
     InternalOptions options = command.getInternalOptions();
     options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
     D8.runForTesting(command.getInputApp(), options);
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index f1c0fac..311a3dd 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -29,6 +29,7 @@
  *
  * <p>This consumer receives DEX file content for each Java class-file input.
  */
+@KeepForSubclassing
 public interface DexFilePerClassFileConsumer extends ProgramConsumer {
 
   /**
@@ -57,6 +58,7 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
+  @Keep
   class ForwardingConsumer implements DexFilePerClassFileConsumer {
 
     private static final DexFilePerClassFileConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -92,6 +94,7 @@
   }
 
   /** Consumer to write program resources to an output. */
+  @Keep
   class ArchiveConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
   }
 
   /** Directory consumer to write program resources to a directory. */
+  @Keep
   class DirectoryConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final OutputBuilder outputBuilder;
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index 3bbab64..e802103 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -31,6 +31,7 @@
  * <p>This consumer receives DEX file content using standard indexed-multidex for programs larger
  * than a single DEX file. This is the default consumer for DEX programs.
  */
+@KeepForSubclassing
 public interface DexIndexedConsumer extends ProgramConsumer {
 
   /**
@@ -58,6 +59,7 @@
   }
 
   /** Forwarding consumer to delegate to an optional existing consumer. */
+  @Keep
   class ForwardingConsumer implements DexIndexedConsumer {
 
     private static final DexIndexedConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
@@ -100,6 +102,7 @@
   }
 
   /** Consumer to write program resources to an output. */
+  @Keep
   class ArchiveConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     protected final OutputBuilder outputBuilder;
@@ -181,6 +184,7 @@
     }
   }
 
+  @Keep
   class DirectoryConsumer extends ForwardingConsumer
       implements DataResourceConsumer, InternalProgramOutputPathConsumer {
     private final Path directory;
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 1fcd70a..3e299f2 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -31,7 +31,8 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
-public class DexSplitterHelper {
+@Keep
+public final class DexSplitterHelper {
 
   public static void run(
       D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
@@ -40,8 +41,7 @@
     try {
       ExceptionUtils.withCompilationHandler(
           command.getReporter(),
-          () -> run(command, featureClassMapping, output, proguardMap, executor),
-          CompilationException::getMessage);
+          () -> run(command, featureClassMapping, output, proguardMap, executor));
     } finally {
       executor.shutdown();
     }
@@ -53,7 +53,7 @@
       String output,
       String proguardMap,
       ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException {
     InternalOptions options = command.getInternalOptions();
     options.enableDesugaring = false;
     options.enableMainDexListCheck = false;
@@ -135,7 +135,7 @@
   }
 
   public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
-      throws IOException, CompilationException {
+      throws IOException {
     InternalOptions options = command.getInternalOptions();
     options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
     D8.runForTesting(command.getInputApp(), options);
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 7896251..3184d54 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.java
@@ -9,6 +9,7 @@
 /**
  * Interface for all diagnostic message produced by D8 and R8.
  */
+@Keep
 public interface Diagnostic {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 8212835..74a9a96 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -10,6 +10,7 @@
  *
  * <p>During compilation the warning and info methods will be called.
  */
+@Keep
 public interface DiagnosticsHandler {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
index 5d5f751..50aff3f 100644
--- a/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/DirectoryClassFileProvider.java
@@ -19,6 +19,7 @@
  * Lazy resource provider returning class file resources based
  * on filesystem directory content.
  */
+@Keep
 public final class DirectoryClassFileProvider implements ClassFileResourceProvider {
   private final Path root;
 
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index ced0b4c..734dc4a 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -94,7 +94,7 @@
   }
 
   public static void main(String[] args)
-      throws IOException, CompilationException, ExecutionException, ResourceException {
+      throws IOException, ExecutionException, ResourceException {
     ExtractMarkerCommand.Builder builder = ExtractMarkerCommand.parse(args);
     ExtractMarkerCommand command = builder.build();
     if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java b/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
index 9555d3b..598d8e0 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.errors.CompilationError;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -54,7 +55,7 @@
       return this;
     }
 
-    public ExtractMarkerCommand build() throws CompilationException, IOException {
+    public ExtractMarkerCommand build() throws IOException {
       // If printing versions ignore everything else.
       if (isPrintHelp()) {
         return new ExtractMarkerCommand(isPrintHelp());
@@ -76,15 +77,13 @@
     return new Builder();
   }
 
-  public static Builder parse(String[] args)
-      throws CompilationException, IOException {
+  public static Builder parse(String[] args) throws IOException {
     Builder builder = builder();
     parse(args, builder);
     return builder;
   }
 
-  private static void parse(String[] args, Builder builder)
-      throws CompilationException, IOException {
+  private static void parse(String[] args, Builder builder) throws IOException {
     for (int i = 0; i < args.length; i++) {
       String arg = args[i].trim();
       if (arg.length() == 0) {
@@ -101,7 +100,7 @@
         builder.setPrintHelp(true);
       } else {
         if (arg.startsWith("--")) {
-          throw new CompilationException("Unknown option: " + arg);
+          throw new CompilationError("Unknown option: " + arg);
         }
         builder.addProgramFile(Paths.get(arg));
       }
diff --git a/src/main/java/com/android/tools/r8/Keep.java b/src/main/java/com/android/tools/r8/Keep.java
new file mode 100644
index 0000000..13ed8c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Keep.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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;
+
+@Keep
+public @interface Keep {
+
+}
diff --git a/src/main/java/com/android/tools/r8/KeepForSubclassing.java b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
new file mode 100644
index 0000000..6e57114
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/KeepForSubclassing.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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;
+
+@Keep
+public @interface KeepForSubclassing {
+
+}
diff --git a/src/main/java/com/android/tools/r8/OutputMode.java b/src/main/java/com/android/tools/r8/OutputMode.java
index 0da34b7..bc8a8d8 100644
--- a/src/main/java/com/android/tools/r8/OutputMode.java
+++ b/src/main/java/com/android/tools/r8/OutputMode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8;
 
 /** Enumeration of the possible output formats of compilation. */
+@Keep
 public enum OutputMode {
 
   /** Produce DEX files using standard indexed-multidex for programs larger that a single file. */
diff --git a/src/main/java/com/android/tools/r8/ProgramConsumer.java b/src/main/java/com/android/tools/r8/ProgramConsumer.java
index 08d7d7c..b3143a0 100644
--- a/src/main/java/com/android/tools/r8/ProgramConsumer.java
+++ b/src/main/java/com/android/tools/r8/ProgramConsumer.java
@@ -6,6 +6,7 @@
 /**
  * Base for all program consumers to allow abstracting which concrete consumer is provided to D8/R8.
  */
+@KeepForSubclassing
 public interface ProgramConsumer {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ProgramResource.java b/src/main/java/com/android/tools/r8/ProgramResource.java
index cabeabc..ffeb558 100644
--- a/src/main/java/com/android/tools/r8/ProgramResource.java
+++ b/src/main/java/com/android/tools/r8/ProgramResource.java
@@ -20,9 +20,11 @@
  * A resource may optionally include a set describing the class descriptors for each type that is
  * defined by the resource.
  */
+@KeepForSubclassing
 public interface ProgramResource extends Resource {
 
   /** Type of program-format kinds. */
+  @Keep
   enum Kind {
     /** Format-kind for Java class-file resources. */
     CF,
@@ -66,6 +68,7 @@
   Set<String> getClassDescriptors();
 
   /** File-based program resource. */
+  @Keep
   class FileResource implements ProgramResource {
     private final Origin origin;
     private final Kind kind;
@@ -105,6 +108,7 @@
   }
 
   /** Byte-content based program resource. */
+  @Keep
   class ByteResource implements ProgramResource {
     private final Origin origin;
     private final Kind kind;
diff --git a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
index 76d340c..d4b8a87 100644
--- a/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ProgramResourceProvider.java
@@ -6,6 +6,7 @@
 import java.util.Collection;
 
 /** Program resource provider. */
+@KeepForSubclassing
 public interface ProgramResourceProvider {
 
   Collection<ProgramResource> getProgramResources() throws ResourceException;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 16c9de8..077e220 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.ClassAndMemberPublicizer;
 import com.android.tools.r8.graph.DexApplication;
@@ -104,6 +103,7 @@
  * them to DEX bytecode, using {@code androidJar} as the reference of the system runtime library,
  * and then writes the result to the directory or zip archive specified by {@code outputPath}.
  */
+@Keep
 public class R8 {
 
   private final Timing timing = new Timing("R8");
@@ -179,7 +179,7 @@
       String proguardSeedsData,
       InternalOptions options,
       ProguardMapSupplier proguardMapSupplier)
-      throws ExecutionException, DexOverflowException {
+      throws ExecutionException {
     try {
       Marker marker = getMarker(options);
       if (options.isGeneratingClassFiles()) {
@@ -214,8 +214,7 @@
     return result;
   }
 
-  static void runForTesting(AndroidApp app, InternalOptions options)
-      throws IOException, CompilationException {
+  static void runForTesting(AndroidApp app, InternalOptions options) throws IOException {
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
       run(app, options, executor);
@@ -224,16 +223,12 @@
     }
   }
 
-  private static void run(
-      AndroidApp app,
-      InternalOptions options,
-      ExecutorService executor)
-      throws IOException, CompilationException {
+  private static void run(AndroidApp app, InternalOptions options, ExecutorService executor)
+      throws IOException {
     new R8(options).run(app, executor);
   }
 
-  private void run(AndroidApp inputApp, ExecutorService executorService)
-      throws IOException, CompilationException {
+  private void run(AndroidApp inputApp, ExecutorService executorService) throws IOException {
     assert options.programConsumer != null;
     if (options.quiet) {
       System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
@@ -503,31 +498,15 @@
     }
   }
 
-  static void unwrapExecutionException(ExecutionException executionException)
-      throws CompilationException {
+  static void unwrapExecutionException(ExecutionException executionException) {
     Throwable cause = executionException.getCause();
     if (cause instanceof CompilationError) {
       // add original exception as suppressed exception to provide the original stack trace
       cause.addSuppressed(executionException);
       throw (CompilationError) cause;
-    } else if (cause instanceof CompilationException) {
-      cause.addSuppressed(executionException);
-      throw (CompilationException) cause;
     } else if (cause instanceof RuntimeException) {
-      // ForkJoinPool wraps checked exceptions in RuntimeExceptions
-      if (cause.getCause() != null
-          && cause.getCause() instanceof CompilationException) {
-        cause.addSuppressed(executionException);
-        throw (CompilationException) cause.getCause();
-      // ForkJoinPool sometimes uses 2 levels of RuntimeExceptions, to provide accurate stack traces
-      } else if (cause.getCause() != null && cause.getCause().getCause() != null
-          && cause.getCause().getCause() instanceof CompilationException) {
-        cause.addSuppressed(executionException);
-        throw (CompilationException) cause.getCause().getCause();
-      } else {
-        cause.addSuppressed(executionException);
-        throw (RuntimeException) cause;
-      }
+      cause.addSuppressed(executionException);
+      throw (RuntimeException) cause;
     } else {
       throw new RuntimeException(executionException.getMessage(), cause);
     }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 4fc0590..86ff5a5 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.Reporter;
@@ -46,13 +45,15 @@
  *     .build();
  * </pre>
  */
-public class R8Command extends BaseCompilerCommand {
+@Keep
+public final class R8Command extends BaseCompilerCommand {
 
   /**
    * Builder for constructing a R8Command.
    *
    * <p>A builder is obtained by calling {@link R8Command#builder}.
    */
+  @Keep
   public static class Builder extends BaseCompilerCommand.Builder<R8Command, Builder> {
 
     private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
@@ -66,6 +67,8 @@
     // Internal compatibility mode for use from CompatProguard tool.
     Path proguardCompatibilityRulesOutput = null;
 
+    private boolean allowPartiallyImplementedProguardOptions = false;
+
     private StringConsumer mainDexListConsumer = null;
 
     // TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing.
@@ -286,7 +289,8 @@
         mainDexKeepRules = parser.getConfig().getRules();
       }
 
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(factory, reporter);
+      ProguardConfigurationParser parser = new ProguardConfigurationParser(
+          factory, reporter, !allowPartiallyImplementedProguardOptions);
       if (!proguardConfigs.isEmpty()) {
         parser.parse(proguardConfigs);
       }
@@ -384,6 +388,11 @@
             c.accept(builder);
           };
     }
+
+    // Internal for-testing method to add post-processors of the proguard configuration.
+    void allowPartiallyImplementedProguardOptions() {
+      allowPartiallyImplementedProguardOptions = true;
+    }
   }
 
   // Wrapper class to ensure that R8 does not allow DEX as program inputs.
@@ -413,38 +422,7 @@
     }
   }
 
-  // Internal state to verify parsing properties not enforced by the builder.
-  private static class ParseState {
-    CompilationMode mode = null;
-    OutputMode outputMode = null;
-    Path outputPath = null;
-    boolean hasDefinedApiLevel = false;
-  }
-
-  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
-      "Usage: r8 [options] <input-files>",
-      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
-      " and options are:",
-      "  --release                # Compile without debugging information (default).",
-      "  --debug                  # Compile with debugging information.",
-      // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
-      //"  --dex                    # Compile program to DEX file format (default).",
-      //"  --classfile              # Compile program to Java classfile format.",
-      "  --output <file>          # Output result in <file>.",
-      "                           # <file> must be an existing directory or a zip file.",
-      "  --lib <file>             # Add <file> as a library resource.",
-      "  --min-api                # Minimum Android API level compatibility.",
-      "  --pg-conf <file>         # Proguard configuration <file>.",
-      "  --pg-map-output <file>   # Output the resulting name and line mapping to <file>.",
-      "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
-      "  --no-minification        # Force disable minification of names.",
-      "  --no-desugaring          # Force disable desugaring.",
-      "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
-      "                           # primary dex file.",
-      "  --main-dex-list <file>   # List of classes to place in the primary dex file.",
-      "  --main-dex-list-output <file>  # Output the full main-dex list in <file>.",
-      "  --version                # Print the version of r8.",
-      "  --help                   # Print this message."));
+  static final String USAGE_MESSAGE = R8CommandParser.USAGE_MESSAGE;
 
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
   private final StringConsumer mainDexListConsumer;
@@ -480,7 +458,7 @@
    * @return R8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin) {
-    return parse(args, origin, builder());
+    return R8CommandParser.parse(args, origin);
   }
 
   /**
@@ -494,102 +472,7 @@
    * @return R8 command builder with state set up according to parsed command line.
    */
   public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
-    return parse(args, origin, builder(handler));
-  }
-
-  private static Builder parse(String[] args, Origin origin, Builder builder) {
-    ParseState state = new ParseState();
-    parse(args, origin, builder, state);
-    if (state.mode != null) {
-      builder.setMode(state.mode);
-    }
-    Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
-    OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
-    builder.setOutput(outputPath, outputMode);
-    return builder;
-  }
-
-  private static ParseState parse(
-      String[] args,
-      Origin argsOrigin,
-      Builder builder,
-      ParseState state) {
-    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
-    for (int i = 0; i < expandedArgs.length; i++) {
-      String arg = expandedArgs[i].trim();
-      if (arg.length() == 0) {
-        continue;
-      } else if (arg.equals("--help")) {
-        builder.setPrintHelp(true);
-      } else if (arg.equals("--version")) {
-        builder.setPrintVersion(true);
-      } else if (arg.equals("--debug")) {
-        if (state.mode == CompilationMode.RELEASE) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --debug and --release mode.", argsOrigin));
-        }
-        state.mode = CompilationMode.DEBUG;
-      } else if (arg.equals("--release")) {
-        if (state.mode == CompilationMode.DEBUG) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --debug and --release mode.", argsOrigin));
-        }
-        state.mode = CompilationMode.RELEASE;
-      } else if (arg.equals("--dex")) {
-        if (state.outputMode == OutputMode.ClassFile) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
-        }
-        state.outputMode = OutputMode.DexIndexed;
-      } else if (arg.equals("--classfile")) {
-        if (state.outputMode == OutputMode.DexIndexed) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
-        }
-        state.outputMode = OutputMode.ClassFile;
-      } else if (arg.equals("--output")) {
-        String outputPath = expandedArgs[++i];
-        if (state.outputPath != null) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Cannot output both to '"
-                  + state.outputPath.toString()
-                  + "' and '"
-                  + outputPath
-                  + "'",
-              argsOrigin));
-        }
-        state.outputPath = Paths.get(outputPath);
-      } else if (arg.equals("--lib")) {
-        builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--min-api")) {
-        state.hasDefinedApiLevel =
-            parseMinApi(builder, expandedArgs[++i], state.hasDefinedApiLevel, argsOrigin);
-      } else if (arg.equals("--no-tree-shaking")) {
-        builder.setDisableTreeShaking(true);
-      } else if (arg.equals("--no-minification")) {
-        builder.setDisableMinification(true);
-      } else if (arg.equals("--no-desugaring")) {
-        builder.setDisableDesugaring(true);
-      } else if (arg.equals("--main-dex-rules")) {
-        builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--main-dex-list")) {
-        builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--main-dex-list-output")) {
-        builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
-        builder.setOptimizeMultidexForLinearAlloc(true);
-      } else if (arg.equals("--pg-conf")) {
-        builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
-      } else if (arg.equals("--pg-map-output")) {
-        builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
-      } else {
-        if (arg.startsWith("--")) {
-          builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
-        }
-        builder.addProgramFiles(Paths.get(arg));
-      }
-    }
-    return state;
+    return R8CommandParser.parse(args, origin, handler);
   }
 
   private R8Command(
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
new file mode 100644
index 0000000..3d33037
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2018, 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;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+public class R8CommandParser extends BaseCompilerCommandParser {
+
+  public static void main(String[] args) throws CompilationFailedException {
+    R8Command command = parse(args, Origin.root()).build();
+    if (command.isPrintHelp()) {
+      System.out.println(USAGE_MESSAGE);
+      System.exit(1);
+    }
+    R8.run(command);
+  }
+
+  // Internal state to verify parsing properties not enforced by the builder.
+  private static class ParseState {
+    CompilationMode mode = null;
+    OutputMode outputMode = null;
+    Path outputPath = null;
+    boolean hasDefinedApiLevel = false;
+  }
+
+  static final String USAGE_MESSAGE =
+      String.join(
+          "\n",
+          Arrays.asList(
+              "Usage: r8 [options] <input-files>",
+              " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+              " and options are:",
+              "  --release                # Compile without debugging information (default).",
+              "  --debug                  # Compile with debugging information.",
+              // TODO(b/65390962): Add help for output-mode flags once the CF backend is complete.
+              // "  --dex                    # Compile program to DEX file format (default).",
+              // "  --classfile              # Compile program to Java classfile format.",
+              "  --output <file>          # Output result in <file>.",
+              "                           # <file> must be an existing directory or a zip file.",
+              "  --lib <file>             # Add <file> as a library resource.",
+              "  --min-api                # Minimum Android API level compatibility.",
+              "  --pg-conf <file>         # Proguard configuration <file>.",
+              "  --pg-map-output <file>   # Output the resulting name and line mapping to <file>.",
+              "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
+              "  --no-minification        # Force disable minification of names.",
+              "  --no-desugaring          # Force disable desugaring.",
+              "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
+              "                           # primary dex file.",
+              "  --main-dex-list <file>   # List of classes to place in the primary dex file.",
+              "  --main-dex-list-output <file>  # Output the full main-dex list in <file>.",
+              "  --version                # Print the version of r8.",
+              "  --help                   # Print this message."));
+  /**
+   * Parse the R8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @return R8 command builder with state set up according to parsed command line.
+   */
+  public static R8Command.Builder parse(String[] args, Origin origin) {
+    return new R8CommandParser().parse(args, origin, R8Command.builder());
+  }
+
+  /**
+   * Parse the R8 command-line.
+   *
+   * <p>Parsing will set the supplied options or their default value if they have any.
+   *
+   * @param args Command-line arguments array.
+   * @param origin Origin description of the command-line arguments.
+   * @param handler Custom defined diagnostics handler.
+   * @return R8 command builder with state set up according to parsed command line.
+   */
+  public static R8Command.Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+    return new R8CommandParser().parse(args, origin, R8Command.builder(handler));
+  }
+
+  private R8Command.Builder parse(String[] args, Origin origin, R8Command.Builder builder) {
+    ParseState state = new ParseState();
+    parse(args, origin, builder, state);
+    if (state.mode != null) {
+      builder.setMode(state.mode);
+    }
+    Path outputPath = state.outputPath != null ? state.outputPath : Paths.get(".");
+    OutputMode outputMode = state.outputMode != null ? state.outputMode : OutputMode.DexIndexed;
+    builder.setOutput(outputPath, outputMode);
+    return builder;
+  }
+
+  private void parse(
+      String[] args, Origin argsOrigin, R8Command.Builder builder, ParseState state) {
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
+    for (int i = 0; i < expandedArgs.length; i++) {
+      String arg = expandedArgs[i].trim();
+      if (arg.length() == 0) {
+        continue;
+      } else if (arg.equals("--help")) {
+        builder.setPrintHelp(true);
+      } else if (arg.equals("--version")) {
+        builder.setPrintVersion(true);
+      } else if (arg.equals("--debug")) {
+        if (state.mode == CompilationMode.RELEASE) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --debug and --release mode.", argsOrigin));
+        }
+        state.mode = CompilationMode.DEBUG;
+      } else if (arg.equals("--release")) {
+        if (state.mode == CompilationMode.DEBUG) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --debug and --release mode.", argsOrigin));
+        }
+        state.mode = CompilationMode.RELEASE;
+      } else if (arg.equals("--dex")) {
+        if (state.outputMode == OutputMode.ClassFile) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+        }
+        state.outputMode = OutputMode.DexIndexed;
+      } else if (arg.equals("--classfile")) {
+        if (state.outputMode == OutputMode.DexIndexed) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot compile in both --dex and --classfile output mode.", argsOrigin));
+        }
+        state.outputMode = OutputMode.ClassFile;
+      } else if (arg.equals("--output")) {
+        String outputPath = expandedArgs[++i];
+        if (state.outputPath != null) {
+          builder.error(
+              new StringDiagnostic(
+                  "Cannot output both to '"
+                      + state.outputPath.toString()
+                      + "' and '"
+                      + outputPath
+                      + "'",
+                  argsOrigin));
+        }
+        state.outputPath = Paths.get(outputPath);
+      } else if (arg.equals("--lib")) {
+        builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--min-api")) {
+        String minApiString = expandedArgs[++i];
+        if (state.hasDefinedApiLevel) {
+          builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
+        } else {
+          parseMinApi(builder, minApiString, argsOrigin);
+          state.hasDefinedApiLevel = true;
+        }
+      } else if (arg.equals("--no-tree-shaking")) {
+        builder.setDisableTreeShaking(true);
+      } else if (arg.equals("--no-minification")) {
+        builder.setDisableMinification(true);
+      } else if (arg.equals("--no-desugaring")) {
+        builder.setDisableDesugaring(true);
+      } else if (arg.equals("--main-dex-rules")) {
+        builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--main-dex-list")) {
+        builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--main-dex-list-output")) {
+        builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
+        builder.setOptimizeMultidexForLinearAlloc(true);
+      } else if (arg.equals("--pg-conf")) {
+        builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
+      } else if (arg.equals("--pg-map-output")) {
+        builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
+      } else {
+        if (arg.startsWith("--")) {
+          builder.error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
+        }
+        builder.addProgramFiles(Paths.get(arg));
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index b64fe01..50c50cc 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -19,6 +19,7 @@
  * The D8/R8 compilers uses default implementations for various file-system resources, but the
  * client is free to provide their own.
  */
+@KeepForSubclassing
 public interface Resource {
   /**
    * Get the origin of the resource.
diff --git a/src/main/java/com/android/tools/r8/ResourceException.java b/src/main/java/com/android/tools/r8/ResourceException.java
index 3af7efa..2726b92 100644
--- a/src/main/java/com/android/tools/r8/ResourceException.java
+++ b/src/main/java/com/android/tools/r8/ResourceException.java
@@ -11,6 +11,7 @@
  * For example, this is the expected exception that must be thrown if a resource fails to produce
  * its content. See {@link ProgramResource#getByteStream()} and {@link StringResource#getString()}.
  */
+@Keep
 public class ResourceException extends Exception {
 
   private final Origin origin;
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 98ed5e9..24b2cc9 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.2.17-dev";
+  public static final String LABEL = "1.2.19-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index 250ca3b..1336f95 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.benchmarks.BenchmarkUtils.printRuntimeNanoseconds;
 
 import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -112,7 +111,7 @@
       boolean desugar,
       Map<String, ProgramResource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
 
     ProgramConsumer consumer =
         new DexFilePerClassFileConsumer.ForwardingConsumer(null) {
@@ -155,7 +154,7 @@
       boolean desugar,
       Map<String, ProgramResource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     ProgramConsumer consumer =
         new ForwardingConsumer(null) {
           @Override
@@ -199,7 +198,7 @@
 
   private static void merge(
       boolean desugar, Map<String, ProgramResource> outputs, ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException, ResourceException {
+      throws IOException, CompilationFailedException, ResourceException {
     Builder builder =
         D8Command.builder()
             .setMinApiLevel(API)
@@ -221,7 +220,7 @@
   }
 
   public static void main(String[] args)
-      throws IOException, CompilationException, CompilationFailedException, ResourceException {
+      throws IOException, CompilationFailedException, ResourceException {
     boolean desugar = Arrays.asList(args).contains("--desugar");
     Path input = desugar ? JAR_NOT_DESUGARED : JAR_DESUGARED;
     InMemoryClassPathProvider provider = new InMemoryClassPathProvider(input);
diff --git a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
index 0aaefd9..3f241a0 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.benchmarks;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -20,7 +19,7 @@
   private static final int ITERATIONS = 1000;
 
   public static void compile(ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .addProgramFiles(Paths.get("build/test/examples/arithmetic.jar"))
@@ -43,8 +42,7 @@
         executor);
   }
 
-  public static void main(String[] args)
-      throws IOException, CompilationException, CompilationFailedException {
+  public static void main(String[] args) throws IOException, CompilationFailedException {
     int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
     ExecutorService executor = ThreadUtils.getExecutorService(threads);
     try {
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index dcc89c6..7ef43a3 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.utils.AndroidApp;
@@ -175,7 +174,7 @@
   }
 
   private static void writeApp(DexApplication app, Path output, ExecutorService executor)
-      throws IOException, ExecutionException, DexOverflowException {
+      throws IOException, ExecutionException {
     InternalOptions options = new InternalOptions();
     AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
     ApplicationWriter writer = new ApplicationWriter(app, options, null, null, null, null, null);
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index b270200..6599591 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.compatdexbuilder;
 
 import com.android.tools.r8.CompatDxHelper;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -154,7 +153,7 @@
   }
 
   private DexConsumer dexEntry(ZipFile zipFile, ZipEntry classEntry, ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     DexConsumer consumer = new DexConsumer();
     D8Command.Builder builder = D8Command.builder();
     CompatDxHelper.ignoreDexInArchive(builder);
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index b9131e5..f512a34 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -11,7 +11,6 @@
 import static com.android.tools.r8.utils.FileUtils.isZipFile;
 
 import com.android.tools.r8.CompatDxHelper;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
@@ -312,9 +311,6 @@
   public static void main(String[] args) throws IOException {
     try {
       run(args);
-    } catch (CompilationException e) {
-      System.err.println(e.getMessage());
-      System.exit(1);
     } catch (DxUsageMessage e) {
       System.err.println(USAGE_HEADER);
       e.printHelpOn(System.err);
@@ -325,7 +321,7 @@
   }
 
   private static void run(String[] args)
-      throws DxUsageMessage, IOException, CompilationException, CompilationFailedException {
+      throws DxUsageMessage, IOException, CompilationFailedException {
     DxCompatOptions dexArgs = DxCompatOptions.parse(args);
     if (dexArgs.help) {
       printHelpOn(System.out);
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 66931e3..615a3e2 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -5,12 +5,12 @@
 package com.android.tools.r8.compatproguard;
 
 import com.android.tools.r8.CompatProguardCommandBuilder;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.Version;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.utils.AbortException;
 import com.google.common.collect.ImmutableList;
@@ -58,7 +58,7 @@
       this.printHelpAndExit = printHelpAndExit;
     }
 
-    public static CompatProguardOptions parse(String[] args) throws CompilationException {
+    public static CompatProguardOptions parse(String[] args) {
       String output = null;
       int minApi = 1;
       boolean forceProguardCompatibility = false;
@@ -97,7 +97,7 @@
             } else if (arg.equals("--no-locals")) {
               noLocals = true;
             } else if (arg.equals("-outjars")) {
-              throw new CompilationException(
+              throw new CompilationError(
                   "Proguard argument -outjar is not supported. Use R8 compatible --output flag");
             } else {
               if (currentLine.length() > 0) {
@@ -150,8 +150,7 @@
     CompatProguardOptions.print();
   }
 
-  private static void run(String[] args)
-      throws IOException, CompilationException, CompilationFailedException {
+  private static void run(String[] args) throws IOException, CompilationFailedException {
     // Run R8 passing all the options from the command line as a Proguard configuration.
     CompatProguardOptions options = CompatProguardOptions.parse(args);
     if (options.printHelpAndExit || options.output == null) {
@@ -175,9 +174,6 @@
   public static void main(String[] args) throws IOException {
     try {
       run(args);
-    } catch (CompilationException e) {
-      System.err.println(e.getMessage());
-      System.exit(1);
     } catch (CompilationFailedException | AbortException e) {
       // Detail of the errors were already reported
       System.err.println("Compilation failed");
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 9721d3b..e1905ff 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -162,7 +161,7 @@
   }
 
   private Iterable<VirtualFile> distribute(ExecutorService executorService)
-      throws ExecutionException, IOException, DexOverflowException {
+      throws ExecutionException, IOException {
     // Distribute classes into dex files.
     VirtualFile.Distributor distributor;
     if (options.isGeneratingDexFilePerClassFile()) {
@@ -179,8 +178,7 @@
     return distributor.run();
   }
 
-  public void write(ExecutorService executorService)
-      throws IOException, ExecutionException, DexOverflowException {
+  public void write(ExecutorService executorService) throws IOException, ExecutionException {
     application.timing.begin("DexApplication.write");
     try {
       insertAttributeAnnotations();
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 3d0b01c..9e75e45 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.dex.VirtualFile.VirtualFileCycler;
-import com.android.tools.r8.errors.DexOverflowException;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -306,7 +306,7 @@
     directSubClasses = new DirectSubClassesInfo(app, classes);
   }
 
-  public void distribute() throws DexOverflowException {
+  public void distribute() {
     List<ClassGroup> remainingInheritanceGroups = collectInheritanceGroups();
     // Sort to ensure reproducible allocation
     remainingInheritanceGroups.sort(null);
@@ -369,8 +369,7 @@
     return groupClassNumber;
   }
 
-  private Collection<VirtualFile> assignGroup(ClassGroup group,
-      List<VirtualFile> dexBlackList) throws DexOverflowException {
+  private Collection<VirtualFile> assignGroup(ClassGroup group, List<VirtualFile> dexBlackList) {
     VirtualFileCycler cycler = new VirtualFileCycler(dexes, namingLens, dexIndexOffset);
     if (group.members.isEmpty()) {
       return Collections.emptyList();
@@ -411,8 +410,8 @@
    * They will fail to link during DexOpt but they will be loaded only once.
    * @param classes set of classes to assign, the set will be destroyed during assignment.
    */
-  private Collection<VirtualFile> assignClassesWithLinkingError(Set<DexProgramClass> classes,
-      Collection<VirtualFile> dexBlackList) throws DexOverflowException {
+  private Collection<VirtualFile> assignClassesWithLinkingError(
+      Set<DexProgramClass> classes, Collection<VirtualFile> dexBlackList) {
 
     List<ClassGroup> layers = collectNoDirectInheritanceGroups(classes);
 
@@ -440,7 +439,7 @@
             dexForLayer.abortTransaction();
             if (dexForLayer.isEmpty()) {
               // The class is too big to fit in one dex
-              throw new DexOverflowException("Class '" + dexProgramClass.toSourceString()
+              throw new CompilationError("Class '" + dexProgramClass.toSourceString()
                   + "' from " + dexProgramClass.getOrigin().toString()
                   + " is too big to fit in a dex.");
             }
@@ -614,8 +613,8 @@
    * Assign as many classes as possible by layer starting by roots.
    * @return the list of classes that were not assigned.
    */
-  private Set<DexProgramClass> assignFromRoot(VirtualFile dex,
-      Collection<DexProgramClass> classes) throws DexOverflowException {
+  private Set<DexProgramClass> assignFromRoot(
+      VirtualFile dex, Collection<DexProgramClass> classes) {
 
     int totalClasses = classes.size();
     int assignedClasses = 0;
@@ -635,7 +634,7 @@
             dex.abortTransaction();
             if (dex.isEmpty()) {
               // The class is too big to fit in one dex
-              throw new DexOverflowException("Class '" + clazz.toSourceString() + "' from "
+              throw new CompilationError("Class '" + clazz.toSourceString() + "' from "
                   + clazz.getOrigin().toString() + " is too big to fit in a dex.");
             }
             isLayerFullyAssigned = false;
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 7f55b19..9ac752a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -3,9 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.dex;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.errors.MainDexOverflowException;
+import com.android.tools.r8.errors.MainDexOverflow;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
@@ -24,6 +23,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Sets;
@@ -185,15 +185,16 @@
     return transaction.getNumberOfFields();
   }
 
-  void throwIfFull(boolean hasMainDexList) throws DexOverflowException {
+  void throwIfFull(boolean hasMainDexList, Reporter reporter) {
     if (!isFull()) {
       return;
     }
-    throw new MainDexOverflowException(
-        hasMainDexList,
-        transaction.getNumberOfMethods(),
-        transaction.getNumberOfFields(),
-        MAX_ENTRIES);
+    throw reporter.fatalError(
+        new MainDexOverflow(
+            hasMainDexList,
+            transaction.getNumberOfMethods(),
+            transaction.getNumberOfFields(),
+            MAX_ENTRIES));
   }
 
   private boolean isFilledEnough(FillStrategy fillStrategy) {
@@ -226,8 +227,7 @@
       this.writer = writer;
     }
 
-    public abstract Collection<VirtualFile> run()
-        throws ExecutionException, IOException, DexOverflowException;
+    public abstract Collection<VirtualFile> run() throws ExecutionException, IOException;
   }
 
   /**
@@ -296,7 +296,7 @@
       originalNames = computeOriginalNameMapping(classes, application.getProguardMap());
     }
 
-    protected void fillForMainDexList(Set<DexProgramClass> classes) throws DexOverflowException {
+    protected void fillForMainDexList(Set<DexProgramClass> classes) {
       if (!application.mainDexList.isEmpty()) {
         VirtualFile mainDexFile = virtualFiles.get(0);
         for (DexType type : application.mainDexList) {
@@ -314,7 +314,7 @@
           }
           mainDexFile.commitTransaction();
         }
-        mainDexFile.throwIfFull(true);
+        mainDexFile.throwIfFull(true, options.reporter);
       }
     }
 
@@ -363,7 +363,7 @@
     }
 
     @Override
-    public Collection<VirtualFile> run() throws IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws IOException {
       int totalClassNumber = classes.size();
       // First fill required classes into the main dex file.
       fillForMainDexList(classes);
@@ -408,14 +408,13 @@
     }
 
     @Override
-    public Collection<VirtualFile> run()
-        throws ExecutionException, IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws ExecutionException, IOException {
       // Add all classes to the main dex file.
       for (DexProgramClass programClass : classes) {
         mainDexFile.addClass(programClass);
       }
       mainDexFile.commitTransaction();
-      mainDexFile.throwIfFull(false);
+      mainDexFile.throwIfFull(false, options.reporter);
       return virtualFiles;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/errors/DexOverflowException.java b/src/main/java/com/android/tools/r8/errors/DexOverflowException.java
deleted file mode 100644
index a392080..0000000
--- a/src/main/java/com/android/tools/r8/errors/DexOverflowException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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.errors;
-
-import com.android.tools.r8.CompilationException;
-
-/**
- * Signals when there were too many items to fit in a given dex file.
- */
-public class DexOverflowException extends CompilationException {
-
-  protected DexOverflowException() {
-    super();
-  }
-
-  public DexOverflowException(String message) {
-    super(message);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
similarity index 89%
rename from src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java
rename to src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
index e570c81..e33cf59 100644
--- a/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java
+++ b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
@@ -4,16 +4,16 @@
 package com.android.tools.r8.errors;
 
 /**
- * Thrown when running mono dex and not all classes can fit in a dex or when running for multidex
- * legacy and there are too many classes to fit in the main dex.
+ * Info about error when running mono dex and not all classes can fit in a dex or when running for
+ * multidex legacy and there are too many classes to fit in the main dex.
  */
-public class MainDexOverflowException extends DexOverflowException {
+public class MainDexOverflow {
   private final boolean hasMainDexList;
   private final long numOfMethods;
   private final long numOfFields;
   private final long maxNumOfEntries;
 
-  public MainDexOverflowException(
+  public MainDexOverflow(
       boolean hasMainDexList, long numOfMethods, long numOfFields, long maxNumOfEntries) {
     super();
     this.hasMainDexList = hasMainDexList;
@@ -52,7 +52,6 @@
     return messageBuilder.toString();
   }
 
-  @Override
   public String getMessage() {
     // Default message
     return getGeneralMessage()
@@ -62,7 +61,6 @@
         .toString();
   }
 
-  @Override
   public String getMessageForD8() {
     StringBuilder messageBuilder = getGeneralMessage();
     if (!hasMainDexList) {
@@ -74,7 +72,6 @@
     return messageBuilder.toString();
   }
 
-  @Override
   public String getMessageForR8() {
     StringBuilder messageBuilder = getGeneralMessage();
     if (!hasMainDexList) {
@@ -85,5 +82,4 @@
     messageBuilder.append(getNumberRelatedMessage());
     return messageBuilder.toString();
   }
-
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 0914e7b..9f97f88 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -99,6 +99,7 @@
   private Code code;
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
   private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
+  private int classFileVersion = -1;
 
   public DexEncodedMethod(
       DexMethod method,
@@ -114,6 +115,17 @@
     assert code == null || !accessFlags.isAbstract();
   }
 
+  public DexEncodedMethod(
+      DexMethod method,
+      MethodAccessFlags flags,
+      DexAnnotationSet annotationSet,
+      ParameterAnnotationsList annotationsList,
+      Code code,
+      int classFileVersion) {
+    this(method, flags, annotationSet, annotationsList, code);
+    this.classFileVersion = classFileVersion;
+  }
+
   public boolean isProcessed() {
     return compilationState != CompilationState.NOT_PROCESSED;
   }
@@ -303,6 +315,21 @@
     return code.asDexCode().hasDebugPositions();
   }
 
+  public int getClassFileVersion() {
+    assert classFileVersion >= 0;
+    return classFileVersion;
+  }
+
+  public boolean hasClassFileVersion() {
+    return classFileVersion >= 0;
+  }
+
+  public void upgradeClassFileVersion(int version) {
+    assert version >= 0;
+    assert !hasClassFileVersion() || version >= getClassFileVersion();
+    classFileVersion = version;
+  }
+
   public String qualifiedName() {
     return method.qualifiedName();
   }
@@ -753,6 +780,16 @@
     return optimizationInfo;
   }
 
+  public void copyMetadataFromInlinee(DexEncodedMethod inlinee) {
+    // Record that the current method uses identifier name string if the inlinee did so.
+    if (inlinee.getOptimizationInfo().useIdentifierNameString()) {
+      markUseIdentifierNameString();
+    }
+    if (inlinee.classFileVersion > classFileVersion) {
+      upgradeClassFileVersion(inlinee.getClassFileVersion());
+    }
+  }
+
   private static Builder builder(DexEncodedMethod from) {
     return new Builder(from);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index c1b843f..0a33000 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -356,9 +356,14 @@
   }
 
   public void setClassFileVersion(int classFileVersion) {
+    assert classFileVersion >= 0;
     this.classFileVersion = classFileVersion;
   }
 
+  public boolean hasClassFileVersion() {
+    return classFileVersion >= 0;
+  }
+
   public int getClassFileVersion() {
     assert classFileVersion != -1;
     return classFileVersion;
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 02f0801..64ba508 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static org.objectweb.asm.ClassReader.SKIP_CODE;
 import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
 import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
 import static org.objectweb.asm.Opcodes.ASM6;
@@ -90,7 +91,7 @@
     ClassReader reader = new ClassReader(input);
     reader.accept(
         new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
-        SKIP_FRAMES);
+        SKIP_FRAMES | SKIP_CODE);
   }
 
   private static int cleanAccessFlags(int access) {
@@ -560,6 +561,11 @@
 
     @Override
     public void visitCode() {
+      throw new Unreachable("visitCode() should not be called when SKIP_CODE is set");
+    }
+
+    @Override
+    public void visitEnd() {
       if (!flags.isAbstract() && !flags.isNative() && parent.classKind == ClassKind.PROGRAM) {
         if (parent.application.options.enableCfFrontend) {
           code = new LazyCfCode(method, parent.origin, parent.context, parent.application);
@@ -567,12 +573,6 @@
           code = new JarCode(method, parent.origin, parent.context, parent.application);
         }
       }
-    }
-
-    @Override
-    public void visitEnd() {
-      assert flags.isAbstract() || flags.isNative() || parent.classKind != ClassKind.PROGRAM
-          || code != null;
       ParameterAnnotationsList annotationsList;
       if (parameterAnnotationsLists == null) {
         annotationsList = ParameterAnnotationsList.empty();
@@ -595,8 +595,14 @@
             parameterFlags.toArray(new DexValue[parameterFlags.size()]),
             parent.application.getFactory()));
       }
-      DexEncodedMethod dexMethod = new DexEncodedMethod(method, flags,
-          createAnnotationSet(annotations), annotationsList, code);
+      DexEncodedMethod dexMethod =
+          new DexEncodedMethod(
+              method,
+              flags,
+              createAnnotationSet(annotations),
+              annotationsList,
+              code,
+              parent.version);
       if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
         parent.directMethods.add(dexMethod);
       } else {
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 fa00cd1..90a9898 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
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -253,7 +252,7 @@
     }
   }
 
-  private void processCovariantReturnTypeAnnotations(Builder<?> builder) throws ApiLevelException {
+  private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
     if (covariantReturnTypeAnnotationTransformer != null) {
       covariantReturnTypeAnnotationTransformer.process(builder);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 526bd9f..5b397e0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
@@ -56,7 +55,7 @@
     this.factory = factory;
   }
 
-  public void process(DexApplication.Builder<?> builder) throws ApiLevelException {
+  public void process(DexApplication.Builder<?> builder) {
     // List of methods that should be added to the next class.
     List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation = new LinkedList<>();
     List<DexEncodedMethod> covariantReturnTypeMethods = new LinkedList<>();
@@ -114,8 +113,7 @@
   private void buildCovariantReturnTypeMethodsForClass(
       DexClass clazz,
       List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
-      List<DexEncodedMethod> covariantReturnTypeMethods)
-      throws ApiLevelException {
+      List<DexEncodedMethod> covariantReturnTypeMethods) {
     for (DexEncodedMethod method : clazz.virtualMethods()) {
       if (methodHasCovariantReturnTypeAnnotation(method)) {
         methodsWithCovariantReturnTypeAnnotation.add(method);
@@ -137,8 +135,7 @@
   // variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic
   // methods to the list covariantReturnTypeMethods.
   private void buildCovariantReturnTypeMethodsForMethod(
-      DexClass clazz, DexEncodedMethod method, List<DexEncodedMethod> covariantReturnTypeMethods)
-      throws ApiLevelException {
+      DexClass clazz, DexEncodedMethod method, List<DexEncodedMethod> covariantReturnTypeMethods) {
     assert methodHasCovariantReturnTypeAnnotation(method);
     for (DexType covariantReturnType : getCovariantReturnTypes(clazz, method)) {
       DexEncodedMethod covariantReturnTypeMethod =
@@ -153,8 +150,7 @@
   //
   // Note: any "synchronized" or "strictfp" modifier could be dropped safely.
   private DexEncodedMethod buildCovariantReturnTypeMethod(
-      DexClass clazz, DexEncodedMethod method, DexType covariantReturnType)
-      throws ApiLevelException {
+      DexClass clazz, DexEncodedMethod method, DexType covariantReturnType) {
     DexProto newProto =
         factory.createProto(
             covariantReturnType, method.method.proto.shorty, method.method.proto.parameters);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index d4cb8a8..b75597d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -468,10 +468,7 @@
                   method.accessFlags.unsetBridge();
                 }
 
-                // Record that the current method uses identifier name string if the inlinee did so.
-                if (target.getOptimizationInfo().useIdentifierNameString()) {
-                  method.markUseIdentifierNameString();
-                }
+                method.copyMetadataFromInlinee(target);
               }
             }
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 2e5b703..6cb8f6e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -64,6 +64,7 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.BiConsumer;
+import org.objectweb.asm.Opcodes;
 
 /**
  * Support class for implementing outlining (i.e. extracting common code patterns as methods).
@@ -872,6 +873,8 @@
               Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
       DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
       DexMethod method = outline.buildMethod(type, methodName);
+      List<DexEncodedMethod> sites = outlineSites.get(outline);
+      assert !sites.isEmpty();
       direct[count] =
           new DexEncodedMethod(
               method,
@@ -879,6 +882,9 @@
               DexAnnotationSet.empty(),
               ParameterAnnotationsList.empty(),
               new OutlineCode(outline));
+      if (options.isGeneratingClassFiles()) {
+        direct[count].upgradeClassFileVersion(sites.get(0).getClassFileVersion());
+      }
       generatedOutlines.put(outline, method);
       count++;
     }
@@ -908,10 +914,9 @@
             DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
             options.itemFactory.getSkipNameValidationForTesting());
     if (options.isGeneratingClassFiles()) {
-      // Don't set class file version below 50.0 (JDK release 1.6).
-      clazz.setClassFileVersion(Math.max(50, getClassFileVersion(outlines)));
+      // All program classes must have a class-file version. Use Java 6.
+      clazz.setClassFileVersion(Opcodes.V1_6);
     }
-
     return clazz;
   }
 
@@ -927,26 +932,6 @@
     return result;
   }
 
-  private int getClassFileVersion(List<Outline> outlines) {
-    assert options.isGeneratingClassFiles();
-    int classFileVersion = -1;
-    Set<DexType> seen = Sets.newIdentityHashSet();
-    for (Outline outline : outlines) {
-      List<DexEncodedMethod> methods = outlineSites.get(outline);
-      for (DexEncodedMethod method : methods) {
-        DexType holder = method.method.holder;
-        if (seen.add(holder)) {
-          DexProgramClass programClass = appInfo.definitionFor(holder).asProgramClass();
-          assert programClass != null : "Attempt to outline from library class";
-          assert programClass.originatesFromClassResource()
-              : "Attempt to outline from non-classfile input to classfile output";
-          classFileVersion = Math.max(classFileVersion, programClass.getClassFileVersion());
-        }
-      }
-    }
-    return classFileVersion;
-  }
-
   public void applyOutliningCandidate(IRCode code, DexEncodedMethod method) {
     assert !(method.getCode() instanceof OutlineCode);
     ListIterator<BasicBlock> blocksIterator = code.blocks.listIterator();
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 bb27c08..df30c18 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
@@ -390,10 +390,14 @@
         // Compute the final change in locals and insert it before nextInstruction.
         boolean localsChanged = !ending.isEmpty() || !starting.isEmpty();
         if (localsChanged) {
-          DebugLocalsChange change = createLocalsChange(ending, starting);
-          if (change != null) {
-            // Insert the DebugLocalsChange instruction before nextInstruction.
-            instructionIterator.add(change);
+          boolean skipChange =
+              nextInstruction == nextInstruction.getBlock().exit() && nextInstruction.isGoto();
+          if (!skipChange) {
+            DebugLocalsChange change = createLocalsChange(ending, starting);
+            if (change != null) {
+              // Insert the DebugLocalsChange instruction before nextInstruction.
+              instructionIterator.add(change);
+            }
           }
           // Create new maps for the next DebugLocalsChange instruction.
           ending = new Int2ReferenceOpenHashMap<>();
@@ -809,8 +813,8 @@
       if (overlappingMoveExceptionIntervals) {
         for (LiveIntervals intervals : moveExceptionIntervals) {
           if (intervals.getUses().size() > 1) {
-            LiveIntervalsUse secondUse = intervals.getUses().stream().skip(1).findFirst().get();
-            LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+            LiveIntervals split =
+                intervals.splitBefore(intervals.getFirstUse() + INSTRUCTION_NUMBER_DELTA);
             unhandled.add(split);
           }
         }
@@ -1350,6 +1354,12 @@
     if (!hasDedicatedMoveExceptionRegister()) {
       return false;
     }
+    // If there are that many move exception intervals we don't spent the time
+    // going through them all. In that case it is unlikely that we can reuse the move exception
+    // register in any case.
+    if (moveExceptionIntervals.size() > 1000) {
+      return true;
+    }
     for (LiveIntervals moveExceptionInterval : moveExceptionIntervals) {
       if (intervals.anySplitOverlaps(moveExceptionInterval)) {
         return true;
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 2970fb4..7a25e1a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -108,7 +108,7 @@
   private void writeClass(DexProgramClass clazz, ClassFileConsumer consumer) throws IOException {
     ClassWriter writer = new ClassWriter(0);
     writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null);
-    int version = clazz.getClassFileVersion();
+    int version = getClassFileVersion(clazz);
     int access = clazz.accessFlags.getAsCfAccessFlags();
     String desc = namingLens.lookupDescriptor(clazz.type).toString();
     String name = namingLens.lookupInternalName(clazz.type);
@@ -161,6 +161,17 @@
         options.reporter, handler -> consumer.accept(result, desc, handler));
   }
 
+  private int getClassFileVersion(DexProgramClass clazz) {
+    int version = clazz.getClassFileVersion();
+    for (DexEncodedMethod method : clazz.directMethods()) {
+      version = Math.max(version, method.getClassFileVersion());
+    }
+    for (DexEncodedMethod method : clazz.virtualMethods()) {
+      version = Math.max(version, method.getClassFileVersion());
+    }
+    return version;
+  }
+
   private DexValue getSystemAnnotationValue(DexAnnotationSet annotations, DexType type) {
     DexAnnotation annotation = annotations.getFirstMatching(type);
     if (annotation == null) {
diff --git a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
index 0a6c84a..37ae662 100644
--- a/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/ArchiveEntryOrigin.java
@@ -4,9 +4,12 @@
 
 package com.android.tools.r8.origin;
 
+import com.android.tools.r8.Keep;
+
 /**
  * Origin representing an entry in an archive.
  */
+@Keep
 public class ArchiveEntryOrigin extends Origin {
 
   final String entryName;
diff --git a/src/main/java/com/android/tools/r8/origin/Origin.java b/src/main/java/com/android/tools/r8/origin/Origin.java
index c0bc2ec..3a8aa97 100644
--- a/src/main/java/com/android/tools/r8/origin/Origin.java
+++ b/src/main/java/com/android/tools/r8/origin/Origin.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.origin;
 
+import com.android.tools.r8.KeepForSubclassing;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -22,6 +23,7 @@
  * Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
  * origin (e.g., for generated resources of raw bytes).
  */
+@KeepForSubclassing
 public abstract class Origin implements Comparable<Origin> {
 
   private static final Origin ROOT =
diff --git a/src/main/java/com/android/tools/r8/origin/PathOrigin.java b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
index 95ee46f..0ff8080 100644
--- a/src/main/java/com/android/tools/r8/origin/PathOrigin.java
+++ b/src/main/java/com/android/tools/r8/origin/PathOrigin.java
@@ -4,11 +4,13 @@
 
 package com.android.tools.r8.origin;
 
+import com.android.tools.r8.Keep;
 import java.nio.file.Path;
 
 /**
  * Path component in an origin description.
  */
+@Keep
 public class PathOrigin extends Origin {
 
   private final Path path;
diff --git a/src/main/java/com/android/tools/r8/position/BinaryPosition.java b/src/main/java/com/android/tools/r8/position/BinaryPosition.java
deleted file mode 100644
index 5eb2da2..0000000
--- a/src/main/java/com/android/tools/r8/position/BinaryPosition.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.position;
-
-public class BinaryPosition implements Position {
-
-  /**
-   * Byte offset from start of the resource.
-   */
-  private final long offset;
-
-  public BinaryPosition(long offset) {
-    assert offset >= 0;
-    this.offset = offset;
-  }
-
-  public long getOffset() {
-    return offset;
-  }
-
-  @Override
-  public String toString() {
-    return "Index : " + offset;
-  }
-
-  @Override
-  public int hashCode() {
-    return Long.hashCode(offset);
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (o == this) {
-      return true;
-    }
-    if (o != null  && o.getClass().equals(BinaryPosition.class)) {
-      BinaryPosition other = (BinaryPosition) o;
-      return offset == other.offset;
-    }
-    return false;
-  }
-
-  @Override
-  public String getDescription() {
-    return "Offset " + offset;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/position/Position.java b/src/main/java/com/android/tools/r8/position/Position.java
index 902fad5..618fd51 100644
--- a/src/main/java/com/android/tools/r8/position/Position.java
+++ b/src/main/java/com/android/tools/r8/position/Position.java
@@ -4,10 +4,13 @@
 
 package com.android.tools.r8.position;
 
+import com.android.tools.r8.Keep;
+
 /**
  * Represent a position in a resource, it can for example be line in a text file of the byte offset
  * in a binary stream.
  */
+@Keep
 public interface Position {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/position/TextPosition.java b/src/main/java/com/android/tools/r8/position/TextPosition.java
index 9136fa7..0dd4090 100644
--- a/src/main/java/com/android/tools/r8/position/TextPosition.java
+++ b/src/main/java/com/android/tools/r8/position/TextPosition.java
@@ -3,10 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.position;
 
+import com.android.tools.r8.Keep;
+
 /**
  * A {@link Position} in a text file determined by line and column.
  * Line and column numbers start at 1.
  */
+@Keep
 public class TextPosition implements Position {
 
   /**
diff --git a/src/main/java/com/android/tools/r8/position/TextRange.java b/src/main/java/com/android/tools/r8/position/TextRange.java
index d7c0f43..0e642d8 100644
--- a/src/main/java/com/android/tools/r8/position/TextRange.java
+++ b/src/main/java/com/android/tools/r8/position/TextRange.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.position;
 
+import com.android.tools.r8.Keep;
+
+@Keep
 public class TextRange implements Position {
   private final TextPosition start;
   private final TextPosition end;
@@ -45,6 +48,11 @@
   }
 
   @Override
+  public String toString() {
+      return "Text range from: '" + getStart() + "', to: '" + getEnd() + "'";
+  }
+
+  @Override
   public String getDescription() {
     return start.getDescription();
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index d718026..085d9b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -460,10 +460,11 @@
         DexType baseType = type.toBaseType(appInfo.dexItemFactory);
         if (baseType.isClassType()) {
           DexClass baseClass = appInfo.definitionFor(baseType);
-          if (baseClass != null && !baseClass.isLibraryClass()) {
+          if (baseClass != null && baseClass.isProgramClass()
+              && baseClass.hasDefaultInitializer()) {
             markClassAsInstantiatedWithCompatRule(baseClass);
           } else {
-            // This handles reporting of missing classes.
+            // This also handles reporting of missing classes.
             markTypeAsLive(baseType);
           }
           return true;
@@ -784,6 +785,9 @@
     // Mark the type live here, so that the class exists at runtime. Note that this also marks all
     // supertypes as live, so even if the field is actually on a supertype, its class will be live.
     markTypeAsLive(field.clazz);
+    if (field.type.isClassType()) {
+      markTypeAsLive(field.type);
+    }
     // Find the actual field.
     DexEncodedField encodedField = appInfo.resolveFieldOn(field.clazz, field);
     if (encodedField == null) {
@@ -813,6 +817,9 @@
   private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
     assert field != null;
     markTypeAsLive(field.field.clazz);
+    if (field.field.type.isClassType()) {
+      markTypeAsLive(field.field.type);
+    }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index dd82dfd..d4d4bcb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -305,8 +305,8 @@
         // disableOptimization();
       }
 
-      if ((keepAttributePatterns.isEmpty()
-          && (rulesWasEmpty || (forceProguardCompatibility && !isObfuscating())))
+      if ((keepAttributePatterns.isEmpty() && rulesWasEmpty)
+          || (forceProguardCompatibility && !isObfuscating())
           || !isShrinking()) {
         keepAttributePatterns.addAll(ProguardKeepAttributes.KEEP_ALL);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 4a25e42..ed9cbdb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -47,6 +47,7 @@
   private final DexItemFactory dexItemFactory;
 
   private final Reporter reporter;
+  private final boolean failOnPartiallyImplementedOptions;
 
   private static final List<String> IGNORED_SINGLE_ARG_OPTIONS = ImmutableList.of(
       "protomapping",
@@ -96,10 +97,16 @@
 
   public ProguardConfigurationParser(
       DexItemFactory dexItemFactory, Reporter reporter) {
+    this(dexItemFactory, reporter, true);
+  }
+
+  public ProguardConfigurationParser(
+      DexItemFactory dexItemFactory, Reporter reporter, boolean failOnPartiallyImplementedOptions) {
     this.dexItemFactory = dexItemFactory;
     configurationBuilder = ProguardConfiguration.builder(dexItemFactory, reporter);
 
     this.reporter = reporter;
+    this.failOnPartiallyImplementedOptions = failOnPartiallyImplementedOptions;
   }
 
   public ProguardConfiguration.Builder getConfigurationBuilder() {
@@ -219,6 +226,10 @@
         ProguardCheckDiscardRule rule = parseCheckDiscardRule();
         configurationBuilder.addRule(rule);
       } else if (acceptString("keepdirectories")) {
+        // TODO(74279367): Report an error until it's fully supported.
+        if (failOnPartiallyImplementedOptions) {
+          failPartiallyImplementedOption("-keepdirectories", optionStart);
+        }
         parsePathFilter(configurationBuilder::addKeepDirectories);
       } else if (acceptString("keep")) {
         ProguardKeepRule rule = parseKeepRule();
@@ -339,11 +350,17 @@
       } else if (acceptString("adaptclassstrings")) {
         parseClassFilter(configurationBuilder::addAdaptClassStringsPattern);
       } else if (acceptString("adaptresourcefilenames")) {
-        // TODO(b/76377381): should be report an error until it's fully supported.
+        // TODO(76377381): Report an error until it's fully supported.
+        if (failOnPartiallyImplementedOptions) {
+          failPartiallyImplementedOption("-adaptresourcefilenames", optionStart);
+        }
         parsePathFilter(configurationBuilder::addAdaptResourceFilenames);
       } else if (acceptString("adaptresourcefilecontents")) {
+        // TODO(36847655): Report an error until it's fully supported.
+        if (failOnPartiallyImplementedOptions) {
+          failPartiallyImplementedOption("-adaptresourcefilecontents", optionStart);
+        }
         parsePathFilter(configurationBuilder::addAdaptResourceFilecontents);
-        // TODO(b/37139570): should be report an error until it's fully supported.
       } else if (acceptString("identifiernamestring")) {
         configurationBuilder.addRule(parseIdentifierNameStringRule());
       } else if (acceptString("if")) {
@@ -1473,6 +1490,11 @@
           "Option -" + optionName + " overrides -" + victim, origin, getPosition(start)));
     }
 
+    private void failPartiallyImplementedOption(String optionName, TextPosition start) {
+      throw reporter.fatalError(new StringDiagnostic(
+          "Option " + optionName + " currently not supported", origin, getPosition(start)));
+    }
+
     private Position getPosition(TextPosition start) {
       if (start.getOffset() == position) {
         return start;
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 9c7f525..fe8adfa 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.ResourceException;
@@ -15,7 +14,6 @@
 import java.nio.file.FileSystemException;
 import java.nio.file.Paths;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 public abstract class ExceptionUtils {
 
@@ -37,31 +35,26 @@
   }
 
   public interface CompileAction {
-    void run() throws IOException, CompilationException, CompilationError, ResourceException;
+    void run() throws IOException, CompilationError, ResourceException;
   }
 
   public static void withD8CompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
-    withCompilationHandler(reporter, action, CompilationException::getMessageForD8);
+    withCompilationHandler(reporter, action);
   }
 
   public static void withR8CompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
-    withCompilationHandler(reporter, action, CompilationException::getMessageForR8);
+    withCompilationHandler(reporter, action);
   }
 
-  public static void withCompilationHandler(
-      Reporter reporter,
-      CompileAction action,
-      Function<CompilationException, String> compilerMessage)
+  public static void withCompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
     try {
       try {
         action.run();
       } catch (IOException e) {
         throw reporter.fatalError(new ExceptionDiagnostic(e, extractIOExceptionOrigin(e)));
-      } catch (CompilationException e) {
-        throw reporter.fatalError(new StringDiagnostic(compilerMessage.apply(e)), e);
       } catch (CompilationError e) {
         throw reporter.fatalError(e);
       } catch (ResourceException e) {
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 873013e..8e84935 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.Keep;
 import com.android.tools.r8.dexsplitter.DexSplitter;
 import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
 import com.android.tools.r8.origin.PathOrigin;
@@ -38,6 +39,7 @@
  * <p>Note that this format does not allow specifying inter-module dependencies, this is simply a
  * placement tool.
  */
+@Keep
 public class FeatureClassMapping {
 
   HashMap<String, String> parsedRules = new HashMap<>(); // Already parsed rules.
@@ -198,6 +200,7 @@
         "Invalid mappings specification: " + error + "\n in file " + mappingFile + ":" + line);
   }
 
+  @Keep
   public static class FeatureMappingException extends Exception {
     FeatureMappingException(String message) {
       super(message);
diff --git a/src/main/java/com/android/tools/r8/utils/FlagFile.java b/src/main/java/com/android/tools/r8/utils/FlagFile.java
index 4791217..5f5003f 100644
--- a/src/main/java/com/android/tools/r8/utils/FlagFile.java
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.BaseCommand;
 import com.android.tools.r8.origin.Origin;
 import java.io.IOException;
 import java.nio.file.Files;
@@ -28,7 +29,7 @@
     }
   }
 
-  public static String[] expandFlagFiles(String[] args, Reporter reporter) {
+  public static String[] expandFlagFiles(String[] args, BaseCommand.Builder builder) {
     List<String> flags = new ArrayList<>(args.length);
     for (String arg : args) {
       if (arg.startsWith("@")) {
@@ -37,7 +38,7 @@
           flags.addAll(Files.readAllLines(flagFilePath));
         } catch (IOException e) {
           Origin origin = new FlagFileOrigin(flagFilePath);
-          reporter.error(new ExceptionDiagnostic(e, origin));
+          builder.error(new ExceptionDiagnostic(e, origin));
         }
       } else {
         flags.add(arg);
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index ebb7ce6..b09f164 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -3,10 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.BaseCompilerCommand;
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.MainDexOverflow;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
@@ -16,12 +20,18 @@
 public class Reporter implements DiagnosticsHandler {
 
   private final DiagnosticsHandler clientHandler;
+  private final BaseCompilerCommand command;
   private int errorCount = 0;
   private Diagnostic lastError;
   private final Collection<Throwable> suppressedExceptions = new ArrayList<>();
 
   public Reporter(DiagnosticsHandler clientHandler) {
+    this(clientHandler, null);
+  }
+
+  public Reporter(DiagnosticsHandler clientHandler, BaseCompilerCommand command) {
     this.clientHandler = clientHandler;
+    this.command = command;
   }
 
   @Override
@@ -75,6 +85,19 @@
   }
 
   /**
+   * @throws AbortException always.
+   */
+  public RuntimeException fatalError(MainDexOverflow e) {
+    if (command instanceof R8Command) {
+      return fatalError(new StringDiagnostic(e.getMessageForR8()));
+    } else if (command instanceof D8Command) {
+      return fatalError(new StringDiagnostic(e.getMessageForD8()));
+    } else {
+      return fatalError(new StringDiagnostic(e.getMessage()));
+    }
+  }
+
+  /**
    * @throws AbortException if any error was reported.
    */
   public void failIfPendingErrors() {
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index b7cd855..06ab8a9 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 
+@Keep
 public class StringDiagnostic implements Diagnostic {
 
   private final Origin origin;
diff --git a/src/main/keep.txt b/src/main/keep.txt
new file mode 100644
index 0000000..2d74e47
--- /dev/null
+++ b/src/main/keep.txt
@@ -0,0 +1,6 @@
+# Copyright (c) 2018, 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.
+
+-keep @com.android.tools.r8.Keep class * { public *; }
+-keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
diff --git a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
new file mode 100644
index 0000000..7a4950c
--- /dev/null
+++ b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, 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 catchhandleroverlap;
+
+public class CatchHandlerOverlap {
+  private static void f() throws Exception {
+    throw new Exception("f");
+  }
+
+  private static void g() throws Exception {
+    throw new Exception("g");
+  }
+
+  private static void h(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9,
+      int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) {
+    System.out.println(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
+        i12 + i13 + i14 + i15 + i16 + i17);
+    try {
+      f();
+    } catch (Exception e0) {
+      try {
+        g();
+      } catch (Exception e1) {
+        System.out.println(e0.getMessage() + " " + e1.getMessage());
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+  }
+}
diff --git a/src/test/examples/multidex005/ref-list-4-r8.txt b/src/test/examples/multidex005/ref-list-4-r8.txt
new file mode 100644
index 0000000..ea7b0f7
--- /dev/null
+++ b/src/test/examples/multidex005/ref-list-4-r8.txt
@@ -0,0 +1,8 @@
+Lmultidex005/DirectlyReferenced;
+Lmultidex005/FieldReference;
+Lmultidex005/Interface1;
+Lmultidex005/Interface2;
+Lmultidex005/Interface3;
+Lmultidex005/SuperClass;
+Lmultidex005/SuperInterface;
+Lmultidex005/SuperSuperClass;
\ No newline at end of file
diff --git a/src/test/examples/multidex005/ref-list-4.txt b/src/test/examples/multidex005/ref-list-4.txt
index ea7b0f7..7733fba 100644
--- a/src/test/examples/multidex005/ref-list-4.txt
+++ b/src/test/examples/multidex005/ref-list-4.txt
@@ -1,5 +1,8 @@
 Lmultidex005/DirectlyReferenced;
 Lmultidex005/FieldReference;
+Lmultidex005/IndirectlyReferenced;
+Lmultidex005/IndirectlyReferencedInterface;
+Lmultidex005/IndirectlyReferencedSuperClass;
 Lmultidex005/Interface1;
 Lmultidex005/Interface2;
 Lmultidex005/Interface3;
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index db26fe6..9cc2b48 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -54,8 +54,8 @@
   }
 
   private void ensureSameOutput(String main, AndroidApp app, byte[]... classes)
-      throws IOException, CompilationException, ExecutionException, CompilationFailedException,
-      ProguardRuleParserException {
+      throws IOException, ExecutionException, CompilationFailedException,
+          ProguardRuleParserException {
     ProcessResult javaResult = runOnJava(main, classes);
     ProcessResult d8Result = runOnArtRaw(compileWithD8(app), main);
     ProcessResult r8Result = runOnArtRaw(compileWithR8(app), main);
@@ -88,8 +88,8 @@
   }
 
   protected void ensureSameOutputAfterMerging(String main, byte[]... classes)
-      throws IOException, CompilationException, ExecutionException,
-      CompilationFailedException, ProguardRuleParserException {
+      throws IOException, ExecutionException, CompilationFailedException,
+          ProguardRuleParserException {
     AndroidApp app = buildAndroidApp(classes);
     // Compile to dex files with D8.
     AndroidApp dexApp = compileWithD8(app);
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index dc4a9b4..1d9ec50 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -119,7 +119,7 @@
   }
 
   private AndroidApp mergeDexResources(int minAPILevel, List<ProgramResource> individalDexes)
-      throws IOException, CompilationException, CompilationFailedException, ResourceException {
+      throws IOException, CompilationFailedException, ResourceException {
     D8Command.Builder builder = D8Command.builder()
         .setMinApiLevel(minAPILevel);
     for (ProgramResource resource : individalDexes) {
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 77959d9..ede7e53 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -76,7 +76,7 @@
 
       // compilation should have failed on CompilationError since A is declaring a default method.
       Assert.fail();
-    } catch (CompilationError | CompilationException e) {
+    } catch (CompilationError e) {
       // Expected.
     }
   }
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index 6f3473e..edf7f20 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -131,8 +131,7 @@
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
   }
 
-  private R8Command getCommand(Path out)
-      throws CompilationException, IOException, CompilationFailedException {
+  private R8Command getCommand(Path out) throws IOException, CompilationFailedException {
     return R8Command.builder()
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
         .addProgramFiles(INPUT_JAR)
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index b27569b..b12eb2d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1388,7 +1388,7 @@
       boolean disableInlining,
       boolean disableClassInlining,
       boolean hasMissingClasses)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
         disableInlining, disableClassInlining, hasMissingClasses);
@@ -1403,7 +1403,7 @@
       boolean disableInlining,
       boolean disableClassInlining,
       boolean hasMissingClasses)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
         CompilationFailedException {
     assert mode != null;
     switch (compilerUnderTest) {
@@ -1434,7 +1434,7 @@
                   }
                 }
               } catch (ResourceException e) {
-                throw new CompilationException(e);
+                throw new CompilationError("", e);
               }
             }
           }
@@ -1621,7 +1621,7 @@
 
   protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
       String fullClassName)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     DexVm dexVm = ToolHelper.getDexVm();
 
@@ -1751,7 +1751,7 @@
       CompilationMode mode,
       DexVm dexVm,
       File resultDir)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode,
         specification.disableInlining, specification.disableClassInlining,
@@ -1930,7 +1930,7 @@
             compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
             specification.disableInlining, specification.disableClassInlining,
             specification.hasMissingClasses);
-      } catch (CompilationException | CompilationFailedException e) {
+      } catch (CompilationFailedException e) {
         throw new CompilationError(e.getMessage(), e);
       } catch (ExecutionException e) {
         throw e.getCause();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 2ac50a6..d8b886c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -32,6 +32,7 @@
         "arrayaccess.ArrayAccess",
         "barray.BArray",
         "bridge.BridgeMethod",
+        "catchhandleroverlap.CatchHandlerOverlap",
         "cse.CommonSubexpressionElimination",
         "constants.Constants",
         "controlflow.ControlFlow",
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index a504c7c..36a4f00 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -30,7 +30,7 @@
 
   @Ignore
   @Test
-  public void UnreachableCode() throws IOException, ExecutionException, CompilationException {
+  public void UnreachableCode() throws IOException, ExecutionException {
     String name = "unreachable-code-1";
     AndroidApp input =
         AndroidApp.builder()
diff --git a/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java b/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java
index 301f78f..d21d156 100644
--- a/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java
+++ b/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java
@@ -73,8 +73,7 @@
   }
 
   @Test
-  public void testEmptyClass()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+  public void testEmptyClass() throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(EmptyClass.class);
 
     assertThat(analysis.integers, is(Sets.newHashSet()));
@@ -95,7 +94,7 @@
 
   @Test
   public void testConstsAndFieldAndMethods()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+      throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(ConstInCode.class);
 
     assertThat(analysis.integers, is(Sets.newHashSet(10, 11)));
@@ -125,7 +124,7 @@
 
   @Test
   public void testStaticValues()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+      throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(StaticFields.class);
 
     assertThat(analysis.integers, hasItems(10, 11, 12, 13));
@@ -163,8 +162,7 @@
   }
 
   @Test
-  public void testAnnotations()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+  public void testAnnotations() throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(IntAnnotation.class, OuterAnnotation.class, Annotated.class);
 
     assertThat(analysis.integers, hasItems(10, 11, 12, 13, 14, 15, 42));
@@ -184,7 +182,7 @@
 
   @Test
   public void testWithSkippingSome()
-      throws ExecutionException, CompilationFailedException, CompilationException, IOException {
+      throws ExecutionException, CompilationFailedException, IOException {
     TrackAll analysis = runAnalysis(ResourceClassToSkip.class, ToProcess.class);
 
     assertThat(analysis.integers, hasItems(10, 11, 12));
@@ -222,13 +220,13 @@
   }
 
   private TrackAll runAnalysis(Class<?>... classes)
-      throws IOException, CompilationException, ExecutionException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidApp app = readClasses(classes);
     return runOnApp(app);
   }
 
   private TrackAll runOnApp(AndroidApp app)
-      throws IOException, ExecutionException, CompilationFailedException, CompilationException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidApp outputApp = compileWithD8(app);
     Path outputDex = tmp.newFolder().toPath().resolve("classes.dex");
     outputApp.writeToDirectory(outputDex.getParent(), OutputMode.DexIndexed);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 96591d8..331af0a 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -215,7 +215,7 @@
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app)
-      throws CompilationException, ExecutionException, IOException, CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     D8Command.Builder builder = ToolHelper.prepareD8CommandBuilder(app);
     AndroidAppConsumers appSink = new AndroidAppConsumers(builder);
     D8.run(builder.build());
@@ -226,7 +226,7 @@
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ExecutionException, IOException, CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     return ToolHelper.runD8(app, optionsConsumer);
   }
 
@@ -234,7 +234,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return ToolHelper.runR8(readClasses(classes));
   }
@@ -243,7 +243,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(List<Class> classes)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command);
@@ -253,7 +253,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command, optionsConsumer);
@@ -263,7 +263,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(AndroidApp app)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(app).build();
     return ToolHelper.runR8(command);
@@ -273,7 +273,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(app).build();
     return ToolHelper.runR8(command, optionsConsumer);
@@ -283,7 +283,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, String proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig);
   }
@@ -293,7 +293,7 @@
    */
   protected AndroidApp compileWithR8(
       List<Class> classes, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig, optionsConsumer);
   }
@@ -302,7 +302,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Path proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig);
   }
@@ -311,7 +311,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(AndroidApp app, Path proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
@@ -324,7 +324,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(AndroidApp app, String proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(app, proguardConfig, null);
   }
@@ -334,7 +334,7 @@
    */
   protected AndroidApp compileWithR8(
       AndroidApp app, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
@@ -348,7 +348,7 @@
    */
   protected AndroidApp compileWithR8(
       AndroidApp app, Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d7090b4..68d87c1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -776,7 +775,7 @@
 
   public static ProguardConfiguration loadProguardConfiguration(
       DexItemFactory factory, List<Path> configPaths)
-      throws IOException, ProguardRuleParserException, CompilationException {
+      throws IOException, ProguardRuleParserException {
     Reporter reporter = new Reporter(new DefaultDiagnosticsHandler());
     if (configPaths.isEmpty()) {
       return ProguardConfiguration.defaultConfiguration(factory, reporter);
@@ -797,7 +796,7 @@
     return R8Command.builder(app).setProgramConsumer(DexIndexedConsumer.emptyConsumer());
   }
 
-  public static AndroidApp runR8(AndroidApp app) throws IOException, CompilationException {
+  public static AndroidApp runR8(AndroidApp app) throws IOException {
     try {
       return runR8(prepareR8CommandBuilder(app).build());
     } catch (CompilationFailedException e) {
@@ -806,7 +805,7 @@
   }
 
   public static AndroidApp runR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      throws IOException {
     try {
       return runR8(prepareR8CommandBuilder(app).build(), optionsConsumer);
     } catch (CompilationFailedException e) {
@@ -814,18 +813,17 @@
     }
   }
 
-  public static AndroidApp runR8(R8Command command) throws IOException, CompilationException {
+  public static AndroidApp runR8(R8Command command) throws IOException {
     return runR8(command, null);
   }
 
   public static AndroidApp runR8(R8Command command, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      throws IOException {
     return runR8WithFullResult(command, optionsConsumer);
   }
 
   public static AndroidApp runR8WithFullResult(
-      R8Command command, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      R8Command command, Consumer<InternalOptions> optionsConsumer) throws IOException {
     // TODO(zerny): Should we really be adding the android library in ToolHelper?
     AndroidApp app = command.getInputApp();
     if (app.getLibraryResourceProviders().isEmpty()) {
@@ -856,12 +854,12 @@
             ImmutableList.of("!junit/**", "!android/test/**"))));
   }
 
-  public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
+  public static AndroidApp runD8(AndroidApp app) throws IOException {
     return runD8(app, null);
   }
 
   public static AndroidApp runD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, IOException {
+      throws IOException {
     try {
       return runD8(D8Command.builder(app), optionsConsumer);
     } catch (CompilationFailedException e) {
@@ -870,13 +868,13 @@
   }
 
   public static AndroidApp runD8(D8Command.Builder builder)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     return runD8(builder, null);
   }
 
   public static AndroidApp runD8(
       D8Command.Builder builder, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     AndroidAppConsumers compatSink = new AndroidAppConsumers(builder);
     D8Command command = builder.build();
     InternalOptions options = command.getInternalOptions();
@@ -1336,6 +1334,12 @@
     return builder;
   }
 
+  public static R8Command.Builder allowPartiallyImplementedProguardOptions(
+      R8Command.Builder builder) {
+    builder.allowPartiallyImplementedProguardOptions();
+    return builder;
+  }
+
   public static AndroidApp getApp(BaseCommand command) {
     return command.getInputApp();
   }
@@ -1362,7 +1366,7 @@
   }
 
   public static void writeApplication(DexApplication application, InternalOptions options)
-      throws ExecutionException, DexOverflowException {
+      throws ExecutionException {
     R8.writeApplication(
         Executors.newSingleThreadExecutor(),
         application,
diff --git a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
index d6e0542..ad6a72b 100644
--- a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.cf;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.CompilationMode;
@@ -13,6 +14,8 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import java.nio.file.Path;
@@ -33,9 +36,18 @@
     ProcessResult run1 = ToolHelper.runJava(out1, CLASS.getCanonicalName());
     assertEquals(runInput.toString(), run1.toString());
     Path out2 = temp.getRoot().toPath().resolve("out2.zip");
-    build(builder -> builder.addProgramFiles(out1), new ClassFileConsumer.ArchiveConsumer(out2));
-    ProcessResult run2 = ToolHelper.runJava(out2, CLASS.getCanonicalName());
-    assertEquals(runInput.toString(), run2.toString());
+    boolean invalidDebugInfo = false;
+    try {
+      build(builder -> builder.addProgramFiles(out1), new ClassFileConsumer.ArchiveConsumer(out2));
+    } catch (CompilationError e) {
+      invalidDebugInfo = e.getCause() instanceof InvalidDebugInfoException;
+    }
+    // TODO(b/77522100): Change to assertFalse when fixed.
+    assertTrue(invalidDebugInfo);
+    if (!invalidDebugInfo) {
+      ProcessResult run2 = ToolHelper.runJava(out2, CLASS.getCanonicalName());
+      assertEquals(runInput.toString(), run2.toString());
+    }
   }
 
   private void build(ThrowingConsumer<Builder, Exception> input, ProgramConsumer consumer)
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index c6a2bd3..d5d2c39 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -43,7 +42,7 @@
   }
 
   private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     ToolHelper.runR8(
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
index 479a558..d0ad1df 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -5,7 +5,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -32,8 +31,7 @@
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
-  static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException,
-      CompilationFailedException {
+  static AndroidApp compileWithD8(Class... classes) throws IOException, CompilationFailedException {
     D8Command.Builder builder = D8Command.builder();
     for (Class clazz : classes) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 89935a7..ead9175 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -108,8 +107,7 @@
   }
 
   @Test
-  public void manyFilesWithSharedSynthesizedClass()
-      throws ExecutionException, IOException, DexOverflowException {
+  public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
 
     // Create classes that all reference enough strings to overflow the index, but are all
     // at different offsets in the strings array. This ensures we trigger multiple rounds of
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
index 2b2a40b..4b3f4fe 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexFileMergerHelper;
@@ -39,7 +38,7 @@
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   private Path createMergerInputWithTwoClasses(OutputMode outputMode, boolean addMarker)
-      throws CompilationFailedException, CompilationException, IOException {
+      throws CompilationFailedException, IOException {
     // Compile Class1 and Class2
     Path mergerInputZip = temp.newFolder().toPath().resolve("merger-input.zip");
     D8Command command =
@@ -55,8 +54,7 @@
   }
 
   private void testMarker(boolean addMarkerToInput)
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexIndexed, addMarkerToInput);
 
     Marker inputMarker = ExtractMarker.extractMarkerFromDexFile(mergerInputZip);
@@ -74,20 +72,18 @@
 
   @Test
   public void testMarkerPreserved()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(true);
   }
 
   @Test
   public void testMarkerNotAdded()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(false);
   }
 
   @Test
-  public void mergeTwoFiles() throws CompilationFailedException, CompilationException, IOException {
+  public void mergeTwoFiles() throws CompilationFailedException, IOException {
     Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexFilePerClassFile, false);
 
     Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
@@ -107,7 +103,7 @@
   }
 
   private void generateClassesAndTest(int extraMethodCount, int programResourcesSize)
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidApp generatedApp =
         MainDexListTests.generateApplication(
             ImmutableList.of("A", "B"),
@@ -125,8 +121,7 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  public void failIfTooBig()
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+  public void failIfTooBig() throws IOException, ExecutionException, CompilationFailedException {
     // Generates an application with two classes, each with the number of methods just enough not to
     // fit into a single dex file.
     generateClassesAndTest(1, 2);
@@ -134,7 +129,7 @@
 
   @Test
   public void failIfTooBigControl()
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     // Control test for failIfTooBig to make sure we don't fail with less methods.
     generateClassesAndTest(0, 1);
   }
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 5bbb9d5..a0b2515 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexSplitterHelper;
@@ -56,7 +55,7 @@
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   private Path createInput(boolean dontCreateMarkerInD8)
-      throws IOException, CompilationFailedException, CompilationException {
+      throws IOException, CompilationFailedException {
     // Initial normal compile to create dex files.
     Path inputZip = temp.newFolder().toPath().resolve("input.zip");
     D8Command command =
@@ -76,8 +75,7 @@
   }
 
   private void testMarker(boolean addMarkerToInput)
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     Path inputZip = createInput(!addMarkerToInput);
 
     Path output = temp.newFolder().toPath().resolve("output");
@@ -102,15 +100,13 @@
 
   @Test
   public void testMarkerPreserved()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(true);
   }
 
   @Test
   public void testMarkerNotAdded()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(false);
   }
 
@@ -124,14 +120,13 @@
   @Test
   public void splitFilesNoObfuscation()
       throws CompilationFailedException, IOException, FeatureMappingException, ResourceException,
-      CompilationException, ExecutionException {
+          ExecutionException {
     noObfuscation(false);
     noObfuscation(true);
   }
 
   private void noObfuscation(boolean useOptions)
-      throws IOException, CompilationFailedException, FeatureMappingException,
-      ResourceException, ExecutionException, CompilationException {
+      throws IOException, CompilationFailedException, FeatureMappingException {
     Path inputZip = createInput(false);
     Path output = temp.newFolder().toPath().resolve("output");
     Files.createDirectory(output);
@@ -228,7 +223,7 @@
   @Test
   public void splitFilesFromJar()
       throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
-      CompilationException, ExecutionException {
+          ExecutionException {
     splitFromJars(true, true);
     splitFromJars(false, true);
     splitFromJars(true, false);
@@ -236,8 +231,7 @@
   }
 
   private void splitFromJars(boolean useOptions, boolean explicitBase)
-      throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
-      ExecutionException, CompilationException {
+      throws IOException, CompilationFailedException, FeatureMappingException {
     Path inputZip = createInput(false);
     Path output = temp.newFolder().toPath().resolve("output");
     Files.createDirectory(output);
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 3cab10a..bda9ac5 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -10,7 +10,6 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
@@ -165,7 +164,7 @@
   }
 
   @Test
-  public void lookupFieldWithDefaultInInterface() throws DexOverflowException {
+  public void lookupFieldWithDefaultInInterface() {
     SmaliBuilder builder = new SmaliBuilder();
 
     builder.addInterface("Interface");
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 a542b70..39c8d28 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -64,14 +63,14 @@
       String referenceApk,
       String pgConf,
       String input)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
           CompilationFailedException {
     return runAndCheckVerification(
         compiler, mode, referenceApk, pgConf, null, Collections.singletonList(input));
   }
 
   public AndroidApp runAndCheckVerification(D8Command.Builder builder, String referenceApk)
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidAppConsumers appSink = new AndroidAppConsumers(builder);
     D8.run(builder.build());
     AndroidApp result = appSink.build();
@@ -86,7 +85,7 @@
       String pgConf,
       Consumer<InternalOptions> optionsConsumer,
       List<String> inputs)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     assertTrue(referenceApk == null || new File(referenceApk).exists());
     AndroidAppConsumers outputApp;
@@ -99,6 +98,7 @@
       builder.setMode(mode);
       builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
       builder.setMinApiLevel(AndroidApiLevel.L.getLevel());
+      ToolHelper.allowPartiallyImplementedProguardOptions(builder);
       ToolHelper.addProguardConfigurationConsumer(
           builder,
           pgConfig -> {
diff --git a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
index e546607..ab430d5 100644
--- a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
@@ -3,7 +3,6 @@
 // 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.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -19,7 +18,7 @@
   private static final String JAR = "third_party/framework/framework_160115954.jar";
 
   private AndroidApp doRun(D8Command.Builder builder)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     builder.setProgramConsumer(null);
     AndroidAppConsumers appSink = new AndroidAppConsumers(builder);
     D8.run(builder.build());
diff --git a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
index 0946ded..390105e 100644
--- a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
@@ -3,7 +3,6 @@
 // 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.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
@@ -17,7 +16,7 @@
       "third_party/photos/2017-06-06/PhotosEnglishOnlyLegacy_proguard.jar";
 
   public void runD8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     runAndCheckVerification(CompilerUnderTest.D8, mode, version, null, version);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
index df4bbfe..f0b1ece 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
@@ -3,7 +3,6 @@
 // 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.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
@@ -33,14 +32,14 @@
   static final String REFERENCE_APK = "noshrink_x86_GmsCore_prod_alldpi_release_unsigned.apk";
 
   public void runR8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     runAndCheckVerification(CompilerUnderTest.R8, mode, version);
   }
 
   public void runAndCheckVerification(
       CompilerUnderTest compiler, CompilationMode mode, String version)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     runAndCheckVerification(
         compiler, mode, version + GMSCORE_APK, null, Paths.get(version, GMSCORE_APK).toString());
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
index 5f635ff..09ce657 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -3,7 +3,6 @@
 // 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.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
@@ -19,7 +18,7 @@
 
   public AndroidApp buildFromDeployJar(
       CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     return runAndCheckVerification(
         compiler, mode, hasReference ? base + REFERENCE_APK : null, null, base + DEPLOY_JAR);
@@ -29,7 +28,7 @@
   public AndroidApp buildFromDeployJar(
       CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
       Consumer<InternalOptions> optionsConsumer)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     return runAndCheckVerification(
         compiler,
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index 5e1c61f..abc7a73 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -3,7 +3,6 @@
 // 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.CompilationFailedException;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.OutputMode;
@@ -33,7 +32,7 @@
   }
 
   private AndroidApp doRun()
-      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     R8Command command =
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 5afb068..d4ab6c6 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -37,7 +36,7 @@
       return buildApplication(
           AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
           options);
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -118,8 +117,7 @@
       return iterator;
     }
 
-    private AndroidApp writeDex(DexApplication application, InternalOptions options)
-        throws DexOverflowException {
+    private AndroidApp writeDex(DexApplication application, InternalOptions options) {
       try {
         ToolHelper.writeApplication(application, options);
         options.signalFinishedToConsumers();
@@ -129,7 +127,7 @@
       }
     }
 
-    public String run() throws DexOverflowException, IOException {
+    public String run() throws IOException {
       AppInfo appInfo = new AppInfo(application);
       IRConverter converter = new IRConverter(appInfo, options);
       converter.replaceCodeForTesting(method, code);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index 47f0db3..a5c7093 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -99,8 +98,7 @@
         });
   }
 
-  private Path runR8(Path proguardConfig)
-      throws IOException, CompilationException, CompilationFailedException {
+  private Path runR8(Path proguardConfig) throws IOException, CompilationFailedException {
     Path dexOutputDir = temp.newFolder().toPath();
     ToolHelper.runR8(
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 990fe6e..7b74453 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jsr45;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
@@ -50,7 +49,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   private AndroidApp compileWithD8(Path intputPath, Path outputPath)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8Command.Builder builder =
         D8Command.builder()
             .setMinApiLevel(AndroidApiLevel.O.getLevel())
@@ -62,7 +61,7 @@
   }
 
   private AndroidApp compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     return ToolHelper.runR8(
         R8Command.builder()
             .addProgramFiles(inputPath)
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 590baa6..4214a67 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -10,11 +10,12 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.StringResource;
@@ -23,9 +24,7 @@
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DebugLocalInfo;
@@ -54,9 +53,11 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
@@ -64,6 +65,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
@@ -102,6 +104,8 @@
   public static TemporaryFolder generatedApplicationsFolder =
       ToolHelper.getTemporaryFolderForTest();
 
+  private List<Diagnostic> errors = new ArrayList<>();
+
   // Generate the test applications in a @BeforeClass method, as they are used by several tests.
   @BeforeClass
   public static void generateTestApplications() throws Throwable {
@@ -179,12 +183,15 @@
     try {
       verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath(), false);
       fail("Expect to fail, for there are too many classes for the main-dex list.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was _not_ used.
-      assertFalse(e.getMessage().contains("single dex file"));
+      assertFalse(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT)));
+      assertTrue(
+          message.contains(
+              "# methods: " + String.valueOf(TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT)));
     }
   }
 
@@ -220,12 +227,16 @@
     try {
       verifyMainDexContains(MANY_CLASSES, getManyClassesMultiDexAppPath(), false);
       fail("Expect to fail, for there are too many classes for the main-dex list.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was _not_ used.
-      assertFalse(e.getMessage().contains("single dex file"));
+      assertFalse(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
+      assertTrue(
+          message.contains(
+              "# methods: "
+                  + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
     }
   }
 
@@ -444,15 +455,22 @@
     // Notice that this one fails due to the min API.
     try {
       generateApplication(
-          MANY_CLASSES, AndroidApiLevel.K.getLevel(), false,
-          MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
+          MANY_CLASSES,
+          AndroidApiLevel.K.getLevel(),
+          false,
+          MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS,
+          new TestDiagnosticsHandler());
       fail("Expect to fail, for there are many classes while multidex is not enabled.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was used.
-      assertTrue(e.getMessage().contains("single dex file"));
+      assertTrue(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
+      assertTrue(
+          message.contains(
+              "# methods: "
+                  + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
     }
   }
 
@@ -498,7 +516,7 @@
   private void doVerifyMainDexContains(
       List<String> mainDex, Path app, boolean singleDexApp, boolean minimalMainDex,
       MultiDexTestMode testMode)
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
+      throws IOException, ExecutionException, ProguardRuleParserException,
       CompilationFailedException {
     AndroidApp originalApp = AndroidApp.builder().addProgramFiles(app).build();
     DexInspector originalInspector = new DexInspector(originalApp);
@@ -508,7 +526,7 @@
     }
     Path outDir = temp.newFolder().toPath();
     R8Command.Builder builder =
-        R8Command.builder()
+        R8Command.builder(new TestDiagnosticsHandler())
             .addProgramFiles(app)
             .setMode(
                 minimalMainDex && mainDex.size() > 0
@@ -587,15 +605,27 @@
   }
 
   public static AndroidApp generateApplication(List<String> classes, int minApi, int methodCount)
-      throws IOException, ExecutionException, CompilationException {
+      throws IOException, ExecutionException {
     return generateApplication(classes, minApi, false, methodCount);
   }
 
   private static AndroidApp generateApplication(
       List<String> classes, int minApi, boolean intermediate, int methodCount)
-      throws IOException, ExecutionException, CompilationException {
+      throws IOException, ExecutionException {
+    return generateApplication(
+        classes, minApi, intermediate, methodCount, new DefaultDiagnosticsHandler());
+  }
+
+  private static AndroidApp generateApplication(
+      List<String> classes,
+      int minApi,
+      boolean intermediate,
+      int methodCount,
+      DiagnosticsHandler diagnosticsHandler)
+      throws IOException, ExecutionException {
     Timing timing = new Timing("MainDexListTests");
-    InternalOptions options = new InternalOptions();
+    InternalOptions options =
+        new InternalOptions(new DexItemFactory(), new Reporter(diagnosticsHandler));
     options.minApiLevel = minApi;
     options.intermediate = intermediate;
     DexItemFactory factory = options.itemFactory;
@@ -765,4 +795,12 @@
       throw new Unreachable();
     }
   }
+
+  private class TestDiagnosticsHandler implements DiagnosticsHandler {
+
+    @Override
+    public void error(Diagnostic error) {
+      errors.add(error);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index 43b4c0f..374d58f 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.GenerateMainDexListCommand;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -30,19 +31,15 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
 
-public class MainDexTracingTest {
+public class MainDexTracingTest extends TestBase {
 
   private static final String EXAMPLE_BUILD_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
   private static final String EXAMPLE_O_BUILD_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
   private static final String EXAMPLE_SRC_DIR = ToolHelper.EXAMPLES_DIR;
   private static final String EXAMPLE_O_SRC_DIR = ToolHelper.EXAMPLES_ANDROID_O_DIR;
 
-  @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
   @Test
   public void traceMainDexList001_whyareyoukeeping() throws Throwable {
     PrintStream stdout = System.out;
@@ -54,6 +51,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules-whyareyoukeeping.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
         AndroidApiLevel.I);
     String output = new String(baos.toByteArray(), Charset.defaultCharset());
     Assert.assertTrue(output.contains("is live because referenced in keep rule:"));
@@ -68,6 +66,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -79,6 +78,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "main-dex-rules-2.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex001", "ref-list-2.txt"),
         AndroidApiLevel.I);
   }
 
@@ -90,6 +90,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex002", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -101,6 +102,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex003", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -112,6 +114,7 @@
         EXAMPLE_O_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex", "main-dex-rules.txt"),
         Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -132,7 +135,14 @@
 
   @Test
   public void traceMainDexList005_4() throws Throwable {
-    doTest5(4);
+    doTest(
+        "traceMainDexList005",
+        "multidex005",
+        EXAMPLE_BUILD_DIR,
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-4.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4-r8.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-4.txt"),
+        AndroidApiLevel.I);
   }
 
   @Test
@@ -158,6 +168,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex006", "main-dex-rules-1.txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex006", "ref-list-1.txt"),
         AndroidApiLevel.I);
   }
 
@@ -168,6 +179,7 @@
         EXAMPLE_BUILD_DIR,
         Paths.get(EXAMPLE_SRC_DIR, "multidex005", "main-dex-rules-" + variant + ".txt"),
         Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
+        Paths.get(EXAMPLE_SRC_DIR, "multidex005", "ref-list-" + variant + ".txt"),
         AndroidApiLevel.I);
   }
 
@@ -176,6 +188,7 @@
       String packageName,
       String buildDir,
       Path mainDexRules,
+      Path expectedR8MainDexList,
       Path expectedMainDexList,
       AndroidApiLevel minSdk)
       throws Throwable {
@@ -184,6 +197,7 @@
         packageName,
         buildDir,
         mainDexRules,
+        expectedR8MainDexList,
         expectedMainDexList,
         minSdk,
         (options) -> {
@@ -196,6 +210,7 @@
       String packageName,
       String buildDir,
       Path mainDexRules,
+      Path expectedR8MainDexList,
       Path expectedMainDexList,
       AndroidApiLevel minSdk,
       Consumer<InternalOptions> optionsConsumer)
@@ -259,17 +274,22 @@
               .map(this::mainDexStringToDescriptor)
               .sorted()
               .collect(Collectors.toList());
-      // Check that both generated lists are the same as the reference list, except for lambda
+      // Check that generated lists are the same as the reference list, except for lambda
       // classes which are only produced when running R8.
+      String[] r8RefList = new String(Files.readAllBytes(
+          expectedR8MainDexList), StandardCharsets.UTF_8).split("\n");
+      for (int i = 0; i < r8RefList.length; i++) {
+        String reference = r8RefList[i].trim();
+        if (r8MainDexList.size() <= i) {
+          Assert.fail("R8 main dex list is missing '" + reference + "'");
+        }
+        checkSameMainDexEntry(reference, r8MainDexList.get(i));
+      }
       String[] refList = new String(Files.readAllBytes(
           expectedMainDexList), StandardCharsets.UTF_8).split("\n");
       int nonLambdaOffset = 0;
       for (int i = 0; i < refList.length; i++) {
         String reference = refList[i].trim();
-        if (r8MainDexList.size() <= i) {
-          Assert.fail("R8 main dex list is missing '" + reference + "'");
-        }
-        checkSameMainDexEntry(reference, r8MainDexList.get(i));
         // The main dex list generator does not do any lambda desugaring.
         if (!isLambda(reference)) {
           if (mainDexGeneratorMainDexList.size() <= i - nonLambdaOffset) {
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index deb90e4..38af4de 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -270,8 +269,7 @@
   }
 
   private R8Command.Builder getCommandForInstrumentation(
-      Path out, Path flag, Path mainApp, Path instrApp)
-      throws CompilationException, IOException {
+      Path out, Path flag, Path mainApp, Path instrApp) throws IOException {
     return R8Command.builder()
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar(), mainApp)
         .addProgramFiles(instrApp)
@@ -279,9 +277,8 @@
         .addProguardConfigurationFiles(flag);
   }
 
-  private R8Command.Builder getCommandForApps(
-      Path out, Path flag, Path... jars)
-      throws CompilationException, IOException {
+  private R8Command.Builder getCommandForApps(Path out, Path flag, Path... jars)
+      throws IOException {
     return R8Command.builder()
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
         .addProgramFiles(jars)
@@ -290,7 +287,7 @@
   }
 
   private static AndroidApp runR8(R8Command command)
-      throws ProguardRuleParserException, ExecutionException, CompilationException, IOException {
+      throws ProguardRuleParserException, ExecutionException, IOException {
     return ToolHelper.runR8(command, options -> {
       // Disable inlining to make this test not depend on inlining decisions.
       options.enableInlining = false;
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index b60ede7..d9808c0 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.ClassAndMemberPublicizer;
@@ -65,7 +64,7 @@
   }
 
   NamingLens runMinifier(List<Path> configPaths)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException {
     ProguardConfiguration configuration =
         ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
     InternalOptions options = new InternalOptions(configuration,
diff --git a/src/test/java/com/android/tools/r8/resource/DataResourceTest.java b/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
index dbd4e23..2df0612 100644
--- a/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
+++ b/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.resource;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
@@ -25,8 +24,7 @@
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @Test
-  public void dataResourceTest()
-      throws IOException, CompilationFailedException, CompilationException {
+  public void dataResourceTest() throws IOException, CompilationFailedException {
     String packageName = "dataresource";
     String mainClassName = packageName + ".ResourceTest";
     Path inputJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR,
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
index e6c9ada..e0d9ecf 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.rewrite.longcompare;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
@@ -35,7 +34,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   void compileWithD8(Path intputPath, Path outputPath)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .addProgramFiles(intputPath)
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
index fe74da9..a50a667 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.rewrite.longcompare;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -34,7 +33,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .setMode(mode)
@@ -68,7 +67,7 @@
   }
 
   private void runTest(CompilationMode mode)
-      throws IOException, CompilationException, ExecutionException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 9e0429b..8f00f8d 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -21,22 +21,15 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(VmTestRunner.class)
 public class FieldTypeTest extends TestBase {
 
-  @Ignore("b/78788577")
-  @Test
-  public void test_brokenTypeHierarchy() throws Exception {
-    JasminBuilder jasminBuilder = new JasminBuilder();
-    // interface Itf
-    ClassBuilder itf = jasminBuilder.addInterface("Itf");
-    MethodSignature foo = itf.addAbstractMethod("foo", ImmutableList.of(), "V");
-    // class Impl /* implements Itf */
-    ClassBuilder impl = jasminBuilder.addClass("Impl");
+  private ClassBuilder addImplementor(
+      JasminBuilder jasminBuilder, String name, String superName, String... interfaces) {
+    ClassBuilder impl = jasminBuilder.addClass(name, superName, interfaces);
     impl.addDefaultConstructor();
     impl.addVirtualMethod("foo", ImmutableList.of(), "V",
         ".limit locals 2",
@@ -50,15 +43,43 @@
         ".limit stack 2",
         "ldc \"" + impl.name + "\"",
         "areturn");
+    return impl;
+  }
+
+  @Test
+  public void test_brokenTypeHierarchy() throws Exception {
+    JasminBuilder jasminBuilder = new JasminBuilder();
+    // interface Itf1
+    ClassBuilder itf1 = jasminBuilder.addInterface("Itf1");
+    MethodSignature foo1 = itf1.addAbstractMethod("foo", ImmutableList.of(), "V");
+    // class Impl1 /* implements Itf1 */
+    ClassBuilder impl1 = addImplementor(jasminBuilder, "Impl1", "java/lang/Object");
+
+    // Another interface and implementer with a correct relation.
+    ClassBuilder itf2 = jasminBuilder.addInterface("Itf2");
+    MethodSignature foo2 = itf2.addAbstractMethod("foo", ImmutableList.of(), "V");
+    ClassBuilder impl2 = addImplementor(jasminBuilder, "Impl2", "java/lang/Object", itf2.name);
+
     ClassBuilder client = jasminBuilder.addClass("Client");
-    FieldSignature obj = client.addStaticFinalField("obj", itf.getDescriptor(), null);
+    client.setAccess("final");
+    client.addDefaultConstructor();
+    FieldSignature a = client.addStaticFinalField("a", "Ljava/lang/Object;", null);
+    FieldSignature obj1 = client.addField("private static", "obj1", itf1.getDescriptor(), null);
+    FieldSignature obj2 = client.addStaticFinalField("obj2", itf2.getDescriptor(), null);
     client.addClassInitializer(
         ".limit locals 1",
         ".limit stack 2",
-        "new " + impl.name,
+        "aconst_null",
+        "putstatic " + client.name + "/" + a.name  + " " + "Ljava/lang/Object;",
+        "new " + impl1.name,
         "dup",
-        "invokespecial " + impl.name + "/<init>()V",
-        "putstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
+        "invokespecial " + impl1.name + "/<init>()V",
+        // Unused, i.e., not read, field, yet still remained in the output.
+        "putstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
+        "new " + impl2.name,
+        "dup",
+        "invokespecial " + impl2.name + "/<init>()V",
+        "putstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
         "return"
     );
 
@@ -67,8 +88,8 @@
         ".limit locals 2",
         ".limit stack 2",
         "getstatic java/lang/System/out Ljava/io/PrintStream;",
-        "getstatic " + client.name + "/" + obj.name + " " + itf.getDescriptor(),
         /*
+        "getstatic " + client.name + "/" + obj1.name + " " + itf1.getDescriptor(),
         "astore_0",
         "aload_0",
         // java.lang.IncompatibleClassChangeError:
@@ -76,19 +97,24 @@
         "invokeinterface " + itf.name + "/" + foo.name + "()V 1",
         "aload_0",
         */
+        "getstatic " + client.name + "/" + obj2.name + " " + itf2.getDescriptor(),
         "invokevirtual java/io/PrintStream/print(Ljava/lang/Object;)V",
         "return"
     );
 
     final String mainClassName = mainClass.name;
-    String proguardConfig = keepMainProguardConfiguration(mainClass.name, false, false);
+    String proguardConfig =
+        keepMainProguardConfiguration(mainClass.name, false, false)
+            // AGP default is to not turn optimizations on, which disables MemberValuePropagation,
+            // resulting in the problematic putstatic being remained.
+            + "-dontoptimize\n";
 
     // Run input program on java.
     Path outputDirectory = temp.newFolder().toPath();
     jasminBuilder.writeClassFiles(outputDirectory);
     ProcessResult javaResult = ToolHelper.runJava(outputDirectory, mainClassName);
     assertEquals(0, javaResult.exitCode);
-    assertThat(javaResult.stdout, containsString(impl.name));
+    assertThat(javaResult.stdout, containsString(impl2.name));
 
     AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
         // Disable inlining to avoid the (short) tested method from being inlined and then removed.
@@ -97,12 +123,12 @@
     // Run processed (output) program on ART
     ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
     assertEquals(0, artResult.exitCode);
-    assertThat(artResult.stdout, containsString(impl.name));
+    assertThat(artResult.stdout, containsString(impl2.name));
     assertEquals(-1, artResult.stderr.indexOf("DoFieldPut"));
 
     DexInspector inspector = new DexInspector(processedApp);
-    ClassSubject itfSubject = inspector.clazz(itf.name);
-    assertThat(itfSubject, isPresent());
+    ClassSubject itf1Subject = inspector.clazz(itf1.name);
+    assertThat(itf1Subject, isPresent());
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index f558ee7..c176ff3 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -149,6 +149,13 @@
     parser = new ProguardConfigurationParser(new DexItemFactory(), reporter);
   }
 
+  @Before
+  public void resetAllowPartiallyImplementedOptions() {
+    handler = new KeepingDiagnosticHandler();
+    reporter = new Reporter(handler);
+    parser = new ProguardConfigurationParser(new DexItemFactory(), reporter, false);
+  }
+
   @Test
   public void parse() throws Exception {
     ProguardConfigurationParser parser;
@@ -859,7 +866,7 @@
   @Test
   public void parseKeepdirectories() throws Exception {
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+        new ProguardConfigurationParser(new DexItemFactory(), reporter, false);
     parser.parse(Paths.get(KEEPDIRECTORIES));
     verifyParserEndsCleanly();
   }
@@ -1216,6 +1223,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_noArguments1() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilenames",
         "-adaptresourcefilecontents",
@@ -1228,6 +1236,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_noArguments2() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-keepdirectories",
         "-adaptresourcefilenames",
@@ -1240,6 +1249,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_noArguments3() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilecontents",
         "-keepdirectories",
@@ -1261,6 +1271,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_singleArgument() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilenames " + FILE_FILTER_SINGLE,
         "-adaptresourcefilecontents " + FILE_FILTER_SINGLE,
@@ -1293,6 +1304,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_multipleArgument() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilenames " + FILE_FILTER_MULTIPLE,
         "-adaptresourcefilecontents " + FILE_FILTER_MULTIPLE,
@@ -1309,7 +1321,7 @@
         "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
     for (String option : options) {
       try {
-        reset();
+        resetAllowPartiallyImplementedOptions();
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " ,")));
         fail("Expect to fail due to the lack of path filter.");
       } catch (AbortException e) {
@@ -1324,7 +1336,7 @@
         "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
     for (String option : options) {
       try {
-        reset();
+        resetAllowPartiallyImplementedOptions();
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,,yyy")));
         fail("Expect to fail due to the lack of path filter.");
       } catch (AbortException e) {
@@ -1339,7 +1351,7 @@
         "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
     for (String option : options) {
       try {
-        reset();
+        resetAllowPartiallyImplementedOptions();
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,")));
         fail("Expect to fail due to the lack of path filter.");
       } catch (AbortException e) {
@@ -1745,6 +1757,24 @@
     verifyWithProguard(proguardConfig);
   }
 
+  public void testNotSupported(String option) {
+    try {
+      reset();
+      parser.parse(createConfigurationForTesting(ImmutableList.of(option)));
+      fail("Expect to fail due to unsupported option.");
+    } catch (AbortException e) {
+      checkDiagnostic(handler.errors, null, 1, 1, "Option " + option + " currently not supported");
+    }
+  }
+
+  @Test
+  public void parse_pariallyImplemented_notSupported() {
+    testNotSupported("-keepdirectories");
+    testNotSupported("-adaptresourcefilenames");
+    testNotSupported("-adaptresourcefilecontents");
+  }
+
+
   private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) {
     parser.parse(createConfigurationForTesting(config));
     verifyParserEndsCleanly();
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
new file mode 100644
index 0000000..7fcb318
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2018, 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.shaking.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.io.ByteStreams;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+public class InliningClassVersionTest extends TestBase {
+
+  private final int OLD_VERSION = Opcodes.V1_6;
+  private final String BASE_DESCRIPTOR = DescriptorUtils.javaTypeToDescriptor(Base.class.getName());
+
+  private static class Base {
+
+    public static void main(String[] args) {
+      System.out.println(Inlinee.foo());
+    }
+  }
+
+  private static class Inlinee {
+    public static String foo() {
+      return "Hello from Inlinee!";
+    }
+  }
+
+  private static class DowngradeVisitor extends ClassVisitor {
+
+    private final int version;
+
+    DowngradeVisitor(ClassVisitor cv, int version) {
+      super(Opcodes.ASM6, cv);
+      this.version = version;
+    }
+
+    @Override
+    public void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] interfaces) {
+      assert version > this.version
+          : "Going from " + version + " to " + this.version + " is not a downgrade";
+      super.visit(this.version, access, name, signature, superName, interfaces);
+    }
+  }
+
+  private static byte[] downgradeClass(byte[] classBytes, int version) {
+    ClassWriter writer = new ClassWriter(0);
+    new ClassReader(classBytes).accept(new DowngradeVisitor(writer, version), 0);
+    return writer.toByteArray();
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path inputJar = writeInput();
+    assertEquals(OLD_VERSION, getBaseClassVersion(inputJar));
+    ProcessResult runInput = run(inputJar);
+    assertEquals(0, runInput.exitCode);
+    Path outputJar = runR8(inputJar);
+    ProcessResult runOutput = run(outputJar);
+    assertEquals(runInput.toString(), runOutput.toString());
+    assertNotEquals(
+        "Inliner did not upgrade classfile version", OLD_VERSION, getBaseClassVersion(outputJar));
+  }
+
+  private int getBaseClassVersion(Path jar) throws Exception {
+    return getClassVersion(jar, BASE_DESCRIPTOR);
+  }
+
+  private int getClassVersion(Path jar, String descriptor) throws Exception {
+
+    class ClassVersionReader extends ClassVisitor {
+      private int version = -1;
+
+      private ClassVersionReader() {
+        super(Opcodes.ASM6);
+      }
+
+      @Override
+      public void visit(
+          int version,
+          int access,
+          String name,
+          String signature,
+          String superName,
+          String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+        assert version != -1;
+        this.version = version;
+      }
+    }
+
+    byte[] bytes =
+        ByteStreams.toByteArray(
+            new ArchiveClassFileProvider(jar).getProgramResource(descriptor).getByteStream());
+    ClassVersionReader reader = new ClassVersionReader();
+    new ClassReader(bytes).accept(reader, 0);
+    assert reader.version != -1;
+    return reader.version;
+  }
+
+  private Path writeInput() throws Exception {
+    Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+    ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(inputJar);
+    consumer.accept(
+        downgradeClass(ToolHelper.getClassAsBytes(Base.class), OLD_VERSION), BASE_DESCRIPTOR, null);
+    consumer.accept(
+        ToolHelper.getClassAsBytes(Inlinee.class),
+        DescriptorUtils.javaTypeToDescriptor(Inlinee.class.getName()),
+        null);
+    consumer.finished(null);
+    return inputJar;
+  }
+
+  private ProcessResult run(Path jar) throws Exception {
+    return ToolHelper.runJava(jar, Base.class.getName());
+  }
+
+  private Path runR8(Path inputJar) throws Exception {
+    List<String> keepRule =
+        Collections.singletonList(
+            "-keep class " + Base.class.getName() + " { public static void main(...); }");
+    Path outputJar = temp.getRoot().toPath().resolve("output.jar");
+    ToolHelper.runR8(
+        R8Command.builder()
+            .addProgramFiles(inputJar)
+            .addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME))
+            .addProguardConfiguration(keepRule, Origin.unknown())
+            .setOutput(outputJar, OutputMode.ClassFile)
+            .build());
+    return outputJar;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 91fc9d4..642045c 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -110,7 +110,7 @@
 
     // The test contains only a member class so the enclosing-method attribute will be null.
     assertEquals(
-        !keepAnnotations && forceProguardCompatibility,
+        forceProguardCompatibility,
         !clazz.getDexClass().getInnerClasses().isEmpty());
     assertEquals(forceProguardCompatibility || keepAnnotations,
         clazz.annotation(annotationClass.getCanonicalName()).isPresent());
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index d578815..cae3b06 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -94,12 +94,13 @@
 
       Result result = runTest(mainClass, proguardConfig);
 
-      // Without includedescriptorclasses return type argument type and field type are removed.
+      // Without includedescriptorclasses return type and argument type are removed.
       result.assertKept(ClassWithNativeMethods.class);
       result.assertRemoved(NativeArgumentType.class);
       result.assertRemoved(NativeReturnType.class);
-      result.assertRemoved(InstanceFieldType.class);
-      result.assertRemoved(StaticFieldType.class);
+      // Field type is not removed due to the concern about the broken type hierarchy.
+      result.assertRenamed(InstanceFieldType.class);
+      result.assertRenamed(StaticFieldType.class);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
index 1fd6afd..3381473 100644
--- a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
@@ -585,7 +585,7 @@
 
   private void notIntMethodBuilder(SmaliBuilder builder, String name, Object parameters) {
     Integer value = (Integer) parameters;
-    builder.addStaticMethod("long", name, Collections.emptyList(),
+    builder.addStaticMethod("int", name, Collections.emptyList(),
         1,
         "    const v0, " + value,
         "    not-int v0, v0",
@@ -950,6 +950,7 @@
     addCmpFloatFoldTests(testBuilder);
     addCmpDoubleFoldTests(testBuilder);
     addCmpLongFold(testBuilder);
+    runDex2Oat(testBuilder.builder.build());
     testBuilder.run();
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java b/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java
new file mode 100644
index 0000000..c92b73b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2018, 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.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class DexMoveInstructionsTest extends SmaliTestBase {
+
+  public static final String CLASS = "Test";
+
+  @Test
+  public void testValidObjectMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToPass", "Ljava/lang/String;", Arrays.asList(
+        "move-object",
+        "move-object/from16",
+        "move-object/16"));
+    assertEquals(result.toString(), 0, result.exitCode);
+  }
+
+  @Test
+  public void testInvalidObjectMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToFail", "Ljava/lang/String;", Arrays.asList(
+        "move",
+        "move/from16",
+        "move/16"));
+    assertEquals(result.toString(), 1, result.exitCode);
+    assertTrue("Did not find 'Verification error' in " + result.stderr,
+        result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+  }
+
+  @Test
+  public void testValidSingleMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToPass", "I", Arrays.asList(
+        "move",
+        "move/from16",
+        "move/16"));
+    assertEquals(result.toString(), 0, result.exitCode);
+  }
+
+  @Test
+  public void testInvalidSingleMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToFail", "I", Arrays.asList(
+        "move-object",
+        "move-object/from16",
+        "move-object/16"));
+    assertEquals(result.toString(), 1, result.exitCode);
+    assertTrue("Did not find 'Verification error' in " + result.stderr,
+        result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+  }
+
+  private ProcessResult testMoves(String clazz, String typeDesc, List<String> moveOps)
+      throws Throwable {
+    String typeName = DescriptorUtils.descriptorToJavaType(typeDesc);
+
+    SmaliBuilder builder = new SmaliBuilder(clazz);
+    int i = 0;
+    for (String moveOp : moveOps) {
+      builder.addStaticMethod(typeName, "test" + i++, Collections.singletonList(typeName),
+          1,
+          "    " + moveOp + " v0, p0",
+          typeDesc.startsWith("L") ? "return-object v0" : "    return v0"
+      );
+    }
+
+    List<String> main = new ArrayList<>();
+    main.add("  sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;");
+    main.add("  const v2, 0");
+    i = 0;
+    for (String moveOp : moveOps) {
+      main.add("  invoke-static { v2 }, L" + clazz + ";->test" + i++
+          + "(" + typeDesc + ")" + typeDesc);
+      if (typeDesc.startsWith("L")) {
+        main.add("  move-result-object v1");
+      } else {
+        main.add("  move-result v1");
+      }
+      main.add("  invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(" + typeDesc + ")V");
+    }
+    main.add("  return-void");
+    builder.addMainMethod(3, main.toArray(new String[0]));
+
+    return runOnArtRaw(builder.build(), clazz);
+  }
+}
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 eb66c51..371c4d1 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -94,7 +94,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -158,7 +158,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test1\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -231,7 +231,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -354,7 +354,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         3,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-wide          v1, 0x7fffffff00000000L",
         "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -430,7 +430,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         3,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-wide          v1, 0x3ff0000000000000L",
         "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -793,7 +793,7 @@
         "method1",
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -809,7 +809,7 @@
         "method2",
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -878,7 +878,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
diff --git a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
index dae717e..5270cd4 100644
--- a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
@@ -6,12 +6,16 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
 import org.junit.Test;
 
 public class RemoveWriteOfUnusedFieldsTest extends SmaliTestBase {
@@ -33,20 +37,28 @@
     builder.addStaticField("stringField", "Ljava/lang/String;");
     builder.addStaticField("testField", "LTest;");
 
+    boolean isDalvik = ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST);
+    String additionalConstZero = isDalvik ? "const v0, 0" : "";
+    String additionalConstZeroWide = isDalvik ? "const-wide v0, 0" : "";
+
     builder.addStaticMethod("void", "test", ImmutableList.of(),
         2,
         "const               v0, 0",
-        "sput-byte           v0, LTest;->booleanField:Z",
+        "sput-boolean        v0, LTest;->booleanField:Z",
         "sput-byte           v0, LTest;->byteField:B",
         "sput-short          v0, LTest;->shortField:S",
         "sput                v0, LTest;->intField:I",
+        // Dalvik 4.x. does not require a new const 0 here.
         "sput                v0, LTest;->floatField:F",
+        additionalConstZero,  // Required for Dalvik 4.x.
         "sput-char           v0, LTest;->charField:C",
+        "const               v0, 0",
         "sput-object         v0, LTest;->objectField:Ljava/lang/Object;",
         "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
         "sput-object         v0, LTest;->testField:LTest;",
         "const-wide          v0, 0",
         "sput-wide           v0, LTest;->longField:J",
+        additionalConstZeroWide,  // Required for Dalvik 4.x.
         "sput-wide           v0, LTest;->doubleField:D",
         "return-void");
 
@@ -55,9 +67,16 @@
         "    invoke-static       { }, LTest;->test()V",
         "    return-void                             ");
 
+    AndroidApp input =
+        AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
+
+    Path inputPath = temp.getRoot().toPath().resolve("input.zip");
+    input.writeToZip(inputPath, OutputMode.DexIndexed);
+    ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
+
     AndroidApp app =
         compileWithR8(
-            AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
+            input,
             keepMainProguardConfiguration("Test"),
             options -> options.enableInlining = false);
 
@@ -84,32 +103,53 @@
     builder.addInstanceField("stringField", "Ljava/lang/String;");
     builder.addInstanceField("testField", "LTest;");
 
+    boolean isDalvik = ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST);
+    String additionalConstZero = isDalvik ? "const v0, 0" : "";
+    String additionalConstZeroWide = isDalvik ? "const-wide v0, 0" : "";
+
     builder.addInstanceMethod("void", "test", ImmutableList.of(),
         2,
         "const               v0, 0",
-        "iput-byte           v0, p0, LTest;->booleanField:Z",
+        "iput-boolean        v0, p0, LTest;->booleanField:Z",
         "iput-byte           v0, p0, LTest;->byteField:B",
         "iput-short          v0, p0, LTest;->shortField:S",
         "iput                v0, p0, LTest;->intField:I",
+        // Dalvik 4.x. does not require a new const 0 here.
         "iput                v0, p0, LTest;->floatField:F",
+        additionalConstZero,  // Required for Dalvik 4.x.
         "iput-char           v0, p0, LTest;->charField:C",
+        "const               v0, 0",
         "iput-object         v0, p0, LTest;->objectField:Ljava/lang/Object;",
         "iput-object         v0, p0, LTest;->stringField:Ljava/lang/String;",
         "iput-object         v0, p0, LTest;->testField:LTest;",
         "const-wide          v0, 0",
         "iput-wide           v0, p0, LTest;->longField:J",
+        additionalConstZeroWide,  // Required for Dalvik 4.x.
         "iput-wide           v0, p0, LTest;->doubleField:D",
         "return-void");
 
+    builder.addInitializer(ImmutableList.of(), 0,
+        "invoke-direct {p0}, Ljava/lang/Object;-><init>()V",
+        "return-void"
+    );
+
     builder.addMainMethod(
         1,
         "    new-instance         v0, LTest;",
+        "    invoke-direct        { v0 }, LTest;-><init>()V",
         "    invoke-virtual       { v0 }, LTest;->test()V",
         "    return-void                             ");
 
+    AndroidApp input =
+        AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
+
+    Path inputPath = temp.getRoot().toPath().resolve("input.zip");
+    input.writeToZip(inputPath, OutputMode.DexIndexed);
+    ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
+
     AndroidApp app =
         compileWithR8(
-            AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
+            input,
             keepMainProguardConfiguration("Test"),
             options -> options.enableInlining = false);
 
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
index 2f6eb51..9a6e778 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.smali;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -334,13 +333,11 @@
     return result;
   }
 
-  public byte[] compile()
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+  public byte[] compile() throws IOException, RecognitionException, ExecutionException {
     return Smali.compile(buildSource());
   }
 
-  public AndroidApp build()
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+  public AndroidApp build() throws IOException, RecognitionException, ExecutionException {
     return AndroidApp.builder().addDexProgramData(compile(), Origin.unknown()).build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
index a380834..2297ebd 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.SmaliWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -27,7 +26,7 @@
       AndroidApp application =
           AndroidApp.builder().addDexProgramData(Smali.compile(smali), Origin.unknown()).build();
       assertEquals(smali, SmaliWriter.smali(application, new InternalOptions()));
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
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 3c101ad..6c4b884 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.OutputMode;
@@ -15,7 +14,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
@@ -46,7 +44,7 @@
   protected AndroidApp buildApplication(SmaliBuilder builder) {
     try {
       return AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
-    } catch (IOException | RecognitionException | DexOverflowException | ExecutionException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -57,7 +55,7 @@
           .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
           .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
           .build();
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -79,7 +77,7 @@
       Consumer<InternalOptions> optionsConsumer) {
     try {
       return ToolHelper.runR8(application, optionsConsumer);
-    } catch (IOException | CompilationException e) {
+    } catch (IOException e) {
       throw new RuntimeException(e);
     }
   }
@@ -105,7 +103,9 @@
           .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE);
       ToolHelper.runR8WithFullResult(command.build(), optionsConsumer);
       return dexOutputDir.resolve("classes.dex");
-    } catch (CompilationException | IOException | RecognitionException | ExecutionException
+    } catch (IOException
+        | RecognitionException
+        | ExecutionException
         | CompilationFailedException e) {
       throw new RuntimeException(e);
     }
@@ -232,13 +232,11 @@
         processdApplication, DEFAULT_CLASS_NAME, returnType, DEFAULT_METHOD_NAME, parameters);
   }
 
-  public String runArt(AndroidApp application) throws DexOverflowException {
+  public String runArt(AndroidApp application) {
     return runArt(application, DEFAULT_MAIN_CLASS_NAME);
   }
 
-
-  public String runArt(AndroidApp application, String mainClass)
-      throws DexOverflowException {
+  public String runArt(AndroidApp application, String mainClass) {
     try {
       Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
       // TODO(sgjesse): Pass in a unique temp directory for each run.
@@ -257,8 +255,7 @@
     }
   }
 
-  public void runDex2Oat(AndroidApp application)
-      throws DexOverflowException {
+  public void runDex2Oat(AndroidApp application) {
     try {
       Path dexOut = temp.getRoot().toPath().resolve("run-dex2oat-input.zip");
       Path oatFile = temp.getRoot().toPath().resolve("oat-file");
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 30aa7ef..68de752 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
@@ -37,27 +36,27 @@
 public class Smali {
 
   public static byte[] compile(String smaliText)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(smaliText, 15);
   }
 
   public static byte[] compile(String... smaliText)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(Arrays.asList(smaliText), 15);
   }
 
   public static byte[] compile(String smaliText, int apiLevel)
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+      throws IOException, RecognitionException, ExecutionException {
     return compile(ImmutableList.of(smaliText), apiLevel);
   }
 
   public static byte[] compile(List<String> smaliTexts)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(smaliTexts, 15);
   }
 
   public static byte[] compile(List<String> smaliTexts, int apiLevel)
-      throws RecognitionException, IOException, ExecutionException, DexOverflowException {
+      throws RecognitionException, IOException, ExecutionException {
     DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(apiLevel));
 
     for (String smaliText : smaliTexts) {
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/d8_api_usage_sample.jar
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index f901b9d..7d503a5 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
new file mode 100755
index 0000000..abaa619
--- /dev/null
+++ b/tools/build_r8lib.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, 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.
+
+'''
+Build r8lib.jar using src/main/keep.txt and test that d8_api_usage_sample.jar
+works with the minified R8.
+'''
+
+import argparse
+import os
+import subprocess
+import toolhelper
+import utils
+
+parser = argparse.ArgumentParser(description=__doc__.strip(),
+                                 formatter_class=argparse.RawTextHelpFormatter)
+
+SAMPLE_JAR = os.path.join(utils.REPO_ROOT, 'tests/d8_api_usage_sample.jar')
+KEEP_RULES = os.path.join(utils.REPO_ROOT, 'src/main/keep.txt')
+R8LIB_JAR = os.path.join(utils.LIBS, 'r8lib.jar')
+R8LIB_MAP_FILE = os.path.join(utils.LIBS, 'r8lib-map.txt')
+
+API_LEVEL = 26
+ANDROID_JAR = 'third_party/android_jar/lib-v%s/android.jar' % API_LEVEL
+
+
+def build_r8lib():
+  toolhelper.run(
+      'r8',
+      ('--release',
+       '--classfile',
+       '--lib', utils.RT_JAR,
+       utils.R8_JAR,
+       '--output', R8LIB_JAR,
+       '--pg-conf', KEEP_RULES,
+       '--pg-map-output', R8LIB_MAP_FILE))
+
+
+def test_d8sample():
+  with utils.TempDir() as path:
+    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+            'com.android.tools.apiusagesample.D8ApiUsageSample',
+            '--output', path,
+            '--min-api', str(API_LEVEL),
+            '--lib', ANDROID_JAR,
+            '--classpath', utils.R8_JAR,
+            '--main-dex-list', '/dev/null',
+            os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+    utils.PrintCmd(args)
+    subprocess.check_call(args)
+
+
+def test_r8command():
+  with utils.TempDir() as path:
+    # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+    # R8CommandParser should have been minified in R8LIB_JAR.
+    # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+    # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+            'com.android.tools.r8.R8CommandParser',
+            '--output', path + "/output.zip",
+            '--min-api', str(API_LEVEL),
+            '--lib', ANDROID_JAR,
+            '--main-dex-list', '/dev/null',
+            os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+    utils.PrintCmd(args)
+    subprocess.check_call(args)
+
+
+def test_r8cfcommand():
+  with utils.TempDir() as path:
+    # SAMPLE_JAR and R8LIB_JAR should not have any classes in common, since e.g.
+    # R8CommandParser should have been minified in R8LIB_JAR.
+    # Just in case R8CommandParser is also present in R8LIB_JAR, we put
+    # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
+    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, R8LIB_JAR),
+            'com.android.tools.r8.R8CommandParser',
+            '--classfile',
+            '--output', path + "/output.jar",
+            '--lib', utils.RT_JAR,
+            os.path.join(utils.BUILD, 'test/examples/hello.jar')]
+    utils.PrintCmd(args)
+    subprocess.check_call(args)
+
+
+def main():
+  # Handle --help
+  parser.parse_args()
+
+  build_r8lib()
+  test_d8sample()
+  test_r8command()
+  test_r8cfcommand()
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 787c1e1..8f440dd 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -27,7 +27,6 @@
 MANIFEST_PATH = 'META-INF/MANIFEST.MF'
 MANIFEST = 'Manifest-Version: 1.0\nMain-Class: %s\n\n'
 MANIFEST_PATTERN = r'Main-Class:\s*(\S+)'
-RT = os.path.join(utils.REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 
 parser = argparse.ArgumentParser(description=__doc__.strip(),
                                  formatter_class=argparse.RawTextHelpFormatter)
@@ -38,7 +37,7 @@
     '-o', '--output-jar',
     help='Path to output JAR (default: build/libs/<MainClass>-min.jar)')
 parser.add_argument(
-    '-l', '--lib', default=RT,
+    '-l', '--lib', default=utils.RT_JAR,
     help='Path to rt.jar to use instead of OpenJDK 1.8')
 parser.add_argument(
     '-m', '--mainclass',
@@ -85,8 +84,8 @@
           'No --mainclass specified and no Main-Class in input JAR manifest.')
     return mo.group(1)
 
-def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, lib=RT,
-                debug=True, build=True, benchmark_name=None):
+def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None,
+                lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None):
   if output_jar is None:
     output_jar = generate_output_name(input_jar, mainclass)
   with utils.TempDir() as path:
diff --git a/tools/utils.py b/tools/utils.py
index 9f6959f..3aab572 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -35,6 +35,7 @@
 COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')
 MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
 GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
+RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 
 def PrintCmd(s):
   if type(s) is list: