Merge "Update patch version for release - new version is 1.4.14-dev"
diff --git a/.gitignore b/.gitignore
index cb3eaab..22b29da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,8 @@
 tools/*/art-7.0.0.tar.gz
 tools/*/art-8.1.0
 tools/*/art-8.1.0.tar.gz
+tools/*/art-9.0.0
+tools/*/art-9.0.0.tar.gz
 tools/*/dalvik
 tools/*/dalvik.tar.gz
 tools/*/dalvik-4.0.4
diff --git a/build.gradle b/build.gradle
index 5ec2f08..ec00f11 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,7 +18,7 @@
     gsonVersion = '2.7'
     junitVersion = '4.12'
     kotlinVersion = '1.2.30'
-    kotlinExtMetadataJVMVersion = '0.0.3'
+    kotlinExtMetadataJVMVersion = '0.0.4'
     smaliVersion = '2.2b4'
 }
 
@@ -246,6 +246,7 @@
     supportLibs "junit:junit:$junitVersion"
     supportLibs "com.android.support.test.espresso:espresso-core:$espressoVersion"
     apiUsageSampleCompile sourceSets.main.output
+    apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
     debugTestResourcesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     examplesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
@@ -297,6 +298,7 @@
                 "linux/art-6.0.1",
                 "linux/art-7.0.0",
                 "linux/art-8.1.0",
+                "linux/art-9.0.0",
                 "linux/dalvik",
                 "linux/dalvik-4.0.4",
                 "${osString}/dx",
@@ -309,8 +311,8 @@
             def outputDir = "${entry.key}/${entryFile}"
             def gzFile = "${outputDir}.tar.gz"
             def sha1File = "${gzFile}.sha1"
-            inputs.file sha1File
-            outputs.file gzFile
+            // Make the output file part of the input dependencies explictly.
+            inputs.files files(sha1File, gzFile)
             outputs.dir outputDir
             List<String> dlFromStorageArgs = ["-n", "-b", "r8-deps", "-u", "-s", "${sha1File}"]
             if (OperatingSystem.current().isWindows()) {
@@ -376,6 +378,16 @@
     }
 }
 
