Merge "ART: Set Runtime fault message on abort"
diff --git a/build/Android.bp b/build/Android.bp
index 09d3a18..46fb0c5 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -7,6 +7,7 @@
         "blueprint-proptools",
         "soong",
         "soong-android",
+        "soong-apex",
         "soong-cc",
     ],
     srcs: [
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index f1a21e8..88178a0 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -49,10 +49,13 @@
 ]
 
 // Modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
-art_tools_binaries = [
+art_tools_common_binaries = [
     "dexdiag",
     "dexdump",
     "dexlist",
+]
+
+art_tools_device_binaries = [
     "oatdump",
 ]
 
@@ -66,6 +69,8 @@
     // ...
 ]
 
+art_tools_binaries = art_tools_common_binaries + art_tools_device_binaries
+
 apex_key {
     name: "com.android.runtime.key",
     public_key: "com.android.runtime.avbpubkey",
@@ -135,3 +140,39 @@
     prebuilts: ["com.android.runtime.ld.config.txt"],
     key: "com.android.runtime.key",
 }
+
+// TODO: Do this better. art_apex will disable host builds when
+// HOST_PREFER_32_BIT is set. We cannot simply use com.android.runtime.debug
+// because binaries have different multilib classes and 'multilib: {}' isn't
+// supported by target: { ... }.
+// See b/120617876 for more information.
+art_apex {
+    name: "com.android.runtime.host",
+    compile_multilib: "both",
+    payload_type: "zip",
+    host_supported: true,
+    device_supported: false,
+    manifest: "manifest.json",
+    native_shared_libs: art_runtime_base_native_shared_libs
+        + art_runtime_fake_native_shared_libs
+        + art_runtime_debug_native_shared_libs,
+    multilib: {
+        both: {
+            // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
+            // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp).
+            binaries: art_runtime_base_binaries_both,
+        },
+        first: {
+            // TODO: oatdump cannot link with host linux_bionic due to not using clang ld
+            binaries: art_tools_common_binaries
+                + art_runtime_base_binaries_prefer32
+                + art_runtime_debug_binaries_prefer32,
+        }
+    },
+    key: "com.android.runtime.key",
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 86cd8cb..c19c7bd 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -69,51 +69,6 @@
 work_dir=$(mktemp -d)
 mount_point="$work_dir/image"
 
-# Garbage collection.
-function finish {
-  # Don't fail early during cleanup.
-  set +e
-  guestunmount "$mount_point"
-  rm -rf "$work_dir"
-}
-
-trap finish EXIT
-
-# TODO: Also exercise the Release Runtime APEX (`com.android.runtime.release`).
-apex_module="com.android.runtime.debug"
-
-# Build the Android Runtime APEX package (optional).
-$build_apex_p && say "Building package" && make "$apex_module"
-
-system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
-apex_package="$system_apexdir/$apex_module.apex"
-
-say "Extracting and mounting image"
-
-# Extract the image from the Android Runtime APEX.
-image_filename="image.img"
-unzip -q "$apex_package" "$image_filename" -d "$work_dir"
-mkdir "$mount_point"
-image_file="$work_dir/$image_filename"
-
-# Check filesystems in the image.
-image_filesystems="$work_dir/image_filesystems"
-virt-filesystems -a "$image_file" >"$image_filesystems"
-# We expect a single partition (/dev/sda) in the image.
-partition="/dev/sda"
-echo "$partition" | cmp "$image_filesystems" -
-
-# Mount the image from the Android Runtime APEX.
-guestmount -a "$image_file" -m "$partition" "$mount_point"
-
-# List the contents of the mounted image (optional).
-$list_image_files_p && say "Listing image files" && ls -ld "$mount_point" && tree -ap "$mount_point"
-
-say "Running tests"
-
-# Check that the mounted image contains a manifest.
-[[ -f "$mount_point/manifest.json" ]]
-
 function check_binary {
   [[ -x "$mount_point/bin/$1" ]] || die "Cannot find binary '$1' in mounted image"
 }
@@ -136,65 +91,178 @@
     || die "Cannot find library '$1' in mounted image"
 }
 
-# Check that the mounted image contains ART base binaries.
-check_multilib_binary dalvikvm
-# TODO: Does not work yet.
-: check_binary_symlink dalvikvm
-check_binary dex2oat
-check_binary dexoptanalyzer
-check_binary profman
+function build_apex {
+  if $build_apex_p; then
+    say "Building package $1" && make "$1" || die "Cannot build $1"
+  fi
+}
 
-# Check that the mounted image contains ART tools binaries.
-check_binary dexdiag
-check_binary dexdump
-check_binary dexlist
+function check_contents {
+
+  # Check that the mounted image contains a manifest.
+  [[ -f "$mount_point/apex_manifest.json" ]] || die "no manifest"
+
+  # Check that the mounted image contains ART base binaries.
+  check_multilib_binary dalvikvm
+  # TODO: Does not work yet.
+  : check_binary_symlink dalvikvm
+  check_binary dex2oat
+  check_binary dexoptanalyzer
+  check_binary profman
+
+  # Check that the mounted image contains ART tools binaries.
+  check_binary dexdiag
+  check_binary dexdump
+  check_binary dexlist
+  # oatdump is only in device apex's due to build rules
+  # check_binary oatdump
+
+  # Check that the mounted image contains ART debug binaries.
+  check_binary dex2oatd
+  check_binary dexoptanalyzerd
+  check_binary profmand
+
+  # Check that the mounted image contains ART libraries.
+  check_library libart-compiler.so
+  check_library libart.so
+  check_library libopenjdkjvm.so
+  check_library libopenjdkjvmti.so
+  check_library libadbconnection.so
+  # TODO: Should we check for these libraries too, even if they are not explicitly
+  # listed as dependencies in the Android Runtime APEX module rule?
+  check_library libartbase.so
+  check_library libart-dexlayout.so
+  check_library libdexfile.so
+  check_library libprofile.so
+
+  # Check that the mounted image contains ART debug libraries.
+  check_library libartd-compiler.so
+  check_library libartd.so
+  check_library libopenjdkd.so
+  check_library libopenjdkjvmd.so
+  check_library libopenjdkjvmtid.so
+  check_library libadbconnectiond.so
+  # TODO: Should we check for these libraries too, even if they are not explicitly
+  # listed as dependencies in the Android Runtime APEX module rule?
+  check_library libdexfiled.so
+  check_library libartbased.so
+  check_library libartd-dexlayout.so
+  check_library libprofiled.so
+
+  # TODO: Should we check for other libraries, such as:
+  #
+  #   libbacktrace.so
+  #   libbase.so
+  #   liblog.so
+  #   libsigchain.so
+  #   libtombstoned_client.so
+  #   libunwindstack.so
+  #   libvixl.so
+  #   libvixld.so
+  #   ...
+  #
+  # ?
+}
+
+
+# *****************************************
+# * Testing for com.android.runtime.debug *
+# *****************************************
+
+# Garbage collection.
+function finish_device_debug {
+  # Don't fail early during cleanup.
+  set +e
+  guestunmount "$mount_point"
+  rm -rf "$work_dir"
+}
+
+trap finish_device_debug EXIT
+
+# TODO: Also exercise the Release Runtime APEX (`com.android.runtime.release`).
+apex_module="com.android.runtime.debug"
+
+# Build the Android Runtime APEX package (optional).
+build_apex $apex_module
+
+system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
+apex_package="$system_apexdir/$apex_module.apex"
+
+say "Extracting and mounting image"
+
+# Extract the payload from the Android Runtime APEX.
+image_filename="apex_payload.img"
+unzip -q "$apex_package" "$image_filename" -d "$work_dir"
+mkdir "$mount_point"
+image_file="$work_dir/$image_filename"
+
+# Check filesystems in the image.
+image_filesystems="$work_dir/image_filesystems"
+virt-filesystems -a "$image_file" >"$image_filesystems"
+# We expect a single partition (/dev/sda) in the image.
+partition="/dev/sda"
+echo "$partition" | cmp "$image_filesystems" -
+
+# Mount the image from the Android Runtime APEX.
+guestmount -a "$image_file" -m "$partition" "$mount_point"
+
+# List the contents of the mounted image (optional).
+$list_image_files_p && say "Listing image files" && ls -ld "$mount_point" && tree -ap "$mount_point"
+
+say "Running tests"
+
+check_contents
+
+# Check for files pulled in from device-only oatdump.
 check_binary oatdump
-
-# Check that the mounted image contains ART debug binaries.
-check_binary dex2oatd
-check_binary dexoptanalyzerd
-check_binary profmand
-
-# Check that the mounted image contains ART libraries.
-check_library libart-compiler.so
-check_library libart.so
-check_library libopenjdkjvm.so
-check_library libopenjdkjvmti.so
-check_library libadbconnection.so
-# TODO: Should we check for these libraries too, even if they are not explicitly
-# listed as dependencies in the Android Runtime APEX module rule?
-check_library libartbase.so
-check_library libart-dexlayout.so
 check_library libart-disassembler.so
-check_library libdexfile.so
-check_library libprofile.so
 
-# Check that the mounted image contains ART debug libraries.
-check_library libartd-compiler.so
-check_library libartd.so
-check_library libdexfiled.so
-check_library libopenjdkd.so
-check_library libopenjdkjvmd.so
-check_library libopenjdkjvmtid.so
-check_library libadbconnectiond.so
-# TODO: Should we check for these libraries too, even if they are not explicitly
-# listed as dependencies in the Android Runtime APEX module rule?
-check_library libartbased.so
-check_library libartd-dexlayout.so
-check_library libprofiled.so
+# Cleanup
+trap - EXIT
+guestunmount "$mount_point"
+rm -rf "$work_dir"
 
-# TODO: Should we check for other libraries, such as:
-#
-#   libbacktrace.so
-#   libbase.so
-#   liblog.so
-#   libsigchain.so
-#   libtombstoned_client.so
-#   libunwindstack.so
-#   libvixl.so
-#   libvixld.so
-#   ...
-#
-# ?
+say "$apex_module Tests passed"
+
+# ****************************************
+# * Testing for com.android.runtime.host *
+# ****************************************
+
+# Garbage collection.
+function finish_host {
+  # Don't fail early during cleanup.
+  set +e
+  rm -rf "$work_dir"
+}
+
+work_dir=$(mktemp -d)
+mount_point="$work_dir/zip"
+
+trap finish_host EXIT
+
+apex_module="com.android.runtime.host"
+
+# Build the Android Runtime APEX package (optional).
+build_apex $apex_module
+
+system_apexdir="$ANDROID_HOST_OUT/apex"
+apex_package="$system_apexdir/$apex_module.zipapex"
+
+say "Extracting payload"
+
+# Extract the payload from the Android Runtime APEX.
+image_filename="apex_payload.zip"
+unzip -q "$apex_package" "$image_filename" -d "$work_dir"
+mkdir "$mount_point"
+image_file="$work_dir/$image_filename"
+
+# Unzipping the payload
+unzip -q "$image_file" -d "$mount_point"
+
+say "Running tests"
+
+check_contents
+
+say "$apex_module Tests passed"
 
 say "Tests passed"
diff --git a/build/art.go b/build/art.go
index 1c8be0f..01848c8 100644
--- a/build/art.go
+++ b/build/art.go
@@ -16,8 +16,10 @@
 
 import (
 	"android/soong/android"
+	"android/soong/apex"
 	"android/soong/cc"
 	"fmt"
+	"log"
 	"sync"
 
 	"github.com/google/blueprint/proptools"
@@ -289,6 +291,36 @@
 	android.RegisterModuleType("libart_static_cc_defaults", libartStaticDefaultsFactory)
 	android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
 	android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
+
+	// TODO: This makes the module disable itself for host if HOST_PREFER_32_BIT is
+	// set. We need this because the multilib types of binaries listed in the apex
+	// rule must match the declared type. This is normally not difficult but HOST_PREFER_32_BIT
+	// changes this to 'prefer32' on all host binaries. Since HOST_PREFER_32_BIT is
+	// only used for testing we can just disable the module.
+	// See b/120617876 for more information.
+	android.RegisterModuleType("art_apex", artApexBundleFactory)
+}
+
+func artApexBundleFactory() android.Module {
+	module := apex.ApexBundleFactory()
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		if envTrue(ctx, "HOST_PREFER_32_BIT") {
+			type props struct {
+				Target struct {
+					Host struct {
+						Enabled *bool
+					}
+				}
+			}
+
+			p := &props{}
+			p.Target.Host.Enabled = proptools.BoolPtr(false)
+			ctx.AppendProperties(p)
+			log.Print("Disabling host build of " + ctx.ModuleName() + " for HOST_PREFER_32_BIT=true")
+		}
+	})
+
+	return module
 }
 
 func artGlobalDefaultsFactory() android.Module {
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 66421e2..be6da71 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -22,6 +22,7 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/callee_save_type.h"
+#include "base/casts.h"
 #include "base/enums.h"
 #include "base/utils.h"
 #include "class_linker.h"
@@ -152,6 +153,10 @@
 
     CreateCompilerDriver();
   }
+  // Note: We cannot use MemMap because some tests tear down the Runtime and destroy
+  // the gMaps, so when destroying the MemMap, the test would crash.
+  inaccessible_page_ = mmap(nullptr, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  CHECK(inaccessible_page_ != MAP_FAILED) << strerror(errno);
 }
 
 void CommonCompilerTest::ApplyInstructionSet() {
@@ -190,9 +195,7 @@
   compiler_options_->image_classes_.swap(*GetImageClasses());
   compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo();
   compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
-                                            verification_results_.get(),
                                             compiler_kind_,
-                                            &compiler_options_->image_classes_,
                                             number_of_threads_,
                                             /* swap_fd */ -1));
 }
@@ -222,6 +225,10 @@
   verification_results_.reset();
   compiler_options_.reset();
   image_reservation_.Reset();
+  if (inaccessible_page_ != nullptr) {
+    munmap(inaccessible_page_, kPageSize);
+    inaccessible_page_ = nullptr;
+  }
 
   CommonRuntimeTest::TearDown();
 }
@@ -267,8 +274,16 @@
 
     compiler_driver_->InitializeThreadPools();
 
-    compiler_driver_->PreCompile(class_loader, dex_files, &timings);
+    compiler_driver_->PreCompile(class_loader,
+                                 dex_files,
+                                 &timings,
+                                 &compiler_options_->image_classes_,
+                                 verification_results_.get());
 
+    // Verification results in the `callback_` should not be used during compilation.
+    down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+        reinterpret_cast<VerificationResults*>(inaccessible_page_));
+    compiler_options_->verification_results_ = verification_results_.get();
     compiler_driver_->CompileOne(self,
                                  class_loader,
                                  *dex_file,
@@ -279,6 +294,9 @@
                                  code_item,
                                  dex_cache,
                                  h_class_loader);
+    compiler_options_->verification_results_ = nullptr;
+    down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+        verification_results_.get());
 
     compiler_driver_->FreeThreadPools();
 
