Merge "Don't crash if the perf map creation failed"
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index d68835a..af6f91f 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -1076,7 +1076,7 @@
       pc_rel_temp_(nullptr),
       dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()),
       cfi_(&last_lir_insn_,
-           cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(),
+           cu->compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo(),
            arena),
       in_to_reg_storage_mapping_(arena) {
   switch_tables_.reserve(4);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index c5df134..0cd41bb 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -71,6 +71,7 @@
       nullptr,
       false,
       "",
+      false,
       false);
     VerificationResults verification_results(&compiler_options);
     DexFileToMethodInlinerMap method_inliner_map;
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index d63878d..efdc333 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -54,6 +54,7 @@
         nullptr,
         false,
         "",
+        false,
         false));
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
     method_inliner_map_.reset(new DexFileToMethodInlinerMap());
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 818d50a..f1b7458 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -39,6 +39,7 @@
 #include "compiler_driver-inl.h"
 #include "dex_compilation_unit.h"
 #include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
 #include "dex/dex_to_dex_compiler.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
@@ -365,7 +366,7 @@
       classes_to_compile_(compiled_classes),
       methods_to_compile_(compiled_methods),
       had_hard_verifier_failure_(false),
-      thread_count_(thread_count),
+      parallel_thread_count_(thread_count),
       stats_(new AOTCompilationStats),
       dump_stats_(dump_stats),
       dump_passes_(dump_passes),
@@ -435,24 +436,27 @@
                                 const std::vector<const DexFile*>& dex_files,
                                 TimingLogger* timings) {
   DCHECK(!Runtime::Current()->IsStarted());
-  std::unique_ptr<ThreadPool> thread_pool(
-      new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
+
+  InitializeThreadPools();
+
   VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
   // Precompile:
   // 1) Load image classes
   // 2) Resolve all classes
   // 3) Attempt to verify all classes
   // 4) Attempt to initialize image classes, and trivially initialized classes
-  PreCompile(class_loader, dex_files, thread_pool.get(), timings);
+  PreCompile(class_loader, dex_files, timings);
   // Compile:
   // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
   //    compilation.
   if (!GetCompilerOptions().VerifyAtRuntime()) {
-    Compile(class_loader, dex_files, thread_pool.get(), timings);
+    Compile(class_loader, dex_files, timings);
   }
   if (dump_stats_) {
     stats_->Dump();
   }
+
+  FreeThreadPools();
 }
 
 static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
@@ -653,8 +657,9 @@
   std::vector<const DexFile*> dex_files;
   dex_files.push_back(dex_file);
 
-  std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", 0U));
-  PreCompile(jclass_loader, dex_files, thread_pool.get(), timings);
+  InitializeThreadPools();
+
+  PreCompile(jclass_loader, dex_files, timings);
 
   // Can we run DEX-to-DEX compiler on this class ?
   optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -677,20 +682,147 @@
                 true,
                 dex_cache);
 
+  FreeThreadPools();
+
   self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
 }
 
-void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                             ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Resolve(jobject class_loader,
+                             const std::vector<const DexFile*>& dex_files,
+                             TimingLogger* timings) {
+  // Resolution allocates classes and needs to run single-threaded to be deterministic.
+  bool force_determinism = GetCompilerOptions().IsForceDeterminism();
+  ThreadPool* resolve_thread_pool = force_determinism
+                                     ? single_thread_pool_.get()
+                                     : parallel_thread_pool_.get();
+  size_t resolve_thread_count = force_determinism ? 1U : parallel_thread_count_;
+
   for (size_t i = 0; i != dex_files.size(); ++i) {
     const DexFile* dex_file = dex_files[i];
     CHECK(dex_file != nullptr);
-    ResolveDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+    ResolveDexFile(class_loader,
+                   *dex_file,
+                   dex_files,
+                   resolve_thread_pool,
+                   resolve_thread_count,
+                   timings);
   }
 }
 
-void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                                ThreadPool* thread_pool, TimingLogger* timings) {
+// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now
+// this is single-threaded for simplicity.
+// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
+//       stable order.
+
+static void ResolveConstStrings(CompilerDriver* driver,
+                                const DexFile& dex_file,
+                                const DexFile::CodeItem* code_item) {
+  if (code_item == nullptr) {
+    // Abstract or native method.
+    return;
+  }
+
+  const uint16_t* code_ptr = code_item->insns_;
+  const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
+
+  while (code_ptr < code_end) {
+    const Instruction* inst = Instruction::At(code_ptr);
+    switch (inst->Opcode()) {
+      case Instruction::CONST_STRING: {
+        uint32_t string_index = inst->VRegB_21c();
+        driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+        break;
+      }
+      case Instruction::CONST_STRING_JUMBO: {
+        uint32_t string_index = inst->VRegB_31c();
+        driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+        break;
+      }
+
+      default:
+        break;
+    }
+
+    code_ptr += inst->SizeInCodeUnits();
+  }
+}
+
+static void ResolveConstStrings(CompilerDriver* driver,
+                                const std::vector<const DexFile*>& dex_files,
+                                TimingLogger* timings) {
+  for (const DexFile* dex_file : dex_files) {
+    TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
+
+    size_t class_def_count = dex_file->NumClassDefs();
+    for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+
+      const uint8_t* class_data = dex_file->GetClassData(class_def);
+      if (class_data == nullptr) {
+        // empty class, probably a marker interface
+        continue;
+      }
+
+      ClassDataItemIterator it(*dex_file, class_data);
+      // Skip fields
+      while (it.HasNextStaticField()) {
+        it.Next();
+      }
+      while (it.HasNextInstanceField()) {
+        it.Next();
+      }
+
+      bool compilation_enabled = driver->IsClassToCompile(
+          dex_file->StringByTypeIdx(class_def.class_idx_));
+      if (!compilation_enabled) {
+        // Compilation is skipped, do not resolve const-string in code of this class.
+        // TODO: Make sure that inlining honors this.
+        continue;
+      }
+
+      // Direct methods.
+      int64_t previous_direct_method_idx = -1;
+      while (it.HasNextDirectMethod()) {
+        uint32_t method_idx = it.GetMemberIndex();
+        if (method_idx == previous_direct_method_idx) {
+          // smali can create dex files with two encoded_methods sharing the same method_idx
+          // http://code.google.com/p/smali/issues/detail?id=119
+          it.Next();
+          continue;
+        }
+        previous_direct_method_idx = method_idx;
+        ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+        it.Next();
+      }
+      // Virtual methods.
+      int64_t previous_virtual_method_idx = -1;
+      while (it.HasNextVirtualMethod()) {
+        uint32_t method_idx = it.GetMemberIndex();
+        if (method_idx == previous_virtual_method_idx) {
+          // smali can create dex files with two encoded_methods sharing the same method_idx
+          // http://code.google.com/p/smali/issues/detail?id=119
+          it.Next();
+          continue;
+        }
+        previous_virtual_method_idx = method_idx;
+        ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
+    }
+  }
+}
+
+inline void CompilerDriver::CheckThreadPools() {
+  DCHECK(parallel_thread_pool_ != nullptr);
+  DCHECK(single_thread_pool_ != nullptr);
+}
+
+void CompilerDriver::PreCompile(jobject class_loader,
+                                const std::vector<const DexFile*>& dex_files,
+                                TimingLogger* timings) {
+  CheckThreadPools();
+
   LoadImageClasses(timings);
   VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
@@ -700,20 +832,26 @@
   // We need to resolve for never_verify since it needs to run dex to dex to add the
   // RETURN_VOID_NO_BARRIER.
   if (never_verify || verification_enabled) {
-    Resolve(class_loader, dex_files, thread_pool, timings);
+    Resolve(class_loader, dex_files, timings);
     VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
   }
 
   if (never_verify) {
     VLOG(compiler) << "Verify none mode specified, skipping verification.";
-    SetVerified(class_loader, dex_files, thread_pool, timings);
+    SetVerified(class_loader, dex_files, timings);
   }
 
   if (!verification_enabled) {
     return;
   }
 
-  Verify(class_loader, dex_files, thread_pool, timings);
+  if (GetCompilerOptions().IsForceDeterminism() && IsBootImage()) {
+    // Resolve strings from const-string. Do this now to have a deterministic image.
+    ResolveConstStrings(this, dex_files, timings);
+    VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
+  }
+
+  Verify(class_loader, dex_files, timings);
   VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
 
   if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -721,7 +859,7 @@
                << "situations. Please check the log.";
   }
 
-  InitializeClasses(class_loader, dex_files, thread_pool, timings);
+  InitializeClasses(class_loader, dex_files, timings);
   VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
 
   UpdateImageClasses(timings);
@@ -1759,6 +1897,9 @@
 
     // Wait for all the worker threads to finish.
     thread_pool_->Wait(self, true, false);
+
+    // And stop the workers accepting jobs.
+    thread_pool_->StopWorkers(self);
   }
 
   size_t NextIndex() {
@@ -1995,9 +2136,12 @@
   const ParallelCompilationManager* const manager_;
 };
 
-void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::ResolveDexFile(jobject class_loader,
+                                    const DexFile& dex_file,
                                     const std::vector<const DexFile*>& dex_files,
-                                    ThreadPool* thread_pool, TimingLogger* timings) {
+                                    ThreadPool* thread_pool,
+                                    size_t thread_count,
+                                    TimingLogger* timings) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
   // TODO: we could resolve strings here, although the string table is largely filled with class
@@ -2010,27 +2154,43 @@
     // classdefs are resolved by ResolveClassFieldsAndMethods.
     TimingLogger::ScopedTiming t("Resolve Types", timings);
     ResolveTypeVisitor visitor(&context);
-    context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count_);
+    context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count);
   }
 
   TimingLogger::ScopedTiming t("Resolve MethodsAndFields", timings);
   ResolveClassFieldsAndMethodsVisitor visitor(&context);
-  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
 }
 
-void CompilerDriver::SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                                 ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::SetVerified(jobject class_loader,
+                                 const std::vector<const DexFile*>& dex_files,
+                                 TimingLogger* timings) {
+  // This can be run in parallel.
   for (const DexFile* dex_file : dex_files) {
     CHECK(dex_file != nullptr);
-    SetVerifiedDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+    SetVerifiedDexFile(class_loader,
+                       *dex_file,
+                       dex_files,
+                       parallel_thread_pool_.get(),
+                       parallel_thread_count_,
+                       timings);
   }
 }
 
-void CompilerDriver::Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                            ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Verify(jobject class_loader,
+                            const std::vector<const DexFile*>& dex_files,
+                            TimingLogger* timings) {
+  // Note: verification should not be pulling in classes anymore when compiling the boot image,
+  //       as all should have been resolved before. As such, doing this in parallel should still
+  //       be deterministic.
   for (const DexFile* dex_file : dex_files) {
     CHECK(dex_file != nullptr);
-    VerifyDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+    VerifyDexFile(class_loader,
+                  *dex_file,
+                  dex_files,
+                  parallel_thread_pool_.get(),
+                  parallel_thread_count_,
+                  timings);
   }
 }
 
@@ -2104,15 +2264,18 @@
   const ParallelCompilationManager* const manager_;
 };
 
-void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::VerifyDexFile(jobject class_loader,
+                                   const DexFile& dex_file,
                                    const std::vector<const DexFile*>& dex_files,
-                                   ThreadPool* thread_pool, TimingLogger* timings) {
+                                   ThreadPool* thread_pool,
+                                   size_t thread_count,
+                                   TimingLogger* timings) {
   TimingLogger::ScopedTiming t("Verify Dex File", timings);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
                                      thread_pool);
   VerifyClassVisitor visitor(&context);
-  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
 }
 
 class SetVerifiedClassVisitor : public CompilationVisitor {
@@ -2162,15 +2325,18 @@
   const ParallelCompilationManager* const manager_;
 };
 
-void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::SetVerifiedDexFile(jobject class_loader,
+                                        const DexFile& dex_file,
                                         const std::vector<const DexFile*>& dex_files,
-                                        ThreadPool* thread_pool, TimingLogger* timings) {
+                                        ThreadPool* thread_pool,
+                                        size_t thread_count,
+                                        TimingLogger* timings) {
   TimingLogger::ScopedTiming t("Verify Dex File", timings);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
                                      thread_pool);
   SetVerifiedClassVisitor visitor(&context);
-  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
 }
 
 class InitializeClassVisitor : public CompilationVisitor {
@@ -2271,31 +2437,37 @@
   const ParallelCompilationManager* const manager_;
 };
 
-void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
+void CompilerDriver::InitializeClasses(jobject jni_class_loader,
+                                       const DexFile& dex_file,
                                        const std::vector<const DexFile*>& dex_files,
-                                       ThreadPool* thread_pool, TimingLogger* timings) {
+                                       TimingLogger* timings) {
   TimingLogger::ScopedTiming t("InitializeNoClinit", timings);
+
+  // Initialization allocates objects and needs to run single-threaded to be deterministic.
+  bool force_determinism = GetCompilerOptions().IsForceDeterminism();
+  ThreadPool* init_thread_pool = force_determinism
+                                     ? single_thread_pool_.get()
+                                     : parallel_thread_pool_.get();
+  size_t init_thread_count = force_determinism ? 1U : parallel_thread_count_;
+
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, dex_files,
-                                     thread_pool);
-  size_t thread_count;
+                                     init_thread_pool);
   if (IsBootImage()) {
     // TODO: remove this when transactional mode supports multithreading.
-    thread_count = 1U;
-  } else {
-    thread_count = thread_count_;
+    init_thread_count = 1U;
   }
   InitializeClassVisitor visitor(&context);
-  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
+  context.ForAll(0, dex_file.NumClassDefs(), &visitor, init_thread_count);
 }
 
 void CompilerDriver::InitializeClasses(jobject class_loader,
                                        const std::vector<const DexFile*>& dex_files,
-                                       ThreadPool* thread_pool, TimingLogger* timings) {
+                                       TimingLogger* timings) {
   for (size_t i = 0; i != dex_files.size(); ++i) {
     const DexFile* dex_file = dex_files[i];
     CHECK(dex_file != nullptr);
-    InitializeClasses(class_loader, *dex_file, dex_files, thread_pool, timings);
+    InitializeClasses(class_loader, *dex_file, dex_files, timings);
   }
   if (IsBootImage()) {
     // Prune garbage objects created during aborted transactions.
@@ -2303,8 +2475,9 @@
   }
 }
 
-void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                             ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Compile(jobject class_loader,
+                             const std::vector<const DexFile*>& dex_files,
+                             TimingLogger* timings) {
   if (kDebugProfileGuidedCompilation) {
     LOG(INFO) << "[ProfileGuidedCompilation] " <<
         ((profile_compilation_info_ == nullptr)
@@ -2314,7 +2487,12 @@
   for (size_t i = 0; i != dex_files.size(); ++i) {
     const DexFile* dex_file = dex_files[i];
     CHECK(dex_file != nullptr);
-    CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+    CompileDexFile(class_loader,
+                   *dex_file,
+                   dex_files,
+                   parallel_thread_pool_.get(),
+                   parallel_thread_count_,
+                   timings);
   }
   VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
 }
@@ -2421,14 +2599,17 @@
   const ParallelCompilationManager* const manager_;
 };
 
-void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::CompileDexFile(jobject class_loader,
+                                    const DexFile& dex_file,
                                     const std::vector<const DexFile*>& dex_files,
-                                    ThreadPool* thread_pool, TimingLogger* timings) {
+                                    ThreadPool* thread_pool,
+                                    size_t thread_count,
+                                    TimingLogger* timings) {
   TimingLogger::ScopedTiming t("Compile Dex File", timings);
   ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
                                      &dex_file, dex_files, thread_pool);
   CompileClassVisitor visitor(&context);
-  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
 }
 
 void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref,
@@ -2582,11 +2763,24 @@
                                        const DexFile* inlined_into) const {
   // We're not allowed to inline across dex files if we're the no-inline-from dex file.
   if (inlined_from != inlined_into &&
-      compiler_options_->GetNoInlineFromDexFile() == inlined_from) {
+      compiler_options_->GetNoInlineFromDexFile() != nullptr &&
+      ContainsElement(*compiler_options_->GetNoInlineFromDexFile(), inlined_from)) {
     return false;
   }
 
   return true;
 }
 
