Merge "Reproduce b/122819537: kotlinc-generated synthetic methods are not traced."
diff --git a/build.gradle b/build.gradle
index 00b8a46..c205b2d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -676,9 +676,6 @@
 task testJar(type: ShadowJar, dependsOn: testClasses) {
     baseName = "r8tests"
     from sourceSets.test.output
-    if (!project.hasProperty('exclude_deps')) {
-        relocate('org.objectweb.asm', 'com.android.tools.r8.org.objectweb.asm')
-    }
 }
 
 task testJarNoDeps(type: ShadowJar, dependsOn: testClasses) {
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayData.java b/src/main/java/com/android/tools/r8/code/FillArrayData.java
index 4569897..a49a31d 100644
--- a/src/main/java/com/android/tools/r8/code/FillArrayData.java
+++ b/src/main/java/com/android/tools/r8/code/FillArrayData.java
@@ -44,4 +44,9 @@
   public String toSmaliString(ClassNameMapper naming) {
     return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
   }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 73f7921..5e71072 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -441,7 +441,7 @@
     dest.putShort((short) code.incomingRegisterSize);
     dest.putShort((short) code.outgoingRegisterSize);
     dest.putShort((short) code.tries.length);
-    dest.putInt(mixedSectionOffsets.getOffsetFor(code.getDebugInfo()));
+    dest.putInt(mixedSectionOffsets.getOffsetFor(code.getDebugInfoForWriting()));
     // Jump over the size.
     int insnSizeOffset = dest.position();
     dest.forward(4);
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index b754c9a..e02869d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -40,6 +40,7 @@
 
   public DexString highestSortingString;
   private DexDebugInfo debugInfo;
+  private DexDebugInfoForWriting debugInfoForWriting;
 
   public DexCode(
       int registerSize,
@@ -97,6 +98,9 @@
 
   public void setDebugInfo(DexDebugInfo debugInfo) {
     this.debugInfo = debugInfo;
+    if (debugInfoForWriting != null) {
+      debugInfoForWriting = null;
+    }
   }
 
   public DexDebugInfo debugInfoWithAdditionalFirstParameter(DexString name) {
@@ -382,7 +386,7 @@
       }
     }
     if (debugInfo != null) {
-      debugInfo.collectIndexedItems(indexedItems);
+      getDebugInfoForWriting().collectIndexedItems(indexedItems);
     }
     if (handlers != null) {
       for (TryHandler handler : handlers) {
@@ -391,6 +395,17 @@
     }
   }
 
+  public DexDebugInfoForWriting getDebugInfoForWriting() {
+    if (debugInfo == null) {
+      return null;
+    }
+    if (debugInfoForWriting == null) {
+      debugInfoForWriting = new DexDebugInfoForWriting(debugInfo);
+    }
+
+    return debugInfoForWriting;
+  }
+
   private void updateHighestSortingString(DexString candidate) {
     assert candidate != null;
     if (highestSortingString == null || highestSortingString.slowCompareTo(candidate) < 0) {
@@ -406,7 +421,7 @@
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     if (mixedItems.add(this)) {
       if (debugInfo != null) {
-        debugInfo.collectMixedSectionItems(mixedItems);
+        getDebugInfoForWriting().collectMixedSectionItems(mixedItems);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
new file mode 100644
index 0000000..0777b85
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfoForWriting.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
+import java.util.Arrays;
+
+/**
+ * Wraps DexDebugInfo to make comparison and hashcode not consider
+ * the SetInlineFrames
+ */
+public class DexDebugInfoForWriting extends DexDebugInfo {
+
+  public DexDebugInfoForWriting(DexDebugInfo dexDebugInfo) {
+    super(dexDebugInfo.startLine, dexDebugInfo.parameters,
+        Arrays.stream(dexDebugInfo.events)
+            .filter(d -> !(d instanceof SetInlineFrame))
+            .toArray(DexDebugEvent[]::new));
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index abcf374..898afed 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -855,9 +855,11 @@
     @Override
     public String toString() {
       StringBuilder builder = new StringBuilder();
-      for (Map.Entry<DexType, DexType> entry : typeMap.entrySet()) {
-        builder.append(entry.getKey().toSourceString()).append(" -> ");
-        builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
+      if (typeMap != null) {
+        for (Map.Entry<DexType, DexType> entry : typeMap.entrySet()) {
+          builder.append(entry.getKey().toSourceString()).append(" -> ");
+          builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
+        }
       }
       for (Map.Entry<DexMethod, DexMethod> entry : methodMap.entrySet()) {
         builder.append(entry.getKey().toSourceString()).append(" -> ");
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 36df23e..3ce1467 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -118,6 +118,10 @@
     return true;
   }
 
+  public boolean hasLocalVariableTable() {
+    return getNode().localVariables != null && !getNode().localVariables.isEmpty();
+  }
+
   @Override
   public IRCode buildIR(
       DexEncodedMethod encodedMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index de0f48c..ed51913 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.code.FillArrayDataPayload;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -74,9 +73,8 @@
   }
 
   @Override
-  public boolean canBeDeadCode(AppInfo appInfo, IRCode code) {
-    // Side-effects its input values.
-    return false;
+  public boolean instructionTypeCanThrow() {
+    return true;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 6e5e5d7..4dcc839 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -149,7 +149,9 @@
     computeInitializers();
     typeVerificationHelper = new TypeVerificationHelper(code, factory, appInfo);
     typeVerificationHelper.computeVerificationTypes();
-    splitExceptionalBlocks();
+    if (!options.testing.noSplittingExceptionalEdges) {
+      splitExceptionalBlocks();
+    }
     rewriter.converter.deadCodeRemover.run(code);
     rewriteNots();
     LoadStoreHelper loadStoreHelper = new LoadStoreHelper(code, typeVerificationHelper, appInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 78e5e7e..881b255 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -356,6 +356,10 @@
       return index;
     }
     if (dex.canThrow()) {
+      // TODO(zerny): Remove this from block computation.
+      if (dex.hasPayload()) {
+        arrayFilledDataPayloadResolver.addPayloadUser((FillArrayData) dex);
+      }
       // If the instruction can throw and is in a try block, add edges to its catch successors.
       Try tryRange = getTryForOffset(offset);
       if (tryRange != null) {
@@ -398,10 +402,6 @@
       builder.ensureNormalSuccessorBlock(offset, offset + dex.getSize());
       return index;
     }
-    // TODO(zerny): Remove this from block computation.
-    if (dex.hasPayload()) {
-      arrayFilledDataPayloadResolver.addPayloadUser((FillArrayData) dex);
-    }
     // This instruction does not close the block.
     return -1;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 4035611..045ba3e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -1661,9 +1661,11 @@
   }
 
   public void addNewArrayFilledData(int arrayRef, int elementWidth, long size, short[] data) {
-    add(
+    NewArrayFilledData instruction =
         new NewArrayFilledData(
-            readRegister(arrayRef, ValueTypeConstraint.OBJECT), elementWidth, size, data));
+            readRegister(arrayRef, ValueTypeConstraint.OBJECT), elementWidth, size, data);
+    assert instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
   }
 
   public void addNewInstance(int dest, DexType type) {
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 e96acfb..b1ea3f9 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
@@ -879,6 +879,10 @@
     printC1VisualizerHeader(method);
     String previous = printMethod(code, "Initial IR (SSA)", null);
 
+    if (options.testing.irModifier != null) {
+      options.testing.irModifier.accept(code);
+    }
+
     if (options.canHaveArtStringNewInitBug()) {
       CodeRewriter.ensureDirectStringNewToInit(code);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5938781..7447f76 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2701,6 +2701,9 @@
           if (contents == null) {
             continue;
           }
+          if (block.hasCatchHandlers()) {
+            continue;
+          }
           int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
           NewArrayFilledData fillArray =
               new NewArrayFilledData(newArray.outValue(), elementSize, arraySize, contents);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 59d8717..f8ddcb6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -291,7 +291,7 @@
   //    invoke-virtual { s1, ... } mtd1
   //    goto Exit
   //  b2:
-  //    s2 <- static-get singleoton
+  //    s2 <- static-get singleton
   //    ...
   //    invoke-virtual { s2, ... } mtd1
   //    goto Exit
@@ -503,7 +503,7 @@
       }
     }
 
-    if (!methodMapping.isEmpty() || fieldMapping.isEmpty()) {
+    if (!methodMapping.isEmpty() || !fieldMapping.isEmpty()) {
       classStaticizer.converter.appView.setGraphLense(
           new ClassStaticizerGraphLense(
               classStaticizer.converter.graphLense(),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 47d3743..1b74e3b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -401,13 +401,22 @@
             && (unknownOption.equals("forceinline") || unknownOption.equals("neverinline"))) {
           devMessage = ", this option needs to be turned on explicitly if used for tests.";
         }
-        reporter.error(new StringDiagnostic(
-            "Unknown option \"-" + unknownOption + "\"" + devMessage,
-            origin, getPosition(optionStart)));
+        unknownOption(unknownOption, optionStart, devMessage);
       }
       return true;
     }
 
+    private void unknownOption(String unknownOption, TextPosition optionStart) {
+      unknownOption(unknownOption, optionStart, "");
+    }
+
+    private void unknownOption(
+        String unknownOption, TextPosition optionStart, String additionalMessage) {
+      throw reporter.fatalError((new StringDiagnostic(
+          "Unknown option \"-" + unknownOption + "\"" + additionalMessage,
+          origin, getPosition(optionStart))));
+    }
+
     private boolean parseUnsupportedOptionAndErr(TextPosition optionStart) {
       String option = Iterables.find(UNSUPPORTED_FLAG_OPTIONS, this::skipFlag, null);
       if (option != null) {
@@ -768,14 +777,19 @@
           TextPosition start = getPosition();
           acceptString("-");
           String unknownOption = acceptString();
-          throw reporter.fatalError(new StringDiagnostic(
-              "Unknown option \"-" + unknownOption + "\"",
-              origin,
-              start));
+          unknownOption(unknownOption, start);
         }
       } else {
         builder.setType(ProguardKeepRuleType.KEEP);
       }
+      if (!eof() && !Character.isWhitespace(peekChar()) && peekChar() != ',') {
+        // The only path to here is through "-keep" with an unsupported suffix.
+        unacceptString("-keep");
+        TextPosition start = getPosition();
+        acceptString("-");
+        String unknownOption = acceptString();
+        unknownOption(unknownOption, start);
+      }
       parseRuleModifiers(builder);
     }
 
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 4921c79..f34e059 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -532,6 +533,8 @@
     public boolean noLocalsTableOnInput = false;
     public boolean forceNameReflectionOptimization = false;
     public boolean disallowLoadStoreOptimization = false;
+    public Consumer<IRCode> irModifier = null;
+    public boolean noSplittingExceptionalEdges = false;
   }
 
   private boolean hasMinApi(AndroidApiLevel level) {
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 1daaad3..9dfb30d 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8;
 
+import static com.android.tools.r8.ToolHelper.CLASSPATH_SEPARATOR;
 import static com.android.tools.r8.ToolHelper.getJavaExecutable;
 import static org.junit.Assert.assertEquals;
 
@@ -76,7 +77,9 @@
 
       String classPath =
           addR8ExternalDeps
-              ? r8jar.toAbsolutePath().toString() + ":" + ToolHelper.DEPS_NOT_RELOCATED
+              ? r8jar.toAbsolutePath().toString()
+                  + CLASSPATH_SEPARATOR
+                  + ToolHelper.DEPS_NOT_RELOCATED
               : r8jar.toAbsolutePath().toString();
 
       List<String> command = new ArrayList<>();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e0baab1..c24ed3b 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -163,6 +163,8 @@
 
   /**
    * Write lines of text to a temporary file.
+   *
+   * The file will include a line separator after the last line.
    */
   protected Path writeTextToTempFile(String... lines) throws IOException {
     return writeTextToTempFile(System.lineSeparator(), Arrays.asList(lines));
@@ -170,11 +172,28 @@
 
   /**
    * Write lines of text to a temporary file, along with the specified line separator.
+   *
+   * The file will include a line separator after the last line.
    */
   protected Path writeTextToTempFile(String lineSeparator, List<String> lines)
       throws IOException {
+    return writeTextToTempFile(lineSeparator, lines, true);
+  }
+
+  /**
+   * Write lines of text to a temporary file, along with the specified line separator.
+   *
+   * The argument <code>includeTerminatingLineSeparator</code> control if the file will include
+   * a line separator after the last line.
+   */
+  protected Path writeTextToTempFile(
+      String lineSeparator, List<String> lines, boolean includeTerminatingLineSeparator)
+      throws IOException {
     Path file = temp.newFile().toPath();
-    String contents = String.join(lineSeparator, lines) + lineSeparator;
+    String contents = String.join(lineSeparator, lines);
+    if (includeTerminatingLineSeparator) {
+      contents += lineSeparator;
+    }
     Files.write(file, contents.getBytes(StandardCharsets.UTF_8));
     return file;
   }
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
index 43d71b1..15c03dc 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessagesImpl.java
@@ -91,9 +91,9 @@
     StringBuilder builder = new StringBuilder("No warning matches " + matcher.toString());
     builder.append(System.lineSeparator());
     if (getWarnings().size() == 0) {
-      builder.append("There where no warnings.");
+      builder.append("There were no warnings.");
     } else {
-      builder.append("There where " + getWarnings().size() + " warnings:");
+      builder.append("There were " + getWarnings().size() + " warnings:");
       builder.append(System.lineSeparator());
       for (int i = 0; i < getWarnings().size(); i++) {
         builder.append(getWarnings().get(i).getDiagnosticMessage());
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 8fe189b..bab857c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -97,7 +97,8 @@
   public static final String SMALI_BUILD_DIR = TESTS_BUILD_DIR + "smali/";
 
   public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
-  public static final String PATH_SEPARATOR = File.pathSeparator;
+  public static final String CLASSPATH_SEPARATOR = File.pathSeparator;
+
   public static final String DEFAULT_DEX_FILENAME = "classes.dex";
   public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
 
@@ -1086,7 +1087,8 @@
   }
 
   public static ProcessResult runJava(List<Path> classpath, String... args) throws IOException {
-    String cp = classpath.stream().map(Path::toString).collect(Collectors.joining(PATH_SEPARATOR));
+    String cp =
+        classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR));
     List<String> cmdline = new ArrayList<String>(Arrays.asList(getJavaExecutable(), "-cp", cp));
     cmdline.addAll(Arrays.asList(args));
     ProcessBuilder builder = new ProcessBuilder(cmdline);
@@ -1106,7 +1108,8 @@
 
   public static ProcessResult runJavaNoVerify(
       List<Path> classpath, String mainClass, List<String> args) throws IOException {
-    String cp = classpath.stream().map(Path::toString).collect(Collectors.joining(PATH_SEPARATOR));
+    String cp =
+        classpath.stream().map(Path::toString).collect(Collectors.joining(CLASSPATH_SEPARATOR));
     ArrayList<String> cmdline = Lists.newArrayList(
         getJavaExecutable(), "-cp", cp, "-noverify", mainClass);
     cmdline.addAll(args);
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestLimitRange.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestLimitRange.java
new file mode 100644
index 0000000..5e97ab0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestLimitRange.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.NeverInline;
+
+public class TryRangeTestLimitRange {
+
+  @NeverInline
+  public static float doSomething(int x) throws Exception {
+    if (x == 42) {
+      throw new Exception("is 42");
+    } else {
+      return 1;
+    }
+  }
+
+  public static void main(String[] args) {
+    int x = args.length;
+    int y = x + 1;
+    try {
+      doSomething(y);
+    } catch (Exception ex) {
+      System.out.println(x + ": " + y);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index 98542ff..e4c7098 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -4,8 +4,14 @@
 
 package com.android.tools.r8.cf;
 
+import static org.junit.Assert.assertTrue;
+
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import java.util.ListIterator;
 import org.junit.Test;
 
 /**
@@ -18,7 +24,7 @@
 public class TryRangeTestRunner extends TestBase {
 
   @Test
-  public void test() throws Exception {
+  public void testRegisterAllocationLimitTrailingRange() throws Exception {
     testForR8(Backend.CF)
         .addProgramClasses(TryRangeTest.class)
         .addKeepMainRule(TryRangeTest.class)
@@ -26,8 +32,55 @@
         .minification(false)
         .noTreeShaking()
         .enableInliningAnnotations()
-        .addOptionsModification(o -> o.testing.disallowLoadStoreOptimization = true)
+        .addOptionsModification(
+            o -> {
+              o.testing.disallowLoadStoreOptimization = true;
+            })
         .run(TryRangeTest.class)
         .assertSuccess();
   }
+
+  @Test
+  public void testRegisterAllocationLimitLeadingRange() throws Exception {
+    testForR8(Backend.CF)
+        .addProgramClasses(TryRangeTestLimitRange.class)
+        .addKeepMainRule(TryRangeTestLimitRange.class)
+        .setMode(CompilationMode.RELEASE)
+        .minification(false)
+        .noTreeShaking()
+        .enableInliningAnnotations()
+        .addOptionsModification(
+            o -> {
+              o.testing.disallowLoadStoreOptimization = true;
+              o.testing.irModifier = this::processIR;
+              // TODO(mkroghj) Remove this option entirely when splittingExceptionalEdges is moved.
+              o.testing.noSplittingExceptionalEdges = true;
+            })
+        .run(TryRangeTestLimitRange.class)
+        .assertFailure();
+  }
+
+  private void processIR(IRCode code) {
+    if (!code.method.qualifiedName().equals(TryRangeTestLimitRange.class.getName() + ".main")) {
+      return;
+    }
+    BasicBlock entryBlock = code.blocks.get(0);
+    BasicBlock tryBlock = code.blocks.get(1);
+    assertTrue(tryBlock.hasCatchHandlers());
+    ListIterator<Instruction> it = entryBlock.getInstructions().listIterator();
+    Instruction constNumber = it.next();
+    while (!constNumber.isConstNumber()) {
+      constNumber = it.next();
+    }
+    it.remove();
+    Instruction add = it.next();
+    while (!add.isAdd()) {
+      add = it.next();
+    }
+    it.remove();
+    constNumber.setBlock(tryBlock);
+    add.setBlock(tryBlock);
+    tryBlock.getInstructions().add(0, add);
+    tryBlock.getInstructions().add(0, constNumber);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/debuginfo/CannonicalizeWithInline.java b/src/test/java/com/android/tools/r8/debuginfo/CannonicalizeWithInline.java
new file mode 100644
index 0000000..5d6e151
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/CannonicalizeWithInline.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.DexParser;
+import com.android.tools.r8.dex.DexSection;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CannonicalizeWithInline extends TestBase {
+
+  private int getNumberOfDebugInfos(Path file) throws IOException {
+    DexSection[] dexSections = DexParser.parseMapFrom(file);
+    for (DexSection dexSection : dexSections) {
+      if (dexSection.type == Constants.TYPE_DEBUG_INFO_ITEM) {
+        return dexSection.length;
+      }
+    }
+    return 0;
+  }
+
+  @Test
+  public void testCannonicalize() throws Exception {
+    Class clazzA = ClassA.class;
+    Class clazzB = ClassB.class;
+
+    R8TestCompileResult result = testForR8(Backend.DEX)
+        .addProgramClasses(clazzA, clazzB)
+        .addKeepRules(
+            "-keepattributes SourceFile,LineNumberTable",
+            "-keep class ** {\n" +
+                "public void call(int);\n" +
+            "}"
+        )
+        .compile();
+    Path classesPath = temp.getRoot().toPath();
+    result.app.write(classesPath, OutputMode.DexIndexed);
+    int numberOfDebugInfos = getNumberOfDebugInfos(
+        Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex"));
+    Assert.assertEquals(1, numberOfDebugInfos);
+  }
+
+  // Two classes which has debug info that looks exactly the same, except for SetInlineFrame.
+  // R8 will inline the call to foobar in both classes, causing us to store a SetInlineFrame in the
+  // debug info.
+  // Ensure that we still canonicalize when writing.
+  public static class ClassA {
+
+    public void call(int a) {
+        foobar(a);
+    }
+
+    private String foobar(int a) {
+      String s = "aFoobar" + a;
+      return s;
+    }
+  }
+
+  public static class ClassB {
+
+    public void call(int a) {
+      foobar(a);
+    }
+
+    private String foobar(int a) {
+      String s = "bFoobar" + a;
+      return s;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index 97047e8..8af2f8e 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
+import static com.android.tools.r8.ToolHelper.CLASSPATH_SEPARATOR;
+
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.errors.CompilationError;
@@ -30,8 +32,6 @@
 @RunWith(Parameterized.class)
 public class BasicTestDependenciesDesugaringTest {
 
-  private static final String CLASSPATH_SEPARATOR = File.pathSeparator;
-
   private static final String[] allLibs;
   static {
     try {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index df797ad..7b3ed4a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -29,11 +29,14 @@
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.CandidateConflictField;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.CandidateConflictMethod;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.CandidateOk;
+import com.android.tools.r8.ir.optimize.staticizer.movetohost.CandidateOkFieldOnly;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.CandidateOkSideEffects;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.HostConflictField;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.HostConflictMethod;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.HostOk;
+import com.android.tools.r8.ir.optimize.staticizer.movetohost.HostOkFieldOnly;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.HostOkSideEffects;
+import com.android.tools.r8.ir.optimize.staticizer.movetohost.MoveToHostFieldOnlyTestClass;
 import com.android.tools.r8.ir.optimize.staticizer.movetohost.MoveToHostTestClass;
 import com.android.tools.r8.ir.optimize.staticizer.trivial.Simple;
 import com.android.tools.r8.ir.optimize.staticizer.trivial.SimpleWithGetter;
@@ -150,6 +153,36 @@
   }
 
   @Test
+  public void testMoveToHost_fieldOnly() throws Exception {
+    assumeTrue("b/112831361", backend == Backend.DEX);
+    Class<?> main = MoveToHostFieldOnlyTestClass.class;
+    Class<?>[] classes = {
+        NeverInline.class,
+        MoveToHostFieldOnlyTestClass.class,
+        HostOkFieldOnly.class,
+        CandidateOkFieldOnly.class
+    };
+    TestRunResult result = testForR8(backend)
+        .addProgramClasses(classes)
+        .enableProguardTestOptions()
+        .enableInliningAnnotations()
+        .addKeepMainRule(main)
+        .noMinification()
+        .addKeepRules("-allowaccessmodification")
+        .addOptionsModification(this::configure)
+        .run(main);
+
+    CodeInspector inspector = result.inspector();
+    ClassSubject clazz = inspector.clazz(main);
+
+    assertEquals(
+        Lists.newArrayList(),
+        references(clazz, "testOk_fieldOnly", "void"));
+
+    assertFalse(inspector.clazz(CandidateOkFieldOnly.class).isPresent());
+  }
+
+  @Test
   public void testMoveToHost() throws Exception {
     assumeTrue("b/112831361", backend == Backend.DEX);
     Class<?> main = MoveToHostTestClass.class;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkFieldOnly.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkFieldOnly.java
new file mode 100644
index 0000000..48963f4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOkFieldOnly.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+
+public class CandidateOkFieldOnly {
+  // No instance methods.
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostOkFieldOnly.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostOkFieldOnly.java
new file mode 100644
index 0000000..e132627
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/HostOkFieldOnly.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+
+public class HostOkFieldOnly {
+  static CandidateOkFieldOnly INSTANCE = new CandidateOkFieldOnly();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostFieldOnlyTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostFieldOnlyTestClass.java
new file mode 100644
index 0000000..f725d21
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/MoveToHostFieldOnlyTestClass.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.staticizer.movetohost;
+
+import com.android.tools.r8.NeverInline;
+
+public class MoveToHostFieldOnlyTestClass {
+
+  public static void main(String[] args) {
+    MoveToHostFieldOnlyTestClass test = new MoveToHostFieldOnlyTestClass();
+    test.testOk_fieldOnly();
+  }
+
+  @NeverInline
+  private void testOk_fieldOnly() {
+    // Any instance method call whose target holder is not the candidate will invalidate candidacy,
+    // for example, toString() without overriding, getClass(), etc.
+    // Note that having instance methods in the candidate class guarantees that method mappings will
+    // exist when field mappings do so.
+    // Any other uses other than invoke-virtual or invoke-direct (to either <init> or private) are
+    // not allowed, e.g., System.out.println(INSTANCE), null check, or static-put to somewhere else.
+    // Therefore, it's merely dead code, and thus it has not been harmful to forget to create a
+    // staticizer lense when there is no method mapping (for instance methods to staticized ones)
+    // while there are field mappings as shown in this example.
+    Object x = HostOkFieldOnly.INSTANCE;
+  }
+}
+
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
index 2258d67..f7b95f9 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
@@ -56,6 +56,10 @@
             .assertFailure();
 
     // Extract actual stack trace and retraced stack trace from failed run result.
+    // TODO(122940268): Remove test code when fixed.
+    System.out.println("<--- TEST RESULT START --->");
+    System.out.println(result);
+    System.out.println("<--- TEST RESULT END --->");
     StackTrace actualStackTrace = StackTrace.extractFromArt(result.getStdErr());
     StackTrace retracedStackTrace =
         actualStackTrace.retrace(result.proguardMap(), temp.newFolder().toPath());
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
index 0ecd3e4..dd8bc9c 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -165,9 +165,27 @@
     }
     // Take all lines from the bottom starting with "\tat ".
     int first = last;
+    // TODO(122940268): Remove test code when fixed.
+    System.out.println("TOTAL STDERR LINES: " + stderrLines.size());
+    for (int i = 0; i < last; i++) {
+      System.out.print("LINE " + i + ": " + stderrLines.get(i));
+      if (stderrLines.get(i).length() > 3) {
+        System.out.print(" (" + ((int) stderrLines.get(i).charAt(0)));
+        System.out.print(", " + ((int) stderrLines.get(i).charAt(1)));
+        System.out.print(", " + ((int) stderrLines.get(i).charAt(2) + ")"));
+      } else {
+        System.out.print(" (less than three chars)");
+      }
+     if (stderrLines.get(i).startsWith(TAB_AT_PREFIX)) {
+        System.out.println(" IS STACKTRACE LINE");
+      } else {
+        System.out.println(" IS NOT STACKTRACE LINE");
+      }
+    }
     while (first - 1 >= 0 && stderrLines.get(first - 1).startsWith(TAB_AT_PREFIX)) {
       first--;
     }
+    System.out.println("STACKTRACE LINES ARE " + first + " to " + (last - 1));
     for (int i = first; i < last; i++) {
       stackTraceLines.add(StackTraceLine.parse(stderrLines.get(i)));
     }
diff --git a/src/test/java/com/android/tools/r8/regress/b122887884/Regress122887884.java b/src/test/java/com/android/tools/r8/regress/b122887884/Regress122887884.java
new file mode 100644
index 0000000..23650c9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b122887884/Regress122887884.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.regress.b122887884;
+
+public class Regress122887884 {
+
+  public static int[] foo(String[] args) {
+    if (args.length == 0) {
+      return new int[] {0, 0};
+    }
+    String first = args[0];
+    try {
+      String[] split = first.split("");
+      if (split.length != 2) {
+        // This results in a new-array v2 v2 int[] at which point the exception handler must split.
+        return new int[] {0, 0};
+      }
+      return new int[] {1, 1};
+    } catch (Throwable t) {
+      return new int[] {0, 0};
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.println(foo(args)[0]);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b122887884/Regress122887884Runner.java b/src/test/java/com/android/tools/r8/regress/b122887884/Regress122887884Runner.java
new file mode 100644
index 0000000..54f32fd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b122887884/Regress122887884Runner.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.regress.b122887884;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+public class Regress122887884Runner extends TestBase {
+
+  private final Class<?> CLASS = Regress122887884.class;
+  private final String EXPECTED = StringUtils.lines("0");
+
+  @Test
+  public void test() throws Exception {
+    testForD8().addProgramClasses(CLASS).run(CLASS).assertSuccessWithOutput(EXPECTED);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index ff84486..db5f631 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1113,6 +1113,39 @@
   }
 
   @Test
+  public void parseInvalidKeepOption() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        "-keepx public class * {       ",
+        "  native <methods>;           ",
+        "}                             "
+    );
+    try {
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(proguardConfig);
+      fail();
+    } catch (AbortException e) {
+      checkDiagnostics(handler.errors, proguardConfig, 1, 1,
+          "Unknown option", "-keepx");
+    }
+  }
+
+  @Test
+  public void parseKeepOptionEOF() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+        System.lineSeparator(), ImmutableList.of("-keep"), false);
+    try {
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(proguardConfig);
+      fail();
+    } catch (AbortException e) {
+      checkDiagnostics(handler.errors, proguardConfig, 1, 6,
+          "Expected [!]interface|@interface|class|enum");
+    }
+  }
+
+  @Test
   public void parseInvalidKeepClassOption() throws Exception {
     Path proguardConfig = writeTextToTempFile(
         "-keepclassx public class * {  ",
@@ -1493,7 +1526,7 @@
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " class A { *; }")));
         fail("Expect to fail due to testing option being turned off.");
       } catch (AbortException e) {
-        assertEquals(2, handler.errors.size());
+        assertEquals(1, handler.errors.size());
         checkDiagnostics(handler.errors, 0, null, 1, 1, "Unknown option \"" + option + "\"");
       }
     }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index d878a86..6c23162 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -202,8 +202,7 @@
       return !code.asCfCode().getLocalVariables().isEmpty();
     }
     if (code.isJarCode()) {
-      return code.asJarCode().getNode().localVariables != null
-          && !code.asJarCode().getNode().localVariables.isEmpty();
+      return code.asJarCode().hasLocalVariableTable();
     }
     throw new Unreachable("Unexpected code type: " + code.getClass().getSimpleName());
   }
diff --git a/tools/compare_apk_sizes.py b/tools/compare_apk_sizes.py
index e477320..78327c2 100755
--- a/tools/compare_apk_sizes.py
+++ b/tools/compare_apk_sizes.py
@@ -23,6 +23,9 @@
   result = optparse.OptionParser(usage=USAGE)
   result.add_option('--temp',
                     help='Temporary directory to store extracted classes in')
+  result.add_option('--use_code_size',
+      help='Use the size of code segments instead of the full size of the dex.',
+      default=False, action='store_true')
   result.add_option('--report',
                     help='Print comparison to this location instead of stdout')
   return result.parse_args()
@@ -50,20 +53,35 @@
   if toolhelper.run('d8', args) is not 0:
     raise Exception('Failed running d8')
 
+def get_code_size(path):
+  segments = toolhelper.run('dexsegments',
+                            [path],
+                            build=False,
+                            return_stdout=True)
+  for line in segments.splitlines():
+    if 'Code' in line:
+      # The code size line looks like:
+      #  - Code: 264 / 4
+      splits = line.split(' ')
+      return int(splits[3])
+
 class FileInfo:
-  def __init__(self, path, root):
+  def __init__(self, path, root, use_code_size):
     self.path = path
     self.full_path = os.path.join(root, path)
-    self.size = os.path.getsize(self.full_path)
+    if use_code_size:
+      self.size = get_code_size(self.full_path)
+    else:
+      self.size = os.path.getsize(self.full_path)
 
-def generate_file_info(path):
+def generate_file_info(path, options):
   file_info_map = {}
   with utils.ChangedWorkingDirectory(path):
     for root, dirs, files in os.walk('.'):
       for f in files:
         assert f.endswith('dex')
         file_path = os.path.join(root, f)
-        entry = FileInfo(file_path, path)
+        entry = FileInfo(file_path, path, use_code_size=options.use_code_size)
         file_info_map[file_path] = entry
   return file_info_map
 
@@ -85,9 +103,9 @@
   output.write('\n\n')
 
 
-def compare(app1_classes_dir, app2_classes_dir, app1, app2, report):
-  app1_files = generate_file_info(app1_classes_dir)
-  app2_files = generate_file_info(app2_classes_dir)
+def compare(app1_classes_dir, app2_classes_dir, app1, app2, options):
+  app1_files = generate_file_info(app1_classes_dir, options)
+  app2_files = generate_file_info(app2_classes_dir, options)
   only_in_app1 = [k for k in app1_files if k not in app2_files]
   only_in_app2 = [k for k in app2_files if k not in app1_files]
   in_both = [k for k in app2_files if k in app1_files]
@@ -105,12 +123,12 @@
       bigger_in_app2[f] = app2_entry.size - app1_entry.size
     else:
       same_size.append(f)
-  output = open(report, 'w') if report else sys.stdout
+  output = open(options.report, 'w') if options.report else sys.stdout
   print_info(app1, app1_files, only_in_app1, bigger_in_app1, output)
   print_info(app2, app2_files, only_in_app2, bigger_in_app2, output)
   output.write('Same size\n')
   output.write('\n'.join(['  %s' % x for x in same_size]))
-  if report:
+  if options.report:
     output.close()
 
 def Main():
@@ -137,7 +155,7 @@
 
     extract_classes(app1_input, app1_classes_dir)
     extract_classes(app2_input, app2_classes_dir)
-    compare(app1_classes_dir, app2_classes_dir, app1, app2, options.report)
+    compare(app1_classes_dir, app2_classes_dir, app1, app2, options)
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/dex2oat.py b/tools/dex2oat.py
index 1e2cffd..e21dc7d 100755
--- a/tools/dex2oat.py
+++ b/tools/dex2oat.py
@@ -14,6 +14,8 @@
 
 VERSIONS = [
   'default',
+  '9.0.0',
+  '8.1.0',
   '7.0.0',
   '6.0.1',
   '5.1.1',
@@ -21,6 +23,8 @@
 
 DIRS = {
   'default': 'art',
+  '9.0.0': 'art-9.0.0',
+  '8.1.0': 'art-8.1.0',
   '7.0.0': 'art-7.0.0',
   '6.0.1': 'art-6.0.1',
   '5.1.1': 'art-5.1.1',
@@ -28,6 +32,8 @@
 
 PRODUCTS = {
   'default': 'angler',
+  '9.0.0': 'marlin',
+  '8.1.0': 'marlin',
   '7.0.0': 'angler',
   '6.0.1': 'angler',
   '5.1.1': 'mako',
@@ -35,11 +41,23 @@
 
 ARCHS = {
   'default': 'arm64',
+  '9.0.0': 'arm64',
+  '8.1.0': 'arm64',
   '7.0.0': 'arm64',
   '6.0.1': 'arm64',
   '5.1.1': 'arm',
 }
 
+VERBOSE_OPTIONS = [
+  'verifier',
+  'compiler',
+  'gc',
+  'jit',
+  'jni',
+  'class',
+  'all',
+]
+
 def ParseOptions():
   parser = optparse.OptionParser()
   parser.add_option('--version',
@@ -53,6 +71,10 @@
   parser.add_option('--output',
                     help='Where to place the output oat (defaults to no output / temp file).',
                     default=None)
+  parser.add_option('--verbose',
+                    help='Enable verbose dex2oat logging.',
+                    choices=VERBOSE_OPTIONS,
+                    default=None)
   return parser.parse_args()
 
 def Main():
@@ -66,12 +88,15 @@
   dexfile = args[0]
   oatfile = options.output
   versions = VERSIONS if options.all else [options.version]
+  verbose = [options.verbose] if options.verbose else []
+  if 'all' in verbose:
+    verbose = [x for x in VERBOSE_OPTIONS if x is not 'all']
   for version in versions:
-    run(dexfile, oatfile, version)
+    run(dexfile, oatfile, version, verbose)
     print
   return 0
 
-def run(dexfile, oatfile=None, version='default'):
+def run(dexfile, oatfile=None, version='default', verbose=[]):
   # dex2oat accepts non-existent dex files, check here instead
   if not os.path.exists(dexfile):
     raise Exception('DEX file not found: "{}"'.format(dexfile))
@@ -90,6 +115,8 @@
       '--oat-file=' + oatfile,
       '--instruction-set=' + arch,
     ]
+    for flag in verbose:
+      cmd += ['--runtime-arg', '-verbose:' + flag]
     env = {"LD_LIBRARY_PATH": os.path.join(base, 'lib')}
     utils.PrintCmd(cmd)
     subprocess.check_call(cmd, env = env)
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 86787be..d7cf222 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -10,7 +10,7 @@
 
 def run(tool, args, build=None, debug=True,
         profile=False, track_memory_file=None, extra_args=None,
-        stderr=None, stdout=None):
+        stderr=None, stdout=None, return_stdout=False):
   if build is None:
     build, args = extract_build_from_args(args)
   if build:
@@ -36,6 +36,8 @@
     cmd.extend(["--lib", lib])
   cmd.extend(args)
   utils.PrintCmd(cmd)
+  if return_stdout:
+    return subprocess.check_output(cmd)
   return subprocess.call(cmd, stdout=stdout, stderr=stderr)
 
 def run_in_tests(tool, args, build=None, debug=True, extra_args=None):