@@ -334,6 +352,32 @@
   CHECK(image_reservation_.IsValid()) << error_msg;
 }
 
+void CommonCompilerTest::CompileAll(jobject class_loader,
+                                    const std::vector<const DexFile*>& dex_files,
+                                    TimingLogger* timings) {
+  TimingLogger::ScopedTiming t(__FUNCTION__, timings);
+  SetDexFilesForOatFile(dex_files);
+
+  compiler_driver_->InitializeThreadPools();
+
+  compiler_driver_->PreCompile(class_loader,
+                               dex_files,
+                               timings,
+                               &compiler_options_->image_classes_,
+                               verification_results_.get());
+
+  // Verification results in the `callback_` should not be used during compilation.
+  down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+      reinterpret_cast<VerificationResults*>(inaccessible_page_));
+  compiler_options_->verification_results_ = verification_results_.get();
+  compiler_driver_->CompileAll(class_loader, dex_files, timings);
+  compiler_options_->verification_results_ = nullptr;
+  down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+      verification_results_.get());
+
+  compiler_driver_->FreeThreadPools();
+}
+
 void CommonCompilerTest::UnreserveImageSpace() {
   image_reservation_.Reset();
 }
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index e6d1564..a71908e 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -20,6 +20,8 @@
 #include <list>
 #include <vector>
 
+#include <jni.h>
+
 #include "arch/instruction_set.h"
 #include "arch/instruction_set_features.h"
 #include "base/hash_set.h"
@@ -37,6 +39,7 @@
 class CumulativeLogger;
 class DexFile;
 class ProfileCompilationInfo;
+class TimingLogger;
 class VerificationResults;
 
 template<class T> class Handle;
@@ -88,6 +91,10 @@
                             const char* method_name, const char* signature)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void CompileAll(jobject class_loader,
+                  const std::vector<const DexFile*>& dex_files,
+                  TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
+
   void ApplyInstructionSet();
   void OverrideInstructionSetFeatures(InstructionSet instruction_set, const std::string& variant);
 
@@ -116,6 +123,7 @@
 
  private:
   MemMap image_reservation_;
+  void* inaccessible_page_;
 
   // Chunks must not move their storage after being created - use the node-based std::list.
   std::list<std::vector<uint8_t>> header_code_and_maps_chunks_;
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index baf8643..1ecb1d8e 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -138,7 +138,7 @@
   CHECK(builder->Good());
   std::vector<uint8_t> compressed_buffer;
   compressed_buffer.reserve(buffer.size() / 4);
-  XzCompress(ArrayRef<uint8_t>(buffer), &compressed_buffer);
+  XzCompress(ArrayRef<const uint8_t>(buffer), &compressed_buffer);
   return compressed_buffer;
 }
 
diff --git a/compiler/debug/xz_utils.cc b/compiler/debug/xz_utils.cc
index a9e30a6..a8f60ac 100644
--- a/compiler/debug/xz_utils.cc
+++ b/compiler/debug/xz_utils.cc
@@ -17,13 +17,16 @@
 #include "xz_utils.h"
 
 #include <vector>
+#include <mutex>
 
 #include "base/array_ref.h"
-#include "dwarf/writer.h"
+#include "base/bit_utils.h"
 #include "base/leb128.h"
+#include "dwarf/writer.h"
 
 // liblzma.
 #include "7zCrc.h"
+#include "Xz.h"
 #include "XzCrc64.h"
 #include "XzEnc.h"
 
@@ -32,10 +35,17 @@
 
 constexpr size_t kChunkSize = kPageSize;
 
-static void XzCompressChunk(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst) {
+static void XzInitCrc() {
+  static std::once_flag crc_initialized;
+  std::call_once(crc_initialized, []() {
+    CrcGenerateTable();
+    Crc64GenerateTable();
+  });
+}
+
+static void XzCompressChunk(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
   // Configure the compression library.
-  CrcGenerateTable();
-  Crc64GenerateTable();
+  XzInitCrc();
   CLzma2EncProps lzma2Props;
   Lzma2EncProps_Init(&lzma2Props);
   lzma2Props.lzmaProps.level = 1;  // Fast compression.
@@ -62,7 +72,7 @@
       return SZ_OK;
     }
     size_t src_pos_;
-    ArrayRef<uint8_t> src_;
+    ArrayRef<const uint8_t> src_;
     std::vector<uint8_t>* dst_;
   };
   XzCallbacks callbacks;
@@ -85,7 +95,7 @@
 // In short, the file format is: [header] [compressed_block]* [index] [footer]
 // Where [index] is: [num_records] ([compressed_size] [uncompressed_size])* [crc32]
 //
-void XzCompress(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst) {
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
   uint8_t header[] = { 0xFD, '7', 'z', 'X', 'Z', 0, 0, 1, 0x69, 0x22, 0xDE, 0x36 };
   uint8_t footer[] = { 0, 1, 'Y', 'Z' };
   dst->insert(dst->end(), header, header + sizeof(header));
@@ -138,6 +148,47 @@
     writer.UpdateUint32(0, CrcCalc(tmp.data() + 4, 6));
     dst->insert(dst->end(), tmp.begin(), tmp.end());
   }
+
+  // Decompress the data back and check that we get the original.
+  if (kIsDebugBuild) {
+    std::vector<uint8_t> decompressed;
+    XzDecompress(ArrayRef<const uint8_t>(*dst), &decompressed);
+    DCHECK_EQ(decompressed.size(), src.size());
+    DCHECK_EQ(memcmp(decompressed.data(), src.data(), src.size()), 0);
+  }
+}
+
+void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
+  XzInitCrc();
+  std::unique_ptr<CXzUnpacker> state(new CXzUnpacker());
+  ISzAlloc alloc;
+  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+  XzUnpacker_Construct(state.get(), &alloc);
+
+  size_t src_offset = 0;
+  size_t dst_offset = 0;
+  ECoderStatus status;
+  do {
+    dst->resize(RoundUp(dst_offset + kPageSize / 4, kPageSize));
+    size_t src_remaining = src.size() - src_offset;
+    size_t dst_remaining = dst->size() - dst_offset;
+    int return_val = XzUnpacker_Code(state.get(),
+                                     dst->data() + dst_offset,
+                                     &dst_remaining,
+                                     src.data() + src_offset,
+                                     &src_remaining,
+                                     true,
+                                     CODER_FINISH_ANY,
+                                     &status);
+    CHECK_EQ(return_val, SZ_OK);
+    src_offset += src_remaining;
+    dst_offset += dst_remaining;
+  } while (status == CODER_STATUS_NOT_FINISHED);
+  CHECK_EQ(src_offset, src.size());
+  CHECK(XzUnpacker_IsStreamWasFinished(state.get()));
+  XzUnpacker_Free(state.get());
+  dst->resize(dst_offset);
 }
 
 }  // namespace debug
diff --git a/compiler/debug/xz_utils.h b/compiler/debug/xz_utils.h
index c4076c6..731b03c 100644
--- a/compiler/debug/xz_utils.h
+++ b/compiler/debug/xz_utils.h
@@ -24,7 +24,8 @@
 namespace art {
 namespace debug {
 
-void XzCompress(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst);
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst);
+void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst);
 
 }  // namespace debug
 }  // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 04ad10c..c124ef5 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -528,7 +528,7 @@
       class_def_idx,
       method_idx,
       access_flags,
-      driver_->GetVerifiedMethod(&dex_file, method_idx),
+      driver_->GetCompilerOptions().GetVerifiedMethod(&dex_file, method_idx),
       hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)));
 
   std::vector<uint8_t> quicken_data;
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index f61e6c4..b055416 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -39,16 +39,15 @@
 class DexToDexDecompilerTest : public CommonCompilerTest {
  public:
   void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
-    TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
-    TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+    TimingLogger timings("DexToDexDecompilerTest::CompileAll", false, false);
     compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
     compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
     // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
     // the results for all the dex files, not just the results for the current dex file.
     down_cast<QuickCompilerCallbacks*>(Runtime::Current()->GetCompilerCallbacks())->SetVerifierDeps(
         new verifier::VerifierDeps(GetDexFiles(class_loader)));
-    SetDexFilesForOatFile(GetDexFiles(class_loader));
-    compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
+    std::vector<const DexFile*> dex_files = GetDexFiles(class_loader);
+    CommonCompilerTest::CompileAll(class_loader, dex_files, &timings);
   }
 
   void RunTest(const char* dex_name) {
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index dd947d9..5a34efb 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -97,7 +97,7 @@
   }
 }
 
-const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
+const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) const {
   const VerifiedMethod* ret = nullptr;
   if (atomic_verified_methods_.Get(ref, &ret)) {
     return ret;
@@ -129,13 +129,13 @@
   DCHECK(IsClassRejected(ref));
 }
 
-bool VerificationResults::IsClassRejected(ClassReference ref) {
+bool VerificationResults::IsClassRejected(ClassReference ref) const {
   ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
   return (rejected_classes_.find(ref) != rejected_classes_.end());
 }
 
 bool VerificationResults::IsCandidateForCompilation(MethodReference&,
-                                                    const uint32_t access_flags) {
+                                                    const uint32_t access_flags) const {
   if (!compiler_options_->IsAotCompilationEnabled()) {
     return false;
   }
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 56f0030..04c4fa6 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -51,13 +51,13 @@
   void CreateVerifiedMethodFor(MethodReference ref)
       REQUIRES(!verified_methods_lock_);
 
-  const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
+  const VerifiedMethod* GetVerifiedMethod(MethodReference ref) const
       REQUIRES(!verified_methods_lock_);
 
   void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
-  bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+  bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_);
 
-  bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags);
+  bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags) const;
 
   // Add a dex file to enable using the atomic map.
   void AddDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
@@ -74,10 +74,12 @@
   // GetVerifiedMethod.
   AtomicMap atomic_verified_methods_;
 
-  ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+  mutable ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 
   // Rejected classes.
-  ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+  mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
 
   friend class verifier::VerifierDepsTest;
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 792f508..63dcb46 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -99,11 +99,6 @@
   return std::make_pair(fast_get, fast_put);
 }
 
-inline VerificationResults* CompilerDriver::GetVerificationResults() const {
-  DCHECK(Runtime::Current()->IsAotCompiler());
-  return verification_results_;
-}
-
 }  // namespace art
 
 #endif  // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 67cabef..18f7105 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -245,16 +245,12 @@
 
 CompilerDriver::CompilerDriver(
     const CompilerOptions* compiler_options,
-    VerificationResults* verification_results,
     Compiler::Kind compiler_kind,
-    HashSet<std::string>* image_classes,
     size_t thread_count,
     int swap_fd)
     : compiler_options_(compiler_options),
-      verification_results_(verification_results),
       compiler_(Compiler::Create(this, compiler_kind)),
       compiler_kind_(compiler_kind),
-      image_classes_(std::move(image_classes)),
       number_of_soft_verifier_failures_(0),
       had_hard_verifier_failure_(false),
       parallel_thread_count_(thread_count),
@@ -266,10 +262,6 @@
 
   compiler_->Init();
 
-  if (GetCompilerOptions().IsBootImage()) {
-    CHECK(image_classes_ != nullptr) << "Expected image classes for boot image";
-  }
-
   compiled_method_storage_.SetDedupeEnabled(compiler_options_->DeduplicateCode());
 }
 