+void CompilerDriver::InitializeThreadPools() {
+  size_t parallel_count = parallel_thread_count_ > 0 ? parallel_thread_count_ - 1 : 0;
+  parallel_thread_pool_.reset(
+      new ThreadPool("Compiler driver thread pool", parallel_count));
+  single_thread_pool_.reset(new ThreadPool("Single-threaded Compiler driver thread pool", 0));
+}
+
+void CompilerDriver::FreeThreadPools() {
+  parallel_thread_pool_.reset();
+  single_thread_pool_.reset();
+}
+
 }  // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 6a2f7bf..5e35cbb 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -411,7 +411,7 @@
   }
 
   size_t GetThreadCount() const {
-    return thread_count_;
+    return parallel_thread_count_;
   }
 
   bool GetDumpStats() const {
@@ -550,8 +550,9 @@
       SHARED_REQUIRES(Locks::mutator_lock_);
 
  private:
-  void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                  ThreadPool* thread_pool, TimingLogger* timings)
+  void PreCompile(jobject class_loader,
+                  const std::vector<const DexFile*>& dex_files,
+                  TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
 
   void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
@@ -559,49 +560,71 @@
   // Attempt to resolve all type, methods, fields, and strings
   // referenced from code in the dex file following PathClassLoader
   // ordering semantics.
-  void Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-               ThreadPool* thread_pool, TimingLogger* timings)
+  void Resolve(jobject class_loader,
+               const std::vector<const DexFile*>& dex_files,
+               TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
-  void ResolveDexFile(jobject class_loader, const DexFile& dex_file,
+  void ResolveDexFile(jobject class_loader,
+                      const DexFile& dex_file,
                       const std::vector<const DexFile*>& dex_files,
-                      ThreadPool* thread_pool, TimingLogger* timings)
+                      ThreadPool* thread_pool,
+                      size_t thread_count,
+                      TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
-  void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-              ThreadPool* thread_pool, TimingLogger* timings);
-  void VerifyDexFile(jobject class_loader, const DexFile& dex_file,
+  void Verify(jobject class_loader,
+              const std::vector<const DexFile*>& dex_files,
+              TimingLogger* timings);
+  void VerifyDexFile(jobject class_loader,
+                     const DexFile& dex_file,
                      const std::vector<const DexFile*>& dex_files,
-                     ThreadPool* thread_pool, TimingLogger* timings)
+                     ThreadPool* thread_pool,
+                     size_t thread_count,
+                     TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
-  void SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                   ThreadPool* thread_pool, TimingLogger* timings);
-  void SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
+  void SetVerified(jobject class_loader,
+                   const std::vector<const DexFile*>& dex_files,
+                   TimingLogger* timings);
+  void SetVerifiedDexFile(jobject class_loader,
+                          const DexFile& dex_file,
                           const std::vector<const DexFile*>& dex_files,
-                          ThreadPool* thread_pool, TimingLogger* timings)
+                          ThreadPool* thread_pool,
+                          size_t thread_count,
+                          TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
-  void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-                         ThreadPool* thread_pool, TimingLogger* timings)
-      REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
-  void InitializeClasses(jobject class_loader, const DexFile& dex_file,
+  void InitializeClasses(jobject class_loader,
                          const std::vector<const DexFile*>& dex_files,
-                         ThreadPool* thread_pool, TimingLogger* timings)
+                         TimingLogger* timings)
+      REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
+  void InitializeClasses(jobject class_loader,
+                         const DexFile& dex_file,
+                         const std::vector<const DexFile*>& dex_files,
+                         TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
 
   void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
   static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
-               ThreadPool* thread_pool, TimingLogger* timings);
-  void CompileDexFile(jobject class_loader, const DexFile& dex_file,
+  void Compile(jobject class_loader,
+               const std::vector<const DexFile*>& dex_files,
+               TimingLogger* timings);
+  void CompileDexFile(jobject class_loader,
+                      const DexFile& dex_file,
                       const std::vector<const DexFile*>& dex_files,
-                      ThreadPool* thread_pool, TimingLogger* timings)
+                      ThreadPool* thread_pool,
+                      size_t thread_count,
+                      TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
   bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const;
 
+  void InitializeThreadPools();
+  void FreeThreadPools();
+  void CheckThreadPools();
+
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
   DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -652,7 +675,12 @@
 
   bool had_hard_verifier_failure_;
 
-  size_t thread_count_;
+  // A thread pool that can (potentially) run tasks in parallel.
+  std::unique_ptr<ThreadPool> parallel_thread_pool_;
+  size_t parallel_thread_count_;
+
+  // A thread pool that guarantees running single-threaded on the main thread.
+  std::unique_ptr<ThreadPool> single_thread_pool_;
 
   class AOTCompilationStats;
   std::unique_ptr<AOTCompilationStats> stats_;
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 2644528..3bf8921 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -37,6 +37,7 @@
       debuggable_(false),
       native_debuggable_(kDefaultNativeDebuggable),
       generate_debug_info_(kDefaultGenerateDebugInfo),
+      generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo),
       implicit_null_checks_(true),
       implicit_so_checks_(true),
       implicit_suspend_checks_(false),
@@ -46,7 +47,8 @@
       abort_on_hard_verifier_failure_(false),
       init_failure_output_(nullptr),
       dump_cfg_file_name_(""),
-      dump_cfg_append_(false) {
+      dump_cfg_append_(false),
+      force_determinism_(false) {
 }
 
 CompilerOptions::~CompilerOptions() {
@@ -62,7 +64,7 @@
                                  size_t num_dex_methods_threshold,
                                  size_t inline_depth_limit,
                                  size_t inline_max_code_units,
-                                 const DexFile* no_inline_from,
+                                 const std::vector<const DexFile*>* no_inline_from,
                                  bool include_patch_information,
                                  double top_k_profile_threshold,
                                  bool debuggable,
@@ -75,7 +77,8 @@
                                  std::ostream* init_failure_output,
                                  bool abort_on_hard_verifier_failure,
                                  const std::string& dump_cfg_file_name,
-                                 bool dump_cfg_append
+                                 bool dump_cfg_append,
+                                 bool force_determinism
                                  ) :  // NOLINT(whitespace/parens)
     compiler_filter_(compiler_filter),
     huge_method_threshold_(huge_method_threshold),
@@ -91,6 +94,7 @@
     debuggable_(debuggable),
     native_debuggable_(kDefaultNativeDebuggable),
     generate_debug_info_(generate_debug_info),
+    generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo),
     implicit_null_checks_(implicit_null_checks),
     implicit_so_checks_(implicit_so_checks),
     implicit_suspend_checks_(implicit_suspend_checks),
@@ -100,7 +104,8 @@
     abort_on_hard_verifier_failure_(abort_on_hard_verifier_failure),
     init_failure_output_(init_failure_output),
     dump_cfg_file_name_(dump_cfg_file_name),
-    dump_cfg_append_(dump_cfg_append) {
+    dump_cfg_append_(dump_cfg_append),
+    force_determinism_(force_determinism) {
 }
 
 void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
@@ -215,6 +220,10 @@
     generate_debug_info_ = true;
   } else if (option == "--no-generate-debug-info") {
     generate_debug_info_ = false;
+  } else if (option == "--generate-mini-debug-info") {
+    generate_mini_debug_info_ = true;
+  } else if (option == "--no-generate-mini-debug-info") {
+    generate_mini_debug_info_ = false;
   } else if (option == "--debuggable") {
     debuggable_ = true;
   } else if (option == "--native-debuggable") {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index d47fc2a..39372b3 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -51,6 +51,7 @@
   static constexpr double kDefaultTopKProfileThreshold = 90.0;
   static const bool kDefaultNativeDebuggable = false;
   static const bool kDefaultGenerateDebugInfo = false;
+  static const bool kDefaultGenerateMiniDebugInfo = false;
   static const bool kDefaultIncludePatchInformation = false;
   static const size_t kDefaultInlineDepthLimit = 3;
   static const size_t kDefaultInlineMaxCodeUnits = 32;
@@ -72,7 +73,7 @@
                   size_t num_dex_methods_threshold,
                   size_t inline_depth_limit,
                   size_t inline_max_code_units,
-                  const DexFile* no_inline_from,
+                  const std::vector<const DexFile*>* no_inline_from,
                   bool include_patch_information,
                   double top_k_profile_threshold,
                   bool debuggable,
@@ -85,7 +86,8 @@
                   std::ostream* init_failure_output,
                   bool abort_on_hard_verifier_failure,
                   const std::string& dump_cfg_file_name,
-                  bool dump_cfg_append);
+                  bool dump_cfg_append,
+                  bool force_determinism);
 
   CompilerFilter GetCompilerFilter() const {
     return compiler_filter_;
@@ -170,10 +172,20 @@
     return native_debuggable_;
   }
 
+  // This flag controls whether the compiler collects debugging information.
+  // The other flags control how the information is written to disk.
+  bool GenerateAnyDebugInfo() const {
+    return GetGenerateDebugInfo() || GetGenerateMiniDebugInfo();
+  }
+
   bool GetGenerateDebugInfo() const {
     return generate_debug_info_;
   }
 
+  bool GetGenerateMiniDebugInfo() const {
+    return generate_mini_debug_info_;
+  }
+
   bool GetImplicitNullChecks() const {
     return implicit_null_checks_;
   }
@@ -220,7 +232,7 @@
     return abort_on_hard_verifier_failure_;
   }
 
-  const DexFile* GetNoInlineFromDexFile() const {
+  const std::vector<const DexFile*>* GetNoInlineFromDexFile() const {
     return no_inline_from_;
   }
 
@@ -234,6 +246,10 @@
     return dump_cfg_append_;
   }
 
+  bool IsForceDeterminism() const {
+    return force_determinism_;
+  }
+
  private:
   void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
   void ParsePassOptions(const StringPiece& option, UsageFn Usage);
@@ -257,8 +273,10 @@
   size_t inline_depth_limit_;
   size_t inline_max_code_units_;
 
-  // A dex file from which we should not inline code.
-  const DexFile* no_inline_from_;
+  // Dex files from which we should not inline code.
+  // This is usually a very short list (i.e. a single dex file), so we
+  // prefer vector<> over a lookup-oriented container, such as set<>.
+  const std::vector<const DexFile*>* no_inline_from_;
 
   bool include_patch_information_;
   // When using a profile file only the top K% of the profiled samples will be compiled.
@@ -266,6 +284,7 @@
   bool debuggable_;
   bool native_debuggable_;
   bool generate_debug_info_;
+  bool generate_mini_debug_info_;
   bool implicit_null_checks_;
   bool implicit_so_checks_;
   bool implicit_suspend_checks_;
@@ -286,6 +305,10 @@
   std::string dump_cfg_file_name_;
   bool dump_cfg_append_;
 
+  // Whether the compiler should trade performance for determinism to guarantee exactly reproducable
+  // outcomes.
+  bool force_determinism_;
+
   friend class Dex2Oat;
 
   DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 46484b1..3d24d19 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -165,10 +165,15 @@
       }
     }
 
-    // Set desired allocation size for .bss section.
-    void SetSize(Elf_Word size) {
-      CHECK_EQ(header_.sh_type, (Elf_Word)SHT_NOBITS);
+    // Write this section as "NOBITS" section. (used for the .bss section)
+    // This means that the ELF file does not contain the initial data for this section
+    // and it will be zero-initialized when the ELF file is loaded in the running program.
+    void WriteNoBitsSection(Elf_Word size) {
+      DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u);
+      Start();
+      header_.sh_type = SHT_NOBITS;
       header_.sh_size = size;
+      End();
     }
 
     // This function always succeeds to simplify code.
@@ -346,6 +351,12 @@
     other_sections_.push_back(std::move(s));
   }
 
+  // Set where the next section will be allocated in the virtual address space.
+  void SetVirtualAddress(Elf_Addr address) {
+    DCHECK_GE(address, virtual_address_);
+    virtual_address_ = address;
+  }
+
   void Start() {
     // Reserve space for ELF header and program headers.
     // We do not know the number of headers until later, so
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 73e6aa3..2e98b69 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -16,6 +16,7 @@
 
 #include "elf_writer_debug.h"
 
+#include <algorithm>
 #include <unordered_set>
 #include <vector>
 #include <cstdio>
@@ -40,6 +41,11 @@
 #include "stack_map.h"
 #include "utils.h"
 
+// liblzma.
+#include "XzEnc.h"
+#include "7zCrc.h"
+#include "XzCrc64.h"
+
 namespace art {
 namespace dwarf {
 
@@ -222,7 +228,8 @@
 template<typename ElfTypes>
 void WriteCFISection(ElfBuilder<ElfTypes>* builder,
                      const ArrayRef<const MethodDebugInfo>& method_infos,
-                     CFIFormat format) {
+                     CFIFormat format,
+                     bool write_oat_patches) {
   CHECK(format == DW_DEBUG_FRAME_FORMAT || format == DW_EH_FRAME_FORMAT);
   typedef typename ElfTypes::Addr Elf_Addr;
 
@@ -238,6 +245,24 @@
     patch_locations.reserve(method_infos.size());
   }
 
+  // The methods can be written any order.
+  // Let's therefore sort them in the lexicographical order of the opcodes.
+  // This has no effect on its own. However, if the final .debug_frame section is
+  // compressed it reduces the size since similar opcodes sequences are grouped.
+  std::vector<const MethodDebugInfo*> sorted_method_infos;
+  sorted_method_infos.reserve(method_infos.size());
+  for (size_t i = 0; i < method_infos.size(); i++) {
+    sorted_method_infos.push_back(&method_infos[i]);
+  }
+  std::sort(
+      sorted_method_infos.begin(),
+      sorted_method_infos.end(),
+      [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) {
+        ArrayRef<const uint8_t> l = lhs->compiled_method_->GetCFIInfo();
+        ArrayRef<const uint8_t> r = rhs->compiled_method_->GetCFIInfo();
+        return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+      });
+
   // Write .eh_frame/.debug_frame section.
   auto* cfi_section = (format == DW_DEBUG_FRAME_FORMAT
                        ? builder->GetDebugFrame()
@@ -256,11 +281,11 @@
     cfi_section->WriteFully(buffer.data(), buffer.size());
     buffer_address += buffer.size();
     buffer.clear();
-    for (const MethodDebugInfo& mi : method_infos) {
-      if (!mi.deduped_) {  // Only one FDE per unique address.
-        ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo();
+    for (const MethodDebugInfo* mi : sorted_method_infos) {
+      if (!mi->deduped_) {  // Only one FDE per unique address.
+        ArrayRef<const uint8_t> opcodes = mi->compiled_method_->GetCFIInfo();
         if (!opcodes.empty()) {
-          const Elf_Addr code_address = text_address + mi.low_pc_;
+          const Elf_Addr code_address = text_address + mi->low_pc_;
           if (format == DW_EH_FRAME_FORMAT) {
             binary_search_table.push_back(
                 dchecked_integral_cast<uint32_t>(code_address));
@@ -268,7 +293,7 @@
                 dchecked_integral_cast<uint32_t>(buffer_address));
           }
           WriteFDE(is64bit, cfi_address, cie_address,
-                   code_address, mi.high_pc_ - mi.low_pc_,
+                   code_address, mi->high_pc_ - mi->low_pc_,
                    opcodes, format, buffer_address, &buffer,
                    &patch_locations);
           cfi_section->WriteFully(buffer.data(), buffer.size());
@@ -309,8 +334,10 @@
     header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
     header_section->End();
   } else {
-    builder->WritePatches(".debug_frame.oat_patches",
-                          ArrayRef<const uintptr_t>(patch_locations));
+    if (write_oat_patches) {
+      builder->WritePatches(".debug_frame.oat_patches",
+                            ArrayRef<const uintptr_t>(patch_locations));
+    }
   }
 }
 
@@ -1338,8 +1365,9 @@
 }
 
 template <typename ElfTypes>
-void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
-                       const ArrayRef<const MethodDebugInfo>& method_infos) {
+static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
+                              const ArrayRef<const MethodDebugInfo>& method_infos,
+                              bool with_signature) {
   bool generated_mapping_symbol = false;
   auto* strtab = builder->GetStrTab();
   auto* symtab = builder->GetSymTab();
@@ -1359,22 +1387,31 @@
 
   strtab->Start();
   strtab->Write("");  // strtab should start with empty string.
+  std::string last_name;
+  size_t last_name_offset = 0;
   for (const MethodDebugInfo& info : method_infos) {
     if (info.deduped_) {
       continue;  // Add symbol only for the first instance.
     }
-    std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, true);
+    std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, with_signature);
     if (deduped_addresses.find(info.low_pc_) != deduped_addresses.end()) {
       name += " [DEDUPED]";
     }
+    // If we write method names without signature, we might see the same name multiple times.
+    size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
 
     const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr;
     const bool is_relative = (text != nullptr);
     uint32_t low_pc = info.low_pc_;
     // Add in code delta, e.g., thumb bit 0 for Thumb2 code.
     low_pc += info.compiled_method_->CodeDelta();
-    symtab->Add(strtab->Write(name), text, low_pc,
-                is_relative, info.high_pc_ - info.low_pc_, STB_GLOBAL, STT_FUNC);
+    symtab->Add(name_offset,
+                text,
+                low_pc,
+                is_relative,
+                info.high_pc_ - info.low_pc_,
+                STB_GLOBAL,
+                STT_FUNC);
 
     // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
     // instructions, so that disassembler tools can correctly disassemble.
@@ -1387,6 +1424,9 @@
         generated_mapping_symbol = true;
       }
     }
+
+    last_name = std::move(name);
+    last_name_offset = name_offset;
   }
   strtab->End();
 
@@ -1402,13 +1442,83 @@
                     const ArrayRef<const MethodDebugInfo>& method_infos,
                     CFIFormat cfi_format) {
   // Add methods to .symtab.
-  WriteDebugSymbols(builder, method_infos);
+  WriteDebugSymbols(builder, method_infos, true /* with_signature */);
   // Generate CFI (stack unwinding information).
-  WriteCFISection(builder, method_infos, cfi_format);
+  WriteCFISection(builder, method_infos, cfi_format, true /* write_oat_patches */);
   // Write DWARF .debug_* sections.
   WriteDebugSections(builder, method_infos);
 }
 
+static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) {
+  // Configure the compression library.
+  CrcGenerateTable();
+  Crc64GenerateTable();
+  CLzma2EncProps lzma2Props;
+  Lzma2EncProps_Init(&lzma2Props);
+  lzma2Props.lzmaProps.level = 1;  // Fast compression.
+  Lzma2EncProps_Normalize(&lzma2Props);
+  CXzProps props;
+  XzProps_Init(&props);
+  props.lzma2Props = &lzma2Props;
+  // Implement the required interface for communication (written in C so no virtual methods).
+  struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress {
+    static SRes ReadImpl(void* p, void* buf, size_t* size) {
+      auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p));
+      *size = std::min(*size, ctx->src_->size() - ctx->src_pos_);
+      memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size);
+      ctx->src_pos_ += *size;
+      return SZ_OK;
+    }
+    static size_t WriteImpl(void* p, const void* buf, size_t size) {
+      auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p));
+      const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf);
+      ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size);
+      return size;
+    }
+    static SRes ProgressImpl(void* , UInt64, UInt64) {
+      return SZ_OK;
+    }
+    size_t src_pos_;
+    const std::vector<uint8_t>* src_;
+    std::vector<uint8_t>* dst_;
+  };
+  XzCallbacks callbacks;
+  callbacks.Read = XzCallbacks::ReadImpl;
+  callbacks.Write = XzCallbacks::WriteImpl;
+  callbacks.Progress = XzCallbacks::ProgressImpl;
+  callbacks.src_pos_ = 0;
+  callbacks.src_ = src;
+  callbacks.dst_ = dst;
+  // Compress.
+  SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks);
+  CHECK_EQ(res, SZ_OK);
+}
+
+template <typename ElfTypes>
+void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* parent_builder,
+                        const ArrayRef<const MethodDebugInfo>& method_infos) {
+  const InstructionSet isa = parent_builder->GetIsa();
+  std::vector<uint8_t> buffer;
+  buffer.reserve(KB);
+  VectorOutputStream out("Mini-debug-info ELF file", &buffer);
+  std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+  builder->Start();
+  // Write .rodata and .text as NOBITS sections.
+  // This allows tools to detect virtual address relocation of the parent ELF file.
+  builder->SetVirtualAddress(parent_builder->GetRoData()->GetAddress());
+  builder->GetRoData()->WriteNoBitsSection(parent_builder->GetRoData()->GetSize());
+  builder->SetVirtualAddress(parent_builder->GetText()->GetAddress());
+  builder->GetText()->WriteNoBitsSection(parent_builder->GetText()->GetSize());
+  WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */);
+  WriteCFISection(builder.get(), method_infos, DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */);
+  builder->End();
+  CHECK(builder->Good());
+  std::vector<uint8_t> compressed_buffer;
+  compressed_buffer.reserve(buffer.size() / 4);
+  XzCompress(&buffer, &compressed_buffer);
+  parent_builder->WriteSection(".gnu_debugdata", &compressed_buffer);
+}
+
 template <typename ElfTypes>
 static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
     const dwarf::MethodDebugInfo& method_info) {
@@ -1481,6 +1591,12 @@
     ElfBuilder<ElfTypes64>* builder,
     const ArrayRef<const MethodDebugInfo>& method_infos,
     CFIFormat cfi_format);
