Merge "Mark __jit_debug_register_code as a unique symbol"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index a93d8a8..43e1457 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -206,6 +206,7 @@
 ART_C_INCLUDES := \
   external/gtest/include \
   external/icu/icu4c/source/common \
+  external/lz4/lib \
   external/valgrind/include \
   external/valgrind \
   external/vixl/src \
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 3b2d1cc..cb6d340 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -201,6 +201,9 @@
 # $(5): library dependencies (host only)
 # $(6): extra include directories
 # $(7): multilib (default: empty), valid values: {,32,64,both})
+# $(8): host prefer 32-bit: {true, false} (default: false).  If argument
+#       `multilib` is explicitly set to 64, ignore the "host prefer 32-bit"
+#       setting and only build a 64-bit executable on host.
 define build-art-multi-executable
   $(foreach debug_flavor,ndebug debug,
     $(foreach target_flavor,host target,
@@ -211,6 +214,7 @@
       art-multi-lib-dependencies-host := $(5)
       art-multi-include-extra := $(6)
       art-multi-multilib := $(7)
+      art-multi-host-prefer-32-bit := $(8)
 
       # Add either -host or -target specific lib dependencies to the lib dependencies.
       art-multi-lib-dependencies += $$(art-multi-lib-dependencies-$(target_flavor))
@@ -223,6 +227,14 @@
       # Build the env guard var name, e.g. ART_BUILD_HOST_NDEBUG.
       art-multi-env-guard := $$(call art-string-to-uppercase,ART_BUILD_$(target_flavor)_$(debug_flavor))
 
+      ifeq ($(target_flavor),host)
+        ifeq ($$(art-multi-host-prefer-32-bit),true)
+          ifneq ($$(art-multi-multilib),64)
+            art-multi-multilib := 32
+          endif
+        endif
+      endif
+
       # Build the art executable only if the corresponding env guard was set.
       ifeq ($$($$(art-multi-env-guard)),true)
         $$(eval $$(call build-art-executable,$$(art-multi-binary-name),$$(art-multi-source-files),$$(art-multi-lib-dependencies),$$(art-multi-include-extra),$(target_flavor),$(debug_flavor),$$(art-multi-multilib)))
@@ -236,6 +248,7 @@
       art-multi-lib-dependencies-host :=
       art-multi-include-extra :=
       art-multi-multilib :=
+      art-multi-host-prefer-32-bit :=
       art-multi-env-guard :=
     )
   )
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 348eabd..bdd9a84 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -258,9 +258,9 @@
   ifeq ($$(art_ndebug_or_debug),ndebug)
     LOCAL_MODULE := libart-compiler
     ifeq ($$(art_static_or_shared), static)
-      LOCAL_STATIC_LIBRARIES += libart
+      LOCAL_STATIC_LIBRARIES += libart liblz4
     else
-      LOCAL_SHARED_LIBRARIES += libart
+      LOCAL_SHARED_LIBRARIES += libart liblz4
     endif
     ifeq ($$(art_target_or_host),target)
       LOCAL_FDO_SUPPORT := true
@@ -268,9 +268,9 @@
   else # debug
     LOCAL_MODULE := libartd-compiler
     ifeq ($$(art_static_or_shared), static)
-      LOCAL_STATIC_LIBRARIES += libartd
+      LOCAL_STATIC_LIBRARIES += libartd liblz4
     else
-      LOCAL_SHARED_LIBRARIES += libartd
+      LOCAL_SHARED_LIBRARIES += libartd liblz4
     endif
   endif
 
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index e6cc50c..638b897 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -189,17 +189,29 @@
     }
 
     timer_.reset(new CumulativeLogger("Compilation times"));
-    compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
-                                              verification_results_.get(),
-                                              method_inliner_map_.get(),
-                                              compiler_kind_, instruction_set,
-                                              instruction_set_features_.get(),
-                                              true,
-                                              GetImageClasses(),
-                                              GetCompiledClasses(),
-                                              GetCompiledMethods(),
-                                              2, true, true, "", false, timer_.get(), -1, ""));
+    CreateCompilerDriver(compiler_kind_, instruction_set);
   }
+}
+
+void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) {
+  compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+                                            verification_results_.get(),
+                                            method_inliner_map_.get(),
+                                            kind,
+                                            isa,
+                                            instruction_set_features_.get(),
+                                            true,
+                                            GetImageClasses(),
+                                            GetCompiledClasses(),
+                                            GetCompiledMethods(),
+                                            2,
+                                            true,
+                                            true,
+                                            "",
+                                            false,
+                                            timer_.get(),
+                                            -1,
+                                            ""));
   // We typically don't generate an image in unit tests, disable this optimization by default.
   compiler_driver_->SetSupportBootImageFixup(false);
 }
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 1b57b7d..b491946 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -90,6 +90,8 @@
                             const char* method_name, const char* signature)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa);
+
   void ReserveImageSpace();
 
   void UnreserveImageSpace();
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 39f8ee8..18ce563 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -1430,8 +1430,4 @@
                                  method_lowering_infos_.data(), count);
 }
 
-bool MIRGraph::SkipCompilationByName(const std::string& methodname) {
-  return cu_->compiler_driver->SkipCompilation(methodname);
-}
-
 }  // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 2da8a98..3191fe9 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -564,11 +564,6 @@
   bool SkipCompilation(std::string* skip_message);
 
   /*
-   * Should we skip the compilation of this method based on its name?
-   */
-  bool SkipCompilationByName(const std::string& methodname);
-
-  /*
    * Parse dex method and add MIR at current insert point.  Returns id (which is
    * actually the index of the method in the m_units_ array).
    */
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index eaf2408..f48947d 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -50,6 +50,23 @@
     true,   // kIntrinsicMinMaxLong
     true,   // kIntrinsicMinMaxFloat
     true,   // kIntrinsicMinMaxDouble
+    true,   // kIntrinsicCos
+    true,   // kIntrinsicSin
+    true,   // kIntrinsicAcos
+    true,   // kIntrinsicAsin
+    true,   // kIntrinsicAtan
+    true,   // kIntrinsicAtan2
+    true,   // kIntrinsicCbrt
+    true,   // kIntrinsicCosh
+    true,   // kIntrinsicExp
+    true,   // kIntrinsicExpm1
+    true,   // kIntrinsicHypot
+    true,   // kIntrinsicLog
+    true,   // kIntrinsicLog10
+    true,   // kIntrinsicNextAfter
+    true,   // kIntrinsicSinh
+    true,   // kIntrinsicTan
+    true,   // kIntrinsicTanh
     true,   // kIntrinsicSqrt
     true,   // kIntrinsicCeil
     true,   // kIntrinsicFloor
@@ -95,6 +112,23 @@
 static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong_must_be_static");
 static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat_must_be_static");
 static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble_must_be_static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCos], "Cos must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicSin], "Sin must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAcos], "Acos must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAsin], "Asin must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAtan], "Atan must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAtan2], "Atan2 must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCbrt], "Cbrt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCosh], "Cosh must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicExp], "Exp must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicExpm1], "Expm1 must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicHypot], "Hypot must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicLog], "Log must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicLog10], "Log10 must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNextAfter], "NextAfter must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicSinh], "Sinh must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicTan], "Tan must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicTanh], "Tanh must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicSqrt], "Sqrt must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicCeil], "Ceil must be static");
 static_assert(kIntrinsicIsStatic[kIntrinsicFloor], "Floor must be static");
@@ -196,6 +230,23 @@
     "abs",                   // kNameCacheAbs
     "max",                   // kNameCacheMax
     "min",                   // kNameCacheMin
+    "cos",                   // kNameCacheCos
+    "sin",                   // kNameCacheSin
+    "acos",                  // kNameCacheAcos
+    "asin",                  // kNameCacheAsin
+    "atan",                  // kNameCacheAtan
+    "atan2",                 // kNameCacheAtan2
+    "cbrt",                  // kNameCacheCbrt
+    "cosh",                  // kNameCacheCosh
+    "exp",                   // kNameCacheExp
+    "expm1",                 // kNameCacheExpm1
+    "hypot",                 // kNameCacheHypot
+    "log",                   // kNameCacheLog
+    "log10",                 // kNameCacheLog10
+    "nextAfter",             // kNameCacheNextAfter
+    "sinh",                  // kNameCacheSinh
+    "tan",                   // kNameCacheTan
+    "tanh",                  // kNameCacheTanh
     "sqrt",                  // kNameCacheSqrt
     "ceil",                  // kNameCacheCeil
     "floor",                 // kNameCacheFloor
@@ -425,6 +476,23 @@
     INTRINSIC(JavaLangMath,       Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
     INTRINSIC(JavaLangStrictMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
 
+    INTRINSIC(JavaLangMath,       Cos, D_D, kIntrinsicCos, 0),
+    INTRINSIC(JavaLangMath,       Sin, D_D, kIntrinsicSin, 0),
+    INTRINSIC(JavaLangMath,       Acos, D_D, kIntrinsicAcos, 0),
+    INTRINSIC(JavaLangMath,       Asin, D_D, kIntrinsicAsin, 0),
+    INTRINSIC(JavaLangMath,       Atan, D_D, kIntrinsicAtan, 0),
+    INTRINSIC(JavaLangMath,       Atan2, DD_D, kIntrinsicAtan2, 0),
+    INTRINSIC(JavaLangMath,       Cbrt, D_D, kIntrinsicCbrt, 0),
+    INTRINSIC(JavaLangMath,       Cosh, D_D, kIntrinsicCosh, 0),
+    INTRINSIC(JavaLangMath,       Exp, D_D, kIntrinsicExp, 0),
+    INTRINSIC(JavaLangMath,       Expm1, D_D, kIntrinsicExpm1, 0),
+    INTRINSIC(JavaLangMath,       Hypot, DD_D, kIntrinsicHypot, 0),
+    INTRINSIC(JavaLangMath,       Log, D_D, kIntrinsicLog, 0),
+    INTRINSIC(JavaLangMath,       Log10, D_D, kIntrinsicLog10, 0),
+    INTRINSIC(JavaLangMath,       NextAfter, DD_D, kIntrinsicNextAfter, 0),
+    INTRINSIC(JavaLangMath,       Sinh, D_D, kIntrinsicSinh, 0),
+    INTRINSIC(JavaLangMath,       Tan, D_D, kIntrinsicTan, 0),
+    INTRINSIC(JavaLangMath,       Tanh, D_D, kIntrinsicTanh, 0),
     INTRINSIC(JavaLangMath,       Sqrt, D_D, kIntrinsicSqrt, 0),
     INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0),
 
@@ -603,6 +671,25 @@
       return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_double */);
     case kIntrinsicMinMaxDouble:
       return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_double */);
+    case kIntrinsicCos:
+    case kIntrinsicSin:
+    case kIntrinsicAcos:
+    case kIntrinsicAsin:
+    case kIntrinsicAtan:
+    case kIntrinsicAtan2:
+    case kIntrinsicCbrt:
+    case kIntrinsicCosh:
+    case kIntrinsicExp:
+    case kIntrinsicExpm1:
+    case kIntrinsicHypot:
+    case kIntrinsicLog:
+    case kIntrinsicLog10:
+    case kIntrinsicNextAfter:
+    case kIntrinsicSinh:
+    case kIntrinsicTan:
+    case kIntrinsicTanh:
+      // Not implemented in Quick.
+      return false;
     case kIntrinsicSqrt:
       return backend->GenInlinedSqrt(info);
     case kIntrinsicCeil:
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 5ce110c..ac70577 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -162,6 +162,23 @@
       kNameCacheAbs,
       kNameCacheMax,
       kNameCacheMin,
+      kNameCacheCos,
+      kNameCacheSin,
+      kNameCacheAcos,
+      kNameCacheAsin,
+      kNameCacheAtan,
+      kNameCacheAtan2,
+      kNameCacheCbrt,
+      kNameCacheCosh,
+      kNameCacheExp,
+      kNameCacheExpm1,
+      kNameCacheHypot,
+      kNameCacheLog,
+      kNameCacheLog10,
+      kNameCacheNextAfter,
+      kNameCacheSinh,
+      kNameCacheTan,
+      kNameCacheTanh,
       kNameCacheSqrt,
       kNameCacheCeil,
       kNameCacheFloor,
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 05dde9f..3260a7a 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -780,14 +780,6 @@
   PassDriverMEOpts pass_driver(GetPreOptPassManager(), GetPostOptPassManager(), &cu);
   pass_driver.Launch();
 
-  /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */
-  if (cu.compiler_driver->ProfilePresent()
-      && !cu.mir_graph->MethodIsLeaf()
-      && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) {
-    cu.EndTiming();
-    return nullptr;
-  }
-
   if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
     cu.mir_graph->DumpCheckStats();
   }
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index 03bda78..2532bda 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -22,14 +22,10 @@
 
 namespace art {
 
-bool QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
-  bool result = verification_results_->ProcessVerifiedMethod(verifier);
-  if (result) {
-    MethodReference ref = verifier->GetMethodReference();
-    method_inliner_map_->GetMethodInliner(ref.dex_file)
-        ->AnalyseMethodCode(verifier);
-  }
-  return result;
+void QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
+  verification_results_->ProcessVerifiedMethod(verifier);
+  MethodReference ref = verifier->GetMethodReference();
+  method_inliner_map_->GetMethodInliner(ref.dex_file)->AnalyseMethodCode(verifier);
 }
 
 void QuickCompilerCallbacks::ClassRejected(ClassReference ref) {
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 03bf57b..4f5ea76 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -37,7 +37,7 @@
 
     ~QuickCompilerCallbacks() { }
 
-    bool MethodVerified(verifier::MethodVerifier* verifier)
+    void MethodVerified(verifier::MethodVerifier* verifier)
         SHARED_REQUIRES(Locks::mutator_lock_) OVERRIDE;
 
     void ClassRejected(ClassReference ref) OVERRIDE;
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 65b0ad6..dd24220 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -44,14 +44,14 @@
   }
 }
 
-bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
   DCHECK(method_verifier != nullptr);
   MethodReference ref = method_verifier->GetMethodReference();
   bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
   const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
   if (verified_method == nullptr) {
-    // Do not report an error to the verifier. We'll just punt this later.
-    return true;
+    // We'll punt this later.
+    return;
   }
 
   WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -69,11 +69,10 @@
     // is unsafe to replace the existing one since the JIT may be using it to generate a
     // native GC map.
     delete verified_method;
-    return true;
+    return;
   }
   verified_methods_.Put(ref, verified_method);
   DCHECK(verified_methods_.find(ref) != verified_methods_.end());
-  return true;
 }
 
 const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 9934f6b..da80bf0 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -42,7 +42,7 @@
     explicit VerificationResults(const CompilerOptions* compiler_options);
     ~VerificationResults();
 
-    bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
+    void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
         SHARED_REQUIRES(Locks::mutator_lock_)
         REQUIRES(!verified_methods_lock_);
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a05105b..ba8f1d0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -80,6 +80,9 @@
 // given, too all compilations.
 static constexpr bool kRestrictCompilationFiltersToImage = true;
 
+// Print additional info during profile guided compilation.
+static constexpr bool kDebugProfileGuidedCompilation = false;
+
 static double Percentage(size_t x, size_t y) {
   return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
 }
@@ -344,8 +347,7 @@
                                const std::string& dump_cfg_file_name, bool dump_cfg_append,
                                CumulativeLogger* timer, int swap_fd,
                                const std::string& profile_file)
-    : profile_present_(false),
-      compiler_options_(compiler_options),
+    : compiler_options_(compiler_options),
       verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
       compiler_(Compiler::Create(this, compiler_kind)),
@@ -383,12 +385,8 @@
 
   // Read the profile file if one is provided.
   if (!profile_file.empty()) {
-    profile_present_ = profile_file_.LoadFile(profile_file);
-    if (profile_present_) {
-      LOG(INFO) << "Using profile data form file " << profile_file;
-    } else {
-      LOG(INFO) << "Failed to load profile file " << profile_file;
-    }
+    profile_compilation_info_.reset(new ProfileCompilationInfo(profile_file));
+    LOG(INFO) << "Using profile data from file " << profile_file;
   }
 }
 
@@ -569,7 +567,9 @@
         (verified_method->GetEncounteredVerificationFailures() &
             (verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 &&
         // Is eligable for compilation by methods-to-compile filter.
-        driver->IsMethodToCompile(method_ref);
+        driver->IsMethodToCompile(method_ref) &&
+        driver->ShouldCompileBasedOnProfile(method_ref);
+
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return null.
       compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
@@ -766,6 +766,22 @@
   return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end();
 }
 
+bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const {
+  if (profile_compilation_info_ == nullptr) {
+    // If we miss profile information it means that we don't do a profile guided compilation.
+    // Return true, and let the other filters decide if the method should be compiled.
+    return true;
+  }
+  bool result = profile_compilation_info_->ContainsMethod(method_ref);
+
+  if (kDebugProfileGuidedCompilation) {
+    LOG(INFO) << "[ProfileGuidedCompilation] "
+        << (result ? "Compiled" : "Skipped") << " method:"
+        << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file, true);
+  }
+  return result;
+}
+
 class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
  public:
   ResolveCatchBlockExceptionsClassVisitor(
@@ -1134,15 +1150,21 @@
   // See also Compiler::ResolveDexFile
 
   bool result = false;
-  if (IsBootImage()) {
-    // We resolve all const-string strings when building for the image.
+  if (IsBootImage() || Runtime::Current()->UseJit()) {
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
     Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(
         soa.Self(), dex_file, false)));
-    class_linker->ResolveString(dex_file, string_idx, dex_cache);
-    result = true;
+    if (IsBootImage()) {
+      // We resolve all const-string strings when building for the image.
+      class_linker->ResolveString(dex_file, string_idx, dex_cache);
+      result = true;
+    } else {
+      // Just check whether the dex cache already has the string.
+      DCHECK(Runtime::Current()->UseJit());
+      result = (dex_cache->GetResolvedString(string_idx) != nullptr);
+    }
   }
   if (result) {
     stats_->StringInDexCache();
@@ -2037,6 +2059,7 @@
                                                 dex_cache,
                                                 class_loader,
                                                 &class_def,
+                                                Runtime::Current()->GetCompilerCallbacks(),
                                                 true /* allow soft failures */,
                                                 true /* log hard failures */,
                                                 &error_msg) ==
@@ -2273,6 +2296,16 @@
 
 void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                              ThreadPool* thread_pool, TimingLogger* timings) {
+  if (profile_compilation_info_ != nullptr) {
+    if (!profile_compilation_info_->Load(dex_files)) {
+      LOG(WARNING) << "Failed to load offline profile info from "
+          << profile_compilation_info_->GetFilename()
+          << ". No methods will be compiled";
+    } else if (kDebugProfileGuidedCompilation) {
+      LOG(INFO) << "[ProfileGuidedCompilation] "
+          << profile_compilation_info_->DumpInfo();
+    }
+  }
   for (size_t i = 0; i != dex_files.size(); ++i) {
     const DexFile* dex_file = dex_files[i];
     CHECK(dex_file != nullptr);
@@ -2510,39 +2543,6 @@
   return freezing_constructor_classes_.count(ClassReference(dex_file, class_def_index)) != 0;
 }
 
-bool CompilerDriver::SkipCompilation(const std::string& method_name) {
-  if (!profile_present_) {
-    return false;
-  }
-  // First find the method in the profile file.
-  ProfileFile::ProfileData data;
-  if (!profile_file_.GetProfileData(&data, method_name)) {
-    // Not in profile, no information can be determined.
-    if (kIsDebugBuild) {
-      VLOG(compiler) << "not compiling " << method_name << " because it's not in the profile";
-    }
-    return true;
-  }
-
-  // Methods that comprise top_k_threshold % of the total samples will be compiled.
-  // Compare against the start of the topK percentage bucket just in case the threshold
-  // falls inside a bucket.
-  bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent()
-                 <= compiler_options_->GetTopKProfileThreshold();
-  if (kIsDebugBuild) {
-    if (compile) {
-      LOG(INFO) << "compiling method " << method_name << " because its usage is part of top "
-          << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%"
-          << " (topKThreshold=" << compiler_options_->GetTopKProfileThreshold() << ")";
-    } else {
-      VLOG(compiler) << "not compiling method " << method_name
-          << " because it's not part of leading " << compiler_options_->GetTopKProfileThreshold()
-          << "% samples)";
-    }
-  }
-  return !compile;
-}
-
 std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
   std::ostringstream oss;
   Runtime* const runtime = Runtime::Current();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 1347b37..f0360ce 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -31,11 +31,11 @@
 #include "compiler.h"
 #include "dex_file.h"
 #include "driver/compiled_method_storage.h"
+#include "jit/offline_profiling_info.h"
 #include "invoke_type.h"
 #include "method_reference.h"
 #include "mirror/class.h"  // For mirror::Class::Status.
 #include "os.h"
-#include "profiler.h"
 #include "runtime.h"
 #include "safe_map.h"
 #include "thread_pool.h"
@@ -147,10 +147,6 @@
     return compiler_.get();
   }
 
-  bool ProfilePresent() const {
-    return profile_present_;
-  }
-
   // Are we compiling and creating an image file?
   bool IsBootImage() const {
     return boot_image_;
@@ -161,16 +157,11 @@
   }
 
   // Generate the trampolines that are invoked by unresolved direct methods.
-  const std::vector<uint8_t>* CreateJniDlsymLookup() const
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const
-      SHARED_REQUIRES(Locks::mutator_lock_);
+  const std::vector<uint8_t>* CreateJniDlsymLookup() const;
+  const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const;
+  const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const;
+  const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const;
+  const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const;
 
   CompiledClass* GetCompiledClass(ClassReference ref) const
       REQUIRES(!compiled_classes_lock_);
@@ -445,6 +436,10 @@
   // Checks whether the provided method should be compiled, i.e., is in method_to_compile_.
   bool IsMethodToCompile(const MethodReference& method_ref) const;
 
+  // Checks whether profile guided compilation is enabled and if the method should be compiled
+  // according to the profile file.
+  bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const;
+
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       REQUIRES(!compiled_classes_lock_);
 
@@ -454,9 +449,6 @@
                                        uint16_t class_def_idx,
                                        const DexFile& dex_file) const;
 
-  // Should the compiler run on this method given profile information?
-  bool SkipCompilation(const std::string& method_name);
-
   // Get memory usage during compilation.
   std::string GetMemoryUsageString(bool extended) const;
 
@@ -595,9 +587,6 @@
                       ThreadPool* thread_pool, TimingLogger* timings)
       REQUIRES(!Locks::mutator_lock_);
 
-  ProfileFile profile_file_;
-  bool profile_present_;
-
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
   DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -647,6 +636,9 @@
   // This option may be restricted to the boot image, depending on a flag in the implementation.
   std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
 
+  // Info for profile guided compilation.
+  std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
+
   bool had_hard_verifier_failure_;
 
   size_t thread_count_;
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 9dc6565..e806acf 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -293,12 +293,137 @@
   }
 }
 
-struct CompilationUnit {
-  std::vector<const MethodDebugInfo*> methods_;
-  size_t debug_line_offset_ = 0;
-  uint32_t low_pc_ = 0xFFFFFFFFU;
-  uint32_t high_pc_ = 0;
-};
+namespace {
+  struct CompilationUnit {
+    std::vector<const MethodDebugInfo*> methods_;
+    size_t debug_line_offset_ = 0;
+    uint32_t low_pc_ = 0xFFFFFFFFU;
+    uint32_t high_pc_ = 0;
+  };
+
+  struct LocalVariable {
+    uint16_t vreg;
+    uint32_t dex_pc_low;
+    uint32_t dex_pc_high;
+    const char* name;
+    const char* type;
+    const char* sig;
+  };
+
+  struct DebugInfoCallback {
+    static void NewLocal(void* ctx,
+                         uint16_t vreg,
+                         uint32_t start,
+                         uint32_t end,
+                         const char* name,
+                         const char* type,
+                         const char* sig) {
+      auto* context = static_cast<DebugInfoCallback*>(ctx);
+      if (name != nullptr && type != nullptr) {
+        context->local_variables_.push_back({vreg, start, end, name, type, sig});
+      }
+    }
+    std::vector<LocalVariable> local_variables_;
+  };
+
+  std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
+    std::vector<const char*> names;
+    const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
+    if (stream != nullptr) {
+      DecodeUnsignedLeb128(&stream);  // line.
+      uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+      for (uint32_t i = 0; i < parameters_size; ++i) {
+        uint32_t id = DecodeUnsignedLeb128P1(&stream);
+        names.push_back(mi->dex_file_->StringDataByIdx(id));
+      }
+    }
+    return names;
+  }
+
+  struct VariableLocation {
+    uint32_t low_pc;
+    uint32_t high_pc;
+    DexRegisterLocation reg_lo;  // May be None if the location is unknown.
+    DexRegisterLocation reg_hi;  // Most significant bits of 64-bit value.
+  };
+
+  // Get the location of given dex register (e.g. stack or machine register).
+  // Note that the location might be different based on the current pc.
+  // The result will cover all ranges where the variable is in scope.
+  std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
+                                                     uint16_t vreg,
+                                                     bool is64bitValue,
+                                                     uint32_t dex_pc_low,
+                                                     uint32_t dex_pc_high) {
+    std::vector<VariableLocation> variable_locations;
+
+    // Get stack maps sorted by pc (they might not be sorted internally).
+    const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
+    const StackMapEncoding encoding = code_info.ExtractEncoding();
+    std::map<uint32_t, StackMap> stack_maps;
+    for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+      StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+      DCHECK(stack_map.IsValid());
+      const uint32_t low_pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
+      DCHECK_LE(low_pc, method_info->high_pc_);
+      stack_maps.emplace(low_pc, stack_map);
+    }
+
+    // Create entries for the requested register based on stack map data.
+    for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
+      const StackMap& stack_map = it->second;
+      const uint32_t low_pc = it->first;
+      auto next_it = it;
+      next_it++;
+      const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
+                                                           : method_info->high_pc_;
+      DCHECK_LE(low_pc, high_pc);
+      if (low_pc == high_pc) {
+        continue;  // Ignore if the address range is empty.
+      }
+
+      // Check that the stack map is in the requested range.
+      uint32_t dex_pc = stack_map.GetDexPc(encoding);
+      if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
+        continue;
+      }
+
+      // Find the location of the dex register.
+      DexRegisterLocation reg_lo = DexRegisterLocation::None();
+      DexRegisterLocation reg_hi = DexRegisterLocation::None();
+      if (stack_map.HasDexRegisterMap(encoding)) {
+        DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+            stack_map, encoding, method_info->code_item_->registers_size_);
+        reg_lo = dex_register_map.GetDexRegisterLocation(
+            vreg, method_info->code_item_->registers_size_, code_info, encoding);
+        if (is64bitValue) {
+          reg_hi = dex_register_map.GetDexRegisterLocation(
+              vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
+        }
+      }
+
+      // Add location entry for this address range.
+      if (!variable_locations.empty() &&
+          variable_locations.back().reg_lo == reg_lo &&
+          variable_locations.back().reg_hi == reg_hi &&
+          variable_locations.back().high_pc == low_pc) {
+        // Merge with the previous entry (extend its range).
+        variable_locations.back().high_pc = high_pc;
+      } else {
+        variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
+      }
+    }
+
+    return variable_locations;
+  }
+
+  bool IsFromOptimizingCompiler(const MethodDebugInfo* method_info) {
+    return method_info->compiled_method_->GetQuickCode().size() > 0 &&
+           method_info->compiled_method_->GetVmapTable().size() > 0 &&
+           method_info->compiled_method_->GetGcMap().size() == 0 &&
+           method_info->code_item_ != nullptr;
+  }
+}  // namespace
 
 // Helper class to write .debug_info and its supporting sections.
 template<typename ElfTypes>
@@ -321,6 +446,7 @@
       info_.StartTag(DW_TAG_compile_unit);
       info_.WriteStrp(DW_AT_producer, owner_->WriteString("Android dex2oat"));
       info_.WriteData1(DW_AT_language, DW_LANG_Java);
+      info_.WriteStrp(DW_AT_comp_dir, owner_->WriteString("$JAVA_SRC_ROOT"));
       info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_);
       info_.WriteUdata(DW_AT_high_pc, compilation_unit.high_pc_ - compilation_unit.low_pc_);
       info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset_);
@@ -332,6 +458,7 @@
         const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
         const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
         const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
+        const bool is_static = (mi->access_flags_ & kAccStatic) != 0;
 
         // Enclose the method in correct class definition.
         if (last_dex_class_desc != dex_class_desc) {
@@ -346,17 +473,17 @@
           last_dex_class_desc = dex_class_desc;
         }
 
+        // Collect information about local variables and parameters.
+        DebugInfoCallback debug_info_callback;
         std::vector<const char*> param_names;
         if (mi->code_item_ != nullptr) {
-          const uint8_t* stream = dex->GetDebugInfoStream(mi->code_item_);
-          if (stream != nullptr) {
-            DecodeUnsignedLeb128(&stream);  // line.
-            uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-            for (uint32_t i = 0; i < parameters_size; ++i) {
-              uint32_t id = DecodeUnsignedLeb128P1(&stream);
-              param_names.push_back(mi->dex_file_->StringDataByIdx(id));
-            }
-          }
+          dex->DecodeDebugInfo(mi->code_item_,
+                               is_static,
+                               mi->dex_method_index_,
+                               nullptr,
+                               DebugInfoCallback::NewLocal,
+                               &debug_info_callback);
+          param_names = GetParamNames(mi);
         }
 
         int start_depth = info_.Depth();
@@ -367,19 +494,19 @@
         uint8_t frame_base[] = { DW_OP_call_frame_cfa };
         info_.WriteExprLoc(DW_AT_frame_base, &frame_base, sizeof(frame_base));
         WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
+        uint32_t vreg = mi->code_item_ == nullptr ? 0 :
+            mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+        if (!is_static) {
+          info_.StartTag(DW_TAG_formal_parameter);
+          WriteName("this");
+          info_.WriteFlag(DW_AT_artificial, true);
+          WriteLazyType(dex_class_desc);
+          const bool is64bitValue = false;
+          WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+          vreg++;
+          info_.EndTag();
+        }
         if (dex_params != nullptr) {
-          uint32_t vreg = mi->code_item_ == nullptr ? 0 :
-              mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
-          if ((mi->access_flags_ & kAccStatic) == 0) {
-            info_.StartTag(DW_TAG_formal_parameter);
-            WriteName("this");
-            info_.WriteFlag(DW_AT_artificial, true);
-            WriteLazyType(dex_class_desc);
-            const bool is64bitValue = false;
-            WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
-            vreg++;
-            info_.EndTag();
-          }
           for (uint32_t i = 0; i < dex_params->Size(); ++i) {
             info_.StartTag(DW_TAG_formal_parameter);
             // Parameter names may not be always available.
@@ -399,6 +526,18 @@
             CHECK_EQ(vreg, mi->code_item_->registers_size_);
           }
         }
+        for (const LocalVariable& var : debug_info_callback.local_variables_) {
+          const uint32_t first_arg = mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+          if (var.vreg < first_arg) {
+            info_.StartTag(DW_TAG_variable);
+            WriteName(var.name);
+            WriteLazyType(var.type);
+            bool is64bitValue = var.type[0] == 'D' || var.type[0] == 'J';
+            WriteRegLocation(mi, var.vreg, is64bitValue, compilation_unit.low_pc_,
+                             var.dex_pc_low, var.dex_pc_high);
+            info_.EndTag();
+          }
+        }
         info_.EndTag();
         CHECK_EQ(info_.Depth(), start_depth);  // Balanced start/end.
       }
@@ -420,57 +559,40 @@
     // Write table into .debug_loc which describes location of dex register.
     // The dex register might be valid only at some points and it might
     // move between machine registers and stack.