@@ -325,9 +317,8 @@
                                 TimingLogger* timings) {
   DCHECK(!Runtime::Current()->IsStarted());
 
-  InitializeThreadPools();
+  CheckThreadPools();
 
-  PreCompile(class_loader, dex_files, timings);
   if (GetCompilerOptions().IsBootImage()) {
     // We don't need to setup the intrinsics for non boot image compilation, as
     // those compilations will pick up a boot image that have the ArtMethod already
@@ -343,8 +334,6 @@
   if (GetCompilerOptions().GetDumpStats()) {
     stats_->Dump();
   }
-
-  FreeThreadPools();
 }
 
 static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel(
@@ -496,7 +485,7 @@
     optimizer::DexToDexCompiler* const compiler = &driver->GetDexToDexCompiler();
 
     if (compiler->ShouldCompileMethod(method_ref)) {
-      VerificationResults* results = driver->GetVerificationResults();
+      const VerificationResults* results = driver->GetCompilerOptions().GetVerificationResults();
       DCHECK(results != nullptr);
       const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
       // Do not optimize if a VerifiedMethod is missing. SafeCast elision,
@@ -574,7 +563,7 @@
     } else if ((access_flags & kAccAbstract) != 0) {
       // Abstract methods don't have code.
     } else {
-      VerificationResults* results = driver->GetVerificationResults();
+      const VerificationResults* results = driver->GetCompilerOptions().GetVerificationResults();
       DCHECK(results != nullptr);
       const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
       bool compile =
@@ -881,7 +870,9 @@
 
 void CompilerDriver::PreCompile(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
-                                TimingLogger* timings) {
+                                TimingLogger* timings,
+                                /*inout*/ HashSet<std::string>* image_classes,
+                                /*out*/ VerificationResults* verification_results) {
   CheckThreadPools();
 
   VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
@@ -898,7 +889,7 @@
   // 6) Update the set of image classes.
   // 7) For deterministic boot image, initialize bitstrings for type checking.
 
-  LoadImageClasses(timings);
+  LoadImageClasses(timings, image_classes);
   VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
   if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -932,7 +923,7 @@
     ResolveConstStrings(dex_files, /*only_startup_strings=*/ true, timings);
   }
 
-  Verify(class_loader, dex_files, timings);
+  Verify(class_loader, dex_files, timings, verification_results);
   VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
 
   if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -957,7 +948,7 @@
     VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
   }
 
-  UpdateImageClasses(timings);
+  UpdateImageClasses(timings, image_classes);
   VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
 
   if (kBitstringSubtypeCheckEnabled &&
@@ -1071,7 +1062,8 @@
 };
 
 // Make a list of descriptors for classes to include in the image
-void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
+void CompilerDriver::LoadImageClasses(TimingLogger* timings,
+                                      /*inout*/ HashSet<std::string>* image_classes) {
   CHECK(timings != nullptr);
   if (!GetCompilerOptions().IsBootImage()) {
     return;
@@ -1082,15 +1074,15 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  CHECK(image_classes_ != nullptr);
-  for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) {
+  CHECK(image_classes != nullptr);
+  for (auto it = image_classes->begin(), end = image_classes->end(); it != end;) {
     const std::string& descriptor(*it);
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> klass(
         hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str())));
     if (klass == nullptr) {
       VLOG(compiler) << "Failed to find class " << descriptor;
-      it = image_classes_->erase(it);
+      it = image_classes->erase(it);
       self->ClearException();
     } else {
       ++it;
@@ -1140,10 +1132,10 @@
   // We walk the roots looking for classes so that we'll pick up the
   // above classes plus any classes them depend on such super
   // classes, interfaces, and the required ClassLinker roots.
-  RecordImageClassesVisitor visitor(image_classes_);
+  RecordImageClassesVisitor visitor(image_classes);
   class_linker->VisitClasses(&visitor);
 
-  CHECK(!image_classes_->empty());
+  CHECK(!image_classes->empty());
 }
 
 static void MaybeAddToImageClasses(Thread* self,
@@ -1312,7 +1304,8 @@
   DISALLOW_COPY_AND_ASSIGN(ClinitImageUpdate);
 };
 
-void CompilerDriver::UpdateImageClasses(TimingLogger* timings) {
+void CompilerDriver::UpdateImageClasses(TimingLogger* timings,
+                                        /*inout*/ HashSet<std::string>* image_classes) {
   if (GetCompilerOptions().IsBootImage()) {
     TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
 
@@ -1324,7 +1317,7 @@
     VariableSizedHandleScope hs(Thread::Current());
     std::string error_msg;
     std::unique_ptr<ClinitImageUpdate> update(ClinitImageUpdate::Create(hs,
-                                                                        image_classes_,
+                                                                        image_classes,
                                                                         Thread::Current(),
                                                                         runtime->GetClassLinker()));
 
@@ -1393,12 +1386,6 @@
   }
 }
 
-const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file,
-                                                        uint32_t method_idx) const {
-  MethodReference ref(dex_file, method_idx);
-  return verification_results_->GetVerifiedMethod(ref);
-}
-
 bool CompilerDriver::IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc) {
   if (!compiler_options_->IsVerificationEnabled()) {
     // If we didn't verify, every cast has to be treated as non-safe.
@@ -1764,7 +1751,8 @@
 
 bool CompilerDriver::FastVerify(jobject jclass_loader,
                                 const std::vector<const DexFile*>& dex_files,
-                                TimingLogger* timings) {
+                                TimingLogger* timings,
+                                /*out*/ VerificationResults* verification_results) {
   verifier::VerifierDeps* verifier_deps =
       Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
   // If there exist VerifierDeps that aren't the ones we just created to output, use them to verify.
@@ -1812,7 +1800,7 @@
           // - Quickening will not do checkcast ellision.
           // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
           for (const ClassAccessor::Method& method : accessor.GetMethods()) {
-            verification_results_->CreateVerifiedMethodFor(method.GetReference());
+            verification_results->CreateVerifiedMethodFor(method.GetReference());
           }
         }
       } else if (!compiler_only_verifies) {
@@ -1830,8 +1818,9 @@
 
 void CompilerDriver::Verify(jobject jclass_loader,
                             const std::vector<const DexFile*>& dex_files,
-                            TimingLogger* timings) {
-  if (FastVerify(jclass_loader, dex_files, timings)) {
+                            TimingLogger* timings,
+                            /*out*/ VerificationResults* verification_results) {
+  if (FastVerify(jclass_loader, dex_files, timings, verification_results)) {
     return;
   }
 
@@ -2530,7 +2519,7 @@
     // SetVerificationAttempted so that the access flags are set. If we do not do this they get
     // changed at runtime resulting in more dirty image pages.
     // Also create conflict tables.
-    // Only useful if we are compiling an image (image_classes_ is not null).
+    // Only useful if we are compiling an image.
     ScopedObjectAccess soa(Thread::Current());
     VariableSizedHandleScope hs(soa.Self());
     InitializeArrayClassesAndCreateConflictTablesVisitor visitor(hs);
@@ -2569,8 +2558,9 @@
     ClassReference ref(&dex_file, class_def_index);
     const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
     ClassAccessor accessor(dex_file, class_def_index);
+    CompilerDriver* const driver = context.GetCompiler();
     // Skip compiling classes with generic verifier failures since they will still fail at runtime
-    if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) {
+    if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) {
       return;
     }
     // Use a scoped object access to perform to the quick SkipClass check.
@@ -2602,8 +2592,6 @@
     // Go to native so that we don't block GC during compilation.
     ScopedThreadSuspension sts(soa.Self(), kNative);
 
-    CompilerDriver* const driver = context.GetCompiler();
-
     // Can we run DEX-to-DEX compiler on this class ?
     optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
         GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
@@ -2775,31 +2763,6 @@
   return compiled_method;
 }
 
-bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
-                                                     uint16_t class_def_idx,
-                                                     const DexFile& dex_file) const {
-  const VerifiedMethod* verified_method = GetVerifiedMethod(&dex_file, method_idx);
-  if (verified_method != nullptr) {
-    return !verified_method->HasVerificationFailures();
-  }
-
-  // If we can't find verification metadata, check if this is a system class (we trust that system
-  // classes have their methods verified). If it's not, be conservative and assume the method
-  // has not been verified successfully.
-
-  // TODO: When compiling the boot image it should be safe to assume that everything is verified,
-  // even if methods are not found in the verification cache.
-  const char* descriptor = dex_file.GetClassDescriptor(dex_file.GetClassDef(class_def_idx));
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Thread* self = Thread::Current();
-  ScopedObjectAccess soa(self);
-  bool is_system_class = class_linker->FindSystemClass(self, descriptor) != nullptr;
-  if (!is_system_class) {
-    self->ClearException();
-  }
-  return is_system_class;
-}
-
 std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
   std::ostringstream oss;
   const gc::Heap* const heap = Runtime::Current()->GetHeap();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 714b2d1..7c0fc64 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -76,7 +76,6 @@
 class TimingLogger;
 class VdexFile;
 class VerificationResults;
-class VerifiedMethod;
 
 enum EntryPointCallingConvention {
   // ABI of invocations to a method's interpreter entry point.
@@ -95,9 +94,7 @@
   // can assume will be in the image, with null implying all available
   // classes.
   CompilerDriver(const CompilerOptions* compiler_options,
-                 VerificationResults* verification_results,
                  Compiler::Kind compiler_kind,
-                 HashSet<std::string>* image_classes,
                  size_t thread_count,
                  int swap_fd);
 
@@ -106,6 +103,17 @@
   // Set dex files classpath.
   void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files);
 
+  // Initialize and destroy thread pools. This is exposed because we do not want
+  // to do this twice, for PreCompile() and CompileAll().
+  void InitializeThreadPools();
+  void FreeThreadPools();
+
+  void PreCompile(jobject class_loader,
+                  const std::vector<const DexFile*>& dex_files,
+                  TimingLogger* timings,
+                  /*inout*/ HashSet<std::string>* image_classes,
+                  /*out*/ VerificationResults* verification_results)
+      REQUIRES(!Locks::mutator_lock_);
   void CompileAll(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
                   TimingLogger* timings)
@@ -124,8 +132,6 @@
                   Handle<mirror::ClassLoader> h_class_loader)
       REQUIRES(!Locks::mutator_lock_);
 
-  VerificationResults* GetVerificationResults() const;
-
   const CompilerOptions& GetCompilerOptions() const {
     return *compiler_options_;
   }
@@ -194,7 +200,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
 
-  const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
   bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
 
   size_t GetThreadCount() const {
@@ -219,12 +224,6 @@
 
   void RecordClassStatus(const ClassReference& ref, ClassStatus status);
 
-  // Checks if the specified method has been verified without failures. Returns
-  // false if the method is not in the verification results (GetVerificationResults).
-  bool IsMethodVerifiedWithoutFailures(uint32_t method_idx,
-                                       uint16_t class_def_idx,
-                                       const DexFile& dex_file) const;
-
   // Get memory usage during compilation.
   std::string GetMemoryUsageString(bool extended) const;
 
@@ -265,13 +264,9 @@
   }
 
  private:
-  void PreCompile(jobject class_loader,
-                  const std::vector<const DexFile*>& dex_files,
-                  TimingLogger* timings)
+  void LoadImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes)
       REQUIRES(!Locks::mutator_lock_);
 
-  void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
-
   // Attempt to resolve all type, methods, fields, and strings
   // referenced from code in the dex file following PathClassLoader
   // ordering semantics.
@@ -291,11 +286,13 @@
   // verification was successful.
   bool FastVerify(jobject class_loader,
                   const std::vector<const DexFile*>& dex_files,
-                  TimingLogger* timings);
+                  TimingLogger* timings,
+                  /*out*/ VerificationResults* verification_results);
 
   void Verify(jobject class_loader,
               const std::vector<const DexFile*>& dex_files,
-              TimingLogger* timings);
+              TimingLogger* timings,
+              /*out*/ VerificationResults* verification_results);
 
   void VerifyDexFile(jobject class_loader,
                      const DexFile& dex_file,
@@ -326,14 +323,13 @@
                          TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
-  void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
+  void UpdateImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes)
+      REQUIRES(!Locks::mutator_lock_);
 
   void Compile(jobject class_loader,
                const std::vector<const DexFile*>& dex_files,
                TimingLogger* timings);
 
-  void InitializeThreadPools();
-  void FreeThreadPools();
   void CheckThreadPools();
 
   // Resolve const string literals that are loaded from dex code. If only_startup_strings is
@@ -343,7 +339,6 @@
                            /*inout*/ TimingLogger* timings);
 
   const CompilerOptions* const compiler_options_;
-  VerificationResults* const verification_results_;
 
   std::unique_ptr<Compiler> compiler_;
   Compiler::Kind compiler_kind_;
@@ -359,12 +354,6 @@
   // All method references that this compiler has compiled.
   MethodTable compiled_methods_;
 
-  // Image classes to be updated by PreCompile().
-  // TODO: Remove this member which is a non-const pointer to the CompilerOptions' data.
-  //       Pass this explicitly to the PreCompile() which should be called directly from
-  //       Dex2Oat rather than implicitly by CompileAll().
-  HashSet<std::string>* image_classes_;
-
   std::atomic<uint32_t> number_of_soft_verifier_failures_;
 
   bool had_hard_verifier_failure_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index fe1568d..b924129 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -42,20 +42,18 @@
 
 class CompilerDriverTest : public CommonCompilerTest {
  protected:
-  void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
-    TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
-    TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+  void CompileAllAndMakeExecutable(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
+    TimingLogger timings("CompilerDriverTest::CompileAllAndMakeExecutable", false, false);
     dex_files_ = GetDexFiles(class_loader);
-    SetDexFilesForOatFile(dex_files_);
-    compiler_driver_->CompileAll(class_loader, dex_files_, &timings);
-    t.NewTiming("MakeAllExecutable");
+    CompileAll(class_loader, dex_files_, &timings);
+    TimingLogger::ScopedTiming t("MakeAllExecutable", &timings);
     MakeAllExecutable(class_loader);
   }
 
   void EnsureCompiled(jobject class_loader, const char* class_name, const char* method,
                       const char* signature, bool is_virtual)
       REQUIRES(!Locks::mutator_lock_) {
-    CompileAll(class_loader);
+    CompileAllAndMakeExecutable(class_loader);
     Thread::Current()->TransitionFromSuspendedToRunnable();
     bool started = runtime_->Start();
     CHECK(started);
@@ -106,7 +104,7 @@
 // Disabled due to 10 second runtime on host
 // TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
 TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
-  CompileAll(nullptr);
+  CompileAllAndMakeExecutable(nullptr);
 
   // All libcore references should resolve
   ScopedObjectAccess soa(Thread::Current());
@@ -266,7 +264,7 @@
     ASSERT_TRUE(dex_file->EnableWrite());
   }
 
-  CompileAll(class_loader);
+  CompileAllAndMakeExecutable(class_loader);
 
   std::unordered_set<std::string> m = GetExpectedMethodsForClass("Main");
   std::unordered_set<std::string> s = GetExpectedMethodsForClass("Second");
@@ -310,7 +308,7 @@
   }
   ASSERT_NE(class_loader, nullptr);
 
-  CompileAll(class_loader);
+  CompileAllAndMakeExecutable(class_loader);
 
   CheckVerifiedClass(class_loader, "LMain;");
   CheckVerifiedClass(class_loader, "LSecond;");
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index b28c7e0..8d1ae3d 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -24,9 +24,14 @@
 #include "arch/instruction_set_features.h"
 #include "base/runtime_debug.h"
 #include "base/variant_map.h"
+#include "class_linker.h"
 #include "cmdline_parser.h"
 #include "compiler_options_map-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/verification_results.h"
+#include "dex/verified_method.h"
 #include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
 #include "simple_compiler_options_map.h"
 
 namespace art {
@@ -44,6 +49,7 @@
       no_inline_from_(),
       dex_files_for_oat_file_(),
       image_classes_(),
+      verification_results_(nullptr),
       image_type_(ImageType::kNone),
       compiling_with_core_image_(false),
       baseline_(false),
@@ -141,4 +147,40 @@
   return image_classes_.find(StringPiece(descriptor)) != image_classes_.end();
 }
 
+const VerificationResults* CompilerOptions::GetVerificationResults() const {
+  DCHECK(Runtime::Current()->IsAotCompiler());
+  return verification_results_;
+}
+
+const VerifiedMethod* CompilerOptions::GetVerifiedMethod(const DexFile* dex_file,
+                                                         uint32_t method_idx) const {
+  MethodReference ref(dex_file, method_idx);
+  return verification_results_->GetVerifiedMethod(ref);
+}
+
+bool CompilerOptions::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+                                                      uint16_t class_def_idx,
+                                                      const DexFile& dex_file) const {
+  const VerifiedMethod* verified_method = GetVerifiedMethod(&dex_file, method_idx);
+  if (verified_method != nullptr) {
+    return !verified_method->HasVerificationFailures();
+  }
+
+  // If we can't find verification metadata, check if this is a system class (we trust that system
+  // classes have their methods verified). If it's not, be conservative and assume the method
+  // has not been verified successfully.
+
+  // TODO: When compiling the boot image it should be safe to assume that everything is verified,
+  // even if methods are not found in the verification cache.
+  const char* descriptor = dex_file.GetClassDescriptor(dex_file.GetClassDef(class_def_idx));
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+  bool is_system_class = class_linker->FindSystemClass(self, descriptor) != nullptr;
+  if (!is_system_class) {
+    self->ClearException();
+  }
+  return is_system_class;
+}
+
 }  // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a8f246d..bd12bf7 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -47,6 +47,8 @@
 enum class InstructionSet;
 class InstructionSetFeatures;
 class ProfileCompilationInfo;
+class VerificationResults;
+class VerifiedMethod;
 
 // Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
 enum class ProfileMethodsCheck : uint8_t {
@@ -283,6 +285,16 @@
 
   bool IsImageClass(const char* descriptor) const;
 
+  const VerificationResults* GetVerificationResults() const;
+
+  const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
+
+  // Checks if the specified method has been verified without failures. Returns
+  // false if the method is not in the verification results (GetVerificationResults).
+  bool IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+                                       uint16_t class_def_idx,
+                                       const DexFile& dex_file) const;
+
   bool ParseCompilerOptions(const std::vector<std::string>& options,
                             bool ignore_unrecognized,
                             std::string* error_msg);
@@ -381,6 +393,9 @@
   // Must not be empty for real boot image, only for tests pretending to compile boot image.
   HashSet<std::string> image_classes_;
 