+template void WriteMiniDebugInfo<ElfTypes32>(
+    ElfBuilder<ElfTypes32>* builder,
+    const ArrayRef<const MethodDebugInfo>& method_infos);
+template void WriteMiniDebugInfo<ElfTypes64>(
+    ElfBuilder<ElfTypes64>* builder,
+    const ArrayRef<const MethodDebugInfo>& method_infos);
 
 }  // namespace dwarf
 }  // namespace art
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index e4bc856..e19da08 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -35,6 +35,10 @@
                     const ArrayRef<const MethodDebugInfo>& method_infos,
                     CFIFormat cfi_format);
 
+template <typename ElfTypes>
+void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* builder,
+                        const ArrayRef<const MethodDebugInfo>& method_infos);
+
 ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info);
 
 ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 7b1bdd7..6bf080a 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -137,9 +137,7 @@
 void ElfWriterQuick<ElfTypes>::SetBssSize(size_t bss_size) {
   auto* bss = builder_->GetBss();
   if (bss_size != 0u) {
-    bss->Start();
-    bss->SetSize(bss_size);
-    bss->End();
+    bss->WriteNoBitsSection(bss_size);
   }
 }
 
@@ -152,8 +150,13 @@
 void ElfWriterQuick<ElfTypes>::WriteDebugInfo(
     const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) {
   if (compiler_options_->GetGenerateDebugInfo()) {
+    // Generate all the debug information we can.
     dwarf::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat);
   }
+  if (compiler_options_->GetGenerateMiniDebugInfo()) {
+    // Generate only some information and compress it.
+    dwarf::WriteMiniDebugInfo(builder_.get(), method_infos);
+  }
 }
 
 template <typename ElfTypes>
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index b65fb36..a5a7796 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -119,6 +119,7 @@
           compiler_driver_->GetInstructionSet(),
           compiler_driver_->GetInstructionSetFeatures(),
           &key_value_store,
+          /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
           &opened_dex_files_map,
           &opened_dex_files);
       ASSERT_TRUE(dex_files_ok);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 72c615e..c8720ea 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1866,6 +1866,9 @@
   orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
   FixupClassVisitor visitor(this, copy);
   static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
+
+  // Remove the clinitThreadId. This is required for image determinism.
+  copy->SetClinitThreadId(static_cast<pid_t>(0));
 }
 
 void ImageWriter::FixupObject(Object* orig, Object* copy) {
@@ -1993,6 +1996,10 @@
       mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
     }
   }
+
+  // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
+  // compiler pointers in here will make the output non-deterministic.
+  copy_dex_cache->SetDexFile(nullptr);
 }
 
 const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const {
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 083ba8f..6774758 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -112,7 +112,8 @@
       /* init_failure_output */ nullptr,
       /* abort_on_hard_verifier_failure */ false,
       /* dump_cfg_file_name */ "",
-      /* dump_cfg_append */ false));
+      /* dump_cfg_append */ false,
+      /* force_determinism */ false));
   for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
     compiler_options_->ParseCompilerOption(argument, Usage);
   }
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 52a2382..e920460 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -95,7 +95,7 @@
 
   // Assembler that holds generated instructions
   std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set, instruction_set_features));
-  jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo());
+  jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GenerateAnyDebugInfo());
 
   // Offsets into data structures
   // TODO: if cross compiling these offsets are for the host not the target
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index c0d15f3..cff2f47 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -126,7 +126,8 @@
 
   bool WriteElf(File* file,
                 const std::vector<const DexFile*>& dex_files,
-                SafeMap<std::string, std::string>& key_value_store) {
+                SafeMap<std::string, std::string>& key_value_store,
+                bool verify) {
     TimingLogger timings("WriteElf", false, false);
     OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
     for (const DexFile* dex_file : dex_files) {
@@ -139,12 +140,13 @@
         return false;
       }
     }
-    return DoWriteElf(file, oat_writer, key_value_store);
+    return DoWriteElf(file, oat_writer, key_value_store, verify);
   }
 
   bool WriteElf(File* file,
                 const std::vector<const char*>& dex_filenames,
-                SafeMap<std::string, std::string>& key_value_store) {
+                SafeMap<std::string, std::string>& key_value_store,
+                bool verify) {
     TimingLogger timings("WriteElf", false, false);
     OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
     for (const char* dex_filename : dex_filenames) {
@@ -152,24 +154,26 @@
         return false;
       }
     }
-    return DoWriteElf(file, oat_writer, key_value_store);
+    return DoWriteElf(file, oat_writer, key_value_store, verify);
   }
 
   bool WriteElf(File* file,
                 ScopedFd&& zip_fd,
                 const char* location,
-                SafeMap<std::string, std::string>& key_value_store) {
+                SafeMap<std::string, std::string>& key_value_store,
+                bool verify) {
     TimingLogger timings("WriteElf", false, false);
     OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
     if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
       return false;
     }
-    return DoWriteElf(file, oat_writer, key_value_store);
+    return DoWriteElf(file, oat_writer, key_value_store, verify);
   }
 
   bool DoWriteElf(File* file,
                   OatWriter& oat_writer,
-                  SafeMap<std::string, std::string>& key_value_store) {
+                  SafeMap<std::string, std::string>& key_value_store,
+                  bool verify) {
     std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
         compiler_driver_->GetInstructionSet(),
         &compiler_driver_->GetCompilerOptions(),
@@ -183,6 +187,7 @@
                                          compiler_driver_->GetInstructionSet(),
                                          compiler_driver_->GetInstructionSetFeatures(),
                                          &key_value_store,
+                                         verify,
                                          &opened_dex_files_map,
                                          &opened_dex_files)) {
       return false;
@@ -219,6 +224,9 @@
     return elf_writer->End();
   }
 
+  void TestDexFileInput(bool verify);
+  void TestZipFileInput(bool verify);
+
   std::unique_ptr<const InstructionSetFeatures> insn_features_;
   std::unique_ptr<QuickCompilerCallbacks> callbacks_;
 };
@@ -354,7 +362,7 @@
   ScratchFile tmp;
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
-  bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store);
+  bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store, false);
   ASSERT_TRUE(success);
 
   if (kCompile) {  // OatWriter strips the code, regenerate to compare
@@ -480,7 +488,7 @@
   ScratchFile tmp;
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
-  bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store);
+  bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store, false);
   ASSERT_TRUE(success);
 
   std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
@@ -494,7 +502,15 @@
   EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength()));
 }
 
-TEST_F(OatTest, DexFileInput) {
+static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>& data) {
+  // If in verify mode (= fail the verifier mode), make sure we fail early. We'll fail already
+  // because of the missing map, but that may lead to out of bounds reads.
+  if (verify) {
+    const_cast<DexFile::Header*>(&data->GetHeader())->checksum_++;
+  }
+}
+
+void OatTest::TestDexFileInput(bool verify) {
   TimingLogger timings("OatTest::DexFileInput", false, false);
 
   std::vector<const char*> input_filenames;
@@ -504,6 +520,9 @@
   builder1.AddField("Lsome.TestClass;", "int", "someField");
   builder1.AddMethod("Lsome.TestClass;", "()I", "foo");
   std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+  MaybeModifyDexFileToFail(verify, dex_file1_data);
+
   bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
                                                  dex_file1_data->GetHeader().file_size_);
   ASSERT_TRUE(success);
@@ -516,6 +535,9 @@
   builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
   builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
   std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+  MaybeModifyDexFileToFail(verify, dex_file2_data);
+
   success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
                                             dex_file2_data->GetHeader().file_size_);
   ASSERT_TRUE(success);
@@ -526,7 +548,14 @@
   ScratchFile oat_file;
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
-  success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store);
+  success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
+
+  // In verify mode, we expect failure.
+  if (verify) {
+    ASSERT_FALSE(success);
+    return;
+  }
+
   ASSERT_TRUE(success);
 
   std::string error_msg;
@@ -557,7 +586,15 @@
   ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation());
 }
 
-TEST_F(OatTest, ZipFileInput) {
+TEST_F(OatTest, DexFileInputCheckOutput) {
+  TestDexFileInput(false);
+}
+
+TEST_F(OatTest, DexFileInputCheckVerifier) {
+  TestDexFileInput(true);
+}
+
+void OatTest::TestZipFileInput(bool verify) {
   TimingLogger timings("OatTest::DexFileInput", false, false);
 
   ScratchFile zip_file;
@@ -568,6 +605,9 @@
   builder1.AddField("Lsome.TestClass;", "long", "someField");
   builder1.AddMethod("Lsome.TestClass;", "()D", "foo");
   std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+  MaybeModifyDexFileToFail(verify, dex_file1_data);
+
   bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
                                                  dex_file1_data->GetHeader().file_size_);
   ASSERT_TRUE(success);
@@ -583,6 +623,9 @@
   builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
   builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
   std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+  MaybeModifyDexFileToFail(verify, dex_file2_data);
+
   success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
                                             dex_file2_data->GetHeader().file_size_);
   ASSERT_TRUE(success);
@@ -603,37 +646,42 @@
     std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() };  // NOLINT [readability/braces] [4]
 
     ScratchFile oat_file;
-    success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store);
-    ASSERT_TRUE(success);
+    success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
 
-    std::string error_msg;
-    std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
-                                                           oat_file.GetFilename(),
-                                                           nullptr,
-                                                           nullptr,
-                                                           false,
-                                                           nullptr,
-                                                           &error_msg));
-    ASSERT_TRUE(opened_oat_file != nullptr);
-    ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
-    std::unique_ptr<const DexFile> opened_dex_file1 =
-        opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
-    std::unique_ptr<const DexFile> opened_dex_file2 =
-        opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+    if (verify) {
+      ASSERT_FALSE(success);
+    } else {
+      ASSERT_TRUE(success);
 
-    ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
-    ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
-                        &opened_dex_file1->GetHeader(),
-                        dex_file1_data->GetHeader().file_size_));
-    ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
-              opened_dex_file1->GetLocation());
+      std::string error_msg;
+      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+                                                             oat_file.GetFilename(),
+                                                             nullptr,
+                                                             nullptr,
+                                                             false,
+                                                             nullptr,
+                                                             &error_msg));
+      ASSERT_TRUE(opened_oat_file != nullptr);
+      ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+      std::unique_ptr<const DexFile> opened_dex_file1 =
+          opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+      std::unique_ptr<const DexFile> opened_dex_file2 =
+          opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
 
-    ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
-    ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
-                        &opened_dex_file2->GetHeader(),
-                        dex_file2_data->GetHeader().file_size_));
-    ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
-              opened_dex_file2->GetLocation());
+      ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+      ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+                          &opened_dex_file1->GetHeader(),
+                          dex_file1_data->GetHeader().file_size_));
+      ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+                opened_dex_file1->GetLocation());
+
+      ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+      ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+                          &opened_dex_file2->GetHeader(),
+                          dex_file2_data->GetHeader().file_size_));
+      ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+                opened_dex_file2->GetLocation());
+    }
   }
 
   {
@@ -645,38 +693,51 @@
     success = WriteElf(oat_file.GetFile(),
                        std::move(zip_fd),
                        zip_file.GetFilename().c_str(),
-                       key_value_store);
-    ASSERT_TRUE(success);
+                       key_value_store,
+                       verify);
+    if (verify) {
+      ASSERT_FALSE(success);
+    } else {
+      ASSERT_TRUE(success);
 
-    std::string error_msg;
-    std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
-                                                           oat_file.GetFilename(),
-                                                           nullptr,
-                                                           nullptr,
-                                                           false,
-                                                           nullptr,
-                                                           &error_msg));
-    ASSERT_TRUE(opened_oat_file != nullptr);
-    ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
-    std::unique_ptr<const DexFile> opened_dex_file1 =
-        opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
-    std::unique_ptr<const DexFile> opened_dex_file2 =
-        opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+      std::string error_msg;
+      std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+                                                             oat_file.GetFilename(),
+                                                             nullptr,
+                                                             nullptr,
+                                                             false,
+                                                             nullptr,
+                                                             &error_msg));
+      ASSERT_TRUE(opened_oat_file != nullptr);
+      ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+      std::unique_ptr<const DexFile> opened_dex_file1 =
+          opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+      std::unique_ptr<const DexFile> opened_dex_file2 =
+          opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
 
-    ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
-    ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
-                        &opened_dex_file1->GetHeader(),
-                        dex_file1_data->GetHeader().file_size_));
-    ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
-              opened_dex_file1->GetLocation());
+      ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+      ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+                          &opened_dex_file1->GetHeader(),
+                          dex_file1_data->GetHeader().file_size_));
+      ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+                opened_dex_file1->GetLocation());
 
-    ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
-    ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
-                        &opened_dex_file2->GetHeader(),
-                        dex_file2_data->GetHeader().file_size_));
-    ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
-              opened_dex_file2->GetLocation());
+      ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+      ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+                          &opened_dex_file2->GetHeader(),
+                          dex_file2_data->GetHeader().file_size_));
+      ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+                opened_dex_file2->GetLocation());
+    }
   }
 }
 
+TEST_F(OatTest, ZipFileInputCheckOutput) {
+  TestZipFileInput(false);
+}
+
+TEST_F(OatTest, ZipFileInputCheckVerifier) {
+  TestZipFileInput(true);
+}
+
 }  // namespace art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index a542162..90ac499 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -397,6 +397,7 @@
     InstructionSet instruction_set,
     const InstructionSetFeatures* instruction_set_features,
     SafeMap<std::string, std::string>* key_value_store,
+    bool verify,
     /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   CHECK(write_state_ == WriteState::kAddingDexFileSources);
@@ -424,7 +425,7 @@
   }
   if (!WriteOatDexFiles(rodata) ||
       !ExtendForTypeLookupTables(rodata, file, size_after_type_lookup_tables) ||
-      !OpenDexFiles(file, &dex_files_map, &dex_files) ||
+      !OpenDexFiles(file, verify, &dex_files_map, &dex_files) ||
       !WriteTypeLookupTables(dex_files_map.get(), dex_files)) {
     return false;
   }
@@ -806,7 +807,7 @@
         }
       }
 
-      if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
+      if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) {
         // Record debug information for this function if we are doing that.
         const uint32_t quick_code_start = quick_code_offset -
             writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -2143,6 +2144,7 @@
 
 bool OatWriter::OpenDexFiles(
     File* file,
+    bool verify,
     /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
@@ -2201,9 +2203,11 @@
                                          oat_dex_file.GetLocation(),
                                          oat_dex_file.dex_file_location_checksum_,
                                          /* oat_dex_file */ nullptr,
+                                         verify,
                                          &error_msg));
     if (dex_files.back() == nullptr) {
-      LOG(ERROR) << "Failed to open dex file from oat file. File:" << oat_dex_file.GetLocation();
+      LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
+                 << " Error: " << error_msg;
       return false;
     }
   }
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index d681998..14c6d50 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -139,12 +139,15 @@
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
   dchecked_vector<const char*> GetSourceLocations() const;
 
-  // Write raw dex files to the .rodata section and open them from the oat file.
+  // Write raw dex files to the .rodata section and open them from the oat file. The verify
+  // setting dictates whether the dex file verifier should check the dex files. This is generally
+  // the case, and should only be false for tests.
   bool WriteAndOpenDexFiles(OutputStream* rodata,
                             File* file,
                             InstructionSet instruction_set,
                             const InstructionSetFeatures* instruction_set_features,
                             SafeMap<std::string, std::string>* key_value_store,
+                            bool verify,
                             /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
                             /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
   // Prepare layout of remaining data.
@@ -258,6 +261,7 @@
   bool WriteOatDexFiles(OutputStream* rodata);
   bool ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset);
   bool OpenDexFiles(File* file,
+                    bool verify,
                     /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
                     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
   bool WriteTypeLookupTables(MemMap* opened_dex_files_map,
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 2805162..9d796c1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -507,6 +507,7 @@
         || IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName)
         || IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName)
         || IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName)