-    void WriteRegLocation(const MethodDebugInfo* method_info, uint16_t vreg,
-                          bool is64bitValue, uint32_t compilation_unit_low_pc) {
+    void WriteRegLocation(const MethodDebugInfo* method_info,
+                          uint16_t vreg,
+                          bool is64bitValue,
+                          uint32_t compilation_unit_low_pc,
+                          uint32_t dex_pc_low = 0,
+                          uint32_t dex_pc_high = 0xFFFFFFFF) {
       using Kind = DexRegisterLocation::Kind;
-      bool is_optimizing = method_info->compiled_method_->GetQuickCode().size() > 0 &&
-                           method_info->compiled_method_->GetVmapTable().size() > 0 &&
-                           method_info->compiled_method_->GetGcMap().size() == 0 &&
-                           method_info->code_item_ != nullptr;
-      if (!is_optimizing) {
+      if (!IsFromOptimizingCompiler(method_info)) {
         return;
       }
 
-      Writer<> writer(&owner_->debug_loc_);
-      info_.WriteSecOffset(DW_AT_location, writer.size());
+      Writer<> debug_loc(&owner_->debug_loc_);
+      Writer<> debug_ranges(&owner_->debug_ranges_);
+      info_.WriteSecOffset(DW_AT_location, debug_loc.size());
+      info_.WriteSecOffset(DW_AT_start_scope, debug_ranges.size());
 
+      std::vector<VariableLocation> variable_locations = GetVariableLocations(
+          method_info,
+          vreg,
+          is64bitValue,
+          dex_pc_low,
+          dex_pc_high);
+
+      // Write .debug_loc entries.
       const InstructionSet isa = owner_->builder_->GetIsa();
       const bool is64bit = Is64BitInstructionSet(isa);
-      const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
-      const StackMapEncoding encoding = code_info.ExtractEncoding();
-      DexRegisterLocation last_reg_lo = DexRegisterLocation::None();
-      DexRegisterLocation last_reg_hi = DexRegisterLocation::None();
-      size_t offset_of_last_end_address = 0;
-      for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
-        StackMap stack_map = code_info.GetStackMapAt(s, encoding);
-        DCHECK(stack_map.IsValid());
-
-        // Find the location of the dex register.
-        DexRegisterLocation reg_lo = DexRegisterLocation::None();
-        DexRegisterLocation reg_hi = DexRegisterLocation::None();
-        if (stack_map.HasDexRegisterMap(encoding)) {
-          DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
-              stack_map, encoding, method_info->code_item_->registers_size_);
-          reg_lo = dex_register_map.GetDexRegisterLocation(
-              vreg, method_info->code_item_->registers_size_, code_info, encoding);
-          if (is64bitValue) {
-            reg_hi = dex_register_map.GetDexRegisterLocation(
-                vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
-          }
-        }
-        if ((reg_lo == last_reg_lo && reg_hi == last_reg_hi) ||
-            reg_lo.GetKind() == Kind::kNone) {
-          // Skip identical or undefined locations.
-          continue;
-        }
-        last_reg_lo = reg_lo;
-        last_reg_hi = reg_hi;
-
+      for (const VariableLocation& variable_location : variable_locations) {
         // Translate dex register location to DWARF expression.
         // Note that 64-bit value might be split to two distinct locations.
         // (for example, two 32-bit machine registers, or even stack and register)
         uint8_t buffer[64];
         uint8_t* pos = buffer;
+        DexRegisterLocation reg_lo = variable_location.reg_lo;
+        DexRegisterLocation reg_hi = variable_location.reg_hi;
         for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
           DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
           const Kind kind = reg_loc.GetKind();
@@ -529,43 +651,56 @@
           }
         }
 
-        // Write end address for previous entry.
-        const uint32_t pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
-        if (offset_of_last_end_address != 0) {
-          if (is64bit) {
-            writer.UpdateUint64(offset_of_last_end_address, pc - compilation_unit_low_pc);
-          } else {
-            writer.UpdateUint32(offset_of_last_end_address, pc - compilation_unit_low_pc);
-          }
-        }
-        offset_of_last_end_address = 0;
-
-        DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer));
+        // Check that the buffer is large enough; keep half of it empty for safety.
+        DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer) / 2);
         if (pos > buffer) {
-          // Write start/end address.
           if (is64bit) {
-            writer.PushUint64(pc - compilation_unit_low_pc);
-            offset_of_last_end_address = writer.size();
-            writer.PushUint64(method_info->high_pc_ - compilation_unit_low_pc);
+            debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
+            debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
           } else {
-            writer.PushUint32(pc - compilation_unit_low_pc);
-            offset_of_last_end_address = writer.size();
-            writer.PushUint32(method_info->high_pc_ - compilation_unit_low_pc);
+            debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
+            debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
           }
           // Write the expression.
-          writer.PushUint16(pos - buffer);
-          writer.PushData(buffer, pos - buffer);
+          debug_loc.PushUint16(pos - buffer);
+          debug_loc.PushData(buffer, pos - buffer);
         } else {
-          // Otherwise leave the address range undefined.
+          // Do not generate .debug_loc if the location is not known.
         }
       }
       // Write end-of-list entry.
       if (is64bit) {
-        writer.PushUint64(0);
-        writer.PushUint64(0);
+        debug_loc.PushUint64(0);
+        debug_loc.PushUint64(0);
       } else {
-        writer.PushUint32(0);
-        writer.PushUint32(0);
+        debug_loc.PushUint32(0);
+        debug_loc.PushUint32(0);
+      }
+
+      // Write .debug_ranges entries.
+      // This includes ranges where the variable is in scope but the location is not known.
+      for (size_t i = 0; i < variable_locations.size(); i++) {
+        uint32_t low_pc = variable_locations[i].low_pc;
+        uint32_t high_pc = variable_locations[i].high_pc;
+        while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
+          // Merge address range with the next entry.
+          high_pc = variable_locations[++i].high_pc;
+        }
+        if (is64bit) {
+          debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
+          debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
+        } else {
+          debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
+          debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
+        }
+      }
+      // Write end-of-list entry.
+      if (is64bit) {
+        debug_ranges.PushUint64(0);
+        debug_ranges.PushUint64(0);
+      } else {
+        debug_ranges.PushUint32(0);
+        debug_ranges.PushUint32(0);
       }
     }
 
@@ -748,6 +883,7 @@
     builder_->WriteSection(".debug_abbrev", &debug_abbrev_.Data());
     builder_->WriteSection(".debug_str", &debug_str_.Data());
     builder_->WriteSection(".debug_loc", &debug_loc_);
+    builder_->WriteSection(".debug_ranges", &debug_ranges_);
   }
 
  private:
@@ -760,6 +896,7 @@
   DedupVector debug_abbrev_;
   DedupVector debug_str_;
   std::vector<uint8_t> debug_loc_;
+  std::vector<uint8_t> debug_ranges_;
 
   std::unordered_set<const char*> defined_dex_classes_;  // For CHECKs only.
 };
@@ -807,10 +944,6 @@
         break;
     }
     DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
-    opcodes.SetAddress(text_address + compilation_unit.low_pc_);
-    if (dwarf_isa != -1) {
-      opcodes.SetISA(dwarf_isa);
-    }
     for (const MethodDebugInfo* mi : compilation_unit.methods_) {
       // Ignore function if we have already generated line table for the same address.
       // It would confuse the debugger and the DWARF specification forbids it.
@@ -818,9 +951,35 @@
         continue;
       }
 
+      ArrayRef<const SrcMapElem> src_mapping_table;
+      std::vector<SrcMapElem> src_mapping_table_from_stack_maps;
+      if (IsFromOptimizingCompiler(mi)) {
+        // Use stack maps to create mapping table from pc to dex.
+        const CodeInfo code_info(mi->compiled_method_->GetVmapTable().data());
+        const StackMapEncoding encoding = code_info.ExtractEncoding();
+        for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+          StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+          DCHECK(stack_map.IsValid());
+          const uint32_t pc = stack_map.GetNativePcOffset(encoding);
+          const int32_t dex = stack_map.GetDexPc(encoding);
+          src_mapping_table_from_stack_maps.push_back({pc, dex});
+        }
+        std::sort(src_mapping_table_from_stack_maps.begin(),
+                  src_mapping_table_from_stack_maps.end());
+        src_mapping_table = ArrayRef<const SrcMapElem>(src_mapping_table_from_stack_maps);
+      } else {
+        // Use the mapping table provided by the quick compiler.
+        src_mapping_table = mi->compiled_method_->GetSrcMappingTable();
+      }
+
+      if (src_mapping_table.empty()) {
+        continue;
+      }
+
+      // Create mapping table from dex to source line.
       struct DebugInfoCallbacks {
         static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
-          auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
+          auto* context = static_cast<DebugInfoCallbacks*>(ctx);
           context->dex2line_.push_back({address, static_cast<int32_t>(line)});
           return false;
         }
@@ -839,6 +998,15 @@
                              &debug_info_callbacks);
       }
 
+      if (debug_info_callbacks.dex2line_.empty()) {
+        continue;
+      }
+
+      opcodes.SetAddress(method_address);
+      if (dwarf_isa != -1) {
+        opcodes.SetISA(dwarf_isa);
+      }
+
       // Get and deduplicate directory and filename.
       int file_index = 0;  // 0 - primary source file of the compilation.
       auto& dex_class_def = dex->GetClassDef(mi->class_def_index_);
@@ -888,7 +1056,7 @@
       const DefaultSrcMap& dex2line_map = debug_info_callbacks.dex2line_;
       if (file_index != 0 && !dex2line_map.empty()) {
         bool first = true;
-        for (SrcMapElem pc2dex : mi->compiled_method_->GetSrcMappingTable()) {
+        for (SrcMapElem pc2dex : src_mapping_table) {
           uint32_t pc = pc2dex.from_;
           int dex_pc = pc2dex.to_;
           auto dex2line = dex2line_map.Find(static_cast<uint32_t>(dex_pc));
@@ -915,9 +1083,10 @@
         // line 0 - instruction cannot be attributed to any source line.
         opcodes.AddRow(method_address, 0);
       }
+
+      opcodes.AdvancePC(text_address + mi->high_pc_);
+      opcodes.EndSequence();
     }
-    opcodes.AdvancePC(text_address + compilation_unit.high_pc_);
-    opcodes.EndSequence();
     std::vector<uint8_t> buffer;
     buffer.reserve(opcodes.data()->size() + KB);
     size_t offset = builder_->GetDebugLine()->GetSize();
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index cda6240..15812dc 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -43,10 +43,17 @@
     ReserveImageSpace();
     CommonCompilerTest::SetUp();
   }
+  void TestWriteRead(ImageHeader::StorageMode storage_mode);
 };
 
-TEST_F(ImageTest, WriteRead) {
-  TEST_DISABLED_FOR_NON_PIC_COMPILING_WITH_OPTIMIZING();
+void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
+  // TODO: Test does not currently work with optimizing.
+  CreateCompilerDriver(Compiler::kQuick, kRuntimeISA);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  // Enable write for dex2dex.
+  for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
+    dex_file->EnableWrite();
+  }
   // Create a generic location tmp file, to be the base of the .art and .oat temporary files.
   ScratchFile location;
   ScratchFile image_location(location, ".art");
@@ -68,17 +75,14 @@
   std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_,
                                                       requested_image_base,
                                                       /*compile_pic*/false,
-                                                      /*compile_app_image*/false));
+                                                      /*compile_app_image*/false,
+                                                      storage_mode));
   // TODO: compile_pic should be a test argument.
   {
     {
       jobject class_loader = nullptr;
-      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       TimingLogger timings("ImageTest::WriteRead", false, false);
       TimingLogger::ScopedTiming t("CompileAll", &timings);
-      for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
-        dex_file->EnableWrite();
-      }
       compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
       compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
 
@@ -113,14 +117,9 @@
       elf_writer->EndText(text);
 
       elf_writer->SetBssSize(oat_writer.GetBssSize());
-
       elf_writer->WriteDynamicSection();
-
-      ArrayRef<const dwarf::MethodDebugInfo> method_infos(oat_writer.GetMethodDebugInfo());
-      elf_writer->WriteDebugInfo(method_infos);
-
-      ArrayRef<const uintptr_t> patch_locations(oat_writer.GetAbsolutePatchLocations());
-      elf_writer->WritePatchLocations(patch_locations);
+      elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
+      elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
 
       success = elf_writer->End();
 
@@ -209,7 +208,13 @@
 
   gc::space::ImageSpace* image_space = heap->GetBootImageSpace();
   ASSERT_TRUE(image_space != nullptr);
-  ASSERT_LE(image_space->Size(), image_file_size);
+  if (storage_mode == ImageHeader::kStorageModeUncompressed) {
+    // Uncompressed, image should be smaller than file.
+    ASSERT_LE(image_space->Size(), image_file_size);
+  } else {
+    // Compressed, file should be smaller than image.
+    ASSERT_LE(image_file_size, image_space->Size());
+  }
 
   image_space->VerifyImageAllocations();
   uint8_t* image_begin = image_space->Begin();
@@ -237,6 +242,14 @@
   CHECK_EQ(0, rmdir_result);
 }
 
+TEST_F(ImageTest, WriteReadUncompressed) {
+  TestWriteRead(ImageHeader::kStorageModeUncompressed);
+}
+
+TEST_F(ImageTest, WriteReadLZ4) {
+  TestWriteRead(ImageHeader::kStorageModeLZ4);
+}
+
 TEST_F(ImageTest, ImageHeaderIsValid) {
     uint32_t image_begin = ART_BASE_ADDRESS;
     uint32_t image_size_ = 16 * KB;
@@ -257,7 +270,9 @@
                              oat_data_end,
                              oat_file_end,
                              sizeof(void*),
-                             /*compile_pic*/false);
+                             /*compile_pic*/false,
+                             ImageHeader::kDefaultStorageMode,
+                             /*data_size*/0u);
     ASSERT_TRUE(image_header.IsValid());
 
     char* magic = const_cast<char*>(image_header.GetMagic());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index bf1fcdd..f9f0eb8 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -17,6 +17,7 @@
 #include "image_writer.h"
 
 #include <sys/stat.h>
+#include <lz4.h>
 
 #include <memory>
 #include <numeric>
@@ -225,27 +226,72 @@
     return EXIT_FAILURE;
   }
 
-  // Write out the image + fields + methods.
+  std::unique_ptr<char[]> compressed_data;
+  // Image data size excludes the bitmap and the header.
   ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
-  const auto write_count = image_header->GetImageSize();
-  if (!image_file->WriteFully(image_->Begin(), write_count)) {
-    PLOG(ERROR) << "Failed to write image file " << image_filename;
+  const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader);
+  char* image_data = reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader);
+  size_t data_size;
+  const char* image_data_to_write;
+
+  CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
+  switch (image_storage_mode_) {
+    case ImageHeader::kStorageModeLZ4: {
+      size_t compressed_max_size = LZ4_compressBound(image_data_size);
+      compressed_data.reset(new char[compressed_max_size]);
+      data_size = LZ4_compress(
+          reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader),
+          &compressed_data[0],
+          image_data_size);
+      image_data_to_write = &compressed_data[0];
+      VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size;
+      break;
+    }
+    case ImageHeader::kStorageModeUncompressed: {
+      data_size = image_data_size;
+      image_data_to_write = image_data;
+      break;
+    }
+    default: {
+      LOG(FATAL) << "Unsupported";
+      UNREACHABLE();
+    }
+  }
+
+  // Write header first, as uncompressed.
+  image_header->data_size_ = data_size;
+  if (!image_file->WriteFully(image_->Begin(), sizeof(ImageHeader))) {
+    PLOG(ERROR) << "Failed to write image file header " << image_filename;
     image_file->Erase();
     return false;
   }
 
-  // Write out the image bitmap at the page aligned start of the image end.
+  // Write out the image + fields + methods.
+  const bool is_compressed = compressed_data != nullptr;
+  if (!image_file->WriteFully(image_data_to_write, data_size)) {
+    PLOG(ERROR) << "Failed to write image file data " << image_filename;
+    image_file->Erase();
+    return false;
+  }
+
+  // Write out the image bitmap at the page aligned start of the image end, also uncompressed for
+  // convenience.
   const ImageSection& bitmap_section = image_header->GetImageSection(
       ImageHeader::kSectionImageBitmap);
-  CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
+  // Align up since data size may be unaligned if the image is compressed.
+  size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize);
+  if (!is_compressed) {
+    CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
+  }
   if (!image_file->Write(reinterpret_cast<char*>(image_bitmap_->Begin()),
-                         bitmap_section.Size(), bitmap_section.Offset())) {
+                         bitmap_section.Size(),
+                         bitmap_position_in_file)) {
     PLOG(ERROR) << "Failed to write image file " << image_filename;
     image_file->Erase();
     return false;
   }
-
-  CHECK_EQ(bitmap_section.End(), static_cast<size_t>(image_file->GetLength()));
+  CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
+           static_cast<size_t>(image_file->GetLength()));
   if (image_file->FlushCloseOrErase() != 0) {
     PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
     return false;
@@ -1247,7 +1293,8 @@
   }
   CHECK_EQ(AlignUp(image_begin_ + image_end, kPageSize), oat_file_begin) <<
       "Oat file should be right after the image.";
-  // Create the header.
+  // Create the header, leave 0 for data size since we will fill this in as we are writing the
+  // image.
   new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_),
                                                           image_end,
                                                           sections,
@@ -1258,7 +1305,9 @@
                                                           PointerToLowMemUInt32(oat_data_end),
                                                           PointerToLowMemUInt32(oat_file_end),
                                                           target_ptr_size_,
-                                                          compile_pic_);
+                                                          compile_pic_,
+                                                          image_storage_mode_,
+                                                          /*data_size*/0u);
 }
 
 ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) {
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 386838f..c20d836 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -30,6 +30,7 @@
 #include "base/macros.h"
 #include "driver/compiler_driver.h"
 #include "gc/space/space.h"
+#include "image.h"
 #include "length_prefixed_array.h"
 #include "lock_word.h"
 #include "mem_map.h"
@@ -54,7 +55,8 @@
   ImageWriter(const CompilerDriver& compiler_driver,
               uintptr_t image_begin,
               bool compile_pic,
-              bool compile_app_image)
+              bool compile_app_image,
+              ImageHeader::StorageMode image_storage_mode)
       : compiler_driver_(compiler_driver),
         image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
         image_end_(0),
@@ -73,7 +75,8 @@
         image_method_array_(ImageHeader::kImageMethodsCount),
         dirty_methods_(0u),
         clean_methods_(0u),