+  // Results of AOT verification.
+  const VerificationResults* verification_results_;
+
   ImageType image_type_;
   bool compiling_with_core_image_;
   bool baseline_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 0eab835..b9fd868 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -167,9 +167,7 @@
 
   compiler_driver_.reset(new CompilerDriver(
       compiler_options_.get(),
-      /* verification_results */ nullptr,
       Compiler::kOptimizing,
-      /* image_classes */ nullptr,
       /* thread_count */ 1,
       /* swap_fd */ -1));
   // Disable dedupe so we can remove compiled methods.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index ec93222..417d794 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -28,7 +28,6 @@
 #include "dex/inline_method_analyser.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
-#include "driver/compiler_driver-inl.h"
 #include "driver/compiler_options.h"
 #include "driver/dex_compilation_unit.h"
 #include "instruction_simplifier.h"
@@ -408,7 +407,7 @@
   return single_impl;
 }
 
-static bool IsMethodUnverified(CompilerDriver* const compiler_driver, ArtMethod* method)
+static bool IsMethodUnverified(const CompilerOptions& compiler_options, ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (!method->GetDeclaringClass()->IsVerified()) {
     if (Runtime::Current()->UseJitCompilation()) {
@@ -417,8 +416,9 @@
       return true;
     }
     uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
-    if (!compiler_driver->IsMethodVerifiedWithoutFailures(
-        method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
+    if (!compiler_options.IsMethodVerifiedWithoutFailures(method->GetDexMethodIndex(),
+                                                          class_def_idx,
+                                                          *method->GetDexFile())) {
       // Method has soft or hard failures, don't analyze.
       return true;
     }
@@ -426,11 +426,11 @@
   return false;
 }
 
-static bool AlwaysThrows(CompilerDriver* const compiler_driver, ArtMethod* method)
+static bool AlwaysThrows(const CompilerOptions& compiler_options, ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(method != nullptr);
   // Skip non-compilable and unverified methods.
-  if (!method->IsCompilable() || IsMethodUnverified(compiler_driver, method)) {
+  if (!method->IsCompilable() || IsMethodUnverified(compiler_options, method)) {
     return false;
   }
   // Skip native methods, methods with try blocks, and methods that are too large.
@@ -518,7 +518,7 @@
           MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
         }
       }
-    } else if (!cha_devirtualize && AlwaysThrows(compiler_driver_, actual_method)) {
+    } else if (!cha_devirtualize && AlwaysThrows(codegen_->GetCompilerOptions(), actual_method)) {
       // Set always throws property for non-inlined method call with single target
       // (unless it was obtained through CHA, because that would imply we have
       // to add the CHA dependency, which seems not worth it).
@@ -1500,7 +1500,7 @@
     return false;
   }
 
-  if (IsMethodUnverified(compiler_driver_, method)) {
+  if (IsMethodUnverified(codegen_->GetCompilerOptions(), method)) {
     LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified)
         << "Method " << method->PrettyMethod()
         << " couldn't be verified, so it cannot be inlined";
@@ -2069,7 +2069,6 @@
                    codegen_,
                    outer_compilation_unit_,
                    dex_compilation_unit,
-                   compiler_driver_,
                    handles_,
                    inline_stats_,
                    total_number_of_dex_registers_ + accessor.RegistersSize(),
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 6fd0c20..8ac2163 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -38,7 +38,6 @@
            CodeGenerator* codegen,
            const DexCompilationUnit& outer_compilation_unit,
            const DexCompilationUnit& caller_compilation_unit,
-           CompilerDriver* compiler_driver,
            VariableSizedHandleScope* handles,
            OptimizingCompilerStats* stats,
            size_t total_number_of_dex_registers,
@@ -51,7 +50,6 @@
         outer_compilation_unit_(outer_compilation_unit),
         caller_compilation_unit_(caller_compilation_unit),
         codegen_(codegen),
-        compiler_driver_(compiler_driver),
         total_number_of_dex_registers_(total_number_of_dex_registers),
         total_number_of_instructions_(total_number_of_instructions),
         parent_(parent),
@@ -280,7 +278,6 @@
   const DexCompilationUnit& outer_compilation_unit_;
   const DexCompilationUnit& caller_compilation_unit_;
   CodeGenerator* const codegen_;
-  CompilerDriver* const compiler_driver_;
   const size_t total_number_of_dex_registers_;
   size_t total_number_of_instructions_;
 
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 6d04b0e..1688ea7 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2950,6 +2950,151 @@
   __ Mvn(out, out);
 }
 
+// The threshold for sizes of arrays to use the library provided implementation
+// of CRC32.updateBytes instead of the intrinsic.
+static constexpr int32_t kCRC32UpdateBytesThreshold = 64 * 1024;
+
+void IntrinsicLocationsBuilderARM64::VisitCRC32UpdateBytes(HInvoke* invoke) {
+  if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
+    return;
+  }
+
+  LocationSummary* locations
+      = new (allocator_) LocationSummary(invoke,
+                                         LocationSummary::kCallOnSlowPath,
+                                         kIntrinsified);
+
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RegisterOrConstant(invoke->InputAt(2)));
+  locations->SetInAt(3, Location::RequiresRegister());
+  locations->AddTemp(Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+// Lower the invoke of CRC32.updateBytes(int crc, byte[] b, int off, int len)
+//
+// Note: The intrinsic is not used if len exceeds a threshold.
+void IntrinsicCodeGeneratorARM64::VisitCRC32UpdateBytes(HInvoke* invoke) {
+  DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
+
+  auto masm = GetVIXLAssembler();
+  auto locations = invoke->GetLocations();
+
+  auto slow_path =
+    new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
+  codegen_->AddSlowPath(slow_path);
+
+  Register length = WRegisterFrom(locations->InAt(3));
+  __ Cmp(length, kCRC32UpdateBytesThreshold);
+  __ B(slow_path->GetEntryLabel(), hi);
+
+  const uint32_t array_data_offset =
+      mirror::Array::DataOffset(Primitive::kPrimByte).Uint32Value();
+  Register ptr = XRegisterFrom(locations->GetTemp(0));
+  Register array = XRegisterFrom(locations->InAt(1));
+  auto offset = locations->InAt(2);
+  if (offset.IsConstant()) {
+    int32_t offset_value = offset.GetConstant()->AsIntConstant()->GetValue();
+    __ Add(ptr, array, array_data_offset + offset_value);
+  } else {
+    __ Add(ptr, array, array_data_offset);
+    __ Add(ptr, ptr, XRegisterFrom(offset));
+  }
+
+  // The algorithm of CRC32 of bytes is:
+  //   crc = ~crc
+  //   process a few first bytes to make the array 8-byte aligned
+  //   while array has 8 bytes do:
+  //     crc = crc32_of_8bytes(crc, 8_bytes(array))
+  //   if array has 4 bytes:
+  //     crc = crc32_of_4bytes(crc, 4_bytes(array))
+  //   if array has 2 bytes:
+  //     crc = crc32_of_2bytes(crc, 2_bytes(array))
+  //   if array has a byte:
+  //     crc = crc32_of_byte(crc, 1_byte(array))
+  //   crc = ~crc
+
+  vixl::aarch64::Label loop, done;
+  vixl::aarch64::Label process_4bytes, process_2bytes, process_1byte;
+  vixl::aarch64::Label aligned2, aligned4, aligned8;
+
+  // Use VIXL scratch registers as the VIXL macro assembler won't use them in
+  // instructions below.
+  UseScratchRegisterScope temps(masm);
+  Register len = temps.AcquireW();
+  Register array_elem = temps.AcquireW();
+
+  Register out = WRegisterFrom(locations->Out());
+  __ Mvn(out, WRegisterFrom(locations->InAt(0)));
+  __ Mov(len, length);
+
+  __ Tbz(ptr, 0, &aligned2);
+  __ Subs(len, len, 1);
+  __ B(&done, lo);
+  __ Ldrb(array_elem, MemOperand(ptr, 1, PostIndex));
+  __ Crc32b(out, out, array_elem);
+
+  __ Bind(&aligned2);
+  __ Tbz(ptr, 1, &aligned4);
+  __ Subs(len, len, 2);
+  __ B(&process_1byte, lo);
+  __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex));
+  __ Crc32h(out, out, array_elem);
+
+  __ Bind(&aligned4);
+  __ Tbz(ptr, 2, &aligned8);
+  __ Subs(len, len, 4);
+  __ B(&process_2bytes, lo);
+  __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex));
+  __ Crc32w(out, out, array_elem);
+
+  __ Bind(&aligned8);
+  __ Subs(len, len, 8);
+  // If len < 8 go to process data by 4 bytes, 2 bytes and a byte.
+  __ B(&process_4bytes, lo);
+
+  // The main loop processing data by 8 bytes.
+  __ Bind(&loop);
+  __ Ldr(array_elem.X(), MemOperand(ptr, 8, PostIndex));
+  __ Subs(len, len, 8);
+  __ Crc32x(out, out, array_elem.X());
+  // if len >= 8, process the next 8 bytes.
+  __ B(&loop, hs);
+
+  // Process the data which is less than 8 bytes.
+  // The code generated below works with values of len
+  // which come in the range [-8, 0].
+  // The first three bits are used to detect whether 4 bytes or 2 bytes or
+  // a byte can be processed.
+  // The checking order is from bit 2 to bit 0:
+  //  bit 2 is set: at least 4 bytes available
+  //  bit 1 is set: at least 2 bytes available
+  //  bit 0 is set: at least a byte available
+  __ Bind(&process_4bytes);
+  // Goto process_2bytes if less than four bytes available
+  __ Tbz(len, 2, &process_2bytes);
+  __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex));
+  __ Crc32w(out, out, array_elem);
+
+  __ Bind(&process_2bytes);
+  // Goto process_1bytes if less than two bytes available
+  __ Tbz(len, 1, &process_1byte);
+  __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex));
+  __ Crc32h(out, out, array_elem);
+
+  __ Bind(&process_1byte);
+  // Goto done if no bytes available
+  __ Tbz(len, 0, &done);
+  __ Ldrb(array_elem, MemOperand(ptr));
+  __ Crc32b(out, out, array_elem);
+
+  __ Bind(&done);
+  __ Mvn(out, out);
+
+  __ Bind(slow_path->GetExitLabel());
+}
+
 UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
 
 UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 4d45a99..88f1457 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -3060,6 +3060,7 @@
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateBytes)
 
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 21fb7d7..08ba0a0 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2697,6 +2697,7 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(MIPS, CRC32UpdateBytes)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 4b86f5d..59d3ba2 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2347,6 +2347,7 @@
 UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
 UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32UpdateBytes)
 
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index a73f4e8..1d94950 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -3071,6 +3071,7 @@
 UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateBytes)
 
 UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 88c766f..4f0b61d 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2738,6 +2738,7 @@
 UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
 UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
 UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(X86_64, CRC32UpdateBytes)
 
 UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 0f971e1..b75afad 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -181,7 +181,6 @@
     HGraph* graph,
     OptimizingCompilerStats* stats,
     CodeGenerator* codegen,
-    CompilerDriver* driver,
     const DexCompilationUnit& dex_compilation_unit,
     VariableSizedHandleScope* handles) {
   ArenaVector<HOptimization*> optimizations(allocator->Adapter());
@@ -258,7 +257,6 @@
                                        codegen,
                                        dex_compilation_unit,    // outer_compilation_unit
                                        dex_compilation_unit,    // outermost_compilation_unit
-                                       driver,
                                        handles,
                                        stats,
                                        accessor.RegistersSize(),
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index 490007d..ce44b5f 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -147,7 +147,6 @@
     HGraph* graph,
     OptimizingCompilerStats* stats,
     CodeGenerator* codegen,
-    CompilerDriver* driver,
     const DexCompilationUnit& dex_compilation_unit,
     VariableSizedHandleScope* handles);
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 641368b..92aaa19 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -321,7 +321,6 @@
         graph,
         compilation_stats_.get(),
         codegen,
-        GetCompilerDriver(),
         dex_compilation_unit,
         handles);
     DCHECK_EQ(length, optimizations.size());
@@ -962,9 +961,9 @@
       arena_stack,
       dex_file,
       method_idx,
-      compiler_driver->GetCompilerOptions().GetInstructionSet(),
+      compiler_options.GetInstructionSet(),
       kInvalidInvokeType,
-      compiler_driver->GetCompilerOptions().GetDebuggable(),
+      compiler_options.GetDebuggable(),
       /* osr */ false);
 
   DCHECK(Runtime::Current()->IsAotCompiler());
@@ -1043,12 +1042,13 @@
                                             const DexFile& dex_file,
                                             Handle<mirror::DexCache> dex_cache) const {
   CompilerDriver* compiler_driver = GetCompilerDriver();
+  const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions();
   CompiledMethod* compiled_method = nullptr;
   Runtime* runtime = Runtime::Current();
   DCHECK(runtime->IsAotCompiler());
-  const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
+  const VerifiedMethod* verified_method = compiler_options.GetVerifiedMethod(&dex_file, method_idx);
   DCHECK(!verified_method->HasRuntimeThrow());
-  if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) ||
+  if (compiler_options.IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) ||
       verifier::CanCompilerHandleVerificationFailure(
           verified_method->GetEncounteredVerificationFailures())) {
     ArenaAllocator allocator(runtime->GetArenaPool());
@@ -1080,7 +1080,7 @@
       // Go to native so that we don't block GC during compilation.
       ScopedThreadSuspension sts(soa.Self(), kNative);
       if (method != nullptr && UNLIKELY(method->IsIntrinsic())) {
-        DCHECK(compiler_driver->GetCompilerOptions().IsBootImage());
+        DCHECK(compiler_options.IsBootImage());
         codegen.reset(
             TryCompileIntrinsic(&allocator,
                                 &arena_stack,
@@ -1099,7 +1099,7 @@
                        &code_allocator,
                        dex_compilation_unit,
                        method,
-                       compiler_driver->GetCompilerOptions().IsBaseline(),
+                       compiler_options.IsBaseline(),
                        /* osr= */ false,
                        &handles));
       }
@@ -1128,7 +1128,7 @@
     }
   } else {
     MethodCompilationStat method_stat;
-    if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
+    if (compiler_options.VerifyAtRuntime()) {
       method_stat = MethodCompilationStat::kNotCompiledVerifyAtRuntime;
     } else {
       method_stat = MethodCompilationStat::kNotCompiledVerificationError;
@@ -1137,8 +1137,8 @@
   }
 
   if (kIsDebugBuild &&
-      compiler_driver->GetCompilerOptions().CompilingWithCoreImage() &&
-      IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) {
+      compiler_options.CompilingWithCoreImage() &&
+      IsInstructionSetSupported(compiler_options.GetInstructionSet())) {
     // For testing purposes, we put a special marker on method names
     // that should be compiled with this compiler (when the
     // instruction set is supported). This makes sure we're not
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index eb44dd7..8c90aa7 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -93,12 +93,12 @@
       verifier_deps_.reset(deps);
     }
     callbacks_->SetVerifierDeps(deps);
-    compiler_driver_->Verify(class_loader_, dex_files_, &timings);
+    compiler_driver_->Verify(class_loader_, dex_files_, &timings, verification_results_.get());
     callbacks_->SetVerifierDeps(nullptr);
     // Clear entries in the verification results to avoid hitting a DCHECK that
     // we always succeed inserting a new entry after verifying.
     AtomicDexRefMap<MethodReference, const VerifiedMethod*>* map =
-        &compiler_driver_->GetVerificationResults()->atomic_verified_methods_;
+        &verification_results_->atomic_verified_methods_;
     map->Visit([](const DexFileReference& ref ATTRIBUTE_UNUSED, const VerifiedMethod* method) {
       delete method;
     });
@@ -126,7 +126,7 @@
       class_linker_->RegisterDexFile(*dex_file, loader.Get());
     }
     for (const DexFile* dex_file : dex_files_) {
-      compiler_driver_->GetVerificationResults()->AddDexFile(dex_file);
+      verification_results_->AddDexFile(dex_file);
     }
     SetDexFilesForOatFile(dex_files_);
   }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index edd6189..f729934 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -630,6 +630,7 @@
       thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
       start_ns_(NanoTime()),
       start_cputime_ns_(ProcessCpuNanoTime()),
