Merge "Use a pool for DexDebugEvents."
diff --git a/build.gradle b/build.gradle
index 4caf5fa..695e898 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,6 +29,12 @@
         }
         output.resourcesDir = 'build/classes/debugTestResources'
     }
+    debugTestResourcesJava8 {
+        java {
+            srcDirs = ['src/test/debugTestResourcesJava8']
+        }
+        output.resourcesDir = 'build/classes/debugTestResourcesJava8'
+    }
     examples {
         java {
             srcDirs = ['src/test/examples']
@@ -251,6 +257,20 @@
     }
 }
 
+task D8Logger(type: Jar) {
+    from sourceSets.main.output
+    baseName 'd8logger'
+    manifest {
+      attributes 'Main-Class': 'com.android.tools.r8.D8Logger'
+    }
+    // In order to build without dependencies, pass the exclude_deps property using:
+    // gradle -Pexclude_deps D8Logger
+    if (!project.hasProperty('exclude_deps')) {
+        // Also include dependencies
+        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+    }
+}
+
 task disasm(type: Jar) {
     from sourceSets.main.output
     baseName 'disasm'
@@ -295,13 +315,12 @@
 
 task createArtTests(type: Exec) {
     def outputDir = "build/generated/test/java/com/android/tools/r8/art"
-    def createArtTestsScript = "scripts/create-art-tests.sh"
+    def createArtTestsScript = "tools/create_art_tests.py"
     inputs.file "tests/art.tar.gz"
     inputs.file createArtTestsScript
     outputs.dir outputDir
     dependsOn downloadDeps
-    executable "bash"
-    args "${projectDir}/${createArtTestsScript}"
+    commandLine "python", createArtTestsScript
     workingDir = projectDir
 }
 
@@ -352,7 +371,6 @@
 }
 
 task buildDebugTestResourcesJars {
-    dependsOn downloadDeps
     def resourcesDir = file("src/test/debugTestResources")
     def hostJar = "debug_test_resources.jar"
     task "compile_debugTestResources"(type: JavaCompile) {
@@ -369,7 +387,25 @@
         from "build/test/debugTestResources/classes"
         include "**/*.class"
     }
+    def java8ResourcesDir = file("src/test/debugTestResourcesJava8")
+    def java8HostJar = "debug_test_resources_java8.jar"
+    task "compile_debugTestResourcesJava8"(type: JavaCompile) {
+        source = fileTree(dir: java8ResourcesDir, include: '**/*.java')
+        destinationDir = file("build/test/debugTestResourcesJava8/classes")
+        classpath = sourceSets.main.compileClasspath
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+        options.compilerArgs += ["-g", "-Xlint:-options"]
+    }
+    task "jar_debugTestResourcesJava8"(type: Jar, dependsOn: "compile_debugTestResourcesJava8") {
+        archiveName = java8HostJar
+        destinationDir = file("build/test/")
+        from "build/test/debugTestResourcesJava8/classes"
+        include "**/*.class"
+    }
+    dependsOn downloadDeps
     dependsOn jar_debugTestResources
+    dependsOn jar_debugTestResourcesJava8
 }
 
 task buildExampleJars {
diff --git a/scripts/aosp_helper.sh b/scripts/aosp_helper.sh
new file mode 100755
index 0000000..d375953
--- /dev/null
+++ b/scripts/aosp_helper.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# 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.
+
+# Helper script for tools/test_android_cts.py
+
+readonly AOSP_PRESET="$1"
+shift
+readonly TASK="$1"
+shift
+
+. build/envsetup.sh
+lunch "$AOSP_PRESET"
+
+if [[ "$TASK" == "make" ]]; then
+  make "$@"
+elif [[ "$TASK" == "emulator" ]]; then
+  emulator "$@"
+elif [[ "$TASK" == "run-cts" ]]; then
+  adb wait-for-device
+  adb shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'
+
+  echo "exit" | \
+    ANDROID_BUILD_TOP= \
+    "$@"
+else
+  echo "Invalid task: '$TASK'" >&2
+  exit 1
+fi
diff --git a/scripts/create-art-tests.sh b/scripts/create-art-tests.sh
deleted file mode 100755
index dc6d6d2..0000000
--- a/scripts/create-art-tests.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2016, 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.
-
-function generate_test() {
-  local name=$1
-  local testClassName=$2
-  local testGeneratingToolchain=$3
-  # The bash uppercase substitution ^^ is not supported on the bash version on Mac OS.
-  local testGeneratingToolchainEnum=$(echo $testGeneratingToolchain | tr /a-z/ /A-Z/)
-  local fileName=$4
-  local compilerUnderTest=$5
-  local compilerUnderTestEnum=$(echo ${compilerUnderTest} | tr /a-z/ /A-Z/)
-
-  cat <<EOF > $fileName
-// Copyright (c) 2016, 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.art.${testGeneratingToolchain}.${compilerUnderTest};
-
-import static com.android.tools.r8.R8RunArtTestsTest.DexTool.${testGeneratingToolchainEnum};
-
-import com.android.tools.r8.R8RunArtTestsTest;
-import org.junit.Test;
-
-/**
- * Auto-generated test for the art ${name} test using the ${testGeneratingToolchain} toolchain.
- *
- * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN scripts/create-art-tests.sh INSTEAD!
- */
-public class ${testClassName} extends R8RunArtTestsTest {
-
-    public ${testClassName}() {
-      super("${name}", ${testGeneratingToolchainEnum});
-    }
-
-    @Test
-    public void run${testClassName}() throws Throwable {
-      // For testing with other Art VMs than the default pass the VM version as a argument to
-      // runArtTest, e.g. runArtTest(ToolHelper.ART_4_4_4).
-      runArtTest(CompilerUnderTest.${compilerUnderTestEnum});
-    }
-}
-EOF
-}
-
-TESTDIR="tests/art"
-TOOLCHAINS=("dx" "jack" "none")
-DESTINATIONDIR="build/generated/test/java/com/android/tools/r8/art"
-
-if [ ! -e $TESTDIR ]; then
-  echo "Missing art tests in $TESTDIR."
-  exit
-fi
-
-for TOOLCHAIN in ${TOOLCHAINS[@]}; do
-  for d in $DESTINATIONDIR/$TOOLCHAIN/r8 $DESTINATIONDIR/$TOOLCHAIN/d8; do
-    rm -rf $d
-    mkdir -p $d
-  done
-  # class files are found in the dx directory.
-  if [ "$TOOLCHAIN" == "none" ]; then
-    SOURCEDIR=${TESTDIR}/dx
-  else
-    SOURCEDIR=${TESTDIR}/${TOOLCHAIN}
-  fi
-  for TEST in ${SOURCEDIR}/*; do
-    TESTNAME=$(basename $TEST)
-    TESTCLASSNAME="Art${TESTNAME//-/_}Test"
-    generate_test $TESTNAME $TESTCLASSNAME ${TOOLCHAIN} $DESTINATIONDIR/$TOOLCHAIN/r8/$TESTCLASSNAME.java r8
-    generate_test $TESTNAME $TESTCLASSNAME ${TOOLCHAIN} $DESTINATIONDIR/$TOOLCHAIN/d8/$TESTCLASSNAME.java d8
-  done
-done
diff --git a/scripts/create_d8_replay.sh b/scripts/create_d8_replay.sh
new file mode 100755
index 0000000..0b94705
--- /dev/null
+++ b/scripts/create_d8_replay.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# Take a file where each line is a tab-separated list of arguments for D8
+# and create a self-contained directory with all the input files and a script
+# which replays the same D8 invocations as the original list.
+#
+# Usage:
+#
+#     create_d8_replay <d8-args-script> <output-dir>
+#
+# The <d8-args-script> is a text file where each line contains tab-separated
+# arguments for a D8 call.
+# The script 'scripts/test_android_cts.sh' can log D8 invocations during an AOSP
+# build to such a file.
+set -e
+
+# This function will be called with the out_dir, input_counter and
+# arguments for the original D8 invocation. It copies the inputs
+# of the D8 call into the output directory and appends the replay
+# script with a new line which invokes D8 on the local inputs.
+function process_line {
+  local out_dir="$1"
+  shift
+  local input_counter=$1
+  shift
+
+  args=()
+  inputs=()
+  while (( "$#" )); do
+    if [[ "$1" =~ ^--output=(.*) ]]; then
+      :
+    elif [[ "$1" =~ ^(--.*) ]]; then
+      args+=("$1")
+    else
+      # copy $1 to local dir with unique_name
+      if [[ -f "$1" ]]; then
+        :
+      elif [[ -d "$1" ]]; then
+        echo "Adding directories ('$1') to the replay script is not implemented."
+      else
+        echo "The input to D8 does not exist: '$1'."
+      fi
+
+      input_file="in/${input_counter}_$(basename $1)"
+      cp "$1" "$out_dir/$input_file"
+      inputs+=("\$SCRIPT_DIR/$input_file")
+    fi
+    shift
+  done
+  echo "mkdir -p \"\$SCRIPT_DIR/out/$input_counter\"" \
+    >> "$out_dir/replay.sh"
+  echo "\$D8_COMMAND ${args[@]} \"--output=\$SCRIPT_DIR/out/$input_counter\" \"${inputs[@]}\"" \
+    >> "$out_dir/replay.sh"
+}
+
+
+### MAIN ###
+
+if (( "$#" != 2 )); then
+  echo "Usage: $0 <d8-args-script> <output-dir>" >&2
+  echo "See docs in source for details." >&2
+  exit 1
+fi
+
+input_script="$1"
+out_dir="$2"
+
+if [[ -d "$out_dir" ]]; then
+  rmdir "$out_dir" # make sure to write only to empty out dir
+fi
+mkdir -p "$out_dir/in"
+
+# write first lines of the replay script
+cat > "$out_dir/replay.sh" << EOF
+#!/bin/bash
+set -e
+readonly SCRIPT_DIR=\$(cd "\$(dirname \${BASH_SOURCE[0]})"; pwd)
+if [[ -z "\$D8_COMMAND" ]]; then
+  echo "Set D8_COMMAND to, e.g. 'java -jar d8|compatdx.jar'" >&2
+  exit 1
+fi
+rm -rf out
+EOF
+
+chmod +x "$out_dir/replay.sh"
+
+
+# process input file
+
+input_counter=1
+while IFS=$'\t' read -r -a args; do
+  process_line "$out_dir" $input_counter "${args[@]}"
+  input_counter=$((input_counter+1))
+done <"$input_script"
+
diff --git a/scripts/d8_for_aosp.sh b/scripts/d8_for_aosp.sh
deleted file mode 100755
index 422aa85..0000000
--- a/scripts/d8_for_aosp.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-#
-# 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.
-#
-# Replacement for 'dx' script invoked by the AOSP makefiles.
-# This script invokes 'd8' instead while providing the same API.
-#
-# Based on this file:
-#
-#     repo: https://android.googlesource.com/platform/prebuilts/sdk
-#     file: tools/dx
-#     SHA1: 6f6b5641b531f18c8e8d314b4b0560370ffbf1ab
-#
-# or this (identical)
-#
-#     repo: https://android.googlesource.com/platform/dalvik
-#     file: dx/etc/dx
-#     SHA1: 9fa280c2171b3a016d63e80cda7be031519b7ef7
-
-readonly ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
-
-defaultMx="-Xmx1024M"
-
-# The following will extract any initial parameters of the form
-# "-J<stuff>" from the command line and pass them to the Java
-# invocation (instead of to dx). This makes it possible for you to add
-# a command-line parameter such as "-JXmx256M" in your scripts, for
-# example. "java" (with no args) and "java -X" give a summary of
-# available options.
-
-javaOpts=""
-
-while expr "x$1" : 'x-J' >/dev/null; do
-    opt=`expr "x$1" : 'x-J\(.*\)'`
-    javaOpts="${javaOpts} -${opt}"
-    if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then
-        defaultMx="no"
-    fi
-    shift
-done
-
-if [ "${defaultMx}" != "no" ]; then
-    javaOpts="${javaOpts} ${defaultMx}"
-fi
-
-jarpath="$ROOT/build/libs/d8.jar"
-
-exec java $javaOpts -jar "$jarpath" --dex "$@"
-
diff --git a/scripts/test_android_cts.sh b/scripts/test_android_cts.sh
deleted file mode 100755
index 49175ec..0000000
--- a/scripts/test_android_cts.sh
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/bin/bash
-#
-# 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.
-#
-# Clone and build AOSP, using D8 instead of JACK or DX,
-# then run the Android-CTS on the emulator and compare results
-# to a baseline.
-#
-# This script uses the repo manifest file 'third_party/aosp_manifest.xml'
-# which is a snapshot of the aosp repo set.
-# The manifest file can be updated with the following commands:
-#
-#     cd build/aosp
-#     repo manifest -o ../../third_party/aosp_manifest.xml -r
-#
-# The baseline is the `test_result.xml` file which is created with an AOSP
-# build which uses the default (JACK) toolset.
-#
-# To reproduce the baseline results, follow the instructions in this script,
-# except don't set `ANDROID_COMPILE_WITH_JACK=false` for the `make` command.
-# Also, you don't need to apply either of the patches (PATCH#1 and #2, see
-# below)
-#
-
-set -e
-
-readonly R8_ROOT=$(cd "$(dirname ${BASH_SOURCE[0]})"/..; pwd)
-readonly AOSP_ROOT="$R8_ROOT/build/aosp"
-readonly CTS_BASELINE="$R8_ROOT/third_party/android_cts_baseline/test_result.xml"
-readonly D8_JAR="$R8_ROOT/build/libs/d8.jar"
-readonly J_OPTION="-j8"
-readonly OUT_IMG=out_img # output dir for android image build
-readonly OUT_CTS=out_cts # output dir for CTS build
-
-# Process an Android CTS test_result.xml file for easy comparison with a
-# baseline.
-#
-# The function transforms these lines:
-#
-#     <Test result="pass|fail" name="<name>" />
-#
-# to this:
-#
-#     <module-name>/<testcase-name>/<name> -> PASS|FAIL
-#
-flatten_xml() {
-  local input_file="$1"
-  local module
-  local testcase
-  local testname
-  while IFS='' read -r line || [[ -n "$line" ]]; do
-    if [[ $line =~ \<Module\ name=\"([^\"]*)\" ]]; then
-      module=${BASH_REMATCH[1]}
-    elif [[ $line =~ \<TestCase\ name=\"([^\"]*)\" ]]; then
-      testcase=${BASH_REMATCH[1]}
-    elif [[ $line =~ \<Test\ result=\"pass\"\ name=\"([^\"]*)\" ]]; then
-      echo "$module/$testcase/${BASH_REMATCH[1]} -> PASS"
-    elif [[ $line =~ \<Test\ result=\"fail\"\ name=\"([^\"]*)\" ]]; then
-      echo "$module/$testcase/${BASH_REMATCH[1]} -> FAIL"
-    fi
-  done < "$input_file"
-}
-
-#### MAIN ####
-
-cd "$R8_ROOT"
-tools/gradle.py d8
-
-mkdir -p "$AOSP_ROOT"
-cd "$AOSP_ROOT"
-
-# Two output dirs, one for the android image and one for cts tests. The output
-# is compiled with d8 and jack, respectively.
-mkdir -p "$OUT_IMG" "$OUT_CTS"
-
-# remove dex files older than the current d8 tool
-find "$OUT_IMG" ! -newer "$R8_ROOT/build/libs/d8.jar" -name '*.dex' -exec rm {} \;
-
-# checkout AOSP source
-mkdir -p .repo/manifests
-cp "$R8_ROOT/third_party/aosp_manifest.xml" .repo/manifests
-
-repo init -u "https://android.googlesource.com/platform/manifest" -m aosp_manifest.xml
-repo sync -dq $J_OPTION
-
-# activate $OUT_CTS
-rm -rf out
-ln -s "$OUT_CTS" out
-
-. build/envsetup.sh
-lunch aosp_x86-eng
-make $J_OPTION cts
-
-# activate $OUT_IMG
-rm -rf out
-ln -s "$OUT_IMG" out
-
-. build/envsetup.sh
-lunch aosp_x86-eng
-make $J_OPTION ANDROID_COMPILE_WITH_JACK=false DX_ALT_JAR="$D8_JAR"
-
-# create sdcard image for media tests
-
-mkdir -p "$R8_ROOT/build/tmp"
-sdcard_file="$R8_ROOT/build/tmp/sdcard.img"
-rm -f "$sdcard_file"
-mksdcard 4G "$sdcard_file"
-
-emulator -partition-size 4096 -wipe-data -sdcard "$sdcard_file" &
-emulator_pid=$!
-
-adb wait-for-device
-adb shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'
-
-echo "exit" | \
-  ANDROID_BUILD_TOP= \
-    "$OUT_CTS/host/linux-x86/cts/android-cts/tools/cts-tradefed" run cts
-
-kill $emulator_pid
-rm -f "$sdcard_file"
-
-# find the newest test_result.xml
-
-results_dir="$OUT_CTS/host/linux-x86/cts/android-cts/results"
-timestamp="$(ls --group-directories-first -t "$results_dir" | head -1)"
-results_xml="$results_dir/$timestamp/test_result.xml"
-
-echo "Summary from current test results: $results_xml"
-grep "<Summary " "$results_xml"
-
-echo "Summary from baseline: $CTS_BASELINE"
-grep "<Summary " "$CTS_BASELINE"
-
-echo "Comparing test results to baseline"
-
-diff <(flatten_xml "$results_xml") <(flatten_xml "$CTS_BASELINE")
-exit $? # make it explicit that the result of the diff must be the result of this script
-
diff --git a/src/main/java/com/android/tools/r8/D8Logger.java b/src/main/java/com/android/tools/r8/D8Logger.java
new file mode 100644
index 0000000..25f238b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8Logger.java
@@ -0,0 +1,54 @@
+// 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 com.android.tools.r8.compatdx.CompatDx;
+import com.google.common.collect.ImmutableList;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+import static java.util.Arrays.stream;
+
+public final class D8Logger {
+
+  private static final int STATUS_ERROR = 1;
+
+  private static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+      "Usage: java -jar d8logger.jar <compiler-options>",
+      " where <compiler-options> will be",
+      "",
+      " 1. forwarded to the 'd8' or 'compatdx' tool (depending on the presence of the '--dex'",
+      "    option), and also",
+      " 2. appended to the file in the environment variable 'D8LOGGER_OUTPUT'",
+      "",
+      " The options will be appended as a new line with TAB characters between the arguments."));
+
+  public static void main(String[] args) throws IOException {
+    if (args.length == 0) {
+      System.err.println(USAGE_MESSAGE);
+      System.exit(STATUS_ERROR);
+    }
+    String output = System.getenv("D8LOGGER_OUTPUT");
+    if (output == null) {
+      throw new IOException("D8Logger: D8LOGGER_OUTPUT environment variable must be set.");
+    }
+
+    if (output.length() > 0) {
+      String[] absArgs = Arrays.stream(args)
+          .map(s -> s.startsWith("-") ? s : Paths.get(s).toAbsolutePath().toString())
+          .toArray(String[]::new);
+      FileWriter fw = new FileWriter(output, true);
+      fw.write(String.join("\t", absArgs) + "\n");
+      fw.close();
+    }
+
+    if (Arrays.stream(args).anyMatch(s -> s.equals("--dex"))) {
+      CompatDx.main(args);
+    } else {
+      D8.main(args);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
index e2e972c..3c3c2b9 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
@@ -7,7 +7,7 @@
 
   private static void publicizeAllMethods(DexEncodedMethod[] methods) {
     for (DexEncodedMethod method : methods) {
-      method.accessFlags.promoteToPublic();
+      method.accessFlags.promoteNonPrivateToPublic();
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java b/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
index f409c4a..a8f9526 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
@@ -263,13 +263,18 @@
     set(Constants.ACC_DECLARED_SYNCHRONIZED);
   }
 
-  public void promoteToPublic() {
+  public void promoteNonPrivateToPublic() {
     if (!isPrivate()) {
       flags &= ~Constants.ACC_PROTECTED;
       flags |= Constants.ACC_PUBLIC;
     }
   }
 
+  public void promoteToPublic() {
+    flags &= ~Constants.ACC_PROTECTED & ~Constants.ACC_PRIVATE;
+    flags |= Constants.ACC_PUBLIC;
+  }
+
   private boolean isSet(int flag) {
     return (flags & flag) != 0;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
index f9c0b30..2f2523d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
@@ -17,7 +17,8 @@
 
   @Override
   protected boolean doEquivalent(Instruction a, Instruction b) {
-    return a.identicalAfterRegisterAllocation(b, allocator);
+    return a.identicalAfterRegisterAllocation(b, allocator)
+        && a.getBlock().getCatchHandlers().equals(b.getBlock().getCatchHandlers());
   }
 
   @Override
@@ -32,6 +33,7 @@
         hash += allocator.getRegisterForValue(inValue, instruction.getNumber());
       }
     }
+    hash = hash * 37 + instruction.getBlock().getCatchHandlers().hashCode();
     return hash;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index f2d039d..f621c67 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -429,9 +429,48 @@
   private boolean performAllocation(ArgumentReuseMode mode) {
     boolean result = performAllocationWithoutMoveInsertion(mode);
     insertMoves();
+    if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
+      // Now that we know the max register number we can compute whether it is safe to use
+      // argument registers in place. If it is, we redo move insertion to get rid of the moves
+      // caused by splitting of the argument registers.
+      if (unsplitArguments()) {
+        removeSpillAndPhiMoves();
+        insertMoves();
+      }
+    }
     return result;
   }
 
+  // When argument register reuse is disallowed, we split argument values to make sure that
+  // we can get the argument into low enough registers at uses that require low numbers. After
+  // register allocation we can check if it is safe to just use the argument register itself
+  // for all uses and thereby avoid moving argument values around.
+  private boolean unsplitArguments() {
+    boolean argumentRegisterUnsplit = false;
+    Value current = preArgumentSentinelValue;
+    while (current != null) {
+      LiveIntervals intervals = current.getLiveIntervals();
+      assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
+      boolean canUseArgumentRegister = true;
+      for (LiveIntervals child : intervals.getSplitChildren()) {
+        if (child.getRegisterLimit() < registersUsed()) {
+          canUseArgumentRegister = false;
+          break;
+        }
+      }
+      if (canUseArgumentRegister) {
+        argumentRegisterUnsplit = true;
+        for (LiveIntervals child : intervals.getSplitChildren()) {
+          child.clearRegisterAssignment();
+          child.setRegister(intervals.getRegister());
+          child.setSpilled(false);
+        }
+      }
+      current = current.getNextConsecutive();
+    }
+    return argumentRegisterUnsplit;
+  }
+
   private void removeSpillAndPhiMoves() {
     for (BasicBlock block : code.blocks) {
       InstructionListIterator it = block.listIterator();
diff --git a/src/test/debugTestResourcesJava8/DebugDefaultMethod.java b/src/test/debugTestResourcesJava8/DebugDefaultMethod.java
new file mode 100644
index 0000000..db216b3
--- /dev/null
+++ b/src/test/debugTestResourcesJava8/DebugDefaultMethod.java
@@ -0,0 +1,35 @@
+// 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.
+
+public class DebugDefaultMethod {
+
+  interface I {
+    default void doSomething(String msg) {
+      String name = getClass().getName();
+      System.out.println(name + ": " + msg);
+    }
+  }
+
+  static class DefaultImpl implements I {
+  }
+
+  static class OverrideImpl implements I {
+
+    @Override
+    public void doSomething(String msg) {
+      String newMsg = "OVERRIDE" + msg;
+      System.out.println(newMsg);
+    }
+  }
+
+  private static void testDefaultMethod(I i) {
+    i.doSomething("Test");
+  }
+
+  public static void main(String[] args) {
+    testDefaultMethod(new DefaultImpl());
+    testDefaultMethod(new OverrideImpl());
+  }
+
+}
diff --git a/src/test/debugTestResourcesJava8/DebugLambda.java b/src/test/debugTestResourcesJava8/DebugLambda.java
new file mode 100644
index 0000000..c3b8695
--- /dev/null
+++ b/src/test/debugTestResourcesJava8/DebugLambda.java
@@ -0,0 +1,23 @@
+// 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.
+
+public class DebugLambda {
+
+  interface I {
+    int getInt();
+  }
+
+  private static void printInt(I i) {
+    System.out.println(i.getInt());
+  }
+
+  public static void testLambda(int i, int j) {
+    printInt(() -> i + j);
+  }
+
+  public static void main(String[] args) {
+    DebugLambda.testLambda(5, 10);
+  }
+
+}
diff --git a/src/test/examples/nestedtrycatches/NestedTryCatches.java b/src/test/examples/nestedtrycatches/NestedTryCatches.java
new file mode 100644
index 0000000..00bec2a
--- /dev/null
+++ b/src/test/examples/nestedtrycatches/NestedTryCatches.java
@@ -0,0 +1,38 @@
+// 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 nestedtrycatches;
+
+public class NestedTryCatches {
+  private static void throwException() {
+    throw new RuntimeException("IGNORED");
+  }
+
+  private static void test() throws Throwable {
+    RuntimeException _primaryExc = null;
+    try {
+      throw new RuntimeException("PRIMARY");
+    } catch (RuntimeException _t) {
+      _primaryExc = _t;
+      throw _t;
+    } finally {
+      // Keep the two calls to throwException() the same line
+      if(_primaryExc!=null) {
+        try {
+          throwException();
+        } catch(Throwable _suppressed) {
+        }
+      } else {
+        throwException();
+      }
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    try {
+      test();
+    } catch (Throwable e) {
+      System.out.println("EXCEPTION: " + e.getMessage());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 628036c..9e7588d 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -86,6 +86,7 @@
         {"throwing.Throwing", "Throwing"},
         {"trivial.Trivial", null},
         {"trycatch.TryCatch", "Success!"},
+        {"nestedtrycatches.NestedTryCatches", "EXCEPTION: PRIMARY"},
         {"trycatchmany.TryCatchMany", "Success!"},
         {"invokeempty.InvokeEmpty", "AB"},
         {"regress.Regress", null},
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 0b042a9..5f9a95c 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -4,11 +4,12 @@
 package com.android.tools.r8.debug;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
 import java.nio.file.Path;
@@ -78,11 +79,14 @@
 
   private static final Path DEBUGGEE_JAR = Paths
       .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
+  private static final Path DEBUGGEE_JAVA8_JAR = Paths
+      .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_java8.jar");
 
   @ClassRule
   public static TemporaryFolder temp = new TemporaryFolder();
   private static Path jdwpDexD8 = null;
   private static Path debuggeeDexD8 = null;
+  private static Path debuggeeJava8DexD8 = null;
 
   @Rule
   public TestName testName = new TestName();
@@ -95,7 +99,7 @@
       Path jdwpJar = ToolHelper.getJdwpTestsJarPath(minSdk);
       Path dexOutputDir = temp.newFolder("d8-jdwp-jar").toPath();
       jdwpDexD8 = dexOutputDir.resolve("classes.dex");
-      D8.run(
+      ToolHelper.runD8(
           D8Command.builder()
               .addProgramFiles(jdwpJar)
               .setOutputPath(dexOutputDir)
@@ -106,7 +110,7 @@
     {
       Path dexOutputDir = temp.newFolder("d8-debuggee-jar").toPath();
       debuggeeDexD8 = dexOutputDir.resolve("classes.dex");
-      D8.run(
+      ToolHelper.runD8(
           D8Command.builder()
               .addProgramFiles(DEBUGGEE_JAR)
               .setOutputPath(dexOutputDir)
@@ -114,6 +118,25 @@
               .setMode(CompilationMode.DEBUG)
               .build());
     }
+    {
+      Path dexOutputDir = temp.newFolder("d8-debuggee-java8-jar").toPath();
+      debuggeeJava8DexD8 = dexOutputDir.resolve("classes.dex");
+      ToolHelper.runD8(
+          D8Command.builder()
+              .addProgramFiles(DEBUGGEE_JAVA8_JAR)
+              .setOutputPath(dexOutputDir)
+              .setMinApiLevel(minSdk)
+              .setMode(CompilationMode.DEBUG)
+              .build(),
+          options -> {
+            // Enable desugaring for preN runtimes
+            options.interfaceMethodDesugaring = OffOrAuto.Auto;
+          });
+    }
+  }
+
+  protected final boolean supportsDefaultMethod() {
+    return ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()) >= Constants.ANDROID_N_API;
   }
 
   protected final void runDebugTest(String debuggeeClass, JUnit3Wrapper.Command... commands)
@@ -123,6 +146,22 @@
 
   protected final void runDebugTest(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
+    runDebugTest(debuggeeDexD8, debuggeeClass, commands);
+  }
+
+  protected final void runDebugTestJava8(String debuggeeClass, JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runDebugTestJava8(debuggeeClass, Arrays.asList(commands));
+  }
+
+  protected final void runDebugTestJava8(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
+    runDebugTest(debuggeeJava8DexD8, debuggeeClass, commands);
+  }
+
+  private void runDebugTest(Path debuggeeExec, String debuggeeClass,
+      List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
     // Skip test due to unsupported runtime.
     Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
         ToolHelper.artSupported());
@@ -131,7 +170,7 @@
             .getDexVm(), UNSUPPORTED_ART_VERSIONS.contains(ToolHelper.getDexVm()));
 
     // Run with ART.
-    String[] paths = new String[]{jdwpDexD8.toString(), debuggeeDexD8.toString()};
+    String[] paths = new String[]{jdwpDexD8.toString(), debuggeeExec.toString()};
     new JUnit3Wrapper(debuggeeClass, paths, commands).runBare();
   }
 
@@ -172,6 +211,10 @@
     return new JUnit3Wrapper.Command.StepCommand(stepDepth, stepFilter);
   }
 
+  protected final JUnit3Wrapper.Command checkLocal(String localName) {
+    return inspect(t -> t.checkLocal(localName));
+  }
+
   protected final JUnit3Wrapper.Command checkLocal(String localName, Value expectedValue) {
     return inspect(t -> t.checkLocal(localName, expectedValue));
   }
@@ -308,6 +351,18 @@
         // Capture the context of the event suspension.
         updateEventContext((EventThread) parsedEvent);
 
+        if (DEBUG_TESTS && debuggeeState.location != null) {
+          // Dump location
+          String classSig = getMirror().getClassSignature(debuggeeState.location.classID);
+          String methodName = getMirror()
+              .getMethodName(debuggeeState.location.classID, debuggeeState.location.methodID);
+          String methodSig = getMirror()
+              .getMethodSignature(debuggeeState.location.classID, debuggeeState.location.methodID);
+          System.out.println(String
+              .format("Suspended in %s#%s%s@%x", classSig, methodName, methodSig,
+                  Long.valueOf(debuggeeState.location.index)));
+        }
+
         // Handle event.
         EventHandler eh = events.get(requestID);
         assert eh != null;
@@ -346,6 +401,10 @@
         return this.location;
       }
 
+      public void checkLocal(String localName) {
+        getVariableAt(getLocation(), localName);
+      }
+
       public void checkLocal(String localName, Value expectedValue) {
         Variable localVar = getVariableAt(getLocation(), localName);
 
@@ -767,7 +826,27 @@
       public boolean skipLocation(VmMirror mirror, Location location) {
         // TODO(shertz) we also need to skip class loaders to act like IntelliJ.
         // Skip synthetic methods.
-        return isSyntheticMethod(mirror, location);
+        if (isLambdaMethod(mirror, location)) {
+          // Lambda methods are synthetic but we do want to stop there.
+          if (DEBUG_TESTS) {
+            System.out.println("NOT skipping lambda implementation method");
+          }
+          return false;
+        }
+        if (isInLambdaClass(mirror, location)) {
+          // Lambda classes must be skipped since they are only wrappers around lambda code.
+          if (DEBUG_TESTS) {
+            System.out.println("Skipping lambda class wrapper method");
+          }
+          return true;
+        }
+        if (isSyntheticMethod(mirror, location)) {
+          if (DEBUG_TESTS) {
+            System.out.println("Skipping synthetic method");
+          }
+          return true;
+        }
+        return false;
       }
 
       private static boolean isSyntheticMethod(VmMirror mirror, Location location) {
@@ -787,6 +866,16 @@
         }
         return false;
       }
+
+      private static boolean isInLambdaClass(VmMirror mirror, Location location) {
+        String classSig = mirror.getClassSignature(location.classID);
+        return classSig.contains("$$Lambda$");
+      }
+
+      private boolean isLambdaMethod(VmMirror mirror, Location location) {
+        String methodName = mirror.getMethodName(location.classID, location.methodID);
+        return methodName.startsWith("lambda$");
+      }
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java b/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java
new file mode 100644
index 0000000..ea116a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java
@@ -0,0 +1,65 @@
+// 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.debug;
+
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+public class DefaultMethodTest extends DebugTestBase {
+
+  @Test
+  public void testDefaultMethod() throws Throwable {
+    String debuggeeClass = "DebugDefaultMethod";
+    String parameterName = "msg";
+    String localVariableName = "name";
+
+    List<Command> commands = new ArrayList<>();
+    commands.add(breakpoint(debuggeeClass, "testDefaultMethod"));
+    commands.add(run());
+    commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
+    commands.add(checkLine(27));
+    if (!supportsDefaultMethod()) {
+      // We desugared default method. This means we're going to step through an extra (forward)
+      // method first.
+      commands.add(stepInto());
+    }
+    commands.add(stepInto());
+    commands.add(checkLocal(parameterName));
+    commands.add(stepOver());
+    commands.add(checkLocal(parameterName));
+    commands.add(checkLocal(localVariableName));
+    // TODO(shertz) check current method name ?
+    commands.add(run());
+    commands.add(run()  /* resume after 2nd breakpoint */);
+
+    runDebugTestJava8(debuggeeClass, commands);
+  }
+
+  @Test
+  public void testOverrideDefaultMethod() throws Throwable {
+    String debuggeeClass = "DebugDefaultMethod";
+    String parameterName = "msg";
+    String localVariableName = "newMsg";
+
+    List<Command> commands = new ArrayList<>();
+    commands.add(breakpoint(debuggeeClass, "testDefaultMethod"));
+    commands.add(run());
+    commands.add(run() /* resume after 1st breakpoint */);
+    commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
+    commands.add(checkLine(27));
+    commands.add(stepInto());
+    commands.add(checkMethod("DebugDefaultMethod$OverrideImpl", "doSomething"));
+    commands.add(checkLocal(parameterName));
+    commands.add(stepOver());
+    commands.add(checkLocal(parameterName));
+    commands.add(checkLocal(localVariableName));
+    commands.add(run());
+
+    runDebugTestJava8(debuggeeClass, commands);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaTest.java b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
new file mode 100644
index 0000000..467ae09
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
@@ -0,0 +1,25 @@
+// 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.debug;
+
+import org.junit.Test;
+
+public class LambdaTest extends DebugTestBase {
+
+  @Test
+  public void testLambdaDebugging() throws Throwable {
+    String debuggeeClass = "DebugLambda";
+    String initialMethodName = "printInt";
+    // TODO(shertz) test local variables
+    runDebugTestJava8(debuggeeClass,
+        breakpoint(debuggeeClass, initialMethodName),
+        run(),
+        checkMethod(debuggeeClass, initialMethodName),
+        checkLine(12),
+        stepInto(INTELLIJ_FILTER),
+        checkLine(16),
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
index 229a6fb..8b5b814 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -11,23 +11,28 @@
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalResource;
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class YouTubeTreeShakeJarVerificationTest extends YouTubeCompilationBase {
 
   @Test
-  @Ignore("b/35656577")
   public void buildAndTreeShakeFromDeployJar()
       throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
-    int maxSize = 16000000;
+    int maxSize = 20000000;
     AndroidApp app = runAndCheckVerification(
-        CompilerUnderTest.R8, CompilationMode.RELEASE,
-        BASE + APK, null, BASE + PG_CONF, BASE + DEPLOY_JAR);
+        CompilerUnderTest.R8,
+        CompilationMode.RELEASE,
+        BASE + APK,
+        null,
+        BASE + PG_CONF,
+        // Don't pass any inputs. The input will be read from the -injars in the Proguard
+        // configuration file.
+        ImmutableList.of());
     int bytes = 0;
     try (Closer closer = Closer.create()) {
       for (InternalResource dex : app.getDexProgramResources()) {
diff --git a/tools/apk-masseur.py b/tools/apk-masseur.py
index b0c990f..6e3528a 100755
--- a/tools/apk-masseur.py
+++ b/tools/apk-masseur.py
@@ -24,6 +24,10 @@
   parser.add_option('--keystore',
                     help='keystore file (default ~/.android/app.keystore)',
                     default=None)
+  parser.add_option('--install',
+                    help='install the generated apk with adb options -t -r -d',
+                    default=False,
+                    action='store_true')
   (options, args) = parser.parse_args()
   if len(args) != 1:
     parser.error('Expected <apk> argument, got: ' + ' '.join(args))
@@ -102,6 +106,10 @@
     aligned_apk = align(signed_apk, temp)
     print 'Writing result to', options.out
     shutil.copyfile(aligned_apk, options.out)
+    if options.install:
+      cmd = ['adb', 'install', '-t', '-r', '-d', options.out]
+      utils.PrintCmd(cmd)
+      subprocess.check_call(cmd)
   return 0
 
 if __name__ == '__main__':
diff --git a/tools/create_art_tests.py b/tools/create_art_tests.py
new file mode 100755
index 0000000..3a83cbc
--- /dev/null
+++ b/tools/create_art_tests.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# 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.
+
+from os import makedirs, listdir
+from os.path import join, exists, isdir
+from string import Template, upper
+from sys import exit
+from shutil import rmtree
+
+OUTPUT_DIR = "build/generated/test/java/com/android/tools/r8/art"
+TEST_DIR = "tests/art"
+TOOLCHAINS = ["dx", "jack", "none"]
+TOOLS = ["r8", "d8"]
+TEMPLATE = Template(
+"""// Copyright (c) 2016, 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.art.$testGeneratingToolchain.$compilerUnderTest;
+
+import static com.android.tools.r8.R8RunArtTestsTest.DexTool.$testGeneratingToolchainEnum;
+
+import com.android.tools.r8.R8RunArtTestsTest;
+import org.junit.Test;
+
+/**
+ * Auto-generated test for the art $name test using the $testGeneratingToolchain toolchain.
+ *
+ * DO NOT EDIT THIS FILE. EDIT THE HERE DOCUMENT TEMPLATE IN tools/create_art_tests.py INSTEAD!
+ */
+public class $testClassName extends R8RunArtTestsTest {
+
+    public $testClassName() {
+      super("$name", $testGeneratingToolchainEnum);
+    }
+
+    @Test
+    public void run$testClassName() throws Throwable {
+      // For testing with other Art VMs than the default pass the VM version as a argument to
+      // runArtTest, e.g. runArtTest(ToolHelper.ART_4_4_4).
+      runArtTest(CompilerUnderTest.$compilerUnderTestEnum);
+    }
+}
+""")
+
+def create_toolchain_dirs(toolchain):
+  toolchain_dir = join(OUTPUT_DIR, toolchain)
+  if exists(toolchain_dir):
+    rmtree(toolchain_dir)
+  makedirs(join(toolchain_dir, "d8"))
+  makedirs(join(toolchain_dir, "r8"))
+
+def write_file(toolchain, tool, class_name, contents):
+  file_name = join(OUTPUT_DIR, toolchain, tool, class_name + ".java")
+  with open(file_name, "w") as file:
+    file.write(contents)
+
+def create_tests(toolchain):
+  source_dir = join(TEST_DIR, "dx" if toolchain == "none" else toolchain)
+  dirs = [d for d in listdir(source_dir)
+          if isdir(join(source_dir, d))]
+  for dir in dirs:
+    class_name = "Art" + dir.replace("-", "_") + "Test"
+    for tool in TOOLS:
+      contents = TEMPLATE.substitute(
+          name=dir,
+          compilerUnderTestEnum=upper(tool),
+          compilerUnderTest=tool,
+          testGeneratingToolchain=toolchain,
+          testGeneratingToolchainEnum=upper(toolchain),
+          testClassName=class_name)
+      write_file(toolchain, tool, class_name, contents)
+
+
+def main():
+  for toolchain in TOOLCHAINS:
+    create_toolchain_dirs(toolchain)
+    create_tests(toolchain)
+
+if __name__ == "__main__":
+  exit(main())
diff --git a/tools/linux/dx.tar.gz.sha1 b/tools/linux/dx.tar.gz.sha1
index f13e394..92e67ce 100644
--- a/tools/linux/dx.tar.gz.sha1
+++ b/tools/linux/dx.tar.gz.sha1
@@ -1 +1 @@
-6976e6a1768527b2388b1fdda5868dfa6b80d844
\ No newline at end of file
+da8789846188590e69dbac06f4d387762e71a616
\ No newline at end of file
diff --git a/tools/test_android_cts.py b/tools/test_android_cts.py
new file mode 100755
index 0000000..b4af1f1
--- /dev/null
+++ b/tools/test_android_cts.py
@@ -0,0 +1,294 @@
+#!/usr/bin/env python
+# 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.
+
+# Clone and build AOSP, using D8 instead of JACK or DX,
+# then run the Android-CTS on the emulator and compare results
+# to a baseline.
+#
+# This script uses the repo manifest file 'third_party/aosp_manifest.xml'
+# which is a snapshot of the aosp repo set.
+# The manifest file can be updated with the following commands:
+#
+#   cd build/aosp
+#   repo manifest -o ../../third_party/aosp_manifest.xml -r
+#
+# The baseline is the `test_result.xml` file which is created with an AOSP
+# build which uses the default (JACK) toolset.
+#
+# Use this script, with '--tool=jack' to reproduce the baseline results
+#
+
+from __future__ import print_function
+from glob import glob
+from itertools import chain
+from os.path import join
+from shutil import copy2
+from subprocess import check_call, Popen
+import argparse
+import os
+import re
+import sys
+
+import gradle
+import utils
+
+CTS_BASELINE = join(utils.REPO_ROOT,
+  'third_party/android_cts_baseline/test_result.xml')
+AOSP_MANIFEST_XML = join(utils.REPO_ROOT, 'third_party',
+  'aosp_manifest.xml')
+AOSP_HELPER_SH = join(utils.REPO_ROOT, 'scripts', 'aosp_helper.sh')
+
+D8_JAR = join(utils.REPO_ROOT, 'build/libs/d8.jar')
+COMPATDX_JAR = join(utils.REPO_ROOT, 'build/libs/compatdx.jar')
+D8LOGGER_JAR = join(utils.REPO_ROOT, 'build/libs/d8logger.jar')
+
+AOSP_ROOT = join(utils.REPO_ROOT, 'build/aosp')
+
+AOSP_MANIFEST_URL = 'https://android.googlesource.com/platform/manifest'
+AOSP_PRESET = 'aosp_x86-eng'
+
+AOSP_OUT = join(AOSP_ROOT, 'out')
+OUT_IMG = join(AOSP_ROOT, 'out_img') # output dir for android img build
+OUT_CTS = join(AOSP_ROOT, 'out_cts') # output dir for CTS build
+RESULTS_DIR_BASE = join(OUT_CTS, 'host/linux-x86/cts/android-cts/results')
+CTS_TRADEFED = join(OUT_CTS,
+  'host/linux-x86/cts/android-cts/tools/cts-tradefed')
+
+J_OPTION = '-j8'
+
+EXIT_FAILURE = 1
+
+def parse_arguments():
+  parser = argparse.ArgumentParser(
+      description = 'Download the AOSP source tree, build an Android image'
+      ' and the CTS targets and run CTS with the emulator on the image.')
+  parser.add_argument('--tool',
+      choices = ['jack', 'dx', 'd8'],
+      default = 'd8',
+      help='compiler tool to use')
+  parser.add_argument('--d8log',
+      metavar = 'FILE',
+      help = 'Enable logging d8 (compatdx) calls to the specified file. Works'
+          ' only with --tool=d8')
+  return parser.parse_args()
+
+# return False on error
+def remove_aosp_out():
+  if os.path.exists(AOSP_OUT):
+    if os.path.islink(AOSP_OUT):
+      os.remove(AOSP_OUT)
+    else:
+      print("The AOSP out directory ('" + AOSP_OUT + "') is expected"
+        " to be a symlink", file = sys.stderr)
+      return False
+  return True
+
+# Read the xml test result file into an in-memory tree:
+# Extract only the Module/TestCase/Test names and outcome (True|False for
+# PASS|FAIL):
+#
+#     tree[module_name][testcase_name][test_name] = True|False
+#
+def read_test_result_into_tree(filename):
+  re_module = re.compile('<Module name="([^"]*)"')
+  re_testcase = re.compile('<TestCase name="([^"]*)"')
+  re_test = re.compile('<Test result="(pass|fail)" name="([^"]*)"')
+  tree = {}
+  module = None
+  testcase = None
+  with open(filename) as f:
+    for line in f:
+      m = re_module.search(line)
+      if m:
+        module_name = m.groups()[0]
+        tree[module_name] = {}
+        module = tree[module_name]
+        continue
+
+      m = re_testcase.search(line)
+      if m:
+        testcase_name = m.groups()[0]
+        module[testcase_name] = {}
+        testcase = module[testcase_name]
+        continue
+
+      m = re_test.search(line)
+      if m:
+        outcome = m.groups()[0]
+        test_name = m.groups()[1]
+        assert outcome in ["fail", "pass"]
+        testcase[test_name] = outcome == "pass"
+  return tree
+
+# Report the items with the title
+def report_key_diff(title, items, prefix = ''):
+  if len(items) > 0:
+    print(title, ":")
+    for x in items:
+      print("- {}{}".format(prefix, x))
+    print()
+
+
+def diff_sets(base_minus_result_title, result_minus_base_title,
+    base_set, result_set, prefix = ''):
+  base_minus_result = base_set - result_set
+  result_minus_base = result_set - base_set
+  report_key_diff(base_minus_result_title, base_minus_result, prefix)
+  report_key_diff(result_minus_base_title, result_minus_base, prefix)
+  return len(base_minus_result) > 0 or len(result_minus_base) > 0
+
+def diff_tree_report(baseline_tree, result_tree):
+  baseline_modules = set(baseline_tree.keys())
+  result_modules = set(result_tree.keys())
+  differ = diff_sets('Modules missing from current result',
+      'New modules appeared in current result',
+      baseline_modules, result_modules)
+  for module in (result_modules & baseline_modules):
+    baseline_module = baseline_tree[module]
+    result_module = result_tree[module]
+    baseline_testcases = set(baseline_module.keys())
+    result_testcases = set(result_module.keys())
+    differ = diff_sets('Test cases missing from current result',
+        'New test cases appeared in current result',
+        baseline_testcases, result_testcases, module + '/') \
+        or differ
+    for testcase in (result_testcases & baseline_testcases):
+      baseline_testcase = baseline_module[testcase]
+      result_testcase = result_module[testcase]
+      baseline_tests = set(baseline_testcase.keys())
+      result_tests = set(result_testcase.keys())
+      differ = diff_sets('Tests missing from current result',
+          'New tests appeared in current result',
+          baseline_tests, result_tests, module + '/' + testcase + '/') \
+          or differ
+      need_newline_at_end = False
+      for test in (result_tests & baseline_tests):
+        baseline_outcome = baseline_testcase[test]
+        result_outcome = result_testcase[test]
+        if baseline_outcome != result_outcome:
+          differ = True
+          print('Test: {}/{}/{}, change: {}'.format(
+            module, testcase, test,
+            'PASS -> FAIL' if baseline_outcome else 'FAIL -> PASS'))
+          need_newline_at_end = True
+      if need_newline_at_end:
+        print()
+  return differ
+
+def setup_and_clean():
+  # Two output dirs, one for the android image and one for cts tests.
+  # The output is compiled with d8 and jack, respectively.
+  utils.makedirs_if_needed(AOSP_ROOT)
+  utils.makedirs_if_needed(OUT_IMG)
+  utils.makedirs_if_needed(OUT_CTS)
+
+  # remove dex files older than the current d8 tool
+  d8jar_mtime = os.path.getmtime(D8_JAR)
+  dex_files = (chain.from_iterable(glob(join(x[0], '*.dex'))
+    for x in os.walk(OUT_IMG)))
+  for f in dex_files:
+    if os.path.getmtime(f) <= d8jar_mtime:
+      os.remove(f)
+
+def checkout_aosp():
+  # checkout AOSP source
+  manifests_dir = join(AOSP_ROOT, '.repo', 'manifests')
+  utils.makedirs_if_needed(manifests_dir)
+
+  copy2(AOSP_MANIFEST_XML, manifests_dir)
+  check_call(['repo', 'init', '-u', AOSP_MANIFEST_URL, '-m',
+    'aosp_manifest.xml', '--depth=1'], cwd = AOSP_ROOT)
+
+  check_call(['repo', 'sync', '-dq', J_OPTION], cwd = AOSP_ROOT)
+
+def Main():
+  args = parse_arguments()
+
+  if args.d8log and args.tool != 'd8':
+    print("The '--d8log' option works only with '--tool=d8'.",
+        file = sys.stderr)
+    return EXIT_FAILURE
+
+  assert args.tool in ['jack', 'dx', 'd8']
+
+  jack_option = 'ANDROID_COMPILE_WITH_JACK=' \
+      + 'true' if args.tool == 'jack' else 'false'
+
+  alt_jar_option = ''
+  if args.tool == 'd8':
+    if args.d8log:
+      alt_jar_option = 'DX_ALT_JAR=' + D8LOGGER_JAR
+      os.environ['D8LOGGER_OUTPUT'] = args.d8log
+    else:
+      alt_jar_option = 'DX_ALT_JAR=' + COMPATDX_JAR
+
+  gradle.RunGradle(['d8','d8logger', 'compatdx'])
+
+  setup_and_clean()
+
+  checkout_aosp()
+
+  # activate OUT_CTS and build Android CTS
+  # AOSP has no clean way to set the output directory.
+  # In order to do incremental builds we apply the following symlink-based
+  # workaround.
+  # Note: this does not work on windows, but the AOSP
+  # doesn't build, either
+
+  if not remove_aosp_out():
+    return EXIT_FAILURE
+  os.symlink(OUT_CTS, AOSP_OUT)
+  check_call([AOSP_HELPER_SH, AOSP_PRESET, 'make', J_OPTION, 'cts'],
+      cwd = AOSP_ROOT)
+
+  # activate OUT_IMG and build the Android image
+  if not remove_aosp_out():
+    return EXIT_FAILURE
+  os.symlink(OUT_IMG, AOSP_OUT)
+  check_call([AOSP_HELPER_SH, AOSP_PRESET, 'make', J_OPTION, jack_option,
+      alt_jar_option], cwd = AOSP_ROOT)
+
+  emulator_proc = Popen([AOSP_HELPER_SH, AOSP_PRESET,
+      'emulator', '-partition-size', '4096', '-wipe-data'], cwd = AOSP_ROOT)
+
+  if emulator_proc.poll() is not None:
+    print("Can't start Android Emulator.", file = sys.stderr)
+
+  check_call([AOSP_HELPER_SH, AOSP_PRESET, 'run-cts',
+      CTS_TRADEFED, 'run', 'cts'], cwd = AOSP_ROOT)
+
+  emulator_proc.terminate()
+
+  # find the newest test_result.xml
+  result_dirs = \
+      [f for f in glob(join(RESULTS_DIR_BASE, '*')) if os.path.isdir(f)]
+  if len(result_dirs) == 0:
+    print("Can't find result directories in ", RESULTS_DIR_BASE)
+    return EXIT_FAILURE
+  result_dirs.sort(key = os.path.getmtime)
+  results_xml = join(result_dirs[-1], 'test_result.xml')
+
+  # print summaries
+  re_summary = re.compile('<Summary ')
+  for (title, result_file) in [
+    ('Summary from current test results: ', results_xml),
+    ('Summary from baseline: ', CTS_BASELINE)
+    ]:
+    print(title, result_file)
+    with open(result_file) as f:
+      for line in f:
+        if re_summary.search(line):
+          print(line)
+          break
+
+  print('Comparing test results to baseline:\n')
+
+  result_tree = read_test_result_into_tree(results_xml)
+  baseline_tree = read_test_result_into_tree(CTS_BASELINE)
+
+  return EXIT_FAILURE if diff_tree_report(baseline_tree, result_tree) else 0
+
+if __name__ == '__main__':
+  sys.exit(Main())
diff --git a/tools/utils.py b/tools/utils.py
index d5d27ca..67cac46 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -38,6 +38,13 @@
       sha1.update(chunk)
   return sha1.hexdigest()
 
+def makedirs_if_needed(path):
+  try:
+    os.makedirs(path)
+  except OSError:
+    if not os.path.isdir(path):
+        raise
+
 class TempDir(object):
  def __init__(self, prefix=''):
    self._temp_dir = None