-        class_table_bytes_(0u) {
+        class_table_bytes_(0u),
+        image_storage_mode_(image_storage_mode) {
     CHECK_NE(image_begin, 0U);
     std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
     std::fill_n(oat_address_offsets_, arraysize(oat_address_offsets_), 0);
@@ -460,6 +463,9 @@
   // Number of image class table bytes.
   size_t class_table_bytes_;
 
+  // Which mode the image is stored as, see image.h
+  const ImageHeader::StorageMode image_storage_mode_;
+
   friend class ContainsBootClassLoaderNonImageClassVisitor;
   friend class FixupClassVisitor;
   friend class FixupRootVisitor;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index b8610d0..a2b29a3 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -157,14 +157,9 @@
     elf_writer->EndText(text);
 
     elf_writer->SetBssSize(oat_writer.GetBssSize());
-
     elf_writer->WriteDynamicSection();
-
-    ArrayRef<const dwarf::MethodDebugInfo> method_infos(oat_writer.GetMethodDebugInfo());
-    elf_writer->WriteDebugInfo(method_infos);
-
-    ArrayRef<const uintptr_t> patch_locations(oat_writer.GetAbsolutePatchLocations());
-    elf_writer->WritePatchLocations(patch_locations);
+    elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
+    elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
 
     return elf_writer->End();
   }
@@ -260,7 +255,7 @@
   EXPECT_EQ(72U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(114 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+  EXPECT_EQ(131 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
 }
 
 TEST_F(OatTest, OatHeaderIsValid) {
@@ -269,14 +264,9 @@
     std::unique_ptr<const InstructionSetFeatures> insn_features(
         InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
     ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
-    std::vector<const DexFile*> dex_files;
-    uint32_t image_file_location_oat_checksum = 0;
-    uint32_t image_file_location_oat_begin = 0;
     std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set,
                                                             insn_features.get(),
-                                                            &dex_files,
-                                                            image_file_location_oat_checksum,
-                                                            image_file_location_oat_begin,
+                                                            0u,
                                                             nullptr));
     ASSERT_NE(oat_header.get(), nullptr);
     ASSERT_TRUE(oat_header->IsValid());
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index e8e775f..2b2f0e8 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -52,6 +52,97 @@
 
 namespace art {
 
+class OatWriter::OatClass {
+ public:
+  OatClass(size_t offset,
+           const dchecked_vector<CompiledMethod*>& compiled_methods,
+           uint32_t num_non_null_compiled_methods,
+           mirror::Class::Status status);
+  OatClass(OatClass&& src) = default;
+  size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
+  size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
+  size_t SizeOf() const;
+  bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+  CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
+    return compiled_methods_[class_def_method_index];
+  }
+
+  // Offset of start of OatClass from beginning of OatHeader. It is
+  // used to validate file position when writing.
+  size_t offset_;
+
+  // CompiledMethods for each class_def_method_index, or null if no method is available.
+  dchecked_vector<CompiledMethod*> compiled_methods_;
+
+  // Offset from OatClass::offset_ to the OatMethodOffsets for the
+  // class_def_method_index. If 0, it means the corresponding
+  // CompiledMethod entry in OatClass::compiled_methods_ should be
+  // null and that the OatClass::type_ should be kOatClassBitmap.
+  dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
+
+  // Data to write.
+
+  static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
+  int16_t status_;
+
+  static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
+  uint16_t type_;
+
+  uint32_t method_bitmap_size_;
+
+  // bit vector indexed by ClassDef method index. When
+  // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
+  // method has an OatMethodOffsets in methods_offsets_, otherwise
+  // the entry was ommited to save space. If OatClassType::type_ is
+  // not is kOatClassBitmap, the bitmap will be null.
+  std::unique_ptr<BitVector> method_bitmap_;
+
+  // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+  // present in the OatClass. Note that some may be missing if
+  // OatClass::compiled_methods_ contains null values (and
+  // oat_method_offsets_offsets_from_oat_class_ should contain 0
+  // values in this case).
+  dchecked_vector<OatMethodOffsets> method_offsets_;
+  dchecked_vector<OatQuickMethodHeader> method_headers_;
+
+ private:
+  size_t GetMethodOffsetsRawSize() const {
+    return method_offsets_.size() * sizeof(method_offsets_[0]);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(OatClass);
+};
+
+class OatWriter::OatDexFile {
+ public:
+  OatDexFile(size_t offset, const DexFile& dex_file);
+  OatDexFile(OatDexFile&& src) = default;
+
+  size_t SizeOf() const;
+  bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+  // Offset of start of OatDexFile from beginning of OatHeader. It is
+  // used to validate file position when writing.
+  size_t offset_;
+
+  // Data to write.
+  uint32_t dex_file_location_size_;
+  const uint8_t* dex_file_location_data_;
+  uint32_t dex_file_location_checksum_;
+  uint32_t dex_file_offset_;
+  uint32_t lookup_table_offset_;
+  TypeLookupTable* lookup_table_;  // Owned by the dex file.
+  dchecked_vector<uint32_t> class_offsets_;
+
+ private:
+  size_t GetClassOffsetsRawSize() const {
+    return class_offsets_.size() * sizeof(class_offsets_[0]);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(OatDexFile);
+};
+
 #define DCHECK_OFFSET() \
   DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
     << "file_offset=" << file_offset << " relative_offset=" << relative_offset
@@ -106,14 +197,14 @@
     size_oat_dex_file_location_data_(0),
     size_oat_dex_file_location_checksum_(0),
     size_oat_dex_file_offset_(0),
-    size_oat_dex_file_methods_offsets_(0),
+    size_oat_dex_file_lookup_table_offset_(0),
+    size_oat_dex_file_class_offsets_(0),
+    size_oat_lookup_table_alignment_(0),
+    size_oat_lookup_table_(0),
     size_oat_class_type_(0),
     size_oat_class_status_(0),
     size_oat_class_method_bitmaps_(0),
     size_oat_class_method_offsets_(0),
-    size_oat_lookup_table_alignment_(0),
-    size_oat_lookup_table_offset_(0),
-    size_oat_lookup_table_(0),
     method_offset_map_() {
   CHECK(key_value_store != nullptr);
   if (compiling_boot_image) {
@@ -180,9 +271,6 @@
 }
 
 OatWriter::~OatWriter() {
-  delete oat_header_;
-  STLDeleteElements(&oat_dex_files_);
-  STLDeleteElements(&oat_classes_);
 }
 
 struct OatWriter::GcMapDataAccess {
@@ -326,6 +414,11 @@
     : DexMethodVisitor(writer, offset),
       compiled_methods_(),
       num_non_null_compiled_methods_(0u) {
+    size_t num_classes = 0u;
+    for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) {
+      num_classes += oat_dex_file.class_offsets_.size();
+    }
+    writer_->oat_classes_.reserve(num_classes);
     compiled_methods_.reserve(256u);
   }
 
@@ -364,16 +457,16 @@
       status = mirror::Class::kStatusNotReady;
     }
 
-    OatClass* oat_class = new OatClass(offset_, compiled_methods_,
-                                       num_non_null_compiled_methods_, status);
-    writer_->oat_classes_.push_back(oat_class);
-    oat_class->UpdateChecksum(writer_->oat_header_);
-    offset_ += oat_class->SizeOf();
+    writer_->oat_classes_.emplace_back(offset_,
+                                       compiled_methods_,
+                                       num_non_null_compiled_methods_,
+                                       status);
+    offset_ += writer_->oat_classes_.back().SizeOf();
     return DexMethodVisitor::EndClass();
   }
 
  private:
-  std::vector<CompiledMethod*> compiled_methods_;
+  dchecked_vector<CompiledMethod*> compiled_methods_;
   size_t num_non_null_compiled_methods_;
 };
 
@@ -396,7 +489,7 @@
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
     if (compiled_method != nullptr) {
@@ -583,7 +676,7 @@
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
     if (compiled_method != nullptr) {
@@ -600,7 +693,6 @@
           DataAccess::SetOffset(oat_class, method_offsets_index_, offset_);
           dedupe_map_.PutBefore(lb, map.data(), offset_);
           offset_ += map_size;
-          writer_->oat_header_->UpdateChecksum(&map[0], map_size);
         }
       }
       ++method_offsets_index_;
@@ -624,7 +716,7 @@
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
     OatMethodOffsets offsets(0u);
@@ -715,7 +807,7 @@
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
       SHARED_REQUIRES(Locks::mutator_lock_) {
-    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
     // No thread suspension since dex_cache_ that may get invalidated if that occurs.
@@ -752,8 +844,7 @@
             << PrettyMethod(it.GetMemberIndex(), *dex_file_);
         const OatQuickMethodHeader& method_header =
             oat_class->method_headers_[method_offsets_index_];
-        writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
-        if (!out->WriteFully(&method_header, sizeof(method_header))) {
+        if (!writer_->WriteData(out, &method_header, sizeof(method_header))) {
           ReportWriteFailure("method header", it);
           return false;
         }
@@ -790,8 +881,7 @@
           }
         }
 
-        writer_->oat_header_->UpdateChecksum(quick_code.data(), code_size);
-        if (!out->WriteFully(quick_code.data(), code_size)) {
+        if (!writer_->WriteData(out, quick_code.data(), code_size)) {
           ReportWriteFailure("method code", it);
           return false;
         }
@@ -945,7 +1035,7 @@
   }
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
-    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
     const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
 
     if (compiled_method != nullptr) {  // ie. not an abstract method
@@ -963,7 +1053,7 @@
           << map_size << " " << map_offset << " " << offset_ << " "
           << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " for " << DataAccess::Name();
       if (map_size != 0u && map_offset == offset_) {
-        if (UNLIKELY(!out->WriteFully(&map[0], map_size))) {
+        if (UNLIKELY(!writer_->WriteData(out, map.data(), map_size))) {
           ReportWriteFailure(it);
           return false;
         }
@@ -1028,12 +1118,12 @@
 }
 
 size_t OatWriter::InitOatHeader() {
-  oat_header_ = OatHeader::Create(compiler_driver_->GetInstructionSet(),
-                                  compiler_driver_->GetInstructionSetFeatures(),
-                                  dex_files_,
-                                  image_file_location_oat_checksum_,
-                                  image_file_location_oat_begin_,
-                                  key_value_store_);
+  oat_header_.reset(OatHeader::Create(compiler_driver_->GetInstructionSet(),
+                                      compiler_driver_->GetInstructionSetFeatures(),
+                                      dchecked_integral_cast<uint32_t>(dex_files_->size()),
+                                      key_value_store_));
+  oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum_);
+  oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin_);
 
   return oat_header_->GetHeaderSize();
 }
@@ -1043,9 +1133,8 @@
   for (size_t i = 0; i != dex_files_->size(); ++i) {
     const DexFile* dex_file = (*dex_files_)[i];
     CHECK(dex_file != nullptr);
-    OatDexFile* oat_dex_file = new OatDexFile(offset, *dex_file);
-    oat_dex_files_.push_back(oat_dex_file);
-    offset += oat_dex_file->SizeOf();
+    oat_dex_files_.emplace_back(offset, *dex_file);
+    offset += oat_dex_files_.back().SizeOf();
   }
   return offset;
 }
@@ -1059,12 +1148,12 @@
     size_dex_file_alignment_ += offset - original_offset;
 
     // set offset in OatDexFile to DexFile
-    oat_dex_files_[i]->dex_file_offset_ = offset;
+    oat_dex_files_[i].dex_file_offset_ = offset;
 
     const DexFile* dex_file = (*dex_files_)[i];
 
     // Initialize type lookup table
-    oat_dex_files_[i]->lookup_table_ = dex_file->GetTypeLookupTable();
+    oat_dex_files_[i].lookup_table_ = dex_file->GetTypeLookupTable();
 
     offset += dex_file->GetHeader().file_size_;
   }
@@ -1072,14 +1161,14 @@
 }
 
 size_t OatWriter::InitLookupTables(size_t offset) {
-  for (OatDexFile* oat_dex_file : oat_dex_files_) {
-    if (oat_dex_file->lookup_table_ != nullptr) {
+  for (OatDexFile& oat_dex_file : oat_dex_files_) {
+    if (oat_dex_file.lookup_table_ != nullptr) {
       uint32_t aligned_offset = RoundUp(offset, 4);
-      oat_dex_file->lookup_table_offset_ = aligned_offset;
+      oat_dex_file.lookup_table_offset_ = aligned_offset;
       size_oat_lookup_table_alignment_ += aligned_offset - offset;
-      offset = aligned_offset + oat_dex_file->lookup_table_->RawDataLength();
+      offset = aligned_offset + oat_dex_file.lookup_table_->RawDataLength();
     } else {
-      oat_dex_file->lookup_table_offset_ = 0;
+      oat_dex_file.lookup_table_offset_ = 0;
     }
   }
   return offset;
@@ -1094,13 +1183,12 @@
 
   // Update oat_dex_files_.
   auto oat_class_it = oat_classes_.begin();
-  for (OatDexFile* oat_dex_file : oat_dex_files_) {
-    for (uint32_t& method_offset : oat_dex_file->methods_offsets_) {
+  for (OatDexFile& oat_dex_file : oat_dex_files_) {
+    for (uint32_t& class_offset : oat_dex_file.class_offsets_) {
       DCHECK(oat_class_it != oat_classes_.end());
-      method_offset = (*oat_class_it)->offset_;
+      class_offset = oat_class_it->offset_;
       ++oat_class_it;
     }
-    oat_dex_file->UpdateChecksum(oat_header_);
   }
   CHECK(oat_class_it == oat_classes_.end());
 
@@ -1184,17 +1272,14 @@
 }
 
 bool OatWriter::WriteRodata(OutputStream* out) {
-  const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
-  if (raw_file_offset == (off_t) -1) {
-    LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+  if (!GetOatDataOffset(out)) {
     return false;
   }
-  const size_t file_offset = static_cast<size_t>(raw_file_offset);
-  oat_data_offset_ = file_offset;
+  const size_t file_offset = oat_data_offset_;
 
   // Reserve space for header. It will be written last - after updating the checksum.
   size_t header_size = oat_header_->GetHeaderSize();
-  if (out->Seek(header_size, kSeekCurrent) == (off_t) -1) {
+  if (out->Seek(header_size, kSeekCurrent) == static_cast<off_t>(-1)) {
     PLOG(ERROR) << "Failed to reserve space for oat header in " << out->GetLocation();
     return false;
   }
@@ -1207,7 +1292,7 @@
   }
 
   off_t tables_end_offset = out->Seek(0, kSeekCurrent);
-  if (tables_end_offset == (off_t) -1) {
+  if (tables_end_offset == static_cast<off_t>(-1)) {
     LOG(ERROR) << "Failed to seek to oat code position in " << out->GetLocation();
     return false;
   }
@@ -1252,7 +1337,7 @@
   }
 
   const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent);
-  if (oat_end_file_offset == (off_t) -1) {
+  if (oat_end_file_offset == static_cast<off_t>(-1)) {
     LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation();
     return false;
   }
@@ -1288,14 +1373,14 @@
     DO_STAT(size_oat_dex_file_location_data_);
     DO_STAT(size_oat_dex_file_location_checksum_);
     DO_STAT(size_oat_dex_file_offset_);
-    DO_STAT(size_oat_dex_file_methods_offsets_);
+    DO_STAT(size_oat_dex_file_lookup_table_offset_);
+    DO_STAT(size_oat_dex_file_class_offsets_);
+    DO_STAT(size_oat_lookup_table_alignment_);
+    DO_STAT(size_oat_lookup_table_);
     DO_STAT(size_oat_class_type_);
     DO_STAT(size_oat_class_status_);
     DO_STAT(size_oat_class_method_bitmaps_);
     DO_STAT(size_oat_class_method_offsets_);
-    DO_STAT(size_oat_lookup_table_alignment_);
-    DO_STAT(size_oat_lookup_table_offset_);
-    DO_STAT(size_oat_lookup_table_);
     #undef DO_STAT
 
     VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
@@ -1306,17 +1391,20 @@
   CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset));
   CHECK_EQ(size_, relative_offset);
 
+  // Finalize the header checksum.
+  oat_header_->UpdateChecksumWithHeaderData();
+
   // Write the header now that the checksum is final.
-  if (out->Seek(file_offset, kSeekSet) == (off_t) -1) {
+  if (out->Seek(file_offset, kSeekSet) == static_cast<off_t>(-1)) {
     PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation();
     return false;
   }
   DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent)));
-  if (!out->WriteFully(oat_header_, header_size)) {
+  if (!out->WriteFully(oat_header_.get(), header_size)) {
     PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
     return false;
   }
-  if (out->Seek(oat_end_file_offset, kSeekSet) == (off_t) -1) {
+  if (out->Seek(oat_end_file_offset, kSeekSet) == static_cast<off_t>(-1)) {
     PLOG(ERROR) << "Failed to seek to end after writing oat header to " << out->GetLocation();
     return false;
   }
@@ -1327,13 +1415,13 @@
 
 bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) {
   for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
-    if (!oat_dex_files_[i]->Write(this, out, file_offset)) {
+    if (!oat_dex_files_[i].Write(this, out, file_offset)) {
       PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation();
       return false;
     }
   }
   for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
-    uint32_t expected_offset = file_offset + oat_dex_files_[i]->dex_file_offset_;
+    uint32_t expected_offset = file_offset + oat_dex_files_[i].dex_file_offset_;
     off_t actual_offset = out->Seek(expected_offset, kSeekSet);
     if (static_cast<uint32_t>(actual_offset) != expected_offset) {
       const DexFile* dex_file = (*dex_files_)[i];
@@ -1353,7 +1441,7 @@
     return false;
   }
   for (size_t i = 0; i != oat_classes_.size(); ++i) {
-    if (!oat_classes_[i]->Write(this, out, file_offset)) {
+    if (!oat_classes_[i].Write(this, out, file_offset)) {
       PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation();
       return false;
     }
@@ -1363,8 +1451,8 @@
 
 bool OatWriter::WriteLookupTables(OutputStream* out, const size_t file_offset) {
   for (size_t i = 0; i < oat_dex_files_.size(); ++i) {
-    const uint32_t lookup_table_offset = oat_dex_files_[i]->lookup_table_offset_;
-    const TypeLookupTable* table = oat_dex_files_[i]->lookup_table_;
+    const uint32_t lookup_table_offset = oat_dex_files_[i].lookup_table_offset_;
+    const TypeLookupTable* table = oat_dex_files_[i].lookup_table_;
     DCHECK_EQ(lookup_table_offset == 0, table == nullptr);
     if (lookup_table_offset == 0) {
       continue;
@@ -1378,7 +1466,7 @@
       return false;
     }
     if (table != nullptr) {
-      if (!out->WriteFully(table->RawData(), table->RawDataLength())) {
+      if (!WriteData(out, table->RawData(), table->RawDataLength())) {
         const DexFile* dex_file = (*dex_files_)[i];
         PLOG(ERROR) << "Failed to write lookup table for " << dex_file->GetLocation()
                     << " to " << out->GetLocation();
@@ -1427,7 +1515,7 @@
         uint32_t alignment_padding = aligned_offset - relative_offset; \
         out->Seek(alignment_padding, kSeekCurrent); \
         size_trampoline_alignment_ += alignment_padding; \
-        if (!out->WriteFully(&(*field)[0], field->size())) { \
+        if (!WriteData(out, field->data(), field->size())) { \
           PLOG(ERROR) << "Failed to write " # field " to " << out->GetLocation(); \
           return false; \
         } \
@@ -1469,6 +1557,17 @@
   return relative_offset;
 }
 
+bool OatWriter::GetOatDataOffset(OutputStream* out) {
+  // Get the elf file offset of the oat file.
+  const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
+  if (raw_file_offset == static_cast<off_t>(-1)) {
+    LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+    return false;
+  }
+  oat_data_offset_ = static_cast<size_t>(raw_file_offset);
+  return true;
+}
+
 bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) {
   static const uint8_t kPadding[] = {
       0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
@@ -1481,6 +1580,11 @@
   return true;
 }
 
+bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) {
+  oat_header_->UpdateChecksum(data, size);
+  return out->WriteFully(data, size);
+}
+
 std::pair<bool, uint32_t> OatWriter::MethodOffsetMap::FindMethodOffset(MethodReference ref) {
   auto it = map.find(ref);
   if (it == map.end()) {
@@ -1498,7 +1602,7 @@
   dex_file_location_checksum_ = dex_file.GetLocationChecksum();
   dex_file_offset_ = 0;
   lookup_table_offset_ = 0;
-  methods_offsets_.resize(dex_file.NumClassDefs());
+  class_offsets_.resize(dex_file.NumClassDefs());
 }
 
 size_t OatWriter::OatDexFile::SizeOf() const {
@@ -1507,63 +1611,50 @@
           + sizeof(dex_file_location_checksum_)
           + sizeof(dex_file_offset_)
           + sizeof(lookup_table_offset_)
-          + (sizeof(methods_offsets_[0]) * methods_offsets_.size());
-}
-
-void OatWriter::OatDexFile::UpdateChecksum(OatHeader* oat_header) const {
-  oat_header->UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_));
-  oat_header->UpdateChecksum(dex_file_location_data_, dex_file_location_size_);
-  oat_header->UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_));
-  oat_header->UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_));
-  oat_header->UpdateChecksum(&lookup_table_offset_, sizeof(lookup_table_offset_));
-  if (lookup_table_ != nullptr) {
-    oat_header->UpdateChecksum(lookup_table_->RawData(), lookup_table_->RawDataLength());
-  }
-  oat_header->UpdateChecksum(&methods_offsets_[0],
-                            sizeof(methods_offsets_[0]) * methods_offsets_.size());
+          + (sizeof(class_offsets_[0]) * class_offsets_.size());
 }
 
 bool OatWriter::OatDexFile::Write(OatWriter* oat_writer,
                                   OutputStream* out,
                                   const size_t file_offset) const {
   DCHECK_OFFSET_();
-  if (!out->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
+  if (!oat_writer->WriteData(out, &dex_file_location_size_, sizeof(dex_file_location_size_))) {
     PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation();
     return false;
   }
   oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_);
-  if (!out->WriteFully(dex_file_location_data_, dex_file_location_size_)) {
+  if (!oat_writer->WriteData(out, dex_file_location_data_, dex_file_location_size_)) {
     PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation();
     return false;
   }
   oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_;
-  if (!out->WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) {
+  if (!oat_writer->WriteData(out,
+                             &dex_file_location_checksum_,
+                             sizeof(dex_file_location_checksum_))) {
     PLOG(ERROR) << "Failed to write dex file location checksum to " << out->GetLocation();
     return false;
   }
   oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_);
-  if (!out->WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) {
+  if (!oat_writer->WriteData(out, &dex_file_offset_, sizeof(dex_file_offset_))) {
     PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation();
     return false;
   }
   oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
-  if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) {
+  if (!oat_writer->WriteData(out, &lookup_table_offset_, sizeof(lookup_table_offset_))) {
     PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation();
     return false;
   }
-  oat_writer->size_oat_lookup_table_offset_ += sizeof(lookup_table_offset_);
-  if (!out->WriteFully(&methods_offsets_[0],
-                      sizeof(methods_offsets_[0]) * methods_offsets_.size())) {
+  oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_);
+  if (!oat_writer->WriteData(out, class_offsets_.data(), GetClassOffsetsRawSize())) {
     PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation();
     return false;
   }
-  oat_writer->size_oat_dex_file_methods_offsets_ +=
-      sizeof(methods_offsets_[0]) * methods_offsets_.size();
+  oat_writer->size_oat_dex_file_class_offsets_ += GetClassOffsetsRawSize();
   return true;
 }
 
 OatWriter::OatClass::OatClass(size_t offset,
-                              const std::vector<CompiledMethod*>& compiled_methods,
+                              const dchecked_vector<CompiledMethod*>& compiled_methods,
                               uint32_t num_non_null_compiled_methods,
                               mirror::Class::Status status)
     : compiled_methods_(compiled_methods) {
@@ -1593,7 +1684,7 @@
 
   uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
   if (type_ == kOatClassSomeCompiled) {
-    method_bitmap_ = new BitVector(num_methods, false, Allocator::GetMallocAllocator());
+    method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
     method_bitmap_size_ = method_bitmap_->GetSizeOf();
     oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
     oat_method_offsets_offset_from_oat_class += method_bitmap_size_;
@@ -1616,10 +1707,6 @@
   }
 }
 
-OatWriter::OatClass::~OatClass() {
-  delete method_bitmap_;
-}
-
 size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
     size_t class_def_method_index_) const {
   uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
@@ -1642,51 +1729,42 @@
           + (sizeof(method_offsets_[0]) * method_offsets_.size());
 }
 
-void OatWriter::OatClass::UpdateChecksum(OatHeader* oat_header) const {
-  oat_header->UpdateChecksum(&status_, sizeof(status_));
-  oat_header->UpdateChecksum(&type_, sizeof(type_));
-  if (method_bitmap_size_ != 0) {
-    CHECK_EQ(kOatClassSomeCompiled, type_);
-    oat_header->UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_));
-    oat_header->UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_);
-  }
-  oat_header->UpdateChecksum(&method_offsets_[0],
-                             sizeof(method_offsets_[0]) * method_offsets_.size());
-}
-
 bool OatWriter::OatClass::Write(OatWriter* oat_writer,
                                 OutputStream* out,
                                 const size_t file_offset) const {
   DCHECK_OFFSET_();
-  if (!out->WriteFully(&status_, sizeof(status_))) {
+  if (!oat_writer->WriteData(out, &status_, sizeof(status_))) {
     PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
     return false;
   }
   oat_writer->size_oat_class_status_ += sizeof(status_);
-  if (!out->WriteFully(&type_, sizeof(type_))) {
+
+  if (!oat_writer->WriteData(out, &type_, sizeof(type_))) {
     PLOG(ERROR) << "Failed to write oat class type to " << out->GetLocation();
     return false;
   }
   oat_writer->size_oat_class_type_ += sizeof(type_);
+
   if (method_bitmap_size_ != 0) {
     CHECK_EQ(kOatClassSomeCompiled, type_);
-    if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
+    if (!oat_writer->WriteData(out, &method_bitmap_size_, sizeof(method_bitmap_size_))) {
       PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
       return false;
     }
     oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
-    if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+
+    if (!oat_writer->WriteData(out, method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
       PLOG(ERROR) << "Failed to write method bitmap to " << out->GetLocation();
       return false;
     }
     oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
   }
-  if (!out->WriteFully(&method_offsets_[0],
-                      sizeof(method_offsets_[0]) * method_offsets_.size())) {
+
+  if (!oat_writer->WriteData(out, method_offsets_.data(), GetMethodOffsetsRawSize())) {
     PLOG(ERROR) << "Failed to write method offsets to " << out->GetLocation();
     return false;
   }
-  oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
+  oat_writer->size_oat_class_method_offsets_ += GetMethodOffsetsRawSize();
   return true;
 }
 
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 6c46ebc..5feb5fc 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -21,12 +21,14 @@
 #include <cstddef>
 #include <memory>
 
+#include "base/dchecked_vector.h"
 #include "linker/relative_patcher.h"  // For linker::RelativePatcherTargetProvider.
 #include "mem_map.h"
 #include "method_reference.h"
 #include "mirror/class.h"
 #include "oat.h"
 #include "safe_map.h"
+#include "utils/array_ref.h"
 
 namespace art {
 
@@ -124,8 +126,8 @@
     return bss_size_;
   }
 
-  const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
-    return absolute_patch_locations_;
+  ArrayRef<const uintptr_t> GetAbsolutePatchLocations() const {
+    return ArrayRef<const uintptr_t>(absolute_patch_locations_);
   }
 
   bool WriteRodata(OutputStream* out);
@@ -133,8 +135,8 @@
 
   ~OatWriter();
 
-  const std::vector<dwarf::MethodDebugInfo>& GetMethodDebugInfo() const {
-    return method_info_;
+  ArrayRef<const dwarf::MethodDebugInfo> GetMethodDebugInfo() const {
+    return ArrayRef<const dwarf::MethodDebugInfo>(method_info_);
   }
 
   const CompilerDriver* GetCompilerDriver() {
@@ -142,6 +144,9 @@
   }
 
  private:
+  class OatClass;
+  class OatDexFile;
+
   // The DataAccess classes are helper classes that provide access to members related to
   // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
   // we can share a lot of code for processing the maps with template classes below.
@@ -175,10 +180,8 @@
   size_t InitDexFiles(size_t offset);
   size_t InitOatClasses(size_t offset);
   size_t InitOatMaps(size_t offset);
-  size_t InitOatCode(size_t offset)
-      SHARED_REQUIRES(Locks::mutator_lock_);
-  size_t InitOatCodeDexFiles(size_t offset)
-      SHARED_REQUIRES(Locks::mutator_lock_);
+  size_t InitOatCode(size_t offset);
+  size_t InitOatCodeDexFiles(size_t offset);
 
   bool WriteTables(OutputStream* out, const size_t file_offset);
   bool WriteLookupTables(OutputStream* out, const size_t file_offset);
@@ -186,93 +189,11 @@
   size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
   size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
 
+  bool GetOatDataOffset(OutputStream* out);
   bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
+  bool WriteData(OutputStream* out, const void* data, size_t size);
 
-  class OatDexFile {
-   public:
-    OatDexFile(size_t offset, const DexFile& dex_file);
-    size_t SizeOf() const;
-    void UpdateChecksum(OatHeader* oat_header) const;
-    bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
-
-    // Offset of start of OatDexFile from beginning of OatHeader. It is
-    // used to validate file position when writing.
-    size_t offset_;
-
-    // data to write
-    uint32_t dex_file_location_size_;
-    const uint8_t* dex_file_location_data_;
-    uint32_t dex_file_location_checksum_;
-    uint32_t dex_file_offset_;
-    uint32_t lookup_table_offset_;
-    TypeLookupTable* lookup_table_;  // Owned by the dex file.
-    std::vector<uint32_t> methods_offsets_;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(OatDexFile);
-  };
-
-  class OatClass {
-   public:
-    OatClass(size_t offset,
-             const std::vector<CompiledMethod*>& compiled_methods,
-             uint32_t num_non_null_compiled_methods,
-             mirror::Class::Status status);
-    ~OatClass();
-    size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
-    size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
-    size_t SizeOf() const;
-    void UpdateChecksum(OatHeader* oat_header) const;
-    bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
-
-    CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
-      DCHECK_LT(class_def_method_index, compiled_methods_.size());
-      return compiled_methods_[class_def_method_index];
-    }
-
-    // Offset of start of OatClass from beginning of OatHeader. It is
-    // used to validate file position when writing.
-    size_t offset_;
-
-    // CompiledMethods for each class_def_method_index, or null if no method is available.
-    std::vector<CompiledMethod*> compiled_methods_;
-
-    // Offset from OatClass::offset_ to the OatMethodOffsets for the
-    // class_def_method_index. If 0, it means the corresponding
-    // CompiledMethod entry in OatClass::compiled_methods_ should be
-    // null and that the OatClass::type_ should be kOatClassBitmap.
-    std::vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
-
-    // data to write
-
-    static_assert(mirror::Class::Status::kStatusMax < (2 ^ 16), "class status won't fit in 16bits");
-    int16_t status_;
-
-    static_assert(OatClassType::kOatClassMax < (2 ^ 16), "oat_class type won't fit in 16bits");
-    uint16_t type_;
-
-    uint32_t method_bitmap_size_;
-
-    // bit vector indexed by ClassDef method index. When
-    // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
-    // method has an OatMethodOffsets in methods_offsets_, otherwise
-    // the entry was ommited to save space. If OatClassType::type_ is
-    // not is kOatClassBitmap, the bitmap will be null.
-    BitVector* method_bitmap_;
-
-    // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
-    // present in the OatClass. Note that some may be missing if
-    // OatClass::compiled_methods_ contains null values (and
-    // oat_method_offsets_offsets_from_oat_class_ should contain 0
-    // values in this case).
-    std::vector<OatMethodOffsets> method_offsets_;
-    std::vector<OatQuickMethodHeader> method_headers_;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(OatClass);
-  };
-
-  std::vector<dwarf::MethodDebugInfo> method_info_;
+  dchecked_vector<dwarf::MethodDebugInfo> method_info_;
 
   const CompilerDriver* const compiler_driver_;
   ImageWriter* const image_writer_;
@@ -301,9 +222,9 @@
 
   // data to write
   SafeMap<std::string, std::string>* key_value_store_;
-  OatHeader* oat_header_;
-  std::vector<OatDexFile*> oat_dex_files_;
-  std::vector<OatClass*> oat_classes_;
+  std::unique_ptr<OatHeader> oat_header_;
+  dchecked_vector<OatDexFile> oat_dex_files_;
+  dchecked_vector<OatClass> oat_classes_;
   std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
   std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
   std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_;
@@ -336,14 +257,14 @@
   uint32_t size_oat_dex_file_location_data_;
   uint32_t size_oat_dex_file_location_checksum_;
   uint32_t size_oat_dex_file_offset_;
-  uint32_t size_oat_dex_file_methods_offsets_;
+  uint32_t size_oat_dex_file_lookup_table_offset_;
+  uint32_t size_oat_dex_file_class_offsets_;
+  uint32_t size_oat_lookup_table_alignment_;
+  uint32_t size_oat_lookup_table_;
   uint32_t size_oat_class_type_;
   uint32_t size_oat_class_status_;
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
-  uint32_t size_oat_lookup_table_alignment_;
-  uint32_t size_oat_lookup_table_offset_;
-  uint32_t size_oat_lookup_table_;
 
   std::unique_ptr<linker::RelativePatcher> relative_patcher_;
 
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 7dbfd7c..4c3f66a 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1138,8 +1138,8 @@
   void VisitArrayGet(HArrayGet* array_get) OVERRIDE {
     if (!has_deoptimization_on_constant_subscripts_ && array_get->IsInLoop()) {
       HLoopInformation* loop = array_get->GetBlock()->GetLoopInformation();
-      if (loop->IsLoopInvariant(array_get->InputAt(0), false) &&
-          loop->IsLoopInvariant(array_get->InputAt(1), false)) {
+      if (loop->IsDefinedOutOfTheLoop(array_get->InputAt(0)) &&
+          loop->IsDefinedOutOfTheLoop(array_get->InputAt(1))) {
         SideEffects loop_effects = side_effects_.GetLoopEffects(loop->GetHeader());
         if (!array_get->GetSideEffects().MayDependOn(loop_effects)) {
           HoistToPreheaderOrDeoptBlock(loop, array_get);
@@ -1349,7 +1349,7 @@
    * by handling the null check under the hood of the array length operation.
    */
   bool CanHandleLength(HLoopInformation* loop, HInstruction* length, bool needs_taken_test) {
-    if (loop->IsLoopInvariant(length, false)) {
+    if (loop->IsDefinedOutOfTheLoop(length)) {
       return true;
     } else if (length->IsArrayLength() && length->GetBlock()->GetLoopInformation() == loop) {
       if (CanHandleNullCheck(loop, length->InputAt(0), needs_taken_test)) {
@@ -1365,11 +1365,11 @@
    * by generating a deoptimization test.
    */
   bool CanHandleNullCheck(HLoopInformation* loop, HInstruction* check, bool needs_taken_test) {
-    if (loop->IsLoopInvariant(check, false)) {
+    if (loop->IsDefinedOutOfTheLoop(check)) {
       return true;
     } else if (check->IsNullCheck() && check->GetBlock()->GetLoopInformation() == loop) {
       HInstruction* array = check->InputAt(0);
-      if (loop->IsLoopInvariant(array, false)) {
+      if (loop->IsDefinedOutOfTheLoop(array)) {
         // Generate: if (array == null) deoptimize;
         HBasicBlock* block = TransformLoopForDeoptimizationIfNeeded(loop, needs_taken_test);
         HInstruction* cond =
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 2bbf500..1178d0f 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -785,7 +785,7 @@
     ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
         vtable_index, class_linker->GetImagePointerSize());
     if (actual_method != resolved_method &&
-        !IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+        !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
       // TODO: The actual method could still be referenced in the current dex file, so we
       // could try locating it.
       // TODO: Remove the dex_file restriction.
@@ -2841,15 +2841,21 @@
     }
 
     case Instruction::CONST_STRING: {
+      uint32_t string_index = instruction.VRegB_21c();
+      bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
+          *dex_file_, string_index);
       current_block_->AddInstruction(
-          new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_21c(), dex_pc));
+          new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
 
     case Instruction::CONST_STRING_JUMBO: {
+      uint32_t string_index = instruction.VRegB_31c();
+      bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
+          *dex_file_, string_index);
       current_block_->AddInstruction(
-          new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_31c(), dex_pc));
+          new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
       UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
       break;
     }
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0baa0e3..53d3615 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -246,10 +246,7 @@
         InitLocationsBaseline(current);
       }
       DCHECK(CheckTypeConsistency(current));
-      uintptr_t native_pc_begin = GetAssembler()->CodeSize();
       current->Accept(instruction_visitor);
-      uintptr_t native_pc_end = GetAssembler()->CodeSize();
-      RecordNativeDebugInfo(current->GetDexPc(), native_pc_begin, native_pc_end);
     }
   }
 
@@ -926,17 +923,6 @@
   stack_map_stream_.FillIn(region);
 }
 
-void CodeGenerator::RecordNativeDebugInfo(uint32_t dex_pc,
-                                          uintptr_t native_pc_begin,
-                                          uintptr_t native_pc_end) {
-  if (compiler_options_.GetGenerateDebugInfo() &&
-      dex_pc != kNoDexPc &&
-      native_pc_begin != native_pc_end) {
-    src_map_.push_back(SrcMapElem({static_cast<uint32_t>(native_pc_begin),
-                                   static_cast<int32_t>(dex_pc)}));
-  }
-}
-
 void CodeGenerator::RecordPcInfo(HInstruction* instruction,
                                  uint32_t dex_pc,
                                  SlowPathCode* slow_path) {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 114d97b..eade05d 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -269,8 +269,6 @@
 
   // Record native to dex mapping for a suspend point.  Required by runtime.
   void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
-  // Record additional native to dex mappings for native debugging/profiling tools.
-  void RecordNativeDebugInfo(uint32_t dex_pc, uintptr_t native_pc_begin, uintptr_t native_pc_end);
 
   bool CanMoveNullCheckToUser(HNullCheck* null_check);
   void MaybeRecordImplicitNullCheck(HInstruction* instruction);
@@ -452,10 +450,6 @@
   // Copy the result of a call into the given target.
   virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0;
 
-  const ArenaVector<SrcMapElem>& GetSrcMappingTable() const {
-    return src_map_;
-  }
-
  protected:
   // Method patch info used for recording locations of required linker patches and
   // target methods. The target method can be used for various purposes, whether for
@@ -498,7 +492,6 @@
         stats_(stats),
         graph_(graph),
         compiler_options_(compiler_options),
-        src_map_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         slow_paths_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
         current_slow_path_(nullptr),
         current_block_index_(0),
@@ -616,8 +609,6 @@
   HGraph* const graph_;
   const CompilerOptions& compiler_options_;
 
-  // Native to dex_pc map used for native debugging/profiling tools.
-  ArenaVector<SrcMapElem> src_map_;
   ArenaVector<SlowPathCode*> slow_paths_;
 
   // The current slow path that we're generating code for.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0a26786..a999c4c 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5067,16 +5067,15 @@
 }
 
 void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
-  codegen_->AddSlowPath(slow_path);
-
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
@@ -5107,8 +5106,12 @@
     __ LoadFromOffset(kLoadWord, out, out, cache_offset);
   }
 
-  __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  if (!load->IsInDexCache()) {
+    SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
+    codegen_->AddSlowPath(slow_path);
+    __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 static int32_t GetExceptionTlsOffset() {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 227f4be..f6834d4 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3850,16 +3850,15 @@
 }
 
 void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
-  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
-  codegen_->AddSlowPath(slow_path);
-
   Location out_loc = load->GetLocations()->Out();
   Register out = OutputRegister(load);
   Register current_method = InputRegisterAt(load, 0);
@@ -3889,8 +3888,12 @@
     __ Ldr(out, MemOperand(out.X(), cache_offset));
   }
 
-  __ Cbz(out, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  if (!load->IsInDexCache()) {
+    SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
+    codegen_->AddSlowPath(slow_path);
+    __ Cbz(out, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 void LocationsBuilderARM64::VisitLocal(HLocal* local) {
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index f872bfe..b000c77 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2218,6 +2218,171 @@
   }
 }
 
+void InstructionCodeGeneratorMIPS::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  Register out = locations->Out().AsRegister<Register>();
+  Register dividend = locations->InAt(0).AsRegister<Register>();
+  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  DCHECK(imm == 1 || imm == -1);
+
+  if (instruction->IsRem()) {
+    __ Move(out, ZERO);
+  } else {
+    if (imm == -1) {
+      __ Subu(out, ZERO, dividend);
+    } else if (out != dividend) {
+      __ Move(out, dividend);
+    }
+  }
+}
+
+void InstructionCodeGeneratorMIPS::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  Register out = locations->Out().AsRegister<Register>();
+  Register dividend = locations->InAt(0).AsRegister<Register>();
+  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+  uint32_t abs_imm = static_cast<uint32_t>(std::abs(imm));
+  DCHECK(IsPowerOfTwo(abs_imm));
+  int ctz_imm = CTZ(abs_imm);
+
+  if (instruction->IsDiv()) {
+    if (ctz_imm == 1) {
+      // Fast path for division by +/-2, which is very common.
+      __ Srl(TMP, dividend, 31);
+    } else {
+      __ Sra(TMP, dividend, 31);
+      __ Srl(TMP, TMP, 32 - ctz_imm);
+    }
+    __ Addu(out, dividend, TMP);
+    __ Sra(out, out, ctz_imm);
+    if (imm < 0) {
+      __ Subu(out, ZERO, out);
+    }
+  } else {
+    if (ctz_imm == 1) {
+      // Fast path for modulo +/-2, which is very common.
+      __ Sra(TMP, dividend, 31);
+      __ Subu(out, dividend, TMP);
+      __ Andi(out, out, 1);
+      __ Addu(out, out, TMP);
+    } else {
+      __ Sra(TMP, dividend, 31);
+      __ Srl(TMP, TMP, 32 - ctz_imm);
+      __ Addu(out, dividend, TMP);
+      if (IsUint<16>(abs_imm - 1)) {
+        __ Andi(out, out, abs_imm - 1);
+      } else {
+        __ Sll(out, out, 32 - ctz_imm);
+        __ Srl(out, out, 32 - ctz_imm);
+      }
+      __ Subu(out, out, TMP);
+    }
+  }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Location second = locations->InAt(1);
+  DCHECK(second.IsConstant());
+
+  Register out = locations->Out().AsRegister<Register>();
+  Register dividend = locations->InAt(0).AsRegister<Register>();
+  int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+
+  int64_t magic;
+  int shift;
+  CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
+
+  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+
+  __ LoadConst32(TMP, magic);
+  if (isR6) {
+    __ MuhR6(TMP, dividend, TMP);
+  } else {
+    __ MultR2(dividend, TMP);
+    __ Mfhi(TMP);
+  }
+  if (imm > 0 && magic < 0) {
+    __ Addu(TMP, TMP, dividend);
+  } else if (imm < 0 && magic > 0) {
+    __ Subu(TMP, TMP, dividend);
+  }
+
+  if (shift != 0) {
+    __ Sra(TMP, TMP, shift);
+  }
+
+  if (instruction->IsDiv()) {
+    __ Sra(out, TMP, 31);
+    __ Subu(out, TMP, out);
+  } else {
+    __ Sra(AT, TMP, 31);
+    __ Subu(AT, TMP, AT);
+    __ LoadConst32(TMP, imm);
+    if (isR6) {
+      __ MulR6(TMP, AT, TMP);
+    } else {
+      __ MulR2(TMP, AT, TMP);
+    }
+    __ Subu(out, dividend, TMP);
+  }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateDivRemIntegral(HBinaryOperation* instruction) {
+  DCHECK(instruction->IsDiv() || instruction->IsRem());
+  DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+  LocationSummary* locations = instruction->GetLocations();
+  Register out = locations->Out().AsRegister<Register>();
+  Location second = locations->InAt(1);
+
+  if (second.IsConstant()) {
+    int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+    if (imm == 0) {
+      // Do not generate anything. DivZeroCheck would prevent any code to be executed.
+    } else if (imm == 1 || imm == -1) {
+      DivRemOneOrMinusOne(instruction);
+    } else if (IsPowerOfTwo(std::abs(imm))) {
+      DivRemByPowerOfTwo(instruction);
+    } else {
+      DCHECK(imm <= -2 || imm >= 2);
+      GenerateDivRemWithAnyConstant(instruction);
+    }
+  } else {
+    Register dividend = locations->InAt(0).AsRegister<Register>();
+    Register divisor = second.AsRegister<Register>();
+    bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+    if (instruction->IsDiv()) {
+      if (isR6) {
+        __ DivR6(out, dividend, divisor);
+      } else {
+        __ DivR2(out, dividend, divisor);
+      }
+    } else {
+      if (isR6) {
+        __ ModR6(out, dividend, divisor);
+      } else {
+        __ ModR2(out, dividend, divisor);
+      }
+    }
+  }
+}
+
 void LocationsBuilderMIPS::VisitDiv(HDiv* div) {
   Primitive::Type type = div->GetResultType();
   LocationSummary::CallKind call_kind = (type == Primitive::kPrimLong)
@@ -2229,7 +2394,7 @@
   switch (type) {
     case Primitive::kPrimInt:
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
 
@@ -2258,20 +2423,11 @@
 void InstructionCodeGeneratorMIPS::VisitDiv(HDiv* instruction) {
   Primitive::Type type = instruction->GetType();
   LocationSummary* locations = instruction->GetLocations();
-  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
 
   switch (type) {
-    case Primitive::kPrimInt: {
-      Register dst = locations->Out().AsRegister<Register>();
-      Register lhs = locations->InAt(0).AsRegister<Register>();
-      Register rhs = locations->InAt(1).AsRegister<Register>();
-      if (isR6) {
-        __ DivR6(dst, lhs, rhs);
-      } else {
-        __ DivR2(dst, lhs, rhs);
-      }
+    case Primitive::kPrimInt:
+      GenerateDivRemIntegral(instruction);
       break;
-    }
     case Primitive::kPrimLong: {
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv),
                               instruction,
@@ -3257,24 +3413,28 @@
 }
 
 void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) {
-  SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load);
-  codegen_->AddSlowPath(slow_path);
-
   LocationSummary* locations = load->GetLocations();
   Register out = locations->Out().AsRegister<Register>();
   Register current_method = locations->InAt(0).AsRegister<Register>();
   __ LoadFromOffset(kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
   __ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
   __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
-  __ Beqz(out, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+
+  if (!load->IsInDexCache()) {
+    SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load);
+    codegen_->AddSlowPath(slow_path);
+    __ Beqz(out, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 void LocationsBuilderMIPS::VisitLocal(HLocal* local) {
@@ -3666,7 +3826,7 @@
   switch (type) {
     case Primitive::kPrimInt:
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
 
@@ -3696,21 +3856,11 @@
 
 void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) {
   Primitive::Type type = instruction->GetType();
-  LocationSummary* locations = instruction->GetLocations();
-  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
 
   switch (type) {
-    case Primitive::kPrimInt: {
-      Register dst = locations->Out().AsRegister<Register>();
-      Register lhs = locations->InAt(0).AsRegister<Register>();
-      Register rhs = locations->InAt(1).AsRegister<Register>();
-      if (isR6) {
-        __ ModR6(dst, lhs, rhs);
-      } else {
-        __ ModR2(dst, lhs, rhs);
-      }
+    case Primitive::kPrimInt:
+      GenerateDivRemIntegral(instruction);
       break;
-    }
     case Primitive::kPrimLong: {
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod),
                               instruction,
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index e3a2cb4..caf3174 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -229,6 +229,10 @@
                              size_t condition_input_index,
                              MipsLabel* true_target,
                              MipsLabel* false_target);
+  void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+  void DivRemByPowerOfTwo(HBinaryOperation* instruction);
+  void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
+  void GenerateDivRemIntegral(HBinaryOperation* instruction);
   void HandleGoto(HInstruction* got, HBasicBlock* successor);
 
   MipsAssembler* const assembler_;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 78f5644..9f8558d 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3105,16 +3105,15 @@
 }
 
 void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
-  SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
-  codegen_->AddSlowPath(slow_path);
-
   LocationSummary* locations = load->GetLocations();
   GpuRegister out = locations->Out().AsRegister<GpuRegister>();
   GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
@@ -3123,8 +3122,13 @@
   __ LoadFromOffset(kLoadDoubleword, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
   __ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
   // TODO: We will need a read barrier here.
-  __ Beqzc(out, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+
+  if (!load->IsInDexCache()) {
+    SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
+    codegen_->AddSlowPath(slow_path);
+    __ Beqzc(out, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 void LocationsBuilderMIPS64::VisitLocal(HLocal* local) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 19f03df..8c50650 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5611,16 +5611,15 @@
 }
 
 void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
-  codegen_->AddSlowPath(slow_path);
-
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
@@ -5651,9 +5650,13 @@
     __ movl(out, Address(out, cache_offset));
   }
 
-  __ testl(out, out);
-  __ j(kEqual, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  if (!load->IsInDexCache()) {
+    SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
+    codegen_->AddSlowPath(slow_path);
+    __ testl(out, out);
+    __ j(kEqual, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 static Address GetExceptionTlsAddress() {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 44a51ea..9cb6a2c 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5220,16 +5220,15 @@
 }
 
 void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+  LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+      ? LocationSummary::kCallOnSlowPath
+      : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
-  codegen_->AddSlowPath(slow_path);
-
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
   CpuRegister out = out_loc.AsRegister<CpuRegister>();
@@ -5260,9 +5259,13 @@
     __ movl(out, Address(out, cache_offset));
   }
 
-  __ testl(out, out);
-  __ j(kEqual, slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  if (!load->IsInDexCache()) {
+    SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
+    codegen_->AddSlowPath(slow_path);
+    __ testl(out, out);
+    __ j(kEqual, slow_path->GetEntryLabel());
+    __ Bind(slow_path->GetExitLabel());
+  }
 }
 
 static Address GetExceptionTlsAddress() {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 145b1f3..7351fed 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -47,6 +47,12 @@
 static constexpr size_t kRuntimeParameterFpuRegistersLength =
     arraysize(kRuntimeParameterFpuRegisters);
 
+// These XMM registers are non-volatile in ART ABI, but volatile in native ABI.
+// If the ART ABI changes, this list must be updated.  It is used to ensure that
+// these are not clobbered by any direct call to native code (such as math intrinsics).
+static constexpr FloatRegister non_volatile_xmm_regs[] = { XMM12, XMM13, XMM14, XMM15 };
+
+
 class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> {
  public:
   InvokeRuntimeCallingConvention()
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index fdf8cc9..0b7fdf8 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -705,7 +705,8 @@
       return loop_it->second;
     }
   }
-  if (loop->IsLoopInvariant(instruction, true)) {
+  if (loop->IsDefinedOutOfTheLoop(instruction)) {
+    DCHECK(instruction->GetBlock()->Dominates(loop->GetPreHeader()));
     InductionInfo* info = CreateInvariantFetch(instruction);
     AssignInfo(loop, instruction, info);
     return info;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 8340811..7127215 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -189,6 +189,42 @@
       return ((method.d.data & kIntrinsicFlagMin) == 0) ?
           Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
 
+    // More math builtins.
+    case kIntrinsicCos:
+      return Intrinsics::kMathCos;
+    case kIntrinsicSin:
+      return Intrinsics::kMathSin;
+    case kIntrinsicAcos:
+      return Intrinsics::kMathAcos;
+    case kIntrinsicAsin:
+      return Intrinsics::kMathAsin;
+    case kIntrinsicAtan:
+      return Intrinsics::kMathAtan;
+    case kIntrinsicAtan2:
+      return Intrinsics::kMathAtan2;
+    case kIntrinsicCbrt:
+      return Intrinsics::kMathCbrt;
+    case kIntrinsicCosh:
+      return Intrinsics::kMathCosh;
+    case kIntrinsicExp:
+      return Intrinsics::kMathExp;
+    case kIntrinsicExpm1:
+      return Intrinsics::kMathExpm1;
+    case kIntrinsicHypot:
+      return Intrinsics::kMathHypot;
+    case kIntrinsicLog:
+      return Intrinsics::kMathLog;
+    case kIntrinsicLog10:
+      return Intrinsics::kMathLog10;
+    case kIntrinsicNextAfter:
+      return Intrinsics::kMathNextAfter;
+    case kIntrinsicSinh:
+      return Intrinsics::kMathSinh;
+    case kIntrinsicTan:
+      return Intrinsics::kMathTan;
+    case kIntrinsicTanh:
+      return Intrinsics::kMathTanh;
+
     // Misc math.
     case kIntrinsicSqrt:
       return Intrinsics::kMathSqrt;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 5329b5c..9820c5d 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1718,6 +1718,23 @@
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
 
 #undef UNIMPLEMENTED_INTRINSIC
 
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 962c4d5..2e5b022 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1532,6 +1532,24 @@
 UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
 
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
+
 #undef UNIMPLEMENTED_INTRINSIC
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 8f1d5e1..96f43a0 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -51,6 +51,23 @@
   V(MathMaxFloatFloat, kStatic, kNeedsEnvironmentOrCache) \
   V(MathMaxLongLong, kStatic, kNeedsEnvironmentOrCache) \
   V(MathMaxIntInt, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathCos, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathSin, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathAcos, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathAsin, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathAtan, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathAtan2, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathCbrt, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathCosh, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathExp, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathExpm1, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathHypot, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathLog, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathLog10, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathNextAfter, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathSinh, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathTan, kStatic, kNeedsEnvironmentOrCache) \
+  V(MathTanh, kStatic, kNeedsEnvironmentOrCache) \
   V(MathSqrt, kStatic, kNeedsEnvironmentOrCache) \
   V(MathCeil, kStatic, kNeedsEnvironmentOrCache) \
   V(MathFloor, kStatic, kNeedsEnvironmentOrCache) \
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 9ecce0e..06fab61 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -989,6 +989,23 @@
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
 
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
 #undef UNIMPLEMENTED_INTRINSIC
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 36e1b20..8aa7d9f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1730,6 +1730,24 @@
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
 
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
+
 #undef UNIMPLEMENTED_INTRINSIC
 
 #undef __
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 5b67cde..3370e73 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -788,6 +788,195 @@
   __ Bind(&done);
 }
 
+static void CreateFPToFPCallLocations(ArenaAllocator* arena,
+                                      HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCall,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetOut(Location::FpuRegisterLocation(XMM0));
+}
+
+static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86* codegen, QuickEntrypointEnum entry) {
+  LocationSummary* locations = invoke->GetLocations();
+  DCHECK(locations->WillCall());
+  DCHECK(invoke->IsInvokeStaticOrDirect());
+  X86Assembler* assembler = codegen->GetAssembler();
+
+  // We need some place to pass the parameters.
+  __ subl(ESP, Immediate(16));
+  __ cfi().AdjustCFAOffset(16);
+
+  // Pass the parameters at the bottom of the stack.
+  __ movsd(Address(ESP, 0), XMM0);
+
+  // If we have a second parameter, pass it next.
+  if (invoke->GetNumberOfArguments() == 2) {
+    __ movsd(Address(ESP, 8), XMM1);
+  }
+
+  // Now do the actual call.
+  __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(entry)));
+
+  // Extract the return value from the FP stack.
+  __ fstpl(Address(ESP, 0));
+  __ movsd(XMM0, Address(ESP, 0));
+
+  // And clean up the stack.
+  __ addl(ESP, Immediate(16));
+  __ cfi().AdjustCFAOffset(-16);
+
+  codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathCos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathCos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCos);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathSin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathSin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSin);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAcos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAcos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAcos);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAsin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAsin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAsin);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAtan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAtan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAtan);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathCbrt(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathCbrt(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCbrt);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathCosh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathCosh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCosh);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathExp(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathExp(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExp);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathExpm1(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathExpm1(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExpm1);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathLog(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathLog(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathLog10(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathLog10(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog10);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathSinh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathSinh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSinh);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathTan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathTan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTan);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathTanh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathTanh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTanh);
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena,
+                                        HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCall,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+  locations->SetOut(Location::FpuRegisterLocation(XMM0));
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAtan2(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAtan2(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAtan2);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathHypot(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathHypot(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickHypot);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathNextAfter(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
+}
+
 void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) {
   // The inputs plus one temp.
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index ecd129f..e862573 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -703,6 +703,188 @@
   __ Bind(&done);
 }
 
+static void CreateFPToFPCallLocations(ArenaAllocator* arena,
+                                      HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCall,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetOut(Location::FpuRegisterLocation(XMM0));
+
+  // We have to ensure that the native code doesn't clobber the XMM registers which are
+  // non-volatile for ART, but volatile for Native calls.  This will ensure that they are
+  // saved in the prologue and properly restored.
+  for (auto fp_reg : non_volatile_xmm_regs) {
+    locations->AddTemp(Location::FpuRegisterLocation(fp_reg));
+  }
+}
+
+static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86_64* codegen,
+                          QuickEntrypointEnum entry) {
+  LocationSummary* locations = invoke->GetLocations();
+  DCHECK(locations->WillCall());
+  DCHECK(invoke->IsInvokeStaticOrDirect());
+  X86_64Assembler* assembler = codegen->GetAssembler();
+
+  __ gs()->call(Address::Absolute(GetThreadOffset<kX86_64WordSize>(entry), true));
+  codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathCos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathCos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCos);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathSin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathSin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSin);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAcos(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAcos(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAcos);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAsin(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAsin(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAsin);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAtan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAtan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAtan);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathCbrt(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathCbrt(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCbrt);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathCosh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathCosh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickCosh);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathExp(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathExp(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExp);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathExpm1(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathExpm1(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickExpm1);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathLog(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathLog(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathLog10(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathLog10(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickLog10);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathSinh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathSinh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickSinh);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathTan(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathTan(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTan);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathTanh(HInvoke* invoke) {
+  CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathTanh(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickTanh);
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena,
+                                        HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kCall,
+                                                           kIntrinsified);
+  InvokeRuntimeCallingConvention calling_convention;
+  locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+  locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+  locations->SetOut(Location::FpuRegisterLocation(XMM0));
+
+  // We have to ensure that the native code doesn't clobber the XMM registers which are
+  // non-volatile for ART, but volatile for Native calls.  This will ensure that they are
+  // saved in the prologue and properly restored.
+  for (auto fp_reg : non_volatile_xmm_regs) {
+    locations->AddTemp(Location::FpuRegisterLocation(fp_reg));
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAtan2(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAtan2(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickAtan2);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathHypot(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathHypot(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickHypot);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathNextAfter(HInvoke* invoke) {
+  CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathNextAfter(HInvoke* invoke) {
+  GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
+}
+
 void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
   // The inputs plus one temp.
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 680f89f..adde004 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -335,16 +335,24 @@
     return true;
   }
 
-  ReferenceInfo* GetOrCreateReferenceInfo(HInstruction* ref) {
-    ReferenceInfo* ref_info = FindReferenceInfoOf(ref);
+  ReferenceInfo* GetOrCreateReferenceInfo(HInstruction* instruction) {
+    ReferenceInfo* ref_info = FindReferenceInfoOf(instruction);
     if (ref_info == nullptr) {
       size_t pos = ref_info_array_.size();
-      ref_info = new (GetGraph()->GetArena()) ReferenceInfo(ref, pos);
+      ref_info = new (GetGraph()->GetArena()) ReferenceInfo(instruction, pos);
       ref_info_array_.push_back(ref_info);
     }
     return ref_info;
   }
 
+  void CreateReferenceInfoForReferenceType(HInstruction* instruction) {
+    if (instruction->GetType() != Primitive::kPrimNot) {
+      return;
+    }
+    DCHECK(FindReferenceInfoOf(instruction) == nullptr);
+    GetOrCreateReferenceInfo(instruction);
+  }
+
   HeapLocation* GetOrCreateHeapLocation(HInstruction* ref,
                                         size_t offset,
                                         HInstruction* index,
@@ -378,6 +386,7 @@
 
   void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE {
     VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
+    CreateReferenceInfoForReferenceType(instruction);
   }
 
   void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE {
@@ -387,6 +396,7 @@
 
   void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE {
     VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
+    CreateReferenceInfoForReferenceType(instruction);
   }
 
   void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE {
@@ -399,6 +409,7 @@
 
   void VisitArrayGet(HArrayGet* instruction) OVERRIDE {
     VisitArrayAccess(instruction->InputAt(0), instruction->InputAt(1));
+    CreateReferenceInfoForReferenceType(instruction);
   }
 
   void VisitArraySet(HArraySet* instruction) OVERRIDE {
@@ -408,7 +419,23 @@
 
   void VisitNewInstance(HNewInstance* new_instance) OVERRIDE {
     // Any references appearing in the ref_info_array_ so far cannot alias with new_instance.
-    GetOrCreateReferenceInfo(new_instance);
+    CreateReferenceInfoForReferenceType(new_instance);
+  }
+
+  void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE {
+    CreateReferenceInfoForReferenceType(instruction);
+  }
+
+  void VisitInvokeVirtual(HInvokeVirtual* instruction) OVERRIDE {
+    CreateReferenceInfoForReferenceType(instruction);
+  }
+
+  void VisitInvokeInterface(HInvokeInterface* instruction) OVERRIDE {
+    CreateReferenceInfoForReferenceType(instruction);
+  }
+
+  void VisitParameterValue(HParameterValue* instruction) OVERRIDE {
+    CreateReferenceInfoForReferenceType(instruction);
   }
 
   void VisitDeoptimize(HDeoptimize* instruction ATTRIBUTE_UNUSED) OVERRIDE {
@@ -725,7 +752,7 @@
           // instruction is a store in the loop so the loop must does write.
           DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
 
-          if (loop_info->IsLoopInvariant(original_ref, false)) {
+          if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
             DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
             // Keep the store since its value may be needed at the loop header.
             possibly_redundant = false;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 9b26de4..926bc15 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -587,15 +587,8 @@
   return other.blocks_.IsBitSet(header_->GetBlockId());
 }
 
-bool HLoopInformation::IsLoopInvariant(HInstruction* instruction, bool must_dominate) const {
-  HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation();
-  if (other_loop != this && (other_loop == nullptr || !other_loop->IsIn(*this))) {
-    if (must_dominate) {
-      return instruction->GetBlock()->Dominates(GetHeader());
-    }
-    return true;
-  }
-  return false;
+bool HLoopInformation::IsDefinedOutOfTheLoop(HInstruction* instruction) const {
+  return !blocks_.IsBitSet(instruction->GetBlock()->GetBlockId());
 }
 
 size_t HLoopInformation::GetLifetimeEnd() const {
@@ -1467,6 +1460,24 @@
   }
 }
 
+// Should be called on instructions in a dead block in post order. This method
+// assumes `insn` has been removed from all users with the exception of catch
+// phis because of missing exceptional edges in the graph. It removes the
+// instruction from catch phi uses, together with inputs of other catch phis in
+// the catch block at the same index, as these must be dead too.
+static void RemoveUsesOfDeadInstruction(HInstruction* insn) {
+  DCHECK(!insn->HasEnvironmentUses());
+  while (insn->HasNonEnvironmentUses()) {
+    HUseListNode<HInstruction*>* use = insn->GetUses().GetFirst();
+    size_t use_index = use->GetIndex();
+    HBasicBlock* user_block =  use->GetUser()->GetBlock();
+    DCHECK(use->GetUser()->IsPhi() && user_block->IsCatchBlock());
+    for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+      phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
+    }
+  }
+}
+
 void HBasicBlock::DisconnectAndDelete() {
   // Dominators must be removed after all the blocks they dominate. This way
   // a loop header is removed last, a requirement for correct loop information
@@ -1569,21 +1580,13 @@
   //     graph will always remain consistent.
   for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
     HInstruction* insn = it.Current();
-    while (insn->HasUses()) {
-      DCHECK(IsTryBlock());
-      HUseListNode<HInstruction*>* use = insn->GetUses().GetFirst();
-      size_t use_index = use->GetIndex();
-      HBasicBlock* user_block =  use->GetUser()->GetBlock();
-      DCHECK(use->GetUser()->IsPhi() && user_block->IsCatchBlock());
-      for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
-        phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
-      }
-    }
-
+    RemoveUsesOfDeadInstruction(insn);
     RemoveInstruction(insn);
   }
   for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
-    RemovePhi(it.Current()->AsPhi());
+    HPhi* insn = it.Current()->AsPhi();
+    RemoveUsesOfDeadInstruction(insn);
+    RemovePhi(insn);
   }
 
   // Disconnect from the dominator.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 9d3c88c..accd89e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -564,11 +564,8 @@
   // Note that `other` *must* be populated before entering this function.
   bool IsIn(const HLoopInformation& other) const;
 
-  // Returns true if instruction is not defined within this loop or any loop nested inside
-  // this loop. If must_dominate is set, only definitions that actually dominate the loop
-  // header can be invariant. Otherwise, any definition outside the loop, including
-  // definitions that appear after the loop, is invariant.
-  bool IsLoopInvariant(HInstruction* instruction, bool must_dominate) const;
+  // Returns true if instruction is not defined within this loop.
+  bool IsDefinedOutOfTheLoop(HInstruction* instruction) const;
 
   const ArenaBitVector& GetBlocks() const { return blocks_; }
 
@@ -4928,9 +4925,13 @@
 
 class HLoadString : public HExpression<1> {
  public:
-  HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc)
+  HLoadString(HCurrentMethod* current_method,
+              uint32_t string_index,
+              uint32_t dex_pc,
+              bool is_in_dex_cache)
       : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
-        string_index_(string_index) {
+        string_index_(string_index),
+        is_in_dex_cache_(is_in_dex_cache) {
     SetRawInputAt(0, current_method);
   }
 
@@ -4948,6 +4949,7 @@
   bool NeedsEnvironment() const OVERRIDE { return false; }
   bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
   bool CanBeNull() const OVERRIDE { return false; }
+  bool IsInDexCache() const { return is_in_dex_cache_; }
 
   static SideEffects SideEffectsForArchRuntimeCalls() {
     return SideEffects::CanTriggerGC();
@@ -4957,6 +4959,7 @@
 
  private:
   const uint32_t string_index_;
+  const bool is_in_dex_cache_;
 
   DISALLOW_COPY_AND_ASSIGN(HLoadString);
 };
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4643aed..831b626 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -614,7 +614,7 @@
       codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
       codegen->GetCoreSpillMask(),
       codegen->GetFpuSpillMask(),
-      ArrayRef<const SrcMapElem>(codegen->GetSrcMappingTable()),
+      ArrayRef<const SrcMapElem>(),
       ArrayRef<const uint8_t>(),  // mapping_table.
       ArrayRef<const uint8_t>(stack_map),
       ArrayRef<const uint8_t>(),  // native_gc_map.
@@ -648,7 +648,7 @@
       codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
       codegen->GetCoreSpillMask(),
       codegen->GetFpuSpillMask(),
-      ArrayRef<const SrcMapElem>(codegen->GetSrcMappingTable()),
+      ArrayRef<const SrcMapElem>(),
       AlignVectorSize(mapping_table),
       AlignVectorSize(vmap_table),
       AlignVectorSize(gc_map),
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 39e5259..48465e6 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -57,7 +57,7 @@
       __ LoadFromOffset(kLoadWord, PC, R0, offset.Int32Value());
       break;
     case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (R0).
-      __ LoadFromOffset(kLoadWord, IP, R0, JNIEnvExt::SelfOffset().Int32Value());
+      __ LoadFromOffset(kLoadWord, IP, R0, JNIEnvExt::SelfOffset(4).Int32Value());
       __ LoadFromOffset(kLoadWord, PC, IP, offset.Int32Value());
       break;
     case kQuickAbi:  // R9 holds Thread*.
@@ -91,7 +91,7 @@
     case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (X0).
       __ LoadRawPtr(Arm64ManagedRegister::FromXRegister(IP1),
                       Arm64ManagedRegister::FromXRegister(X0),
-                      Offset(JNIEnvExt::SelfOffset().Int32Value()));
+                      Offset(JNIEnvExt::SelfOffset(8).Int32Value()));
 
       __ JumpTo(Arm64ManagedRegister::FromXRegister(IP1), Offset(offset.Int32Value()),
                 Arm64ManagedRegister::FromXRegister(IP0));
@@ -126,7 +126,7 @@
       __ LoadFromOffset(kLoadWord, T9, A0, offset.Int32Value());
       break;
     case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (A0).
-      __ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
+      __ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset(4).Int32Value());
       __ LoadFromOffset(kLoadWord, T9, T9, offset.Int32Value());
       break;
     case kQuickAbi:  // S1 holds Thread*.
@@ -158,7 +158,7 @@
       __ LoadFromOffset(kLoadDoubleword, T9, A0, offset.Int32Value());
       break;
     case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (A0).
-      __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
+      __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset(8).Int32Value());
       __ LoadFromOffset(kLoadDoubleword, T9, T9, offset.Int32Value());
       break;
     case kQuickAbi:  // Fall-through.
diff --git a/compiler/trampolines/trampoline_compiler.h b/compiler/trampolines/trampoline_compiler.h
index 9fb2245..66d5ac3 100644
--- a/compiler/trampolines/trampoline_compiler.h
+++ b/compiler/trampolines/trampoline_compiler.h
@@ -25,12 +25,12 @@
 namespace art {
 
 // Create code that will invoke the function held in thread local storage.
-const std::vector<uint8_t>* CreateTrampoline32(InstructionSet isa, EntryPointCallingConvention abi,
-                                               ThreadOffset<4> entry_point_offset)
-    SHARED_REQUIRES(Locks::mutator_lock_);
-const std::vector<uint8_t>* CreateTrampoline64(InstructionSet isa, EntryPointCallingConvention abi,
-                                               ThreadOffset<8> entry_point_offset)
-    SHARED_REQUIRES(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateTrampoline32(InstructionSet isa,
+                                               EntryPointCallingConvention abi,
+                                               ThreadOffset<4> entry_point_offset);
+const std::vector<uint8_t>* CreateTrampoline64(InstructionSet isa,
+                                               EntryPointCallingConvention abi,
+                                               ThreadOffset<8> entry_point_offset);
 
 }  // namespace art
 
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 42f21e6..733ad2c 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -249,6 +249,11 @@
   EmitR(0, rs, rt, rd, 2, 0x18);
 }
 
+void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
+  CHECK(IsR6());
+  EmitR(0, rs, rt, rd, 3, 0x18);
+}
+
 void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
   CHECK(IsR6());
   EmitR(0, rs, rt, rd, 3, 0x19);
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index d50b4f6..62366f6 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -119,6 +119,7 @@
   void DivuR2(Register rd, Register rs, Register rt);  // R2
   void ModuR2(Register rd, Register rs, Register rt);  // R2
   void MulR6(Register rd, Register rs, Register rt);  // R6
+  void MuhR6(Register rd, Register rs, Register rt);  // R6
   void MuhuR6(Register rd, Register rs, Register rt);  // R6
   void DivR6(Register rd, Register rs, Register rt);  // R6
   void ModR6(Register rd, Register rs, Register rt);  // R6
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index e252765..77f8d6c 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -38,9 +38,10 @@
   dex2oat_target_arch := 32
 endif
 
-# We need to explcitly give the arch, as giving 'both' will make the
-# build-art-executable rule compile dex2oat for 64bits.
 ifeq ($(HOST_PREFER_32_BIT),true)
+  # We need to explicitly restrict the host arch to 32-bit only, as
+  # giving 'both' would make build-art-executable generate a build
+  # rule for a 64-bit dex2oat executable too.
   dex2oat_host_arch := 32
 else
   dex2oat_host_arch := both
@@ -56,19 +57,23 @@
 
 # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch)))
   ifeq ($(ART_BUILD_HOST_STATIC),true)
     $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixl liblog libz \
-        libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase,art/compiler,host,ndebug,$(dex2oat_host_arch),static))
+        libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch),static))
   endif
 endif
 
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host liblz4,art/compiler,host,debug,$(dex2oat_host_arch)))
   ifeq ($(ART_BUILD_HOST_STATIC),true)
     $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixld liblog libz \
-        libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase,art/compiler,host,debug,$(dex2oat_host_arch),static))
+        libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,debug,$(dex2oat_host_arch),static))
   endif
 endif
 
+# Clear locals now they've served their purpose.
+dex2oat_target_arch :=
+dex2oat_host_arch :=
+
 endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 2aa4085..6fae8e4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -71,6 +71,7 @@
 #include "oat_writer.h"
 #include "os.h"
 #include "runtime.h"
+#include "runtime_options.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "utils.h"
@@ -208,6 +209,11 @@
   UsageError("  --image=<file.art>: specifies the output image filename.");
   UsageError("      Example: --image=/system/framework/boot.art");
   UsageError("");
+  UsageError("  --image-format=(uncompressed|lz4):");
+  UsageError("      Which format to store the image.");
+  UsageError("      Example: --image-format=lz4");
+  UsageError("      Default: uncompressed");
+  UsageError("");
   UsageError("  --image-classes=<classname-file>: specifies classes to include in an image.");
   UsageError("      Example: --image=frameworks/base/preloaded-classes");
   UsageError("");
@@ -490,6 +496,7 @@
       image_base_(0U),
       image_classes_zip_filename_(nullptr),
       image_classes_filename_(nullptr),
+      image_storage_mode_(ImageHeader::kStorageModeUncompressed),
       compiled_classes_zip_filename_(nullptr),
       compiled_classes_filename_(nullptr),
       compiled_methods_zip_filename_(nullptr),
@@ -621,6 +628,19 @@
     }
   }
 
+  void ParseImageFormat(const StringPiece& option) {
+    const StringPiece substr("--image-format=");
+    DCHECK(option.starts_with(substr));
+    const StringPiece format_str = option.substr(substr.length());
+    if (format_str == "lz4") {
+      image_storage_mode_ = ImageHeader::kStorageModeLZ4;
+    } else if (format_str == "uncompressed") {
+      image_storage_mode_ = ImageHeader::kStorageModeUncompressed;
+    } else {
+      Usage("Unknown image format: %s", format_str.data());
+    }
+  }
+
   void ProcessOptions(ParserOptions* parser_options) {
     boot_image_ = !image_filename_.empty();
     app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
@@ -667,15 +687,14 @@
       parser_options->boot_image_filename += "/framework/boot.art";
     }
     if (!parser_options->boot_image_filename.empty()) {
-      boot_image_option_ += "-Ximage:";
-      boot_image_option_ += parser_options->boot_image_filename;
+      boot_image_filename_ = parser_options->boot_image_filename;
     }
 
     if (image_classes_filename_ != nullptr && !IsBootImage()) {
       Usage("--image-classes should only be used with --image");
     }
 
-    if (image_classes_filename_ != nullptr && !boot_image_option_.empty()) {
+    if (image_classes_filename_ != nullptr && !boot_image_filename_.empty()) {
       Usage("--image-classes should not be used with --boot-image");
     }
 
@@ -687,7 +706,7 @@
       Usage("--compiled-classes should only be used with --image");
     }
 
-    if (compiled_classes_filename_ != nullptr && !boot_image_option_.empty()) {
+    if (compiled_classes_filename_ != nullptr && !boot_image_filename_.empty()) {
       Usage("--compiled-classes should not be used with --boot-image");
     }
 
@@ -719,7 +738,7 @@
       Usage("--zip-location should be supplied with --zip-fd");
     }
 
-    if (boot_image_option_.empty()) {
+    if (boot_image_filename_.empty()) {
       if (image_base_ == 0) {
         Usage("Non-zero --base not specified");
       }
@@ -877,6 +896,8 @@
         image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
       } else if (option.starts_with("--image-classes-zip=")) {
         image_classes_zip_filename_ = option.substr(strlen("--image-classes-zip=")).data();
+      } else if (option.starts_with("--image-format=")) {
+        ParseImageFormat(option);
       } else if (option.starts_with("--compiled-classes=")) {
         compiled_classes_filename_ = option.substr(strlen("--compiled-classes=")).data();
       } else if (option.starts_with("--compiled-classes-zip=")) {
@@ -1014,20 +1035,10 @@
   // boot class path.
   bool Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
-    RuntimeOptions runtime_options;
     art::MemMap::Init();  // For ZipEntry::ExtractToMemMap.
-    if (boot_image_option_.empty()) {
-      std::string boot_class_path = "-Xbootclasspath:";
-      boot_class_path += Join(dex_filenames_, ':');
-      runtime_options.push_back(std::make_pair(boot_class_path, nullptr));
-      std::string boot_class_path_locations = "-Xbootclasspath-locations:";
-      boot_class_path_locations += Join(dex_locations_, ':');
-      runtime_options.push_back(std::make_pair(boot_class_path_locations, nullptr));
-    } else {
-      runtime_options.push_back(std::make_pair(boot_image_option_, nullptr));
-    }
-    for (size_t i = 0; i < runtime_args_.size(); i++) {
-      runtime_options.push_back(std::make_pair(runtime_args_[i], nullptr));
+
+    if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
+      return false;
     }
 
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
@@ -1037,23 +1048,15 @@
         IsBootImage() ?
             CompilerCallbacks::CallbackMode::kCompileBootImage :
             CompilerCallbacks::CallbackMode::kCompileApp));
-    runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
-    runtime_options.push_back(
-        std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
 
-    // Only allow no boot image for the runtime if we're compiling one. When we compile an app,
-    // we don't want fallback mode, it will abort as we do not push a boot classpath (it might
-    // have been stripped in preopting, anyways).
-    if (!IsBootImage()) {
-      runtime_options.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr));
+    RuntimeArgumentMap runtime_options;
+    if (!PrepareRuntimeOptions(&runtime_options)) {
+      return false;
     }
-    // Disable libsigchain. We don't don't need it during compilation and it prevents us
-    // from getting a statically linked version of dex2oat (because of dlsym and RTLD_NEXT).
-    runtime_options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
 
     {
       TimingLogger::ScopedTiming t_runtime("Create runtime", timings_);
-      if (!CreateRuntime(runtime_options)) {
+      if (!CreateRuntime(std::move(runtime_options))) {
         return false;
       }
     }
@@ -1068,66 +1071,8 @@
     // Whilst we're in native take the opportunity to initialize well known classes.
     WellKnownClasses::Init(self->GetJniEnv());
 
-    // If --image-classes was specified, calculate the full list of classes to include in the image
-    if (image_classes_filename_ != nullptr) {
-      std::string error_msg;
-      if (image_classes_zip_filename_ != nullptr) {
-        image_classes_.reset(ReadImageClassesFromZip(image_classes_zip_filename_,
-                                                     image_classes_filename_,
-                                                     &error_msg));
-      } else {
-        image_classes_.reset(ReadImageClassesFromFile(image_classes_filename_));
-      }
-      if (image_classes_.get() == nullptr) {
-        LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename_ <<
-            "': " << error_msg;
-        return false;
-      }
-    } else if (IsBootImage()) {
-      image_classes_.reset(new std::unordered_set<std::string>);
-    }
-    // If --compiled-classes was specified, calculate the full list of classes to compile in the
-    // image.
-    if (compiled_classes_filename_ != nullptr) {
-      std::string error_msg;
-      if (compiled_classes_zip_filename_ != nullptr) {
-        compiled_classes_.reset(ReadImageClassesFromZip(compiled_classes_zip_filename_,
-                                                        compiled_classes_filename_,
-                                                        &error_msg));
-      } else {
-        compiled_classes_.reset(ReadImageClassesFromFile(compiled_classes_filename_));
-      }
-      if (compiled_classes_.get() == nullptr) {
-        LOG(ERROR) << "Failed to create list of compiled classes from '"
-                   << compiled_classes_filename_ << "': " << error_msg;
-        return false;
-      }
-    } else {
-      compiled_classes_.reset(nullptr);  // By default compile everything.
-    }
-    // If --compiled-methods was specified, read the methods to compile from the given file(s).
-    if (compiled_methods_filename_ != nullptr) {
-      std::string error_msg;
-      if (compiled_methods_zip_filename_ != nullptr) {
-        compiled_methods_.reset(ReadCommentedInputFromZip(compiled_methods_zip_filename_,
-                                                          compiled_methods_filename_,
-                                                          nullptr,            // No post-processing.
-                                                          &error_msg));
-      } else {
-        compiled_methods_.reset(ReadCommentedInputFromFile(compiled_methods_filename_,
-                                                           nullptr));         // No post-processing.
-      }
-      if (compiled_methods_.get() == nullptr) {
-        LOG(ERROR) << "Failed to create list of compiled methods from '"
-            << compiled_methods_filename_ << "': " << error_msg;
-        return false;
-      }
-    } else {
-      compiled_methods_.reset(nullptr);  // By default compile everything.
-    }
-
     ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-    if (boot_image_option_.empty()) {
+    if (boot_image_filename_.empty()) {
       dex_files_ = class_linker->GetBootClassPath();
     } else {
       TimingLogger::ScopedTiming t_dex("Opening dex files", timings_);
@@ -1164,22 +1109,7 @@
 
       constexpr bool kSaveDexInput = false;
       if (kSaveDexInput) {
-        for (size_t i = 0; i < dex_files_.size(); ++i) {
-          const DexFile* dex_file = dex_files_[i];
-          std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex",
-                                                 getpid(), i));
-          std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
-          if (tmp_file.get() == nullptr) {
-            PLOG(ERROR) << "Failed to open file " << tmp_file_name
-                << ". Try: adb shell chmod 777 /data/local/tmp";
-            continue;
-          }
-          // This is just dumping files for debugging. Ignore errors, and leave remnants.
-          UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
-          UNUSED(tmp_file->Flush());
-          UNUSED(tmp_file->Close());
-          LOG(INFO) << "Wrote input to " << tmp_file_name;
-        }
+        SaveDexInput();
       }
     }
     // Ensure opened dex files are writable for dex-to-dex transformations. Also ensure that
@@ -1238,16 +1168,13 @@
     jobject class_path_class_loader = nullptr;
     Thread* self = Thread::Current();
 
-    if (!boot_image_option_.empty()) {
+    if (!boot_image_filename_.empty()) {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
       ScopedObjectAccess soa(self);
 
       // Classpath: first the class-path given.
-      std::vector<const DexFile*> class_path_files;
-      for (auto& class_path_file : class_path_files_) {
-        class_path_files.push_back(class_path_file.get());
-      }
+      std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
 
       // Store the classpath we have right now.
       key_value_store_->Put(OatHeader::kClassPathKey,
@@ -1432,14 +1359,9 @@
       elf_writer->EndText(text);
 
       elf_writer->SetBssSize(oat_writer->GetBssSize());
-
       elf_writer->WriteDynamicSection();
-
-      ArrayRef<const dwarf::MethodDebugInfo> method_infos(oat_writer->GetMethodDebugInfo());
-      elf_writer->WriteDebugInfo(method_infos);
-
-      ArrayRef<const uintptr_t> patch_locations(oat_writer->GetAbsolutePatchLocations());
-      elf_writer->WritePatchLocations(patch_locations);
+      elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
+      elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
 
       if (!elf_writer->End()) {
         LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
@@ -1552,6 +1474,16 @@
   }
 
  private:
+  template <typename T>
+  static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
+    std::vector<T*> result;
+    result.reserve(src.size());
+    for (const std::unique_ptr<T>& t : src) {
+      result.push_back(t.get());
+    }
+    return result;
+  }
+
   static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
                              const std::vector<const char*>& dex_locations,
                              std::vector<std::unique_ptr<const DexFile>>* dex_files) {
@@ -1612,10 +1544,138 @@
     }
   }
 
+  bool PrepareImageClasses() {
+    // If --image-classes was specified, calculate the full list of classes to include in the image.
+    if (image_classes_filename_ != nullptr) {
+      image_classes_ =
+          ReadClasses(image_classes_zip_filename_, image_classes_filename_, "image");
+      if (image_classes_ == nullptr) {
+        return false;
+      }
+    } else if (IsBootImage()) {
+      image_classes_.reset(new std::unordered_set<std::string>);
+    }
+    return true;
+  }
+
+  bool PrepareCompiledClasses() {
+    // If --compiled-classes was specified, calculate the full list of classes to compile in the
+    // image.
+    if (compiled_classes_filename_ != nullptr) {
+      compiled_classes_ =
+          ReadClasses(compiled_classes_zip_filename_, compiled_classes_filename_, "compiled");
+      if (compiled_classes_ == nullptr) {
+        return false;
+      }
+    } else {
+      compiled_classes_.reset(nullptr);  // By default compile everything.
+    }
+    return true;
+  }
+
+  static std::unique_ptr<std::unordered_set<std::string>> ReadClasses(const char* zip_filename,
+                                                                      const char* classes_filename,
+                                                                      const char* tag) {
+    std::unique_ptr<std::unordered_set<std::string>> classes;
+    std::string error_msg;
+    if (zip_filename != nullptr) {
+      classes.reset(ReadImageClassesFromZip(zip_filename, classes_filename, &error_msg));
+    } else {
+      classes.reset(ReadImageClassesFromFile(classes_filename));
+    }
+    if (classes == nullptr) {
+      LOG(ERROR) << "Failed to create list of " << tag << " classes from '"
+                 << classes_filename << "': " << error_msg;
+    }
+    return classes;
+  }
+
+  bool PrepareCompiledMethods() {
+    // If --compiled-methods was specified, read the methods to compile from the given file(s).
+    if (compiled_methods_filename_ != nullptr) {
+      std::string error_msg;
+      if (compiled_methods_zip_filename_ != nullptr) {
+        compiled_methods_.reset(ReadCommentedInputFromZip(compiled_methods_zip_filename_,
+                                                          compiled_methods_filename_,
+                                                          nullptr,            // No post-processing.
+                                                          &error_msg));
+      } else {
+        compiled_methods_.reset(ReadCommentedInputFromFile(compiled_methods_filename_,
+                                                           nullptr));         // No post-processing.
+      }
+      if (compiled_methods_.get() == nullptr) {
+        LOG(ERROR) << "Failed to create list of compiled methods from '"
+            << compiled_methods_filename_ << "': " << error_msg;
+        return false;
+      }
+    } else {
+      compiled_methods_.reset(nullptr);  // By default compile everything.
+    }
+    return true;
+  }
+
+  void SaveDexInput() {
+    for (size_t i = 0; i < dex_files_.size(); ++i) {
+      const DexFile* dex_file = dex_files_[i];
+      std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex",
+                                             getpid(), i));
+      std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
+      if (tmp_file.get() == nullptr) {
+        PLOG(ERROR) << "Failed to open file " << tmp_file_name
+            << ". Try: adb shell chmod 777 /data/local/tmp";
+        continue;
+      }
+      // This is just dumping files for debugging. Ignore errors, and leave remnants.
+      UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
+      UNUSED(tmp_file->Flush());
+      UNUSED(tmp_file->Close());
+      LOG(INFO) << "Wrote input to " << tmp_file_name;
+    }
+  }
+
+  bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options) {
+    RuntimeOptions raw_options;
+    if (boot_image_filename_.empty()) {
+      std::string boot_class_path = "-Xbootclasspath:";
+      boot_class_path += Join(dex_filenames_, ':');
+      raw_options.push_back(std::make_pair(boot_class_path, nullptr));
+      std::string boot_class_path_locations = "-Xbootclasspath-locations:";
+      boot_class_path_locations += Join(dex_locations_, ':');
+      raw_options.push_back(std::make_pair(boot_class_path_locations, nullptr));
+    } else {
+      std::string boot_image_option = "-Ximage:";
+      boot_image_option += boot_image_filename_;
+      raw_options.push_back(std::make_pair(boot_image_option, nullptr));
+    }
+    for (size_t i = 0; i < runtime_args_.size(); i++) {
+      raw_options.push_back(std::make_pair(runtime_args_[i], nullptr));
+    }
+
+    raw_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+    raw_options.push_back(
+        std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
+
+    // Only allow no boot image for the runtime if we're compiling one. When we compile an app,
+    // we don't want fallback mode, it will abort as we do not push a boot classpath (it might
+    // have been stripped in preopting, anyways).
+    if (!IsBootImage()) {
+      raw_options.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr));
+    }
+    // Disable libsigchain. We don't don't need it during compilation and it prevents us
+    // from getting a statically linked version of dex2oat (because of dlsym and RTLD_NEXT).
+    raw_options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+
+    if (!Runtime::ParseOptions(raw_options, false, runtime_options)) {
+      LOG(ERROR) << "Failed to parse runtime options";
+      return false;
+    }
+    return true;
+  }
+
   // Create a runtime necessary for compilation.
-  bool CreateRuntime(const RuntimeOptions& runtime_options)
+  bool CreateRuntime(RuntimeArgumentMap&& runtime_options)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    if (!Runtime::Create(runtime_options, false)) {
+    if (!Runtime::Create(std::move(runtime_options))) {
       LOG(ERROR) << "Failed to create runtime";
       return false;
     }
@@ -1643,7 +1703,8 @@
     image_writer_.reset(new ImageWriter(*driver_,
                                         image_base,
                                         compiler_options_->GetCompilePic(),
-                                        IsAppImage()));
+                                        IsAppImage(),
+                                        image_storage_mode_));
   }
 
   // Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file.
@@ -1812,12 +1873,13 @@
   std::vector<const char*> dex_locations_;
   int zip_fd_;
   std::string zip_location_;
-  std::string boot_image_option_;
+  std::string boot_image_filename_;
   std::vector<const char*> runtime_args_;
   std::string image_filename_;
   uintptr_t image_base_;
   const char* image_classes_zip_filename_;
   const char* image_classes_filename_;
+  ImageHeader::StorageMode image_storage_mode_;
   const char* compiled_classes_zip_filename_;
   const char* compiled_classes_filename_;
   const char* compiled_methods_zip_filename_;
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 52e6c02..d387726 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -27,7 +27,6 @@
  * Differences between XML output and the "current.xml" file:
  * - classes in same package are not all grouped together; nothing is sorted
  * - no "deprecated" on fields and methods
- * - no "value" on fields
  * - no parameter names
  * - no generic signatures on parameters, e.g. type="java.lang.Class&lt;?&gt;"
  * - class shows declared fields and methods; does not show inherited fields
@@ -1019,9 +1018,126 @@
 }
 
 /*
+ * Dumps a string value with some escape characters.
+ */
+static void dumpEscapedString(const char* p) {
+  for (; *p; p++) {
+    switch (*p) {
+      case '\\':
+        fputs("\\\\", gOutFile);
+        break;
+      case '\"':
+        fputs("\\\"", gOutFile);
+        break;
+      case '\t':
+        fputs("\\t", gOutFile);
+        break;
+      case '\n':
+        fputs("\\n", gOutFile);
+        break;
+      case '\r':
+        fputs("\\r", gOutFile);
+        break;
+      default:
+        putc(*p, gOutFile);
+    }
+  }
+}
+
+/*
+ * Dumps an XML attribute value between double-quotes.
+ */
+static void dumpXmlAttribute(const char* p) {
+  for (; *p; p++) {
+    switch (*p) {
+      case '&':
+        fputs("&amp;", gOutFile);
+        break;
+      case '<':
+        fputs("&lt;", gOutFile);
+        break;
+      case '"':
+        fputs("&quot;", gOutFile);
+        break;
+      case '\t':
+        fputs("&#x9;", gOutFile);
+        break;
+      case '\n':
+        fputs("&#xA;", gOutFile);
+        break;
+      case '\r':
+        fputs("&#xD;", gOutFile);
+        break;
+      default:
+        putc(*p, gOutFile);
+    }
+  }
+}
+
+/*
+ * Dumps a value of static (class) field.
+ */
+static void dumpSFieldValue(const DexFile* pDexFile,
+                            EncodedStaticFieldValueIterator::ValueType valueType,
+                            const jvalue* pValue) {
+  switch (valueType) {
+    case EncodedStaticFieldValueIterator::kByte:
+      fprintf(gOutFile, "%" PRIu8, pValue->b);
+      break;
+    case EncodedStaticFieldValueIterator::kShort:
+      fprintf(gOutFile, "%" PRId16, pValue->s);
+      break;
+    case EncodedStaticFieldValueIterator::kChar:
+      fprintf(gOutFile, "%" PRIu16, pValue->c);
+      break;
+    case EncodedStaticFieldValueIterator::kInt:
+      fprintf(gOutFile, "%" PRId32, pValue->i);
+      break;
+    case EncodedStaticFieldValueIterator::kLong:
+      fprintf(gOutFile, "%" PRId64, pValue->j);
+      break;
+    case EncodedStaticFieldValueIterator::kFloat:
+      fprintf(gOutFile, "%f", pValue->f);
+      break;
+    case EncodedStaticFieldValueIterator::kDouble:
+      fprintf(gOutFile, "%f", pValue->d);
+      break;
+    case EncodedStaticFieldValueIterator::kString: {
+      const char* str =
+          pDexFile->GetStringData(pDexFile->GetStringId(pValue->i));
+      if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        fputs("\"", gOutFile);
+        dumpEscapedString(str);
+        fputs("\"", gOutFile);
+      } else {
+        dumpXmlAttribute(str);
+      }
+      break;
+    }
+    case EncodedStaticFieldValueIterator::kNull:
+      fputs("null", gOutFile);
+      break;
+    case EncodedStaticFieldValueIterator::kBoolean:
+      fputs(pValue->z ? "true" : "false", gOutFile);
+      break;
+
+    case EncodedStaticFieldValueIterator::kAnnotation:
+    case EncodedStaticFieldValueIterator::kArray:
+    case EncodedStaticFieldValueIterator::kEnum:
+    case EncodedStaticFieldValueIterator::kField:
+    case EncodedStaticFieldValueIterator::kMethod:
+    case EncodedStaticFieldValueIterator::kType:
+    default:
+      fprintf(gOutFile, "Unexpected static field type: %d", valueType);
+  }
+}
+
+/*
  * Dumps a static (class) field.
  */
-static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
+static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i,
+                       EncodedStaticFieldValueIterator::ValueType valueType,
+                       const jvalue* pValue) {
   // Bail for anything private if export only requested.
   if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) {
     return;
@@ -1038,6 +1154,11 @@
     fprintf(gOutFile, "      name          : '%s'\n", name);
     fprintf(gOutFile, "      type          : '%s'\n", typeDescriptor);
     fprintf(gOutFile, "      access        : 0x%04x (%s)\n", flags, accessStr);
+    if (pValue != nullptr) {
+      fputs("      value         : ", gOutFile);
+      dumpSFieldValue(pDexFile, valueType, pValue);
+      fputs("\n", gOutFile);
+    }
   } else if (gOptions.outputFormat == OUTPUT_XML) {
     fprintf(gOutFile, "<field name=\"%s\"\n", name);
     char *tmp = descriptorToDot(typeDescriptor);
@@ -1050,7 +1171,12 @@
     fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0));
     // The "deprecated=" is not knowable w/o parsing annotations.
     fprintf(gOutFile, " visibility=%s\n", quotedVisibility(flags));
-    fprintf(gOutFile, ">\n</field>\n");
+    if (pValue != nullptr) {
+      fputs(" value=\"", gOutFile);
+      dumpSFieldValue(pDexFile, valueType, pValue);
+      fputs("\"\n", gOutFile);
+    }
+    fputs(">\n</field>\n", gOutFile);
   }
 
   free(accessStr);
@@ -1060,7 +1186,8 @@
  * Dumps an instance field.
  */
 static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
-  dumpSField(pDexFile, idx, flags, i);
+  dumpSField(pDexFile, idx, flags, i,
+             EncodedStaticFieldValueIterator::kByte, nullptr);
 }
 
 /*
@@ -1222,10 +1349,23 @@
     if (gOptions.outputFormat == OUTPUT_PLAIN) {
       fprintf(gOutFile, "  Static fields     -\n");
     }
+    EncodedStaticFieldValueIterator staticFieldValues(*pDexFile, pClassDef);
     for (int i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) {
+      EncodedStaticFieldValueIterator::ValueType valueType =
+          EncodedStaticFieldValueIterator::kByte;
+      const jvalue* pValue = nullptr;
+      if (staticFieldValues.HasNext()) {
+        valueType = staticFieldValues.GetValueType();
+        pValue = &staticFieldValues.GetJavaValue();
+      }
       dumpSField(pDexFile, pClassData.GetMemberIndex(),
-                           pClassData.GetRawMemberAccessFlags(), i);
+                           pClassData.GetRawMemberAccessFlags(), i,
+                 valueType, pValue);
+      if (staticFieldValues.HasNext()) {
+        staticFieldValues.Next();
+      }
     }  // for
+    DCHECK(!staticFieldValues.HasNext());
     if (gOptions.outputFormat == OUTPUT_PLAIN) {
       fprintf(gOutFile, "  Instance fields   -\n");
     }
diff --git a/imgdiag/Android.mk b/imgdiag/Android.mk
index d5d7c22..83315be 100644
--- a/imgdiag/Android.mk
+++ b/imgdiag/Android.mk
@@ -25,4 +25,8 @@
 # that the image it's analyzing be the same ISA as the runtime ISA.
 
 # Build variants {target,host} x {debug,ndebug} x {32,64}
-$(eval $(call build-art-multi-executable,imgdiag,$(IMGDIAG_SRC_FILES),libart-compiler libbacktrace,libcutils,libziparchive-host,art/compiler,both))
+#
+# Honor HOST_PREFER_32_BIT, as building a 64-bit imgdiag executable
+# when HOST_PREFER_32_BIT is true would require an unmet dependency on
+# 64-bit libbacktrace.
+$(eval $(call build-art-multi-executable,imgdiag,$(IMGDIAG_SRC_FILES),libart-compiler libbacktrace,libcutils,libziparchive-host,art/compiler,both,$(HOST_PREFER_32_BIT)))
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 723bb17..46ab34b 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -153,6 +153,12 @@
     return false;
   }
 
+  if (image_header.GetStorageMode() != ImageHeader::kStorageModeUncompressed) {
+    LOG(ERROR) << "Patchoat is not supported with compressed image files "
+               << input_image->GetPath();
+    return false;
+  }
+
   /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
   // Nothing special to do right now since the image always needs to get patched.
   // Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
diff --git a/runtime/Android.mk b/runtime/Android.mk
index f0bb10d..f2d7f17 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -501,9 +501,9 @@
   LOCAL_C_INCLUDES += art
 
   ifeq ($$(art_static_or_shared),static)
-    LOCAL_STATIC_LIBRARIES := libnativehelper libnativebridge libsigchain_dummy libbacktrace
+    LOCAL_STATIC_LIBRARIES := libnativehelper libnativebridge libsigchain_dummy libbacktrace liblz4
   else
-    LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain libbacktrace
+    LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain libbacktrace liblz4
   endif
 
   ifeq ($$(art_target_or_host),target)
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index e200018..aca49cc 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -93,6 +93,25 @@
   qpoints->pLockObject = art_quick_lock_object;
   qpoints->pUnlockObject = art_quick_unlock_object;
 
+  // More math.
+  qpoints->pCos = cos;
+  qpoints->pSin = sin;
+  qpoints->pAcos = acos;
+  qpoints->pAsin = asin;
+  qpoints->pAtan = atan;
+  qpoints->pAtan2 = atan2;
+  qpoints->pCbrt = cbrt;
+  qpoints->pCosh = cosh;
+  qpoints->pExp = exp;
+  qpoints->pExpm1 = expm1;
+  qpoints->pHypot = hypot;
+  qpoints->pLog = log;
+  qpoints->pLog10 = log10;
+  qpoints->pNextAfter = nextafter;
+  qpoints->pSinh = sinh;
+  qpoints->pTan = tan;
+  qpoints->pTanh = tanh;
+
   // Math
   qpoints->pD2l = art_quick_d2l;
   qpoints->pF2l = art_quick_f2l;
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 2b38c9d..ebe6d40 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -98,6 +98,25 @@
   qpoints->pLockObject = art_quick_lock_object;
   qpoints->pUnlockObject = art_quick_unlock_object;
 
+  // More math.
+  qpoints->pCos = cos;
+  qpoints->pSin = sin;
+  qpoints->pAcos = acos;
+  qpoints->pAsin = asin;
+  qpoints->pAtan = atan;
+  qpoints->pAtan2 = atan2;
+  qpoints->pCbrt = cbrt;
+  qpoints->pCosh = cosh;
+  qpoints->pExp = exp;
+  qpoints->pExpm1 = expm1;
+  qpoints->pHypot = hypot;
+  qpoints->pLog = log;
+  qpoints->pLog10 = log10;
+  qpoints->pNextAfter = nextafter;
+  qpoints->pSinh = sinh;
+  qpoints->pTan = tan;
+  qpoints->pTanh = tanh;
+
   // Math
   qpoints->pD2l = art_d2l;
   qpoints->pF2l = art_f2l;
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index b548dfb..29c8232 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -122,7 +122,7 @@
             art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
 
 // Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 151 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
             art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
 // Offset of field Thread::tlsPtr_.thread_local_end.
diff --git a/runtime/base/dchecked_vector.h b/runtime/base/dchecked_vector.h
index 2bd12df..51dfba8 100644
--- a/runtime/base/dchecked_vector.h
+++ b/runtime/base/dchecked_vector.h
@@ -33,7 +33,7 @@
 // but we do not use exceptions, so this accessor is deliberately hidden.
 // Note: The common pattern &v[0] used to retrieve pointer to the data is not
 // valid for an empty dchecked_vector<>. Use data() to avoid checking empty().
-template <typename T, typename Alloc>
+template <typename T, typename Alloc = std::allocator<T>>
 class dchecked_vector : private std::vector<T, Alloc> {
  private:
   // std::vector<> has a slightly different specialization for bool. We don't provide that.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0a37f26..f5085ed 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3056,10 +3056,12 @@
   verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
   std::string error_msg;
   if (!preverified) {
+    Runtime* runtime = Runtime::Current();
     verifier_failure = verifier::MethodVerifier::VerifyClass(self,
                                                              klass.Get(),
-                                                             Runtime::Current()->IsAotCompiler(),
-                                                             Runtime::Current()->IsAotCompiler(),
+                                                             runtime->GetCompilerCallbacks(),
+                                                             runtime->IsAotCompiler(),
+                                                             runtime->IsAotCompiler(),
                                                              &error_msg);
   }
   if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index af7b04f..a39d682 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -37,8 +37,8 @@
 
   virtual ~CompilerCallbacks() { }
 
-  virtual bool MethodVerified(verifier::MethodVerifier* verifier)
-  SHARED_REQUIRES(Locks::mutator_lock_) = 0;
+  virtual void MethodVerified(verifier::MethodVerifier* verifier)
+      SHARED_REQUIRES(Locks::mutator_lock_) = 0;
   virtual void ClassRejected(ClassReference ref) = 0;
 
   // Return true if we should attempt to relocate to a random base address if we have not already
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 30d921a..4e15e80 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -2208,14 +2208,24 @@
 }
 
 EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(
+    const DexFile& dex_file,
+    const DexFile::ClassDef& class_def)
+    : EncodedStaticFieldValueIterator(dex_file, nullptr, nullptr,
+                                      nullptr, class_def) {
+}
+
+EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(
     const DexFile& dex_file, Handle<mirror::DexCache>* dex_cache,
     Handle<mirror::ClassLoader>* class_loader, ClassLinker* linker,
     const DexFile::ClassDef& class_def)
-    : dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), linker_(linker),
-      array_size_(), pos_(-1), type_(kByte) {
-  DCHECK(dex_cache != nullptr);
-  DCHECK(class_loader != nullptr);
-  ptr_ = dex_file.GetEncodedStaticFieldValuesArray(class_def);
+    : dex_file_(dex_file),
+      dex_cache_(dex_cache),
+      class_loader_(class_loader),
+      linker_(linker),
+      array_size_(),
+      pos_(-1),
+      type_(kByte) {
+  ptr_ = dex_file_.GetEncodedStaticFieldValuesArray(class_def);
   if (ptr_ == nullptr) {
     array_size_ = 0;
   } else {
@@ -2288,6 +2298,8 @@
 
 template<bool kTransactionActive>
 void EncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) const {
+  DCHECK(dex_cache_ != nullptr);
+  DCHECK(class_loader_ != nullptr);
   switch (type_) {
     case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z);
         break;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 1e44f50..6b019f1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1510,6 +1510,12 @@
 
 class EncodedStaticFieldValueIterator {
  public:
+  // A constructor for static tools. You cannot call
+  // ReadValueToField() for an object created by this.
+  EncodedStaticFieldValueIterator(const DexFile& dex_file,
+                                  const DexFile::ClassDef& class_def);
+
+  // A constructor meant to be called from runtime code.
   EncodedStaticFieldValueIterator(const DexFile& dex_file, Handle<mirror::DexCache>* dex_cache,
                                   Handle<mirror::ClassLoader>* class_loader,
                                   ClassLinker* linker, const DexFile::ClassDef& class_def)
@@ -1541,6 +1547,9 @@
     kBoolean = 0x1f
   };
 
+  ValueType GetValueType() const { return type_; }
+  const jvalue& GetJavaValue() const { return jval_; }
+
  private:
   static constexpr uint8_t kEncodedValueTypeMask = 0x1f;  // 0b11111
   static constexpr uint8_t kEncodedValueArgShift = 5;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index ee7b986..3eea723 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -86,6 +86,23 @@
   V(CmpgFloat, int32_t, float, float) \
   V(CmplDouble, int32_t, double, double) \
   V(CmplFloat, int32_t, float, float) \
+  V(Cos, double, double) \
+  V(Sin, double, double) \
+  V(Acos, double, double) \
+  V(Asin, double, double) \
+  V(Atan, double, double) \
+  V(Atan2, double, double, double) \
+  V(Cbrt, double, double) \
+  V(Cosh, double, double) \
+  V(Exp, double, double) \
+  V(Expm1, double, double) \
+  V(Hypot, double, double, double) \
+  V(Log, double, double) \
+  V(Log10, double, double) \
+  V(NextAfter, double, double, double) \
+  V(Sinh, double, double) \
+  V(Tan, double, double) \
+  V(Tanh, double, double) \
   V(Fmod, double, double, double) \
   V(L2d, double, int64_t) \
   V(Fmodf, float, float, float) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 8587ede..391eb72 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -223,7 +223,24 @@
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgDouble, pCmpgFloat, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgFloat, pCmplDouble, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplDouble, pCmplFloat, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplFloat, pFmod, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplFloat, pCos, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCos, pSin, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSin, pAcos, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAcos, pAsin, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAsin, pAtan, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAtan, pAtan2, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAtan2, pCbrt, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCbrt, pCosh, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCosh, pExp, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pExp, pExpm1, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pExpm1, pHypot, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHypot, pLog, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLog, pLog10, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLog10, pNextAfter, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNextAfter, pSinh, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSinh, pTan, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTan, pTanh, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTanh, pFmod, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmod, pL2d, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pL2d, pFmodf, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmodf, pL2f, sizeof(void*));
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index f741117..4884e66 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -113,7 +113,9 @@
         /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size),
         /*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size),
         /*pointer_size*/sizeof(void*),
-        /*compile_pic*/false);
+        /*compile_pic*/false,
+        ImageHeader::kStorageModeUncompressed,
+        /*storage_size*/0u);
     return new DummyImageSpace(map.release(), live_bitmap.release());
   }
 };
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e2b2431..8f67c21 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -17,12 +17,12 @@
 #include "image_space.h"
 
 #include <dirent.h>