+task downloadOpenJDKrt {
+   cloudDependencies.each { entry ->
+        entry.value.each { entryFile ->
+            if (entryFile.contains("openjdk-rt")) {
+                dependsOn "download_deps_${entry.key}/${entryFile}"
+            }
+        }
+    }
+}
+
 task downloadDx {
     cloudDependencies.each { entry ->
         entry.value.each { entryFile ->
@@ -581,6 +593,59 @@
     }
 }
 
+def r8CfCommandLine(input, output, pgconf) {
+    return ["java", "-ea", "-jar", R8.outputs.files[0],
+        "--classfile", "--release",
+        input,
+        "--lib", "third_party/openjdk/openjdk-rt-1.8/rt.jar",
+        "--output", output,
+        "--pg-conf", pgconf]
+}
+
+task R8R8(type: Exec) {
+    def pgconf = "src/main/keep.txt"
+    def output = "build/libs/r8-r8.jar"
+    inputs.files files(pgconf, "build/libs/r8.jar")
+    outputs.file output
+    dependsOn R8
+    dependsOn downloadOpenJDKrt
+    commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+    workingDir = projectDir
+}
+
+task D8R8(type: Exec) {
+    def pgconf = "src/main/keep.txt"
+    def output = "build/libs/d8-r8.jar"
+    inputs.files files(pgconf, "build/libs/d8.jar")
+    outputs.file output
+    dependsOn downloadOpenJDKrt
+    dependsOn D8
+    commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+    workingDir = projectDir
+}
+
+task CompatDxR8(type: Exec) {
+    def pgconf = "src/main/keep-compatdx.txt"
+    def output = "build/libs/compatdx-r8.jar"
+    inputs.files files(pgconf, "build/libs/compatdx.jar")
+    outputs.file output
+    dependsOn CompatDx
+    dependsOn downloadOpenJDKrt
+    commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+    workingDir = projectDir
+}
+
+task CompatProguardR8(type: Exec) {
+    def pgconf = "src/main/keep-compatproguard.txt"
+    def output = "build/libs/compatproguard-r8.jar"
+    inputs.files files(pgconf, "build/libs/compatproguard.jar")
+    outputs.file output
+    dependsOn CompatProguard
+    dependsOn downloadOpenJDKrt
+    commandLine r8CfCommandLine(R8.outputs.files[0], output, pgconf)
+    workingDir = projectDir
+}
+
 task sourceJar(type: Jar, dependsOn: classes) {
     classifier = 'src'
     from sourceSets.main.allSource
diff --git a/scripts/update-host-art.sh b/scripts/update-host-art.sh
index 8bfc753..fb45744 100755
--- a/scripts/update-host-art.sh
+++ b/scripts/update-host-art.sh
@@ -130,6 +130,23 @@
 mkdir -p $DEST/usr/icu
 cp -r $ANDROID_HOST_BUILD/usr/icu/* $DEST/usr/icu
 
+# Update links for vdex files for Android P and later.
+if [ -f $DEST/product/$ANDROID_PRODUCT/system/framework/boot.vdex ]; then
+  for VDEXFILE in $DEST/product/$ANDROID_PRODUCT/system/framework/*.vdex; do
+    VDEXNAME=$(basename ${VDEXFILE});
+    for ARCH in arm arm64; do
+      rm $DEST/product/$ANDROID_PRODUCT/system/framework/$ARCH/${VDEXNAME};
+      # This relative link command will create a symbolic link of the form
+      # ../${VDEXNAME} for each architecture.
+      # ln -r -s $DEST/product/$ANDROID_PRODUCT/system/framework/${VDEXNAME} $DEST/product/$ANDROID_PRODUCT/system/framework/$ARCH/${VDEXNAME};
+      # The Cloud Storage dependency tool (download_from_google_storage.py) does
+      # not allow synlinks at all, so instad of the ln in the comment above just
+      # copy the ${VDEXNAME} files.
+      cp $DEST/product/$ANDROID_PRODUCT/system/framework/${VDEXNAME} $DEST/product/$ANDROID_PRODUCT/system/framework/$ARCH/${VDEXNAME};
+    done
+  done
+fi
+
 # Allow failure for strip commands below.
 set +e
 
diff --git a/src/main/java/com/android/tools/r8/ApiLevelException.java b/src/main/java/com/android/tools/r8/ApiLevelException.java
index 6d110a4..0ad75c7 100644
--- a/src/main/java/com/android/tools/r8/ApiLevelException.java
+++ b/src/main/java/com/android/tools/r8/ApiLevelException.java
@@ -6,9 +6,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.AndroidApiLevel;
 
-/**
- * Exception to signal features that are not supported until a given API level.
- */
+/** Exception to signal features that are not supported until a given API level. */
 public class ApiLevelException extends CompilationError {
 
   public ApiLevelException(
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index b84b398..00157f5 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -3,9 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.utils.FileUtils.isArchive;
-
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -24,6 +21,17 @@
 
 public class D8CommandParser extends BaseCompilerCommandParser {
 
+  private static final String APK_EXTENSION = ".apk";
+  private static final String JAR_EXTENSION = ".jar";
+  private static final String ZIP_EXTENSION = ".zip";
+
+  private static boolean isArchive(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(APK_EXTENSION)
+        || name.endsWith(JAR_EXTENSION)
+        || name.endsWith(ZIP_EXTENSION);
+  }
+
   static class OrderedClassFileResourceProvider implements ClassFileResourceProvider {
     static class Builder {
       private final ImmutableList.Builder<ClassFileResourceProvider> builder =
@@ -143,101 +151,98 @@
     OrderedClassFileResourceProvider.Builder classpathBuilder =
         OrderedClassFileResourceProvider.builder();
     String[] expandedArgs = FlagFile.expandFlagFiles(args, builder);
-    try {
-      for (int i = 0; i < expandedArgs.length; i++) {
-        String arg = expandedArgs[i].trim();
-        if (arg.length() == 0) {
+    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;
-        } 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.DexFilePerClass;
-        } 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")) {
-          Path file = Paths.get(expandedArgs[++i]);
-          try {
-            if (!Files.exists(file)) {
-              throw new NoSuchFileException(file.toString());
-            }
-            if (isArchive(file)) {
-              classpathBuilder.addClassFileResourceProvider(new ArchiveClassFileProvider(file));
-            } else if (Files.isDirectory(file)) {
-              classpathBuilder.addClassFileResourceProvider(
-                  DirectoryClassFileProvider.fromDirectory(file));
-            } else {
-              throw new CompilationError("Unsupported classpath file type", new PathOrigin(file));
-            }
-          } catch (IOException e) {
-            builder.error(new ExceptionDiagnostic(e, new PathOrigin(file)));
-          }
-        } 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("--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));
         }
+        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.DexFilePerClass;
+      } 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")) {
+        Path file = Paths.get(expandedArgs[++i]);
+        try {
+          if (!Files.exists(file)) {
+            throw new NoSuchFileException(file.toString());
+          }
+          if (isArchive(file)) {
+            classpathBuilder.addClassFileResourceProvider(new ArchiveClassFileProvider(file));
+          } else if (Files.isDirectory(file)) {
+            classpathBuilder.addClassFileResourceProvider(
+                DirectoryClassFileProvider.fromDirectory(file));
+          } else {
+            builder.error(
+                new StringDiagnostic("Unsupported classpath file type", new PathOrigin(file)));
+          }
+        } catch (IOException e) {
+          builder.error(new ExceptionDiagnostic(e, new PathOrigin(file)));
+        }
+      } 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("--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 (!classpathBuilder.isEmpty()) {
-        builder.addClasspathResourceProvider(classpathBuilder.build());
-      }
-      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);
     }
+    if (!classpathBuilder.isEmpty()) {
+      builder.addClasspathResourceProvider(classpathBuilder.build());
+    }
+    if (compilationMode != null) {
+      builder.setMode(compilationMode);
+    }
+    if (outputMode == null) {
+      outputMode = OutputMode.DexIndexed;
+    }
+    if (outputPath == null) {
+      outputPath = Paths.get(".");
+    }
+    return builder.setOutput(outputPath, outputMode);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index e235e67..118df2e 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -160,7 +160,7 @@
     public void accept(
         int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
       super.accept(fileIndex, data, descriptors, handler);
-      outputBuilder.addIndexedClassFile(fileIndex, getDexFileName(fileIndex), data, handler);
+      outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/errors/CompilationError.java b/src/main/java/com/android/tools/r8/errors/CompilationError.java
index 1104b29..466bf8a 100644
--- a/src/main/java/com/android/tools/r8/errors/CompilationError.java
+++ b/src/main/java/com/android/tools/r8/errors/CompilationError.java
@@ -9,9 +9,9 @@
 
 /**
  * Exception to signal an compilation error.
- * <p>
- * This is always an expected error and considered a user input issue. A user-understandable message
- * must be provided.
+ *
+ * <p>This is always an expected error and considered a user input issue. A user-understandable
+ * message must be provided.
  */
 public class CompilationError extends RuntimeException implements Diagnostic {
 
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index afbb13f..ebeae02 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -9,7 +9,7 @@
 import java.util.function.BooleanSupplier;
 
 /** Access flags common to classes, methods and fields. */
-public abstract class AccessFlags {
+public abstract class AccessFlags<T extends AccessFlags<T>> {
 
   protected static final int BASE_FLAGS
       = Constants.ACC_PUBLIC
@@ -46,26 +46,43 @@
   }
 
   protected int flags;
+  protected boolean isPublicized = false;
 
   protected AccessFlags(int flags) {
     this.flags = flags;
   }
 
+  public abstract T copy();
+
+  public abstract T self();
+
+  public int materialize() {
+    if (isPromotedToPublic()) {
+      return ((flags | Constants.ACC_PUBLIC) & ~Constants.ACC_PROTECTED) & ~Constants.ACC_PRIVATE;
+    }
+    return flags;
+  }
+
   public abstract int getAsCfAccessFlags();
 
   public abstract int getAsDexAccessFlags();
 
+  public final int getOriginalCfAccessFlags() {
+    return flags;
+  }
+
   @Override
-  public boolean equals(Object other) {
-    if (other instanceof AccessFlags) {
-      return flags == ((AccessFlags) other).flags;
+  public boolean equals(Object object) {
+    if (object instanceof AccessFlags) {
+      AccessFlags other = (AccessFlags) object;
+      return flags == other.flags && isPublicized == other.isPublicized;
     }
     return false;
   }
 
   @Override
   public int hashCode() {
-    return flags;
+    return (flags << 1) | (isPublicized ? 1 : 0);
   }
 
   public boolean isMoreVisibleThan(AccessFlags other) {
@@ -92,7 +109,7 @@
   }
 
   public boolean isPublic() {
-    return isSet(Constants.ACC_PUBLIC);
+    return isSet(Constants.ACC_PUBLIC) || isPromotedToPublic();
   }
 
   public void setPublic() {
@@ -105,7 +122,7 @@
   }
 
   public boolean isPrivate() {
-    return isSet(Constants.ACC_PRIVATE);
+    return isSet(Constants.ACC_PRIVATE) && !isPromotedToPublic();
   }
 
   public void setPrivate() {
@@ -118,7 +135,7 @@
   }
 
   public boolean isProtected() {
-    return isSet(Constants.ACC_PROTECTED);
+    return isSet(Constants.ACC_PROTECTED) && !isPromotedToPublic();
   }
 
   public void setProtected() {
@@ -162,10 +179,21 @@
     unset(Constants.ACC_SYNTHETIC);
   }
 
+  public boolean isPromotedToPublic() {
+    return isPublicized;
+  }
+
+  public T setPromotedToPublic(boolean isPublicized) {
+    this.isPublicized = isPublicized;
+    return self();
+  }
+
   public void promoteToPublic() {
-    unsetProtected();
-    unsetPrivate();
-    setPublic();
+    isPublicized = true;
+  }
+
+  public void unsetPromotedToPublic() {
+    isPublicized = false;
   }
 
   protected boolean isSet(int flag) {
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 44d6726..6c7caf9 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -8,7 +8,7 @@
 import java.util.List;
 import java.util.function.BooleanSupplier;
 
-public class ClassAccessFlags extends AccessFlags {
+public class ClassAccessFlags extends AccessFlags<ClassAccessFlags> {
 
   // List of valid flags for both DEX and Java.
   private static final int SHARED_FLAGS
@@ -68,24 +68,30 @@
     return new ClassAccessFlags(access & CF_FLAGS);
   }
 
+  @Override
   public ClassAccessFlags copy() {
-    return new ClassAccessFlags(flags);
+    return new ClassAccessFlags(flags).setPromotedToPublic(isPromotedToPublic());
+  }
+
+  @Override
+  public ClassAccessFlags self() {
+    return this;
   }
 
   @Override
   public int getAsDexAccessFlags() {
     // We unset the super flag here, as it is meaningless in DEX. Furthermore, we add missing
     // abstract to interfaces to work around a javac bug when generating package-info classes.
+    int flags = materialize() & ~Constants.ACC_SUPER;
     if (isInterface()) {
-      return (flags & ~Constants.ACC_SUPER) | Constants.ACC_ABSTRACT;
-    } else {
-      return flags & ~Constants.ACC_SUPER;
+      return flags | Constants.ACC_ABSTRACT;
     }
+    return flags;
   }
 
   @Override
   public int getAsCfAccessFlags() {
-    return flags;
+    return materialize();
   }
 
   /**
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 46738e2..79ef9d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -211,7 +211,7 @@
 
   public boolean isPublicized() {
     checkIfObsolete();
-    return optimizationInfo.isPublicized();
+    return accessFlags.isPromotedToPublic();
   }
 
   public boolean isPublicMethod() {
@@ -889,11 +889,6 @@
     }
 
     @Override
-    public boolean isPublicized() {
-      return NOT_PUBLICZED;
-    }
-
-    @Override
     public boolean isInitializerEnablingJavaAssertions() {
       return UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS;
     }
@@ -1044,11 +1039,6 @@
     }
 
     @Override
-    public boolean isPublicized() {
-      return publicized;
-    }
-
-    @Override
     public boolean isInitializerEnablingJavaAssertions() {
       return initializerEnablingJavaAssertions;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index e9966f5..1c04f9d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -993,13 +993,14 @@
 
   public ReferenceTypeLatticeElement createReferenceTypeLatticeElement(
       DexType type, boolean isNullable, AppInfo appInfo) {
+    ReferenceTypeLatticeElement typeLattice = referenceTypeLatticeElements.get(type);
+    if (typeLattice != null) {
+      return isNullable == typeLattice.isNullable() ? typeLattice
+          : typeLattice.getOrCreateDualLattice();
+    }
     synchronized (type) {
-      ReferenceTypeLatticeElement typeLattice = referenceTypeLatticeElements.get(type);
-      if (typeLattice != null) {
-        typeLattice = isNullable == typeLattice.isNullable() ? typeLattice
-            : typeLattice.getOrCreateDualLattice();
-        assert typeLattice.isNullable() == isNullable;
-      } else {
+      typeLattice = referenceTypeLatticeElements.get(type);
+      if (typeLattice == null) {
         if (type.isClassType()) {
           if (!type.isUnknown() && type.isInterface()) {
             typeLattice = new ClassTypeLatticeElement(
@@ -1020,9 +1021,11 @@
         }
         referenceTypeLatticeElements.put(type, typeLattice);
       }
-      assert typeLattice.isNullable() == isNullable;
-      return typeLattice;
     }
+    // The call to getOrCreateDualLattice can't be under the DexType synchronized block, since that
+    // can create deadlocks with ClassTypeLatticeElement::getInterfaces (both lock on the lattice).
+    return isNullable == typeLattice.isNullable() ? typeLattice
+        : typeLattice.getOrCreateDualLattice();
   }
 
   private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
index b56e60f..e940269 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
@@ -8,7 +8,7 @@
 import java.util.List;
 import java.util.function.BooleanSupplier;
 
-public class FieldAccessFlags extends AccessFlags {
+public class FieldAccessFlags extends AccessFlags<FieldAccessFlags> {
 
   private static final int FLAGS
       = AccessFlags.BASE_FLAGS
@@ -40,8 +40,14 @@
     super(flags);
   }
 
+  @Override
   public FieldAccessFlags copy() {
-    return new FieldAccessFlags(flags);
+    return new FieldAccessFlags(flags).setPromotedToPublic(isPromotedToPublic());
+  }
+
+  @Override
+  public FieldAccessFlags self() {
+    return this;
   }
 
   public static FieldAccessFlags fromSharedAccessFlags(int access) {
@@ -59,12 +65,12 @@
 
   @Override
   public int getAsCfAccessFlags() {
-    return flags;
+    return materialize();
   }
 
   @Override
   public int getAsDexAccessFlags() {
-    return flags;
+    return materialize();
   }
 
   public boolean isVolatile() {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 37a6fac..ca507b7 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -8,7 +8,7 @@
 import java.util.List;
 import java.util.function.BooleanSupplier;
 
-public class MethodAccessFlags extends AccessFlags {
+public class MethodAccessFlags extends AccessFlags<MethodAccessFlags> {
 
   private static final int SHARED_FLAGS
       = AccessFlags.BASE_FLAGS
@@ -57,8 +57,14 @@
     super(flags);
   }
 
+  @Override
   public MethodAccessFlags copy() {
-    return new MethodAccessFlags(flags);
+    return new MethodAccessFlags(flags).setPromotedToPublic(isPromotedToPublic());
+  }
+
+  @Override
+  public MethodAccessFlags self() {
+    return this;
   }
 
   public static MethodAccessFlags fromSharedAccessFlags(int access, boolean isConstructor) {
@@ -88,12 +94,12 @@
       copy.unsetSynchronized();
       copy.setDeclaredSynchronized();
     }
-    return copy.flags;
+    return copy.materialize();
   }
 
   @Override
   public int getAsCfAccessFlags() {
-    return flags & ~Constants.ACC_CONSTRUCTOR;
+    return materialize() & ~Constants.ACC_CONSTRUCTOR;
   }
 
   public boolean isSynchronized() {
diff --git a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
index 4fc81d0..1a58eba 100644
--- a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
@@ -41,8 +41,6 @@
 
   long getReturnedConstant();
 
-  boolean isPublicized();
-
   boolean forceInline();
 
   boolean neverInline();
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 20c04ab..3d1f080 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
@@ -509,6 +509,8 @@
     // 2) Perform outlining for the collected candidates.
     // Ideally, we should outline eagerly when threshold for a template has been reached.
 
+    printPhase("Primary optimization pass");
+
     // Process the application identifying outlining candidates.
     OptimizationFeedbackDelayed feedback = new OptimizationFeedbackDelayed();
     {
@@ -533,30 +535,39 @@
 
     // TODO(b/112831361): Implement support for staticizeClasses in CF backend.
     if (!options.isGeneratingClassFiles()) {
+      printPhase("Class staticizer post processing");
       staticizeClasses(feedback, executorService);
     }
 
     // Second inlining pass for dealing with double inline callers.
     if (inliner != null) {
+      printPhase("Double caller inlining");
       // Use direct feedback still, since methods after inlining may
       // change their status or other properties.
       inliner.processDoubleInlineCallers(this, feedback);
     }
 
+    printPhase("Lambda class synthesis");
     synthesizeLambdaClasses(builder, executorService);
-    desugarInterfaceMethods(builder, IncludeAllResources, executorService);
-    synthesizeTwrCloseResourceUtilityClass(builder);
 
+    printPhase("Interface method desugaring");
+    desugarInterfaceMethods(builder, IncludeAllResources, executorService);
+
+    printPhase("Twr close resource utility class synthesis");
+    synthesizeTwrCloseResourceUtilityClass(builder);
     handleSynthesizedClassMapping(builder);
+
+    printPhase("Lambda merging finalization");
     finalizeLambdaMerging(application, feedback, builder, executorService);
 
     if (outliner != null) {
+      printPhase("Outlining");
       timing.begin("IR conversion phase 2");
       if (outliner.selectMethodsForOutlining()) {
         forEachSelectedOutliningMethod(
             executorService,
             (code, method) -> {
-              printMethod(code, "IR before outlining (SSA)");
+              printMethod(code, "IR before outlining (SSA)", null);
               outliner.identifyOutlineSites(code, method);
             });
         DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
@@ -565,7 +576,7 @@
             executorService,
             (code, method) -> {
               outliner.applyOutliningCandidate(code, method);
-              printMethod(code, "IR after outlining (SSA)");
+              printMethod(code, "IR after outlining (SSA)", null);
               finalizeIR(method, code, ignoreOptimizationFeedback);
             });
         assert outliner.checkAllOutlineSitesFoundAgain();
@@ -831,7 +842,7 @@
     }
     // Compilation header if printing CFGs for this method.
     printC1VisualizerHeader(method);
-    printMethod(code, "Initial IR (SSA)");
+    String previous = printMethod(code, "Initial IR (SSA)", null);
 
     if (method.getCode() != null && method.getCode().isJarCode()) {
       computeKotlinNonNullParamHints(feedback, method, code);
@@ -879,6 +890,8 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after class staticizer (SSA)", previous);
+
     if (identifierNameStringMarker != null) {
       identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(method, code);
       assert code.isConsistentSSA();
@@ -903,6 +916,8 @@
       inliner.performInlining(method, code, isProcessedConcurrently, callSiteInformation);
     }
 
+    previous = printMethod(code, "IR after inlining (SSA)", previous);
+
     if (appInfo.hasLiveness()) {
       // Reflection optimization 1. getClass() -> const-class
       ReflectionOptimizer.rewriteGetClass(appInfo.withLiveness(), code);
@@ -982,8 +997,12 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after lambda desugaring (SSA)", previous);
+
     assert code.verifyTypes(appInfo, appView, graphLense());
 
+    previous = printMethod(code, "IR before class inlining (SSA)", previous);
+
     if (classInliner != null) {
       // Class inliner should work before lambda merger, so if it inlines the
       // lambda, it does not get collected by merger.
@@ -1000,25 +1019,35 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after class inlining (SSA)", previous);
+
     if (interfaceMethodRewriter != null) {
       interfaceMethodRewriter.rewriteMethodReferences(method, code);
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
+
     if (twrCloseResourceRewriter != null) {
       twrCloseResourceRewriter.rewriteMethodCode(code);
     }
 
+    previous = printMethod(code, "IR after twr close resource rewriter (SSA)", previous);
+
     if (lambdaMerger != null) {
       lambdaMerger.processMethodCode(method, code);
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after lambda merger (SSA)", previous);
+
     if (options.outline.enabled) {
       outlineHandler.accept(code, method);
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after outline handler (SSA)", previous);
+
     // TODO(mkroghj) Test if shorten live ranges is worth it.
     if (!options.isGeneratingClassFiles()) {
       ConstantCanonicalizer.canonicalize(code);
@@ -1027,10 +1056,14 @@
     }
     idempotentFunctionCallCanonicalizer.canonicalize(code);
 
+    previous =
+        printMethod(code, "IR after idempotent function call canonicalization (SSA)", previous);
+
     codeRewriter.identifyReturnsArgument(method, code, feedback);
     if (options.enableInlining && inliner != null) {
       codeRewriter.identifyInvokeSemanticsForInlining(method, code, feedback);
     }
+
     // If hints from Kotlin metadata or use of Kotlin Intrinsics were not available, track usage of
     // parameters and compute their nullability.
     if (method.getOptimizationInfo().getNonNullParamHints() == null) {
@@ -1043,8 +1076,13 @@
       assert code.isConsistentSSA();
     }
 
+    previous = printMethod(code, "IR after argument type logging (SSA)", previous);
+
     // Analysis must be done after method is rewritten by logArgumentTypes()
     codeRewriter.identifyClassInlinerEligibility(method, code, feedback);
+
+    previous = printMethod(code, "IR after class inliner eligibility (SSA)", previous);
+
     if (method.isInstanceInitializer() || method.isClassInitializer()) {
       codeRewriter.identifyTrivialInitializer(method, code, feedback);
     }
@@ -1057,7 +1095,7 @@
       codeRewriter.workaroundNumberConversionRegisterAllocationBug(code);
     }
 
-    printMethod(code, "Optimized IR (SSA)");
+    printMethod(code, "Optimized IR (SSA)", previous);
     finalizeIR(method, code, feedback);
   }
 
@@ -1174,7 +1212,7 @@
       Log.debug(getClass(), "Resulting dex code for %s:\n%s",
           method.toSourceString(), logCode(options, method));
     }
-    printMethod(code, "Final IR (non-SSA)");
+    printMethod(code, "Final IR (non-SSA)", null);
     markProcessed(method, code, feedback);
   }
 
@@ -1213,7 +1251,7 @@
     if (options.canHaveExceptionTargetingLoopHeaderBug()) {
       codeRewriter.workaroundExceptionTargetingLoopHeaderBug(code);
     }
-    printMethod(code, "After register allocation (non-SSA)");
+    printMethod(code, "After register allocation (non-SSA)", null);
     for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
       CodeRewriter.collapseTrivialGotos(method, code);
       PeepholeOptimizer.optimize(code, registerAllocator);
@@ -1399,7 +1437,13 @@
     }
   }
 
-  private void printMethod(IRCode code, String title) {
+  private void printPhase(String phase) {
+    if (!options.extensiveLoggingFilter.isEmpty()) {
+      System.out.println("Entering phase: " + phase);
+    }
+  }
+
+  private String printMethod(IRCode code, String title, String previous) {
     if (printer != null) {
       printer.resetUnusedValue();
       printer.begin("cfg");
@@ -1407,5 +1451,20 @@
       code.print(printer);
       printer.end("cfg");
     }
+    if (options.extensiveLoggingFilter.contains(code.method.method.toSourceString())) {
+      String current = code.toString();
+      System.out.println();
+      System.out.println("-----------------------------------------------------------------------");
+      System.out.println(title);
+      System.out.println("-----------------------------------------------------------------------");
+      if (previous != null && previous.equals(current)) {
+        System.out.println("Unchanged");
+      } else {
+        System.out.println(current);
+      }
+      System.out.println("-----------------------------------------------------------------------");
+      return current;
+    }
+    return previous;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 35cc716..ade48f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -399,7 +399,11 @@
         continue;
       }
 
-      throw new Unreachable("Unexpected usage left after method inlining: " + user);
+      throw new Unreachable(
+          "Unexpected usage left in method `"
+              + method.method.toSourceString()
+              + "` after inlining: "
+              + user);
     }
 
     if (needToRemoveUnreachableBlocks) {
@@ -423,7 +427,11 @@
         continue;
       }
 
-      throw new Unreachable("Unexpected usage left after method inlining: " + user);
+      throw new Unreachable(
+          "Unexpected usage left in method `"
+              + method.method.toSourceString()
+              + "` after inlining: "
+              + user);
     }
   }
 
@@ -448,10 +456,18 @@
   private void removeFieldWrites() {
     for (Instruction user : eligibleInstance.uniqueUsers()) {
       if (!user.isInstancePut()) {
-        throw new Unreachable("Unexpected usage left after field reads removed: " + user);
+        throw new Unreachable(
+            "Unexpected usage left in method `"
+                + method.method.toSourceString()
+                + "` after field reads removed: "
+                + user);
       }
       if (user.asInstancePut().getField().clazz != eligibleClass) {
-        throw new Unreachable("Unexpected field write left after field reads removed: " + user);
+        throw new Unreachable(
+            "Unexpected field write left in method `"
+                + method.method.toSourceString()
+                + "` after field reads removed: "
+                + user);
       }
       removeInstruction(user);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 605fdf9..8469004 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -53,7 +53,7 @@
     DexEncodedMethod mainMethod = null;
 
     for (DexEncodedMethod method : lambda.virtualMethods()) {
-      if (method.accessFlags.equals(MAIN_METHOD_FLAGS)) {
+      if (method.accessFlags.materialize() == MAIN_METHOD_FLAGS.materialize()) {
         if (mainMethod != null) {
           throw new LambdaStructureError("more than one main method found");
         }
@@ -208,7 +208,7 @@
   static <T extends AccessFlags> void checkAccessFlags(
       String message, T actual, T... expected) throws LambdaStructureError {
     for (T flag : expected) {
-      if (flag.equals(actual)) {
+      if (flag.materialize() == actual.materialize()) {
         return;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java b/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java
index 21b9409..20621f9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java
+++ b/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java
@@ -69,8 +69,10 @@
       return new JvmFunctionExtensionVisitor() {
         @Override
         public void visit(JvmMethodSignature desc) {
-          name = desc.getName();
-          descriptor = desc.getDesc();
+          if (desc != null) {
+            name = desc.getName();
+            descriptor = desc.getDesc();
+          }
         }
       };
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index d39f81b..b59e3e9 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -7,36 +7,32 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.ir.optimize.MethodPoolCollection;
 import com.android.tools.r8.optimize.PublicizerLense.PublicizedLenseBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Equivalence;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
 public final class ClassAndMemberPublicizer {
+
   private final DexApplication application;
   private final AppView appView;
   private final RootSet rootSet;
-  private final PublicizedLenseBuilder lenseBuilder;
-
-  private final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
   private final MethodPoolCollection methodPoolCollection;
 
+  private final PublicizedLenseBuilder lenseBuilder = PublicizerLense.createBuilder();
+
   private ClassAndMemberPublicizer(DexApplication application, AppView appView, RootSet rootSet) {
     this.application = application;
     this.appView = appView;
     this.methodPoolCollection = new MethodPoolCollection(application);
     this.rootSet = rootSet;
-    lenseBuilder = PublicizerLense.createBuilder();
   }
 
   /**
@@ -99,15 +95,13 @@
     }
 
     if (!accessFlags.isPrivate()) {
-      accessFlags.unsetProtected();
-      accessFlags.setPublic();
+      accessFlags.promoteToPublic();
       return false;
     }
     assert accessFlags.isPrivate();
 
     if (appView.dexItemFactory().isConstructor(encodedMethod.method)) {
-      accessFlags.unsetPrivate();
-      accessFlags.setPublic();
+      accessFlags.promoteToPublic();
       return false;
     }
 
@@ -134,9 +128,8 @@
         return false;
       }
       lenseBuilder.add(encodedMethod.method);
-      accessFlags.unsetPrivate();
       accessFlags.setFinal();
-      accessFlags.setPublic();
+      accessFlags.promoteToPublic();
       // Although the current method became public, it surely has the single virtual target.
       encodedMethod.method.setSingleVirtualMethodCache(
           encodedMethod.method.getHolder(), encodedMethod);
@@ -148,8 +141,7 @@
     // even though JLS prevents from declaring static method in derived class if
     // an instance method with same signature exists in superclass, JVM actually
     // does not take into account access of the static methods.
-    accessFlags.unsetPrivate();
-    accessFlags.setPublic();
+    accessFlags.promoteToPublic();
     return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
index b11393a..e51b38d 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
@@ -18,7 +18,7 @@
   private final AppView appView;
   private final Set<DexMethod> publicizedMethods;
 
-  PublicizerLense(AppView appView, Set<DexMethod> publicizedMethods) {
+  private PublicizerLense(AppView appView, Set<DexMethod> publicizedMethods) {
     // This lense does not map any DexItem's at all.
     // It will just tweak invoke type for publicized methods from invoke-direct to invoke-virtual.
     super(
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index eec636a..9d129d6 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -69,15 +69,17 @@
         continue;
       }
       if (isAnnotation(dexType) && isAnnotationWithEnum(dexType)) {
-        addDirectDependencyOrRuntimeAnnotationsWithEnum(dexType);
+        addAnnotationsWithEnum(clazz);
         continue;
       }
+      // Classes with annotations must be in the same dex file as the annotation. As all
+      // annotations with enums goes into the main dex, move annotated classes there as well.
       clazz.forEachAnnotation(
           annotation -> {
             if (!mainDexClassesBuilder.contains(dexType)
                 && annotation.visibility == DexAnnotation.VISIBILITY_RUNTIME
                 && isAnnotationWithEnum(annotation.annotation.type)) {
-              addDirectDependencyOrRuntimeAnnotationsWithEnum(dexType);
+              addClassAnnotatedWithAnnotationWithEnum(dexType);
             }
           });
     }
@@ -89,9 +91,9 @@
       DexClass clazz = appInfo.definitionFor(dexType);
       if (clazz == null) {
         // Information is missing lets be conservative.
-        value = Boolean.TRUE;
+        value = true;
       } else {
-        value = Boolean.FALSE;
+        value = false;
         // Browse annotation values types in search for enum.
         // Each annotation value is represented by a virtual method.
         for (DexEncodedMethod method : clazz.virtualMethods()) {
@@ -99,10 +101,10 @@
           if (proto.parameters.isEmpty()) {
             DexType valueType = proto.returnType.toBaseType(appInfo.dexItemFactory);
             if (isEnum(valueType)) {
-              value = Boolean.TRUE;
+              value = true;
               break;
             } else if (isAnnotation(valueType) && isAnnotationWithEnum(valueType)) {
-              value = Boolean.TRUE;
+              value = true;
               break;
             }
           }
@@ -110,7 +112,7 @@
       }
       annotationTypeContainEnum.put(dexType, value);
     }
-    return value.booleanValue();
+    return value;
   }
 
   private boolean isEnum(DexType valueType) {
@@ -127,11 +129,31 @@
   }
 
   private void traceMainDexDirectDependencies() {
-    new MainDexDirectReferenceTracer(appInfo, this::addDirectDependencyOrRuntimeAnnotationsWithEnum)
+    new MainDexDirectReferenceTracer(appInfo, this::addDirectDependency)
         .run(roots);
   }
 
-  private void addDirectDependencyOrRuntimeAnnotationsWithEnum(DexType type) {
+  private void addAnnotationsWithEnum(DexProgramClass clazz) {
+    // Add the annotation class as a direct dependency.
+    addDirectDependency(clazz);
+    // Add enum classes used for values as direct dependencies.
+    for (DexEncodedMethod method : clazz.virtualMethods()) {
+      DexProto proto = method.method.proto;
+      if (proto.parameters.isEmpty()) {
+        DexType valueType = proto.returnType.toBaseType(appInfo.dexItemFactory);
+        if (isEnum(valueType)) {
+          addDirectDependency(valueType);
+        }
+      }
+    }
+  }
+
+  private void addClassAnnotatedWithAnnotationWithEnum(DexType type) {
+    // Just add classes annotated with annotations with enum ad direct dependencies.
+    addDirectDependency(type);
+  }
+
+  private void addDirectDependency(DexType type) {
     // Consider only component type of arrays
     type = type.toBaseType(appInfo.dexItemFactory);
 
@@ -144,18 +166,18 @@
     if (clazz == null || clazz.isLibraryClass()) {
       return;
     }
-    addDirectDependencyOrRuntimeAnnotationsWithEnum(clazz.asProgramClass());
+    addDirectDependency(clazz.asProgramClass());
   }
 
-  private void addDirectDependencyOrRuntimeAnnotationsWithEnum(DexProgramClass dexClass) {
+  private void addDirectDependency(DexProgramClass dexClass) {
     DexType type = dexClass.type;
     assert !mainDexClassesBuilder.contains(type);
     mainDexClassesBuilder.addDependency(type);
     if (dexClass.superType != null) {
-      addDirectDependencyOrRuntimeAnnotationsWithEnum(dexClass.superType);
+      addDirectDependency(dexClass.superType);
     }
     for (DexType interfaze : dexClass.interfaces.values) {
-      addDirectDependencyOrRuntimeAnnotationsWithEnum(interfaze);
+      addDirectDependency(interfaze);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index 7cf8997..9dfddb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -54,15 +54,15 @@
   }
 
   public boolean containsAll(AccessFlags other) {
-    return containsAll(other.getAsCfAccessFlags());
+    return containsAll(other.getOriginalCfAccessFlags());
   }
 
   public boolean containsNone(AccessFlags other) {
-    return containsNone(other.getAsCfAccessFlags());
+    return containsNone(other.getOriginalCfAccessFlags());
   }
 
   public void setFlags(AccessFlags other) {
-    this.flags = other.getAsCfAccessFlags();
+    this.flags = other.getOriginalCfAccessFlags();
   }
 
   public void setPublic() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 5424cc2..ae54a80 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -171,7 +171,6 @@
       case ALL:
       case ALL_FIELDS:
         // Access flags check.
-        // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
         if (!getAccessFlags().containsAll(field.accessFlags)
             || !getNegatedAccessFlags().containsNone(field.accessFlags)) {
           break;
@@ -185,7 +184,6 @@
           break;
         }
         // Access flags check.
-        // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
         if (!getAccessFlags().containsAll(field.accessFlags)
             || !getNegatedAccessFlags().containsNone(field.accessFlags)) {
           break;
@@ -219,7 +217,6 @@
         // Fall through for all other methods.
       case ALL:
         // Access flags check.
-        // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
         if (!getAccessFlags().containsAll(method.accessFlags)
             || !getNegatedAccessFlags().containsNone(method.accessFlags)) {
           break;
@@ -240,7 +237,6 @@
           break;
         }
         // Access flags check.
-        // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
         if (!getAccessFlags().containsAll(method.accessFlags)
             || !getNegatedAccessFlags().containsNone(method.accessFlags)) {
           break;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 4dfee8e..284b03f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1146,11 +1146,11 @@
               ParameterAnnotationsList.empty(),
               code,
               method.hasClassFileVersion() ? method.getClassFileVersion() : -1);
-      if (method.getOptimizationInfo().isPublicized()) {
+      if (method.accessFlags.isPromotedToPublic()) {
         // The bridge is now the public method serving the role of the original method, and should
         // reflect that this method was publicized.
-        bridge.getMutableOptimizationInfo().markPublicized();
-        method.getMutableOptimizationInfo().unsetPublicized();
+        assert bridge.accessFlags.isPromotedToPublic();
+        method.accessFlags.unsetPromotedToPublic();
       }
       return bridge;
     }
@@ -1345,6 +1345,7 @@
 
   private static void makePrivate(DexEncodedMethod method) {
     assert !method.accessFlags.isAbstract();
+    method.accessFlags.unsetPromotedToPublic();
     method.accessFlags.unsetPublic();
     method.accessFlags.unsetProtected();
     method.accessFlags.setPrivate();
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index 69d65ea..4111bed 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -17,10 +17,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipOutputStream;
@@ -31,9 +27,6 @@
   private ZipOutputStream stream = null;
   private boolean closed = false;
   private int openCount = 0;
-  private int classesFileIndex = 0;
-  private Map<Integer, DelayedData> delayedClassesDexFiles = new HashMap<>();
-  private SortedSet<DelayedData> delayedWrites = new TreeSet<>();
 
   public ArchiveBuilder(Path archive) {
     this.archive = archive;
@@ -51,7 +44,6 @@
     assert !closed;
     openCount--;
     if (openCount == 0) {
-      writeDelayed(handler);
       closed = true;
       try {
         getStreamRaw().close();
@@ -62,20 +54,6 @@
     }
   }
 
-  private void writeDelayed(DiagnosticsHandler handler) {
-    // We should never have any indexed files at this point
-    assert delayedClassesDexFiles.isEmpty();
-    for (DelayedData data : delayedWrites) {
-      if (data.isDirectory) {
-        assert data.content == null;
-        writeDirectoryNow(data.name, handler);
-      } else {
-        assert data.content != null;
-        writeFileNow(data.name, data.content, handler);
-      }
-    }
-  }
-
   private ZipOutputStream getStreamRaw() throws IOException {
     if (stream != null) {
       return stream;
@@ -107,11 +85,7 @@
   }
 
   @Override
-  public synchronized void addDirectory(String name, DiagnosticsHandler handler) {
-    delayedWrites.add(DelayedData.createDirectory(name));
-  }
-
-  private void writeDirectoryNow(String name, DiagnosticsHandler handler) {
+  public void addDirectory(String name, DiagnosticsHandler handler) {
     if (name.charAt(name.length() - 1) != DataResource.SEPARATOR) {
       name += DataResource.SEPARATOR;
     }
@@ -131,10 +105,7 @@
   @Override
   public void addFile(String name, DataEntryResource content, DiagnosticsHandler handler) {
     try (InputStream in = content.getByteStream()) {
-      ByteDataView view = ByteDataView.of(ByteStreams.toByteArray(in));
-      synchronized (this) {
-        delayedWrites.add(DelayedData.createFile(name, view));
-      }
+      addFile(name, ByteDataView.of(ByteStreams.toByteArray(in)), handler);
     } catch (IOException e) {
       handleIOException(e, handler);
     } catch (ResourceException e) {
@@ -145,10 +116,6 @@
 
   @Override
   public synchronized void addFile(String name, ByteDataView content, DiagnosticsHandler handler) {
-    delayedWrites.add(DelayedData.createFile(name,  ByteDataView.of(content.copyByteData())));
-  }
-
-  private void writeFileNow(String name, ByteDataView content, DiagnosticsHandler handler) {
     try {
       ZipUtils.writeToZipStream(getStream(handler), name, content, ZipEntry.STORED);
     } catch (IOException e) {
@@ -156,30 +123,6 @@
     }
   }
 
-  private void writeNextIfAvailable(DiagnosticsHandler handler) {
-    DelayedData data = delayedClassesDexFiles.remove(classesFileIndex);
-    while (data != null) {
-      writeFileNow(data.name, data.content, handler);
-      classesFileIndex++;
-      data = delayedClassesDexFiles.remove(classesFileIndex);
-    }
-  }
-
-  @Override
-  public synchronized void addIndexedClassFile(
-      int index, String name, ByteDataView content, DiagnosticsHandler handler) {
-    if (index == classesFileIndex) {
-      // Fast case, we got the file in order (or we only had one).
-      writeFileNow(name, content, handler);
-      classesFileIndex++;
-      writeNextIfAvailable(handler);
-    } else {
-      // Data is released in the application writer, take a copy.
-      delayedClassesDexFiles.put(index,
-          new DelayedData(name, ByteDataView.of(content.copyByteData()), false));
-    }
-  }
-
   @Override
   public Origin getOrigin() {
     return origin;
@@ -189,32 +132,4 @@
   public Path getPath() {
     return archive;
   }
-
-  private static class DelayedData implements Comparable<DelayedData> {
-    public final String name;
-    public final ByteDataView content;
-    public final boolean isDirectory;
-
-    public static DelayedData createFile(String name, ByteDataView content) {
-      return new DelayedData(name, content, false);
-    }
-
-    public static DelayedData createDirectory(String name) {
-      return new DelayedData(name, null, true);
-    }
-
-    private DelayedData(String name, ByteDataView content, boolean isDirectory) {
-      this.name = name;
-      this.content = content;
-      this.isDirectory = isDirectory;
-    }
-
-    @Override
-    public int compareTo(DelayedData other) {
-      if (other == null) {
-        return name.compareTo(null);
-      }
-      return name.compareTo(other.name);
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index f78983f..8e0b054 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -68,12 +68,6 @@
   }
 
   @Override
-  public void addIndexedClassFile(
-      int index, String name, ByteDataView content, DiagnosticsHandler handler) {
-    addFile(name, content, handler);
-  }
-
-  @Override
   public Origin getOrigin() {
     return origin;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
index 99149ff..3e4a44d 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionDiagnostic.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.Keep;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
@@ -11,6 +12,7 @@
 import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.NoSuchFileException;
 
+@Keep
 public class ExceptionDiagnostic extends DiagnosticWithThrowable {
 
   private final Origin origin;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index d8f4dba..fe187d7 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
 import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -207,6 +208,7 @@
     }
   }
 
+  public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
   public List<String> methodsFilter = ImmutableList.of();
   public int minApiLevel = AndroidApiLevel.getDefault().getLevel();
   // Skipping min_api check and compiling an intermediate result intended for later merging.
@@ -265,6 +267,18 @@
 
   public LineNumberOptimization lineNumberOptimization = LineNumberOptimization.ON;
 
+  private static Set<String> getExtensiveLoggingFilter() {
+    String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
+    if (property != null) {
+      ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+      for (String method : property.split(";")) {
+        builder.add(method);
+      }
+      return builder.build();
+    }
+    return ImmutableSet.of();
+  }
+
   public static class InvalidParameterAnnotationInfo {
 
     final DexMethod method;
diff --git a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
index be739e3..9077f7b 100644
--- a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
@@ -23,9 +23,6 @@
 
   void addFile(String name, ByteDataView content, DiagnosticsHandler handler);
 
-  void addIndexedClassFile(
-      int index, String name, ByteDataView content, DiagnosticsHandler handler);
-
   Path getPath();
 
   Origin getOrigin();
diff --git a/src/main/keep-compatdx.txt b/src/main/keep-compatdx.txt
new file mode 100644
index 0000000..51b401c
--- /dev/null
+++ b/src/main/keep-compatdx.txt
@@ -0,0 +1,5 @@
+# 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 public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
diff --git a/src/main/keep-compatproguard.txt b/src/main/keep-compatproguard.txt
new file mode 100644
index 0000000..f508bea
--- /dev/null
+++ b/src/main/keep-compatproguard.txt
@@ -0,0 +1,5 @@
+# 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 public class com.android.tools.r8.compatproguard.CompatProguard { public static void main(java.lang.String[]); }
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 2d74e47..0d3812c 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -4,3 +4,11 @@
 
 -keep @com.android.tools.r8.Keep class * { public *; }
 -keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
+
+-keep public class com.android.tools.r8.D8 { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.R8 { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.ExtractMarker { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.compatdexbuilder.CompatDexBuilder { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.compatdx.CompatDx { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.dexfilemerger.DexFileMerger { public static void main(java.lang.String[]); }
+-keep public class com.android.tools.r8.dexsplitter.DexSplitter { public static void main(java.lang.String[]); }
diff --git a/src/test/examples/multidex002/ref-list-1.txt b/src/test/examples/multidex002/ref-list-1.txt
index 922d417..5fbe5c1 100644
--- a/src/test/examples/multidex002/ref-list-1.txt
+++ b/src/test/examples/multidex002/ref-list-1.txt
@@ -7,6 +7,7 @@
 Lmultidex002/AnnotatedVirtualMethod;
 Lmultidex002/AnnotationWithClass;
 Lmultidex002/AnnotationWithEnum2;
+Lmultidex002/AnnotationWithEnum3$Value3;
 Lmultidex002/AnnotationWithEnum3;
 Lmultidex002/AnnotationWithEnum;
 Lmultidex002/AnnotationWithoutEnum;
diff --git a/src/test/examples/multidex003/ref-list-1.txt b/src/test/examples/multidex003/ref-list-1.txt
index 40c9dcd..cdf1292 100644
--- a/src/test/examples/multidex003/ref-list-1.txt
+++ b/src/test/examples/multidex003/ref-list-1.txt
@@ -1,7 +1,9 @@
 Lmultidex003/Annotated2;
 Lmultidex003/Annotated3;
 Lmultidex003/Annotated;
+Lmultidex003/AnnotationValue;
 Lmultidex003/TestAnnotation2;
+Lmultidex003/TestAnnotation3$Value;
 Lmultidex003/TestAnnotation3;
 Lmultidex003/TestAnnotation;
 Lmultidex003/fakelibrary/MultiDex$V14;
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 129176a..88d077b 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -22,7 +22,7 @@
   }
 
   public static D8TestBuilder create(TestState state) {
-    return new D8TestBuilder(state, D8Command.builder());
+    return new D8TestBuilder(state, D8Command.builder(state.getDiagnosticsHandler()));
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 178d351..79fb293 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -18,6 +18,11 @@
   }
 
   @Override
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    return state.getDiagnosticsMessages();
+  }
+
+  @Override
   public D8TestRunResult createRunResult(AndroidApp app, ProcessResult result) {
     return new D8TestRunResult(app, result);
   }
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index fa1f314..da6a17a 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -19,6 +19,11 @@
   }
 
   @Override
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    throw new UnsupportedOperationException("No diagnostics messages from dx");
+  }
+
+  @Override
   public DXTestRunResult createRunResult(AndroidApp app, ProcessResult result) {
     return new DXTestRunResult(app, result);
   }
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 2c2c5a6..cebfc53 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -40,7 +40,9 @@
           .put("math.BigInteger.ConstructorLjava_lang_String.BigInteger_Constructor_A02", any())
           .put(
               "lang.StringBuffer.insertILjava_lang_Object.StringBuffer_insert_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put("lang.StringBuffer.serialization.StringBuffer_serialization_A01", anyDexVm())
           .put(
               "lang.CloneNotSupportedException.serialization.CloneNotSupportedException_serialization_A01",
@@ -52,12 +54,20 @@
               "lang.StrictMath.roundF.StrictMath_round_A01",
               match(
                   runtimes(
-                      Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.ART_V7_0_0, Runtime.JAVA)))
+                      Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
+                      Runtime.ART_V8_1_0,
+                      Runtime.ART_V7_0_0,
+                      Runtime.JAVA)))
           .put(
               "lang.StrictMath.roundD.StrictMath_round_A01",
               match(
                   runtimes(
-                      Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.ART_V7_0_0, Runtime.JAVA)))
+                      Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
+                      Runtime.ART_V8_1_0,
+                      Runtime.ART_V7_0_0,
+                      Runtime.JAVA)))
           .put("lang.StrictMath.atan2DD.StrictMath_atan2_A01", any())
           .put("lang.Thread.stop.Thread_stop_A05", any())
           .put("lang.Thread.resume.Thread_resume_A02", anyDexVm())
@@ -70,6 +80,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1)))
@@ -199,7 +210,12 @@
               anyDexVm())
           .put(
               "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A07",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.ART_V7_0_0)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
+                      Runtime.ART_V8_1_0,
+                      Runtime.ART_V7_0_0)))
           .put(
               "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A04",
               anyDexVm())
@@ -421,6 +437,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -431,6 +448,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -441,6 +459,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -451,6 +470,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -465,6 +485,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -475,6 +496,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -509,6 +531,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -520,6 +543,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -530,6 +554,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -540,6 +565,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -550,6 +576,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -560,6 +587,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -570,6 +598,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -580,6 +609,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -590,6 +620,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -619,6 +650,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V6_0_1,
@@ -630,7 +662,7 @@
           .put("lang.ref.WeakReference.get.WeakReference_get_A01", any())
           .put(
               "lang.StackTraceElement.toString.StackTraceElement_toString_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0)))
+              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0)))
           .put(
               "lang.NullPointerException.serialization.NullPointerException_serialization_A01",
               anyDexVm())
@@ -686,7 +718,9 @@
               match(artRuntimesFromAndJava(Runtime.ART_V5_1_1)))
           .put(
               "lang.annotation.IncompleteAnnotationException.ConstructorLjava_lang_ClassLjava_lang_String.IncompleteAnnotationException_Constructor_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put(
               "lang.InterruptedException.serialization.InterruptedException_serialization_A01",
               anyDexVm())
@@ -774,6 +808,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V4_4_4,
@@ -814,6 +849,7 @@
               match(
                   runtimes(
                       Runtime.ART_DEFAULT,
+                      Runtime.ART_V9_0_0,
                       Runtime.ART_V8_1_0,
                       Runtime.ART_V7_0_0,
                       Runtime.ART_V4_4_4,
@@ -1167,7 +1203,9 @@
               anyDexVm())
           .put(
               "lang.reflect.Proxy.h.Proxy_h_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put("lang.reflect.Proxy.serialization.Proxy_serialization_A02", any())
           .put(
               "lang.reflect.GenericSignatureFormatError.serialization.GenericSignatureFormatError_serialization_A01",
@@ -1177,7 +1215,9 @@
               anyDexVm())
           .put(
               "lang.reflect.Proxy.ConstructorLjava_lang_reflect_InvocationHandler.Proxy_Constructor_A01",
-              match(runtimes(Runtime.ART_DEFAULT, Runtime.ART_V8_1_0, Runtime.JAVA)))
+              match(
+                  runtimes(
+                      Runtime.ART_DEFAULT, Runtime.ART_V9_0_0, Runtime.ART_V8_1_0, Runtime.JAVA)))
           .put(
               "lang.reflect.Proxy.newProxyInstanceLjava_lang_ClassLoader_Ljava_lang_ClassLjava_lang_reflect_InvocationHandler.Proxy_newProxyInstance_A01",
               anyDexVm())
@@ -1694,14 +1734,20 @@
               match(artRuntimesUpTo(Runtime.ART_V4_4_4)))
           .put(
               "lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
-              match(and(runtimes(Runtime.ART_V8_1_0), artRuntimesUpTo(Runtime.ART_V4_4_4))))
+              match(
+                  and(
+                      runtimes(Runtime.ART_V9_0_0, Runtime.ART_V8_1_0),
+                      artRuntimesUpTo(Runtime.ART_V4_4_4))))
           .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", anyDexVm())
           .put(
               "lang.ref.WeakReference.enqueue.WeakReference_enqueue_A01",
               match(artRuntimesUpTo(Runtime.ART_V4_4_4)))
           .put(
               "lang.ref.SoftReference.isEnqueued.SoftReference_isEnqueued_A01",
-              match(and(runtimes(Runtime.ART_V8_1_0), artRuntimesUpTo(Runtime.ART_V4_4_4))))
+              match(
+                  and(
+                      runtimes(Runtime.ART_V9_0_0, Runtime.ART_V8_1_0),
+                      artRuntimesUpTo(Runtime.ART_V4_4_4))))
           .put(
               "lang.ref.SoftReference.enqueue.SoftReference_enqueue_A01",
               match(artRuntimesUpTo(Runtime.ART_V4_4_4)))
@@ -1712,7 +1758,6 @@
           .put(
               "util.concurrent.AbstractExecutorService.invokeAllLjava_util_CollectionJLjava_util_concurrent_TimeUnit.AbstractExecutorService_invokeAll_A06",
               match(runtimes(Runtime.ART_V4_0_4)))
-
           .build(); // end of flakyWhenRun
 
   public static final Multimap<String, TestCondition> timeoutsWhenRun =
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index bef1e02..4ea8953 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -25,6 +25,11 @@
   }
 
   @Override
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    throw new UnsupportedOperationException("No diagnostics messages from dx");
+  }
+
+  @Override
   public CodeInspector inspector() throws IOException, ExecutionException {
     return new CodeInspector(app, proguardMap);
   }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 5123c51..2a4977d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -97,7 +97,8 @@
       DexVm.Version.V5_1_1,
       DexVm.Version.V6_0_1,
       DexVm.Version.V7_0_0,
-      DexVm.Version.V8_1_0);
+      DexVm.Version.V8_1_0,
+      DexVm.Version.V9_0_0);
 
   // Input jar for jctf tests.
   private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar";
@@ -463,6 +464,16 @@
   static {
     ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
     builder
+        .put(DexVm.Version.V9_0_0, ImmutableList.of(
+            // TODO(120400625): Triage.
+            "454-get-vreg",
+            // TODO(120402198): Triage.
+            "457-regs",
+            // TODO(120401674): Triage.
+            "543-env-long-ref",
+            // TODO(120261858) Triage.
+            "518-null-array-get"
+        ))
         .put(DexVm.Version.V8_1_0, ImmutableList.of(
             // TODO(119938529): Triage.
             "709-checker-varhandles",
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 101fbb8..1c949a4 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -69,6 +69,10 @@
           .put(
               Version.V7_0_0,
               ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
+          .put(
+              Version.V9_0_0,
+              // TODO(120402963) Triage.
+              ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
           .put(Version.DEFAULT, ImmutableList.of())
           .build();
 
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 221d427..793df27 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -208,6 +208,9 @@
         // Early art versions incorrectly print doubles.
         .put("regress_72361252.Test",
             TestCondition.match(TestCondition.runtimesUpTo(Version.V6_0_1)))
+        // TODO(120402200): Triage.
+        .put("regress_62300145.Regress",
+            TestCondition.match(TestCondition.runtimesUpTo(Version.V9_0_0)))
         .build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 5550415..ee0e8af 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -25,7 +25,9 @@
 
   public static R8TestBuilder create(TestState state, Backend backend, R8Mode mode) {
     R8Command.Builder builder =
-        mode == R8Mode.Full ? R8Command.builder() : new CompatProguardCommandBuilder();
+        mode == R8Mode.Full
+            ? R8Command.builder(state.getDiagnosticsHandler())
+            : new CompatProguardCommandBuilder(true, state.getDiagnosticsHandler());
     return new R8TestBuilder(state, builder, backend);
   }
 
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 35cb176..d27a4a0 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -27,6 +27,11 @@
   }
 
   @Override
+  public TestDiagnosticMessages getDiagnosticMessages() {
+    return state.getDiagnosticsMessages();
+  }
+
+  @Override
   public CodeInspector inspector() throws IOException, ExecutionException {
     return new CodeInspector(app, proguardMap);
   }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 68aec3f..75dfc91 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -269,6 +269,12 @@
                 "testMissingSuperDesugaredAndroidO"
             ))
         .put(
+            DexVm.Version.V9_0_0, ImmutableList.of(
+                // TODO(120402963): Triage.
+                "invokecustom",
+                "invokecustom2"
+            ))
+        .put(
             DexVm.Version.DEFAULT, ImmutableList.of()
         );
     failsOn = builder.build();
@@ -577,29 +583,39 @@
       String qualifiedMainClass, Path[] jars, Path[] dexes, List<String> args) throws IOException {
 
     boolean expectedToFail = expectedToFail(testName);
-    if (expectedToFail && !ToolHelper.compareAgaintsGoldenFiles()) {
-      thrown.expect(Throwable.class);
-    }
-    String output = ToolHelper.runArtNoVerificationErrors(
-        Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
-        qualifiedMainClass,
-        builder -> {
-          for (String arg : args) {
-            builder.appendProgramArgument(arg);
-          }
-        });
-    if (!expectedToFail && !skipRunningOnJvm(testName) && !ToolHelper.compareAgaintsGoldenFiles()) {
-      ArrayList<String> javaArgs = Lists.newArrayList(args);
-      javaArgs.add(0, qualifiedMainClass);
-      ToolHelper.ProcessResult javaResult =
-          ToolHelper.runJava(ImmutableList.copyOf(jars), javaArgs.toArray(new String[0]));
-      assertEquals("JVM run failed", javaResult.exitCode, 0);
-      assertTrue(
-          "JVM output does not match art output.\n\tjvm: "
-              + javaResult.stdout
-              + "\n\tart: "
-              + output,
-          output.replace("\r", "").equals(javaResult.stdout.replace("\r", "")));
+    try {
+      String output = ToolHelper.runArtNoVerificationErrors(
+          Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
+          qualifiedMainClass,
+          builder -> {
+            for (String arg : args) {
+              builder.appendProgramArgument(arg);
+            }
+          });
+      if (!expectedToFail
+          && !skipRunningOnJvm(testName)
+          && !ToolHelper.compareAgaintsGoldenFiles()) {
+        ArrayList<String> javaArgs = Lists.newArrayList(args);
+        javaArgs.add(0, qualifiedMainClass);
+        ToolHelper.ProcessResult javaResult =
+            ToolHelper.runJava(ImmutableList.copyOf(jars), javaArgs.toArray(new String[0]));
+        assertEquals("JVM run failed", javaResult.exitCode, 0);
+        assertTrue(
+            "JVM output does not match art output.\n\tjvm: "
+                + javaResult.stdout
+                + "\n\tart: "
+                + output,
+            output.replace("\r", "").equals(javaResult.stdout.replace("\r", "")));
+      }
+    } catch (Throwable t) {
+      if (ToolHelper.isWindows()) {
+        System.out.println("Throwable: " + t.getMessage());
+        t.printStackTrace(System.out);
+        System.out.println("expectedToFail: " + expectedToFail);
+        System.out.println(
+            "ToolHelper.compareAgaintsGoldenFiles(): " + ToolHelper.compareAgaintsGoldenFiles());
+      }
+      assert expectedToFail && !ToolHelper.compareAgaintsGoldenFiles();
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index e0981c4..2ab5e3e 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -186,7 +186,9 @@
           .put(DexVm.Version.V6_0_1, ImmutableList.of("invokecustom"))
           // Dex version not supported
           .put(DexVm.Version.V7_0_0, ImmutableList.of("invokecustom"))
+          // Dex version not supported
           .put(DexVm.Version.V8_1_0, ImmutableList.of("invokecustom"))
+          // Dex version not supported
           .put(DexVm.Version.DEFAULT, ImmutableList.of())
           .build();
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 8a284ac..9c91282 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -231,7 +231,9 @@
               + "1: d>i>s>i>a\n"
               + "2: l>i>s>i>a\n"
               + "3: x>s\n"
-              + "4: c>d>i>s>i>a\n"
+              + "4: c>d>i>s>i>a\n",
+          "varhandle",
+          "true\nfalse\n"
       );
 
   @Rule
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 61f9def..34ffcaa 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -4,6 +4,9 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.TestBase.Backend.DEX;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
 
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.ToolHelper.DexVm;
@@ -18,6 +21,7 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.concurrent.ExecutionException;
+import org.hamcrest.Matcher;
 
 public abstract class TestCompileResult<RR extends TestRunResult> {
   final TestState state;
@@ -30,6 +34,8 @@
 
   public abstract Backend getBackend();
 
+  public abstract TestDiagnosticMessages getDiagnosticMessages();
+
   protected abstract RR createRunResult(AndroidApp add, ProcessResult result);
 
   public RR run(Class<?> mainClass) throws IOException {
@@ -62,6 +68,38 @@
     return this;
   }
 
+  public TestCompileResult<RR> assertNoMessages() {
+    assertEquals(0, getDiagnosticMessages().getInfos().size());
+    assertEquals(0, getDiagnosticMessages().getWarnings().size());
+    assertEquals(0, getDiagnosticMessages().getErrors().size());
+    return this;
+  }
+
+  public TestCompileResult<RR> assertOnlyInfos() {
+    assertNotEquals(0, getDiagnosticMessages().getInfos().size());
+    assertEquals(0, getDiagnosticMessages().getWarnings().size());
+    assertEquals(0, getDiagnosticMessages().getErrors().size());
+    return this;
+  }
+
+  public TestCompileResult<RR> assertOnlyWarnings() {
+    assertEquals(0, getDiagnosticMessages().getInfos().size());
+    assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
+    assertEquals(0, getDiagnosticMessages().getErrors().size());
+    return this;
+  }
+
+  public TestCompileResult<RR> assertWarningMessageThatMatches(Matcher<String> matcher) {
+    assertNotEquals(0, getDiagnosticMessages().getWarnings().size());
+    for (int i = 0; i < getDiagnosticMessages().getWarnings().size(); i++) {
+      if (matcher.matches(getDiagnosticMessages().getWarnings().get(i).getDiagnosticMessage())) {
+        return this;
+      }
+    }
+    fail("No warning matches " + matcher.toString());
+    return this;
+  }
+
   public DebugTestConfig debugConfig() {
     // Rethrow exceptions since debug config is usually used in a delayed wrapper which
     // does not declare exceptions.
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
index 439b9e9..52d7f93 100644
--- a/src/test/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -23,6 +23,7 @@
     ART_V6_0_1,
     ART_V7_0_0,
     ART_V8_1_0,
+    ART_V9_0_0,
     ART_DEFAULT,
     JAVA;
 
@@ -43,6 +44,8 @@
           return ART_V7_0_0;
         case V8_1_0:
           return ART_V8_1_0;
+        case V9_0_0:
+          return ART_V9_0_0;
         case DEFAULT:
           return ART_DEFAULT;
         default:
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
new file mode 100644
index 0000000..3e734cc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -0,0 +1,16 @@
+// 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;
+
+import java.util.List;
+
+public interface TestDiagnosticMessages {
+
+  public List<Diagnostic> getInfos();
+
+  public List<Diagnostic> getWarnings();
+
+  public List<Diagnostic> getErrors();
+}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
new file mode 100644
index 0000000..d0476b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -0,0 +1,41 @@
+// 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestDiagnosticMessagesImpl implements DiagnosticsHandler, TestDiagnosticMessages {
+  private final List<Diagnostic> infos = new ArrayList<>();
+  private final List<Diagnostic> warnings = new ArrayList<>();
+  private final List<Diagnostic> errors = new ArrayList<>();
+
+  @Override
+  public void info(Diagnostic info) {
+    infos.add(info);
+  }
+
+  @Override
+  public void warning(Diagnostic warning) {
+    warnings.add(warning);
+  }
+
+  @Override
+  public void error(Diagnostic error) {
+    errors.add(error);
+  }
+
+  public List<Diagnostic> getInfos() {
+    return infos;
+  }
+
+  public List<Diagnostic> getWarnings() {
+    return warnings;
+  }
+
+  public List<Diagnostic> getErrors() {
+    return errors;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/TestState.java b/src/test/java/com/android/tools/r8/TestState.java
index d516c7c..bdb4e2f 100644
--- a/src/test/java/com/android/tools/r8/TestState.java
+++ b/src/test/java/com/android/tools/r8/TestState.java
@@ -10,6 +10,7 @@
 public class TestState {
 
   private final TemporaryFolder temp;
+  private final TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl();
 
   public TestState(TemporaryFolder temp) {
     this.temp = temp;
@@ -18,4 +19,12 @@
   public Path getNewTempFolder() throws IOException {
     return temp.newFolder().toPath();
   }
+
+  DiagnosticsHandler getDiagnosticsHandler() {
+    return messages;
+  }
+
+  public TestDiagnosticMessages getDiagnosticsMessages() {
+    return messages;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index caeebd9..4f8b2e3 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -126,6 +126,8 @@
     ART_7_0_0_HOST(Version.V7_0_0, Kind.HOST),
     ART_8_1_0_TARGET(Version.V8_1_0, Kind.TARGET),
     ART_8_1_0_HOST(Version.V8_1_0, Kind.HOST),
+    ART_9_0_0_TARGET(Version.V9_0_0, Kind.TARGET),
+    ART_9_0_0_HOST(Version.V9_0_0, Kind.HOST),
     ART_DEFAULT(Version.DEFAULT, Kind.HOST);
 
     private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
@@ -140,6 +142,7 @@
       V6_0_1("6.0.1"),
       V7_0_0("7.0.0"),
       V8_1_0("8.1.0"),
+      V9_0_0("9.0.0"),
       DEFAULT("default");
 
       Version(String shortName) {
@@ -416,6 +419,7 @@
   private static final Map<DexVm, String> ART_DIRS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "art")
+          .put(DexVm.ART_9_0_0_HOST, "art-9.0.0")
           .put(DexVm.ART_8_1_0_HOST, "art-8.1.0")
           .put(DexVm.ART_7_0_0_HOST, "art-7.0.0")
           .put(DexVm.ART_6_0_1_HOST, "art-6.0.1")
@@ -425,6 +429,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS =
       ImmutableMap.<DexVm, String>builder()
           .put(DexVm.ART_DEFAULT, "bin/art")
+          .put(DexVm.ART_9_0_0_HOST, "bin/art")
           .put(DexVm.ART_8_1_0_HOST, "bin/art")
           .put(DexVm.ART_7_0_0_HOST, "bin/art")
           .put(DexVm.ART_6_0_1_HOST, "bin/art")
@@ -435,6 +440,7 @@
   private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
       ImmutableMap.of(
           DexVm.ART_DEFAULT, "bin/art",
+          DexVm.ART_9_0_0_HOST, "bin/art",
           DexVm.ART_8_1_0_HOST, "bin/art",
           DexVm.ART_7_0_0_HOST, "bin/art",
           DexVm.ART_6_0_1_HOST, "bin/art");
@@ -457,6 +463,7 @@
     ImmutableMap.Builder<DexVm, List<String>> builder = ImmutableMap.builder();
     builder
         .put(DexVm.ART_DEFAULT, ART_BOOT_LIBS)
+        .put(DexVm.ART_9_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_8_1_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_7_0_0_HOST, ART_BOOT_LIBS)
         .put(DexVm.ART_6_0_1_HOST, ART_BOOT_LIBS)
@@ -472,6 +479,7 @@
     ImmutableMap.Builder<DexVm, String> builder = ImmutableMap.builder();
     builder
         .put(DexVm.ART_DEFAULT, "angler")
+        .put(DexVm.ART_9_0_0_HOST, "marlin")
         .put(DexVm.ART_8_1_0_HOST, "marlin")
         .put(DexVm.ART_7_0_0_HOST, "angler")
         .put(DexVm.ART_6_0_1_HOST, "angler")
@@ -705,6 +713,12 @@
       return DexVm.ART_DEFAULT;
     } else {
       DexVm artVersionEnum = DexVm.fromShortName(artVersion);
+      if (artVersionEnum == null
+          && !artVersion.endsWith(Kind.HOST.toString())
+          && !artVersion.endsWith(Kind.TARGET.toString())) {
+        // Default to host Art/Dalvik when not specified.
+        artVersionEnum = DexVm.fromShortName(artVersion + '_' + Kind.HOST.toString());
+      }
       if (artVersionEnum == null) {
         throw new RuntimeException("Unsupported Art version " + artVersion);
       } else {
@@ -721,6 +735,8 @@
     switch (dexVm.version) {
       case DEFAULT:
         return AndroidApiLevel.O;
+      case V9_0_0:
+        return AndroidApiLevel.P;
       case V8_1_0:
         return AndroidApiLevel.O;
       case V7_0_0:
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a7b2b223..e6a1fcd 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -1019,6 +1019,10 @@
               artCommandBuilder.appendArtOption("-Xcompiler-option");
               artCommandBuilder.appendArtOption("--debuggable");
             }
+            if (ToolHelper.getDexVm().getVersion().isAtLeast(DexVm.Version.V9_0_0) &&
+                ToolHelper.getDexVm().getVersion() != DexVm.Version.DEFAULT) {
+              artCommandBuilder.appendArtOption("-XjdwpProvider:internal");
+            }
             if (DEBUG_TESTS && ToolHelper.getDexVm().getVersion().isNewerThan(Version.V4_4_4)) {
               artCommandBuilder.appendArtOption("-verbose:jdwp");
             }
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 af238f2..e26ec3d 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -29,7 +28,9 @@
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.KeepingDiagnosticHandler;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
@@ -42,27 +43,32 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import org.junit.Assert;
+import org.junit.Before;
 import org.junit.ComparisonFailure;
 import org.junit.Rule;
 import org.junit.rules.TemporaryFolder;
 
 public abstract class CompilationTestBase {
 
+  protected KeepingDiagnosticHandler handler;
+  protected Reporter reporter;
+
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
+  @Before
+  public void reset() {
+    handler = new KeepingDiagnosticHandler();
+    reporter = new Reporter(handler);
+  }
+
   public AndroidApp runAndCheckVerification(
       CompilerUnderTest compiler,
       CompilationMode mode,
@@ -83,22 +89,6 @@
     return result;
   }
 
-  public void assertIdenticalZipFiles(File file1, File file2) throws IOException {
-    try (ZipFile zipFile1 = new ZipFile(file1); ZipFile zipFile2 = new ZipFile(file2)) {
-      final Enumeration<? extends ZipEntry> entries1 = zipFile1.entries();
-      final Enumeration<? extends ZipEntry> entries2 = zipFile2.entries();
-
-      while (entries1.hasMoreElements()) {
-        Assert.assertTrue(entries2.hasMoreElements());
-        ZipEntry entry1 = entries1.nextElement();
-        ZipEntry entry2 = entries2.nextElement();
-        Assert.assertEquals(entry1.getName(), entry2.getName());
-        Assert.assertEquals(entry1.getCrc(), entry2.getCrc());
-        Assert.assertEquals(entry1.getSize(), entry2.getSize());
-      }
-    }
-  }
-
   public AndroidApp runAndCheckVerification(
       CompilerUnderTest compiler,
       CompilationMode mode,
@@ -107,30 +97,17 @@
       Consumer<InternalOptions> optionsConsumer,
       List<String> inputs)
       throws ExecutionException, IOException, CompilationFailedException {
-    return runAndCheckVerification(compiler, mode, referenceApk, pgConfs, optionsConsumer,
-        DexIndexedConsumer::emptyConsumer, inputs);
-  }
-
-  public AndroidApp runAndCheckVerification(
-      CompilerUnderTest compiler,
-      CompilationMode mode,
-      String referenceApk,
-      List<String> pgConfs,
-      Consumer<InternalOptions> optionsConsumer,
-      Supplier<DexIndexedConsumer> dexIndexedConsumerSupplier,
-      List<String> inputs)
-      throws ExecutionException, IOException, CompilationFailedException {
     assertTrue(referenceApk == null || new File(referenceApk).exists());
     AndroidAppConsumers outputApp;
     if (compiler == CompilerUnderTest.R8) {
-      R8Command.Builder builder = R8Command.builder();
+      R8Command.Builder builder = R8Command.builder(reporter);
       builder.addProgramFiles(ListUtils.map(inputs, Paths::get));
       if (pgConfs != null) {
         builder.addProguardConfigurationFiles(
             pgConfs.stream().map(Paths::get).collect(Collectors.toList()));
       }
       builder.setMode(mode);
-      builder.setProgramConsumer(dexIndexedConsumerSupplier.get());
+      builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
       builder.setMinApiLevel(AndroidApiLevel.L.getLevel());
       ToolHelper.allowPartiallyImplementedProguardOptions(builder);
       ToolHelper.addProguardConfigurationConsumer(
@@ -154,7 +131,6 @@
     return checkVerification(outputApp.build(), referenceApk);
   }
 
-
   public AndroidApp checkVerification(AndroidApp outputApp, String referenceApk)
       throws IOException, ExecutionException {
     Path out = temp.getRoot().toPath().resolve("all.zip");
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 634ff6f..09ce657 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
@@ -14,7 +13,6 @@
 import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 
 public class GMSCoreDeployJarVerificationTest extends GMSCoreCompilationTestBase {
 
@@ -40,20 +38,4 @@
         optionsConsumer,
         Collections.singletonList(base + DEPLOY_JAR));
   }
-
-  public AndroidApp buildFromDeployJar(
-      CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
-      Consumer<InternalOptions> optionsConsumer, Supplier<DexIndexedConsumer> consumerSupplier)
-      throws ExecutionException, IOException, ProguardRuleParserException,
-      CompilationFailedException {
-    return runAndCheckVerification(
-        compiler,
-        mode,
-        hasReference ? base + REFERENCE_APK : null,
-        null,
-        optionsConsumer,
-        consumerSupplier,
-        Collections.singletonList(base + DEPLOY_JAR));
-  }
-
 }
diff --git a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
index e90c504..6514635 100644
--- a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
@@ -4,10 +4,15 @@
 
 package com.android.tools.r8.internal;
 
+import static org.junit.Assert.assertEquals;
+
 import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.utils.AndroidApp;
 import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Stream;
 import org.junit.Test;
 
 public class NestTreeShakeJarVerificationTest extends NestCompilationBase {
@@ -22,5 +27,13 @@
             ImmutableList.of(BASE + PG_CONF, BASE + PG_CONF_NO_OPT),
             options -> options.testing.allowTypeErrors = true,
             ImmutableList.of(BASE + DEPLOY_JAR));
+    assertEquals(0, filterKotlinMetadata(handler.warnings).count());
+    assertEquals(0, filterKotlinMetadata(handler.infos).count());
+  }
+
+  private Stream<Diagnostic> filterKotlinMetadata(List<Diagnostic> warnings) {
+    return warnings.stream().filter(diagnostic -> {
+      return diagnostic.getDiagnosticMessage().contains("kotlin.Metadata");
+    });
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
index 5803355..d881185 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
@@ -7,12 +7,8 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.utils.AndroidApp;
-import java.io.File;
-import java.util.function.Supplier;
 import org.junit.Test;
 
 public class R8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
@@ -23,9 +19,6 @@
   @Test
   public void buildFromDeployJar() throws Exception {
     // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
-    File tempFolder = temp.newFolder();
-    File app1Zip = new File(tempFolder, "app1.zip");
-    File app2Zip = new File(tempFolder, "app2.zip");
     AndroidApp app1 =
         buildFromDeployJar(
             CompilerUnderTest.R8,
@@ -34,8 +27,7 @@
             false,
             options ->
                 options.proguardMapConsumer =
-                    (proguardMap, handler) -> this.proguardMap1 = proguardMap,
-            ()-> new ArchiveConsumer(app1Zip.toPath(), true));
+                    (proguardMap, handler) -> this.proguardMap1 = proguardMap);
     AndroidApp app2 =
         buildFromDeployJar(
             CompilerUnderTest.R8,
@@ -44,14 +36,10 @@
             false,
             options ->
                 options.proguardMapConsumer =
-                    (proguardMap, handler) -> this.proguardMap2 = proguardMap,
-            ()-> new ArchiveConsumer(app2Zip.toPath(), true));
-
-
+                    (proguardMap, handler) -> this.proguardMap2 = proguardMap);
 
     // Verify that the result of the two compilations was the same.
     assertIdenticalApplications(app1, app2);
-    assertIdenticalZipFiles(app1Zip, app2Zip);
     assertEquals(proguardMap1, proguardMap2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 592aeae..415376f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -10,13 +10,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.ir.optimize.classinliner.builders.BuildersTestClass;
 import com.android.tools.r8.ir.optimize.classinliner.builders.ControlFlow;
@@ -41,7 +38,6 @@
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
@@ -50,10 +46,8 @@
 import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import com.android.tools.r8.utils.codeinspector.NewInstanceInstructionSubject;
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
-import java.nio.file.Path;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.Set;
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 e013b02..ddb0e7d 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -20,21 +20,15 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -307,34 +301,6 @@
         nonLambdaOffset++;
       }
     }
-    testZipfileOrder(out);
-  }
-
-  private void testZipfileOrder(Path out) throws IOException {
-    // Sanity check that the classes.dex files are in the correct order
-    ZipFile outZip = new ZipFile(out.toFile());
-    Enumeration<? extends ZipEntry> entries = outZip.entries();
-    int index = 0;
-    LinkedList<String> entryNames = new LinkedList<>();
-    // We expect classes*.dex files first, in order, then the rest of the files, in order.
-    while(entries.hasMoreElements()) {
-      ZipEntry entry = entries.nextElement();
-      if (!entry.getName().startsWith("classes") || !entry.getName().endsWith(".dex")) {
-        entryNames.add(entry.getName());
-        continue;
-      }
-      if (index == 0) {
-        Assert.assertEquals("classes.dex", entry.getName());
-      } else {
-        Assert.assertEquals("classes" + (index + 1) + ".dex", entry.getName());
-      }
-      index++;
-    }
-    // Everything else should be sorted according to name.
-    String[] entriesUnsorted = entryNames.toArray(new String[0]);
-    String[] entriesSorted = entryNames.toArray(new String[0]);
-    Arrays.sort(entriesSorted);
-    Assert.assertArrayEquals(entriesUnsorted, entriesSorted);
   }
 
   private boolean isLambda(String mainDexEntry) {
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
index 8a5975ea..473ca53 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -98,10 +98,11 @@
     // method naming001.A.m should be kept, according to the keep rule.
     assertEquals("m", naming.lookupName(m).toSourceString());
 
-    // method naming001.A.privateFunc is transformed to a public method, and then kept.
-    DexMethod p = dexItemFactory.createMethod(
-        a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
-    assertEquals("privateFunc", naming.lookupName(p).toSourceString());
+    // method naming001.A.privateFunc is not private in the input and should therefore be renamed.
+    DexMethod p =
+        dexItemFactory.createMethod(
+            a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+    assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
   }
 
   private static void test001_rule005(DexItemFactory dexItemFactory, NamingLens naming) {
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
new file mode 100644
index 0000000..6d7ce25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/InliningRetraceTest.java
@@ -0,0 +1,101 @@
+// 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileName;
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.ForceInline;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InliningRetraceTest extends RetraceTestBase {
+
+  @Parameters(name = "Backend: {0}, mode: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(Backend.values(), CompilationMode.values());
+  }
+
+  public InliningRetraceTest(Backend backend, CompilationMode mode) {
+    super(backend, mode);
+  }
+
+  @Override
+  public Class<?> getMainClass() {
+    return Main.class;
+  }
+
+  private int expectedActualStackTraceHeight() {
+    return mode == CompilationMode.RELEASE ? 1 : 4;
+  }
+
+  @Test
+  public void testSourceFileAndLineNumberTable() throws Exception {
+    runTest(
+        "-keepattributes SourceFile,LineNumberTable",
+        (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
+          // Even when SourceFile is present retrace replaces the file name in the stack trace.
+          assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace));
+          assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
+        });
+  }
+
+  @Test
+  public void testLineNumberTableOnly() throws Exception {
+    runTest(
+        "-keepattributes LineNumberTable",
+        (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
+          assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace));
+          assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
+        });
+  }
+
+  @Test
+  public void testNoLineNumberTable() throws Exception {
+    runTest(
+        "",
+        (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
+          assertThat(retracedStackTrace, isSameExceptForFileNameAndLineNumber(expectedStackTrace));
+          assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
+        });
+  }
+}
+
+class Main {
+
+  @ForceInline
+  public static void method3(long j) {
+    System.out.println("In method3");
+    throw null;
+  }
+
+  @ForceInline
+  public static void method2(int j) {
+    System.out.println("In method2");
+    for (int i = 0; i < 10; i++) {
+      method3((long) j);
+    }
+  }
+
+  @ForceInline
+  public static void method1(String s) {
+    System.out.println("In method1");
+    for (int i = 0; i < 10; i++) {
+      method2(Integer.parseInt(s));
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.println("In main");
+    method1("1");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
deleted file mode 100644
index b110ead..0000000
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// 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.naming.retrace;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.function.BiConsumer;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class RetraceTest extends TestBase {
-  private Backend backend;
-  private CompilationMode mode;
-
-  @Parameters(name = "Backend: {0}, mode: {1}")
-  public static Collection<Object[]> data() {
-    return buildParameters(Backend.values(), CompilationMode.values());
-  }
-
-  public RetraceTest(Backend backend, CompilationMode mode) {
-    this.backend = backend;
-    this.mode = mode;
-  }
-
-  private List<String> retrace(String map, List<String> stackTrace) throws IOException {
-    Path t = temp.newFolder().toPath();
-    Path mapFile = t.resolve("map");
-    Path stackTraceFile = t.resolve("stackTrace");
-    FileUtils.writeTextFile(mapFile, map);
-    FileUtils.writeTextFile(stackTraceFile, stackTrace);
-    return StringUtils.splitLines(ToolHelper.runRetrace(mapFile, stackTraceFile));
-  }
-
-  private boolean isDalvik() {
-    return backend == Backend.DEX && ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST);
-  }
-
-  private List<String> extractStackTrace(ProcessResult result) {
-    ImmutableList.Builder<String> builder = ImmutableList.builder();
-    List<String> stderr = StringUtils.splitLines(result.stderr);
-    Iterator<String> iterator = stderr.iterator();
-
-    // A Dalvik stacktrace looks like this:
-    // W(209693) threadid=1: thread exiting with uncaught exception (group=0xf616cb20)  (dalvikvm)
-    // java.lang.NullPointerException
-    // \tat com.android.tools.r8.naming.retrace.Main.a(:133)
-    // \tat com.android.tools.r8.naming.retrace.Main.a(:139)
-    // \tat com.android.tools.r8.naming.retrace.Main.main(:145)
-    // \tat dalvik.system.NativeStart.main(Native Method)
-    //
-    // An Art 5.1.1 and 6.0.1 stacktrace looks like this:
-    // java.lang.NullPointerException: throw with null exception
-    // \tat com.android.tools.r8.naming.retrace.Main.a(:154)
-    // \tat com.android.tools.r8.naming.retrace.Main.a(:160)
-    // \tat com.android.tools.r8.naming.retrace.Main.main(:166)
-    //
-    // An Art 7.0.0 and latest stacktrace looks like this:
-    // Exception in thread "main" java.lang.NullPointerException: throw with null exception
-    // \tat com.android.tools.r8.naming.retrace.Main.a(:150)
-    // \tat com.android.tools.r8.naming.retrace.Main.a(:156)
-    // \tat com.android.tools.r8.naming.retrace.Main.main(:162)
-    int last = stderr.size();
-    if (isDalvik()) {
-      // Skip the bottom frame "dalvik.system.NativeStart.main".
-      last--;
-    }
-    // Take all lines from the bottom starting with "\tat ".
-    int first = last;
-    while (first - 1 >= 0 && stderr.get(first - 1).startsWith("\tat ")) {
-      first--;
-    }
-    for (int i = first; i < last; i++) {
-      builder.add(stderr.get(i));
-    }
-    return builder.build();
-  }
-
-  public void runTest(Class<?> mainClass, BiConsumer<List<String>, List<String>> checker)
-      throws Exception {
-    StringBuilder proguardMapBuilder = new StringBuilder();
-    AndroidApp output =
-        ToolHelper.runR8(
-            R8Command.builder()
-                .setMode(mode)
-                .addClassProgramData(ToolHelper.getClassAsBytes(mainClass), Origin.unknown())
-                .addProguardConfiguration(
-                    ImmutableList.of(keepMainProguardConfiguration(mainClass)), Origin.unknown())
-                .setProgramConsumer(emptyConsumer(backend))
-                .setProguardMapConsumer((string, ignore) -> proguardMapBuilder.append(string))
-                .build());
-
-    ProcessResult result = runOnVMRaw(output, mainClass, backend);
-    List<String> stackTrace = extractStackTrace(result);
-    List<String> retracesStackTrace = retrace(proguardMapBuilder.toString(), stackTrace);
-    checker.accept(stackTrace, retracesStackTrace);
-  }
-
-  @Test
-  public void test() throws Exception {
-    runTest(
-        Main.class,
-        (List<String> stackTrace, List<String> retracesStackTrace) -> {
-          assertEquals(
-              mode == CompilationMode.RELEASE, stackTrace.size() != retracesStackTrace.size());
-          if (mode == CompilationMode.DEBUG) {
-            assertThat(stackTrace.get(0), not(containsString("method2")));
-            assertThat(stackTrace.get(1), not(containsString("method1")));
-            assertThat(stackTrace.get(2), containsString("main"));
-          }
-          assertEquals(3, retracesStackTrace.size());
-          assertThat(retracesStackTrace.get(0), containsString("method2"));
-          assertThat(retracesStackTrace.get(1), containsString("method1"));
-          assertThat(retracesStackTrace.get(2), containsString("main"));
-        });
-  }
-}
-
-class Main {
-  public static void method2(int i) {
-    System.out.println("In method2");
-    throw null;
-  }
-
-  public static void method1(String s) {
-    System.out.println("In method1");
-    for (int i = 0; i < 10; i++) {
-      method2(Integer.parseInt(s));
-    }
-  }
-
-  public static void main(String[] args) {
-    System.out.println("In main");
-    method1("1");
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
new file mode 100644
index 0000000..0d0c21b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
@@ -0,0 +1,58 @@
+// 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.naming.retrace;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import java.util.function.BiConsumer;
+import org.junit.Before;
+
+public abstract class RetraceTestBase extends TestBase {
+  protected Backend backend;
+  protected CompilationMode mode;
+
+  public RetraceTestBase(Backend backend, CompilationMode mode) {
+    this.backend = backend;
+    this.mode = mode;
+  }
+
+  public StackTrace expectedStackTrace;
+
+  public abstract Class<?> getMainClass();
+
+  @Before
+  public void setup() throws Exception {
+    // Get the expected stack trace by running on the JVM.
+    expectedStackTrace =
+        testForJvm()
+            .addTestClasspath()
+            .run(getMainClass())
+            .assertFailure()
+            .map(StackTrace::extractFromJvm);
+  }
+
+  public void runTest(String keepRule, BiConsumer<StackTrace, StackTrace> checker)
+      throws Exception {
+
+    R8TestRunResult result =
+        testForR8(backend)
+            .setMode(mode)
+            .enableProguardTestOptions()
+            .enableInliningAnnotations()
+            .addProgramClasses(getMainClass())
+            .addKeepMainRule(getMainClass())
+            .addKeepRules(keepRule)
+            .run(getMainClass())
+            .assertFailure();
+
+    // Extract actual stack trace and retraced stack trace from failed run result.
+    StackTrace actualStackTrace = StackTrace.extractFromArt(result.getStdErr());
+    StackTrace retracedStackTrace =
+        actualStackTrace.retrace(result.proguardMap(), temp.newFolder().toPath());
+
+    checker.accept(actualStackTrace, retracedStackTrace);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
new file mode 100644
index 0000000..3270e48
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -0,0 +1,438 @@
+// 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.naming.retrace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.base.Equivalence;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+class StackTrace {
+
+  public static String AT_PREFIX = "at ";
+  public static String TAB_AT_PREFIX = "\t" + AT_PREFIX;
+
+  static class StackTraceLine {
+    public final String originalLine;
+    public final String className;
+    public final String methodName;
+    public final String fileName;
+    public final int lineNumber;
+
+    public StackTraceLine(
+        String originalLine, String className, String methodName, String fileName, int lineNumber) {
+      this.originalLine = originalLine;
+      this.className = className;
+      this.methodName = methodName;
+      this.fileName = fileName;
+      this.lineNumber = lineNumber;
+    }
+
+    public static StackTraceLine parse(String line) {
+      String originalLine = line;
+
+      line = line.trim();
+      if (line.startsWith(AT_PREFIX)) {
+        line = line.substring(AT_PREFIX.length());
+      }
+
+      // Expect only one '(', and only one ')' with a ':' in between.
+      int parenBeginIndex = line.indexOf('(');
+      assertTrue(parenBeginIndex > 0);
+      assertEquals(parenBeginIndex, line.lastIndexOf('('));
+      int parenEndIndex = line.indexOf(')');
+      assertTrue(parenBeginIndex < parenEndIndex);
+      assertEquals(parenEndIndex, line.lastIndexOf(')'));
+      int colonIndex = line.indexOf(':');
+      assertTrue(parenBeginIndex < colonIndex && colonIndex < parenEndIndex);
+      assertEquals(parenEndIndex, line.lastIndexOf(')'));
+      String classAndMethod = line.substring(0, parenBeginIndex);
+      int lastDotIndex = classAndMethod.lastIndexOf('.');
+      assertTrue(lastDotIndex > 0);
+      String className = classAndMethod.substring(0, lastDotIndex);
+      String methodName = classAndMethod.substring(lastDotIndex + 1, classAndMethod.length());
+      String fileName = line.substring(parenBeginIndex + 1, colonIndex);
+      int lineNumber = Integer.parseInt(line.substring(colonIndex + 1, parenEndIndex));
+      StackTraceLine result =
+          new StackTraceLine(originalLine, className, methodName, fileName, lineNumber);
+      assertEquals(line, result.toString());
+      return result;
+    }
+
+    @Override
+    public int hashCode() {
+      return className.hashCode() * 31
+          + methodName.hashCode() * 13
+          + fileName.hashCode() * 7
+          + lineNumber;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (other instanceof StackTraceLine) {
+        StackTraceLine o = (StackTraceLine) other;
+        return className.equals(o.className)
+            && methodName.equals(o.methodName)
+            && fileName.equals(o.fileName)
+            && lineNumber == o.lineNumber;
+      }
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return className + '.' + methodName + '(' + fileName + ':' + lineNumber + ')';
+    }
+  }
+
+  private final List<StackTraceLine> stackTraceLines;
+
+  private StackTrace(List<StackTraceLine> stackTraceLines) {
+    assert stackTraceLines.size() > 0;
+    this.stackTraceLines = stackTraceLines;
+  }
+
+  public int size() {
+    return stackTraceLines.size();
+  }
+
+  public StackTraceLine get(int index) {
+    return stackTraceLines.get(index);
+  }
+
+  public static StackTrace extractFromArt(String stderr) {
+    List<StackTraceLine> stackTraceLines = new ArrayList<>();
+    List<String> stderrLines = StringUtils.splitLines(stderr);
+
+    // A Dalvik stacktrace looks like this (apparently the bottom frame
+    // "dalvik.system.NativeStart.main" is not always present)
+    // W(209693) threadid=1: thread exiting with uncaught exception (group=0xf616cb20)  (dalvikvm)
+    // java.lang.NullPointerException
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:133)
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:139)
+    // \tat com.android.tools.r8.naming.retrace.Main.main(:145)
+    // \tat dalvik.system.NativeStart.main(Native Method)
+    //
+    // An Art 5.1.1 and 6.0.1 stacktrace looks like this:
+    // java.lang.NullPointerException: throw with null exception
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:154)
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:160)
+    // \tat com.android.tools.r8.naming.retrace.Main.main(:166)
+    //
+    // An Art 7.0.0 and latest stacktrace looks like this:
+    // Exception in thread "main" java.lang.NullPointerException: throw with null exception
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:150)
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:156)
+    // \tat com.android.tools.r8.naming.retrace.Main.main(:162)
+    int last = stderrLines.size();
+    // Skip the bottom frame "dalvik.system.NativeStart.main" if present.
+    if (ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)
+        && stderrLines.get(last - 1).contains("dalvik.system.NativeStart.main")) {
+      last--;
+    }
+    // Take all lines from the bottom starting with "\tat ".
+    int first = last;
+    while (first - 1 >= 0 && stderrLines.get(first - 1).startsWith(TAB_AT_PREFIX)) {
+      first--;
+    }
+    for (int i = first; i < last; i++) {
+      stackTraceLines.add(StackTraceLine.parse(stderrLines.get(i)));
+    }
+    return new StackTrace(stackTraceLines);
+  }
+
+  public static StackTrace extractFromJvm(String stderr) {
+    return new StackTrace(
+        StringUtils.splitLines(stderr).stream()
+            .filter(s -> s.startsWith(TAB_AT_PREFIX))
+            .map(StackTraceLine::parse)
+            .collect(Collectors.toList()));
+  }
+
+  public static StackTrace extractFromJvm(TestRunResult result) {
+    assertNotEquals(0, result.getExitCode());
+    return extractFromJvm(result.getStdErr());
+  }
+
+  public StackTrace retrace(String map, Path tempFolder) throws IOException {
+    Path mapFile = tempFolder.resolve("map");
+    Path stackTraceFile = tempFolder.resolve("stackTrace");
+    FileUtils.writeTextFile(mapFile, map);
+    FileUtils.writeTextFile(
+        stackTraceFile,
+        stackTraceLines.stream().map(line -> line.originalLine).collect(Collectors.toList()));
+    return StackTrace.extractFromJvm(ToolHelper.runRetrace(mapFile, stackTraceFile));
+  }
+
+  @Override
+  public int hashCode() {
+    return stackTraceLines.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof StackTrace) {
+      return stackTraceLines.equals(((StackTrace) other).stackTraceLines);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public String toString() {
+    return toStringWithPrefix("");
+  }
+
+  public String toStringWithPrefix(String prefix) {
+    StringBuilder builder = new StringBuilder();
+    for (StackTraceLine stackTraceLine : stackTraceLines) {
+      builder.append(prefix).append(stackTraceLine).append(System.lineSeparator());
+    }
+    return builder.toString();
+  }
+
+  public abstract static class StackTraceEquivalence extends Equivalence<StackTrace> {
+    public abstract Equivalence<StackTrace.StackTraceLine> getLineEquivalence();
+
+    @Override
+    protected boolean doEquivalent(StackTrace a, StackTrace b) {
+      if (a.stackTraceLines.size() != b.stackTraceLines.size()) {
+        return false;
+      }
+      for (int i = 0; i < a.stackTraceLines.size(); i++) {
+        if (!getLineEquivalence().equivalent(a.stackTraceLines.get(i), b.stackTraceLines.get(i))) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    protected int doHash(StackTrace stackTrace) {
+      int hashCode = stackTrace.size() * 13;
+      for (StackTrace.StackTraceLine stackTraceLine : stackTrace.stackTraceLines) {
+        hashCode += (hashCode << 4) + getLineEquivalence().hash(stackTraceLine);
+      }
+      return hashCode;
+    }
+  }
+
+  // Equivalence forwarding to default stack trace comparison.
+  public static class EquivalenceFull extends StackTraceEquivalence {
+
+    private static class LineEquivalence extends Equivalence<StackTrace.StackTraceLine> {
+
+      private static final LineEquivalence INSTANCE = new LineEquivalence();
+
+      public static LineEquivalence get() {
+        return INSTANCE;
+      }
+
+      @Override
+      protected boolean doEquivalent(StackTrace.StackTraceLine a, StackTrace.StackTraceLine b) {
+        return a.equals(b);
+      }
+
+      @Override
+      protected int doHash(StackTrace.StackTraceLine stackTraceLine) {
+        return stackTraceLine.hashCode();
+      }
+    }
+
+    private static final EquivalenceFull INSTANCE = new EquivalenceFull();
+
+    public static EquivalenceFull get() {
+      return INSTANCE;
+    }
+
+    @Override
+    public Equivalence<StackTrace.StackTraceLine> getLineEquivalence() {
+      return LineEquivalence.get();
+    }
+
+    @Override
+    protected boolean doEquivalent(StackTrace a, StackTrace b) {
+      return a.equals(b);
+    }
+
+    @Override
+    protected int doHash(StackTrace stackTrace) {
+      return stackTrace.hashCode();
+    }
+  }
+
+  // Equivalence comparing stack traces without taking the file name into account.
+  public static class EquivalenceWithoutFileName extends StackTraceEquivalence {
+
+    private static class LineEquivalence extends Equivalence<StackTrace.StackTraceLine> {
+
+      private static final LineEquivalence INSTANCE = new LineEquivalence();
+
+      public static LineEquivalence get() {
+        return INSTANCE;
+      }
+
+      @Override
+      protected boolean doEquivalent(StackTrace.StackTraceLine a, StackTrace.StackTraceLine b) {
+        return a.className.equals(b.className)
+            && a.methodName.equals(b.methodName)
+            && a.lineNumber == b.lineNumber;
+      }
+
+      @Override
+      protected int doHash(StackTrace.StackTraceLine stackTraceLine) {
+        return stackTraceLine.className.hashCode() * 13
+            + stackTraceLine.methodName.hashCode() * 7
+            + stackTraceLine.lineNumber;
+      }
+    }
+
+    private static final EquivalenceWithoutFileName INSTANCE = new EquivalenceWithoutFileName();
+
+    public static EquivalenceWithoutFileName get() {
+      return INSTANCE;
+    }
+
+    @Override
+    public Equivalence<StackTrace.StackTraceLine> getLineEquivalence() {
+      return LineEquivalence.get();
+    }
+  }
+
+  // Equivalence comparing stack traces without taking the file name and line number into account.
+  public static class EquivalenceWithoutFileNameAndLineNumber extends StackTraceEquivalence {
+
+    private static final EquivalenceWithoutFileNameAndLineNumber INSTANCE =
+        new EquivalenceWithoutFileNameAndLineNumber();
+
+    public static EquivalenceWithoutFileNameAndLineNumber get() {
+      return INSTANCE;
+    }
+
+    public static class LineEquivalence extends Equivalence<StackTrace.StackTraceLine> {
+
+      private static final LineEquivalence INSTANCE = new LineEquivalence();
+
+      public static LineEquivalence get() {
+        return INSTANCE;
+      }
+
+      @Override
+      protected boolean doEquivalent(StackTrace.StackTraceLine a, StackTrace.StackTraceLine b) {
+        return a.className.equals(b.className) && a.methodName.equals(b.methodName);
+      }
+
+      @Override
+      protected int doHash(StackTrace.StackTraceLine stackTraceLine) {
+        return stackTraceLine.className.hashCode() * 13 + stackTraceLine.methodName.hashCode() * 7;
+      }
+    }
+
+    @Override
+    public Equivalence<StackTrace.StackTraceLine> getLineEquivalence() {
+      return LineEquivalence.get();
+    }
+  }
+
+  public static class StackTraceMatcherBase extends TypeSafeMatcher<StackTrace> {
+    private final StackTrace expected;
+    private final StackTraceEquivalence equivalence;
+    private final String comparisonDescription;
+
+    private StackTraceMatcherBase(
+        StackTrace expected, StackTraceEquivalence equivalence, String comparisonDescription) {
+      this.expected = expected;
+      this.equivalence = equivalence;
+      this.comparisonDescription = comparisonDescription;
+    }
+
+    @Override
+    public boolean matchesSafely(StackTrace stackTrace) {
+      return equivalence.equivalent(expected, stackTrace);
+    }
+
+    @Override
+    public void describeTo(Description description) {
+      description
+          .appendText("stacktrace " + comparisonDescription)
+          .appendText(System.lineSeparator())
+          .appendText(expected.toString());
+    }
+
+    @Override
+    public void describeMismatchSafely(final StackTrace stackTrace, Description description) {
+      description.appendText("stacktrace was " + stackTrace);
+      description.appendText(System.lineSeparator());
+      if (expected.size() != stackTrace.size()) {
+        description.appendText("They have different sizes.");
+      } else {
+        for (int i = 0; i < expected.size(); i++) {
+          if (!equivalence.getLineEquivalence().equivalent(expected.get(i), stackTrace.get(i))) {
+            description
+                .appendText("First different entry is index " + i + ":")
+                .appendText(System.lineSeparator())
+                .appendText("Expected: " + expected.get(i))
+                .appendText(System.lineSeparator())
+                .appendText("     Was: " + stackTrace.get(i));
+            return;
+          }
+        }
+      }
+    }
+  }
+
+  public static class StackTraceMatcher extends StackTraceMatcherBase {
+    private StackTraceMatcher(StackTrace expected) {
+      super(expected, EquivalenceFull.get(), "");
+    }
+  }
+
+  public static Matcher<StackTrace> isSame(StackTrace stackTrace) {
+    return new StackTraceMatcher(stackTrace);
+  }
+
+  public static class StackTraceIgnoreFileNameMatcher extends StackTraceMatcherBase {
+    private StackTraceIgnoreFileNameMatcher(StackTrace expected) {
+      super(expected, EquivalenceWithoutFileName.get(), "(ignoring file name)");
+    }
+  }
+
+  public static Matcher<StackTrace> isSameExceptForFileName(StackTrace stackTrace) {
+    return new StackTraceIgnoreFileNameMatcher(stackTrace);
+  }
+
+  public static class StackTraceIgnoreFileNameAndLineNumberMatcher extends StackTraceMatcherBase {
+    private StackTraceIgnoreFileNameAndLineNumberMatcher(StackTrace expected) {
+      super(
+          expected,
+          EquivalenceWithoutFileNameAndLineNumber.get(),
+          "(ignoring file name and line number)");
+    }
+  }
+
+  public static Matcher<StackTrace> isSameExceptForFileNameAndLineNumber(StackTrace stackTrace) {
+    return new StackTraceIgnoreFileNameAndLineNumberMatcher(stackTrace);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTraceTest.java
new file mode 100644
index 0000000..e7ff8eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTraceTest.java
@@ -0,0 +1,68 @@
+// 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.TAB_AT_PREFIX;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+public class StackTraceTest {
+
+  private static String oneLineStackTrace =
+      TAB_AT_PREFIX + "Test.main(Test.java:10)" + System.lineSeparator();
+  private static String twoLineStackTrace =
+      TAB_AT_PREFIX + "Test.a(Test.java:6)" + System.lineSeparator()
+          + TAB_AT_PREFIX + "Test.main(Test.java:10)" + System.lineSeparator();
+
+  private void testEquals(String stderr) {
+    StackTrace stackTrace = StackTrace.extractFromJvm(stderr);
+    assertEquals(stackTrace, stackTrace);
+    assertEquals(stackTrace, StackTrace.extractFromJvm(stderr));
+  }
+
+  @Test
+  public void testOneLine() {
+    StackTrace stackTrace = StackTrace.extractFromJvm(oneLineStackTrace);
+    assertEquals(1, stackTrace.size());
+    StackTraceLine stackTraceLine = stackTrace.get(0);
+    assertEquals("Test", stackTraceLine.className);
+    assertEquals("main", stackTraceLine.methodName);
+    assertEquals("Test.java", stackTraceLine.fileName);
+    assertEquals(10, stackTraceLine.lineNumber);
+    assertEquals(StringUtils.splitLines(oneLineStackTrace).get(0), stackTraceLine.originalLine);
+    assertEquals(oneLineStackTrace, stackTrace.toStringWithPrefix(TAB_AT_PREFIX));
+  }
+
+  @Test
+  public void testTwoLine() {
+    StackTrace stackTrace = StackTrace.extractFromJvm(twoLineStackTrace);
+    StackTraceLine stackTraceLine = stackTrace.get(0);
+    assertEquals("Test", stackTraceLine.className);
+    assertEquals("a", stackTraceLine.methodName);
+    assertEquals("Test.java", stackTraceLine.fileName);
+    assertEquals(6, stackTraceLine.lineNumber);
+    assertEquals(StringUtils.splitLines(twoLineStackTrace).get(0), stackTraceLine.originalLine);
+    stackTraceLine = stackTrace.get(1);
+    assertEquals("Test", stackTraceLine.className);
+    assertEquals("main", stackTraceLine.methodName);
+    assertEquals("Test.java", stackTraceLine.fileName);
+    assertEquals(10, stackTraceLine.lineNumber);
+    assertEquals(StringUtils.splitLines(twoLineStackTrace).get(1), stackTraceLine.originalLine);
+    assertEquals(twoLineStackTrace, stackTrace.toStringWithPrefix(TAB_AT_PREFIX));
+  }
+
+  @Test
+  public void testEqualsOneLine() {
+    testEquals(oneLineStackTrace);
+  }
+
+  @Test
+  public void testEqualsTwoLine() {
+    testEquals(twoLineStackTrace);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
index 6ee7392..4bd0776 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232_WithPhi.java
@@ -52,6 +52,7 @@
         case V4_4_4:
         case V7_0_0:
         case V8_1_0:
+        case V9_0_0:
         case DEFAULT:
           assertNotEquals(-1, d8Result.stderr.indexOf("java.lang.VerifyError"));
           assertNotEquals(-1, r8Result.stderr.indexOf("java.lang.VerifyError"));
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index a603f92..d300a4e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -102,12 +103,6 @@
     assertTrue(methodSubject.getMethod().accessFlags.isPublic());
 
     classSubject = codeInspector.clazz(ClassForSubsequent.class);
-    if (shrinker.isR8()) {
-      // TODO(b/117330692): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
-      // at the 2nd tree shaking.
-      assertThat(classSubject, not(isPresent()));
-      return;
-    }
     assertThat(classSubject, isPresent());
     methodSubject = classSubject.method(publicMethod);
     assertThat(methodSubject, isPresent());
@@ -141,18 +136,12 @@
     assertTrue(methodSubject.getMethod().accessFlags.isPublic());
 
     classSubject = codeInspector.clazz(ClassForSubsequent.class);
-    if (shrinker.isR8()) {
-      // TODO(b/117330692): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
-      // at the 2nd tree shaking.
-      assertThat(classSubject, not(isPresent()));
-      return;
-    }
     assertThat(classSubject, isPresent());
     methodSubject = classSubject.method(publicMethod);
     assertThat(methodSubject, not(isPresent()));
     methodSubject = classSubject.method(nonPublicMethod);
     assertThat(methodSubject, isPresent());
-    assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+    assertEquals(shrinker.isR8(), methodSubject.getMethod().accessFlags.isPublic());
   }
 
   @Test
@@ -217,12 +206,6 @@
     assertThat(methodSubject, not(isPresent()));
     methodSubject = classSubject.method(nonPublicMethod);
     assertThat(methodSubject, isPresent());
-    if (shrinker.isR8()) {
-      // TODO(b/117330692): if kept in the 1st tree shaking, should not be publicized.
-      assertTrue(methodSubject.getMethod().accessFlags.isPublic());
-      return;
-    }
-    assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+    assertEquals(shrinker.isR8(), methodSubject.getMethod().accessFlags.isPublic());
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxation.java b/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxation.java
new file mode 100644
index 0000000..cf6d7c0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxation.java
@@ -0,0 +1,65 @@
+// 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.ifrule.accessrelaxation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IfRuleWithAccessRelaxation extends TestBase {
+
+  private final Backend backend;
+
+  public IfRuleWithAccessRelaxation(Backend backend) {
+    this.backend = backend;
+  }
+
+  @Parameters(name = "Backend: {0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  @Test
+  public void r8Test() throws Exception {
+    CodeInspector inspector =
+        testForR8(backend)
+            .addInnerClasses(IfRuleWithAccessRelaxation.class)
+            .addKeepRules(
+                "-keep class " + TestClass.class.getTypeName() + " { int field; void method(); }",
+                "-if class " + TestClass.class.getTypeName() + " { protected int field; }",
+                "-keep class " + Unused1.class.getTypeName(),
+                "-if class " + TestClass.class.getTypeName() + " { protected void method(); }",
+                "-keep class " + Unused2.class.getTypeName(),
+                "-allowaccessmodification")
+            .compile()
+            .inspector();
+
+    assertTrue(inspector.clazz(TestClass.class).isPublic());
+    assertTrue(inspector.clazz(TestClass.class).uniqueMethodWithName("method").isPublic());
+    assertTrue(inspector.clazz(TestClass.class).uniqueFieldWithName("field").isPublic());
+
+    assertThat(inspector.clazz(Unused1.class), isPresent());
+    assertThat(inspector.clazz(Unused2.class), isPresent());
+  }
+
+  protected static class TestClass {
+
+    protected int field = 42;
+
+    protected void method() {}
+  }
+
+  static class Unused1 {}
+
+  static class Unused2 {}
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 13e6232..0da4787 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -37,6 +37,11 @@
   }
 
   @Override
+  public FieldSubject uniqueFieldWithName(String name) {
+    return new AbsentFieldSubject();
+  }
+
+  @Override
   public boolean isAbstract() {
     return false;
   }
@@ -47,6 +52,11 @@
   }
 
   @Override
+  public boolean isPublic() {
+    return false;
+  }
+
+  @Override
   public DexClass getDexClass() {
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index daf64de..1c9fe9f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -70,6 +70,8 @@
 
   public abstract FieldSubject field(String type, String name);
 
+  public abstract FieldSubject uniqueFieldWithName(String name);
+
   public FoundClassSubject asFoundClassSubject() {
     return null;
   }
@@ -78,6 +80,8 @@
 
   public abstract boolean isAnnotation();
 
+  public abstract boolean isPublic();
+
   public String dumpMethods() {
     StringBuilder dump = new StringBuilder();
     forAllMethods(
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index ed58328..1167d8f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.naming.ClassNamingForNameMapper;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -144,6 +143,18 @@
   }
 
   @Override
+  public FieldSubject uniqueFieldWithName(String name) {
+    FieldSubject fieldSubject = null;
+    for (FoundFieldSubject candidate : allFields()) {
+      if (candidate.getOriginalName().equals(name)) {
+        assert fieldSubject == null;
+        fieldSubject = candidate;
+      }
+    }
+    return fieldSubject != null ? fieldSubject : new AbsentFieldSubject();
+  }
+
+  @Override
   public FoundClassSubject asFoundClassSubject() {
     return this;
   }
@@ -154,6 +165,11 @@
   }
 
   @Override
+  public boolean isPublic() {
+    return dexClass.accessFlags.isPublic();
+  }
+
+  @Override
   public boolean isAnnotation() {
     return dexClass.accessFlags.isAnnotation();
   }
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index 7d503a5..9b7e3f5 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 7d503a5..9b7e3f5 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/archive.py b/tools/archive.py
index 07f138a..ad58bca 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -12,6 +12,7 @@
 import toolhelper
 import utils
 import zipfile
+from build_r8lib import build_r8lib
 
 ARCHIVE_BUCKET = 'r8-releases'
 
@@ -80,15 +81,23 @@
 def Main():
   if not 'BUILDBOT_BUILDERNAME' in os.environ:
     raise Exception('You are not a bot, don\'t archive builds')
-  # Create maven release first which uses a build that exclude dependencies.
+
+  # Generate an r8-ed build without dependencies.
+  # Note: build_r8lib does a gradle-clean, this must be the first command.
+  build_r8lib('r8', True, True, utils.R8LIB_KEEP_RULES, utils.R8_EXCLUDE_DEPS_JAR)
+
+  # Create maven release which uses a build that exclude dependencies.
   create_maven_release.main(["--out", utils.LIBS])
 
-  # Generate and copy the build that exclude dependencies.
+  # Generate and copy a full build without dependencies.
   gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
-  shutil.copyfile(utils.R8_JAR, utils.R8_EXCLUDE_DEPS_JAR)
+  shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
 
   # Ensure all archived artifacts has been built before archiving.
-  gradle.RunGradle([utils.D8, utils.R8, utils.COMPATDX, utils.COMPATPROGUARD])
+  # The target tasks postfixed by 'r8' depend on the actual target task so
+  # building it invokes the original task first.
+  gradle.RunGradle(map((lambda t: t + 'r8'),
+    [utils.D8, utils.R8, utils.COMPATDX, utils.COMPATPROGUARD]))
   version = GetVersion()
   is_master = IsMaster(version)
   if is_master:
@@ -107,12 +116,13 @@
           'releaser=go/r8bot (' + os.environ.get('BUILDBOT_SLAVENAME') + ')\n')
       version_writer.write('version-file.version.code=1\n')
 
-    for file in [utils.D8_JAR,
-                 utils.R8_JAR,
+    for file in [utils.D8_JAR, utils.D8R8_JAR,
+                 utils.R8_JAR, utils.R8R8_JAR,
                  utils.R8_SRC_JAR,
+                 utils.R8_FULL_EXCLUDE_DEPS_JAR,
                  utils.R8_EXCLUDE_DEPS_JAR,
-                 utils.COMPATDX_JAR,
-                 utils.COMPATPROGUARD_JAR,
+                 utils.COMPATDX_JAR, utils.COMPATDXR8_JAR,
+                 utils.COMPATPROGUARD_JAR, utils.COMPATPROGUARDR8_JAR,
                  utils.MAVEN_ZIP,
                  utils.GENERATED_LICENSE]:
       file_name = os.path.basename(file)
diff --git a/tools/archive_logs.py b/tools/archive_logs.py
new file mode 100755
index 0000000..bdae32e
--- /dev/null
+++ b/tools/archive_logs.py
@@ -0,0 +1,11 @@
+#!/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.
+
+# Script for achiving gradle test logs.
+
+import utils
+
+if __name__ == '__main__':
+  utils.archive_failures()
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index 1512a64..095872b 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -42,6 +42,29 @@
   <continue with repo sync as above>
 
 
+art-9.0.0 (Android P)
+---------------------
+Build from branch android-9.0.0_r18.
+
+export BRANCH=android-9.0.0_r18
+mkdir ${BRANCH}
+cd ${BRANCH}
+repo init -u https://android.googlesource.com/platform/manifest -b ${BRANCH}
+repo sync -cq -j24
+source build/envsetup.sh
+lunch aosp_marlin-userdebug
+m -j24
+m -j24 build-art
+m -j24 test-art-host
+
+Collected into tools/linux/art-9.0.0.
+
+  cd <r8 checkout>
+  scripts/update-host-art.sh \
+    --android-checkout /usr/local/ssd/android/${BRANCH} \
+    --art-dir art-9.0.0 \
+    --android-product marlin
+
 art-8.1.0 (Android O MR1)
 -------------------------
 Build from branch android-8.1.0_r51.
@@ -59,6 +82,7 @@
 
 Collected into tools/linux/art-8.1.0.
 
+  cd <r8 checkout>
   scripts/update-host-art.sh \
     --android-checkout /usr/local/ssd/android/${BRANCH} \
     --art-dir art-8.1.0 \
diff --git a/tools/linux/art-9.0.0.tar.gz.sha1 b/tools/linux/art-9.0.0.tar.gz.sha1
new file mode 100644
index 0000000..788e039
--- /dev/null
+++ b/tools/linux/art-9.0.0.tar.gz.sha1
@@ -0,0 +1 @@
+bfd3d6f4c7d7245d93eecdcc386b41299266e2d1
\ No newline at end of file
diff --git a/tools/run-jdwp-tests.py b/tools/run-jdwp-tests.py
index db69197..2ef479d 100755
--- a/tools/run-jdwp-tests.py
+++ b/tools/run-jdwp-tests.py
@@ -15,6 +15,7 @@
 
 VERSIONS = [
   'default',
+  '9.0.0',
   '8.1.0',
   '7.0.0',
   '6.0.1',
@@ -101,6 +102,8 @@
     flags.extend(['-Ximage:%s' % IMAGE])
     if version != '5.1.1':
       flags.extend(['-Xcompiler-option', '--debuggable'])
+  if version == '9.0.0':
+    flags.extend(['-XjdwpProvider:internal'])
   return flags
 
 def get_debuggee_flags(version):
diff --git a/tools/test.py b/tools/test.py
index 31b2134..e4433b3 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -13,13 +13,17 @@
 import subprocess
 import sys
 import utils
-import uuid
 import notify
-import upload_to_x20
 
-
-ALL_ART_VMS = ["default", "8.1.0", "7.0.0", "6.0.1", "5.1.1", "4.4.4", "4.0.4"]
-BUCKET = 'r8-test-results'
+ALL_ART_VMS = [
+    "default",
+    "9.0.0",
+    "8.1.0",
+    "7.0.0",
+    "6.0.1",
+    "5.1.1",
+    "4.4.4",
+    "4.0.4"]
 
 def ParseOptions():
   result = optparse.OptionParser()
@@ -89,15 +93,6 @@
 
   return result.parse_args()
 
-def archive_failures():
-  upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
-  u_dir = uuid.uuid4()
-  destination = 'gs://%s/%s' % (BUCKET, u_dir)
-  utils.upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
-  url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, u_dir)
-  print 'Test results available at: %s' % url
-  print '@@@STEP_LINK@Test failures@%s@@@' % url
-
 def Main():
   (options, args) = ParseOptions()
   if 'BUILDBOT_BUILDERNAME' in os.environ:
@@ -193,7 +188,7 @@
 
     if return_code != 0:
       if options.archive_failures and os.name != 'nt':
-        archive_failures()
+        utils.archive_failures()
       return return_code
 
   return 0
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index 7635ba6..395a4b4 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -35,7 +35,8 @@
     copyfile(src, dest)
 
 def copy_jar_targets(root, target_root):
-  srcs = map((lambda t: t + '.jar'), JAR_TARGETS)
+  # With the '-r8' postfix we're using the R8-processed jars.
+  srcs = map((lambda t: t + '-r8.jar'), JAR_TARGETS)
   dests = map((lambda t: t + '-master.jar'), JAR_TARGETS)
   copy_targets(root, target_root, srcs, dests)
 
@@ -61,12 +62,13 @@
   args = parse_arguments()
   target_root = args.android_root[0]
   if args.commit_hash == None and args.version == None:
-    gradle.RunGradle(JAR_TARGETS)
+    gradle.RunGradle(map((lambda t: t + 'r8'), JAR_TARGETS))
     copy_jar_targets(utils.LIBS, target_root)
     copy_other_targets(utils.GENERATED_LICENSE_DIR, target_root)
   else:
     assert args.commit_hash == None or args.version == None
-    targets = map((lambda t: t + '.jar'), JAR_TARGETS) + OTHER_TARGETS
+    # With the '-r8' postfix we're using the R8-processed jars.
+    targets = map((lambda t: t + '-r8.jar'), JAR_TARGETS) + OTHER_TARGETS
     with utils.TempDir() as root:
       for target in targets:
         if args.commit_hash:
diff --git a/tools/utils.py b/tools/utils.py
index d28643d..d2c4935 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -12,6 +12,7 @@
 import sys
 import tarfile
 import tempfile
+import uuid
 
 ANDROID_JAR = 'third_party/android_jar/lib-v{api}/android.jar'
 TOOLS_DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..')))
@@ -34,16 +35,23 @@
 COMPATPROGUARD = 'compatproguard'
 
 D8_JAR = os.path.join(LIBS, 'd8.jar')
+D8R8_JAR = os.path.join(LIBS, 'd8-r8.jar')
 R8_JAR = os.path.join(LIBS, 'r8.jar')
+R8R8_JAR = os.path.join(LIBS, 'r8-r8.jar')
 R8_SRC_JAR = os.path.join(LIBS, 'r8-src.jar')
 R8_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-exclude-deps.jar')
+R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
 COMPATDX_JAR = os.path.join(LIBS, 'compatdx.jar')
+COMPATDXR8_JAR = os.path.join(LIBS, 'compatdx-r8.jar')
 COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')
+COMPATPROGUARDR8_JAR = os.path.join(LIBS, 'compatproguard-r8.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')
 R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
 
+TEST_RESULT_BUCKET = 'r8-test-results'
+
 def PrintCmd(s):
   if type(s) is list:
     s = ' '.join(s)
@@ -180,6 +188,15 @@
  def __exit__(self, *_):
    shutil.rmtree(self._temp_dir, ignore_errors=True)
 
+def archive_failures():
+  upload_dir = os.path.join(REPO_ROOT, 'build', 'reports', 'tests')
+  u_dir = uuid.uuid4()
+  destination = 'gs://%s/%s' % (TEST_RESULT_BUCKET, u_dir)
+  upload_dir_to_cloud_storage(upload_dir, destination, is_html=True)
+  url = 'http://storage.googleapis.com/%s/%s/test/index.html' % (TEST_RESULT_BUCKET, u_dir)
+  print 'Test results available at: %s' % url
+  print '@@@STEP_LINK@Test failures@%s@@@' % url
+
 class ChangedWorkingDirectory(object):
  def __init__(self, working_directory):
    self._working_directory = working_directory