+      strip_(false),
       oat_fd_(-1),
       input_vdex_fd_(-1),
       output_vdex_fd_(-1),
@@ -1788,9 +1789,7 @@
     compiler_options_->profile_compilation_info_ = profile_compilation_info_.get();
 
     driver_.reset(new CompilerDriver(compiler_options_.get(),
-                                     verification_results_.get(),
                                      compiler_kind_,
-                                     &compiler_options_->image_classes_,
                                      thread_count_,
                                      swap_fd_));
     if (!IsBootImage()) {
@@ -1862,7 +1861,16 @@
                    << soa.Self()->GetException()->Dump();
       }
     }
+    driver_->InitializeThreadPools();
+    driver_->PreCompile(class_loader,
+                        dex_files,
+                        timings_,
+                        &compiler_options_->image_classes_,
+                        verification_results_.get());
+    callbacks_->SetVerificationResults(nullptr);  // Should not be needed anymore.
+    compiler_options_->verification_results_ = verification_results_.get();
     driver_->CompileAll(class_loader, dex_files, timings_);
+    driver_->FreeThreadPools();
     return class_loader;
   }
 
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 6ffcef1..13fa0f0 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -218,11 +218,9 @@
     {
       jobject class_loader = nullptr;
       TimingLogger timings("ImageTest::WriteRead", false, false);
-      TimingLogger::ScopedTiming t("CompileAll", &timings);
-      SetDexFilesForOatFile(class_path);
-      driver->CompileAll(class_loader, class_path, &timings);
+      CompileAll(class_loader, class_path, &timings);
 
-      t.NewTiming("WriteElf");
+      TimingLogger::ScopedTiming t("WriteElf", &timings);
       SafeMap<std::string, std::string> key_value_store;
       key_value_store.Put(OatHeader::kBootClassPathKey,
                           gc::space::ImageSpace::GetMultiImageBootClassPath(
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 6b17ef6..d045698 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -968,7 +968,7 @@
     ClassStatus status;
     bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status);
     if (!found) {
-      VerificationResults* results = writer_->compiler_driver_->GetVerificationResults();
+      const VerificationResults* results = writer_->compiler_options_.GetVerificationResults();
       if (results != nullptr && results->IsClassRejected(class_ref)) {
         // The oat class status is used only for verification of resolved classes,
         // so use ClassStatus::kErrorResolved whether the class was resolved or unresolved
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 598a0f8..5de1540 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -391,8 +391,7 @@
   jobject class_loader = nullptr;
   if (kCompile) {
     TimingLogger timings2("OatTest::WriteRead", false, false);
-    SetDexFilesForOatFile(class_linker->GetBootClassPath());
-    compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
+    CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
   }
 
   ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
@@ -405,7 +404,7 @@
   ASSERT_TRUE(success);
 
   if (kCompile) {  // OatWriter strips the code, regenerate to compare
-    compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
+    CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
   std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
                                                   tmp_oat.GetFilename(),
@@ -513,8 +512,7 @@
     ScopedObjectAccess soa(Thread::Current());
     class_linker->RegisterDexFile(*dex_file, soa.Decode<mirror::ClassLoader>(class_loader));
   }
-  SetDexFilesForOatFile(dex_files);
-  compiler_driver_->CompileAll(class_loader, dex_files, &timings);
+  CompileAll(class_loader, dex_files, &timings);
 
   ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9b2e1a1..c964dbc 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2026,7 +2026,7 @@
     // Image class loader [A][B][C][image dex files]
     // Class loader = [???][dex_elements][image dex files]
     // Need to ensure that [???][dex_elements] == [A][B][C].
-    // For each class loader, PathClassLoader, the laoder checks the parent first. Also the logic
+    // For each class loader, PathClassLoader, the loader checks the parent first. Also the logic
     // for PathClassLoader does this by looping through the array of dex files. To ensure they
     // resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
     // and check that the dex file names are the same.
@@ -2662,7 +2662,7 @@
     return true;
   }
 
-  if (IsPathOrDexClassLoader(soa, class_loader)) {
+  if (IsPathOrDexClassLoader(soa, class_loader) || IsInMemoryDexClassLoader(soa, class_loader)) {
     // For regular path or dex class loader the search order is:
     //    - parent
     //    - shared libraries
@@ -2756,7 +2756,9 @@
     const char* descriptor,
     size_t hash,
     Handle<mirror::ClassLoader> class_loader) {
-  DCHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+  DCHECK(IsPathOrDexClassLoader(soa, class_loader) ||
+         IsInMemoryDexClassLoader(soa, class_loader) ||
+         IsDelegateLastClassLoader(soa, class_loader))
       << "Unexpected class loader for descriptor " << descriptor;
 
   ObjPtr<mirror::Class> ret;
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 69476df..562dc47 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -29,7 +29,7 @@
 namespace art {
 
 // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
-// (they both have the same behaviour with respect to class lockup order)
+// (they both have the same behaviour with respect to class lookup order)
 inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                    Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -41,6 +41,15 @@
           soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
 }
 
+// Returns true if the given class loader is an InMemoryDexClassLoader.
+inline bool IsInMemoryDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                     Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return (class_loader_class ==
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
+}
+
 inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                       Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index d9d81f0..13bead2 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -235,6 +235,7 @@
       case Intrinsics::kUnsafeStoreFence:
       case Intrinsics::kUnsafeFullFence:
       case Intrinsics::kCRC32Update:
+      case Intrinsics::kCRC32UpdateBytes:
       case Intrinsics::kStringNewStringFromBytes:
       case Intrinsics::kStringNewStringFromChars:
       case Intrinsics::kStringNewStringFromString:
diff --git a/runtime/image.cc b/runtime/image.cc
index ae3d8e3..fb581f9 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,7 +29,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '1', '\0' };  // Add image blocks.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '2', '\0' };  // CRC32UpdateBytes intrinsic
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 24a026a..16e118c 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -559,6 +559,7 @@
     UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
     UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
     UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
+    UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */)
     INTRINSIC_CASE(VarHandleFullFence)
     INTRINSIC_CASE(VarHandleAcquireFence)
     INTRINSIC_CASE(VarHandleReleaseFence)
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 093dd7f..82ea476 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -220,6 +220,7 @@
   V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
   V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
   V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \
+  V(CRC32UpdateBytes, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "updateBytes", "(I[BII)I") \
   SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
 
 #endif  // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 4a3ef07..e43d771 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,22 +291,6 @@
   return success;
 }
 
-void Jit::CreateThreadPool() {
-  if (Runtime::Current()->IsSafeMode()) {
-    // Never create the pool in safe mode.
-    return;
-  }
-  // There is a DCHECK in the 'AddSamples' method to ensure the thread pool
-  // is not null when we instrument.
-
-  // We need peers as we may report the JIT thread, e.g., in the debugger.
-  constexpr bool kJitPoolNeedsPeers = true;
-  thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
-
-  thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
-  Start();
-}
-
 void Jit::DeleteThreadPool() {
   Thread* self = Thread::Current();
   DCHECK(Runtime::Current()->IsShuttingDown(self));
@@ -562,10 +546,10 @@
 
 class JitCompileTask final : public Task {
  public:
-  enum TaskKind {
+  enum class TaskKind {
     kAllocateProfile,
     kCompile,
-    kCompileOsr
+    kCompileOsr,
   };
 
   JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
@@ -582,14 +566,20 @@
 
   void Run(Thread* self) override {
     ScopedObjectAccess soa(self);
-    if (kind_ == kCompile) {
-      Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr= */ false);
-    } else if (kind_ == kCompileOsr) {
-      Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr= */ true);
-    } else {
-      DCHECK(kind_ == kAllocateProfile);
-      if (ProfilingInfo::Create(self, method_, /* retry_allocation= */ true)) {
-        VLOG(jit) << "Start profiling " << ArtMethod::PrettyMethod(method_);
+    switch (kind_) {
+      case TaskKind::kCompile:
+      case TaskKind::kCompileOsr: {
+        Runtime::Current()->GetJit()->CompileMethod(
+            method_,
+            self,
+            /* osr= */ (kind_ == TaskKind::kCompileOsr));
+        break;
+      }
+      case TaskKind::kAllocateProfile: {
+        if (ProfilingInfo::Create(self, method_, /* retry_allocation= */ true)) {
+          VLOG(jit) << "Start profiling " << ArtMethod::PrettyMethod(method_);
+        }
+        break;
       }
     }
     ProfileSaver::NotifyJitActivity();
@@ -607,6 +597,18 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
 };
 
+void Jit::CreateThreadPool() {
+  // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
+  // is not null when we instrument.
+
+  // We need peers as we may report the JIT thread, e.g., in the debugger.
+  constexpr bool kJitPoolNeedsPeers = true;
+  thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
+
+  thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
+  Start();
+}
+
 static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
   if (method->IsClassInitializer() || !method->IsCompilable()) {
     // We do not want to compile such methods.
@@ -630,11 +632,10 @@
 
 void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
   if (thread_pool_ == nullptr) {
-    // Should only see this when shutting down, starting up, or in zygote, which doesn't
-    // have a thread pool.
+    // Should only see this when shutting down, starting up, or in safe mode.
     DCHECK(Runtime::Current()->IsShuttingDown(self) ||
            !Runtime::Current()->IsFinishedStarting() ||
-           Runtime::Current()->IsZygote());
+           Runtime::Current()->IsSafeMode());
     return;
   }
   if (IgnoreSamplesForMethod(method)) {
@@ -675,7 +676,8 @@
       if (!success) {
         // We failed allocating. Instead of doing the collection on the Java thread, we push
         // an allocation to a compiler thread, that will do the collection.
-        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
+        thread_pool_->AddTask(
+            self, new JitCompileTask(method, JitCompileTask::TaskKind::kAllocateProfile));
       }
     }
     // Avoid jumping more than one state at a time.
@@ -685,7 +687,7 @@
       if ((new_count >= HotMethodThreshold()) &&
           !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
         DCHECK(thread_pool_ != nullptr);
-        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::TaskKind::kCompile));
       }
       // Avoid jumping more than one state at a time.
       new_count = std::min(new_count, static_cast<uint32_t>(OSRMethodThreshold() - 1));
@@ -697,7 +699,8 @@
       DCHECK(!method->IsNative());  // No back edges reported for native methods.
       if ((new_count >= OSRMethodThreshold()) &&  !code_cache_->IsOsrCompiled(method)) {
         DCHECK(thread_pool_ != nullptr);
-        thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+        thread_pool_->AddTask(
+            self, new JitCompileTask(method, JitCompileTask::TaskKind::kCompileOsr));
       }
     }
   }
@@ -730,7 +733,7 @@
         // The compiler requires a ProfilingInfo object for non-native methods.
         ProfilingInfo::Create(thread, np_method, /* retry_allocation= */ true);
       }
-      JitCompileTask compile_task(method, JitCompileTask::kCompile);
+      JitCompileTask compile_task(method, JitCompileTask::TaskKind::kCompile);
       // Fake being in a runtime thread so that class-load behavior will be the same as normal jit.
       ScopedSetRuntimeThread ssrt(thread);
       compile_task.Run(thread);
@@ -798,7 +801,16 @@
   }
 }
 
-void Jit::PostForkChildAction() {
+void Jit::PostForkChildAction(bool is_zygote) {
+  if (is_zygote) {
+    // Don't transition if this is for a child zygote.
+    return;
+  }
+  if (Runtime::Current()->IsSafeMode()) {
+    // Delete the thread pool, we are not going to JIT.
+    thread_pool_.reset(nullptr);
+    return;
+  }
   // At this point, the compiler options have been adjusted to the particular configuration
   // of the forked child. Parse them again.
   jit_update_options_(jit_compiler_handle_);
@@ -806,6 +818,28 @@
   // Adjust the status of code cache collection: the status from zygote was to not collect.
   code_cache_->SetGarbageCollectCode(!jit_generate_debug_info_(jit_compiler_handle_) &&
       !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled());
+
+  if (thread_pool_ != nullptr) {
+    // Remove potential tasks that have been inherited from the zygote.
+    thread_pool_->RemoveAllTasks(Thread::Current());
+
+    // Resume JIT compilation.
+    thread_pool_->CreateThreads();
+  }
+}
+
+void Jit::PreZygoteFork() {
+  if (thread_pool_ == nullptr) {
+    return;
+  }
+  thread_pool_->DeleteThreads();
+}
+
+void Jit::PostZygoteFork() {
+  if (thread_pool_ == nullptr) {
+    return;
+  }
+  thread_pool_->CreateThreads();
 }
 
 }  // namespace jit
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e12b032..7ce5f07 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -285,8 +285,14 @@
   // Start JIT threads.
   void Start();
 
-  // Transition to a zygote child state.
-  void PostForkChildAction();
+  // Transition to a child state.
+  void PostForkChildAction(bool is_zygote);
+
+  // Prepare for forking.
+  void PreZygoteFork();
+
+  // Adjust state after forking.
+  void PostZygoteFork();
 
  private:
   Jit(JitCodeCache* code_cache, JitOptions* options);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 97887cc..fdb6b86 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -436,6 +436,12 @@
   initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
   max_capacity = RoundDown(max_capacity, 2 * kPageSize);
 
+  used_memory_for_data_ = 0;
+  used_memory_for_code_ = 0;
+  number_of_compilations_ = 0;
+  number_of_osr_compilations_ = 0;
+  number_of_collections_ = 0;
+
   data_pages_ = MemMap();
   exec_pages_ = MemMap();
   non_exec_pages_ = MemMap();
@@ -477,7 +483,7 @@
 JitCodeCache::~JitCodeCache() {}
 
 bool JitCodeCache::ContainsPc(const void* ptr) const {
-  return exec_pages_.Begin() <= ptr && ptr < exec_pages_.End();
+  return exec_pages_.HasAddress(ptr) || zygote_exec_pages_.HasAddress(ptr);
 }
 
 bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) {
@@ -1321,7 +1327,7 @@
             return true;
           }
           const void* code = method_header->GetCode();