+#include <lz4.h>
+#include <random>
 #include <sys/statvfs.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <random>
-
 #include "art_method.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
@@ -677,11 +677,12 @@
     *error_msg = StringPrintf("Invalid image header in '%s'", image_filename);
     return nullptr;
   }
-  // Check that the file is large enough.
-  uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
-  if (image_header.GetImageSize() > image_file_size) {
-    *error_msg = StringPrintf("Image file too small for image heap: %" PRIu64 " vs. %zu.",
-                              image_file_size, image_header.GetImageSize());
+  // Check that the file is larger or equal to the header size + data size.
+  const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
+  if (image_file_size < sizeof(ImageHeader) + image_header.GetDataSize()) {
+    *error_msg = StringPrintf("Image file truncated: %" PRIu64 " vs. %" PRIu64 ".",
+                              image_file_size,
+                              image_header.GetDataSize());
     return nullptr;
   }
 
@@ -697,7 +698,11 @@
   }
 
   const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap);
-  auto end_of_bitmap = static_cast<size_t>(bitmap_section.End());
+  // The location we want to map from is the first aligned page after the end of the stored
+  // (possibly compressed) data.
+  const size_t image_bitmap_offset = RoundUp(sizeof(image_header) + image_header.GetDataSize(),
+                                             kPageSize);
+  const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size();
   if (end_of_bitmap != image_file_size) {
     *error_msg = StringPrintf(
         "Image file size does not equal end of bitmap: size=%" PRIu64 " vs. %zu.", image_file_size,
@@ -706,16 +711,60 @@
   }
 
   // Note: The image header is part of the image due to mmap page alignment required of offset.
-  std::unique_ptr<MemMap> map(MemMap::MapFileAtAddress(image_header.GetImageBegin(),
-                                                       image_header.GetImageSize(),
-                                                       PROT_READ | PROT_WRITE,
+  std::unique_ptr<MemMap> map;
+  if (image_header.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+    map.reset(MemMap::MapFileAtAddress(image_header.GetImageBegin(),
+                                       image_header.GetImageSize(),
+                                       PROT_READ | PROT_WRITE,
+                                       MAP_PRIVATE,
+                                       file->Fd(),
+                                       0,
+                                       /*low_4gb*/false,
+                                       /*reuse*/false,
+                                       image_filename,
+                                       error_msg));
+  } else {
+    // Reserve output and decompress into it.
+    map.reset(MemMap::MapAnonymous(image_location,
+                                   image_header.GetImageBegin(),
+                                   image_header.GetImageSize(),
+                                   PROT_READ | PROT_WRITE,
+                                   /*low_4gb*/false,
+                                   /*reuse*/false,
+                                   error_msg));
+    if (map != nullptr) {
+      const size_t stored_size = image_header.GetDataSize();
+      const size_t write_offset = sizeof(image_header);  // Skip the header.
+      std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
+                                                       PROT_READ,
                                                        MAP_PRIVATE,
                                                        file->Fd(),
-                                                       0,
+                                                       /*offset*/0,
                                                        /*low_4gb*/false,
-                                                       /*reuse*/false,
                                                        image_filename,
                                                        error_msg));
+      if (temp_map == nullptr) {
+        DCHECK(!error_msg->empty());
+        return nullptr;
+      }
+      memcpy(map->Begin(), &image_header, sizeof(image_header));
+      const uint64_t start = NanoTime();
+      const size_t decompressed_size = LZ4_decompress_safe(
+          reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
+          reinterpret_cast<char*>(map->Begin()) + write_offset,
+          stored_size,
+          map->Size());
+      // TODO: VLOG(image)
+      VLOG(class_linker) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
+      if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
+        *error_msg = StringPrintf("Decompressed size does not match expected image size %zu vs %zu",
+                                  decompressed_size + sizeof(ImageHeader),
+                                  image_header.GetImageSize());
+        return nullptr;
+      }
+    }
+  }
+
   if (map == nullptr) {
     DCHECK(!error_msg->empty());
     return nullptr;
@@ -723,16 +772,16 @@
   CHECK_EQ(image_header.GetImageBegin(), map->Begin());
   DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader)));
 