+        || IsPass(RegisterAllocator::kRegisterAllocatorPassName)
         || IsPass(SsaBuilder::kSsaBuilderPassName)) {
       HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
       if (info == nullptr) {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index fffd005..3fac914 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -676,7 +676,7 @@
     return nullptr;
   }
   codegen->GetAssembler()->cfi().SetEnabled(
-      compiler_driver->GetCompilerOptions().GetGenerateDebugInfo());
+      compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo());
 
   PassObserver pass_observer(graph,
                              codegen.get(),
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index d77639d..5cd30ad 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -179,7 +179,7 @@
     }
 
     if (block->IsCatchBlock() ||
-        (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+        (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
       // By blocking all registers at the top of each catch block or irreducible loop, we force
       // intervals belonging to the live-in set of the catch/header block to be spilled.
       // TODO(ngeoffray): Phis in this block could be allocated in register.
@@ -1749,8 +1749,10 @@
   }
 
   // Find the intervals that cover `from` and `to`.
-  LiveInterval* destination = interval->GetSiblingAt(to->GetLifetimeStart());
-  LiveInterval* source = interval->GetSiblingAt(from->GetLifetimeEnd() - 1);
+  size_t destination_position = to->GetLifetimeStart();
+  size_t source_position = from->GetLifetimeEnd() - 1;
+  LiveInterval* destination = interval->GetSiblingAt(destination_position);
+  LiveInterval* source = interval->GetSiblingAt(source_position);
 
   if (destination == source) {
     // Interval was not split.
@@ -1759,7 +1761,8 @@
 
   LiveInterval* parent = interval->GetParent();
   HInstruction* defined_by = parent->GetDefinedBy();
-  if (destination == nullptr) {
+  if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+      (destination == nullptr || !destination->CoversSlow(destination_position))) {
     // Our live_in fixed point calculation has found that the instruction is live
     // in the `to` block because it will eventually enter an irreducible loop. Our
     // live interval computation however does not compute a fixed point, and
@@ -1775,18 +1778,41 @@
     return;
   }
 
+  Location location_source;
+  // `GetSiblingAt` returns the interval whose start and end cover `position`,
+  // but does not check whether the interval is inactive at that position.
+  // The only situation where the interval is inactive at that position is in the
+  // presence of irreducible loops for constants and ArtMethod.
+  if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+      (source == nullptr || !source->CoversSlow(source_position))) {
+    DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by));
+    if (defined_by->IsConstant()) {
+      location_source = defined_by->GetLocations()->Out();
+    } else {
+      DCHECK(defined_by->IsCurrentMethod());
+      location_source = parent->NeedsTwoSpillSlots()
+          ? Location::DoubleStackSlot(parent->GetSpillSlot())
+          : Location::StackSlot(parent->GetSpillSlot());
+    }
+  } else {
+    DCHECK(source != nullptr);
+    DCHECK(source->CoversSlow(source_position));
+    DCHECK(destination->CoversSlow(destination_position));
+    location_source = source->ToLocation();
+  }
+
   // If `from` has only one successor, we can put the moves at the exit of it. Otherwise
   // we need to put the moves at the entry of `to`.
   if (from->GetNormalSuccessors().size() == 1) {
     InsertParallelMoveAtExitOf(from,
                                defined_by,
-                               source->ToLocation(),
+                               location_source,
                                destination->ToLocation());
   } else {
     DCHECK_EQ(to->GetPredecessors().size(), 1u);
     InsertParallelMoveAtEntryOf(to,
                                 defined_by,
-                                source->ToLocation(),
+                                location_source,
                                 destination->ToLocation());
   }
 }
@@ -1890,7 +1916,7 @@
   for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     if (block->IsCatchBlock() ||
-        (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+        (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
       // Instructions live at the top of catch blocks or irreducible loop header
       // were forced to spill.
       if (kIsDebugBuild) {
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index e57a540..2958dc6 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -89,11 +89,12 @@
     DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
     std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
     std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u);
-    header->header_size_ = sizeof(header);
+    header->header_size_ = sizeof(DexFile::Header);
     header->endian_tag_ = DexFile::kDexEndianConstant;
     header->link_size_ = 0u;  // Unused.
     header->link_off_ = 0u;  // Unused.
-    header->map_off_ = 0u;  // Unused.
+    header->map_off_ = 0u;  // Unused. TODO: This is wrong. Dex files created by this builder
+                            //               cannot be verified. b/26808512
 
     uint32_t data_section_size = 0u;
 
@@ -213,13 +214,22 @@
     // Leave signature as zeros.
 
     header->file_size_ = dex_file_data_.size();
+
+    // Write the complete header early, as part of it needs to be checksummed.
+    std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
+
+    // Checksum starts after the checksum field.
     size_t skip = sizeof(header->magic_) + sizeof(header->checksum_);
-    header->checksum_ = adler32(0u, dex_file_data_.data() + skip, dex_file_data_.size() - skip);
+    header->checksum_ = adler32(adler32(0L, Z_NULL, 0),
+                                dex_file_data_.data() + skip,
+                                dex_file_data_.size() - skip);
+
+    // Write the complete header again, just simpler that way.
     std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
 
     std::string error_msg;
     std::unique_ptr<const DexFile> dex_file(DexFile::Open(
-        &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg));
+        &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, false, &error_msg));
     CHECK(dex_file != nullptr) << error_msg;
     return dex_file;
   }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 86f51e1..bbed09c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -318,6 +318,11 @@
   UsageError("");
   UsageError("  --no-generate-debug-info: Do not generate debug information for native debugging.");
   UsageError("");
+  UsageError("  --generate-mini-debug-info: Generate minimal amount of LZMA-compressed");
+  UsageError("      debug information necessary to print backtraces. (disabled by default)");
+  UsageError("");
+  UsageError("  --no-generate-mini-debug-info: Do do generated backtrace info.");
+  UsageError("");
   UsageError("  --debuggable: Produce code debuggable with Java debugger.");
   UsageError("");
   UsageError("  --native-debuggable: Produce code debuggable with native debugger (like LLDB).");
@@ -548,13 +553,15 @@
       driver_(nullptr),
       opened_dex_files_maps_(),
       opened_dex_files_(),
+      no_inline_from_dex_files_(),
       dump_stats_(false),
       dump_passes_(false),
       dump_timing_(false),
       dump_slow_timing_(kIsDebugBuild),
       swap_fd_(-1),
       app_image_fd_(kInvalidFd),
-      timings_(timings) {}
+      timings_(timings),
+      force_determinism_(false) {}
 
   ~Dex2Oat() {
     // Log completion time before deleting the runtime_, because this accesses
@@ -916,6 +923,12 @@
 
     // Fill some values into the key-value store for the oat header.
     key_value_store_.reset(new SafeMap<std::string, std::string>());
+
+    // Automatically force determinism for the boot image in a host build.
+    if (!kIsTargetBuild && IsBootImage()) {
+      force_determinism_ = true;
+    }
+    compiler_options_->force_determinism_ = force_determinism_;
   }
 
   void ExpandOatAndImageFilenames() {
@@ -1159,6 +1172,8 @@
         multi_image_ = true;
       } else if (option.starts_with("--no-inline-from=")) {
         no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
+      } else if (option == "--force-determinism") {
+        force_determinism_ = true;
       } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
         Usage("Unknown argument %s", option.data());
       }
@@ -1351,6 +1366,7 @@
                                                    instruction_set_,
                                                    instruction_set_features_.get(),
                                                    key_value_store_.get(),
+                                                   /* verify */ true,
                                                    &opened_dex_files_map,
                                                    &opened_dex_files)) {
           return false;
@@ -1452,14 +1468,18 @@
     TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
     compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
 
-    // Find the dex file we should not inline from.
+    // Find the dex files we should not inline from.
+
+    std::vector<std::string> no_inline_filters;
+    Split(no_inline_from_string_, ',', &no_inline_filters);
 
     // For now, on the host always have core-oj removed.
-    if (!kIsTargetBuild && no_inline_from_string_.empty()) {
-      no_inline_from_string_ = "core-oj";
+    const std::string core_oj = "core-oj";
+    if (!kIsTargetBuild && !ContainsElement(no_inline_filters, core_oj)) {
+      no_inline_filters.push_back(core_oj);
     }
 
-    if (!no_inline_from_string_.empty()) {
+    if (!no_inline_filters.empty()) {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
       std::vector<const std::vector<const DexFile*>*> dex_file_vectors = {
@@ -1468,34 +1488,30 @@
           &dex_files_
       };
       for (const std::vector<const DexFile*>* dex_file_vector : dex_file_vectors) {
-        if (dex_file_vector == nullptr) {
-          continue;
-        }
-
-        bool found = false;
-
         for (const DexFile* dex_file : *dex_file_vector) {
-          // Try the complete location first.
-          found = no_inline_from_string_ == dex_file->GetLocation();
-          // The try just the name.
-          if (!found) {
-            size_t last_slash = dex_file->GetLocation().rfind('/');
-            if (last_slash != std::string::npos) {
-              found = StartsWith(dex_file->GetLocation().substr(last_slash + 1),
-                                 no_inline_from_string_.c_str());
+          for (const std::string& filter : no_inline_filters) {
+            // Use dex_file->GetLocation() rather than dex_file->GetBaseLocation(). This
+            // allows tests to specify <test-dexfile>:classes2.dex if needed but if the
+            // base location passes the StartsWith() test, so do all extra locations.
+            std::string dex_location = dex_file->GetLocation();
+            if (filter.find('/') == std::string::npos) {
+              // The filter does not contain the path. Remove the path from dex_location as well.
+              size_t last_slash = dex_file->GetLocation().rfind('/');
+              if (last_slash != std::string::npos) {
+                dex_location = dex_location.substr(last_slash + 1);
+              }
+            }
+
+            if (StartsWith(dex_location, filter.c_str())) {
+              VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
+              no_inline_from_dex_files_.push_back(dex_file);
+              break;
             }
           }
-
-          if (found) {
-            VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
-            compiler_options_->no_inline_from_ = dex_file;
-            break;
-          }
         }
-
-        if (found) {
-          break;
-        }
+      }
+      if (!no_inline_from_dex_files_.empty()) {
+        compiler_options_->no_inline_from_ = &no_inline_from_dex_files_;
       }
     }
 
@@ -2105,6 +2121,21 @@
     // foreground collector by default for dex2oat.
     raw_options.push_back(std::make_pair("-XX:DisableHSpaceCompactForOOM", nullptr));
 
+    // If we're asked to be deterministic, ensure non-concurrent GC for determinism. Also
+    // force the free-list implementation for large objects.
+    if (compiler_options_->IsForceDeterminism()) {
+      raw_options.push_back(std::make_pair("-Xgc:nonconcurrent", nullptr));
+      raw_options.push_back(std::make_pair("-XX:LargeObjectSpace=freelist", nullptr));
+
+      // We also need to turn off the nonmoving space. For that, we need to disable HSpace
+      // compaction (done above) and ensure that neither foreground nor background collectors
+      // are concurrent.
+      raw_options.push_back(std::make_pair("-XX:BackgroundGC=nonconcurrent", nullptr));
+
+      // To make identity hashcode deterministic, set a known seed.
+      mirror::Object::SetHashCodeSeed(987654321U);
+    }
+
     if (!Runtime::ParseOptions(raw_options, false, runtime_options)) {
       LOG(ERROR) << "Failed to parse runtime options";
       return false;
@@ -2384,6 +2415,8 @@
   std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 
+  std::vector<const DexFile*> no_inline_from_dex_files_;
+
   std::vector<std::string> verbose_methods_;
   bool dump_stats_;
   bool dump_passes_;
@@ -2406,6 +2439,9 @@
   // Backing storage.
   std::vector<std::string> char_backing_storage_;
 
+  // See CompilerOptions.force_determinism_.
+  bool force_determinism_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 57b52fc..a1f6eee 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -142,9 +142,7 @@
     text->End();
 
     if (oat_file_->BssSize() != 0) {
-      bss->Start();
-      bss->SetSize(oat_file_->BssSize());
-      bss->End();
+      bss->WriteNoBitsSection(oat_file_->BssSize());
     }
 
     builder_->WriteDynamicSection(elf_file->GetPath());
@@ -1656,7 +1654,7 @@
       stats_.file_bytes = file->GetLength();
     }
     size_t header_bytes = sizeof(ImageHeader);
-    const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
+    const auto& object_section = image_header_.GetImageSection(ImageHeader::kSectionObjects);
     const auto& field_section = image_header_.GetImageSection(ImageHeader::kSectionArtFields);
     const auto& method_section = image_header_.GetMethodsSection();
     const auto& dex_cache_arrays_section = image_header_.GetImageSection(
@@ -1665,17 +1663,46 @@
         ImageHeader::kSectionInternedStrings);
     const auto& class_table_section = image_header_.GetImageSection(
         ImageHeader::kSectionClassTable);
+    const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
+
     stats_.header_bytes = header_bytes;
-    stats_.alignment_bytes += RoundUp(header_bytes, kObjectAlignment) - header_bytes;
-    // Add padding between the field and method section.
-    // (Field section is 4-byte aligned, method section is 8-byte aligned on 64-bit targets.)
-    stats_.alignment_bytes += method_section.Offset() -
-        (field_section.Offset() + field_section.Size());
-    // Add padding between the dex cache arrays section and the intern table. (Dex cache
-    // arrays section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
-    stats_.alignment_bytes += intern_section.Offset() -
-        (dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size());
-    stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
+
+    // Objects are kObjectAlignment-aligned.
+    // CHECK_EQ(RoundUp(header_bytes, kObjectAlignment), object_section.Offset());
+    if (object_section.Offset() > header_bytes) {
+      stats_.alignment_bytes += object_section.Offset() - header_bytes;
+    }
+
+    // Field section is 4-byte aligned.
+    constexpr size_t kFieldSectionAlignment = 4U;
+    uint32_t end_objects = object_section.Offset() + object_section.Size();
+    CHECK_EQ(RoundUp(end_objects, kFieldSectionAlignment), field_section.Offset());
+    stats_.alignment_bytes += field_section.Offset() - end_objects;
+
+    // Method section is 4/8 byte aligned depending on target. Just check for 4-byte alignment.
+    uint32_t end_fields = field_section.Offset() + field_section.Size();
+    CHECK_ALIGNED(method_section.Offset(), 4);
+    stats_.alignment_bytes += method_section.Offset() - end_fields;
+
+    // Dex cache arrays section is aligned depending on the target. Just check for 4-byte alignment.
+    uint32_t end_methods = method_section.Offset() + method_section.Size();
+    CHECK_ALIGNED(dex_cache_arrays_section.Offset(), 4);
+    stats_.alignment_bytes += dex_cache_arrays_section.Offset() - end_methods;
+
+    // Intern table is 8-byte aligned.
+    uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size();
+    CHECK_EQ(RoundUp(end_caches, 8U), intern_section.Offset());
+    stats_.alignment_bytes += intern_section.Offset() - end_caches;
+
+    // Add space between intern table and class table.
+    uint32_t end_intern = intern_section.Offset() + intern_section.Size();
+    stats_.alignment_bytes += class_table_section.Offset() - end_intern;
+
+    // Add space between class table and bitmap. Expect the bitmap to be page-aligned.
+    uint32_t end_ctable = class_table_section.Offset() + class_table_section.Size();
+    CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
+    stats_.alignment_bytes += bitmap_section.Offset() - end_ctable;
+
     stats_.bitmap_bytes += bitmap_section.Size();
     stats_.art_field_bytes += field_section.Size();
     stats_.art_method_bytes += method_section.Size();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index bb709e8..44cc9b7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1533,6 +1533,14 @@
         }
       }
       if (!equal) {
+        VLOG(image) << "Image dex files " << image_dex_file_names.size();
+        for (mirror::String* name : image_dex_file_names) {
+          VLOG(image) << name->ToModifiedUtf8();
+        }
+        VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+        for (mirror::String* name : loader_dex_file_names) {
+          VLOG(image) << name->ToModifiedUtf8();
+        }
         *error_msg = "Rejecting application image due to class loader mismatch";
         return false;
       }
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 9b93c13..81a3e4b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -195,6 +195,30 @@
   }
 }
 
+std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size,
+                                             const std::string& location,
+                                             uint32_t location_checksum,
+                                             const OatDexFile* oat_dex_file,
+                                             bool verify,
+                                             std::string* error_msg) {
+  std::unique_ptr<const DexFile> dex_file = OpenMemory(base,
+                                                       size,
+                                                       location,
+                                                       location_checksum,
+                                                       nullptr,
+                                                       oat_dex_file,
+                                                       error_msg);
+  if (verify && !DexFileVerifier::Verify(dex_file.get(),
+                                         dex_file->Begin(),
+                                         dex_file->Size(),
+                                         location.c_str(),
+                                         error_msg)) {
+    return nullptr;
+  }
+
+  return dex_file;
+}
+
 std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
                                                  std::string* error_msg) {
   CHECK(location != nullptr);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 200121e..e497e9c 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -417,9 +417,8 @@
                                              const std::string& location,
                                              uint32_t location_checksum,
                                              const OatDexFile* oat_dex_file,
-                                             std::string* error_msg) {
-    return OpenMemory(base, size, location, location_checksum, nullptr, oat_dex_file, error_msg);
-  }
+                                             bool verify,
+                                             std::string* error_msg);
 
   // Open all classesXXX.dex files from a zip archive.
   static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 137540a..84483b4 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -114,6 +114,9 @@
 // timeout on how long we wait for finalizers to run. b/21544853
 static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u);
 
+// For deterministic compilation, we need the heap to be at a well-known address.
+static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
+
 Heap::Heap(size_t initial_size,
            size_t growth_limit,
            size_t min_free,
@@ -347,11 +350,16 @@
   bool separate_non_moving_space = is_zygote ||
       support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) ||
       IsMovingGc(background_collector_type_);
-  if (foreground_collector_type == kCollectorTypeGSS) {
+  if (foreground_collector_type_ == kCollectorTypeGSS) {
     separate_non_moving_space = false;
   }
   std::unique_ptr<MemMap> main_mem_map_1;
   std::unique_ptr<MemMap> main_mem_map_2;
+
+  // Gross hack to make dex2oat deterministic.
+  if (requested_alloc_space_begin == nullptr && Runtime::Current()->IsAotCompiler()) {
+    requested_alloc_space_begin = reinterpret_cast<uint8_t*>(kAllocSpaceBeginForDeterministicAoT);
+  }
   uint8_t* request_begin = requested_alloc_space_begin;
   if (request_begin != nullptr && separate_non_moving_space) {
     request_begin += non_moving_space_capacity;
@@ -2346,6 +2354,9 @@
     // We still want to GC in case there is some unreachable non moving objects that could cause a
     // suboptimal bin packing when we compact the zygote space.
     CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false);
+    // Trim the pages at the end of the non moving space. Trim while not holding zygote lock since
+    // the trim process may require locking the mutator lock.
+    non_moving_space_->Trim();
   }
   Thread* self = Thread::Current();
   MutexLock mu(self, zygote_creation_lock_);
@@ -2356,8 +2367,6 @@
   Runtime::Current()->GetInternTable()->AddNewTable();
   Runtime::Current()->GetClassLinker()->MoveClassTableToPreZygote();
   VLOG(heap) << "Starting PreZygoteFork";