-          if (code_cache_->ContainsPc(code)) {
+          if (code_cache_->ContainsPc(code) && !code_cache_->IsInZygoteExecSpace(code)) {
             // Use the atomic set version, as multiple threads are executing this code.
             bitmap_->AtomicTestAndSet(FromCodeToAllocation(code));
           }
@@ -1493,7 +1499,7 @@
         // interpreter will update its entry point to the compiled code and call it.
         for (ProfilingInfo* info : profiling_infos_) {
           const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
-          if (ContainsPc(entry_point)) {
+          if (!IsInZygoteDataSpace(info) && ContainsPc(entry_point)) {
             info->SetSavedEntryPoint(entry_point);
             // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring
             // class of the method. We may be concurrently running a GC which makes accessing
@@ -1508,7 +1514,7 @@
         // Change entry points of native methods back to the GenericJNI entrypoint.
         for (const auto& entry : jni_stubs_map_) {
           const JniStubData& data = entry.second;
-          if (!data.IsCompiled()) {
+          if (!data.IsCompiled() || IsInZygoteExecSpace(data.GetCode())) {
             continue;
           }
           // Make sure a single invocation of the GenericJNI trampoline tries to recompile.
@@ -1540,7 +1546,9 @@
     // Iterate over all compiled code and remove entries that are not marked.
     for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
       JniStubData* data = &it->second;
-      if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) {
+      if (IsInZygoteExecSpace(data->GetCode()) ||
+          !data->IsCompiled() ||
+          GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) {
         ++it;
       } else {
         method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode()));
@@ -1550,7 +1558,7 @@
     for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
       const void* code_ptr = it->first;
       uintptr_t allocation = FromCodeToAllocation(code_ptr);
-      if (GetLiveBitmap()->Test(allocation)) {
+      if (IsInZygoteExecSpace(code_ptr) || GetLiveBitmap()->Test(allocation)) {
         ++it;
       } else {
         OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr);
@@ -1571,7 +1579,7 @@
       // Also remove the saved entry point from the ProfilingInfo objects.
       for (ProfilingInfo* info : profiling_infos_) {
         const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
-        if (!ContainsPc(ptr) && !info->IsInUseByCompiler()) {
+        if (!ContainsPc(ptr) && !info->IsInUseByCompiler() && !IsInZygoteDataSpace(info)) {
           info->GetMethod()->SetProfilingInfo(nullptr);
         }
 
@@ -1596,6 +1604,9 @@
     for (const auto& entry : jni_stubs_map_) {
       const JniStubData& data = entry.second;
       const void* code_ptr = data.GetCode();
+      if (IsInZygoteExecSpace(code_ptr)) {
+        continue;
+      }
       const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
       for (ArtMethod* method : data.GetMethods()) {
         if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
@@ -1607,6 +1618,9 @@
     for (const auto& it : method_code_map_) {
       ArtMethod* method = it.second;
       const void* code_ptr = it.first;
+      if (IsInZygoteExecSpace(code_ptr)) {
+        continue;
+      }
       const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
       if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
         GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
@@ -1953,6 +1967,7 @@
         instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
       }
       if (collection_in_progress_) {
+        CHECK(!IsInZygoteExecSpace(data->GetCode()));
         GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode()));
       }
     }
@@ -2057,6 +2072,10 @@
 }
 
 void JitCodeCache::FreeCode(uint8_t* code) {
+  if (IsInZygoteExecSpace(code)) {
+    // No need to free, this is shared memory.
+    return;
+  }
   used_memory_for_code_ -= mspace_usable_size(code);
   mspace_free(exec_mspace_, code);
 }
@@ -2068,6 +2087,10 @@
 }
 
 void JitCodeCache::FreeData(uint8_t* data) {
+  if (IsInZygoteDataSpace(data)) {
+    // No need to free, this is shared memory.
+    return;
+  }
   used_memory_for_data_ -= mspace_usable_size(data);
   mspace_free(data_mspace_, data);
 }
@@ -2091,13 +2114,11 @@
 }
 
 void JitCodeCache::PostForkChildAction(bool is_system_server, bool is_zygote) {
+  if (is_zygote) {
+    // Don't transition if this is for a child zygote.
+    return;
+  }
   MutexLock mu(Thread::Current(), lock_);
-  // Currently, we don't expect any compilations from zygote.
-  CHECK_EQ(number_of_compilations_, 0u);
-  CHECK_EQ(number_of_osr_compilations_, 0u);
-  CHECK(jni_stubs_map_.empty());
-  CHECK(method_code_map_.empty());
-  CHECK(osr_code_map_.empty());
 
   zygote_data_pages_ = std::move(data_pages_);
   zygote_exec_pages_ = std::move(exec_pages_);
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 7a838fd..e2f3357 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -71,6 +71,7 @@
 
 namespace jit {
 
+class MarkCodeClosure;
 class ScopedCodeCacheWrite;
 
 // Alignment in bits that will suit all architectures.
@@ -387,6 +388,14 @@
 
   const MemMap* GetUpdatableCodeMapping() const;
 
+  bool IsInZygoteDataSpace(const void* ptr) const {
+    return zygote_data_pages_.HasAddress(ptr);
+  }
+
+  bool IsInZygoteExecSpace(const void* ptr) const {
+    return zygote_exec_pages_.HasAddress(ptr);
+  }
+
   bool IsWeakAccessEnabled(Thread* self) const;
   void WaitUntilInlineCacheAccessible(Thread* self)
       REQUIRES(!lock_)
@@ -487,6 +496,7 @@
 
   friend class art::JitJniStubTestHelper;
   friend class ScopedCodeCacheWrite;
+  friend class MarkCodeClosure;
 
   DISALLOW_COPY_AND_ASSIGN(JitCodeCache);
 };
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 0f655b9..b7ac1e8 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -249,6 +249,13 @@
   return reinterpret_cast<jlong>(ThreadForEnv(env));
 }
 
+static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) {
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsZygote()) {
+    runtime->PostZygoteFork();
+  }
+}
+
 static void ZygoteHooks_nativePostForkSystemServer(JNIEnv* env ATTRIBUTE_UNUSED,
                                                    jclass klass ATTRIBUTE_UNUSED) {
   // This JIT code cache for system server is created whilst the runtime is still single threaded.
@@ -305,7 +312,7 @@
           /* is_system_server= */ false, is_zygote);
     }
     // This must be called after EnableDebugFeatures.
-    Runtime::Current()->GetJit()->PostForkChildAction();
+    Runtime::Current()->GetJit()->PostForkChildAction(is_zygote);
   }
 
   // Update tracing.
@@ -403,6 +410,7 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
+  NATIVE_METHOD(ZygoteHooks, nativePostZygoteFork, "()V"),
   NATIVE_METHOD(ZygoteHooks, nativePostForkSystemServer, "()V"),
   NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZZLjava/lang/String;)V"),
   NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V"),
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8be3730..182319a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -600,9 +600,18 @@
 }
 
 void Runtime::PreZygoteFork() {
+  if (GetJit() != nullptr) {
+    GetJit()->PreZygoteFork();
+  }
   heap_->PreZygoteFork();
 }
 
+void Runtime::PostZygoteFork() {
+  if (GetJit() != nullptr) {
+    GetJit()->PostZygoteFork();
+  }
+}
+
 void Runtime::CallExitHook(jint status) {
   if (exit_ != nullptr) {
     ScopedThreadStateChange tsc(Thread::Current(), kNative);
@@ -916,10 +925,6 @@
     }
   }
 
-  if (jit_ != nullptr) {
-    jit_->CreateThreadPool();
-  }
-
   if (thread_pool_ == nullptr) {
     constexpr size_t kStackSize = 64 * KB;
     constexpr size_t kMaxRuntimeWorkers = 4u;
@@ -2448,6 +2453,8 @@
     LOG(WARNING) << "Failed to allocate JIT";
     // Release JIT code cache resources (several MB of memory).
     jit_code_cache_.reset();
+  } else {
+    jit->CreateThreadPool();
   }
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 5450c0f..4533376 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -445,6 +445,7 @@
   bool UseJitCompilation() const;
 
   void PreZygoteFork();
+  void PostZygoteFork();
   void InitNonZygoteOrPostFork(
       JNIEnv* env,
       bool is_system_server,
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 0f96510..de698c2 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -136,26 +136,33 @@
     waiting_count_(0),
     start_time_(0),
     total_wait_time_(0),
-    // Add one since the caller of constructor waits on the barrier too.
-    creation_barier_(num_threads + 1),
+    creation_barier_(0),
     max_active_workers_(num_threads),
-    create_peers_(create_peers) {
-  while (GetThreadCount() < num_threads) {
-    const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
-                                                 GetThreadCount());
-    threads_.push_back(new ThreadPoolWorker(this, worker_name, worker_stack_size));
+    create_peers_(create_peers),
+    worker_stack_size_(worker_stack_size) {
+  CreateThreads();
+}
+
+void ThreadPool::CreateThreads() {
+  CHECK(threads_.empty());
+  Thread* self = Thread::Current();
+  {
+    MutexLock mu(self, task_queue_lock_);
+    shutting_down_ = false;
+    // Add one since the caller of constructor waits on the barrier too.
+    creation_barier_.Init(self, max_active_workers_ + 1);
+    while (GetThreadCount() < max_active_workers_) {
+      const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
+                                                   GetThreadCount());
+      threads_.push_back(
+          new ThreadPoolWorker(this, worker_name, worker_stack_size_));
+    }
   }
   // Wait for all of the threads to attach.
   creation_barier_.Wait(Thread::Current());
 }
 
-void ThreadPool::SetMaxActiveWorkers(size_t threads) {
-  MutexLock mu(Thread::Current(), task_queue_lock_);
-  CHECK_LE(threads, GetThreadCount());
-  max_active_workers_ = threads;
-}
-
-ThreadPool::~ThreadPool() {
+void ThreadPool::DeleteThreads() {
   {
     Thread* self = Thread::Current();
     MutexLock mu(self, task_queue_lock_);
@@ -165,10 +172,22 @@
     task_queue_condition_.Broadcast(self);
     completion_condition_.Broadcast(self);
   }
-  // Wait for the threads to finish.
+  // Wait for the threads to finish. We expect the user of the pool
+  // not to run multi-threaded calls to `CreateThreads` and `DeleteThreads`,
+  // so we don't guard the field here.
   STLDeleteElements(&threads_);
 }
 
+void ThreadPool::SetMaxActiveWorkers(size_t max_workers) {
+  MutexLock mu(Thread::Current(), task_queue_lock_);
+  CHECK_LE(max_workers, GetThreadCount());
+  max_active_workers_ = max_workers;
+}
+
+ThreadPool::~ThreadPool() {
+  DeleteThreads();
+}
+
 void ThreadPool::StartWorkers(Thread* self) {
   MutexLock mu(self, task_queue_lock_);
   started_ = true;
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index fee009b..f55d72e 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -129,6 +129,12 @@
              size_t worker_stack_size = ThreadPoolWorker::kDefaultStackSize);
   virtual ~ThreadPool();
 
+  // Create the threads of this pool.
+  void CreateThreads();
+
+  // Stops and deletes all threads in this pool.
+  void DeleteThreads();
+
   // Wait for all tasks currently on queue to get completed. If the pool has been stopped, only
   // wait till all already running tasks are done.
   // When the pool was created with peers for workers, do_work must not be true (see ThreadPool()).
@@ -174,7 +180,6 @@
   // How many worker threads are waiting on the condition.
   volatile size_t waiting_count_ GUARDED_BY(task_queue_lock_);
   std::deque<Task*> tasks_ GUARDED_BY(task_queue_lock_);
-  // TODO: make this immutable/const?
   std::vector<ThreadPoolWorker*> threads_;
   // Work balance detection.
   uint64_t start_time_ GUARDED_BY(task_queue_lock_);
@@ -182,6 +187,7 @@
   Barrier creation_barier_;
   size_t max_active_workers_ GUARDED_BY(task_queue_lock_);
   const bool create_peers_;
+  const size_t worker_stack_size_;
 
  private:
   friend class ThreadPoolWorker;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index cde885c..a19cc92 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -48,6 +48,7 @@
 jclass WellKnownClasses::dalvik_system_DexPathList;
 jclass WellKnownClasses::dalvik_system_DexPathList__Element;
 jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
+jclass WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
 jclass WellKnownClasses::dalvik_system_PathClassLoader;
 jclass WellKnownClasses::dalvik_system_VMRuntime;
 jclass WellKnownClasses::java_lang_annotation_Annotation__array;
@@ -306,6 +307,7 @@
   dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
   dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
   dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
+  dalvik_system_InMemoryDexClassLoader = CacheClass(env, "dalvik/system/InMemoryDexClassLoader");
   dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
   dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime");
 
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 66cbbec..f0e98a8 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -57,6 +57,7 @@
   static jclass dalvik_system_DexPathList;
   static jclass dalvik_system_DexPathList__Element;
   static jclass dalvik_system_EmulatedStackFrame;
+  static jclass dalvik_system_InMemoryDexClassLoader;
   static jclass dalvik_system_PathClassLoader;
   static jclass dalvik_system_VMRuntime;
   static jclass java_lang_annotation_Annotation__array;
diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java
index 7fc1273..6199e9b 100644
--- a/test/580-crc32/src/Main.java
+++ b/test/580-crc32/src/Main.java
@@ -15,29 +15,29 @@
  */
 
 import java.util.zip.CRC32;
+import java.util.Random;
 
 /**
- * The ART compiler can use intrinsics for the java.util.zip.CRC32 method:
- *    private native static int update(int crc, int b)
+ * The ART compiler can use intrinsics for the java.util.zip.CRC32 methods:
+ *   private native static int update(int crc, int b)
+ *   private native static int updateBytes(int crc, byte[] b, int off, int len)
  *
- * As the method is private it is not possible to check the use of intrinsics
- * for it directly.
+ * As the methods are private it is not possible to check the use of intrinsics
+ * for them directly.
  * The tests check that correct checksums are produced.
  */
 public class Main {
-  private static CRC32 crc32 = new CRC32();
-
   public Main() {
   }
 
-  public static long TestInt(int value) {
-    crc32.reset();
+  public static long CRC32Byte(int value) {
+    CRC32 crc32 = new CRC32();
     crc32.update(value);
     return crc32.getValue();
   }
 
-  public static long TestInt(int... values) {
-    crc32.reset();
+  public static long CRC32BytesUsingUpdateInt(int... values) {
+    CRC32 crc32 = new CRC32();
     for (int value : values) {
       crc32.update(value);
     }
@@ -50,82 +50,301 @@
     }
   }
 
-  public static void main(String args[]) {
+  private static void assertEqual(boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new Error("Expected: " + expected + ", found: " + actual);
+    }
+  }
+
+  private static void TestCRC32Update() {
     // public void update(int b)
     //
     // Tests for checksums of the byte 0x0
-    assertEqual(0xD202EF8DL, TestInt(0x0));
-    assertEqual(0xD202EF8DL, TestInt(0x0100));
-    assertEqual(0xD202EF8DL, TestInt(0x010000));
-    assertEqual(0xD202EF8DL, TestInt(0x01000000));
-    assertEqual(0xD202EF8DL, TestInt(0xff00));
-    assertEqual(0xD202EF8DL, TestInt(0xffff00));
-    assertEqual(0xD202EF8DL, TestInt(0xffffff00));
-    assertEqual(0xD202EF8DL, TestInt(0x1200));
-    assertEqual(0xD202EF8DL, TestInt(0x123400));
-    assertEqual(0xD202EF8DL, TestInt(0x12345600));
-    assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE));
+    // Check that only the low eight bits of the argument are used.
+    assertEqual(0xD202EF8DL, CRC32Byte(0x0));
+    assertEqual(0xD202EF8DL, CRC32Byte(0x0100));
+    assertEqual(0xD202EF8DL, CRC32Byte(0x010000));
+    assertEqual(0xD202EF8DL, CRC32Byte(0x01000000));
+    assertEqual(0xD202EF8DL, CRC32Byte(0xff00));
+    assertEqual(0xD202EF8DL, CRC32Byte(0xffff00));
+    assertEqual(0xD202EF8DL, CRC32Byte(0xffffff00));
+    assertEqual(0xD202EF8DL, CRC32Byte(0x1200));
+    assertEqual(0xD202EF8DL, CRC32Byte(0x123400));
+    assertEqual(0xD202EF8DL, CRC32Byte(0x12345600));
+    assertEqual(0xD202EF8DL, CRC32Byte(Integer.MIN_VALUE));
 
     // Tests for checksums of the byte 0x1
-    assertEqual(0xA505DF1BL, TestInt(0x1));
-    assertEqual(0xA505DF1BL, TestInt(0x0101));
-    assertEqual(0xA505DF1BL, TestInt(0x010001));
-    assertEqual(0xA505DF1BL, TestInt(0x01000001));
-    assertEqual(0xA505DF1BL, TestInt(0xff01));
-    assertEqual(0xA505DF1BL, TestInt(0xffff01));
-    assertEqual(0xA505DF1BL, TestInt(0xffffff01));
-    assertEqual(0xA505DF1BL, TestInt(0x1201));
-    assertEqual(0xA505DF1BL, TestInt(0x123401));
-    assertEqual(0xA505DF1BL, TestInt(0x12345601));
+    // Check that only the low eight bits of the argument are used.
+    assertEqual(0xA505DF1BL, CRC32Byte(0x1));
+    assertEqual(0xA505DF1BL, CRC32Byte(0x0101));
+    assertEqual(0xA505DF1BL, CRC32Byte(0x010001));
+    assertEqual(0xA505DF1BL, CRC32Byte(0x01000001));
+    assertEqual(0xA505DF1BL, CRC32Byte(0xff01));
+    assertEqual(0xA505DF1BL, CRC32Byte(0xffff01));
+    assertEqual(0xA505DF1BL, CRC32Byte(0xffffff01));
+    assertEqual(0xA505DF1BL, CRC32Byte(0x1201));
+    assertEqual(0xA505DF1BL, CRC32Byte(0x123401));
+    assertEqual(0xA505DF1BL, CRC32Byte(0x12345601));
 
     // Tests for checksums of the byte 0x0f
-    assertEqual(0x42BDF21CL, TestInt(0x0f));
-    assertEqual(0x42BDF21CL, TestInt(0x010f));
-    assertEqual(0x42BDF21CL, TestInt(0x01000f));
-    assertEqual(0x42BDF21CL, TestInt(0x0100000f));
-    assertEqual(0x42BDF21CL, TestInt(0xff0f));
-    assertEqual(0x42BDF21CL, TestInt(0xffff0f));
-    assertEqual(0x42BDF21CL, TestInt(0xffffff0f));
-    assertEqual(0x42BDF21CL, TestInt(0x120f));
-    assertEqual(0x42BDF21CL, TestInt(0x12340f));
-    assertEqual(0x42BDF21CL, TestInt(0x1234560f));
+    // Check that only the low eight bits of the argument are used.
+    assertEqual(0x42BDF21CL, CRC32Byte(0x0f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0x010f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0x01000f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0x0100000f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0xff0f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0xffff0f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0xffffff0f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0x120f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0x12340f));
+    assertEqual(0x42BDF21CL, CRC32Byte(0x1234560f));
 
     // Tests for checksums of the byte 0xff
-    assertEqual(0xFF000000L, TestInt(0x00ff));
-    assertEqual(0xFF000000L, TestInt(0x01ff));
-    assertEqual(0xFF000000L, TestInt(0x0100ff));
-    assertEqual(0xFF000000L, TestInt(0x010000ff));
-    assertEqual(0xFF000000L, TestInt(0x0000ffff));
-    assertEqual(0xFF000000L, TestInt(0x00ffffff));
-    assertEqual(0xFF000000L, TestInt(0xffffffff));
-    assertEqual(0xFF000000L, TestInt(0x12ff));
-    assertEqual(0xFF000000L, TestInt(0x1234ff));
-    assertEqual(0xFF000000L, TestInt(0x123456ff));
-    assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE));
+    // Check that only the low eight bits of the argument are used.
+    assertEqual(0xFF000000L, CRC32Byte(0x00ff));
+    assertEqual(0xFF000000L, CRC32Byte(0x01ff));
+    assertEqual(0xFF000000L, CRC32Byte(0x0100ff));
+    assertEqual(0xFF000000L, CRC32Byte(0x010000ff));
+    assertEqual(0xFF000000L, CRC32Byte(0x0000ffff));
+    assertEqual(0xFF000000L, CRC32Byte(0x00ffffff));
+    assertEqual(0xFF000000L, CRC32Byte(0xffffffff));
+    assertEqual(0xFF000000L, CRC32Byte(0x12ff));
+    assertEqual(0xFF000000L, CRC32Byte(0x1234ff));
+    assertEqual(0xFF000000L, CRC32Byte(0x123456ff));
+    assertEqual(0xFF000000L, CRC32Byte(Integer.MAX_VALUE));
 
     // Tests for sequences
-    assertEqual(0xFF41D912L, TestInt(0, 0, 0));
-    assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000));
-    assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00));
-    assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600));
+    // Check that only the low eight bits of the values are used.
+    assertEqual(0xFF41D912L, CRC32BytesUsingUpdateInt(0, 0, 0));
+    assertEqual(0xFF41D912L,
+                CRC32BytesUsingUpdateInt(0x0100, 0x010000, 0x01000000));
+    assertEqual(0xFF41D912L,
+                CRC32BytesUsingUpdateInt(0xff00, 0xffff00, 0xffffff00));
+    assertEqual(0xFF41D912L,
+                CRC32BytesUsingUpdateInt(0x1200, 0x123400, 0x12345600));
 