-  std::unique_ptr<MemMap> image_map(MemMap::MapFileAtAddress(nullptr,
-                                                             bitmap_section.Size(),
-                                                             PROT_READ, MAP_PRIVATE,
-                                                             file->Fd(),
-                                                             bitmap_section.Offset(),
-                                                             /*low_4gb*/false,
-                                                             /*reuse*/false,
-                                                             image_filename,
-                                                             error_msg));
-  if (image_map.get() == nullptr) {
+  std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr,
+                                                                    bitmap_section.Size(),
+                                                                    PROT_READ, MAP_PRIVATE,
+                                                                    file->Fd(),
+                                                                    image_bitmap_offset,
+                                                                    /*low_4gb*/false,
+                                                                    /*reuse*/false,
+                                                                    image_filename,
+                                                                    error_msg));
+  if (image_bitmap_map == nullptr) {
     *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
     return nullptr;
   }
@@ -741,9 +790,11 @@
                                        bitmap_index));
   std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(
       accounting::ContinuousSpaceBitmap::CreateFromMemMap(
-          bitmap_name, image_map.release(), reinterpret_cast<uint8_t*>(map->Begin()),
+          bitmap_name,
+          image_bitmap_map.release(),
+          reinterpret_cast<uint8_t*>(map->Begin()),
           accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_section.Size())));