-  // Trim the pages at the end of the non moving space.
-  non_moving_space_->Trim();
   // The end of the non-moving space may be protected, unprotect it so that we can copy the zygote
   // there.
   non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index b6f424b..b3439f7 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -381,19 +381,26 @@
   return (T)static_cast<uintptr_t>(AsIntArray()->GetWithoutChecks(idx));
 }
 
-template<bool kTransactionActive, bool kUnchecked, typename T>
-inline void PointerArray::SetElementPtrSize(uint32_t idx, T element, size_t ptr_size) {
+template<bool kTransactionActive, bool kUnchecked>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size) {
   if (ptr_size == 8) {
     (kUnchecked ? down_cast<LongArray*>(static_cast<Object*>(this)) : AsLongArray())->
-        SetWithoutChecks<kTransactionActive>(idx, (uint64_t)(element));
+        SetWithoutChecks<kTransactionActive>(idx, element);
   } else {
     DCHECK_EQ(ptr_size, 4u);
-    DCHECK_LE((uintptr_t)element, 0xFFFFFFFFu);
+    DCHECK_LE(element, static_cast<uint64_t>(0xFFFFFFFFu));
     (kUnchecked ? down_cast<IntArray*>(static_cast<Object*>(this)) : AsIntArray())
-        ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>((uintptr_t)element));
+        ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>(element));
   }
 }
 
+template<bool kTransactionActive, bool kUnchecked, typename T>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size) {
+  SetElementPtrSize<kTransactionActive, kUnchecked>(idx,
+                                                    reinterpret_cast<uintptr_t>(element),
+                                                    ptr_size);
+}
+
 template <typename Visitor>
 inline void PointerArray::Fixup(mirror::PointerArray* dest,
                                 size_t pointer_size,
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 50d77eb..2bd6c5b 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -187,8 +187,11 @@
   T GetElementPtrSize(uint32_t idx, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  template<bool kTransactionActive = false, bool kUnchecked = false>
+  void SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size)
+      SHARED_REQUIRES(Locks::mutator_lock_);
   template<bool kTransactionActive = false, bool kUnchecked = false, typename T>
-  void SetElementPtrSize(uint32_t idx, T element, size_t ptr_size)
+  void SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 75a3f1a..d5783c0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -157,7 +157,7 @@
 
 inline LengthPrefixedArray<ArtMethod>* Class::GetMethodsPtr() {
   return reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
-      GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_)));
+      static_cast<uintptr_t>(GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_))));
 }
 
 template<VerifyObjectFlags kVerifyFlags>
@@ -208,7 +208,7 @@
 
 inline void Class::SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods) {
   SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, methods_),
-                    reinterpret_cast<uint64_t>(new_methods));
+                    static_cast<uint64_t>(reinterpret_cast<uintptr_t>(new_methods)));
 }
 
 template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 3017820..195bbc9 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -561,7 +561,7 @@
   // The size of java.lang.Class.class.
   static uint32_t ClassClassSize(size_t pointer_size) {
     // The number of vtable entries in java.lang.Class.
-    uint32_t vtable_entries = Object::kVTableLength + 69;
+    uint32_t vtable_entries = Object::kVTableLength + 72;
     return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
   }
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 0ddd4a2..a80585a 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -469,14 +469,21 @@
   return soa.AddLocalReference<jobjectArray>(ret.Get());
 }
 
-static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationType) {
+static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationClass) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+
+  // Handle public contract to throw NPE if the "annotationClass" argument was null.
+  if (UNLIKELY(annotationClass == nullptr)) {
+    ThrowNullPointerException("annotationClass");
+    return nullptr;
+  }
+
   if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
     return nullptr;
   }
-  Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+  Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationClass)));
   return soa.AddLocalReference<jobject>(
       klass->GetDexFile().GetAnnotationForClass(klass, annotation_class));
 }
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 82b3933..8f321a0 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1072,8 +1072,13 @@
 }
 
 std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
-  return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
-                       dex_file_location_checksum_, this, error_msg);
+  return DexFile::Open(dex_file_pointer_,
+                       FileSize(),
+                       dex_file_location_,
+                       dex_file_location_checksum_,
+                       this,
+                       false /* verify */,
+                       error_msg);
 }
 
 uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