-    assertEqual(0x909FB2F2L, TestInt(1, 1, 1));
-    assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001));
-    assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01));
-    assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601));
+    assertEqual(0x909FB2F2L, CRC32BytesUsingUpdateInt(1, 1, 1));
+    assertEqual(0x909FB2F2L,
+                CRC32BytesUsingUpdateInt(0x0101, 0x010001, 0x01000001));
+    assertEqual(0x909FB2F2L,
+                CRC32BytesUsingUpdateInt(0xff01, 0xffff01, 0xffffff01));
+    assertEqual(0x909FB2F2L,
+                CRC32BytesUsingUpdateInt(0x1201, 0x123401, 0x12345601));
 
-    assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f));
-    assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f));
-    assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f));
-    assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f));
+    assertEqual(0xE33A9F71L, CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f));
+    assertEqual(0xE33A9F71L,
+                CRC32BytesUsingUpdateInt(0x010f, 0x01000f, 0x0100000f));
+    assertEqual(0xE33A9F71L,
+                CRC32BytesUsingUpdateInt(0xff0f, 0xffff0f, 0xffffff0f));
+    assertEqual(0xE33A9F71L,
+                CRC32BytesUsingUpdateInt(0x120f, 0x12340f, 0x1234560f));
 
-    assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff));
-    assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff));
-    assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff));
-    assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff));
+    assertEqual(0xFFFFFF00L, CRC32BytesUsingUpdateInt(0x0ff, 0x0ff, 0x0ff));
+    assertEqual(0xFFFFFF00L,
+                CRC32BytesUsingUpdateInt(0x01ff, 0x0100ff, 0x010000ff));
+    assertEqual(0xFFFFFF00L,
+                CRC32BytesUsingUpdateInt(0x00ffff, 0x00ffffff, 0xffffffff));
+    assertEqual(0xFFFFFF00L,
+                CRC32BytesUsingUpdateInt(0x12ff, 0x1234ff, 0x123456ff));
 
-    assertEqual(0xB6CC4292L, TestInt(0x01, 0x02));
+    assertEqual(0xB6CC4292L, CRC32BytesUsingUpdateInt(0x01, 0x02));
 
-    assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+    assertEqual(0xB2DE047CL,
+                CRC32BytesUsingUpdateInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+  }
+
+  private static long CRC32ByteArray(byte[] bytes, int off, int len) {
+    CRC32 crc32 = new CRC32();
+    crc32.update(bytes, off, len);
+    return crc32.getValue();
+  }
+
+  // This is used to test we generate correct code for constant offsets.
+  // In this case the offset is 0.
+  private static long CRC32ByteArray(byte[] bytes) {
+    CRC32 crc32 = new CRC32();
+    crc32.update(bytes);
+    return crc32.getValue();
+  }
+
+  private static long CRC32ByteAndByteArray(int value, byte[] bytes) {
+    CRC32 crc32 = new CRC32();
+    crc32.update(value);
+    crc32.update(bytes);
+    return crc32.getValue();
+  }
+
+  private static long CRC32ByteArrayAndByte(byte[] bytes, int value) {
+    CRC32 crc32 = new CRC32();
+    crc32.update(bytes);
+    crc32.update(value);
+    return crc32.getValue();
+  }
+
+  private static boolean CRC32ByteArrayThrowsAIOOBE(byte[] bytes, int off, int len) {
+    try {
+      CRC32 crc32 = new CRC32();
+      crc32.update(bytes, off, len);
+    } catch (ArrayIndexOutOfBoundsException ex) {
+      return true;
+    }
+    return false;
+  }
+
+  private static boolean CRC32ByteArrayThrowsNPE() {
+    try {
+      CRC32 crc32 = new CRC32();
+      crc32.update(null, 0, 0);
+      return false;
+    } catch (NullPointerException e) {}
+
+    try {
+      CRC32 crc32 = new CRC32();
+      crc32.update(null, 1, 2);
+      return false;
+    } catch (NullPointerException e) {}
+
+    try {
+      CRC32 crc32 = new CRC32();
+      crc32.update((byte[])null);
+      return false;
+    } catch (NullPointerException e) {}
+
+    return true;
+  }
+
+  private static long CRC32BytesUsingUpdateInt(byte[] bytes, int off, int len) {
+    CRC32 crc32 = new CRC32();
+    while (len-- > 0) {
+      crc32.update(bytes[off++]);
+    }
+    return crc32.getValue();
+  }
+
+  private static void TestCRC32UpdateBytes() {
+    assertEqual(0L, CRC32ByteArray(new byte[] {}));
+    assertEqual(0L, CRC32ByteArray(new byte[] {}, 0, 0));
+    assertEqual(0L, CRC32ByteArray(new byte[] {0}, 0, 0));
+    assertEqual(0L, CRC32ByteArray(new byte[] {0}, 1, 0));
+    assertEqual(0L, CRC32ByteArray(new byte[] {0, 0}, 1, 0));
+
+    assertEqual(true, CRC32ByteArrayThrowsNPE());
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, -1, 0));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, -1, 1));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, 0, -1));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, -1));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 1, 0));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, -1, 1));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 1, -1));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, 1));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, 10));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, 0, 10));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 10, 10));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0, 0, 0, 0}, 2, 3));
+    assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0, 0, 0, 0}, 3, 2));
+
+    assertEqual(CRC32Byte(0), CRC32ByteArray(new byte[] {0}));
+    assertEqual(CRC32Byte(0), CRC32ByteArray(new byte[] {0}, 0, 1));
+    assertEqual(CRC32Byte(1), CRC32ByteArray(new byte[] {1}));
+    assertEqual(CRC32Byte(1), CRC32ByteArray(new byte[] {1}, 0, 1));
+    assertEqual(CRC32Byte(0x0f), CRC32ByteArray(new byte[] {0x0f}));
+    assertEqual(CRC32Byte(0x0f), CRC32ByteArray(new byte[] {0x0f}, 0, 1));
+    assertEqual(CRC32Byte(0xff), CRC32ByteArray(new byte[] {-1}));
+    assertEqual(CRC32Byte(0xff), CRC32ByteArray(new byte[] {-1}, 0, 1));
+    assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+                CRC32ByteArray(new byte[] {0, 0, 0}));
+    assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+                CRC32ByteArray(new byte[] {0, 0, 0}, 0, 3));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+                CRC32ByteArray(new byte[] {1, 1, 1}));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+                CRC32ByteArray(new byte[] {1, 1, 1}, 0, 3));
+    assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+                CRC32ByteArray(new byte[] {0x0f, 0x0f, 0x0f}));
+    assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+                CRC32ByteArray(new byte[] {0x0f, 0x0f, 0x0f}, 0, 3));
+    assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+                CRC32ByteArray(new byte[] {-1, -1, -1}));
+    assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+                CRC32ByteArray(new byte[] {-1, -1, -1}, 0, 3));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 2),
+                CRC32ByteArray(new byte[] {1, 2}));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 2),
+                CRC32ByteArray(new byte[] {1, 2}, 0, 2));
+    assertEqual(
+        CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+        CRC32ByteArray(new byte[] {0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE}));
+    assertEqual(
+        CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+        CRC32ByteArray(new byte[] {0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE}, 0, 4));
+
+    assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+                CRC32ByteAndByteArray(0, new byte[] {0, 0}));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+                CRC32ByteAndByteArray(1, new byte[] {1, 1}));
+    assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+                CRC32ByteAndByteArray(0x0f, new byte[] {0x0f, 0x0f}));
+    assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+                CRC32ByteAndByteArray(-1, new byte[] {-1, -1}));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 2, 3),
+                CRC32ByteAndByteArray(1, new byte[] {2, 3}));
+    assertEqual(
+        CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+        CRC32ByteAndByteArray(0, new byte[] {-1, Byte.MIN_VALUE, Byte.MAX_VALUE}));
+
+    assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+                CRC32ByteArrayAndByte(new byte[] {0, 0}, 0));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+                CRC32ByteArrayAndByte(new byte[] {1, 1}, 1));
+    assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+                CRC32ByteArrayAndByte(new byte[] {0x0f, 0x0f}, 0x0f));
+    assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+                CRC32ByteArrayAndByte(new byte[] {-1, -1}, -1));
+    assertEqual(CRC32BytesUsingUpdateInt(1, 2, 3),
+                CRC32ByteArrayAndByte(new byte[] {1, 2}, 3));
+    assertEqual(
+        CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+        CRC32ByteArrayAndByte(new byte[] {0, -1, Byte.MIN_VALUE}, Byte.MAX_VALUE));
+
+    byte[] bytes = new byte[128 * 1024];
+    Random rnd = new Random(0);
+    rnd.nextBytes(bytes);
+
+    assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, bytes.length),
+                CRC32ByteArray(bytes));
+    assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, 8 * 1024),
+                CRC32ByteArray(bytes, 0, 8 * 1024));
+
+    int off = rnd.nextInt(bytes.length / 2);
+    for (int len = 0; len <= 16; ++len) {
+      assertEqual(CRC32BytesUsingUpdateInt(bytes, off, len),
+                  CRC32ByteArray(bytes, off, len));
+    }
+
+    // Check there are no issues with unaligned accesses.
+    for (int o = 1; o < 8; ++o) {
+      for (int l = 0; l <= 16; ++l) {
+        assertEqual(CRC32BytesUsingUpdateInt(bytes, o, l),
+                    CRC32ByteArray(bytes, o, l));
+      }
+    }
+
+    int len = bytes.length / 2;
+    assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len - 1),
+                CRC32ByteArray(bytes, 0, len - 1));
+    assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len),
+                CRC32ByteArray(bytes, 0, len));
+    assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len + 1),
+                CRC32ByteArray(bytes, 0, len + 1));
+
+    len = rnd.nextInt(bytes.length + 1);
+    off = rnd.nextInt(bytes.length - len);
+    assertEqual(CRC32BytesUsingUpdateInt(bytes, off, len),
+                CRC32ByteArray(bytes, off, len));
+  }
+
+  public static void main(String args[]) {
+    TestCRC32Update();
+    TestCRC32UpdateBytes();
   }
 }
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 6305185..433c2c7 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -17,7 +17,6 @@
 package com.android.class2greylist;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSet;