-  if (bitmap.get() == nullptr) {
+  if (bitmap == nullptr) {
     *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
     return nullptr;
   }
diff --git a/runtime/image.cc b/runtime/image.cc
index 2eac3fb..7d2ef75 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '3', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '4', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
@@ -36,7 +36,9 @@
                          uint32_t oat_data_end,
                          uint32_t oat_file_end,
                          uint32_t pointer_size,
-                         bool compile_pic)
+                         bool compile_pic,
+                         StorageMode storage_mode,
+                         size_t data_size)
   : image_begin_(image_begin),
     image_size_(image_size),
     oat_checksum_(oat_checksum),
@@ -47,7 +49,9 @@
     patch_delta_(0),
     image_roots_(image_roots),
     pointer_size_(pointer_size),
-    compile_pic_(compile_pic) {
+    compile_pic_(compile_pic),
+    storage_mode_(storage_mode),
+    data_size_(data_size) {
   CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
   CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
   CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index a16f3c9..3032beb 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -78,10 +78,27 @@
 // header of image files written by ImageWriter, read and validated by Space.
 class PACKED(4) ImageHeader {
  public:
+  enum StorageMode : uint32_t {
+    kStorageModeUncompressed,
+    kStorageModeLZ4,
+    kStorageModeCount,  // Number of elements in enum.
+  };
+  static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed;
+
   ImageHeader()
-      : image_begin_(0U), image_size_(0U), oat_checksum_(0U), oat_file_begin_(0U),
-        oat_data_begin_(0U), oat_data_end_(0U), oat_file_end_(0U), patch_delta_(0),
-        image_roots_(0U), pointer_size_(0U), compile_pic_(0) {}
+      : image_begin_(0U),
+        image_size_(0U),
+        oat_checksum_(0U),
+        oat_file_begin_(0U),
+        oat_data_begin_(0U),
+        oat_data_end_(0U),
+        oat_file_end_(0U),
+        patch_delta_(0),
+        image_roots_(0U),
+        pointer_size_(0U),
+        compile_pic_(0),
+        storage_mode_(kDefaultStorageMode),
+        data_size_(0) {}
 
   ImageHeader(uint32_t image_begin,
               uint32_t image_size,
@@ -93,7 +110,9 @@
               uint32_t oat_data_end,
               uint32_t oat_file_end,
               uint32_t pointer_size,
-              bool compile_pic);
+              bool compile_pic,
+              StorageMode storage_mode,
+              size_t data_size);
 
   bool IsValid() const;
   const char* GetMagic() const;
@@ -194,6 +213,14 @@
     return compile_pic_ != 0;
   }
 
+  StorageMode GetStorageMode() const {
+    return storage_mode_;
+  }
+
+  uint64_t GetDataSize() const {
+    return data_size_;
+  }
+
  private:
   static const uint8_t kImageMagic[4];
   static const uint8_t kImageVersion[4];
@@ -241,6 +268,13 @@
   // Image methods.
   uint64_t image_methods_[kImageMethodsCount];
 
+  // Storage method for the image, the image may be compressed.
+  StorageMode storage_mode_;
+
+  // Data size for the image data excluding the bitmap and the header. For compressed images, this
+  // is the compressed size in the file.
+  uint32_t data_size_;
+
   friend class ImageWriter;
 };
 
@@ -248,6 +282,7 @@
 std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageRoot& policy);
 std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageSections& section);
 std::ostream& operator<<(std::ostream& os, const ImageSection& section);
+std::ostream& operator<<(std::ostream& os, const ImageHeader::StorageMode& mode);
 
 }  // namespace art
 
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index d13526b..2d0ae63 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -344,8 +344,11 @@
     segment_state_.all = new_state;
   }
 
-  static Offset SegmentStateOffset() {
-    return Offset(OFFSETOF_MEMBER(IndirectReferenceTable, segment_state_));
+  static Offset SegmentStateOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
+    // Note: Currently segment_state_ is at offset 0. We're testing the expected value in
+    //       jni_internal_test to make sure it stays correct. It is not OFFSETOF_MEMBER, as that
+    //       is not pointer-size-safe.
+    return Offset(0);
   }
 
   // Release pages past the end of the table that may have previously held references.
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 92aa86e..a653440 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -188,7 +188,7 @@
 
   uint64_t last_update_ns = code_cache_->GetLastUpdateTimeNs();
   if (offline_profile_info_->NeedsSaving(last_update_ns)) {
-    VLOG(profiler) << "Iniate save profiling information to: " << filename;
+    VLOG(profiler) << "Initiate save profiling information to: " << filename;
     std::set<ArtMethod*> methods;
     {
       ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 4450653..7615870 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -68,7 +68,6 @@
   }
 }
 
-
 void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) {
   DCHECK(method != nullptr);
   const DexFile* dex_file = method->GetDexFile();
@@ -80,11 +79,25 @@
   info_it->second.insert(method->GetDexMethodIndex());
 }
 
-static int OpenOrCreateFile(const std::string& filename) {
-  // TODO(calin) allow the shared uid of the app to access the file.
-  int fd = open(filename.c_str(),
-                O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
-                S_IRUSR | S_IWUSR);
+enum OpenMode {
+  READ,
+  READ_WRITE
+};
+
+static int OpenFile(const std::string& filename, OpenMode open_mode) {
+  int fd = -1;
+  switch (open_mode) {
+    case READ:
+      fd = open(filename.c_str(), O_RDONLY);
+      break;
+    case READ_WRITE:
+      // TODO(calin) allow the shared uid of the app to access the file.
+      fd = open(filename.c_str(),
+                    O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
+                    S_IRUSR | S_IWUSR);
+      break;
+  }
+
   if (fd < 0) {
     PLOG(WARNING) << "Failed to open profile file " << filename;
     return -1;
@@ -96,7 +109,6 @@
     PLOG(WARNING) << "Failed to lock profile file " << filename;
     return -1;
   }
-
   return fd;
 }
 
@@ -129,8 +141,8 @@
   } while (length > 0);
 }
 
-static constexpr char kFieldSeparator = ',';
-static constexpr char kLineSeparator = '\n';
+static constexpr const char kFieldSeparator = ',';
+static constexpr const char kLineSeparator = '\n';
 
 /**
  * Serialization format:
@@ -142,7 +154,7 @@
  **/
 bool OfflineProfilingInfo::Serialize(const std::string& filename,
                                      const DexFileToMethodsMap& info) const {
-  int fd = OpenOrCreateFile(filename);
+  int fd = OpenFile(filename, READ_WRITE);
   if (fd == -1) {
     return false;
   }
@@ -168,4 +180,212 @@
 
   return CloseDescriptorForFile(fd, filename);
 }
+
+// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
+// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
+static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
+  const char* p = s.data();
+  const char* end = p + s.size();
+  // Check if the first character is the separator.
+  if (p != end && *p ==separator) {
+    result->push_back("");
+    ++p;
+  }
+  // Process the rest of the characters.
+  while (p != end) {
+    if (*p == separator) {
+      ++p;
+    } else {
+      const char* start = p;
+      while (++p != end && *p != separator) {
+        // Skip to the next occurrence of the separator.
+      }
+      result->push_back(std::string(start, p - start));
+    }
+  }
+}
+
+bool ProfileCompilationInfo::ProcessLine(const std::string& line,
+                                         const std::vector<const DexFile*>& dex_files) {
+  std::vector<std::string> parts;
+  SplitString(line, kFieldSeparator, &parts);
+  if (parts.size() < 3) {
+    LOG(WARNING) << "Invalid line: " << line;
+    return false;
+  }
+
+  const std::string& multidex_suffix = parts[0];
+  uint32_t checksum;
+  if (!ParseInt(parts[1].c_str(), &checksum)) {
+    return false;
+  }
+
+  const DexFile* current_dex_file = nullptr;
+  for (auto dex_file : dex_files) {
+    if (DexFile::GetMultiDexSuffix(dex_file->GetLocation()) == multidex_suffix) {
+      if (checksum != dex_file->GetLocationChecksum()) {
+        LOG(WARNING) << "Checksum mismatch for "
+            << dex_file->GetLocation() << " when parsing " << filename_;
+        return false;
+      }
+      current_dex_file = dex_file;
+      break;
+    }
+  }
+  if (current_dex_file == nullptr) {
+    return true;
+  }
+
+  for (size_t i = 2; i < parts.size(); i++) {
+    uint32_t method_idx;
+    if (!ParseInt(parts[i].c_str(), &method_idx)) {
+      LOG(WARNING) << "Cannot parse method_idx " << parts[i];
+      return false;
+    }
+    uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_;
+    auto info_it = info_.find(current_dex_file);
+    if (info_it == info_.end()) {
+      info_it = info_.Put(current_dex_file, ClassToMethodsMap());
+    }
+    ClassToMethodsMap& class_map = info_it->second;
+    auto class_it = class_map.find(class_idx);
+    if (class_it == class_map.end()) {
+      class_it = class_map.Put(class_idx, std::set<uint32_t>());
+    }
+    class_it->second.insert(method_idx);
+  }
+  return true;
+}
+
+// Parses the buffer (of length n) starting from start_from and identify new lines
+// based on kLineSeparator marker.
+// Returns the first position after kLineSeparator in the buffer (starting from start_from),
+// or -1 if the marker doesn't appear.
+// The processed characters are appended to the given line.
+static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
+  if (start_from >= n) {
+    return -1;
+  }
+  int new_line_pos = -1;
+  for (int i = start_from; i < n; i++) {
+    if (buffer[i] == kLineSeparator) {
+      new_line_pos = i;
+      break;
+    }
+  }
+  int append_limit = new_line_pos == -1 ? n : new_line_pos;
+  line.append(buffer + start_from, append_limit - start_from);
+  // Jump over kLineSeparator and return the position of the next character.
+  return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
+}
+
+bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) {
+  if (dex_files.empty()) {
+    return true;
+  }
+  if (kIsDebugBuild) {
+    // In debug builds verify that the multidex suffixes are unique.
+    std::set<std::string> suffixes;
+    for (auto dex_file : dex_files) {
+      std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_file->GetLocation());
+      DCHECK(suffixes.find(multidex_suffix) == suffixes.end())
+          << "DexFiles appear to belong to different apks."
+          << " There are multiple dex files with the same multidex suffix: "
+          << multidex_suffix;
+      suffixes.insert(multidex_suffix);
+    }
+  }
+  info_.clear();
+
+  int fd = OpenFile(filename_, READ);
+  if (fd == -1) {
+    return false;
+  }
+
+  std::string current_line;
+  const int kBufferSize = 1024;
+  char buffer[kBufferSize];
+  bool success = true;
+
+  while (success) {
+    int n = read(fd, buffer, kBufferSize);
+    if (n < 0) {
+      PLOG(WARNING) << "Error when reading profile file " << filename_;
+      success = false;
+      break;
+    } else if (n == 0) {
+      break;
+    }
+    // Detect the new lines from the buffer. If we manage to complete a line,
+    // process it. Otherwise append to the current line.
+    int current_start_pos = 0;
+    while (current_start_pos < n) {
+      current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
+      if (current_start_pos == -1) {
+        break;
+      }
+      if (!ProcessLine(current_line, dex_files)) {
+        success = false;
+        break;
+      }
+      // Reset the current line (we just processed it).
+      current_line.clear();
+    }
+  }
+  if (!success) {
+    info_.clear();
+  }
+  return CloseDescriptorForFile(fd, filename_) && success;
+}
+
+bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
+  auto info_it = info_.find(method_ref.dex_file);
+  if (info_it != info_.end()) {
+    uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_;
+    const ClassToMethodsMap& class_map = info_it->second;
+    auto class_it = class_map.find(class_idx);
+    if (class_it != class_map.end()) {
+      const std::set<uint32_t>& methods = class_it->second;
+      return methods.find(method_ref.dex_method_index) != methods.end();
+    }
+    return false;
+  }
+  return false;
+}
+
+std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const {
+  std::ostringstream os;
+  if (info_.empty()) {
+    return "ProfileInfo: empty";
+  }
+
+  os << "ProfileInfo:";
+
+  // Use an additional map to achieve a predefined order based on the dex locations.
+  SafeMap<const std::string, const DexFile*> dex_locations_map;
+  for (auto info_it : info_) {
+    dex_locations_map.Put(info_it.first->GetLocation(), info_it.first);
+  }
+
+  const std::string kFirstDexFileKeySubstitute = ":classes.dex";
+  for (auto dex_file_it : dex_locations_map) {
+    os << "\n";
+    const std::string& location = dex_file_it.first;
+    const DexFile* dex_file = dex_file_it.second;
+    if (print_full_dex_location) {
+      os << location;
+    } else {
+      // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
+      std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
+      os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
+    }
+    for (auto class_it : info_.find(dex_file)->second) {
+      for (auto method_it : class_it.second) {
+        os << "\n  " << PrettyMethod(method_it, *dex_file, true);
+      }
+    }
+  }
+  return os.str();
+}
+
 }  // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index e3117eb..90bda60 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -21,6 +21,7 @@
 
 #include "atomic.h"
 #include "dex_file.h"
+#include "method_reference.h"
 #include "safe_map.h"
 
 namespace art {
@@ -50,10 +51,47 @@
   bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const;
 
   // TODO(calin): Verify if Atomic is really needed (are we sure to be called from a
-  // singe thread?)
+  // single thread?)
   Atomic<uint64_t> last_update_time_ns_;
 };
 
+/**
+ * Profile information in a format suitable to be queried by the compiler and performing
+ * profile guided compilation.
+ */
+class ProfileCompilationInfo {
+ public:
+  // Constructs a ProfileCompilationInfo backed by the provided file.
+  explicit ProfileCompilationInfo(const std::string& filename) : filename_(filename) {}
+
+  // Loads profile information corresponding to the provided dex files.
+  // The dex files' multidex suffixes must be unique.
+  // This resets the state of the profiling information
+  // (i.e. all previously loaded info are cleared).
+  bool Load(const std::vector<const DexFile*>& dex_files);
+
+  // Returns true if the method reference is present in the profiling info.
+  bool ContainsMethod(const MethodReference& method_ref) const;
+
+  const std::string& GetFilename() const { return filename_; }
+
+  // Dumps all the loaded profile info into a string and returns it.
+  // This is intended for testing and debugging.
+  std::string DumpInfo(bool print_full_dex_location = true) const;
+
+ private:
+  bool ProcessLine(const std::string& line,
+                   const std::vector<const DexFile*>& dex_files);
+
+  using ClassToMethodsMap = SafeMap<uint32_t, std::set<uint32_t>>;
+  // Map identifying the location of the profiled methods.
+  // dex_file -> class_index -> [dex_method_index]+
+  using DexFileToProfileInfoMap = SafeMap<const DexFile*, ClassToMethodsMap>;
+
+  const std::string filename_;
+  DexFileToProfileInfoMap info_;
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index dab1040..aa25f67 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -105,9 +105,32 @@
   stacked_local_ref_cookies.pop_back();
 }
 
-Offset JNIEnvExt::SegmentStateOffset() {
-  return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
-                IndirectReferenceTable::SegmentStateOffset().Int32Value());
+// Note: the offset code is brittle, as we can't use OFFSETOF_MEMBER or offsetof easily. Thus, there
+//       are tests in jni_internal_test to match the results against the actual values.
+
+// This is encoding the knowledge of the structure and layout of JNIEnv fields.
+static size_t JNIEnvSize(size_t pointer_size) {
+  // A single pointer.
+  return pointer_size;
+}
+
+Offset JNIEnvExt::SegmentStateOffset(size_t pointer_size) {
+  size_t locals_offset = JNIEnvSize(pointer_size) +
+                         2 * pointer_size +          // Thread* self + JavaVMExt* vm.
+                         4 +                         // local_ref_cookie.
+                         (pointer_size - 4);         // Padding.
+  size_t irt_segment_state_offset =
+      IndirectReferenceTable::SegmentStateOffset(pointer_size).Int32Value();
+  return Offset(locals_offset + irt_segment_state_offset);
+}
+
+Offset JNIEnvExt::LocalRefCookieOffset(size_t pointer_size) {
+  return Offset(JNIEnvSize(pointer_size) +
+                2 * pointer_size);          // Thread* self + JavaVMExt* vm
+}
+
+Offset JNIEnvExt::SelfOffset(size_t pointer_size) {
+  return Offset(JNIEnvSize(pointer_size));
 }
 
 // Use some defining part of the caller's frame as the identifying mark for the JNI segment.
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 3828ff0..2f8decf 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -50,15 +50,9 @@
   T AddLocalReference(mirror::Object* obj)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  static Offset SegmentStateOffset();
-
-  static Offset LocalRefCookieOffset() {
-    return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
-  }
-
-  static Offset SelfOffset() {
-    return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
-  }
+  static Offset SegmentStateOffset(size_t pointer_size);
+  static Offset LocalRefCookieOffset(size_t pointer_size);
+  static Offset SelfOffset(size_t pointer_size);
 
   jobject NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
   void DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index d1687d7..b41d16b 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -18,7 +18,9 @@
 
 #include "art_method-inl.h"
 #include "common_compiler_test.h"
+#include "indirect_reference_table.h"
 #include "java_vm_ext.h"
+#include "jni_env_ext.h"
 #include "mirror/string-inl.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
@@ -2261,4 +2263,41 @@
   env_->DeleteGlobalRef(global_ref);
 }
 
+// Test the offset computation of IndirectReferenceTable offsets. b/26071368.
+TEST_F(JniInternalTest, IndirectReferenceTableOffsets) {
+  // The segment_state_ field is private, and we want to avoid friend declaration. So we'll check
+  // by modifying memory.
+  // The parameters don't really matter here.
+  IndirectReferenceTable irt(5, 5, IndirectRefKind::kGlobal, true);
+  uint32_t old_state = irt.GetSegmentState();
+
+  // Write some new state directly. We invert parts of old_state to ensure a new value.
+  uint32_t new_state = old_state ^ 0x07705005;
+  ASSERT_NE(old_state, new_state);
+
+  uint8_t* base = reinterpret_cast<uint8_t*>(&irt);
+  int32_t segment_state_offset =
+      IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
+  *reinterpret_cast<uint32_t*>(base + segment_state_offset) = new_state;
+
+  // Read and compare.
+  EXPECT_EQ(new_state, irt.GetSegmentState());
+}
+
+// Test the offset computation of JNIEnvExt offsets. b/26071368.
+TEST_F(JniInternalTest, JNIEnvExtOffsets) {
+  EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie),
+            JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Int32Value());
+
+  EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Int32Value());
+
+  // segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd
+  // hope it to be.
+  int32_t segment_state_now =
+      OFFSETOF_MEMBER(JNIEnvExt, locals) +
+      IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
+  int32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Int32Value();
+  EXPECT_EQ(segment_state_now, segment_state_computed);
+}
+
 }  // namespace art
diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h
index 1cbf2bb..02081cb 100644
--- a/runtime/noop_compiler_callbacks.h
+++ b/runtime/noop_compiler_callbacks.h
@@ -26,8 +26,7 @@
   NoopCompilerCallbacks() : CompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp) {}
   ~NoopCompilerCallbacks() {}
 
-  bool MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) OVERRIDE {
-    return true;
+  void MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) OVERRIDE {
   }
 
   void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) OVERRIDE {}
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 40aca0d..c787b9a 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -45,9 +45,7 @@
 
 OatHeader* OatHeader::Create(InstructionSet instruction_set,
                              const InstructionSetFeatures* instruction_set_features,
-                             const std::vector<const DexFile*>* dex_files,
-                             uint32_t image_file_location_oat_checksum,
-                             uint32_t image_file_location_oat_data_begin,
+                             uint32_t dex_file_count,
                              const SafeMap<std::string, std::string>* variable_data) {
   // Estimate size of optional data.
   size_t needed_size = ComputeOatHeaderSize(variable_data);
@@ -58,18 +56,29 @@
   // Create the OatHeader in-place.
   return new (memory) OatHeader(instruction_set,
                                 instruction_set_features,
-                                dex_files,
-                                image_file_location_oat_checksum,
-                                image_file_location_oat_data_begin,
+                                dex_file_count,
                                 variable_data);
 }
 
 OatHeader::OatHeader(InstructionSet instruction_set,
                      const InstructionSetFeatures* instruction_set_features,
-                     const std::vector<const DexFile*>* dex_files,
-                     uint32_t image_file_location_oat_checksum,
-                     uint32_t image_file_location_oat_data_begin,
-                     const SafeMap<std::string, std::string>* variable_data) {
+                     uint32_t dex_file_count,
+                     const SafeMap<std::string, std::string>* variable_data)
+    : adler32_checksum_(adler32(0L, Z_NULL, 0)),
+      instruction_set_(instruction_set),
+      instruction_set_features_bitmap_(instruction_set_features->AsBitmap()),
+      dex_file_count_(dex_file_count),
+      executable_offset_(0),
+      interpreter_to_interpreter_bridge_offset_(0),
+      interpreter_to_compiled_code_bridge_offset_(0),
+      jni_dlsym_lookup_offset_(0),
+      quick_generic_jni_trampoline_offset_(0),
+      quick_imt_conflict_trampoline_offset_(0),
+      quick_resolution_trampoline_offset_(0),
+      quick_to_interpreter_bridge_offset_(0),
+      image_patch_delta_(0),
+      image_file_location_oat_checksum_(0),
+      image_file_location_oat_data_begin_(0) {
   // Don't want asserts in header as they would be checked in each file that includes it. But the
   // fields are private, so we check inside a method.
   static_assert(sizeof(magic_) == sizeof(kOatMagic),
@@ -79,46 +88,11 @@
 
   memcpy(magic_, kOatMagic, sizeof(kOatMagic));
   memcpy(version_, kOatVersion, sizeof(kOatVersion));
-  executable_offset_ = 0;
-  image_patch_delta_ = 0;
-
-  adler32_checksum_ = adler32(0L, Z_NULL, 0);
 
   CHECK_NE(instruction_set, kNone);
-  instruction_set_ = instruction_set;
-  UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
-
-  instruction_set_features_bitmap_ = instruction_set_features->AsBitmap();
-  UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
-
-  dex_file_count_ = dex_files->size();
-  UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
-
-  image_file_location_oat_checksum_ = image_file_location_oat_checksum;
-  UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
-
-  CHECK_ALIGNED(image_file_location_oat_data_begin, kPageSize);
-  image_file_location_oat_data_begin_ = image_file_location_oat_data_begin;
-  UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_));
 
   // Flatten the map. Will also update variable_size_data_size_.
   Flatten(variable_data);
-
-  // Update checksum for variable data size.
-  UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_));
-
-  // Update for data, if existing.
-  if (key_value_store_size_ > 0U) {
-    UpdateChecksum(&key_value_store_, key_value_store_size_);
-  }
-
-  interpreter_to_interpreter_bridge_offset_ = 0;
-  interpreter_to_compiled_code_bridge_offset_ = 0;
-  jni_dlsym_lookup_offset_ = 0;
-  quick_generic_jni_trampoline_offset_ = 0;
-  quick_imt_conflict_trampoline_offset_ = 0;
-  quick_resolution_trampoline_offset_ = 0;
-  quick_to_interpreter_bridge_offset_ = 0;
 }
 
 bool OatHeader::IsValid() const {
@@ -175,6 +149,37 @@
   return adler32_checksum_;
 }
 
+void OatHeader::UpdateChecksumWithHeaderData() {
+  UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
+  UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
+  UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
+  UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
+  UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_));
+
+  // Update checksum for variable data size.
+  UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_));
+
+  // Update for data, if existing.
+  if (key_value_store_size_ > 0U) {
+    UpdateChecksum(&key_value_store_, key_value_store_size_);
+  }
+
+  UpdateChecksum(&executable_offset_, sizeof(executable_offset_));
+  UpdateChecksum(&interpreter_to_interpreter_bridge_offset_,
+                 sizeof(interpreter_to_interpreter_bridge_offset_));
+  UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_,
+                 sizeof(interpreter_to_compiled_code_bridge_offset_));
+  UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(jni_dlsym_lookup_offset_));
+  UpdateChecksum(&quick_generic_jni_trampoline_offset_,
+                 sizeof(quick_generic_jni_trampoline_offset_));
+  UpdateChecksum(&quick_imt_conflict_trampoline_offset_,
+                 sizeof(quick_imt_conflict_trampoline_offset_));
+  UpdateChecksum(&quick_resolution_trampoline_offset_,
+                 sizeof(quick_resolution_trampoline_offset_));
+  UpdateChecksum(&quick_to_interpreter_bridge_offset_,
+                 sizeof(quick_to_interpreter_bridge_offset_));
+}
+
 void OatHeader::UpdateChecksum(const void* data, size_t length) {
   DCHECK(IsValid());
   const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
@@ -205,7 +210,6 @@
   DCHECK_EQ(executable_offset_, 0U);
 
   executable_offset_ = executable_offset;
-  UpdateChecksum(&executable_offset_, sizeof(executable_offset));
 }
 
 const void* OatHeader::GetInterpreterToInterpreterBridge() const {
@@ -225,7 +229,6 @@
   DCHECK_EQ(interpreter_to_interpreter_bridge_offset_, 0U) << offset;
 
   interpreter_to_interpreter_bridge_offset_ = offset;
-  UpdateChecksum(&interpreter_to_interpreter_bridge_offset_, sizeof(offset));
 }
 
 const void* OatHeader::GetInterpreterToCompiledCodeBridge() const {
@@ -244,7 +247,6 @@
   DCHECK_EQ(interpreter_to_compiled_code_bridge_offset_, 0U) << offset;
 
   interpreter_to_compiled_code_bridge_offset_ = offset;
-  UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_, sizeof(offset));
 }
 
 const void* OatHeader::GetJniDlsymLookup() const {
@@ -263,7 +265,6 @@
   DCHECK_EQ(jni_dlsym_lookup_offset_, 0U) << offset;
 
   jni_dlsym_lookup_offset_ = offset;
-  UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset));
 }
 
 const void* OatHeader::GetQuickGenericJniTrampoline() const {
@@ -282,7 +283,6 @@
   DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
 
   quick_generic_jni_trampoline_offset_ = offset;
-  UpdateChecksum(&quick_generic_jni_trampoline_offset_, sizeof(offset));
 }
 
 const void* OatHeader::GetQuickImtConflictTrampoline() const {
@@ -301,7 +301,6 @@
   DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
 
   quick_imt_conflict_trampoline_offset_ = offset;
-  UpdateChecksum(&quick_imt_conflict_trampoline_offset_, sizeof(offset));
 }
 
 const void* OatHeader::GetQuickResolutionTrampoline() const {
@@ -320,7 +319,6 @@
   DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
 
   quick_resolution_trampoline_offset_ = offset;
-  UpdateChecksum(&quick_resolution_trampoline_offset_, sizeof(offset));
 }
 
 const void* OatHeader::GetQuickToInterpreterBridge() const {
@@ -339,7 +337,6 @@
   DCHECK_EQ(quick_to_interpreter_bridge_offset_, 0U) << offset;
 
   quick_to_interpreter_bridge_offset_ = offset;
-  UpdateChecksum(&quick_to_interpreter_bridge_offset_, sizeof(offset));
 }
 
 int32_t OatHeader::GetImagePatchDelta() const {
@@ -367,11 +364,22 @@
   return image_file_location_oat_checksum_;
 }
 
+void OatHeader::SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum) {
+  CHECK(IsValid());
+  image_file_location_oat_checksum_ = image_file_location_oat_checksum;
+}
+
 uint32_t OatHeader::GetImageFileLocationOatDataBegin() const {
   CHECK(IsValid());
   return image_file_location_oat_data_begin_;
 }
 
+void OatHeader::SetImageFileLocationOatDataBegin(uint32_t image_file_location_oat_data_begin) {
+  CHECK(IsValid());
+  CHECK_ALIGNED(image_file_location_oat_data_begin, kPageSize);
+  image_file_location_oat_data_begin_ = image_file_location_oat_data_begin;
+}
+
 uint32_t OatHeader::GetKeyValueStoreSize() const {
   CHECK(IsValid());
   return key_value_store_size_;
diff --git a/runtime/oat.h b/runtime/oat.h
index 5b780c3..5ed1977 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,7 +31,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '7', '3', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '7', '4', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -45,15 +45,14 @@
 
   static OatHeader* Create(InstructionSet instruction_set,
                            const InstructionSetFeatures* instruction_set_features,
-                           const std::vector<const DexFile*>* dex_files,
-                           uint32_t image_file_location_oat_checksum,
-                           uint32_t image_file_location_oat_data_begin,
+                           uint32_t dex_file_count,
                            const SafeMap<std::string, std::string>* variable_data);
 
   bool IsValid() const;
   std::string GetValidationErrorMessage() const;
   const char* GetMagic() const;
   uint32_t GetChecksum() const;
+  void UpdateChecksumWithHeaderData();
   void UpdateChecksum(const void* data, size_t length);
   uint32_t GetDexFileCount() const {
     DCHECK(IsValid());
@@ -92,8 +91,11 @@
 
   InstructionSet GetInstructionSet() const;
   uint32_t GetInstructionSetFeaturesBitmap() const;
+
   uint32_t GetImageFileLocationOatChecksum() const;
+  void SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum);
   uint32_t GetImageFileLocationOatDataBegin() const;
+  void SetImageFileLocationOatDataBegin(uint32_t image_file_location_oat_data_begin);
 
   uint32_t GetKeyValueStoreSize() const;
   const uint8_t* GetKeyValueStore() const;
@@ -107,9 +109,7 @@
  private:
   OatHeader(InstructionSet instruction_set,
             const InstructionSetFeatures* instruction_set_features,
-            const std::vector<const DexFile*>* dex_files,
-            uint32_t image_file_location_oat_checksum,
-            uint32_t image_file_location_oat_data_begin,
+            uint32_t dex_file_count,
             const SafeMap<std::string, std::string>* variable_data);
 
   // Returns true if the value of the given key is "true", false otherwise.
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 837662d..6cea902 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -51,6 +51,23 @@
   kIntrinsicMinMaxLong,
   kIntrinsicMinMaxFloat,
   kIntrinsicMinMaxDouble,
+  kIntrinsicCos,
+  kIntrinsicSin,
+  kIntrinsicAcos,
+  kIntrinsicAsin,
+  kIntrinsicAtan,
+  kIntrinsicAtan2,
+  kIntrinsicCbrt,
+  kIntrinsicCosh,
+  kIntrinsicExp,
+  kIntrinsicExpm1,
+  kIntrinsicHypot,
+  kIntrinsicLog,
+  kIntrinsicLog10,
+  kIntrinsicNextAfter,
+  kIntrinsicSinh,
+  kIntrinsicTan,
+  kIntrinsicTanh,
   kIntrinsicSqrt,
   kIntrinsicCeil,
   kIntrinsicFloor,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index dedc110..bfd9ffe 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -219,8 +219,6 @@
     UnloadNativeBridge();
   }
 