diff --git a/test/048-reflect-v8/expected.txt b/test/048-reflect-v8/expected.txt
index 2d0b4cc..3109ecc 100644
--- a/test/048-reflect-v8/expected.txt
+++ b/test/048-reflect-v8/expected.txt
@@ -1,4 +1,95 @@
-Main$DefaultInterface is default = yes
-Main$RegularInterface is default = no
-Main$ImplementsWithDefault is default = yes
-Main$ImplementsWithRegular is default = no
+==============================
+Are These Methods Default:
+==============================
+IsDefaultTest$DefaultInterface is default = yes
+IsDefaultTest$RegularInterface is default = no
+IsDefaultTest$ImplementsWithDefault is default = yes
+IsDefaultTest$ImplementsWithRegular is default = no
+==============================
+Class annotations by type:
+==============================
+Annotations by type, defined by class SingleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by class SingleUser with annotation Calendars: <empty>
+Annotations by type, defined by class User with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by class User with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by class User2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by class User2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by class UserComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by class UserComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Annotations by type, defined by class UserSub with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by class UserSub with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by class UserSub2 with annotation Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by class UserSub2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Class declared annotation:
+==============================
+Declared annotations by class class SingleUser, annotation interface Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Declared annotations by class class SingleUser, annotation interface Calendars: <null>
+Declared annotations by class class User, annotation interface Calendar: <null>
+Declared annotations by class class User, annotation interface Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Declared annotations by class class UserComplex, annotation interface Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6)
+Declared annotations by class class UserComplex, annotation interface Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Declared annotations by class class UserSub, annotation interface Calendar: <null>
+Declared annotations by class class UserSub, annotation interface Calendars: <null>
+Declared annotations by class class UserSub2, annotation interface Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Declared annotations by class class UserSub2, annotation interface Calendars: <null>
+-----------------------------
+-----------------------------
+==============================
+Declared class annotations by type:
+==============================
+Declared annnotations by type, defined by class SingleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Declared annnotations by type, defined by class SingleUser with annotation Calendars: <empty>
+Declared annnotations by type, defined by class User with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Declared annnotations by type, defined by class User with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Declared annnotations by type, defined by class User2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Declared annnotations by type, defined by class User2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Declared annnotations by type, defined by class UserComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Declared annnotations by type, defined by class UserComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Declared annnotations by type, defined by class UserSub with annotation Calendar: <empty>
+Declared annnotations by type, defined by class UserSub with annotation Calendars: <empty>
+Declared annnotations by type, defined by class UserSub2 with annotation Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Declared annnotations by type, defined by class UserSub2 with annotation Calendars: <empty>
+-----------------------------
+-----------------------------
+==============================
+Method annotations by type:
+==============================
+Annotations by type, defined by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by method singleUser with annotation Calendars: <empty>
+Annotations by type, defined by method user with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by method user2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Declared method annotations:
+==============================
+Annotations declared by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations declared by method singleUser with annotation Calendars: <null>
+Annotations declared by method user with annotation Calendar: <null>
+Annotations declared by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations declared by method user2 with annotation Calendar: <null>
+Annotations declared by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations declared by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6)
+Annotations declared by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Declared method annotations by type:
+==============================
+Annotations by type, defined by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by method singleUser with annotation Calendars: <empty>
+Annotations by type, defined by method user with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by method user2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
diff --git a/test/048-reflect-v8/src/AnnotationTest.java b/test/048-reflect-v8/src/AnnotationTest.java
new file mode 100644
index 0000000..75e6845
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTest.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+public class AnnotationTest extends AnnotationTestHelpers {
+  public static void testAnnotationsByType() {
+    System.out.println("==============================");
+    System.out.println("Class annotations by type:");
+    System.out.println("==============================");
+
+    // Print associated annotations:
+    // * A is directly present or repeatably present on an element E;
+    // * No annotation of A is directly/repeatably present on an element
+    //   AND E is a class AND A's type is inheritable, AND A is associated with its superclass.
+    // (Looks through subtypes recursively only if there's 0 result at each level,
+    // and the annotation is @Inheritable).
+    printAnnotationsByType(Calendar.class, SingleUser.class);
+    printAnnotationsByType(Calendars.class, SingleUser.class);
+
+    printAnnotationsByType(Calendar.class, User.class);
+    printAnnotationsByType(Calendars.class, User.class);
+
+    printAnnotationsByType(Calendar.class, User2.class);  // Enforce ordering 'z,x,y'
+    printAnnotationsByType(Calendars.class, User2.class);
+
+    // NOTE:
+    //    Order of outer-most annotations Calendars[C,C],S vs C,Calendars[C,C] is unspecified.
+    //    In particular it's the order of #getDeclaredAnnotations which is completely unmentioned.
+    //    The only requirement for #getAnnotationsByType is to have same ordering as
+    //    #getDeclaredAnnotations.
+    //    (Calendars[] itself has to maintain value() order).
+    printAnnotationsByType(Calendar.class, UserComplex.class);  // Cs(C,C),C collapses into C,C,C.
+    printAnnotationsByType(Calendars.class, UserComplex.class);
+
+    printAnnotationsByType(Calendar.class, UserSub.class);
+    printAnnotationsByType(Calendars.class, UserSub.class);
+
+    printAnnotationsByType(Calendar.class, UserSub2.class);
+    // The directly present "Calendar" annotation masks all the repeatably present
+    // "Calendar" annotations coming from User.
+    printAnnotationsByType(Calendars.class, UserSub2.class);
+    // Edge case: UserSub2 doesn't directly have a Calendars annotation,
+    // so it doesn't mask the "User" Calendars annotation.
+
+    System.out.println("-----------------------------");
+    System.out.println("-----------------------------");
+
+  }
+
+  public static void testDeclaredAnnotation() {
+    System.out.println("==============================");
+    System.out.println("Class declared annotation:");
+    System.out.println("==============================");
+
+    // Print directly present annotations:
+    //
+    // The element E has an annotation_item for it (accessible through an
+    // annotations_directory_item) corresponding to an annotation A,
+    // and A's type_idx must match that on the encoded_annotation (from the annotation_item).
+    // (Does not look through the subtypes recursively)
+    printDeclaredAnnotation(SingleUser.class, Calendar.class);
+    printDeclaredAnnotation(SingleUser.class, Calendars.class);
+
+    printDeclaredAnnotation(User.class, Calendar.class);
+    printDeclaredAnnotation(User.class, Calendars.class);
+
+    printDeclaredAnnotation(UserComplex.class, Calendar.class);
+    printDeclaredAnnotation(UserComplex.class, Calendars.class);
+
+    printDeclaredAnnotation(UserSub.class, Calendar.class);
+    printDeclaredAnnotation(UserSub.class, Calendars.class);
+
+    printDeclaredAnnotation(UserSub2.class, Calendar.class);
+    printDeclaredAnnotation(UserSub2.class, Calendars.class);
+
+    System.out.println("-----------------------------");
+    System.out.println("-----------------------------");
+  }
+
+  public static void testDeclaredAnnotationsByType() {
+    System.out.println("==============================");
+    System.out.println("Declared class annotations by type:");
+    System.out.println("==============================");
+
+    // A is directly present or repeatably present on an element E;
+    // -- (does not do any recursion for classes regardless of @Inherited)
+    printDeclaredAnnotationsByType(Calendar.class, SingleUser.class);
+    printDeclaredAnnotationsByType(Calendars.class, SingleUser.class);
+
+    printDeclaredAnnotationsByType(Calendar.class, User.class);
+    printDeclaredAnnotationsByType(Calendars.class, User.class);
+
+    printDeclaredAnnotationsByType(Calendar.class, User2.class);  // Enforce ordering 'z,x,y'
+    printDeclaredAnnotationsByType(Calendars.class, User2.class);
+
+    printDeclaredAnnotationsByType(Calendar.class, UserComplex.class);
+    printDeclaredAnnotationsByType(Calendars.class, UserComplex.class);
+
+    printDeclaredAnnotationsByType(Calendar.class, UserSub.class);
+    printDeclaredAnnotationsByType(Calendars.class, UserSub.class);
+
+    printDeclaredAnnotationsByType(Calendar.class, UserSub2.class);
+    // The directly present "Calendar" annotation masks all the repeatably present "Calendar"
+    // annotations coming from User.
+    printDeclaredAnnotationsByType(Calendars.class, UserSub2.class);
+    // Edge case: UserSub2 doesn't directly have a Calendars annotation,
+    // so it doesn't mask the "User" Calendars annotation.
+
+    System.out.println("-----------------------------");
+    System.out.println("-----------------------------");
+  }
+
+  // Print the annotation "annotationClass" that is associated with an element denoted by
+  // "annotationUseClass."
+  private static <A extends Annotation> void printAnnotationsByType(Class<A> annotationClass,
+      Class<?> annotationUseClass) {
+    A[] annotationsByType = annotationUseClass.getAnnotationsByType(annotationClass);
+
+    String msg = "Annotations by type, defined by class "
+        + annotationUseClass.getName() + " with annotation " + annotationClass.getName() + ": "
+        + asString(annotationsByType);
+
+
+    System.out.println(msg);
+  }
+
+  private static <A extends Annotation> void printDeclaredAnnotation(Class<?> annotationUseClass,
+      Class<A> annotationDefClass) {
+    A anno = annotationUseClass.getDeclaredAnnotation(annotationDefClass);
+
+    String msg = asString(anno);
+
+    System.out.println("Declared annotations by class " + annotationUseClass
+        + ", annotation " + annotationDefClass + ": " + msg);
+  }
+
+  // Print the annotation "annotationClass" that is directly/indirectly present with an element
+  // denoted by "annotationUseClass."
+  private static <A extends Annotation> void printDeclaredAnnotationsByType(
+      Class<A> annotationClass, Class<?> annotationUseClass) {
+    A[] annotationsByType = annotationUseClass.getDeclaredAnnotationsByType(annotationClass);
+
+    String msg = "Declared annnotations by type, defined by class " + annotationUseClass.getName()
+        + " with annotation " + annotationClass.getName() + ": "
+        + asString(annotationsByType);
+
+    System.out.println(msg);
+  }
+
+  public static void testMethodAnnotationsByType() {
+    System.out.println("==============================");
+    System.out.println("Method annotations by type:");
+    System.out.println("==============================");
+
+    // Print associated annotations:
+    // * A is directly present or repeatably present on an element E;
+    // * No annotation of A is directly/repeatably present on an element AND E is a class
+    //   AND A's type is inheritable, AND A is associated with its superclass.
+    // (Looks through subtypes recursively only if there's 0 result at each level,
+    // and the annotation is @Inheritable).
+    printMethodAnnotationsByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
+    printMethodAnnotationsByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+    printMethodAnnotationsByType(Calendar.class, "user", AnnotationTestFixture.class);
+    printMethodAnnotationsByType(Calendars.class, "user", AnnotationTestFixture.class);
+
+    printMethodAnnotationsByType(Calendar.class, "user2", AnnotationTestFixture.class);
+    printMethodAnnotationsByType(Calendars.class, "user2", AnnotationTestFixture.class);
+
+    printMethodAnnotationsByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
+    printMethodAnnotationsByType(Calendars.class, "userComplex", AnnotationTestFixture.class);
+
+    System.out.println("-----------------------------");
+    System.out.println("-----------------------------");
+  }
+
+  // Print the annotation "annotationClass" that is associated with an element denoted by
+  // "annotationUseClass" method methodName.
+  private static <A extends Annotation> void printMethodAnnotationsByType(Class<A> annotationClass,
+      String methodName, Class<?> annotationUseClass) {
+    Method m = null;
+    try {
+      m = annotationUseClass.getDeclaredMethod(methodName);
+    } catch (Throwable t) {
+      throw new AssertionError(t);
+    }
+    A[] annotationsByType = m.getAnnotationsByType(annotationClass);
+
+    String msg = "Annotations by type, defined by method " + m.getName() + " with annotation " +
+      annotationClass.getName() + ": " +
+      asString(annotationsByType);
+
+    System.out.println(msg);
+  }
+
+  public static void testMethodDeclaredAnnotations() {
+    System.out.println("==============================");
+    System.out.println("Declared method annotations:");
+    System.out.println("==============================");
+
+    printMethodDeclaredAnnotation(Calendar.class, "singleUser", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotation(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+    printMethodDeclaredAnnotation(Calendar.class, "user", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotation(Calendars.class, "user", AnnotationTestFixture.class);
+
+    printMethodDeclaredAnnotation(Calendar.class, "user2", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotation(Calendars.class, "user2", AnnotationTestFixture.class);
+
+    printMethodDeclaredAnnotation(Calendar.class, "userComplex", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotation(Calendars.class, "userComplex", AnnotationTestFixture.class);
+
+    System.out.println("-----------------------------");
+    System.out.println("-----------------------------");
+  }
+
+  // Print the annotation "annotationClass" that is associated with an element denoted by
+  // methodName in annotationUseClass.
+  private static <A extends Annotation> void printMethodDeclaredAnnotation(Class<A> annotationClass,
+      String methodName, Class<?> annotationUseClass) {
+    Method m = null;
+    try {
+      m = annotationUseClass.getDeclaredMethod(methodName);
+    } catch (Throwable t) {
+      throw new AssertionError(t);
+    }
+    Annotation annotationsByType = m.getDeclaredAnnotation(annotationClass);
+
+    String msg = "Annotations declared by method " + m.getName() + " with annotation "
+        + annotationClass.getName() + ": "
+        + asString(annotationsByType);
+
+    System.out.println(msg);
+  }
+
+  public static void testMethodDeclaredAnnotationsByType() {
+    System.out.println("==============================");
+    System.out.println("Declared method annotations by type:");
+    System.out.println("==============================");
+
+    printMethodDeclaredAnnotationByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotationByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+    printMethodDeclaredAnnotationByType(Calendar.class, "user", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotationByType(Calendars.class, "user", AnnotationTestFixture.class);
+
+    printMethodDeclaredAnnotationByType(Calendar.class, "user2", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotationByType(Calendars.class, "user2", AnnotationTestFixture.class);
+
+    printMethodDeclaredAnnotationByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
+    printMethodDeclaredAnnotationByType(Calendars.class, "userComplex",
+        AnnotationTestFixture.class);
+
+    System.out.println("-----------------------------");
+    System.out.println("-----------------------------");
+  }
+
+  // Print the annotation "annotationClass" that is associated with an element denoted by
+  // methodName in annotationUseClass.
+  private static <A extends Annotation> void printMethodDeclaredAnnotationByType(
+      Class<A> annotationClass, String methodName, Class<?> annotationUseClass) {
+    Method m = null;
+    try {
+      m = annotationUseClass.getDeclaredMethod(methodName);
+    } catch (Throwable t) {
+      throw new AssertionError(t);
+    }
+    A[] annotationsByType = m.getDeclaredAnnotationsByType(annotationClass);
+
+    String msg = "Annotations by type, defined by method " + m.getName() + " with annotation "
+        + annotationClass.getName() + ": "
+        + asString(annotationsByType);
+
+    System.out.println(msg);
+  }
+}
diff --git a/test/048-reflect-v8/src/AnnotationTestFixture.java b/test/048-reflect-v8/src/AnnotationTestFixture.java
new file mode 100644
index 0000000..248dfac
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTestFixture.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class AnnotationTestFixture {
+
+  @Calendar(dayOfWeek="single", hour=23)
+  public static void singleUser() {
+
+  }
+  @Calendars ({
+    @Calendar(dayOfMonth="last"),
+    @Calendar(dayOfWeek="Fri", hour=23)
+  })
+  public static void user() {
+
+  }
+
+  @Calendars ({
+    @Calendar(dayOfMonth="z"),
+    @Calendar(dayOfMonth="x"),
+    @Calendar(dayOfMonth="y")
+  })
+  public static void user2() {
+
+  }
+
+  @Calendar(dayOfMonth="afirst")
+  @Calendars ({
+    @Calendar(dayOfMonth="zsecond"),
+    @Calendar(dayOfMonth="athird", hour=23)
+  })
+  public static void userComplex() {
+
+  }
+}
diff --git a/test/048-reflect-v8/src/AnnotationTestHelpers.java b/test/048-reflect-v8/src/AnnotationTestHelpers.java
new file mode 100644
index 0000000..6b5bea2
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTestHelpers.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.annotation.Annotation;
+
+public class AnnotationTestHelpers {
+  // Provide custom print function that print a deterministic output.
+  // Note that Annotation#toString has unspecified order: it prints out the
+  // fields, which is why we can't rely on it.
+
+  public static String asString(Annotation anno) {
+    if (anno instanceof Calendar) {
+      return asString((Calendar)anno);
+    } else if (anno instanceof Calendars) {
+      return asString((Calendars)anno);
+    } else {
+      if (anno == null) {
+        return "<null>";
+      }
+      // Fall-back, usually would only go here in a test failure.
+      return anno.toString();
+    }
+  }
+
+  public static String asString(Annotation[] annos) {
+    String msg = "";
+
+    if (annos == null) {
+      msg += "<null>";
+    } else if (annos.length == 0) {
+      msg += "<empty>";
+    } else {
+      for (int i = 0; i < annos.length; ++i) {
+        msg += asString(annos[i]);
+
+        if (i != annos.length - 1) {
+          msg += ", ";
+        }
+      }
+    }
+
+    return msg;
+  }
+
+  public static String asString(Calendar calendar) {
+    if (calendar == null) {
+      return "<null>";
+    }
+
+    return "@Calendar(dayOfMonth=" + calendar.dayOfMonth() + ", dayOfWeek=" +
+      calendar.dayOfWeek() + ", hour=" + calendar.hour() + ")";
+  }
+
+  public static String asString(Calendars calendars) {
+    if (calendars == null) {
+      return "<null>";
+    }
+
+    String s = "@Calendars(value=[";
+
+    Calendar[] allValues = calendars.value();
+    for (int i = 0; i < allValues.length; ++i) {
+      s += asString(allValues[i]);
+      if (i != allValues.length - 1) {
+        s += ", ";
+      }
+    }
+
+    s += "])";
+
+    return s;
+  }
+}
diff --git a/test/048-reflect-v8/src/Calendar.java b/test/048-reflect-v8/src/Calendar.java
new file mode 100644
index 0000000..4a16573
--- /dev/null
+++ b/test/048-reflect-v8/src/Calendar.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// This is a plain old non-1.8 annotation. At runtime we can see that it has a
+// "Repeatable" annotation if we query with getDeclaredAnnotation(Repeatable.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(Calendars.class)
+@Inherited  // note: container must also be @Inherited by JLS.
+public @interface Calendar {
+    String dayOfMonth() default "unspecified_month";
+    String dayOfWeek() default "unspecified_week";
+    int hour() default 6;
+}
+
diff --git a/test/048-reflect-v8/src/Calendars.java b/test/048-reflect-v8/src/Calendars.java
new file mode 100644
index 0000000..caeda52
--- /dev/null
+++ b/test/048-reflect-v8/src/Calendars.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// Plain old annotation, there's nothing 1.8 specific about it.
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited  // note: elements must also be @Inherited by JLS.
+public @interface Calendars {
+  Calendar[] value();
+}
diff --git a/test/048-reflect-v8/src/IFaceA.java b/test/048-reflect-v8/src/IFaceA.java
new file mode 100644
index 0000000..9b1f610
--- /dev/null
+++ b/test/048-reflect-v8/src/IFaceA.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// in the binary.
+@Calendars ({
+  @Calendar(dayOfMonth="if_a_first"),
+  @Calendar(dayOfMonth="if_b_last")
+})
+public interface IFaceA {
+}
diff --git a/test/048-reflect-v8/src/IFaceSimple.java b/test/048-reflect-v8/src/IFaceSimple.java
new file mode 100644
index 0000000..93cf610
--- /dev/null
+++ b/test/048-reflect-v8/src/IFaceSimple.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Simple annotation, no container.
+@Calendar(dayOfMonth="if_simple_first")
+public interface IFaceSimple {
+
+}
diff --git a/test/048-reflect-v8/src/IsDefaultTest.java b/test/048-reflect-v8/src/IsDefaultTest.java
new file mode 100644
index 0000000..177dcf1
--- /dev/null
+++ b/test/048-reflect-v8/src/IsDefaultTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class IsDefaultTest {
+  interface DefaultInterface {
+    default void sayHi() {
+      System.out.println("hi default");
+    }
+  }
+
+  interface RegularInterface {
+    void sayHi();
+  }
+
+  class ImplementsWithDefault implements DefaultInterface {}
+  class ImplementsWithRegular implements RegularInterface {
+    public void sayHi() {
+      System.out.println("hello specific");
+    }
+  }
+
+  private static void printIsDefault(Class<?> klass) {
+    Method m;
+    try {
+      m = klass.getMethod("sayHi");
+    } catch (Throwable t) {
+      System.out.println(t);
+      return;
+    }
+
+    boolean isDefault = m.isDefault();
+    System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no"));
+  }
+
+  public static void test() {
+    System.out.println("==============================");
+    System.out.println("Are These Methods Default:");
+    System.out.println("==============================");
+
+    printIsDefault(DefaultInterface.class);
+    printIsDefault(RegularInterface.class);
+    printIsDefault(ImplementsWithDefault.class);
+    printIsDefault(ImplementsWithRegular.class);
+  }
+}
diff --git a/test/048-reflect-v8/src/Main.java b/test/048-reflect-v8/src/Main.java
index 7fa2a92..f2b8287 100644
--- a/test/048-reflect-v8/src/Main.java
+++ b/test/048-reflect-v8/src/Main.java
@@ -14,43 +14,14 @@
  * limitations under the License.
  */
 
-import java.lang.reflect.Method;
-
 public class Main {
-  interface DefaultInterface {
-    default void sayHi() {
-      System.out.println("hi default");
-    }
-  }
-
-  interface RegularInterface {
-    void sayHi();
-  }
-
-  class ImplementsWithDefault implements DefaultInterface {}
-  class ImplementsWithRegular implements RegularInterface {
-    public void sayHi() {
-      System.out.println("hello specific");
-    }
-  }
-
-  private static void printIsDefault(Class<?> klass) {
-    Method m;
-    try {
-      m = klass.getMethod("sayHi");
-    } catch (Throwable t) {
-      System.out.println(t);
-      return;
-    }
-
-    boolean isDefault = m.isDefault();
-    System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no"));
-  }
-
   public static void main(String[] args) {
-    printIsDefault(DefaultInterface.class);
-    printIsDefault(RegularInterface.class);
-    printIsDefault(ImplementsWithDefault.class);
-    printIsDefault(ImplementsWithRegular.class);
+    IsDefaultTest.test();
+    AnnotationTest.testAnnotationsByType();
+    AnnotationTest.testDeclaredAnnotation();
+    AnnotationTest.testDeclaredAnnotationsByType();
+    AnnotationTest.testMethodAnnotationsByType();
+    AnnotationTest.testMethodDeclaredAnnotations();
+    AnnotationTest.testMethodDeclaredAnnotationsByType();
   }
 }
diff --git a/test/048-reflect-v8/src/SingleUser.java b/test/048-reflect-v8/src/SingleUser.java
new file mode 100644
index 0000000..0f9c430
--- /dev/null
+++ b/test/048-reflect-v8/src/SingleUser.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Stored as a single "Calendar" annotation in the binary.
+@Calendar(dayOfWeek="single", hour=23)
+public class SingleUser {
+
+}
diff --git a/test/048-reflect-v8/src/User.java b/test/048-reflect-v8/src/User.java
new file mode 100644
index 0000000..003ceeb
--- /dev/null
+++ b/test/048-reflect-v8/src/User.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// in the binary.
+//
+/* FIXME: Use this code instead, when Jack supports repeatable annotations properly.
+ *
+ * @Calendar(dayOfMonth="last")
+ * @Calendar(dayOfWeek="Fri", hour=23)
+ */
+@Calendars ({
+  @Calendar(dayOfMonth="last"),
+  @Calendar(dayOfWeek="Fri", hour=23)
+})
+public class User {
+
+}
diff --git a/test/048-reflect-v8/src/User2.java b/test/048-reflect-v8/src/User2.java
new file mode 100644
index 0000000..1a6049f
--- /dev/null
+++ b/test/048-reflect-v8/src/User2.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar,Calendar)
+// in the binary.
+// (Check for order, should be z,x,y)
+@Calendars ({
+  @Calendar(dayOfMonth="z"),
+  @Calendar(dayOfMonth="x"),
+  @Calendar(dayOfMonth="y")
+})
+public class User2 {
+
+}
diff --git a/test/048-reflect-v8/src/UserComplex.java b/test/048-reflect-v8/src/UserComplex.java
new file mode 100644
index 0000000..e262349
--- /dev/null
+++ b/test/048-reflect-v8/src/UserComplex.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// followed by a Calendar in the binary.
+// In other words { Calendars([C,C]), C }
+//
+// Note that trying to do {C,Calendars,C} or similar
+// is illegal by the JLS.
+@Calendar(dayOfMonth="afirst")
+@Calendars ({
+  @Calendar(dayOfMonth="zsecond"),
+  @Calendar(dayOfMonth="athird", hour=23)
+})
+// @Calendar(dayOfMonth="zlast")  // Leave for future ordering test
+public class UserComplex {
+
+}
diff --git a/test/048-reflect-v8/src/UserSub.java b/test/048-reflect-v8/src/UserSub.java
new file mode 100644
index 0000000..d60aa6a
--- /dev/null
+++ b/test/048-reflect-v8/src/UserSub.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class UserSub
+  extends User
+  implements IFaceA, IFaceSimple {
+
+}
diff --git a/test/048-reflect-v8/src/UserSub2.java b/test/048-reflect-v8/src/UserSub2.java
new file mode 100644
index 0000000..13e2eb0
--- /dev/null
+++ b/test/048-reflect-v8/src/UserSub2.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// This calendar subsumes anything else we would've normally gotten from the subclass.
+@Calendar(dayOfMonth="sub2")
+public class UserSub2
+  extends User
+  implements IFaceA, IFaceSimple {
+
+}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 77301d2..87656bc 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -101,7 +101,12 @@
 }
 #endif
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(
+    JNIEnv*,
+    jobject,
+    jboolean full_signatrues,
+    jint,
+    jboolean) {
 #if __linux__
   if (IsPicImage()) {
     LOG(INFO) << "Image is pic, in-process unwinding check bypassed.";
@@ -122,14 +127,21 @@
   // We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
   // This is also risky, as deduping might play a trick on us, so the test needs to make sure that
   // only unique functions are being expected.
+  // "mini-debug-info" does not include parameters to save space.
   std::vector<std::string> seq = {
       "Java_Main_unwindInProcess",                   // This function.
-      "boolean Main.unwindInProcess(int, boolean)",  // The corresponding Java native method frame.
+      "Main.unwindInProcess",                        // The corresponding Java native method frame.
+      "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)",  // Framework method.
+      "Main.main"                                    // The Java entry method.
+  };
+  std::vector<std::string> full_seq = {
+      "Java_Main_unwindInProcess",                   // This function.
+      "boolean Main.unwindInProcess(boolean, int, boolean)",  // The corresponding Java native method frame.
       "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)",  // Framework method.
       "void Main.main(java.lang.String[])"           // The Java entry method.
   };
 
-  bool result = CheckStack(bt.get(), seq);
+  bool result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
   if (!kCauseSegfault) {
     return result ? JNI_TRUE : JNI_FALSE;
   } else {
@@ -178,7 +190,11 @@
 }
 #endif
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(
+    JNIEnv*,
+    jobject,
+    jboolean full_signatrues,
+    jint pid_int) {
 #if __linux__
   // TODO: What to do on Valgrind?
   pid_t pid = static_cast<pid_t>(pid_int);
@@ -214,17 +230,27 @@
 
   if (result) {
     // See comment in unwindInProcess for non-exact stack matching.
+    // "mini-debug-info" does not include parameters to save space.
     std::vector<std::string> seq = {
         // "Java_Main_sleep",                        // The sleep function being executed in the
                                                      // other runtime.
                                                      // Note: For some reason, the name isn't
                                                      // resolved, so don't look for it right now.
+        "Main.sleep",                                // The corresponding Java native method frame.
+        "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)",  // Framework method.
+        "Main.main"                                  // The Java entry method.
+    };
+    std::vector<std::string> full_seq = {
+        // "Java_Main_sleep",                        // The sleep function being executed in the
+                                                     // other runtime.
+                                                     // Note: For some reason, the name isn't
+                                                     // resolved, so don't look for it right now.
         "boolean Main.sleep(int, boolean, double)",  // The corresponding Java native method frame.
         "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)",  // Framework method.
         "void Main.main(java.lang.String[])"         // The Java entry method.
     };
 
-    result = CheckStack(bt.get(), seq);
+    result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
   }
 
   if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
index 6a5618e..8db7853 100644
--- a/test/137-cfi/expected.txt
+++ b/test/137-cfi/expected.txt
@@ -1 +1,2 @@
 JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/137-cfi/run b/test/137-cfi/run
index 9c567b6..6f4bcfe 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -14,4 +14,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-exec ${RUN} "$@" -Xcompiler-option --generate-debug-info
+# Test with full DWARF debugging information.
+# Check full signatures of methods.
+${RUN} "$@" -Xcompiler-option --generate-debug-info --args --full-signatures
+
+# Test with minimal compressed debugging information.
+# Check only method names (parameters are omitted to save space).
+${RUN} "$@" -Xcompiler-option --generate-mini-debug-info
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
index 5474c9b..7755338 100644
--- a/test/137-cfi/src/Main.java
+++ b/test/137-cfi/src/Main.java
@@ -34,19 +34,28 @@
 
   private boolean secondary;
 
+  private boolean full_signatures;
+
   private boolean passed;
 
-  public Main(boolean secondary) {
+  public Main(boolean secondary, boolean full_signatures) {
       this.secondary = secondary;
+      this.full_signatures = full_signatures;
   }
 
   public static void main(String[] args) throws Exception {
     System.loadLibrary(args[0]);
       boolean secondary = false;
-      if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+      boolean full_signatures = false;
+      for (String arg : args) {
+        if (arg.equals("--secondary")) {
           secondary = true;
+        }
+        if (arg.equals("--full-signatures")) {
+          full_signatures = true;
+        }
       }
-      new Main(secondary).run();
+      new Main(secondary, full_signatures).run();
   }
 
   private void run() {
@@ -96,7 +105,7 @@
               throw new RuntimeException(e);
           }
 
-          if (!unwindOtherProcess(pid)) {
+          if (!unwindOtherProcess(full_signatures, pid)) {
               System.out.println("Unwinding other process failed.");
           }
       } finally {
@@ -154,7 +163,7 @@
       if (b) {
           return sleep(2, b, 1.0);
       } else {
-          return unwindInProcess(1, b);
+          return unwindInProcess(full_signatures, 1, b);
       }
   }
 
@@ -162,6 +171,6 @@
 
   public native boolean sleep(int i, boolean b, double dummy);
 
-  public native boolean unwindInProcess(int i, boolean b);
-  public native boolean unwindOtherProcess(int pid);
+  public native boolean unwindInProcess(boolean full_signatures, int i, boolean b);
+  public native boolean unwindOtherProcess(boolean full_signatures, int pid);
 }
diff --git a/test/564-checker-bitcount/src/Main.java b/test/564-checker-bitcount/src/Main.java
index b250145..2683b25 100644
--- a/test/564-checker-bitcount/src/Main.java
+++ b/test/564-checker-bitcount/src/Main.java
@@ -16,24 +16,20 @@
 
 public class Main {
 
-  // TODO: make this work when b/26700769 is done.
-  //
+  // TODO: make something like this work when b/26700769 is done.
   // CHECK-START-X86_64: int Main.bits32(int) disassembly (after)
   // CHECK-DAG: popcnt
-  //
-  // CHECK-START-X86_64: int Main.bits32(int) disassembly (after)
-  // CHECK-NOT: call
+
+  /// CHECK-START: int Main.bits32(int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
+  /// CHECK-DAG:                 Return [<<Result>>]
   private static int bits32(int x) {
     return Integer.bitCount(x);
   }
 
-  // TODO: make this work when b/26700769 is done.
-  //
-  // CHECK-START-X86_64: int Main.bits64(long) disassembly (after)
-  // CHECK-DAG: popcnt
-  //
-  // CHECK-START-X86_64: int Main.bits64(long) disassembly (after)
-  // CHECK-NOT: call
+  /// CHECK-START: int Main.bits64(long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongBitCount
+  /// CHECK-DAG:                 Return [<<Result>>]
   private static int bits64(long x) {
     return Long.bitCount(x);
   }
diff --git a/test/565-checker-irreducible-loop/expected.txt b/test/565-checker-irreducible-loop/expected.txt
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/test/565-checker-irreducible-loop/expected.txt
@@ -0,0 +1,2 @@
+1
+1
diff --git a/test/565-checker-irreducible-loop/info.txt b/test/565-checker-irreducible-loop/info.txt
new file mode 100644
index 0000000..1e0dd02
--- /dev/null
+++ b/test/565-checker-irreducible-loop/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing in the presence of
+an irreducible loop.
diff --git a/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..29547ca
--- /dev/null
+++ b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -0,0 +1,101 @@
+# Copyright (C) 2016 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.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Check that both the irreducible loop and the other loop entry
+# move the constant-folded value to where it's expected.
+
+## CHECK-START-X86: int IrreducibleLoop.test1(int, long) register (after)
+## CHECK-DAG:                     ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG:                     ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test1(IJ)I
+   .registers 10
+   const/16 v6, 2
+   const/16 v4, 1
+   const-wide/16 v0, 42
+   add-long v2, v0, v0
+
+   if-eqz p0, :loop_entry
+   goto :other_loop_pre_entry
+
+   # The then part: beginning of the irreducible loop.
+   :loop_entry
+   if-eqz p0, :exit
+   cmp-long v6, v2, p1
+   :other_loop_entry
+   sub-int p0, p0, v4
+   goto :loop_entry
+
+   # The other block branching to the irreducible loop.
+   # In that block, v4 has no live range.
+   :other_loop_pre_entry
+   goto :other_loop_entry
+
+   :exit
+   return v6
+.end method
+
+# Check that the compiler does not crash when
+# a live interval is found while connecting siblings, but that
+# live interval is inactive at the desired position.
+
+## CHECK-START-X86: int IrreducibleLoop.test2(int, long) register (after)
+## CHECK-DAG:                     ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG:                     ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test2(IJ)I
+   .registers 14
+   const/16 v6, 2
+   const/16 v4, 1
+   const-wide/16 v0, 42
+   const-wide/16 v8, 68
+   add-long v2, v0, v0
+
+   if-eqz p0, :loop_entry
+   goto :other_loop_pre_entry
+
+   # The then part: beginning of the irreducible loop.
+   :loop_entry
+   if-eqz p0, :exit
+   cmp-long v6, v2, p1
+   :other_loop_entry
+   sub-int p0, p0, v4
+   goto :loop_entry
+
+   # The other block branching to the irreducible loop.
+   :other_loop_pre_entry
+   # Make v2 have a register location.
+   sput-wide v2, LIrreducibleLoop;->myField:J
+   # Stress register allocator on x86 to split v2.
+   sput-wide v0, LIrreducibleLoop;->myField:J
+   sput-wide p1, LIrreducibleLoop;->myField:J
+   sput-wide v8, LIrreducibleLoop;->myField:J
+   if-eqz p0, :join
+   # Stress register allocator on x86 to split v2.
+   sput-wide p1, LIrreducibleLoop;->myField:J
+   sput-wide v8, LIrreducibleLoop;->myField:J
+   sput-wide v0, LIrreducibleLoop;->myField:J
+   # Last use of v2 before the irreducible loop, that
+   # will create an interval hole.
+   sput-wide v2, LIrreducibleLoop;->myField:J
+   :join
+   goto :other_loop_entry
+
+   :exit
+   return v6
+.end method
+
+.field public static volatile myField:J
diff --git a/test/565-checker-irreducible-loop/src/Main.java b/test/565-checker-irreducible-loop/src/Main.java
new file mode 100644
index 0000000..e48bd6b
--- /dev/null
+++ b/test/565-checker-irreducible-loop/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+  // Workaround for b/18051191.
+  class InnerClass {}
+
+  public static void main(String[] args) throws Exception {
+    Class<?> c = Class.forName("IrreducibleLoop");
+    {
+      Method m = c.getMethod("test1", int.class, long.class);
+      Object[] arguments = { 42, 31L };
+      System.out.println(m.invoke(null, arguments));
+    }
+
+    {
+      Method m = c.getMethod("test2", int.class, long.class);
+      Object[] arguments = { 42, 31L };
+      System.out.println(m.invoke(null, arguments));
+    }
+  }
+}
diff --git a/test/565-checker-rotate/expected.txt b/test/565-checker-rotate/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/565-checker-rotate/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/565-checker-rotate/info.txt b/test/565-checker-rotate/info.txt
new file mode 100644
index 0000000..c6a8091
--- /dev/null
+++ b/test/565-checker-rotate/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit rotate operations.
diff --git a/test/565-checker-rotate/src/Main.java b/test/565-checker-rotate/src/Main.java
new file mode 100644
index 0000000..33bbe02
--- /dev/null
+++ b/test/565-checker-rotate/src/Main.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+
+  /// CHECK-START: int Main.rotateLeft32(int, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int rotateLeft32(int x, int y) {
+    return Integer.rotateLeft(x, y);
+  }
+
+  /// CHECK-START: long Main.rotateLeft64(long, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongRotateLeft
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static long rotateLeft64(long x, int y) {
+    return Long.rotateLeft(x, y);
+  }
+
+  /// CHECK-START: int Main.rotateRight32(int, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerRotateRight
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int rotateRight32(int x, int y) {
+    return Integer.rotateRight(x, y);
+  }
+
+  /// CHECK-START: long Main.rotateRight64(long, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongRotateRight
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static long rotateRight64(long x, int y) {
+    return Long.rotateRight(x, y);
+  }
+
+  public static void main(String args[]) {
+    expectEquals32(0x00000001, rotateLeft32(0x00000001, 0));
+    expectEquals32(0x00000002, rotateLeft32(0x00000001, 1));
+    expectEquals32(0x80000000, rotateLeft32(0x00000001, 31));
+    expectEquals32(0x00000001, rotateLeft32(0x00000001, 32));  // overshoot
+    expectEquals32(0x00000003, rotateLeft32(0x80000001, 1));
+    expectEquals32(0x00000006, rotateLeft32(0x80000001, 2));
+    expectEquals32(0x23456781, rotateLeft32(0x12345678, 4));
+    expectEquals32(0xBCDEF09A, rotateLeft32(0x9ABCDEF0, 8));
+    for (int i = 0; i < 40; i++) {  // overshoot a bit
+      int j = i & 31;
+      expectEquals32(0x00000000, rotateLeft32(0x00000000, i));
+      expectEquals32(0xFFFFFFFF, rotateLeft32(0xFFFFFFFF, i));
+      expectEquals32(1 << j, rotateLeft32(0x00000001, i));
+      expectEquals32((0x12345678 << j) | (0x12345678 >>> -j),
+                     rotateLeft32(0x12345678, i));
+    }
+
+    expectEquals64(0x0000000000000001L, rotateLeft64(0x0000000000000001L, 0));
+    expectEquals64(0x0000000000000002L, rotateLeft64(0x0000000000000001L, 1));
+    expectEquals64(0x8000000000000000L, rotateLeft64(0x0000000000000001L, 63));
+    expectEquals64(0x0000000000000001L, rotateLeft64(0x0000000000000001L, 64));  // overshoot
+    expectEquals64(0x0000000000000003L, rotateLeft64(0x8000000000000001L, 1));
+    expectEquals64(0x0000000000000006L, rotateLeft64(0x8000000000000001L, 2));
+    expectEquals64(0x23456789ABCDEF01L, rotateLeft64(0x123456789ABCDEF0L, 4));
+    expectEquals64(0x3456789ABCDEF012L, rotateLeft64(0x123456789ABCDEF0L, 8));
+    for (int i = 0; i < 70; i++) {  // overshoot a bit
+      int j = i & 63;
+      expectEquals64(0x0000000000000000L, rotateLeft64(0x0000000000000000L, i));
+      expectEquals64(0xFFFFFFFFFFFFFFFFL, rotateLeft64(0xFFFFFFFFFFFFFFFFL, i));
+      expectEquals64(1L << j, rotateLeft64(0x0000000000000001, i));
+      expectEquals64((0x123456789ABCDEF0L << j) | (0x123456789ABCDEF0L >>> -j),
+                     rotateLeft64(0x123456789ABCDEF0L, i));
+    }
+
+    expectEquals32(0x80000000, rotateRight32(0x80000000, 0));
+    expectEquals32(0x40000000, rotateRight32(0x80000000, 1));
+    expectEquals32(0x00000001, rotateRight32(0x80000000, 31));
+    expectEquals32(0x80000000, rotateRight32(0x80000000, 32));  // overshoot
+    expectEquals32(0xC0000000, rotateRight32(0x80000001, 1));
+    expectEquals32(0x60000000, rotateRight32(0x80000001, 2));
+    expectEquals32(0x81234567, rotateRight32(0x12345678, 4));
+    expectEquals32(0xF09ABCDE, rotateRight32(0x9ABCDEF0, 8));
+    for (int i = 0; i < 40; i++) {  // overshoot a bit
+      int j = i & 31;
+      expectEquals32(0x00000000, rotateRight32(0x00000000, i));
+      expectEquals32(0xFFFFFFFF, rotateRight32(0xFFFFFFFF, i));
+      expectEquals32(0x80000000 >>> j, rotateRight32(0x80000000, i));
+      expectEquals32((0x12345678 >>> j) | (0x12345678 << -j),
+                     rotateRight32(0x12345678, i));
+    }
+
+    expectEquals64(0x8000000000000000L, rotateRight64(0x8000000000000000L, 0));
+    expectEquals64(0x4000000000000000L, rotateRight64(0x8000000000000000L, 1));
+    expectEquals64(0x0000000000000001L, rotateRight64(0x8000000000000000L, 63));
+    expectEquals64(0x8000000000000000L, rotateRight64(0x8000000000000000L, 64));  // overshoot
+    expectEquals64(0xC000000000000000L, rotateRight64(0x8000000000000001L, 1));
+    expectEquals64(0x6000000000000000L, rotateRight64(0x8000000000000001L, 2));
+    expectEquals64(0x0123456789ABCDEFL, rotateRight64(0x123456789ABCDEF0L, 4));
+    expectEquals64(0xF0123456789ABCDEL, rotateRight64(0x123456789ABCDEF0L, 8));
+    for (int i = 0; i < 70; i++) {  // overshoot a bit
+      int j = i & 63;
+      expectEquals64(0x0000000000000000L, rotateRight64(0x0000000000000000L, i));
+      expectEquals64(0xFFFFFFFFFFFFFFFFL, rotateRight64(0xFFFFFFFFFFFFFFFFL, i));
+      expectEquals64(0x8000000000000000L >>> j, rotateRight64(0x8000000000000000L, i));
+      expectEquals64((0x123456789ABCDEF0L >>> j) | (0x123456789ABCDEF0L << -j),
+                     rotateRight64(0x123456789ABCDEF0L, i));
+    }
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals32(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+  private static void expectEquals64(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/566-checker-signum/expected.txt b/test/566-checker-signum/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/566-checker-signum/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/566-checker-signum/info.txt b/test/566-checker-signum/info.txt
new file mode 100644
index 0000000..328e494
--- /dev/null
+++ b/test/566-checker-signum/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit signum operations.
diff --git a/test/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java
new file mode 100644
index 0000000..cc4a984
--- /dev/null
+++ b/test/566-checker-signum/src/Main.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+
+  /// CHECK-START: int Main.sign32(int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int sign32(int x) {
+    return Integer.signum(x);
+  }
+
+  /// CHECK-START: int Main.sign64(long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int sign64(long x) {
+    return Long.signum(x);
+  }
+
+  public static void main(String args[]) {
+    expectEquals(-1, sign32(Integer.MIN_VALUE));
+    expectEquals(-1, sign32(-12345));
+    expectEquals(-1, sign32(-1));
+    expectEquals(0, sign32(0));
+    expectEquals(1, sign32(1));
+    expectEquals(1, sign32(12345));
+    expectEquals(1, sign32(Integer.MAX_VALUE));
+
+    for (int i = -11; i <= 11; i++) {
+      int expected = 0;
+      if (i < 0) expected = -1;
+      else if (i > 0) expected = 1;
+      expectEquals(expected, sign32(i));
+    }
+
+    expectEquals(-1, sign64(Long.MIN_VALUE));
+    expectEquals(-1, sign64(-12345L));
+    expectEquals(-1, sign64(-1L));
+    expectEquals(0, sign64(0L));
+    expectEquals(1, sign64(1L));
+    expectEquals(1, sign64(12345L));
+    expectEquals(1, sign64(Long.MAX_VALUE));
+
+    for (long i = -11L; i <= 11L; i++) {
+      int expected = 0;
+      if (i < 0) expected = -1;
+      else if (i > 0) expected = 1;
+      expectEquals(expected, sign64(i));
+    }
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/567-checker-compare/expected.txt b/test/567-checker-compare/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/567-checker-compare/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/567-checker-compare/info.txt b/test/567-checker-compare/info.txt
new file mode 100644
index 0000000..5bac7b1
--- /dev/null
+++ b/test/567-checker-compare/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit compare operations.
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-compare/src/Main.java
new file mode 100644
index 0000000..52abb75
--- /dev/null
+++ b/test/567-checker-compare/src/Main.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+
+  /// CHECK-START: int Main.compare32(int, int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int compare32(int x, int y) {
+    return Integer.compare(x, y);
+  }
+
+  /// CHECK-START: int Main.compare64(long, long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongCompare
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int compare64(long x, long y) {
+    return Long.compare(x, y);
+  }
+
+  public static void main(String args[]) {
+    expectEquals(-1, compare32(Integer.MIN_VALUE, Integer.MIN_VALUE + 1));
+    expectEquals(-1, compare32(Integer.MIN_VALUE, -1));
+    expectEquals(-1, compare32(Integer.MIN_VALUE, 0));
+    expectEquals(-1, compare32(Integer.MIN_VALUE, 1));
+    expectEquals(-1, compare32(Integer.MIN_VALUE, Integer.MAX_VALUE));
+    expectEquals(-1, compare32(-1, 0));
+    expectEquals(-1, compare32(-1, 1));
+    expectEquals(-1, compare32(0, 1));
+
+    expectEquals(0, compare32(Integer.MIN_VALUE, Integer.MIN_VALUE));
+    expectEquals(0, compare32(-1, -1));
+    expectEquals(0, compare32(0, 0));
+    expectEquals(0, compare32(1, 1));
+    expectEquals(0, compare32(Integer.MAX_VALUE, Integer.MAX_VALUE));
+
+    expectEquals(1, compare32(0, -1));
+    expectEquals(1, compare32(1, -1));
+    expectEquals(1, compare32(1, 0));
+    expectEquals(1, compare32(Integer.MAX_VALUE, Integer.MIN_VALUE));
+    expectEquals(1, compare32(Integer.MAX_VALUE, -1));
+    expectEquals(1, compare32(Integer.MAX_VALUE, 0));
+    expectEquals(1, compare32(Integer.MAX_VALUE, 1));
+    expectEquals(1, compare32(Integer.MAX_VALUE, Integer.MAX_VALUE - 1));
+
+    for (int i = -11; i <= 11; i++) {
+      for (int j = -11; j <= 11; j++) {
+        int expected = 0;
+        if (i < j) expected = -1;
+        else if (i > j) expected = 1;
+        expectEquals(expected, compare32(i, j));
+      }
+    }
+
+    expectEquals(-1, compare64(Long.MIN_VALUE, Long.MIN_VALUE + 1L));
+    expectEquals(-1, compare64(Long.MIN_VALUE, -1L));
+    expectEquals(-1, compare64(Long.MIN_VALUE, 0L));
+    expectEquals(-1, compare64(Long.MIN_VALUE, 1L));
+    expectEquals(-1, compare64(Long.MIN_VALUE, Long.MAX_VALUE));
+    expectEquals(-1, compare64(-1L, 0L));
+    expectEquals(-1, compare64(-1L, 1L));
+    expectEquals(-1, compare64(0L, 1L));
+
+    expectEquals(0, compare64(Long.MIN_VALUE, Long.MIN_VALUE));
+    expectEquals(0, compare64(-1L, -1L));
+    expectEquals(0, compare64(0L, 0L));
+    expectEquals(0, compare64(1L, 1L));
+    expectEquals(0, compare64(Long.MAX_VALUE, Long.MAX_VALUE));
+
+    expectEquals(1, compare64(0L, -1L));
+    expectEquals(1, compare64(1L, -1L));
+    expectEquals(1, compare64(1L, 0L));
+    expectEquals(1, compare64(Long.MAX_VALUE, Long.MIN_VALUE));
+    expectEquals(1, compare64(Long.MAX_VALUE, -1L));
+    expectEquals(1, compare64(Long.MAX_VALUE, 0L));
+    expectEquals(1, compare64(Long.MAX_VALUE, 1L));
+    expectEquals(1, compare64(Long.MAX_VALUE, Long.MAX_VALUE - 1L));
+
+    for (long i = -11L; i <= 11L; i++) {
+      for (long j = -11L; j <= 11L; j++) {
+        int expected = 0;
+        if (i < j) expected = -1;
+        else if (i > j) expected = 1;
+        expectEquals(expected, compare64(i, j));
+      }
+    }
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/568-checker-onebit/expected.txt b/test/568-checker-onebit/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/568-checker-onebit/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/568-checker-onebit/info.txt b/test/568-checker-onebit/info.txt
new file mode 100644
index 0000000..c2b5bf8
--- /dev/null
+++ b/test/568-checker-onebit/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit high/low-bit operations.
diff --git a/test/568-checker-onebit/src/Main.java b/test/568-checker-onebit/src/Main.java
new file mode 100644
index 0000000..7007c6a
--- /dev/null
+++ b/test/568-checker-onebit/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+
+  /// CHECK-START: int Main.hi32(int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerHighestOneBit
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int hi32(int x) {
+    return Integer.highestOneBit(x);
+  }
+
+  /// CHECK-START: int Main.lo32(int) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerLowestOneBit
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static int lo32(int x) {
+    return Integer.lowestOneBit(x);
+  }
+
+  /// CHECK-START: long Main.hi64(long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongHighestOneBit
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static long hi64(long x) {
+    return Long.highestOneBit(x);
+  }
+
+  /// CHECK-START: long Main.lo64(long) intrinsics_recognition (after)
+  /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongLowestOneBit
+  /// CHECK-DAG:                 Return [<<Result>>]
+  private static long lo64(long x) {
+    return Long.lowestOneBit(x);
+  }
+
+  public static void main(String args[]) {
+    expectEquals32(0x00000000, hi32(0x00000000));
+    expectEquals32(0x00000000, lo32(0x00000000));
+    expectEquals32(0x00010000, hi32(0x00010000));
+    expectEquals32(0x00010000, lo32(0x00010000));
+    expectEquals32(0x00800000, hi32(0x00FF0000));
+    expectEquals32(0x00010000, lo32(0x00FF0000));
+    expectEquals32(0x80000000, hi32(0xFFFFFFFF));
+    expectEquals32(0x00000001, lo32(0xFFFFFFFF));
+
+    for (int i = 0; i < 32; i++) {
+      expectEquals32(1 << i, hi32(1 << i));
+      expectEquals32(1 << i, lo32(1 << i));
+      int expected = i < 29 ? 0x8 << i : 0x80000000;
+      expectEquals32(expected, hi32(0xF << i));
+      expectEquals32(0x1 << i, lo32(0xF << i));
+    }
+
+    expectEquals64(0x0000000000000000L, hi64(0x0000000000000000L));
+    expectEquals64(0x0000000000000000L, lo64(0x0000000000000000L));
+    expectEquals64(0x0000000100000000L, hi64(0x0000000100000000L));
+    expectEquals64(0x0000000100000000L, lo64(0x0000000100000000L));
+    expectEquals64(0x0000008000000000L, hi64(0x000000FF00000000L));
+    expectEquals64(0x0000000100000000L, lo64(0x000000FF00000000L));
+    expectEquals64(0x8000000000000000L, hi64(0xFFFFFFFFFFFFFFFFL));
+    expectEquals64(0x0000000000000001L, lo64(0xFFFFFFFFFFFFFFFFL));
+
+    for (int i = 0; i < 64; i++) {
+      expectEquals64(1L << i, hi64(1L << i));
+      expectEquals64(1L << i, lo64(1L << i));
+      long expected = i < 61 ? 0x8L << i : 0x8000000000000000L;
+      expectEquals64(expected, hi64(0xFL << i));
+      expectEquals64(0x1L << i, lo64(0xFL << i));
+    }
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals32(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+  private static void expectEquals64(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index e004b6c..8245ccf 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -74,6 +74,14 @@
         fi
         ARGS="${ARGS} $1"
         shift
+    elif [ "x$1" = "x--args" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --args" 1>&2
+            exit 1
+        fi
+        ARGS="${ARGS} $1"
+        shift
     elif [ "x$1" = "x-Xcompiler-option" ]; then
         shift
         option="$1"
@@ -533,6 +541,7 @@
 
     cd $ANDROID_BUILD_TOP
 
+    rm -rf ${DEX_LOCATION}/dalvik-cache/
     $mkdir_cmdline || exit 1
     $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
 
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index a3ecf86..da5225c 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -77,6 +77,9 @@
  * Instance.isRoot and Instance.getRootTypes.
 
 Release History:
+ 0.4 Pending
+   Show registered native allocations for heap dumps that support it.
+
  0.3 Dec 15, 2015
    Fix page loading performance by showing a limited number of entries by default.
    Fix mismatch between overview and "roots" totals.
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index fc7911b..2adec6f 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -43,22 +43,27 @@
  * ahat.
  */
 class AhatSnapshot {
-  private Snapshot mSnapshot;
-  private List<Heap> mHeaps;
+  private final Snapshot mSnapshot;
+  private final List<Heap> mHeaps;
 
   // Map from Instance to the list of Instances it immediately dominates.
-  private Map<Instance, List<Instance>> mDominated;
+  private final Map<Instance, List<Instance>> mDominated
+    = new HashMap<Instance, List<Instance>>();
 
   // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
-  private List<Instance> mRooted;
+  private final List<Instance> mRooted = new ArrayList<Instance>();
 
   // Map from roots to their types.
   // Instances are only included if they are roots, and the collection of root
   // types is guaranteed to be non-empty.
-  private Map<Instance, Collection<RootType>> mRoots;
+  private final Map<Instance, Collection<RootType>> mRoots
+    = new HashMap<Instance, Collection<RootType>>();
 
-  private Site mRootSite;
-  private Map<Heap, Long> mHeapSizes;
+  private final Site mRootSite = new Site("ROOT");
+  private final Map<Heap, Long> mHeapSizes = new HashMap<Heap, Long>();
+
+  private final List<InstanceUtils.NativeAllocation> mNativeAllocations
+    = new ArrayList<InstanceUtils.NativeAllocation>();
 
   /**
    * Create an AhatSnapshot from an hprof file.
@@ -77,10 +82,6 @@
   private AhatSnapshot(Snapshot snapshot) {
     mSnapshot = snapshot;
     mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
-    mDominated = new HashMap<Instance, List<Instance>>();
-    mRootSite = new Site("ROOT");
-    mHeapSizes = new HashMap<Heap, Long>();
-    mRooted = new ArrayList<Instance>();
 
     ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
     for (Heap heap : mHeaps) {
@@ -118,13 +119,18 @@
             }
           }
           mRootSite.add(stackId, 0, path.iterator(), inst);
+
+          // Update native allocations.
+          InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
+          if (alloc != null) {
+            mNativeAllocations.add(alloc);
+          }
         }
       }
       mHeapSizes.put(heap, total);
     }
 
     // Record the roots and their types.
-    mRoots = new HashMap<Instance, Collection<RootType>>();
     for (RootObj root : snapshot.getGCRoots()) {
       Instance inst = root.getReferredInstance();
       Collection<RootType> types = mRoots.get(inst);
@@ -259,4 +265,9 @@
     }
     return site;
   }
+
+  // Return a list of known native allocations in the snapshot.
+  public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
+    return mNativeAllocations;
+  }
 }
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index 7fa53c7..8b7f9ea 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -20,6 +20,7 @@
 import com.android.tools.perflib.heap.ClassInstance;
 import com.android.tools.perflib.heap.ClassObj;
 import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.Heap;
 import com.android.tools.perflib.heap.Type;
 import java.awt.image.BufferedImage;
 
@@ -31,7 +32,7 @@
    * Returns true if the given instance is an instance of a class with the
    * given name.
    */
-  public static boolean isInstanceOfClass(Instance inst, String className) {
+  private static boolean isInstanceOfClass(Instance inst, String className) {
     ClassObj cls = (inst == null) ? null : inst.getClassObj();
     return (cls != null && className.equals(cls.getClassName()));
   }
@@ -118,12 +119,12 @@
       return null;
     }
 
-    Integer width = getIntField(inst, "mWidth");
+    Integer width = getIntField(inst, "mWidth", null);
     if (width == null) {
       return null;
     }
 
-    Integer height = getIntField(inst, "mHeight");
+    Integer height = getIntField(inst, "mHeight", null);
     if (height == null) {
       return null;
     }
@@ -186,23 +187,29 @@
   /**
    * Read an int field of an instance.
    * The field is assumed to be an int type.
-   * Returns null if the field value is not an int or could not be read.
+   * Returns <code>def</code> if the field value is not an int or could not be
+   * read.
    */
-  private static Integer getIntField(Instance inst, String fieldName) {
+  private static Integer getIntField(Instance inst, String fieldName, Integer def) {
     Object value = getField(inst, fieldName);
     if (!(value instanceof Integer)) {
-      return null;
+      return def;
     }
     return (Integer)value;
   }
 
   /**
-   * Read an int field of an instance, returning a default value if the field
-   * was not an int or could not be read.
+   * Read a long field of an instance.
+   * The field is assumed to be a long type.
+   * Returns <code>def</code> if the field value is not an long or could not
+   * be read.
    */
-  private static int getIntField(Instance inst, String fieldName, int def) {
-    Integer value = getIntField(inst, fieldName);
-    return value == null ? def : value;
+  private static Long getLongField(Instance inst, String fieldName, Long def) {
+    Object value = getField(inst, fieldName);
+    if (!(value instanceof Long)) {
+      return def;
+    }
+    return (Long)value;
   }
 
   /**
@@ -278,4 +285,73 @@
     }
     return null;
   }
+
+  public static class NativeAllocation {
+    public long size;
+    public Heap heap;
+    public long pointer;
+    public Instance referent;
+
+    public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
+      this.size = size;
+      this.heap = heap;
+      this.pointer = pointer;
+      this.referent = referent;
+    }
+  }
+
+  /**
+   * Assuming inst represents a NativeAllocation, return information about the
+   * native allocation. Returns null if the given instance doesn't represent a
+   * native allocation.
+   */
+  public static NativeAllocation getNativeAllocation(Instance inst) {
+    if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
+      return null;
+    }
+
+    Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
+    if (pointer == null) {
+      return null;
+    }
+
+    // Search for the registry field of inst.
+    // Note: We know inst as an instance of ClassInstance because we already
+    // read the nativePtr field from it.
+    Instance registry = null;
+    for (ClassInstance.FieldValue field : ((ClassInstance)inst).getValues()) {
+      Object fieldValue = field.getValue();
+      if (fieldValue instanceof Instance) {
+        Instance fieldInst = (Instance)fieldValue;
+        if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
+          registry = fieldInst;
+          break;
+        }
+      }
+    }
+
+    if (registry == null) {
+      return null;
+    }
+
+    Long size = InstanceUtils.getLongField(registry, "size", null);
+    if (size == null) {
+      return null;
+    }
+
+    Instance referent = null;
+    for (Instance ref : inst.getHardReferences()) {
+      if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
+        referent = InstanceUtils.getReferent(ref);
+        if (referent != null) {
+          break;
+        }
+      }
+    }
+
+    if (referent == null) {
+      return null;
+    }
+    return new NativeAllocation(size, inst.getHeap(), pointer, referent);
+  }
 }
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 091820f..d784599 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -78,6 +78,7 @@
     server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
     server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
     server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
+    server.createContext("/native", new AhatHttpHandler(new NativeAllocationsHandler(ahat)));
     server.createContext("/bitmap", new BitmapHandler(ahat));
     server.createContext("/help", new HelpHandler());
     server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
diff --git a/tools/ahat/src/Menu.java b/tools/ahat/src/Menu.java
index 018e019..232b849 100644
--- a/tools/ahat/src/Menu.java
+++ b/tools/ahat/src/Menu.java
@@ -27,6 +27,8 @@
       .append(" - ")
       .appendLink(DocString.uri("sites"), DocString.text("allocations"))
       .append(" - ")
+      .appendLink(DocString.uri("native"), DocString.text("native"))
+      .append(" - ")
       .appendLink(DocString.uri("help"), DocString.text("help"));
 
   /**
diff --git a/tools/ahat/src/NativeAllocationsHandler.java b/tools/ahat/src/NativeAllocationsHandler.java
new file mode 100644
index 0000000..17407e1
--- /dev/null
+++ b/tools/ahat/src/NativeAllocationsHandler.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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.ahat;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+class NativeAllocationsHandler implements AhatHandler {
+  private static final String ALLOCATIONS_ID = "allocations";
+
+  private AhatSnapshot mSnapshot;
+
+  public NativeAllocationsHandler(AhatSnapshot snapshot) {
+    mSnapshot = snapshot;
+  }
+
+  @Override
+  public void handle(Doc doc, Query query) throws IOException {
+    List<InstanceUtils.NativeAllocation> allocs = mSnapshot.getNativeAllocations();
+
+    doc.title("Registered Native Allocations");
+
+    doc.section("Overview");
+    long totalSize = 0;
+    for (InstanceUtils.NativeAllocation alloc : allocs) {
+      totalSize += alloc.size;
+    }
+    doc.descriptions();
+    doc.description(DocString.text("Number of Registered Native Allocations"),
+        DocString.format("%,14d", allocs.size()));
+    doc.description(DocString.text("Total Size of Registered Native Allocations"),
+        DocString.format("%,14d", totalSize));
+    doc.end();
+
+    doc.section("List of Allocations");
+    if (allocs.isEmpty()) {
+      doc.println(DocString.text("(none)"));
+    } else {
+      doc.table(
+          new Column("Size", Column.Align.RIGHT),
+          new Column("Heap"),
+          new Column("Native Pointer"),
+          new Column("Referent"));
+      Comparator<InstanceUtils.NativeAllocation> compare
+        = new Sort.WithPriority<InstanceUtils.NativeAllocation>(
+            new Sort.NativeAllocationByHeapName(),
+            new Sort.NativeAllocationBySize());
+      Collections.sort(allocs, compare);
+      SubsetSelector<InstanceUtils.NativeAllocation> selector
+        = new SubsetSelector(query, ALLOCATIONS_ID, allocs);
+      for (InstanceUtils.NativeAllocation alloc : selector.selected()) {
+        doc.row(
+            DocString.format("%,14d", alloc.size),
+            DocString.text(alloc.heap.getName()),
+            DocString.format("0x%x", alloc.pointer),
+            Value.render(mSnapshot, alloc.referent));
+      }
+
+      // Print a summary of the remaining entries if there are any.
+      List<InstanceUtils.NativeAllocation> remaining = selector.remaining();
+      if (!remaining.isEmpty()) {
+        long total = 0;
+        for (InstanceUtils.NativeAllocation alloc : remaining) {
+          total += alloc.size;
+        }
+
+        doc.row(
+            DocString.format("%,14d", total),
+            DocString.text("..."),
+            DocString.text("..."),
+            DocString.text("..."));
+      }
+
+      doc.end();
+      selector.render(doc);
+    }
+  }
+}
+
diff --git a/tools/ahat/src/OverviewHandler.java b/tools/ahat/src/OverviewHandler.java
index 720fcb4..0dbad7e 100644
--- a/tools/ahat/src/OverviewHandler.java
+++ b/tools/ahat/src/OverviewHandler.java
@@ -48,6 +48,22 @@
 
     doc.section("Heap Sizes");
     printHeapSizes(doc, query);
+
+    List<InstanceUtils.NativeAllocation> allocs = mSnapshot.getNativeAllocations();
+    if (!allocs.isEmpty()) {
+      doc.section("Registered Native Allocations");
+      long totalSize = 0;
+      for (InstanceUtils.NativeAllocation alloc : allocs) {
+        totalSize += alloc.size;
+      }
+      doc.descriptions();
+      doc.description(DocString.text("Number of Registered Native Allocations"),
+          DocString.format("%,14d", allocs.size()));
+      doc.description(DocString.text("Total Size of Registered Native Allocations"),
+          DocString.format("%,14d", totalSize));
+      doc.end();
+    }
+
     doc.big(Menu.getMenu());
   }
 
diff --git a/tools/ahat/src/Sort.java b/tools/ahat/src/Sort.java
index 3b79166..c5f89c3 100644
--- a/tools/ahat/src/Sort.java
+++ b/tools/ahat/src/Sort.java
@@ -177,5 +177,31 @@
       return aName.compareTo(bName);
     }
   }
+
+  /**
+   * Compare AhatSnapshot.NativeAllocation by heap name.
+   * Different allocations with the same heap name are considered equal for
+   * the purposes of comparison.
+   */
+  public static class NativeAllocationByHeapName
+      implements Comparator<InstanceUtils.NativeAllocation> {
+    @Override
+    public int compare(InstanceUtils.NativeAllocation a, InstanceUtils.NativeAllocation b) {
+      return a.heap.getName().compareTo(b.heap.getName());
+    }
+  }
+
+  /**
+   * Compare InstanceUtils.NativeAllocation by their size.
+   * Different allocations with the same size are considered equal for the
+   * purposes of comparison.
+   * This sorts allocations from larger size to smaller size.
+   */
+  public static class NativeAllocationBySize implements Comparator<InstanceUtils.NativeAllocation> {
+    @Override
+    public int compare(InstanceUtils.NativeAllocation a, InstanceUtils.NativeAllocation b) {
+      return Long.compare(b.size, a.size);
+    }
+  }
 }
 
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 90cd7af..701d60e 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -19,6 +19,7 @@
 import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
+import libcore.util.NativeAllocationRegistry;
 
 /**
  * Program used to create a heap dump for test purposes.
@@ -47,6 +48,9 @@
       for (int i = 0; i < N; i++) {
         bigArray[i] = (byte)((i*i) & 0xFF);
       }
+
+      NativeAllocationRegistry registry = new NativeAllocationRegistry(0x12345, 42);
+      registry.registerNativeAllocation(anObject, 0xABCDABCD);
     }
   }
 
diff --git a/tools/ahat/test/NativeAllocationTest.java b/tools/ahat/test/NativeAllocationTest.java
new file mode 100644
index 0000000..7ad4c1d
--- /dev/null
+++ b/tools/ahat/test/NativeAllocationTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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.ahat;
+
+import com.android.tools.perflib.heap.Instance;
+import java.io.IOException;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class NativeAllocationTest {
+
+  @Test
+  public void nativeAllocation() throws IOException {
+    TestDump dump = TestDump.getTestDump();
+
+    AhatSnapshot snapshot = dump.getAhatSnapshot();
+    Instance referent = (Instance)dump.getDumpedThing("anObject");
+    for (InstanceUtils.NativeAllocation alloc : snapshot.getNativeAllocations()) {
+      if (alloc.referent == referent) {
+        assertEquals(42 , alloc.size);
+        assertEquals(referent.getHeap(), alloc.heap);
+        assertEquals(0xABCDABCD , alloc.pointer);
+        return;
+      }
+    }
+    fail("No native allocation found with anObject as the referent");
+  }
+}
+
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index e8894e2..3291470 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -23,6 +23,7 @@
     if (args.length == 0) {
       args = new String[]{
         "com.android.ahat.InstanceUtilsTest",
+        "com.android.ahat.NativeAllocationTest",
         "com.android.ahat.PerformanceTest",
         "com.android.ahat.QueryTest",
         "com.android.ahat.SortTest",