@@ -35,16 +34,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.HashMap;
 import java.util.Set;
-import java.util.function.Predicate;
 
 /**
  * Build time tool for extracting a list of members from jar files that have the @UsedByApps
@@ -193,12 +186,31 @@
                 new UnsupportedAppUsageAnnotationHandler(
                     mStatus, mOutput, mPublicApis, TARGET_SDK_TO_LIST_MAP);
         GREYLIST_ANNOTATIONS.forEach(a -> builder.put(a, greylistAnnotationHandler));
+
+        CovariantReturnTypeHandler covariantReturnTypeHandler = new CovariantReturnTypeHandler(
+            mOutput, mPublicApis, FLAG_WHITELIST);
+
+        return addRepeatedAnnotationHandlers(builder, CovariantReturnTypeHandler.ANNOTATION_NAME,
+            CovariantReturnTypeHandler.REPEATED_ANNOTATION_NAME, covariantReturnTypeHandler)
+            .build();
+    }
+
+    /**
+     * Add a handler for an annotation as well as an handler for the container annotation that is
+     * used when the annotation is repeated.
+     *
+     * @param builder the builder for the map to which the handlers will be added.
+     * @param annotationName the name of the annotation.
+     * @param containerAnnotationName the name of the annotation container.
+     * @param handler the handler for the annotation.
+     */
+    private static Builder<String, AnnotationHandler> addRepeatedAnnotationHandlers(
+        Builder<String, AnnotationHandler> builder,
+        String annotationName, String containerAnnotationName,
+        AnnotationHandler handler) {
         return builder
-                .put(CovariantReturnTypeHandler.ANNOTATION_NAME,
-                        new CovariantReturnTypeHandler(mOutput, mPublicApis, FLAG_WHITELIST))
-                .put(CovariantReturnTypeMultiHandler.ANNOTATION_NAME,
-                        new CovariantReturnTypeMultiHandler(mOutput, mPublicApis, FLAG_WHITELIST))
-                .build();
+            .put(annotationName, handler)
+            .put(containerAnnotationName, new RepeatedAnnotationHandler(annotationName, handler));
     }
 
     private void main() throws IOException {
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
index b8de7e9..64d8997 100644
--- a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
+++ b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
@@ -30,6 +30,8 @@
 
     private static final String SHORT_NAME = "CovariantReturnType";
     public static final String ANNOTATION_NAME = "Ldalvik/annotation/codegen/CovariantReturnType;";
+    public static final String REPEATED_ANNOTATION_NAME =
+        "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;";
 
     private static final String RETURN_TYPE = "returnType";
 
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java
deleted file mode 100644
index f2bc525..0000000
--- a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.android.class2greylist;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import org.apache.bcel.classfile.AnnotationElementValue;
-import org.apache.bcel.classfile.AnnotationEntry;
-import org.apache.bcel.classfile.ArrayElementValue;
-import org.apache.bcel.classfile.ElementValue;
-import org.apache.bcel.classfile.ElementValuePair;
-
-import java.util.Set;
-
-/**
- * Handles {@code CovariantReturnType$CovariantReturnTypes} annotations, which
- * are generated by the compiler when multiple {@code CovariantReturnType}
- * annotations appear on a single method.
- *
- * <p>The enclosed annotations are passed to {@link CovariantReturnTypeHandler}.
- */
-public class CovariantReturnTypeMultiHandler extends AnnotationHandler {
-
-    public static final String ANNOTATION_NAME =
-            "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;";
-
-    private static final String VALUE = "value";
-
-    private final CovariantReturnTypeHandler mWrappedHandler;
-    private final String mInnerAnnotationName;
-
-    public CovariantReturnTypeMultiHandler(AnnotationConsumer consumer, Set<String> publicApis,
-            String hiddenapiFlag) {
-        this(consumer, publicApis, hiddenapiFlag, CovariantReturnTypeHandler.ANNOTATION_NAME);
-    }
-
-    @VisibleForTesting
-    public CovariantReturnTypeMultiHandler(AnnotationConsumer consumer, Set<String> publicApis,
-            String hiddenapiFlag, String innerAnnotationName) {
-        mWrappedHandler = new CovariantReturnTypeHandler(consumer, publicApis, hiddenapiFlag);
-        mInnerAnnotationName = innerAnnotationName;
-    }
-
-    @Override
-    public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
-        // Verify that the annotation has the form we expect
-        ElementValuePair value = findValue(annotation);
-        if (value == null) {
-            context.reportError("No value found on CovariantReturnType$CovariantReturnTypes");
-            return;
-        }
-        Preconditions.checkArgument(value.getValue() instanceof ArrayElementValue);
-        ArrayElementValue array = (ArrayElementValue) value.getValue();
-
-        // call wrapped handler on each enclosed annotation:
-        for (ElementValue v : array.getElementValuesArray()) {
-            Preconditions.checkArgument(v instanceof AnnotationElementValue);
-            AnnotationElementValue aev = (AnnotationElementValue) v;
-            Preconditions.checkArgument(
-                    aev.getAnnotationEntry().getAnnotationType().equals(mInnerAnnotationName));
-            mWrappedHandler.handleAnnotation(aev.getAnnotationEntry(), context);
-        }
-    }
-
-    private ElementValuePair findValue(AnnotationEntry a) {
-        for (ElementValuePair property : a.getElementValuePairs()) {
-            if (property.getNameString().equals(VALUE)) {
-                return property;
-            }
-        }
-        // not found
-        return null;
-    }
-}
diff --git a/tools/class2greylist/src/com/android/class2greylist/RepeatedAnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/RepeatedAnnotationHandler.java
new file mode 100644
index 0000000..61949e3
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/RepeatedAnnotationHandler.java
@@ -0,0 +1,57 @@
+package com.android.class2greylist;
+
+import com.google.common.base.Preconditions;
+import org.apache.bcel.classfile.AnnotationElementValue;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ArrayElementValue;
+import org.apache.bcel.classfile.ElementValue;
+import org.apache.bcel.classfile.ElementValuePair;
+
+/**
+ * Handles a repeated annotation container.
+ *
+ * <p>The enclosed annotations are passed to the {@link #mWrappedHandler}.
+ */
+public class RepeatedAnnotationHandler extends AnnotationHandler {
+
+    private static final String VALUE = "value";
+
+    private final AnnotationHandler mWrappedHandler;
+    private final String mInnerAnnotationName;
+
+    RepeatedAnnotationHandler(String innerAnnotationName, AnnotationHandler wrappedHandler) {
+        mWrappedHandler = wrappedHandler;
+        mInnerAnnotationName = innerAnnotationName;
+    }
+
+    @Override
+    public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+        // Verify that the annotation has the form we expect
+        ElementValuePair value = findValue(annotation);
+        if (value == null) {
+            context.reportError("No value found on %s", annotation.getAnnotationType());
+            return;
+        }
+        Preconditions.checkArgument(value.getValue() instanceof ArrayElementValue);
+        ArrayElementValue array = (ArrayElementValue) value.getValue();
+
+        // call wrapped handler on each enclosed annotation:
+        for (ElementValue v : array.getElementValuesArray()) {
+            Preconditions.checkArgument(v instanceof AnnotationElementValue);
+            AnnotationElementValue aev = (AnnotationElementValue) v;
+            Preconditions.checkArgument(
+                    aev.getAnnotationEntry().getAnnotationType().equals(mInnerAnnotationName));
+            mWrappedHandler.handleAnnotation(aev.getAnnotationEntry(), context);
+        }
+    }
+
+    private ElementValuePair findValue(AnnotationEntry a) {
+        for (ElementValuePair property : a.getElementValuePairs()) {
+            if (property.getNameString().equals(VALUE)) {
+                return property;
+            }
+        }
+        // not found
+        return null;
+    }
+}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
deleted file mode 100644
index 25f2844..0000000
--- a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.class2greylist;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import static java.util.Collections.emptySet;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.io.IOException;
-import java.util.Map;
-
-public class CovariantReturnTypeMultiHandlerTest extends AnnotationHandlerTestBase {
-
-    private static final String FLAG = "test-flag";
-
-    @Before
-    public void setup() throws IOException {
-        // To keep the test simpler and more concise, we don't use the real
-        // @CovariantReturnType annotation here, but use our own @Annotation
-        // and @Annotation.Multi that have the same semantics. It doesn't have
-        // to match the real annotation, just have the same properties
-        // (returnType and value).
-        mJavac.addSource("annotation.Annotation", Joiner.on('\n').join(
-                "package annotation;",
-                "import static java.lang.annotation.RetentionPolicy.CLASS;",
-                "import java.lang.annotation.Repeatable;",
-                "import java.lang.annotation.Retention;",
-                "@Repeatable(Annotation.Multi.class)",
-                "@Retention(CLASS)",
-                "public @interface Annotation {",
-                "  Class<?> returnType();",
-                "  @Retention(CLASS)",
-                "  @interface Multi {",
-                "    Annotation[] value();",
-                "  }",
-                "}"));
-    }
-
-    @Test
-    public void testReturnTypeMulti() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Annotation;",
-                "public class Class {",
-                "  @Annotation(returnType=Integer.class)",
-                "  @Annotation(returnType=Long.class)",
-                "  public String method() {return null;}",
-                "}"));
-        mJavac.compile();
-
-        Map<String, AnnotationHandler> handlerMap =
-                ImmutableMap.of("Lannotation/Annotation$Multi;",
-                        new CovariantReturnTypeMultiHandler(
-                                mConsumer,
-                                ImmutableSet.of("La/b/Class;->method()Ljava/lang/String;"),
-                                FLAG,
-                                "Lannotation/Annotation;"));
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
-
-        assertNoErrors();
-        ArgumentCaptor<String> whitelist = ArgumentCaptor.forClass(String.class);
-        verify(mConsumer, times(2)).consume(whitelist.capture(), any(),
-                eq(ImmutableSet.of(FLAG)));
-        assertThat(whitelist.getAllValues()).containsExactly(
-                "La/b/Class;->method()Ljava/lang/Integer;",
-                "La/b/Class;->method()Ljava/lang/Long;");
-    }
-
-    @Test
-    public void testReturnTypeMultiNotPublicApi() throws IOException {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import annotation.Annotation;",
-                "public class Class {",
-                "  @Annotation(returnType=Integer.class)",
-                "  @Annotation(returnType=Long.class)",
-                "  public String method() {return null;}",
-                "}"));
-        mJavac.compile();
-
-        Map<String, AnnotationHandler> handlerMap =
-                ImmutableMap.of("Lannotation/Annotation$Multi;",
-                        new CovariantReturnTypeMultiHandler(
-                                mConsumer,
-                                emptySet(),
-                                FLAG,
-                                "Lannotation/Annotation;"));
-        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
-
-        verify(mStatus, atLeastOnce()).error(any(), any());
-    }
-}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/RepeatedAnnotationHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/RepeatedAnnotationHandlerTest.java
new file mode 100644
index 0000000..f2f70ee
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/RepeatedAnnotationHandlerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.class2greylist;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RepeatedAnnotationHandlerTest extends AnnotationHandlerTestBase {
+
+    @Before
+    public void setup() {
+        // To keep the test simpler and more concise, we don't use a real annotation here, but use
+        // our own @Annotation and @Annotation.Multi that have the same relationship.
+        mJavac.addSource("annotation.Annotation", Joiner.on('\n').join(
+                "package annotation;",
+                "import static java.lang.annotation.RetentionPolicy.CLASS;",
+                "import java.lang.annotation.Repeatable;",
+                "import java.lang.annotation.Retention;",
+                "@Repeatable(Annotation.Multi.class)",
+                "@Retention(CLASS)",
+                "public @interface Annotation {",
+                "  Class<?> clazz();",
+                "  @Retention(CLASS)",
+                "  @interface Multi {",
+                "    Annotation[] value();",
+                "  }",
+                "}"));
+    }
+
+    @Test
+    public void testRepeated() throws IOException {
+        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+                "package a.b;",
+                "import annotation.Annotation;",
+                "public class Class {",
+                "  @Annotation(clazz=Integer.class)",
+                "  @Annotation(clazz=Long.class)",
+                "  public String method() {return null;}",
+                "}"));
+        mJavac.compile();
+
+        TestAnnotationHandler handler = new TestAnnotationHandler();
+        Map<String, AnnotationHandler> handlerMap =
+            ImmutableMap.of("Lannotation/Annotation$Multi;",
+                new RepeatedAnnotationHandler("Lannotation/Annotation;", handler));
+        new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+        assertNoErrors();
+        assertThat(handler.getClasses()).containsExactly(
+                "Ljava/lang/Integer;",
+                "Ljava/lang/Long;");
+    }
+
+    private static class TestAnnotationHandler extends AnnotationHandler {
+
+        private final List<String> classes;
+
+        private TestAnnotationHandler() {
+            this.classes = new ArrayList<>();
+        }
+
+        @Override
+        void handleAnnotation(AnnotationEntry annotation,
+            AnnotationContext context) {
+            classes.add(annotation.getElementValuePairs()[0].getValue().stringifyValue());
+        }
+
+        private List<String> getClasses() {
+            return classes;
+        }
+    }
+}