-  MaybeSaveJitProfilingInfo();
-
   if (dump_gc_performance_on_shutdown_) {
     // This can't be called from the Heap destructor below because it
     // could call RosAlloc::InspectAll() which needs the thread_list
@@ -1293,6 +1291,11 @@
 }
 
 void Runtime::DumpForSigQuit(std::ostream& os) {
+  // Dumping for SIGQIT may cause deadlocks if the the debugger is active. b/26118154
+  if (Dbg::IsDebuggerActive()) {
+    LOG(INFO) << "Skipping DumpForSigQuit due to active debugger";
+    return;
+  }
   GetClassLinker()->DumpForSigQuit(os);
   GetInternTable()->DumpForSigQuit(os);
   GetJavaVM()->DumpForSigQuit(os);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 90539b4..8a8c02f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2478,6 +2478,23 @@
   QUICK_ENTRY_POINT_INFO(pCmpgFloat)
   QUICK_ENTRY_POINT_INFO(pCmplDouble)
   QUICK_ENTRY_POINT_INFO(pCmplFloat)
+  QUICK_ENTRY_POINT_INFO(pCos)
+  QUICK_ENTRY_POINT_INFO(pSin)
+  QUICK_ENTRY_POINT_INFO(pAcos)
+  QUICK_ENTRY_POINT_INFO(pAsin)
+  QUICK_ENTRY_POINT_INFO(pAtan)
+  QUICK_ENTRY_POINT_INFO(pAtan2)
+  QUICK_ENTRY_POINT_INFO(pCbrt)
+  QUICK_ENTRY_POINT_INFO(pCosh)
+  QUICK_ENTRY_POINT_INFO(pExp)
+  QUICK_ENTRY_POINT_INFO(pExpm1)
+  QUICK_ENTRY_POINT_INFO(pHypot)
+  QUICK_ENTRY_POINT_INFO(pLog)
+  QUICK_ENTRY_POINT_INFO(pLog10)
+  QUICK_ENTRY_POINT_INFO(pNextAfter)
+  QUICK_ENTRY_POINT_INFO(pSinh)
+  QUICK_ENTRY_POINT_INFO(pTan)
+  QUICK_ENTRY_POINT_INFO(pTanh)
   QUICK_ENTRY_POINT_INFO(pFmod)
   QUICK_ENTRY_POINT_INFO(pL2d)
   QUICK_ENTRY_POINT_INFO(pFmodf)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index cf27ff2..d75587b 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -116,6 +116,7 @@
 
 MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
                                                         mirror::Class* klass,
+                                                        CompilerCallbacks* callbacks,
                                                         bool allow_soft_failures,
                                                         bool log_hard_failures,
                                                         std::string* error) {
@@ -140,9 +141,9 @@
   }
   if (early_failure) {
     *error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message;
-    if (Runtime::Current()->IsAotCompiler()) {
+    if (callbacks != nullptr) {
       ClassReference ref(&dex_file, klass->GetDexClassDefIndex());
-      Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+      callbacks->ClassRejected(ref);
     }
     return kHardFailure;
   }
@@ -154,6 +155,7 @@
                      dex_cache,
                      class_loader,
                      class_def,
+                     callbacks,
                      allow_soft_failures,
                      log_hard_failures,
                      error);
@@ -172,6 +174,7 @@
                                    ClassDataItemIterator* it,
                                    Handle<mirror::DexCache> dex_cache,
                                    Handle<mirror::ClassLoader> class_loader,
+                                   CompilerCallbacks* callbacks,
                                    bool allow_soft_failures,
                                    bool log_hard_failures,
                                    bool need_precise_constants,
@@ -212,6 +215,7 @@
                                                       it->GetMethodCodeItem(),
                                                       method,
                                                       it->GetMethodAccessFlags(),
+                                                      callbacks,
                                                       allow_soft_failures,
                                                       log_hard_failures,
                                                       need_precise_constants,
@@ -241,6 +245,7 @@
                                                         Handle<mirror::DexCache> dex_cache,
                                                         Handle<mirror::ClassLoader> class_loader,
                                                         const DexFile::ClassDef* class_def,
+                                                        CompilerCallbacks* callbacks,
                                                         bool allow_soft_failures,
                                                         bool log_hard_failures,
                                                         std::string* error) {
@@ -274,6 +279,7 @@
                       &it,
                       dex_cache,
                       class_loader,
+                      callbacks,
                       allow_soft_failures,
                       log_hard_failures,
                       false /* need precise constants */,
@@ -288,6 +294,7 @@
                       &it,
                       dex_cache,
                       class_loader,
+                      callbacks,
                       allow_soft_failures,
                       log_hard_failures,
                       false /* need precise constants */,
@@ -322,6 +329,7 @@
                                                          const DexFile::CodeItem* code_item,
                                                          ArtMethod* method,
                                                          uint32_t method_access_flags,
+                                                         CompilerCallbacks* callbacks,
                                                          bool allow_soft_failures,
                                                          bool log_hard_failures,
                                                          bool need_precise_constants,
@@ -336,6 +344,12 @@
     // Verification completed, however failures may be pending that didn't cause the verification
     // to hard fail.
     CHECK(!verifier.have_pending_hard_failure_);
+
+    if (code_item != nullptr && callbacks != nullptr) {
+      // Let the interested party know that the method was verified.
+      callbacks->MethodVerified(&verifier);
+    }
+
     if (verifier.failures_.size() != 0) {
       if (VLOG_IS_ON(verifier)) {
         verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in "
@@ -363,8 +377,14 @@
             verifier.failure_messages_[verifier.failure_messages_.size() - 1]->str();
       }
       result = kHardFailure;
+
+      if (callbacks != nullptr) {
+        // Let the interested party know that we failed the class.
+        ClassReference ref(dex_file, dex_file->GetIndexForClassDef(*class_def));
+        callbacks->ClassRejected(ref);
+      }
     }
-    if (kDebugVerify) {
+    if (VLOG_IS_ON(verifier)) {
       std::cout << "\n" << verifier.info_messages_.str();
       verifier.Dump(std::cout);
     }
@@ -408,13 +428,18 @@
 }
 
 MethodVerifier::MethodVerifier(Thread* self,
-                               const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+                               const DexFile* dex_file,
+                               Handle<mirror::DexCache> dex_cache,
                                Handle<mirror::ClassLoader> class_loader,
                                const DexFile::ClassDef* class_def,
-                               const DexFile::CodeItem* code_item, uint32_t dex_method_idx,
-                               ArtMethod* method, uint32_t method_access_flags,
-                               bool can_load_classes, bool allow_soft_failures,
-                               bool need_precise_constants, bool verify_to_dump,
+                               const DexFile::CodeItem* code_item,
+                               uint32_t dex_method_idx,
+                               ArtMethod* method,
+                               uint32_t method_access_flags,
+                               bool can_load_classes,
+                               bool allow_soft_failures,
+                               bool need_precise_constants,
+                               bool verify_to_dump,
                                bool allow_thread_suspension)
     : self_(self),
       arena_stack_(Runtime::Current()->GetArenaPool()),
@@ -739,10 +764,7 @@
   result = result && VerifyInstructions();
   // Perform code-flow analysis and return.
   result = result && VerifyCodeFlow();
-  // Compute information for compiler.
-  if (result && runtime->IsCompiler()) {
-    result = runtime->GetCompilerCallbacks()->MethodVerified(this);
-  }
+
   return result;
 }
 
@@ -802,10 +824,6 @@
       // Hard verification failures at compile time will still fail at runtime, so the class is
       // marked as rejected to prevent it from being compiled.
     case VERIFY_ERROR_BAD_CLASS_HARD: {
-      if (Runtime::Current()->IsAotCompiler()) {
-        ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_));
-        Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
-      }
       have_pending_hard_failure_ = true;
       if (VLOG_IS_ON(verifier) && kDumpRegLinesOnHardFailureIfVLOG) {
         ScopedObjectAccess soa(Thread::Current());
@@ -3639,30 +3657,8 @@
   auto* cl = Runtime::Current()->GetClassLinker();
   auto pointer_size = cl->GetImagePointerSize();
 
-  // Check that interface methods are static or match interface classes.
-  // We only allow statics if we don't have default methods enabled.
-  if (klass->IsInterface()) {
-    Runtime* runtime = Runtime::Current();
-    const bool default_methods_supported =
-        runtime == nullptr ||
-        runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
-    if (method_type != METHOD_INTERFACE &&
-        (!default_methods_supported || method_type != METHOD_STATIC)) {
-      Fail(VERIFY_ERROR_CLASS_CHANGE)
-          << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_)
-          << " is in an interface class " << PrettyClass(klass);
-      return nullptr;
-    }
-  } else {
-    if (method_type == METHOD_INTERFACE) {
-      Fail(VERIFY_ERROR_CLASS_CHANGE)
-          << "interface method " << PrettyMethod(dex_method_idx, *dex_file_)
-          << " is in a non-interface class " << PrettyClass(klass);
-      return nullptr;
-    }
-  }
-
   ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx, pointer_size);
+  bool stash_method = false;
   if (res_method == nullptr) {
     const char* name = dex_file_->GetMethodName(method_id);
     const Signature signature = dex_file_->GetMethodSignature(method_id);
@@ -3675,7 +3671,7 @@
       res_method = klass->FindVirtualMethod(name, signature, pointer_size);
     }
     if (res_method != nullptr) {
-      dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
+      stash_method = true;
     } else {
       // If a virtual or interface method wasn't found with the expected type, look in
       // the direct methods. This can happen when the wrong invoke type is used or when
@@ -3704,6 +3700,38 @@
                                       << PrettyMethod(res_method);
     return nullptr;
   }
+
+  // Check that interface methods are static or match interface classes.
+  // We only allow statics if we don't have default methods enabled.
+  //
+  // Note: this check must be after the initializer check, as those are required to fail a class,
+  //       while this check implies an IncompatibleClassChangeError.
+  if (klass->IsInterface()) {
+    Runtime* runtime = Runtime::Current();
+    const bool default_methods_supported =
+        runtime == nullptr ||
+        runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
+    if (method_type != METHOD_INTERFACE &&
+        (!default_methods_supported || method_type != METHOD_STATIC)) {
+      Fail(VERIFY_ERROR_CLASS_CHANGE)
+          << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_)
+          << " is in an interface class " << PrettyClass(klass);
+      return nullptr;
+    }
+  } else {
+    if (method_type == METHOD_INTERFACE) {
+      Fail(VERIFY_ERROR_CLASS_CHANGE)
+          << "interface method " << PrettyMethod(dex_method_idx, *dex_file_)
+          << " is in a non-interface class " << PrettyClass(klass);
+      return nullptr;
+    }
+  }
+
+  // Only stash after the above passed. Otherwise the method wasn't guaranteed to be correct.
+  if (stash_method) {
+    dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
+  }
+
   // Check if access is allowed.
   if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
     Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " << PrettyMethod(res_method)
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 719f0d7..79db576 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -33,6 +33,7 @@
 
 namespace art {
 
+class CompilerCallbacks;
 class Instruction;
 struct ReferenceMap2Visitor;
 class Thread;
@@ -141,6 +142,7 @@
   /* Verify a class. Returns "kNoFailure" on success. */
   static FailureKind VerifyClass(Thread* self,
                                  mirror::Class* klass,
+                                 CompilerCallbacks* callbacks,
                                  bool allow_soft_failures,
                                  bool log_hard_failures,
                                  std::string* error)
@@ -150,6 +152,7 @@
                                  Handle<mirror::DexCache> dex_cache,
                                  Handle<mirror::ClassLoader> class_loader,
                                  const DexFile::ClassDef* class_def,
+                                 CompilerCallbacks* callbacks,
                                  bool allow_soft_failures,
                                  bool log_hard_failures,
                                  std::string* error)
@@ -216,16 +219,34 @@
     return can_load_classes_;
   }
 
-  MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
-                 Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
-                 const DexFile::CodeItem* code_item, uint32_t method_idx,
+  MethodVerifier(Thread* self,
+                 const DexFile* dex_file,
+                 Handle<mirror::DexCache> dex_cache,
+                 Handle<mirror::ClassLoader> class_loader,
+                 const DexFile::ClassDef* class_def,
+                 const DexFile::CodeItem* code_item,
+                 uint32_t method_idx,
                  ArtMethod* method,
-                 uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
-                 bool need_precise_constants, bool allow_thread_suspension)
+                 uint32_t access_flags,
+                 bool can_load_classes,
+                 bool allow_soft_failures,
+                 bool need_precise_constants,
+                 bool allow_thread_suspension)
           SHARED_REQUIRES(Locks::mutator_lock_)
-      : MethodVerifier(self, dex_file, dex_cache, class_loader, class_def, code_item, method_idx,
-                       method, access_flags, can_load_classes, allow_soft_failures,
-                       need_precise_constants, false, allow_thread_suspension) {}
+      : MethodVerifier(self,
+                       dex_file,
+                       dex_cache,
+                       class_loader,
+                       class_def,
+                       code_item,
+                       method_idx,
+                       method,
+                       access_flags,
+                       can_load_classes,
+                       allow_soft_failures,
+                       need_precise_constants,
+                       false,
+                       allow_thread_suspension) {}
 
   ~MethodVerifier();
 
@@ -299,12 +320,20 @@
   }
 
   // Private constructor for dumping.
-  MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
-                 Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
-                 const DexFile::CodeItem* code_item, uint32_t method_idx,
-                 ArtMethod* method, uint32_t access_flags,
-                 bool can_load_classes, bool allow_soft_failures, bool need_precise_constants,
-                 bool verify_to_dump, bool allow_thread_suspension)
+  MethodVerifier(Thread* self,
+                 const DexFile* dex_file,
+                 Handle<mirror::DexCache> dex_cache,
+                 Handle<mirror::ClassLoader> class_loader,
+                 const DexFile::ClassDef* class_def,
+                 const DexFile::CodeItem* code_item,
+                 uint32_t method_idx,
+                 ArtMethod* method,
+                 uint32_t access_flags,
+                 bool can_load_classes,
+                 bool allow_soft_failures,
+                 bool need_precise_constants,
+                 bool verify_to_dump,
+                 bool allow_thread_suspension)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Adds the given string to the beginning of the last failure message.
@@ -323,6 +352,7 @@
                             ClassDataItemIterator* it,
                             Handle<mirror::DexCache> dex_cache,
                             Handle<mirror::ClassLoader> class_loader,
+                            CompilerCallbacks* callbacks,
                             bool allow_soft_failures,
                             bool log_hard_failures,
                             bool need_precise_constants,
@@ -350,6 +380,7 @@
                                   const DexFile::CodeItem* code_item,
                                   ArtMethod* method,
                                   uint32_t method_access_flags,
+                                  CompilerCallbacks* callbacks,
                                   bool allow_soft_failures,
                                   bool log_hard_failures,
                                   bool need_precise_constants,
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index c4123d5..946f842 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -37,8 +37,8 @@
 
     // Verify the class
     std::string error_msg;
-    ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, true, true, &error_msg) == MethodVerifier::kNoFailure)
-        << error_msg;
+    ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, nullptr, true, true, &error_msg)
+                    == MethodVerifier::kNoFailure) << error_msg;
   }
 
   void VerifyDexFile(const DexFile& dex)
diff --git a/test/123-inline-execute2/expected.txt b/test/123-inline-execute2/expected.txt
new file mode 100644
index 0000000..aa74fa3
--- /dev/null
+++ b/test/123-inline-execute2/expected.txt
@@ -0,0 +1,299 @@
+Math.sin(0.0) = 0.000000000000
+Math.sinh(0.0) = 0.000000000000
+Math.asin(0.0) = 0.000000000000
+Math.cos(0.0) = 1.000000000000
+Math.cosh(0.0) = 1.000000000000
+Math.acos(0.0) = 1.570796326795
+Math.tan(0.0) = 0.000000000000
+Math.tanh(0.0) = 0.000000000000
+Math.atan(0.0) = 0.000000000000
+Math.atan2(0.0, 1.0) = 0.000000000000
+Math.sin(0.7853981633974483) = 0.707106781187
+Math.sinh(0.7853981633974483) = 0.868670961486
+Math.asin(0.7853981633974483) = 0.903339110767
+Math.cos(0.7853981633974483) = 0.707106781187
+Math.cosh(0.7853981633974483) = 1.324609089252
+Math.acos(0.7853981633974483) = 0.667457216028
+Math.tan(0.7853981633974483) = 1.000000000000
+Math.tanh(0.7853981633974483) = 0.655794202633
+Math.atan(0.7853981633974483) = 0.665773750028
+Math.atan2(0.7853981633974483, 1.7853981633974483) = 0.414423800577
+Math.sin(1.5707963267948966) = 1.000000000000
+Math.sinh(1.5707963267948966) = 2.301298902307
+Math.asin(1.5707963267948966) = NaN
+Math.cos(1.5707963267948966) = 0.000000000000
+Math.cosh(1.5707963267948966) = 2.509178478658
+Math.acos(1.5707963267948966) = NaN
+Math.tanh(1.5707963267948966) = 0.917152335667
+Math.atan(1.5707963267948966) = 1.003884821854
+Math.atan2(1.5707963267948966, 2.5707963267948966) = 0.548479764417
+Math.sin(2.356194490192345) = 0.707106781187
+Math.sinh(2.356194490192345) = 5.227971924678
+Math.asin(2.356194490192345) = NaN
+Math.cos(2.356194490192345) = -0.707106781187
+Math.cosh(2.356194490192345) = 5.322752149520
+Math.acos(2.356194490192345) = NaN
+Math.tan(2.356194490192345) = -1.000000000000
+Math.tanh(2.356194490192345) = 0.982193380007
+Math.atan(2.356194490192345) = 1.169422824816
+Math.atan2(2.356194490192345, 3.356194490192345) = 0.612096117380
+Math.sin(3.141592653589793) = 0.000000000000
+Math.sinh(3.141592653589793) = 11.548739357258
+Math.asin(3.141592653589793) = NaN
+Math.cos(3.141592653589793) = -1.000000000000
+Math.cosh(3.141592653589793) = 11.591953275522
+Math.acos(3.141592653589793) = NaN
+Math.tan(3.141592653589793) = -0.000000000000
+Math.tanh(3.141592653589793) = 0.996272076221
+Math.atan(3.141592653589793) = 1.262627255679
+Math.atan2(3.141592653589793, 4.141592653589793) = 0.648948780815
+Math.sin(3.9269908169872414) = -0.707106781187
+Math.sinh(3.9269908169872414) = 25.367158319374
+Math.asin(3.9269908169872414) = NaN
+Math.cos(3.9269908169872414) = -0.707106781187
+Math.cosh(3.9269908169872414) = 25.386861192361
+Math.acos(3.9269908169872414) = NaN
+Math.tan(3.9269908169872414) = 1.000000000000
+Math.tanh(3.9269908169872414) = 0.999223894879
+Math.atan(3.9269908169872414) = 1.321447967784
+Math.atan2(3.9269908169872414, 4.926990816987241) = 0.672931229191
+Math.sin(4.71238898038469) = -1.000000000000
+Math.sinh(4.71238898038469) = 55.654397599418
+Math.asin(4.71238898038469) = NaN
+Math.cos(4.71238898038469) = -0.000000000000
+Math.cosh(4.71238898038469) = 55.663380890439
+Math.acos(4.71238898038469) = NaN
+Math.tanh(4.71238898038469) = 0.999838613989
+Math.atan(4.71238898038469) = 1.361691682971
+Math.atan2(4.71238898038469, 5.71238898038469) = 0.689765469251
+Math.sin(5.497787143782138) = -0.707106781187
+Math.sinh(5.497787143782138) = 122.073483514693
+Math.asin(5.497787143782138) = NaN
+Math.cos(5.497787143782138) = 0.707106781187
+Math.cosh(5.497787143782138) = 122.077579339582
+Math.acos(5.497787143782138) = NaN
+Math.tan(5.497787143782138) = -1.000000000000
+Math.tanh(5.497787143782138) = 0.999966449000
+Math.atan(5.497787143782138) = 1.390871988014
+Math.atan2(5.497787143782138, 6.497787143782138) = 0.702226398171
+Math.sin(6.283185307179586) = -0.000000000000
+Math.sinh(6.283185307179586) = 267.744894041016
+Math.asin(6.283185307179586) = NaN
+Math.cos(6.283185307179586) = 1.000000000000
+Math.cosh(6.283185307179586) = 267.746761483748
+Math.acos(6.283185307179586) = NaN
+Math.tan(6.283185307179586) = -0.000000000000
+Math.tanh(6.283185307179586) = 0.999993025340
+Math.atan(6.283185307179586) = 1.412965136507
+Math.atan2(6.283185307179586, 7.283185307179586) = 0.711819549590
+Math.cbrt(-3.0) = -1.442249570307
+Math.log(-3.0) = NaN
+Math.log10(-3.0) = NaN
+Math.log1p(-3.0) = NaN
+Math.exp(-3.0) = 0.049787068368
+Math.expm1(-3.0) = -0.950212931632
+Math.pow(-3.0, -2.0) = 0.111111111111
+Math.hypot(-3.0, -2.0) = 3.605551275464
+Math.cbrt(-2.0) = -1.259921049895
+Math.log(-2.0) = NaN
+Math.log10(-2.0) = NaN
+Math.log1p(-2.0) = NaN
+Math.exp(-2.0) = 0.135335283237
+Math.expm1(-2.0) = -0.864664716763
+Math.pow(-2.0, -1.0) = -0.500000000000
+Math.hypot(-2.0, -1.0) = 2.236067977500
+Math.cbrt(-1.0) = -1.000000000000
+Math.log(-1.0) = NaN
+Math.log10(-1.0) = NaN
+Math.log1p(-1.0) = -Infinity
+Math.exp(-1.0) = 0.367879441171
+Math.expm1(-1.0) = -0.632120558829
+Math.pow(-1.0, 0.0) = 1.000000000000
+Math.hypot(-1.0, 0.0) = 1.000000000000
+Math.cbrt(0.0) = 0.000000000000
+Math.log(0.0) = -Infinity
+Math.log10(0.0) = -Infinity
+Math.log1p(0.0) = 0.000000000000
+Math.exp(0.0) = 1.000000000000
+Math.expm1(0.0) = 0.000000000000
+Math.pow(0.0, 1.0) = 0.000000000000
+Math.hypot(0.0, 1.0) = 1.000000000000
+Math.cbrt(1.0) = 1.000000000000
+Math.log(1.0) = 0.000000000000
+Math.log10(1.0) = 0.000000000000
+Math.log1p(1.0) = 0.693147180560
+Math.exp(1.0) = 2.718281828459
+Math.expm1(1.0) = 1.718281828459
+Math.pow(1.0, 2.0) = 1.000000000000
+Math.hypot(1.0, 2.0) = 2.236067977500
+Math.cbrt(2.0) = 1.259921049895
+Math.log(2.0) = 0.693147180560
+Math.log10(2.0) = 0.301029995664
+Math.log1p(2.0) = 1.098612288668
+Math.exp(2.0) = 7.389056098931
+Math.expm1(2.0) = 6.389056098931
+Math.pow(2.0, 3.0) = 8.000000000000
+Math.hypot(2.0, 3.0) = 3.605551275464
+Math.cbrt(3.0) = 1.442249570307
+Math.log(3.0) = 1.098612288668
+Math.log10(3.0) = 0.477121254720
+Math.log1p(3.0) = 1.386294361120
+Math.exp(3.0) = 20.085536923188
+Math.expm1(3.0) = 19.085536923188
+Math.pow(3.0, 4.0) = 81.000000000000
+Math.hypot(3.0, 4.0) = 5.000000000000
+Math.ceil(0.0001) = 1.000000000000
+Math.floor(0.0001) = 0.000000000000
+Math.nextAfter(1.0, 2.0) = 1.000000000000
+Math.nextAfter(2.0, 1.0) = 2.000000000000
+Math.rint(0.5000001) = 1.000000000000
+StrictMath.sin(0.0) = 0.0
+StrictMath.sinh(0.0) = 0.0
+StrictMath.asin(0.0) = 0.0
+StrictMath.cos(0.0) = 1.0
+StrictMath.cosh(0.0) = 1.0
+StrictMath.acos(0.0) = 1.5707963267948966
+StrictMath.tan(0.0) = 0.0
+StrictMath.tanh(0.0) = 0.0
+StrictMath.atan(0.0) = 0.0
+StrictMath.atan2(0.0, 1.0) = 0.0
+StrictMath.sin(0.7853981633974483) = 0.7071067811865475
+StrictMath.sinh(0.7853981633974483) = 0.8686709614860095
+StrictMath.asin(0.7853981633974483) = 0.9033391107665127
+StrictMath.cos(0.7853981633974483) = 0.7071067811865476
+StrictMath.cosh(0.7853981633974483) = 1.3246090892520057
+StrictMath.acos(0.7853981633974483) = 0.6674572160283838
+StrictMath.tan(0.7853981633974483) = 0.9999999999999999
+StrictMath.tanh(0.7853981633974483) = 0.6557942026326724
+StrictMath.atan(0.7853981633974483) = 0.6657737500283538
+StrictMath.atan2(0.7853981633974483, 1.7853981633974483) = 0.41442380057704103
+StrictMath.sin(1.5707963267948966) = 1.0
+StrictMath.sinh(1.5707963267948966) = 2.3012989023072947
+StrictMath.asin(1.5707963267948966) = NaN
+StrictMath.cos(1.5707963267948966) = 6.123233995736766E-17
+StrictMath.cosh(1.5707963267948966) = 2.5091784786580567
+StrictMath.acos(1.5707963267948966) = NaN
+StrictMath.tan(1.5707963267948966) = 1.633123935319537E16
+StrictMath.tanh(1.5707963267948966) = 0.9171523356672744
+StrictMath.atan(1.5707963267948966) = 1.0038848218538872
+StrictMath.atan2(1.5707963267948966, 2.5707963267948966) = 0.5484797644174059
+StrictMath.sin(2.356194490192345) = 0.7071067811865476
+StrictMath.sinh(2.356194490192345) = 5.227971924677803
+StrictMath.asin(2.356194490192345) = NaN
+StrictMath.cos(2.356194490192345) = -0.7071067811865475
+StrictMath.cosh(2.356194490192345) = 5.322752149519959
+StrictMath.acos(2.356194490192345) = NaN
+StrictMath.tan(2.356194490192345) = -1.0000000000000002
+StrictMath.tanh(2.356194490192345) = 0.9821933800072388
+StrictMath.atan(2.356194490192345) = 1.1694228248157563
+StrictMath.atan2(2.356194490192345, 3.356194490192345) = 0.6120961173796371
+StrictMath.sin(3.141592653589793) = 1.2246467991473532E-16
+StrictMath.sinh(3.141592653589793) = 11.548739357257748
+StrictMath.asin(3.141592653589793) = NaN
+StrictMath.cos(3.141592653589793) = -1.0
+StrictMath.cosh(3.141592653589793) = 11.591953275521519
+StrictMath.acos(3.141592653589793) = NaN
+StrictMath.tan(3.141592653589793) = -1.2246467991473532E-16
+StrictMath.tanh(3.141592653589793) = 0.99627207622075
+StrictMath.atan(3.141592653589793) = 1.2626272556789115
+StrictMath.atan2(3.141592653589793, 4.141592653589793) = 0.6489487808147751
+StrictMath.sin(3.9269908169872414) = -0.7071067811865475
+StrictMath.sinh(3.9269908169872414) = 25.367158319374152
+StrictMath.asin(3.9269908169872414) = NaN
+StrictMath.cos(3.9269908169872414) = -0.7071067811865477
+StrictMath.cosh(3.9269908169872414) = 25.386861192360772
+StrictMath.acos(3.9269908169872414) = NaN
+StrictMath.tan(3.9269908169872414) = 0.9999999999999997
+StrictMath.tanh(3.9269908169872414) = 0.9992238948786412
+StrictMath.atan(3.9269908169872414) = 1.3214479677837223
+StrictMath.atan2(3.9269908169872414, 4.926990816987241) = 0.6729312291908799
+StrictMath.sin(4.71238898038469) = -1.0
+StrictMath.sinh(4.71238898038469) = 55.65439759941754
+StrictMath.asin(4.71238898038469) = NaN
+StrictMath.cos(4.71238898038469) = -1.8369701987210297E-16
+StrictMath.cosh(4.71238898038469) = 55.66338089043867
+StrictMath.acos(4.71238898038469) = NaN
+StrictMath.tan(4.71238898038469) = 5.443746451065123E15
+StrictMath.tanh(4.71238898038469) = 0.9998386139886326
+StrictMath.atan(4.71238898038469) = 1.3616916829711636
+StrictMath.atan2(4.71238898038469, 5.71238898038469) = 0.6897654692509959
+StrictMath.sin(5.497787143782138) = -0.7071067811865477
+StrictMath.sinh(5.497787143782138) = 122.07348351469281
+StrictMath.asin(5.497787143782138) = NaN
+StrictMath.cos(5.497787143782138) = 0.7071067811865474
+StrictMath.cosh(5.497787143782138) = 122.07757933958217
+StrictMath.acos(5.497787143782138) = NaN
+StrictMath.tan(5.497787143782138) = -1.0000000000000004
+StrictMath.tanh(5.497787143782138) = 0.9999664489997958
+StrictMath.atan(5.497787143782138) = 1.390871988014422
+StrictMath.atan2(5.497787143782138, 6.497787143782138) = 0.7022263981709682
+StrictMath.sin(6.283185307179586) = -2.4492935982947064E-16
+StrictMath.sinh(6.283185307179586) = 267.74489404101644
+StrictMath.asin(6.283185307179586) = NaN
+StrictMath.cos(6.283185307179586) = 1.0
+StrictMath.cosh(6.283185307179586) = 267.7467614837482
+StrictMath.acos(6.283185307179586) = NaN
+StrictMath.tan(6.283185307179586) = -2.4492935982947064E-16
+StrictMath.tanh(6.283185307179586) = 0.9999930253396107
+StrictMath.atan(6.283185307179586) = 1.4129651365067377
+StrictMath.atan2(6.283185307179586, 7.283185307179586) = 0.7118195495895945
+StrictMath.cbrt(-3.0) = -1.4422495703074083
+StrictMath.log(-3.0) = NaN
+StrictMath.log10(-3.0) = NaN
+StrictMath.log1p(-3.0) = NaN
+StrictMath.exp(-3.0) = 0.049787068367863944
+StrictMath.expm1(-3.0) = -0.950212931632136
+StrictMath.pow(-3.0, -2.0) = 0.1111111111111111
+StrictMath.hypot(-3.0, -2.0) = 3.605551275463989
+StrictMath.cbrt(-2.0) = -1.2599210498948732
+StrictMath.log(-2.0) = NaN
+StrictMath.log10(-2.0) = NaN
+StrictMath.log1p(-2.0) = NaN
+StrictMath.exp(-2.0) = 0.1353352832366127
+StrictMath.expm1(-2.0) = -0.8646647167633873
+StrictMath.pow(-2.0, -1.0) = -0.5
+StrictMath.hypot(-2.0, -1.0) = 2.23606797749979
+StrictMath.cbrt(-1.0) = -1.0
+StrictMath.log(-1.0) = NaN
+StrictMath.log10(-1.0) = NaN
+StrictMath.log1p(-1.0) = -Infinity
+StrictMath.exp(-1.0) = 0.36787944117144233
+StrictMath.expm1(-1.0) = -0.6321205588285577
+StrictMath.pow(-1.0, 0.0) = 1.0
+StrictMath.hypot(-1.0, 0.0) = 1.0
+StrictMath.cbrt(0.0) = 0.0
+StrictMath.log(0.0) = -Infinity
+StrictMath.log10(0.0) = -Infinity
+StrictMath.log1p(0.0) = 0.0
+StrictMath.exp(0.0) = 1.0
+StrictMath.expm1(0.0) = 0.0
+StrictMath.pow(0.0, 1.0) = 0.0
+StrictMath.hypot(0.0, 1.0) = 1.0
+StrictMath.cbrt(1.0) = 1.0
+StrictMath.log(1.0) = 0.0
+StrictMath.log10(1.0) = 0.0
+StrictMath.log1p(1.0) = 0.6931471805599453
+StrictMath.exp(1.0) = 2.7182818284590455
+StrictMath.expm1(1.0) = 1.718281828459045
+StrictMath.pow(1.0, 2.0) = 1.0
+StrictMath.hypot(1.0, 2.0) = 2.23606797749979
+StrictMath.cbrt(2.0) = 1.2599210498948732
+StrictMath.log(2.0) = 0.6931471805599453
+StrictMath.log10(2.0) = 0.3010299956639812
+StrictMath.log1p(2.0) = 1.0986122886681096
+StrictMath.exp(2.0) = 7.38905609893065
+StrictMath.expm1(2.0) = 6.38905609893065
+StrictMath.pow(2.0, 3.0) = 8.0
+StrictMath.hypot(2.0, 3.0) = 3.605551275463989
+StrictMath.cbrt(3.0) = 1.4422495703074083
+StrictMath.log(3.0) = 1.0986122886681096
+StrictMath.log10(3.0) = 0.47712125471966244
+StrictMath.log1p(3.0) = 1.3862943611198906
+StrictMath.exp(3.0) = 20.085536923187668
+StrictMath.expm1(3.0) = 19.085536923187668
+StrictMath.pow(3.0, 4.0) = 81.0
+StrictMath.hypot(3.0, 4.0) = 5.0
+StrictMath.ceil(0.0001) = 1.0
+StrictMath.floor(0.0001) = 0.0
+StrictMath.nextAfter(1.0, 2.0) = 1.0000000000000002
+StrictMath.rint(0.5000001) = 1.0
diff --git a/test/123-inline-execute2/info.txt b/test/123-inline-execute2/info.txt
new file mode 100644
index 0000000..4a728a7
--- /dev/null
+++ b/test/123-inline-execute2/info.txt
@@ -0,0 +1 @@
+Sanity checks for added InlineNative methods.
diff --git a/test/123-inline-execute2/src/Main.java b/test/123-inline-execute2/src/Main.java
new file mode 100644
index 0000000..9fadcfd
--- /dev/null
+++ b/test/123-inline-execute2/src/Main.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+import java.util.Locale;
+
+public class Main {
+  public static void main(String args[]) {
+    for (int i = 0; i <= 360; i += 45) {
+      double d = i * (Math.PI / 180.0);
+      System.out.println("Math.sin(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.sin(d)));
+
+      System.out.println("Math.sinh(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.sinh(d)));
+      System.out.println("Math.asin(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.asin(d)));
+      System.out.println("Math.cos(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.cos(d)));
+      System.out.println("Math.cosh(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.cosh(d)));
+      System.out.println("Math.acos(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.acos(d)));
+      if ((i + 90) % 180 != 0) {
+        System.out.println("Math.tan(" + d + ") = "
+            + String.format(Locale.US, "%.12f", Math.tan(d)));
+      }
+      System.out.println("Math.tanh(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.tanh(d)));
+      System.out.println("Math.atan(" + d + ") = "
+          + String.format(Locale.US, "%.12f", Math.atan(d)));
+      System.out.println("Math.atan2(" + d + ", " + (d + 1.0) + ") = "
+          + String.format(Locale.US, "%.12f", Math.atan2(d, d + 1.0)));
+    }
+
+    for (int j = -3; j <= 3; j++) {
+      double e = (double) j;
+      System.out.println("Math.cbrt(" + e + ") = "
+          + String.format(Locale.US, "%.12f", Math.cbrt(e)));
+      System.out.println("Math.log(" + e + ") = "
+          + String.format(Locale.US, "%.12f", Math.log(e)));
+      System.out.println("Math.log10(" + e + ") = "
+          + String.format(Locale.US, "%.12f", Math.log10(e)));
+      System.out.println("Math.log1p(" + e + ") = "
+          + String.format(Locale.US, "%.12f", Math.log1p(e)));
+      System.out.println("Math.exp(" + e + ") = "
+          + String.format(Locale.US, "%.12f", Math.exp(e)));
+      System.out.println("Math.expm1(" + e + ") = "
+          + String.format(Locale.US, "%.12f", Math.expm1(e)));
+      System.out.println("Math.pow(" + e + ", " + (e + 1.0) + ") = "
+          + String.format(Locale.US, "%.12f", Math.pow(e, e + 1.0)));
+      System.out.println("Math.hypot(" + e + ", " + (e + 1.0) + ") = "
+          + String.format(Locale.US, "%.12f", Math.hypot(e, e + 1.0)));
+    }
+
+    System.out.println("Math.ceil(0.0001) = "
+        + String.format(Locale.US, "%.12f", Math.ceil(0.0001)));
+    System.out.println("Math.floor(0.0001) = "
+        + String.format(Locale.US, "%.12f", Math.floor(0.0001)));
+    System.out.println("Math.nextAfter(1.0, 2.0) = "
+        + String.format(Locale.US, "%.12f", Math.nextAfter(1.0, 2.0)));
+    System.out.println("Math.nextAfter(2.0, 1.0) = "
+        + String.format(Locale.US, "%.12f", Math.nextAfter(2.0, 1.0)));
+    System.out.println("Math.rint(0.5000001) = "
+        + String.format(Locale.US, "%.12f", Math.rint(0.5000001)));
+
+    for (int i = 0; i <= 360; i += 45) {
+      double d = i * (StrictMath.PI / 180.0);
+      System.out.println("StrictMath.sin(" + d + ") = " + StrictMath.sin(d));
+      System.out.println("StrictMath.sinh(" + d + ") = " + StrictMath.sinh(d));
+      System.out.println("StrictMath.asin(" + d + ") = " + StrictMath.asin(d));
+      System.out.println("StrictMath.cos(" + d + ") = " + StrictMath.cos(d));
+      System.out.println("StrictMath.cosh(" + d + ") = " + StrictMath.cosh(d));
+      System.out.println("StrictMath.acos(" + d + ") = " + StrictMath.acos(d));
+      System.out.println("StrictMath.tan(" + d + ") = " + StrictMath.tan(d));
+      System.out.println("StrictMath.tanh(" + d + ") = " + StrictMath.tanh(d));
+      System.out.println("StrictMath.atan(" + d + ") = " + StrictMath.atan(d));
+      System.out.println("StrictMath.atan2(" + d + ", " + (d + 1.0) + ") = "
+          + StrictMath.atan2(d, d + 1.0));
+    }
+
+    for (int j = -3; j <= 3; j++) {
+      double e = (double) j;
+      System.out.println("StrictMath.cbrt(" + e + ") = " + StrictMath.cbrt(e));
+      System.out.println("StrictMath.log(" + e + ") = " + StrictMath.log(e));
+      System.out.println("StrictMath.log10(" + e + ") = " + StrictMath.log10(e));
+      System.out.println("StrictMath.log1p(" + e + ") = " + StrictMath.log1p(e));
+      System.out.println("StrictMath.exp(" + e + ") = " + StrictMath.exp(e));
+      System.out.println("StrictMath.expm1(" + e + ") = " + StrictMath.expm1(e));
+      System.out.println("StrictMath.pow(" + e + ", " + (e + 1.0) + ") = "
+          + StrictMath.pow(e, e + 1.0));
+      System.out.println("StrictMath.hypot(" + e + ", " + (e + 1.0) + ") = "
+          + StrictMath.hypot(e, e + 1.0));
+    }
+
+    System.out.println("StrictMath.ceil(0.0001) = " + StrictMath.ceil(0.0001));
+    System.out.println("StrictMath.floor(0.0001) = " + StrictMath.floor(0.0001));
+    System.out.println("StrictMath.nextAfter(1.0, 2.0) = " + StrictMath.nextAfter(1.0, 2.0));
+    System.out.println("StrictMath.rint(0.5000001) = " + StrictMath.rint(0.5000001));
+  }
+
+}
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 17e88ce..98251e4 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -25,6 +25,9 @@
 }
 
 class TestClass {
+  static {
+    sTestClassObj = new TestClass(-1, -2);
+  }
   TestClass() {
   }
   TestClass(int i, int j) {
@@ -37,6 +40,7 @@
   TestClass next;
   String str;
   static int si;
+  static TestClass sTestClassObj;
 }
 
 class SubTestClass extends TestClass {
@@ -115,10 +119,11 @@
   }
 
   /// CHECK-START: int Main.test3(TestClass) load_store_elimination (before)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
   /// CHECK: NewInstance
+  /// CHECK: StaticFieldGet
+  /// CHECK: NewInstance
+  /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldSet
   /// CHECK: InstanceFieldGet
@@ -127,24 +132,31 @@
   /// CHECK: InstanceFieldGet
 
   /// CHECK-START: int Main.test3(TestClass) load_store_elimination (after)
-  /// CHECK: InstanceFieldSet
-  /// CHECK: InstanceFieldGet
-  /// CHECK: InstanceFieldSet
   /// CHECK: NewInstance
-  /// CHECK-NOT: InstanceFieldSet
+  /// CHECK: StaticFieldGet
+  /// CHECK: NewInstance
+  /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
   /// CHECK-NOT: InstanceFieldGet
+  /// CHECK-NOT: StaticFieldGet
 
-  // A new allocation shouldn't alias with pre-existing values.
+  // A new allocation (even non-singleton) shouldn't alias with pre-existing values.
   static int test3(TestClass obj) {
     // Do an allocation here to avoid the HLoadClass and HClinitCheck
     // at the second allocation.
     new TestClass();
+    TestClass obj1 = TestClass.sTestClassObj;
+    TestClass obj2 = new TestClass();  // Cannot alias with obj or obj1 which pre-exist.
+    obj.next = obj2;  // Make obj2 a non-singleton.
+    // All stores below need to stay since obj/obj1/obj2 are not singletons.
     obj.i = 1;
-    obj.next.j = 2;
-    TestClass obj2 = new TestClass();
+    obj1.j = 2;
+    // Following stores won't kill values of obj.i and obj1.j.
     obj2.i = 3;
     obj2.j = 4;
-    return obj.i + obj.next.j + obj2.i + obj2.j;
+    return obj.i + obj1.j + obj2.i + obj2.j;
   }
 
   /// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (before)
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index 44e907d..1756fa4 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -202,27 +202,35 @@
 # Test that DCE removes catch phi uses of instructions defined in dead try blocks.
 
 ## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination_final (before)
-## CHECK-DAG:     <<Arg0:i\d+>>     ParameterValue
-## CHECK-DAG:     <<Arg1:i\d+>>     ParameterValue
-## CHECK-DAG:     <<Const0xa:i\d+>> IntConstant 10
-## CHECK-DAG:     <<Const0xb:i\d+>> IntConstant 11
-## CHECK-DAG:     <<Const0xc:i\d+>> IntConstant 12
-## CHECK-DAG:     <<Const0xd:i\d+>> IntConstant 13
-## CHECK-DAG:     <<Const0xe:i\d+>> IntConstant 14
-## CHECK-DAG:     <<Add:i\d+>>      Add [<<Arg0>>,<<Arg1>>]
-## CHECK-DAG:                       Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
-## CHECK-DAG:                       Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG:     <<Arg0:i\d+>>      ParameterValue
+## CHECK-DAG:     <<Arg1:i\d+>>      ParameterValue
+## CHECK-DAG:     <<Const0xa:i\d+>>  IntConstant 10
+## CHECK-DAG:     <<Const0xb:i\d+>>  IntConstant 11
+## CHECK-DAG:     <<Const0xc:i\d+>>  IntConstant 12
+## CHECK-DAG:     <<Const0xd:i\d+>>  IntConstant 13
+## CHECK-DAG:     <<Const0xe:i\d+>>  IntConstant 14
+## CHECK-DAG:     <<Const0xf:i\d+>>  IntConstant 15
+## CHECK-DAG:     <<Const0x10:i\d+>> IntConstant 16
+## CHECK-DAG:     <<Const0x11:i\d+>> IntConstant 17
+## CHECK-DAG:     <<Add:i\d+>>       Add [<<Arg0>>,<<Arg1>>]
+## CHECK-DAG:     <<Phi:i\d+>>       Phi [<<Add>>,<<Const0xf>>] reg:3 is_catch_phi:false
+## CHECK-DAG:                        Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
+## CHECK-DAG:                        Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG:                        Phi [<<Phi>>,<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
 
 ## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination_final (after)
-## CHECK-DAG:     <<Const0xb:i\d+>> IntConstant 11
-## CHECK-DAG:     <<Const0xc:i\d+>> IntConstant 12
-## CHECK-DAG:     <<Const0xd:i\d+>> IntConstant 13
-## CHECK-DAG:     <<Const0xe:i\d+>> IntConstant 14
-## CHECK-DAG:                       Phi [<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
-## CHECK-DAG:                       Phi [<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG:     <<Const0xb:i\d+>>  IntConstant 11
+## CHECK-DAG:     <<Const0xc:i\d+>>  IntConstant 12
+## CHECK-DAG:     <<Const0xd:i\d+>>  IntConstant 13
+## CHECK-DAG:     <<Const0xe:i\d+>>  IntConstant 14
+## CHECK-DAG:     <<Const0x10:i\d+>> IntConstant 16
+## CHECK-DAG:     <<Const0x11:i\d+>> IntConstant 17
+## CHECK-DAG:                        Phi [<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
+## CHECK-DAG:                        Phi [<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG:                        Phi [<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
 
 .method public static testCatchPhiInputs_DefinedInTryBlock(IIII)I
-    .registers 7
+    .registers 8
 
     invoke-static {}, LTestCase;->$inline$False()Z
     move-result v0
@@ -232,17 +240,24 @@
     shr-int/2addr p2, p3
 
     :try_start
-    const v1, 0xa           # dead catch phi input, defined in entry block
-    add-int v2, p0, p1      # dead catch phi input, defined in the dead block
+    const v1, 0xa           # dead catch phi input, defined in entry block (HInstruction)
+    add-int v2, p0, p1      # dead catch phi input, defined in the dead block (HInstruction)
+    move v3, v2
+    if-eqz v3, :define_phi
+    const v3, 0xf
+    :define_phi
+    # v3 = Phi [Add, 0xf]   # dead catch phi input, defined in the dead block (HPhi)
     div-int/2addr p0, v2
 
     :else
     const v1, 0xb           # live catch phi input
     const v2, 0xc           # live catch phi input
+    const v3, 0x10          # live catch phi input
     div-int/2addr p0, p3
 
     const v1, 0xd           # live catch phi input
     const v2, 0xe           # live catch phi input
+    const v3, 0x11          # live catch phi input
     div-int/2addr p0, p1
     :try_end
     .catchall {:try_start .. :try_end} :catch_all
@@ -252,6 +267,7 @@
 
     :catch_all
     sub-int p0, v1, v2      # use catch phi values
+    sub-int p0, p0, v3      # use catch phi values
     goto :return
 
 .end method
@@ -260,8 +276,6 @@
 # dead try blocks.
 
 ## CHECK-START: int TestCase.testCatchPhiInputs_DefinedOutsideTryBlock(int, int, int, int) dead_code_elimination_final (before)
-## CHECK-DAG:     <<Arg0:i\d+>>     ParameterValue
-## CHECK-DAG:     <<Arg1:i\d+>>     ParameterValue
 ## CHECK-DAG:     <<Const0xa:i\d+>> IntConstant 10
 ## CHECK-DAG:     <<Const0xb:i\d+>> IntConstant 11
 ## CHECK-DAG:     <<Const0xc:i\d+>> IntConstant 12
diff --git a/test/554-jit-profile-file/expected.txt b/test/554-jit-profile-file/expected.txt
new file mode 100644
index 0000000..cde211e
--- /dev/null
+++ b/test/554-jit-profile-file/expected.txt
@@ -0,0 +1,7 @@
+JNI_OnLoad called
+ProfileInfo:
+:classes.dex
+  java.lang.String Main.hotMethod()
+  void Main.main(java.lang.String[])
+:classes2.dex
+  java.lang.String OtherDex.hotMethod()
diff --git a/test/554-jit-profile-file/info.txt b/test/554-jit-profile-file/info.txt
new file mode 100644
index 0000000..b1bfe81
--- /dev/null
+++ b/test/554-jit-profile-file/info.txt
@@ -0,0 +1 @@
+Check that saving and restoring profile files works correctly in a JIT environment.
diff --git a/test/554-jit-profile-file/offline_profile.cc b/test/554-jit-profile-file/offline_profile.cc
new file mode 100644
index 0000000..75e441f
--- /dev/null
+++ b/test/554-jit-profile-file/offline_profile.cc
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#include "dex_file.h"
+
+#include "jit/offline_profiling_info.h"
+#include "jni.h"
+#include "mirror/class-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+namespace {
+
+extern "C" JNIEXPORT jstring JNICALL Java_Main_getProfileInfoDump(
+      JNIEnv* env, jclass cls, jstring filename) {
+  std::string dex_location;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    dex_location = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile()->GetLocation();
+  }
+  const OatFile* oat_file = Runtime::Current()->GetOatFileManager().GetPrimaryOatFile();
+  std::vector<std::unique_ptr<const DexFile>> dex_files =
+      OatFileAssistant::LoadDexFiles(*oat_file, dex_location.c_str());
+  const char* filename_chars = env->GetStringUTFChars(filename, nullptr);
+
+  std::vector<const DexFile*> dex_files_raw;
+  for (size_t i = 0; i < dex_files.size(); i++) {
+    dex_files_raw.push_back(dex_files[i].get());
+  }
+
+  ProfileCompilationInfo info(filename_chars);
+
+  std::string result = info.Load(dex_files_raw)
+      ? info.DumpInfo(/*print_full_dex_location*/false)
+      : "Could not load profile info";
+
+  env->ReleaseStringUTFChars(filename, filename_chars);
+  // Return the dump of the profile info. It will be compared against a golden value.
+  return env->NewStringUTF(result.c_str());
+}
+
+}  // namespace
+}  // namespace art
diff --git a/test/554-jit-profile-file/run b/test/554-jit-profile-file/run
new file mode 100644
index 0000000..08dcb38
--- /dev/null
+++ b/test/554-jit-profile-file/run
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+exec ${RUN} \
+  -Xcompiler-option --compiler-filter=interpret-only \
+  --runtime-option -Xjitsaveprofilinginfo \
+  --runtime-option -Xusejit:true \
+  --runtime-option -Xjitthreshold:100 \
+  "${@}"
diff --git a/test/554-jit-profile-file/src-multidex/OtherDex.java b/test/554-jit-profile-file/src-multidex/OtherDex.java
new file mode 100644
index 0000000..51644db
--- /dev/null
+++ b/test/554-jit-profile-file/src-multidex/OtherDex.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+import java.util.HashMap;
+
+public class OtherDex {
+  public void coldMethod() {
+    hotMethod();
+  }
+
+  public String hotMethod() {
+    HashMap<String, String> map = new HashMap<String, String>();
+    for (int i = 0; i < 10; i++) {
+      map.put("" + i, "" + i + 1);
+    }
+    return map.get("1");
+  }
+}
diff --git a/test/554-jit-profile-file/src/Main.java b/test/554-jit-profile-file/src/Main.java
new file mode 100644
index 0000000..b581a67
--- /dev/null
+++ b/test/554-jit-profile-file/src/Main.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+public class Main {
+
+  public void coldMethod() {
+    hotMethod();
+  }
+
+  public String hotMethod() {
+    HashMap<String, String> map = new HashMap<String, String>();
+    for (int i = 0; i < 10; i++) {
+      map.put("" + i, "" + i + 1);
+    }
+    return map.get("1");
+  }
+
+  private static final String PKG_NAME = "test.package";
+  private static final String APP_DIR_PREFIX = "app_dir_";
+  private static final String CODE_CACHE = "code_cache";
+  private static final String PROFILE_FILE = PKG_NAME + ".prof";
+  private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+  private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+  private static final int JIT_INVOCATION_COUNT = 101;
+
+  /* needs to match Runtime:: kProfileBackground */
+  private static final int PROFILE_BACKGROUND = 1;
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    File file = null;
+    File appDir = null;
+    File profileDir = null;
+    File profileFile = null;
+    try {
+      // We don't know where we have rights to create the code_cache. So create
+      // a dummy temporary file and get its parent directory. That will serve as
+      // the app directory.
+      file = createTempFile();
+      appDir = new File(file.getParent(), APP_DIR_PREFIX + file.getName());
+      appDir.mkdir();
+      profileDir = new File(appDir, CODE_CACHE);
+      profileDir.mkdir();
+
+      // Registering the app info will set the profile file name.
+      VMRuntime.registerAppInfo(PKG_NAME, appDir.getPath());
+
+      // Make sure the hot methods are jitted.
+      Main m = new Main();
+      OtherDex o = new OtherDex();
+      for (int i = 0; i < JIT_INVOCATION_COUNT; i++) {
+        m.hotMethod();
+        o.hotMethod();
+      }
+
+      // Updating the process state to BACKGROUND will trigger profile saving.
+      VMRuntime.updateProcessState(PROFILE_BACKGROUND);
+
+      // Check that the profile file exists.
+      profileFile = new File(profileDir, PROFILE_FILE);
+      if (!profileFile.exists()) {
+        throw new RuntimeException("No profile file found");
+      }
+      // Dump the profile file.
+      // We know what methods are hot and we compare with the golden `expected` output.
+      System.out.println(getProfileInfoDump(profileFile.getPath()));
+    } finally {
+      if (file != null) {
+        file.delete();
+      }
+      if (profileFile != null) {
+        profileFile.delete();
+      }
+      if (profileDir != null) {
+        profileDir.delete();
+      }
+      if (appDir != null) {
+        appDir.delete();
+      }
+    }
+  }
+
+  private static class VMRuntime {
+    private static final Method registerAppInfoMethod;
+    private static final Method updateProcessStateMethod;
+    private static final Method getRuntimeMethod;
+    static {
+      try {
+        Class c = Class.forName("dalvik.system.VMRuntime");
+        registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+            String.class, String.class, String.class);
+        updateProcessStateMethod = c.getDeclaredMethod("updateProcessState", Integer.TYPE);
+        getRuntimeMethod = c.getDeclaredMethod("getRuntime");
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    public static void registerAppInfo(String pkgName, String appDir) throws Exception {
+      registerAppInfoMethod.invoke(null, pkgName, appDir, null);
+    }
+    public static void updateProcessState(int state) throws Exception {
+      Object runtime = getRuntimeMethod.invoke(null);
+      updateProcessStateMethod.invoke(runtime, state);
+    }
+  }
+
+  static native String getProfileInfoDump(
+      String filename);
+
+  private static File createTempFile() throws Exception {
+    try {
+      return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+    } catch (IOException e) {
+      System.setProperty("java.io.tmpdir", "/data/local/tmp");
+      try {
+        return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+      } catch (IOException e2) {
+        System.setProperty("java.io.tmpdir", "/sdcard");
+        return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+      }
+    }
+  }
+}
diff --git a/test/556-invoke-super/expected.txt b/test/556-invoke-super/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/556-invoke-super/expected.txt
diff --git a/test/556-invoke-super/info.txt b/test/556-invoke-super/info.txt
new file mode 100644
index 0000000..7de2a4f
--- /dev/null
+++ b/test/556-invoke-super/info.txt
@@ -0,0 +1 @@
+Tests the invoke-super opcode with multidex.
diff --git a/test/556-invoke-super/smali/invokesuper.smali b/test/556-invoke-super/smali/invokesuper.smali
new file mode 100644
index 0000000..ef55000
--- /dev/null
+++ b/test/556-invoke-super/smali/invokesuper.smali
@@ -0,0 +1,40 @@
+#
+# 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.
+
+
+.class public LInvokeSuper;
+.super LSuperClass;
+
+.method public constructor <init>()V
+.registers 1
+    invoke-direct {v0}, LSuperClass;-><init>()V
+    return-void
+.end method
+
+
+.method public run()I
+.registers 2
+    # Do an invoke super on this class, to confuse runtime/compiler.
+    invoke-super {p0}, LInvokeSuper;->returnInt()I
+    move-result v0
+    return v0
+.end method
+
+
+.method public returnInt()I
+.registers 2
+    const v0, 777
+    return v0
+.end method
diff --git a/test/556-invoke-super/src-multidex/SuperClass.java b/test/556-invoke-super/src-multidex/SuperClass.java
new file mode 100644
index 0000000..faf16c4
--- /dev/null
+++ b/test/556-invoke-super/src-multidex/SuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class SuperClass {
+  public int returnInt() {
+    return 42;
+  }
+}
diff --git a/test/556-invoke-super/src/Main.java b/test/556-invoke-super/src/Main.java
new file mode 100644
index 0000000..07289f7
--- /dev/null
+++ b/test/556-invoke-super/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+  // Workaround for b/18051191.
+  class InnerClass {}
+
+  static void assertEquals(int expected, int value) {
+    if (expected != value) {
+      throw new Error("Expected " + expected + ", got " + value);
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    Class<?> c = Class.forName("InvokeSuper");
+    Method m = c.getMethod("run");
+    assertEquals(42, ((Integer)m.invoke(c.newInstance(), new Object[0])).intValue());
+  }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index f74a516..f84dfe6 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -38,7 +38,8 @@
   461-get-reference-vreg/get_reference_vreg_jni.cc \
   466-get-live-vreg/get_live_vreg_jni.cc \
   497-inlining-and-class-loader/clear_dex_cache.cc \
-  543-env-long-ref/env_long_ref.cc
+  543-env-long-ref/env_long_ref.cc \
+  554-jit-profile-file/offline_profile.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 0925d36..72b0dcb 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -276,7 +276,8 @@
 TEST_ART_BROKEN_NO_RELOCATE_TESTS := \
   117-nopatchoat \
   118-noimage-dex2oat \
-  119-noimage-patchoat
+  119-noimage-patchoat \
+  554-jit-profile-file
 
 ifneq (,$(filter no-relocate,$(RELOCATE_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -298,6 +299,7 @@
   412-new-array \
   471-uninitialized-locals \
   506-verify-aput \
+  554-jit-profile-file \
   800-smali
 
 ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
@@ -356,13 +358,15 @@
 # All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
 # Therefore we shouldn't run them in situations where we actually don't have these since they
 # explicitly test for them. These all also assume we have an image.
+# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
 TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
   116-nodex2oat \
   117-nopatchoat \
   118-noimage-dex2oat \
   119-noimage-patchoat \
   137-cfi \
-  138-duplicate-classes-check2
+  138-duplicate-classes-check2 \
+  554-jit-profile-file
 
 # This test fails without an image.
 TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
@@ -413,7 +417,8 @@
 # Known broken tests for the interpreter.
 # CFI unwinding expects managed frames.
 TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
-  137-cfi
+  137-cfi \
+  554-jit-profile-file
 
 ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -520,7 +525,8 @@
 # Tests that should fail in the heap poisoning configuration with the interpreter.
 # 137: Cannot run this with the interpreter.
 TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS := \
-  137-cfi
+  137-cfi \
+  554-jit-profile-file
 
 ifeq ($(ART_HEAP_POISONING),true)
   ifneq (,$(filter default,$(COMPILER_TYPES)))
diff --git a/test/dexdump/bytecodes.txt b/test/dexdump/bytecodes.txt
index d14c47c..4c8b79b 100755
--- a/test/dexdump/bytecodes.txt
+++ b/test/dexdump/bytecodes.txt
@@ -196,6 +196,7 @@
       name          : 'icon'
       type          : 'I'
       access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 2130837504
   Instance fields   -
   Direct methods    -
     #0              : (in Lcom/google/android/test/R$drawable;)
diff --git a/test/dexdump/bytecodes.xml b/test/dexdump/bytecodes.xml
index 0581677..8e54dd3 100755
--- a/test/dexdump/bytecodes.xml
+++ b/test/dexdump/bytecodes.xml
@@ -97,6 +97,7 @@
  static="true"
  final="true"
  visibility="public"
+ value="2130837504"
 >
 </field>
 <constructor name="R.drawable"
diff --git a/test/dexdump/staticfields.dex b/test/dexdump/staticfields.dex
new file mode 100644
index 0000000..a07c46e
--- /dev/null
+++ b/test/dexdump/staticfields.dex
Binary files differ
diff --git a/test/dexdump/staticfields.lst b/test/dexdump/staticfields.lst
new file mode 100644
index 0000000..5375b8e
--- /dev/null
+++ b/test/dexdump/staticfields.lst
@@ -0,0 +1,2 @@
+#staticfields.dex
+0x000001bc 8 StaticFields <init> ()V StaticFields.java 24
diff --git a/test/dexdump/staticfields.txt b/test/dexdump/staticfields.txt
new file mode 100644
index 0000000..022605f
--- /dev/null
+++ b/test/dexdump/staticfields.txt
@@ -0,0 +1,126 @@
+Processing 'staticfields.dex'...
+Opened 'staticfields.dex', DEX version '035'
+DEX file header:
+magic               : 'dex\n035\0'
+checksum            : 52d4fc6d
+signature           : 6e82...2f27
+file_size           : 1264
+header_size         : 112
+link_size           : 0
+link_off            : 0 (0x000000)
+string_ids_size     : 28
+string_ids_off      : 112 (0x000070)
+type_ids_size       : 12
+type_ids_off        : 224 (0x0000e0)
+proto_ids_size       : 1
+proto_ids_off        : 272 (0x000110)
+field_ids_size      : 12
+field_ids_off       : 284 (0x00011c)
+method_ids_size     : 2
+method_ids_off      : 380 (0x00017c)
+class_defs_size     : 1
+class_defs_off      : 396 (0x00018c)
+data_size           : 836
+data_off            : 428 (0x0001ac)
+
+Class #0 header:
+class_idx           : 6
+access_flags        : 1 (0x0001)
+superclass_idx      : 7
+interfaces_off      : 0 (0x000000)
+source_file_idx     : 11
+annotations_off     : 0 (0x000000)
+class_data_off      : 1067 (0x00042b)
+static_fields_size  : 12
+instance_fields_size: 0
+direct_methods_size : 1
+virtual_methods_size: 0
+
+Class #0            -
+  Class descriptor  : 'LStaticFields;'
+  Access flags      : 0x0001 (PUBLIC)
+  Superclass        : 'Ljava/lang/Object;'
+  Interfaces        -
+  Static fields     -
+    #0              : (in LStaticFields;)
+      name          : 'test00_public_static_final_byte_42'
+      type          : 'B'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 42
+    #1              : (in LStaticFields;)
+      name          : 'test01_public_static_final_short_43'
+      type          : 'S'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 43
+    #2              : (in LStaticFields;)
+      name          : 'test02_public_static_final_char_X'
+      type          : 'C'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 88
+    #3              : (in LStaticFields;)
+      name          : 'test03_public_static_final_int_44'
+      type          : 'I'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 44
+    #4              : (in LStaticFields;)
+      name          : 'test04_public_static_final_long_45'
+      type          : 'J'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 45
+    #5              : (in LStaticFields;)
+      name          : 'test05_public_static_final_float_46_47'
+      type          : 'F'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 46.470001
+    #6              : (in LStaticFields;)
+      name          : 'test06_public_static_final_double_48_49'
+      type          : 'D'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : 48.490000
+    #7              : (in LStaticFields;)
+      name          : 'test07_public_static_final_string'
+      type          : 'Ljava/lang/String;'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : "abc \\><\"'&\t\r\n"
+    #8              : (in LStaticFields;)
+      name          : 'test08_public_static_final_object_null'
+      type          : 'Ljava/lang/Object;'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : null
+    #9              : (in LStaticFields;)
+      name          : 'test09_public_static_final_boolean_true'
+      type          : 'Z'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+      value         : true
+    #10              : (in LStaticFields;)
+      name          : 'test10_private_static_final_int_50'
+      type          : 'I'
+      access        : 0x001a (PRIVATE STATIC FINAL)
+      value         : 50
+    #11              : (in LStaticFields;)
+      name          : 'test99_empty_value'
+      type          : 'I'
+      access        : 0x0019 (PUBLIC STATIC FINAL)
+  Instance fields   -
+  Direct methods    -
+    #0              : (in LStaticFields;)
+      name          : '<init>'
+      type          : '()V'
+      access        : 0x10001 (PUBLIC CONSTRUCTOR)
+      code          -
+      registers     : 1
+      ins           : 1
+      outs          : 1
+      insns size    : 4 16-bit code units
+0001ac:                                        |[0001ac] StaticFields.<init>:()V
+0001bc: 7010 0100 0000                         |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0001
+0001c2: 0e00                                   |0003: return-void
+      catches       : (none)
+      positions     : 
+        0x0000 line=24
+      locals        : 
+        0x0000 - 0x0004 reg=0 this LStaticFields; 
+
+  Virtual methods   -
+  source_file_idx   : 11 (StaticFields.java)
+
diff --git a/test/dexdump/staticfields.xml b/test/dexdump/staticfields.xml
new file mode 100644
index 0000000..6cff71b
--- /dev/null
+++ b/test/dexdump/staticfields.xml
@@ -0,0 +1,129 @@
+<api>
+<package name=""
+>
+<class name="StaticFields"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<field name="test00_public_static_final_byte_42"
+ type="byte"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="42"
+>
+</field>
+<field name="test01_public_static_final_short_43"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="43"
+>
+</field>
+<field name="test02_public_static_final_char_X"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="88"
+>
+</field>
+<field name="test03_public_static_final_int_44"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="44"
+>
+</field>
+<field name="test04_public_static_final_long_45"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="45"
+>
+</field>
+<field name="test05_public_static_final_float_46_47"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="46.470001"
+>
+</field>
+<field name="test06_public_static_final_double_48_49"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="48.490000"
+>
+</field>
+<field name="test07_public_static_final_string"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="abc \>&lt;&quot;'&amp;&#x9;&#xD;&#xA;"
+>
+</field>
+<field name="test08_public_static_final_object_null"
+ type="java.lang.Object"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="null"
+>
+</field>
+<field name="test09_public_static_final_boolean_true"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="true"
+>
+</field>
+<field name="test99_empty_value"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+>
+</field>
+<constructor name="StaticFields"
+ type="StaticFields"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+</class>
+</package>
+</api>