Merge "Try to avoid allocation when interning strings."
diff --git a/Android.bp b/Android.bp
index 835048d..77b9ac3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,6 +1,34 @@
+// TODO: These should be handled with transitive static library dependencies
+art_static_dependencies = [
+    // Note: the order is important because of static linking resolution.
+    "libziparchive",
+    "libnativehelper",
+    "libnativebridge",
+    "libnativeloader",
+    "libsigchain_dummy",
+    "liblog",
+    "libz",
+    "libbacktrace",
+    "libcutils",
+    "libunwindbacktrace",
+    "libutils",
+    "libbase",
+    "liblz4",
+    "liblzma",
+]
+
 subdirs = [
+    "benchmark",
     "build",
     "compiler",
+    "dalvikvm",
+    "dexdump",
+    "dexlayout",
+    "dexlist",
+    "disassembler",
+    "oatdump",
     "runtime",
     "sigchainlib",
+    "tools/cpp-define-generator",
+    "tools/dmtracedump",
 ]
diff --git a/Android.mk b/Android.mk
index 4ea169a..5a17379 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,20 +76,14 @@
 ########################################################################
 # product rules
 
-include $(art_path)/dexdump/Android.mk
-include $(art_path)/dexlayout/Android.mk
-include $(art_path)/dexlist/Android.mk
 include $(art_path)/dex2oat/Android.mk
-include $(art_path)/disassembler/Android.mk
 include $(art_path)/oatdump/Android.mk
 include $(art_path)/imgdiag/Android.mk
 include $(art_path)/patchoat/Android.mk
 include $(art_path)/profman/Android.mk
-include $(art_path)/dalvikvm/Android.mk
 include $(art_path)/tools/Android.mk
 include $(art_path)/tools/ahat/Android.mk
 include $(art_path)/tools/dexfuzz/Android.mk
-include $(art_path)/tools/dmtracedump/Android.mk
 include $(art_path)/libart_fake/Android.mk
 
 
@@ -114,7 +108,6 @@
 include $(art_path)/build/Android.common_test.mk
 include $(art_path)/build/Android.gtest.mk
 include $(art_path)/test/Android.run-test.mk
-include $(art_path)/benchmark/Android.mk
 
 TEST_ART_ADB_ROOT_AND_REMOUNT := \
     (adb root && \
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
new file mode 100644
index 0000000..617f5b0
--- /dev/null
+++ b/benchmark/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+art_cc_library {
+    name: "libartbenchmark",
+    host_supported: true,
+    defaults: ["art_defaults", "art_debug_defaults"],
+    srcs: [
+        "jobject-benchmark/jobject_benchmark.cc",
+        "jni-perf/perf_jni.cc",
+        "scoped-primitive-array/scoped_primitive_array.cc",
+    ],
+    shared_libs: [
+        "libart",
+        "libbacktrace",
+        "libnativehelper",
+    ],
+    clang: true,
+    target: {
+        android: {
+            shared_libs: ["libdl"],
+        },
+        host: {
+            host_ldlibs: ["-ldl", "-lpthread"],
+        },
+    },
+    cflags: [
+        "-Wno-frame-larger-than=",
+    ],
+}
diff --git a/benchmark/Android.mk b/benchmark/Android.mk
deleted file mode 100644
index 2360bcc..0000000
--- a/benchmark/Android.mk
+++ /dev/null
@@ -1,79 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include art/build/Android.common_build.mk
-
-LIBARTBENCHMARK_COMMON_SRC_FILES := \
-  jobject-benchmark/jobject_benchmark.cc \
-  jni-perf/perf_jni.cc \
-  scoped-primitive-array/scoped_primitive_array.cc
-
-# $(1): target or host
-define build-libartbenchmark
-  ifneq ($(1),target)
-    ifneq ($(1),host)
-      $$(error expected target or host for argument 1, received $(1))
-    endif
-  endif
-
-  art_target_or_host := $(1)
-
-  include $(CLEAR_VARS)
-  LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
-  LOCAL_MODULE := libartbenchmark
-  ifeq ($$(art_target_or_host),target)
-    LOCAL_MODULE_TAGS := tests
-  endif
-  LOCAL_SRC_FILES := $(LIBARTBENCHMARK_COMMON_SRC_FILES)
-  LOCAL_SHARED_LIBRARIES += libart libbacktrace libnativehelper
-  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
-  LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
-  LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-  ifeq ($$(art_target_or_host),target)
-    LOCAL_CLANG := $(ART_TARGET_CLANG)
-    $(call set-target-local-cflags-vars,debug)
-    LOCAL_SHARED_LIBRARIES += libdl
-    LOCAL_MULTILIB := both
-    # LOCAL_MODULE_PATH_32 := $(ART_TARGET_OUT)/$(ART_TARGET_ARCH_32)
-    # LOCAL_MODULE_PATH_64 := $(ART_TARGET_OUT)/$(ART_TARGET_ARCH_64)
-    LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
-    include $(BUILD_SHARED_LIBRARY)
-  else # host
-    LOCAL_CLANG := $(ART_HOST_CLANG)
-    LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
-    LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS) $(ART_HOST_DEBUG_ASFLAGS)
-    LOCAL_LDLIBS := -ldl -lpthread
-    LOCAL_IS_HOST_MODULE := true
-    LOCAL_MULTILIB := both
-    include $(BUILD_HOST_SHARED_LIBRARY)
-  endif
-
-  # Clear locally used variables.
-  art_target_or_host :=
-endef
-
-ifeq ($(ART_BUILD_TARGET),true)
-  $(eval $(call build-libartbenchmark,target))
-endif
-ifeq ($(ART_BUILD_HOST),true)
-  $(eval $(call build-libartbenchmark,host))
-endif
-
-# Clear locally used variables.
-LOCAL_PATH :=
-LIBARTBENCHMARK_COMMON_SRC_FILES :=
diff --git a/build/Android.bp b/build/Android.bp
index ed9f308..4be43ec 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -101,6 +101,9 @@
                 // Apple, it's a pain.
                 "-Wmissing-noreturn",
             ],
+            host_ldlibs: [
+                "-lrt",
+            ],
         },
         host: {
             cflags: [
@@ -108,6 +111,10 @@
                 // clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
                 "-fno-omit-frame-pointer",
             ],
+            host_ldlibs: [
+                "-ldl",
+                "-lpthread",
+            ],
         },
     },
 
@@ -133,7 +140,6 @@
     },
 
     include_dirs: [
-        "external/gtest/include",
         "external/icu/icu4c/source/common",
         "external/lz4/lib",
         "external/valgrind/include",
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index b5d41d9..7edc1cc 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -87,7 +87,6 @@
 ART_CPP_EXTENSION := .cc
 
 ART_C_INCLUDES := \
-  external/gtest/include \
   external/icu/icu4c/source/common \
   external/lz4/lib \
   external/valgrind/include \
@@ -238,6 +237,17 @@
   art_cflags += -DART_USE_TLAB=1
 endif
 
+# Are additional statically-linked ART host binaries (dex2oats,
+# oatdumps, etc.) getting built?
+ifeq ($(ART_BUILD_HOST_STATIC),true)
+  art_cflags += -DART_BUILD_HOST_STATIC=1
+endif
+
+# Temporary flag allowing to disable recent changes in oat file management.
+ifneq ($(ART_ENABLE_VDEX),false)
+  art_cflags += -DART_ENABLE_VDEX
+endif
+
 # Cflags for non-debug ART and ART tools.
 art_non_debug_cflags := \
   $(ART_NDEBUG_OPT_FLAG)
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 3f25ae1..e88d027 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -123,7 +123,7 @@
 
 # Depend on the -target or -host phony targets generated by the build system
 # for each module
-ART_TARGET_EXECUTABLES :=
+ART_TARGET_EXECUTABLES := dalvikvm-target
 ifneq ($(ART_BUILD_TARGET_NDEBUG),false)
 ART_TARGET_EXECUTABLES += $(foreach name,$(ART_CORE_EXECUTABLES),$(name)-target)
 endif
@@ -131,7 +131,7 @@
 ART_TARGET_EXECUTABLES += $(foreach name,$(ART_CORE_EXECUTABLES),$(name)d-target)
 endif
 
-ART_HOST_EXECUTABLES :=
+ART_HOST_EXECUTABLES := dalvikvm-host
 ifneq ($(ART_BUILD_HOST_NDEBUG),false)
 ART_HOST_EXECUTABLES += $(foreach name,$(ART_CORE_EXECUTABLES),$(name)-host)
 endif
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index e66b30c..c35833d 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -18,7 +18,7 @@
 
 ART_EXECUTABLES_CFLAGS :=
 
-# $(1): executable ("d" will be appended for debug version)
+# $(1): executable ("d" will be appended for debug version, "s" will be appended for static version)
 # $(2): source
 # $(3): extra shared libraries
 # $(4): extra include directories
@@ -67,7 +67,7 @@
   endif
 
   ifeq ($$(art_static_or_shared),static)
-    LOCAL_MODULE := $(LOCAL_MODULE)s
+    LOCAL_MODULE := $$(LOCAL_MODULE)s
   endif
 
   LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS)
@@ -249,3 +249,20 @@
     )
   )
 endef
+
+# Note: the order is important because of static linking resolution.
+ART_STATIC_DEPENDENCIES := \
+  libziparchive \
+  libnativehelper \
+  libnativebridge \
+  libnativeloader \
+  libsigchain_dummy \
+  liblog \
+  libz \
+  libbacktrace \
+  libcutils \
+  libunwindbacktrace \
+  libutils \
+  libbase \
+  liblz4 \
+  liblzma
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c09241a..8a6d52d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -37,6 +37,7 @@
   MyClassNatives \
   Nested \
   NonStaticLeafMethods \
+  Packages \
   ProtoCompare \
   ProtoCompare2 \
   ProfileTestMultiDex \
@@ -69,7 +70,7 @@
 
 ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
-ART_GTEST_dex_cache_test_DEX_DEPS := Main
+ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
@@ -422,7 +423,7 @@
 LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
 LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/cmdline art/compiler
 LOCAL_SHARED_LIBRARIES := libartd libartd-compiler libdl
-LOCAL_STATIC_LIBRARIES += libgtest
+LOCAL_WHOLE_STATIC_LIBRARIES += libgtest
 LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
 $(eval LOCAL_CLANG := $(ART_TARGET_CLANG))
@@ -439,7 +440,7 @@
 LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
 LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/cmdline art/compiler
 LOCAL_SHARED_LIBRARIES := libartd libartd-compiler
-LOCAL_STATIC_LIBRARIES := libgtest_host
+LOCAL_WHOLE_STATIC_LIBRARIES := libgtest
 LOCAL_LDLIBS += -ldl -lpthread
 LOCAL_MULTILIB := both
 LOCAL_CLANG := $(ART_HOST_CLANG)
diff --git a/build/art.go b/build/art.go
index 4e64dcf..da4609d 100644
--- a/build/art.go
+++ b/build/art.go
@@ -43,6 +43,10 @@
 		cflags = append(cflags, "-DART_USE_TLAB=1")
 	}
 
+	if !envFalse(ctx, "ART_ENABLE_VDEX") {
+		cflags = append(cflags, "-DART_ENABLE_VDEX")
+	}
+
 	imtSize := envDefault(ctx, "ART_IMT_SIZE", "43")
 	cflags = append(cflags, "-DIMT_SIZE="+imtSize)
 
@@ -67,6 +71,12 @@
 		cflags = append(cflags, "-fstack-protector")
 	}
 
+	// Are additional statically-linked ART host binaries
+	// (dex2oats, oatdumps, etc.) getting built?
+	if envTrue(ctx, "ART_BUILD_HOST_STATIC") {
+		cflags = append(cflags, "-DART_BUILD_HOST_STATIC=1")
+	}
+
 	return cflags, asflags
 }
 
@@ -135,8 +145,42 @@
 
 type artGlobalDefaults struct{}
 
+func (a *artCustomLinkerCustomizer) CustomizeProperties(ctx android.CustomizePropertiesContext) {
+	linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "")
+	if linker != "" {
+		type props struct {
+			DynamicLinker string
+		}
+
+		p := &props{}
+		p.DynamicLinker = linker
+		ctx.AppendProperties(p)
+	}
+}
+
+type artCustomLinkerCustomizer struct{}
+
+func (a *artPrefer32BitCustomizer) CustomizeProperties(ctx android.CustomizePropertiesContext) {
+	if envTrue(ctx, "HOST_PREFER_32_BIT") {
+		type props struct {
+			Target struct {
+				Host struct {
+					Compile_multilib string
+				}
+			}
+		}
+
+		p := &props{}
+		p.Target.Host.Compile_multilib = "prefer32"
+		ctx.AppendProperties(p)
+	}
+}
+
+type artPrefer32BitCustomizer struct{}
+
 func init() {
 	soong.RegisterModuleType("art_cc_library", artLibrary)
+	soong.RegisterModuleType("art_cc_binary", artBinary)
 	soong.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
 	soong.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
 }
@@ -164,6 +208,16 @@
 	c := &codegenCustomizer{}
 	android.AddCustomizer(library, c)
 	props = append(props, &c.codegenProperties)
+
+	return module, props
+}
+
+func artBinary() (blueprint.Module, []interface{}) {
+	binary, _ := cc.NewBinary(android.HostAndDeviceSupported)
+	module, props := binary.Init()
+
+	android.AddCustomizer(binary, &artCustomLinkerCustomizer{})
+	android.AddCustomizer(binary, &artPrefer32BitCustomizer{})
 	return module, props
 }
 
@@ -178,3 +232,7 @@
 func envTrue(ctx android.BaseContext, key string) bool {
 	return ctx.AConfig().Getenv(key) == "true"
 }
+
+func envFalse(ctx android.BaseContext, key string) bool {
+	return ctx.AConfig().Getenv(key) == "false"
+}
diff --git a/build/codegen.go b/build/codegen.go
index eb2c37d..fde9420 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -112,7 +112,15 @@
 func defaultDeviceCodegenArches(ctx android.CustomizePropertiesContext) []string {
 	arches := make(map[string]bool)
 	for _, a := range ctx.DeviceConfig().Arches() {
-		arches[a.ArchType.String()] = true
+		s := a.ArchType.String()
+		arches[s] = true
+		if s == "arm64" {
+			arches["arm"] = true
+		} else if s == "mips64" {
+			arches["mips"] = true
+		} else if s == "x86_64" {
+			arches["x86"] = true
+		}
 	}
 	ret := make([]string, 0, len(arches))
 	for a := range arches {
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 0143268..595a824 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -188,6 +188,7 @@
         "liblzma",
     ],
     include_dirs: ["art/disassembler"],
+    export_include_dirs: ["."],
 }
 
 gensrcs {
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 9e5fb83..ed42958 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -39,8 +39,9 @@
   };
 
   enum JniOptimizationFlags {
-    kNone,
-    kFastNative,
+    kNone                       = 0x0,
+    kFastNative                 = 0x1,
+    kCriticalNative             = 0x2,
   };
 
   static Compiler* Create(CompilerDriver* driver, Kind kind);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index b5bc2fb..daac7fb 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -616,17 +616,22 @@
               /* referrer */ nullptr,
               invoke_type);
 
-      bool fast_native = false;
-      if (LIKELY(method != nullptr)) {
-        fast_native = method->IsAnnotatedWithFastNative();
-      } else {
+      // Query any JNI optimization annotations such as @FastNative or @CriticalNative.
+      Compiler::JniOptimizationFlags optimization_flags = Compiler::kNone;
+      if (UNLIKELY(method == nullptr)) {
         // Failed method resolutions happen very rarely, e.g. ancestor class cannot be resolved.
         DCHECK(self->IsExceptionPending());
         self->ClearException();
+      } else if (method->IsAnnotatedWithFastNative()) {
+        // TODO: Will no longer need this CHECK once we have verifier checking this.
+        CHECK(!method->IsAnnotatedWithCriticalNative());
+        optimization_flags = Compiler::kFastNative;
+      } else if (method->IsAnnotatedWithCriticalNative()) {
+        // TODO: Will no longer need this CHECK once we have verifier checking this.
+        CHECK(!method->IsAnnotatedWithFastNative());
+        optimization_flags = Compiler::kCriticalNative;
       }
 
-      Compiler::JniOptimizationFlags optimization_flags =
-          fast_native ? Compiler::kFastNative : Compiler::kNone;
       compiled_method = driver->GetCompiler()->JniCompile(access_flags,
                                                           method_idx,
                                                           dex_file,
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index e1ee0d2..a18935f 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -73,10 +73,12 @@
   CHECK_EQ(0, mkdir_result) << image_dir;
   ScratchFile image_file(OS::CreateEmptyFile(image_filename.c_str()));
 
-  std::string oat_filename(image_filename, 0, image_filename.size() - 3);
-  oat_filename += "oat";
+  std::string oat_filename = ReplaceFileExtension(image_filename, "oat");
   ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
 
+  std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex");
+  ScratchFile vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
+
   const uintptr_t requested_image_base = ART_BASE_ADDRESS;
   std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map;
   std::vector<const char*> oat_filename_vector(1, oat_filename.c_str());
@@ -109,7 +111,7 @@
           oat_file.GetFile());
       elf_writer->Start();
       OatWriter oat_writer(/*compiling_boot_image*/true, &timings);
-      OutputStream* rodata = elf_writer->StartRoData();
+      OutputStream* oat_rodata = elf_writer->StartRoData();
       for (const DexFile* dex_file : dex_files) {
         ArrayRef<const uint8_t> raw_dex_file(
             reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()),
@@ -120,16 +122,18 @@
       }
       std::unique_ptr<MemMap> opened_dex_files_map;
       std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-      bool dex_files_ok = oat_writer.WriteAndOpenDexFiles(
-          rodata,
-          oat_file.GetFile(),
-          compiler_driver_->GetInstructionSet(),
-          compiler_driver_->GetInstructionSetFeatures(),
-          &key_value_store,
-          /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
-          &opened_dex_files_map,
-          &opened_dex_files);
-      ASSERT_TRUE(dex_files_ok);
+      {
+        bool dex_files_ok = oat_writer.WriteAndOpenDexFiles(
+            kIsVdexEnabled ? vdex_file.GetFile() : oat_file.GetFile(),
+            oat_rodata,
+            compiler_driver_->GetInstructionSet(),
+            compiler_driver_->GetInstructionSetFeatures(),
+            &key_value_store,
+            /* verify */ false,           // Dex files may be dex-to-dex-ed, don't verify.
+            &opened_dex_files_map,
+            &opened_dex_files);
+        ASSERT_TRUE(dex_files_ok);
+      }
 
       bool image_space_ok = writer->PrepareImageAddressSpace();
       ASSERT_TRUE(image_space_ok);
@@ -138,17 +142,17 @@
                                               instruction_set_features_.get());
       oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files, &patcher);
       size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
-      size_t text_size = oat_writer.GetSize() - rodata_size;
+      size_t text_size = oat_writer.GetOatSize() - rodata_size;
       elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize());
 
       writer->UpdateOatFileLayout(/* oat_index */ 0u,
                                   elf_writer->GetLoadedSize(),
                                   oat_writer.GetOatDataOffset(),
-                                  oat_writer.GetSize());
+                                  oat_writer.GetOatSize());
 
-      bool rodata_ok = oat_writer.WriteRodata(rodata);
+      bool rodata_ok = oat_writer.WriteRodata(oat_rodata);
       ASSERT_TRUE(rodata_ok);
-      elf_writer->EndRoData(rodata);
+      elf_writer->EndRoData(oat_rodata);
 
       OutputStream* text = elf_writer->StartText();
       bool text_ok = oat_writer.WriteCode(text);
@@ -285,6 +289,7 @@
 
   image_file.Unlink();
   oat_file.Unlink();
+  vdex_file.Unlink();
   int rmdir_result = rmdir(image_dir.c_str());
   CHECK_EQ(0, rmdir_result);
 }
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 4b056f5..28b7290 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -64,7 +64,12 @@
     ArenaAllocator arena(&pool);
 
     std::unique_ptr<JniCallingConvention> jni_conv(
-        JniCallingConvention::Create(&arena, is_static, is_synchronized, shorty, isa));
+        JniCallingConvention::Create(&arena,
+                                     is_static,
+                                     is_synchronized,
+                                     /*is_critical_native*/false,
+                                     shorty,
+                                     isa));
     std::unique_ptr<ManagedRuntimeCallingConvention> mr_conv(
         ManagedRuntimeCallingConvention::Create(&arena, is_static, is_synchronized, shorty, isa));
     const int frame_size(jni_conv->FrameSize());
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index b83985a..cdd4c68 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -15,12 +15,14 @@
  */
 
 #include <memory>
+#include <type_traits>
 
 #include <math.h>
 
 #include "art_method-inl.h"
 #include "class_linker.h"
 #include "common_compiler_test.h"
+#include "compiler.h"
 #include "dex_file.h"
 #include "gtest/gtest.h"
 #include "indirect_reference_table.h"
@@ -47,6 +49,171 @@
 
 namespace art {
 
+enum class JniKind {
+  kNormal   = Compiler::kNone,               // Regular kind of un-annotated natives.
+  kFast     = Compiler::kFastNative,         // Native method annotated with @FastNative.
+  kCritical = Compiler::kCriticalNative,     // Native method annotated with @CriticalNative.
+  kCount    = Compiler::kCriticalNative + 1  // How many different types of JNIs we can have.
+};
+
+// Used to initialize array sizes that want to have different state per current jni.
+static constexpr size_t kJniKindCount = static_cast<size_t>(JniKind::kCount);
+// Do not use directly, use the helpers instead.
+uint32_t gCurrentJni = static_cast<uint32_t>(JniKind::kNormal);
+
+// Is the current native method under test @CriticalNative?
+static bool IsCurrentJniCritical() {
+  return gCurrentJni == static_cast<uint32_t>(JniKind::kCritical);
+}
+
+// Is the current native method a plain-old non-annotated native?
+static bool IsCurrentJniNormal() {
+  return gCurrentJni == static_cast<uint32_t>(JniKind::kNormal);
+}
+
+// Signifify that a different kind of JNI is about to be tested.
+static void UpdateCurrentJni(JniKind kind) {
+  gCurrentJni = static_cast<uint32_t>(kind);
+}
+
+// (Match the name suffixes of native methods in MyClassNatives.java)
+static std::string CurrentJniStringSuffix() {
+  switch (gCurrentJni) {
+    case static_cast<uint32_t>(JniKind::kNormal): {
+      return "";
+    }
+    case static_cast<uint32_t>(JniKind::kFast): {
+      return "_Fast";
+    }
+    case static_cast<uint32_t>(JniKind::kCritical): {
+      return "_Critical";
+    }
+    default:
+      LOG(FATAL) << "Invalid current JNI value: " << gCurrentJni;
+      UNREACHABLE();
+  }
+}
+
+// Dummy values passed to our JNI handlers when we enter @CriticalNative.
+// Normally @CriticalNative calling convention strips out the "JNIEnv*, jclass" parameters.
+// However to avoid duplicating every single test method we have a templated handler
+// that inserts dummy parameters (0,1) to make it compatible with a regular JNI handler.
+static JNIEnv* const kCriticalDummyJniEnv = reinterpret_cast<JNIEnv*>(0xDEADFEAD);
+static jclass const kCriticalDummyJniClass = reinterpret_cast<jclass>(0xBEAFBEEF);
+
+// Type trait. Returns true if "T" is the same type as one of the types in Args...
+//
+// Logically equal to OR(std::same_type<T, U> for all U in Args).
+template <typename T, typename ... Args>
+struct is_any_of;
+
+template <typename T, typename U, typename ... Args>
+struct is_any_of<T, U, Args ...> {
+  using value_type = bool;
+  static constexpr const bool value = std::is_same<T, U>::value || is_any_of<T, Args ...>::value;
+};
+
+template <typename T, typename U>
+struct is_any_of<T, U> {
+  using value_type = bool;
+  static constexpr const bool value = std::is_same<T, U>::value;
+};
+
+// Type traits for JNI types.
+template <typename T>
+struct jni_type_traits {
+  // True if type T ends up holding an object reference. False otherwise.
+  // (Non-JNI types will also be false).
+  static constexpr const bool is_ref =
+      is_any_of<T, jclass, jobject, jstring, jobjectArray, jintArray,
+                jcharArray, jfloatArray, jshortArray, jdoubleArray, jlongArray>::value;
+};
+
+template <typename ... Args>
+struct count_refs_helper {
+  using value_type = size_t;
+  static constexpr const size_t value = 0;
+};
+
+template <typename Arg, typename ... Args>
+struct count_refs_helper<Arg, Args ...> {
+  using value_type = size_t;
+  static constexpr size_t value =
+      (jni_type_traits<Arg>::is_ref ? 1 : 0) + count_refs_helper<Args ...>::value;
+};
+
+template <typename T, T fn>
+struct count_refs_fn_helper;
+
+template <typename R, typename ... Args, R fn(Args...)>
+struct count_refs_fn_helper<R(Args...), fn> : public count_refs_helper<Args...> {};
+
+// Given a function type 'T' figure out how many of the parameter types are a reference.
+// -- The implicit jclass and thisObject also count as 1 reference.
+//
+// Fields:
+// * value - the result counting # of refs
+// * value_type - the type of value (size_t)
+template <typename T, T fn>
+struct count_refs : public count_refs_fn_helper<T, fn> {};
+
+// Base case: No parameters = 0 refs.
+size_t count_nonnull_refs_helper() {
+  return 0;
+}
+
+// SFINAE for ref types. 1 if non-null, 0 otherwise.
+template <typename T>
+size_t count_nonnull_refs_single_helper(T arg,
+                                        typename std::enable_if<jni_type_traits<T>::is_ref>::type*
+                                            = nullptr) {
+  return ((arg == NULL) ? 0 : 1);
+}
+
+// SFINAE for non-ref-types. Always 0.
+template <typename T>
+size_t count_nonnull_refs_single_helper(T arg ATTRIBUTE_UNUSED,
+                                        typename std::enable_if<!jni_type_traits<T>::is_ref>::type*
+                                            = nullptr) {
+  return 0;
+}
+
+// Recursive case.
+template <typename T, typename ... Args>
+size_t count_nonnull_refs_helper(T arg, Args ... args) {
+  return count_nonnull_refs_single_helper(arg) + count_nonnull_refs_helper(args...);
+}
+
+// Given any list of parameters, check how many object refs there are and only count
+// them if their runtime value is non-null.
+//
+// For example given (jobject, jint, jclass) we can get (2) if both #0/#2 are non-null,
+// (1) if either #0/#2 are null but not both, and (0) if all parameters are null.
+// Primitive parameters (including JNIEnv*, if present) are ignored.
+template <typename ... Args>
+size_t count_nonnull_refs(Args ... args) {
+  return count_nonnull_refs_helper(args...);
+}
+
+template <typename T, T fn>
+struct remove_extra_parameters_helper;
+
+template <typename R, typename Arg1, typename Arg2, typename ... Args, R fn(Arg1, Arg2, Args...)>
+struct remove_extra_parameters_helper<R(Arg1, Arg2, Args...), fn> {
+  // Note: Do not use Args&& here to maintain C-style parameter types.
+  static R apply(Args... args) {
+    JNIEnv* env = kCriticalDummyJniEnv;
+    jclass kls = kCriticalDummyJniClass;
+    return fn(env, kls, args...);
+  }
+};
+
+// Given a function 'fn' create a function 'apply' which will omit the JNIEnv/jklass parameters
+//
+// i.e. if fn(JNIEnv*,jklass,a,b,c,d,e...) then apply(a,b,c,d,e,...)
+template <typename T, T fn>
+struct jni_remove_extra_parameters : public remove_extra_parameters_helper<T, fn> {};
+
 class JniCompilerTest : public CommonCompilerTest {
  protected:
   void SetUp() OVERRIDE {
@@ -63,8 +230,11 @@
     check_generic_jni_ = generic;
   }
 
-  void CompileForTest(jobject class_loader, bool direct,
-                      const char* method_name, const char* method_sig) {
+ private:
+  void CompileForTest(jobject class_loader,
+                      bool direct,
+                      const char* method_name,
+                      const char* method_sig) {
     ScopedObjectAccess soa(Thread::Current());
     StackHandleScope<1> hs(soa.Self());
     Handle<mirror::ClassLoader> loader(
@@ -87,8 +257,28 @@
     }
   }
 
-  void SetUpForTest(bool direct, const char* method_name, const char* method_sig,
+ protected:
+  void CompileForTestWithCurrentJni(jobject class_loader,
+                                    bool direct,
+                                    const char* method_name_orig,
+                                    const char* method_sig) {
+    // Append the JNI kind to the method name, so that we automatically get the
+    // fast or critical versions of the same method.
+    std::string method_name_str = std::string(method_name_orig) + CurrentJniStringSuffix();
+    const char* method_name = method_name_str.c_str();
+
+    CompileForTest(class_loader, direct, method_name, method_sig);
+  }
+
+  void SetUpForTest(bool direct,
+                    const char* method_name_orig,
+                    const char* method_sig,
                     void* native_fnptr) {
+    // Append the JNI kind to the method name, so that we automatically get the
+    // fast or critical versions of the same method.
+    std::string method_name_str = std::string(method_name_orig) + CurrentJniStringSuffix();
+    const char* method_name = method_name_str.c_str();
+
     // Initialize class loader and compile method when runtime not started.
     if (!runtime_->IsStarted()) {
       {
@@ -129,6 +319,7 @@
   }
 
  public:
+  // Available as statics so our JNI handlers can access these.
   static jclass jklass_;
   static jobject jobj_;
   static jobject class_loader_;
@@ -151,6 +342,8 @@
   void RunStaticReturnTrueImpl();
   void RunStaticReturnFalseImpl();
   void RunGenericStaticReturnIntImpl();
+  void RunGenericStaticReturnDoubleImpl();
+  void RunGenericStaticReturnLongImpl();
   void CompileAndRunStaticIntObjectObjectMethodImpl();
   void CompileAndRunStaticSynchronizedIntObjectObjectMethodImpl();
   void ExceptionHandlingImpl();
@@ -177,10 +370,13 @@
 
   void NormalNativeImpl();
   void FastNativeImpl();
+  void CriticalNativeImpl();
 
   JNIEnv* env_;
   jstring library_search_path_;
   jmethodID jmethod_;
+
+ private:
   bool check_generic_jni_;
 };
 
@@ -188,46 +384,238 @@
 jobject JniCompilerTest::jobj_;
 jobject JniCompilerTest::class_loader_;
 
-#define JNI_TEST(TestName) \
+// Test the normal compiler and normal generic JNI only.
+// The following features are unsupported in @FastNative:
+// 1) JNI stubs (lookup via dlsym) when methods aren't explicitly registered
+// 2) Returning objects from the JNI function
+// 3) synchronized keyword
+// -- TODO: We can support (1) if we remove the mutator lock assert during stub lookup.
+# define JNI_TEST_NORMAL_ONLY(TestName)          \
   TEST_F(JniCompilerTest, TestName ## Default) { \
+    SCOPED_TRACE("Normal JNI with compiler");    \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
     TestName ## Impl();                          \
   }                                              \
-                                                 \
   TEST_F(JniCompilerTest, TestName ## Generic) { \
+    SCOPED_TRACE("Normal JNI with generic");     \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
     TEST_DISABLED_FOR_MIPS();                    \
     SetCheckGenericJni(true);                    \
     TestName ## Impl();                          \
   }
 
-int gJava_MyClassNatives_foo_calls = 0;
-void Java_MyClassNatives_foo(JNIEnv* env, jobject thisObj) {
-  // 1 = thisObj
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  Locks::mutator_lock_->AssertNotHeld(Thread::Current());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_foo_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+// Test normal compiler, @FastNative compiler, and normal/@FastNative generic for normal natives.
+#define JNI_TEST(TestName) \
+  JNI_TEST_NORMAL_ONLY(TestName)                 \
+  TEST_F(JniCompilerTest, TestName ## Fast) {    \
+    SCOPED_TRACE("@FastNative JNI with compiler");  \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
+    TestName ## Impl();                          \
+  }                                              \
+                                          \
+
+// TODO: maybe. @FastNative generic JNI support?
+#if 0
+  TEST_F(JniCompilerTest, TestName ## FastGeneric) { \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
+    TEST_DISABLED_FOR_MIPS();                    \
+    SetCheckGenericJni(true);                    \
+    TestName ## Impl();                          \
+  }
+#endif
+
+#define JNI_TEST_CRITICAL_ONLY(TestName) \
+  TEST_F(JniCompilerTest, TestName ## DefaultCritical) { \
+    SCOPED_TRACE("@CriticalNative JNI with compiler");  \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
+    TestName ## Impl();                          \
+  }
+
+// Test everything above and also the @CriticalNative compiler, and @CriticalNative generic JNI.
+#define JNI_TEST_CRITICAL(TestName)              \
+  JNI_TEST(TestName)                             \
+  JNI_TEST_CRITICAL_ONLY(TestName)               \
+
+// TODO: maybe, more likely since calling convention changed. @Criticalnative generic JNI support?
+#if 0
+  TEST_F(JniCompilerTest, TestName ## GenericCritical) { \
+    gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
+    TestName ## Impl();                          \
+  }
+#endif
+
+static void expectValidThreadState() {
+  // Normal JNI always transitions to "Native". Other JNIs stay in the "Runnable" state.
+  if (IsCurrentJniNormal()) {
+    EXPECT_EQ(kNative, Thread::Current()->GetState());
+  } else {
+    EXPECT_EQ(kRunnable, Thread::Current()->GetState());
+  }
+}
+
+#define EXPECT_THREAD_STATE_FOR_CURRENT_JNI() expectValidThreadState()
+
+static void expectValidMutatorLockHeld() {
+  if (IsCurrentJniNormal()) {
+    Locks::mutator_lock_->AssertNotHeld(Thread::Current());
+  } else {
+    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+  }
+}
+
+#define EXPECT_MUTATOR_LOCK_FOR_CURRENT_JNI() expectValidMutatorLockHeld()
+
+static void expectValidJniEnvAndObject(JNIEnv* env, jobject thisObj) {
+  if (!IsCurrentJniCritical()) {
+    EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+    ASSERT_TRUE(thisObj != nullptr);
+    EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  } else {
+    LOG(FATAL) << "Objects are not supported for @CriticalNative, why is this being tested?";
+    UNREACHABLE();
+  }
+}
+
+// Validates the JNIEnv to be the same as the current thread's JNIEnv, and makes sure
+// that the object here is an instance of the class we registered the method with.
+//
+// Hard-fails if this somehow gets invoked for @CriticalNative since objects are unsupported.
+#define EXPECT_JNI_ENV_AND_OBJECT_FOR_CURRENT_JNI(env, thisObj) \
+    expectValidJniEnvAndObject(env, thisObj)
+
+static void expectValidJniEnvAndClass(JNIEnv* env, jclass kls) {
+  if (!IsCurrentJniCritical()) {
+    EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+    ASSERT_TRUE(kls != nullptr);
+    EXPECT_TRUE(env->IsSameObject(static_cast<jobject>(JniCompilerTest::jklass_),
+                                  static_cast<jobject>(kls)));
+  } else {
+    // This is pretty much vacuously true but catch any testing setup mistakes.
+    EXPECT_EQ(env, kCriticalDummyJniEnv);
+    EXPECT_EQ(kls, kCriticalDummyJniClass);
+  }
+}
+
+// Validates the JNIEnv is the same as the current thread's JNIenv, and makes sure
+// that the jclass we got in the JNI handler is the same one as the class the method was looked
+// up for.
+//
+// (Checks are skipped for @CriticalNative since the two values are dummy).
+#define EXPECT_JNI_ENV_AND_CLASS_FOR_CURRENT_JNI(env, kls) expectValidJniEnvAndClass(env, kls)
+
+// Temporarily disable the EXPECT_NUM_STACK_REFERENCES check (for a single test).
+struct ScopedDisableCheckNumStackReferences {
+  ScopedDisableCheckNumStackReferences() {
+    sCheckNumStackReferences = false;
+  }
+
+  ~ScopedDisableCheckNumStackReferences() {
+    sCheckNumStackReferences = true;
+  }
+
+  static bool sCheckNumStackReferences;
+};
+
+bool ScopedDisableCheckNumStackReferences::sCheckNumStackReferences = true;
+
+static void expectNumStackReferences(size_t val1, size_t val2) {
+  // In rare cases when JNI functions call themselves recursively,
+  // disable this test because it will have a false negative.
+  if (!IsCurrentJniCritical() && ScopedDisableCheckNumStackReferences::sCheckNumStackReferences) {
+    /* @CriticalNative doesn't build a HandleScope, so this test is meaningless then. */
+    ScopedObjectAccess soa(Thread::Current());
+
+    size_t actual_num = Thread::Current()->NumStackReferences();
+    // XX: Not too sure what's going on.
+    // Sometimes null references get placed and sometimes they don't?
+    EXPECT_TRUE(val1 == actual_num || val2 == actual_num)
+      << "expected either " << val1 << " or " << val2
+      << " number of stack references, but got: " << actual_num;
+  }
+}
+
+#define EXPECT_NUM_STACK_REFERENCES(val1, val2) expectNumStackReferences(val1, val2)
+
+template <typename T, T fn>
+struct make_jni_test_decorator;
+
+// Decorator for "static" JNI callbacks.
+template <typename R, typename ... Args, R fn(JNIEnv*, jclass, Args...)>
+struct make_jni_test_decorator<R(JNIEnv*, jclass kls, Args...), fn> {
+  static R apply(JNIEnv* env, jclass kls, Args ... args) {
+    EXPECT_THREAD_STATE_FOR_CURRENT_JNI();
+    EXPECT_MUTATOR_LOCK_FOR_CURRENT_JNI();
+    EXPECT_JNI_ENV_AND_CLASS_FOR_CURRENT_JNI(env, kls);
+    // All incoming parameters + the jclass get put into the transition's StackHandleScope.
+    EXPECT_NUM_STACK_REFERENCES(count_nonnull_refs(kls, args...),
+                                (count_refs_helper<jclass, Args...>::value));
+
+    return fn(env, kls, args...);
+  }
+};
+
+// Decorator for instance JNI callbacks.
+template <typename R, typename ... Args, R fn(JNIEnv*, jobject, Args...)>
+struct make_jni_test_decorator<R(JNIEnv*, jobject, Args...), fn> {
+  static R apply(JNIEnv* env, jobject thisObj, Args ... args) {
+    EXPECT_THREAD_STATE_FOR_CURRENT_JNI();
+    EXPECT_MUTATOR_LOCK_FOR_CURRENT_JNI();
+    EXPECT_JNI_ENV_AND_OBJECT_FOR_CURRENT_JNI(env, thisObj);
+    // All incoming parameters + the implicit 'this' get put into the transition's StackHandleScope.
+    EXPECT_NUM_STACK_REFERENCES(count_nonnull_refs(thisObj, args...),
+                                (count_refs_helper<jobject, Args...>::value));
+
+    return fn(env, thisObj, args...);
+  }
+};
+
+// Decorate the regular JNI callee with the extra gtest checks.
+// This way we can have common test logic for everything generic like checking if a lock is held,
+// checking handle scope state, etc.
+#define MAKE_JNI_TEST_DECORATOR(fn) make_jni_test_decorator<decltype(fn), (fn)>::apply
+
+// Convert function f(JNIEnv*,jclass,a,b,c,d...) into f2(a,b,c,d...)
+// -- This way we don't have to write out each implementation twice for @CriticalNative.
+#define JNI_CRITICAL_WRAPPER(func) jni_remove_extra_parameters<decltype(func), (func)>::apply
+// Get a function pointer whose calling convention either matches a regular native
+// or a critical native depending on which kind of jni is currently under test.
+// -- This also has the benefit of genering a compile time error if the 'func' doesn't properly
+//    have JNIEnv and jclass parameters first.
+#define CURRENT_JNI_WRAPPER(func)                                                         \
+    (IsCurrentJniCritical()                                                               \
+         ? reinterpret_cast<void*>(&JNI_CRITICAL_WRAPPER(MAKE_JNI_TEST_DECORATOR(func)))  \
+         : reinterpret_cast<void*>(&MAKE_JNI_TEST_DECORATOR(func)))
+
+// Do the opposite of the above. Do *not* wrap the function, instead just cast it to a void*.
+// Only for "TEST_JNI_NORMAL_ONLY" configs, and it inserts a test assert to ensure this is the case.
+#define NORMAL_JNI_ONLY_NOWRAP(func) \
+    ({ ASSERT_TRUE(IsCurrentJniNormal()); reinterpret_cast<void*>(&(func)); })
+// Same as above, but with nullptr. When we want to test the stub functionality.
+#define NORMAL_JNI_ONLY_NULLPTR \
+    ({ ASSERT_TRUE(IsCurrentJniNormal()); nullptr; })
+
+
+int gJava_MyClassNatives_foo_calls[kJniKindCount] = {};
+void Java_MyClassNatives_foo(JNIEnv*, jobject) {
+  gJava_MyClassNatives_foo_calls[gCurrentJni]++;
 }
 
 void JniCompilerTest::CompileAndRunNoArgMethodImpl() {
-  SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
+  SetUpForTest(false, "foo", "()V", CURRENT_JNI_WRAPPER(Java_MyClassNatives_foo));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_foo_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_foo_calls[gCurrentJni]);
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
-  EXPECT_EQ(1, gJava_MyClassNatives_foo_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_foo_calls[gCurrentJni]);
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
-  EXPECT_EQ(2, gJava_MyClassNatives_foo_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_foo_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_foo_calls = 0;
+  gJava_MyClassNatives_foo_calls[gCurrentJni] = 0;
 }
 
 JNI_TEST(CompileAndRunNoArgMethod)
 
 void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() {
-  SetUpForTest(false, "bar", "(I)I", nullptr);
+  SetUpForTest(false, "bar", "(I)I", NORMAL_JNI_ONLY_NULLPTR);
   // calling through stub will link with &Java_MyClassNatives_bar
 
   std::string reason;
@@ -239,10 +627,11 @@
   EXPECT_EQ(25, result);
 }
 
-JNI_TEST(CompileAndRunIntMethodThroughStub)
+// TODO: Support @FastNative and @CriticalNative through stubs.
+JNI_TEST_NORMAL_ONLY(CompileAndRunIntMethodThroughStub)
 
 void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() {
-  SetUpForTest(true, "sbar", "(I)I", nullptr);
+  SetUpForTest(true, "sbar", "(I)I", NORMAL_JNI_ONLY_NULLPTR);
   // calling through stub will link with &Java_MyClassNatives_sbar
 
   std::string reason;
@@ -254,174 +643,131 @@
   EXPECT_EQ(43, result);
 }
 
-JNI_TEST(CompileAndRunStaticIntMethodThroughStub)
+// TODO: Support @FastNative and @CriticalNative through stubs.
+JNI_TEST_NORMAL_ONLY(CompileAndRunStaticIntMethodThroughStub)
 
-int gJava_MyClassNatives_fooI_calls = 0;
-jint Java_MyClassNatives_fooI(JNIEnv* env, jobject thisObj, jint x) {
-  // 1 = thisObj
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_fooI_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooI_calls[kJniKindCount] = {};
+jint Java_MyClassNatives_fooI(JNIEnv*, jobject, jint x) {
+  gJava_MyClassNatives_fooI_calls[gCurrentJni]++;
   return x;
 }
 
 void JniCompilerTest::CompileAndRunIntMethodImpl() {
   SetUpForTest(false, "fooI", "(I)I",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooI));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooI));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooI_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooI_calls[gCurrentJni]);
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
   EXPECT_EQ(42, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooI_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooI_calls[gCurrentJni]);
   result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFED00D);
   EXPECT_EQ(static_cast<jint>(0xCAFED00D), result);
-  EXPECT_EQ(2, gJava_MyClassNatives_fooI_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooI_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooI_calls = 0;
+  gJava_MyClassNatives_fooI_calls[gCurrentJni] = 0;
 }
 
 JNI_TEST(CompileAndRunIntMethod)
 
-int gJava_MyClassNatives_fooII_calls = 0;
-jint Java_MyClassNatives_fooII(JNIEnv* env, jobject thisObj, jint x, jint y) {
-  // 1 = thisObj
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_fooII_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooII_calls[kJniKindCount] = {};
+jint Java_MyClassNatives_fooII(JNIEnv*, jobject, jint x, jint y) {
+  gJava_MyClassNatives_fooII_calls[gCurrentJni]++;
   return x - y;  // non-commutative operator
 }
 
 void JniCompilerTest::CompileAndRunIntIntMethodImpl() {
   SetUpForTest(false, "fooII", "(II)I",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooII));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooII));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooII_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooII_calls[gCurrentJni]);
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 99, 10);
   EXPECT_EQ(99 - 10, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooII_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooII_calls[gCurrentJni]);
   result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFEBABE,
                                          0xCAFED00D);
   EXPECT_EQ(static_cast<jint>(0xCAFEBABE - 0xCAFED00D), result);
-  EXPECT_EQ(2, gJava_MyClassNatives_fooII_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooII_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooII_calls = 0;
+  gJava_MyClassNatives_fooII_calls[gCurrentJni] = 0;
 }
 
 JNI_TEST(CompileAndRunIntIntMethod)
 
-int gJava_MyClassNatives_fooJJ_calls = 0;
-jlong Java_MyClassNatives_fooJJ(JNIEnv* env, jobject thisObj, jlong x, jlong y) {
-  // 1 = thisObj
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_fooJJ_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooJJ_calls[kJniKindCount] = {};
+jlong Java_MyClassNatives_fooJJ(JNIEnv*, jobject, jlong x, jlong y) {
+  gJava_MyClassNatives_fooJJ_calls[gCurrentJni]++;
   return x - y;  // non-commutative operator
 }
 
 void JniCompilerTest::CompileAndRunLongLongMethodImpl() {
   SetUpForTest(false, "fooJJ", "(JJ)J",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooJJ));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooJJ_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooJJ_calls[gCurrentJni]);
   jlong a = INT64_C(0x1234567890ABCDEF);
   jlong b = INT64_C(0xFEDCBA0987654321);
   jlong result = env_->CallNonvirtualLongMethod(jobj_, jklass_, jmethod_, a, b);
   EXPECT_EQ(a - b, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooJJ_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooJJ_calls[gCurrentJni]);
   result = env_->CallNonvirtualLongMethod(jobj_, jklass_, jmethod_, b, a);
   EXPECT_EQ(b - a, result);
-  EXPECT_EQ(2, gJava_MyClassNatives_fooJJ_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooJJ_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooJJ_calls = 0;
+  gJava_MyClassNatives_fooJJ_calls[gCurrentJni] = 0;
 }
 
 JNI_TEST(CompileAndRunLongLongMethod)
 
-int gJava_MyClassNatives_fooDD_calls = 0;
-jdouble Java_MyClassNatives_fooDD(JNIEnv* env, jobject thisObj, jdouble x, jdouble y) {
-  // 1 = thisObj
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_fooDD_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooDD_calls[kJniKindCount] = {};
+jdouble Java_MyClassNatives_fooDD(JNIEnv*, jobject, jdouble x, jdouble y) {
+  gJava_MyClassNatives_fooDD_calls[gCurrentJni]++;
   return x - y;  // non-commutative operator
 }
 
 void JniCompilerTest::CompileAndRunDoubleDoubleMethodImpl() {
   SetUpForTest(false, "fooDD", "(DD)D",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooDD));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooDD));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooDD_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooDD_calls[gCurrentJni]);
   jdouble result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_,
                                                     99.0, 10.0);
   EXPECT_DOUBLE_EQ(99.0 - 10.0, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooDD_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooDD_calls[gCurrentJni]);
   jdouble a = 3.14159265358979323846;
   jdouble b = 0.69314718055994530942;
   result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_, a, b);
   EXPECT_DOUBLE_EQ(a - b, result);
-  EXPECT_EQ(2, gJava_MyClassNatives_fooDD_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooDD_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooDD_calls = 0;
+  gJava_MyClassNatives_fooDD_calls[gCurrentJni] = 0;
 }
 
-int gJava_MyClassNatives_fooJJ_synchronized_calls = 0;
-jlong Java_MyClassNatives_fooJJ_synchronized(JNIEnv* env, jobject thisObj, jlong x, jlong y) {
-  // 1 = thisObj
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_fooJJ_synchronized_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooJJ_synchronized_calls[kJniKindCount] = {};
+jlong Java_MyClassNatives_fooJJ_synchronized(JNIEnv*, jobject, jlong x, jlong y) {
+  gJava_MyClassNatives_fooJJ_synchronized_calls[gCurrentJni]++;
   return x | y;
 }
 
 void JniCompilerTest::CompileAndRun_fooJJ_synchronizedImpl() {
   SetUpForTest(false, "fooJJ_synchronized", "(JJ)J",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ_synchronized));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooJJ_synchronized));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooJJ_synchronized_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooJJ_synchronized_calls[gCurrentJni]);
   jlong a = 0x1000000020000000ULL;
   jlong b = 0x00ff000000aa0000ULL;
   jlong result = env_->CallNonvirtualLongMethod(jobj_, jklass_, jmethod_, a, b);
   EXPECT_EQ(a | b, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooJJ_synchronized_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooJJ_synchronized_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooJJ_synchronized_calls = 0;
+  gJava_MyClassNatives_fooJJ_synchronized_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(CompileAndRun_fooJJ_synchronized)
+JNI_TEST_NORMAL_ONLY(CompileAndRun_fooJJ_synchronized)
 
-int gJava_MyClassNatives_fooIOO_calls = 0;
-jobject Java_MyClassNatives_fooIOO(JNIEnv* env, jobject thisObj, jint x, jobject y,
+int gJava_MyClassNatives_fooIOO_calls[kJniKindCount] = {};
+jobject Java_MyClassNatives_fooIOO(JNIEnv*, jobject thisObj, jint x, jobject y,
                             jobject z) {
-  // 3 = this + y + z
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  gJava_MyClassNatives_fooIOO_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  size_t null_args = (y == nullptr ? 1 : 0) + (z == nullptr ? 1 : 0);
-  EXPECT_TRUE(3U == Thread::Current()->NumStackReferences() ||
-              (3U - null_args) == Thread::Current()->NumStackReferences());
+  gJava_MyClassNatives_fooIOO_calls[gCurrentJni]++;
   switch (x) {
     case 1:
       return y;
@@ -435,96 +781,89 @@
 void JniCompilerTest::CompileAndRunIntObjectObjectMethodImpl() {
   SetUpForTest(false, "fooIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooIOO));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooIOO));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
   jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, nullptr, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(1, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
 
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, nullptr, jklass_);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(2, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 1, nullptr, jklass_);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
-  EXPECT_EQ(3, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(3, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, nullptr, jklass_);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(4, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(4, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
 
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, jklass_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(5, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(5, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 1, jklass_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(6, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(6, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
   result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, jklass_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
-  EXPECT_EQ(7, gJava_MyClassNatives_fooIOO_calls);
+  EXPECT_EQ(7, gJava_MyClassNatives_fooIOO_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooIOO_calls = 0;
+  gJava_MyClassNatives_fooIOO_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(CompileAndRunIntObjectObjectMethod)
+// TODO: Maybe. @FastNative support for returning Objects?
+JNI_TEST_NORMAL_ONLY(CompileAndRunIntObjectObjectMethod)
 
-int gJava_MyClassNatives_fooSII_calls = 0;
-jint Java_MyClassNatives_fooSII(JNIEnv* env, jclass klass, jint x, jint y) {
-  // 1 = klass
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(klass != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
-  gJava_MyClassNatives_fooSII_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooSII_calls[kJniKindCount] = {};
+jint Java_MyClassNatives_fooSII(JNIEnv* env ATTRIBUTE_UNUSED,
+                                jclass klass ATTRIBUTE_UNUSED,
+                                jint x,
+                                jint y) {
+  gJava_MyClassNatives_fooSII_calls[gCurrentJni]++;
   return x + y;
 }
 
 void JniCompilerTest::CompileAndRunStaticIntIntMethodImpl() {
   SetUpForTest(true, "fooSII", "(II)I",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooSII));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooSII));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooSII_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooSII_calls[gCurrentJni]);
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 20, 30);
   EXPECT_EQ(50, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooSII_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooSII_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooSII_calls = 0;
+  gJava_MyClassNatives_fooSII_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(CompileAndRunStaticIntIntMethod)
+JNI_TEST_CRITICAL(CompileAndRunStaticIntIntMethod)
 
-int gJava_MyClassNatives_fooSDD_calls = 0;
-jdouble Java_MyClassNatives_fooSDD(JNIEnv* env, jclass klass, jdouble x, jdouble y) {
-  // 1 = klass
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(klass != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
-  gJava_MyClassNatives_fooSDD_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooSDD_calls[kJniKindCount] = {};
+jdouble Java_MyClassNatives_fooSDD(JNIEnv* env ATTRIBUTE_UNUSED,
+                                   jclass klass ATTRIBUTE_UNUSED,
+                                   jdouble x,
+                                   jdouble y) {
+  gJava_MyClassNatives_fooSDD_calls[gCurrentJni]++;
   return x - y;  // non-commutative operator
 }
 
 void JniCompilerTest::CompileAndRunStaticDoubleDoubleMethodImpl() {
   SetUpForTest(true, "fooSDD", "(DD)D",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooSDD));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooSDD));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooSDD_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooSDD_calls[gCurrentJni]);
   jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 99.0, 10.0);
   EXPECT_DOUBLE_EQ(99.0 - 10.0, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_fooSDD_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooSDD_calls[gCurrentJni]);
   jdouble a = 3.14159265358979323846;
   jdouble b = 0.69314718055994530942;
   result = env_->CallStaticDoubleMethod(jklass_, jmethod_, a, b);
   EXPECT_DOUBLE_EQ(a - b, result);
-  EXPECT_DOUBLE_EQ(2, gJava_MyClassNatives_fooSDD_calls);
+  EXPECT_DOUBLE_EQ(2, gJava_MyClassNatives_fooSDD_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooSDD_calls = 0;
+  gJava_MyClassNatives_fooSDD_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(CompileAndRunStaticDoubleDoubleMethod)
+JNI_TEST_CRITICAL(CompileAndRunStaticDoubleDoubleMethod)
 
 // The x86 generic JNI code had a bug where it assumed a floating
 // point return value would be in xmm0. We use log, to somehow ensure
@@ -534,27 +873,47 @@
   return log(x);
 }
 
+jdouble Java_MyClassNatives_logD_notNormal(JNIEnv*, jclass, jdouble x) {
+  EXPECT_DOUBLE_EQ(2.0, x);
+  return log(x);
+}
+
 void JniCompilerTest::RunStaticLogDoubleMethodImpl() {
-  SetUpForTest(true, "logD", "(D)D", reinterpret_cast<void*>(&Java_MyClassNatives_logD));
+  void* jni_handler;
+  if (IsCurrentJniNormal()) {
+    // This test seems a bit special, don't use a JNI wrapper here.
+    jni_handler = NORMAL_JNI_ONLY_NOWRAP(Java_MyClassNatives_logD);
+  } else {
+    jni_handler = CURRENT_JNI_WRAPPER(Java_MyClassNatives_logD_notNormal);
+  }
+  SetUpForTest(true, "logD", "(D)D", jni_handler);
 
   jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0);
   EXPECT_DOUBLE_EQ(log(2.0), result);
 }
 
-JNI_TEST(RunStaticLogDoubleMethod)
+JNI_TEST_CRITICAL(RunStaticLogDoubleMethod)
 
 jfloat Java_MyClassNatives_logF(JNIEnv*, jclass, jfloat x) {
   return logf(x);
 }
 
 void JniCompilerTest::RunStaticLogFloatMethodImpl() {
-  SetUpForTest(true, "logF", "(F)F", reinterpret_cast<void*>(&Java_MyClassNatives_logF));
+  void* jni_handler;
+  if (IsCurrentJniNormal()) {
+    // This test seems a bit special, don't use a JNI wrapper here.
+    jni_handler = NORMAL_JNI_ONLY_NOWRAP(Java_MyClassNatives_logF);
+  } else {
+    jni_handler = CURRENT_JNI_WRAPPER(Java_MyClassNatives_logF);
+  }
+
+  SetUpForTest(true, "logF", "(F)F", jni_handler);
 
   jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0);
   EXPECT_FLOAT_EQ(logf(2.0), result);
 }
 
-JNI_TEST(RunStaticLogFloatMethod)
+JNI_TEST_CRITICAL(RunStaticLogFloatMethod)
 
 jboolean Java_MyClassNatives_returnTrue(JNIEnv*, jclass) {
   return JNI_TRUE;
@@ -569,46 +928,67 @@
 }
 
 void JniCompilerTest::RunStaticReturnTrueImpl() {
-  SetUpForTest(true, "returnTrue", "()Z", reinterpret_cast<void*>(&Java_MyClassNatives_returnTrue));
+  SetUpForTest(true, "returnTrue", "()Z", CURRENT_JNI_WRAPPER(Java_MyClassNatives_returnTrue));
 
   jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_);
   EXPECT_TRUE(result);
 }
 
-JNI_TEST(RunStaticReturnTrue)
+JNI_TEST_CRITICAL(RunStaticReturnTrue)
 
 void JniCompilerTest::RunStaticReturnFalseImpl() {
   SetUpForTest(true, "returnFalse", "()Z",
-               reinterpret_cast<void*>(&Java_MyClassNatives_returnFalse));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_returnFalse));
 
   jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_);
   EXPECT_FALSE(result);
 }
 
-JNI_TEST(RunStaticReturnFalse)
+JNI_TEST_CRITICAL(RunStaticReturnFalse)
 
 void JniCompilerTest::RunGenericStaticReturnIntImpl() {
-  SetUpForTest(true, "returnInt", "()I", reinterpret_cast<void*>(&Java_MyClassNatives_returnInt));
+  SetUpForTest(true, "returnInt", "()I", CURRENT_JNI_WRAPPER(Java_MyClassNatives_returnInt));
 
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_);
   EXPECT_EQ(42, result);
 }
 
-JNI_TEST(RunGenericStaticReturnInt)
+JNI_TEST_CRITICAL(RunGenericStaticReturnInt)
 
-int gJava_MyClassNatives_fooSIOO_calls = 0;
-jobject Java_MyClassNatives_fooSIOO(JNIEnv* env, jclass klass, jint x, jobject y,
-                             jobject z) {
-  // 3 = klass + y + z
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(klass != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
-  gJava_MyClassNatives_fooSIOO_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  size_t null_args = (y == nullptr ? 1 : 0) + (z == nullptr ? 1 : 0);
-  EXPECT_TRUE(3U == Thread::Current()->NumStackReferences() ||
-              (3U - null_args) == Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_returnDouble_calls[kJniKindCount] = {};
+jdouble Java_MyClassNatives_returnDouble(JNIEnv*, jclass) {
+  gJava_MyClassNatives_returnDouble_calls[gCurrentJni]++;
+  return 4.0;
+}
+
+void JniCompilerTest::RunGenericStaticReturnDoubleImpl() {
+  SetUpForTest(true, "returnDouble", "()D", CURRENT_JNI_WRAPPER(Java_MyClassNatives_returnDouble));
+
+  jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_);
+  EXPECT_DOUBLE_EQ(4.0, result);
+  EXPECT_EQ(1, gJava_MyClassNatives_returnDouble_calls[gCurrentJni]);
+
+  gJava_MyClassNatives_returnDouble_calls[gCurrentJni] = 0;
+}
+
+JNI_TEST_CRITICAL(RunGenericStaticReturnDouble)
+
+jlong Java_MyClassNatives_returnLong(JNIEnv*, jclass) {
+  return 0xFEEDDEADFEEDL;
+}
+
+void JniCompilerTest::RunGenericStaticReturnLongImpl() {
+  SetUpForTest(true, "returnLong", "()J", CURRENT_JNI_WRAPPER(Java_MyClassNatives_returnLong));
+
+  jlong result = env_->CallStaticLongMethod(jklass_, jmethod_);
+  EXPECT_EQ(0xFEEDDEADFEEDL, result);
+}
+
+JNI_TEST_CRITICAL(RunGenericStaticReturnLong)
+
+int gJava_MyClassNatives_fooSIOO_calls[kJniKindCount] = {};
+jobject Java_MyClassNatives_fooSIOO(JNIEnv*, jclass klass, jint x, jobject y, jobject z) {
+  gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]++;
   switch (x) {
     case 1:
       return y;
@@ -619,54 +999,45 @@
   }
 }
 
-
 void JniCompilerTest::CompileAndRunStaticIntObjectObjectMethodImpl() {
   SetUpForTest(true, "fooSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooSIOO));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooSIOO));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
   jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, nullptr, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(1, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
 
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, nullptr, jobj_);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(2, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, nullptr, jobj_);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
-  EXPECT_EQ(3, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(3, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, nullptr, jobj_);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(4, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(4, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
 
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(5, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(5, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(6, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(6, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
-  EXPECT_EQ(7, gJava_MyClassNatives_fooSIOO_calls);
+  EXPECT_EQ(7, gJava_MyClassNatives_fooSIOO_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooSIOO_calls = 0;
+  gJava_MyClassNatives_fooSIOO_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(CompileAndRunStaticIntObjectObjectMethod)
+// TODO: Maybe. @FastNative support for returning Objects?
+JNI_TEST_NORMAL_ONLY(CompileAndRunStaticIntObjectObjectMethod)
 
-int gJava_MyClassNatives_fooSSIOO_calls = 0;
-jobject Java_MyClassNatives_fooSSIOO(JNIEnv* env, jclass klass, jint x, jobject y, jobject z) {
-  // 3 = klass + y + z
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(klass != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
-  gJava_MyClassNatives_fooSSIOO_calls++;
-  ScopedObjectAccess soa(Thread::Current());
-  size_t null_args = (y == nullptr ? 1 : 0) + (z == nullptr ? 1 : 0);
-  EXPECT_TRUE(3U == Thread::Current()->NumStackReferences() ||
-              (3U - null_args) == Thread::Current()->NumStackReferences());
+int gJava_MyClassNatives_fooSSIOO_calls[kJniKindCount] = {};
+jobject Java_MyClassNatives_fooSSIOO(JNIEnv*, jclass klass, jint x, jobject y, jobject z) {
+  gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]++;
   switch (x) {
     case 1:
       return y;
@@ -680,37 +1051,38 @@
 void JniCompilerTest::CompileAndRunStaticSynchronizedIntObjectObjectMethodImpl() {
   SetUpForTest(true, "fooSSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooSSIOO));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooSSIOO));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
   jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, nullptr, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(1, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
 
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, nullptr, jobj_);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(2, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, nullptr, jobj_);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
-  EXPECT_EQ(3, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(3, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, nullptr, jobj_);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(4, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(4, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
 
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jklass_, result));
-  EXPECT_EQ(5, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(5, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(jobj_, result));
-  EXPECT_EQ(6, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(6, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
   result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, nullptr);
   EXPECT_TRUE(env_->IsSameObject(nullptr, result));
-  EXPECT_EQ(7, gJava_MyClassNatives_fooSSIOO_calls);
+  EXPECT_EQ(7, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_fooSSIOO_calls = 0;
+  gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(CompileAndRunStaticSynchronizedIntObjectObjectMethod)
+// TODO: Maybe. @FastNative support for returning Objects?
+JNI_TEST_NORMAL_ONLY(CompileAndRunStaticSynchronizedIntObjectObjectMethod)
 
 void Java_MyClassNatives_throwException(JNIEnv* env, jobject) {
   jclass c = env->FindClass("java/lang/RuntimeException");
@@ -724,30 +1096,30 @@
     class_loader_ = LoadDex("MyClassNatives");
 
     // all compilation needs to happen before Runtime::Start
-    CompileForTest(class_loader_, false, "foo", "()V");
-    CompileForTest(class_loader_, false, "throwException", "()V");
-    CompileForTest(class_loader_, false, "foo", "()V");
+    CompileForTestWithCurrentJni(class_loader_, false, "foo", "()V");
+    CompileForTestWithCurrentJni(class_loader_, false, "throwException", "()V");
+    CompileForTestWithCurrentJni(class_loader_, false, "foo", "()V");
   }
   // Start runtime to avoid re-initialization in SetupForTest.
   Thread::Current()->TransitionFromSuspendedToRunnable();
   bool started = runtime_->Start();
   CHECK(started);
 
-  gJava_MyClassNatives_foo_calls = 0;
+  gJava_MyClassNatives_foo_calls[gCurrentJni] = 0;
 
   // Check a single call of a JNI method is ok
-  SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
+  SetUpForTest(false, "foo", "()V", CURRENT_JNI_WRAPPER(Java_MyClassNatives_foo));
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
-  EXPECT_EQ(1, gJava_MyClassNatives_foo_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_foo_calls[gCurrentJni]);
   EXPECT_FALSE(Thread::Current()->IsExceptionPending());
 
   // Get class for exception we expect to be thrown
   ScopedLocalRef<jclass> jlre(env_, env_->FindClass("java/lang/RuntimeException"));
   SetUpForTest(false, "throwException", "()V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_throwException));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_throwException));
   // Call Java_MyClassNatives_throwException (JNI method that throws exception)
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
-  EXPECT_EQ(1, gJava_MyClassNatives_foo_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_foo_calls[gCurrentJni]);
   EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
   ScopedLocalRef<jthrowable> exception(env_, env_->ExceptionOccurred());
   env_->ExceptionClear();
@@ -756,9 +1128,9 @@
   // Check a single call of a JNI method is ok
   SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
-  EXPECT_EQ(2, gJava_MyClassNatives_foo_calls);
+  EXPECT_EQ(2, gJava_MyClassNatives_foo_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_foo_calls = 0;
+  gJava_MyClassNatives_foo_calls[gCurrentJni] = 0;
 }
 
 JNI_TEST(ExceptionHandling)
@@ -782,7 +1154,7 @@
       mirror::StackTraceElement* ste = trace_array->Get(j);
       EXPECT_STREQ("MyClassNatives.java", ste->GetFileName()->ToModifiedUtf8().c_str());
       EXPECT_STREQ("MyClassNatives", ste->GetDeclaringClass()->ToModifiedUtf8().c_str());
-      EXPECT_STREQ("fooI", ste->GetMethodName()->ToModifiedUtf8().c_str());
+      EXPECT_EQ(("fooI" + CurrentJniStringSuffix()), ste->GetMethodName()->ToModifiedUtf8());
     }
 
     // end recursion
@@ -790,7 +1162,9 @@
   } else {
     jclass jklass = env->FindClass("MyClassNatives");
     EXPECT_TRUE(jklass != nullptr);
-    jmethodID jmethod = env->GetMethodID(jklass, "fooI", "(I)I");
+    jmethodID jmethod = env->GetMethodID(jklass,
+                                         ("fooI" + CurrentJniStringSuffix()).c_str(),
+                                         "(I)I");
     EXPECT_TRUE(jmethod != nullptr);
 
     // Recurse with i - 1
@@ -803,8 +1177,13 @@
 
 void JniCompilerTest::NativeStackTraceElementImpl() {
   SetUpForTest(false, "fooI", "(I)I",
-               reinterpret_cast<void*>(&Java_MyClassNatives_nativeUpCall));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_nativeUpCall));
+
+  // Usual # local references on stack check fails because nativeUpCall calls itself recursively,
+  // each time the # of local references will therefore go up.
+  ScopedDisableCheckNumStackReferences disable_num_stack_check;
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 10);
+
   EXPECT_EQ(10+9+8+7+6+5+4+3+2+1, result);
 }
 
@@ -816,13 +1195,14 @@
 
 void JniCompilerTest::ReturnGlobalRefImpl() {
   SetUpForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fooO));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooO));
   jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, jobj_);
   EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(result));
   EXPECT_TRUE(env_->IsSameObject(result, jobj_));
 }
 
-JNI_TEST(ReturnGlobalRef)
+// TODO: Maybe. @FastNative support for returning objects?
+JNI_TEST_NORMAL_ONLY(ReturnGlobalRef)
 
 jint local_ref_test(JNIEnv* env, jobject thisObj, jint x) {
   // Add 10 local references
@@ -834,7 +1214,7 @@
 }
 
 void JniCompilerTest::LocalReferenceTableClearingTestImpl() {
-  SetUpForTest(false, "fooI", "(I)I", reinterpret_cast<void*>(&local_ref_test));
+  SetUpForTest(false, "fooI", "(I)I", CURRENT_JNI_WRAPPER(local_ref_test));
   // 1000 invocations of a method that adds 10 local references
   for (int i = 0; i < 1000; i++) {
     jint result = env_->CallIntMethod(jobj_, jmethod_, i);
@@ -855,7 +1235,7 @@
 
 void JniCompilerTest::JavaLangSystemArrayCopyImpl() {
   SetUpForTest(true, "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V",
-               reinterpret_cast<void*>(&my_arraycopy));
+               CURRENT_JNI_WRAPPER(my_arraycopy));
   env_->CallStaticVoidMethod(jklass_, jmethod_, jobj_, 1234, jklass_, 5678, 9876);
 }
 
@@ -872,7 +1252,7 @@
 
 void JniCompilerTest::CompareAndSwapIntImpl() {
   SetUpForTest(false, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
-               reinterpret_cast<void*>(&my_casi));
+               CURRENT_JNI_WRAPPER(my_casi));
   jboolean result = env_->CallBooleanMethod(jobj_, jmethod_, jobj_, INT64_C(0x12345678ABCDEF88),
                                             0xCAFEF00D, 0xEBADF00D);
   EXPECT_EQ(result, JNI_TRUE);
@@ -891,7 +1271,7 @@
 
 void JniCompilerTest::GetTextImpl() {
   SetUpForTest(true, "getText", "(JLjava/lang/Object;JLjava/lang/Object;)I",
-               reinterpret_cast<void*>(&my_gettext));
+               CURRENT_JNI_WRAPPER(my_gettext));
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 0x12345678ABCDEF88ll, jobj_,
                                           INT64_C(0x7FEDCBA987654321), jobj_);
   EXPECT_EQ(result, 42);
@@ -899,37 +1279,33 @@
 
 JNI_TEST(GetText)
 
-int gJava_MyClassNatives_GetSinkProperties_calls = 0;
-jarray Java_MyClassNatives_GetSinkProperties(JNIEnv* env, jobject thisObj, jstring s) {
-  // 1 = thisObj
-  Thread* self = Thread::Current();
-  EXPECT_EQ(kNative, self->GetState());
-  Locks::mutator_lock_->AssertNotHeld(self);
-  EXPECT_EQ(self->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+int gJava_MyClassNatives_GetSinkProperties_calls[kJniKindCount] = {};
+jarray Java_MyClassNatives_GetSinkProperties(JNIEnv*, jobject thisObj, jstring s) {
   EXPECT_EQ(s, nullptr);
-  gJava_MyClassNatives_GetSinkProperties_calls++;
+  gJava_MyClassNatives_GetSinkProperties_calls[gCurrentJni]++;
+
+  Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
-  EXPECT_EQ(2U, self->NumStackReferences());
   EXPECT_TRUE(self->HoldsLock(soa.Decode<mirror::Object*>(thisObj)));
   return nullptr;
 }
 
 void JniCompilerTest::GetSinkPropertiesNativeImpl() {
   SetUpForTest(false, "getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_GetSinkProperties));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_GetSinkProperties));
 
-  EXPECT_EQ(0, gJava_MyClassNatives_GetSinkProperties_calls);
+  EXPECT_EQ(0, gJava_MyClassNatives_GetSinkProperties_calls[gCurrentJni]);
   jarray result = down_cast<jarray>(
       env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, nullptr));
   EXPECT_EQ(nullptr, result);
-  EXPECT_EQ(1, gJava_MyClassNatives_GetSinkProperties_calls);
+  EXPECT_EQ(1, gJava_MyClassNatives_GetSinkProperties_calls[gCurrentJni]);
 
-  gJava_MyClassNatives_GetSinkProperties_calls = 0;
+  gJava_MyClassNatives_GetSinkProperties_calls[gCurrentJni] = 0;
 }
 
-JNI_TEST(GetSinkPropertiesNative)
+// @FastNative doesn't support 'synchronized' keyword and
+// never will -- locking functions aren't fast.
+JNI_TEST_NORMAL_ONLY(GetSinkPropertiesNative)
 
 // This should return jclass, but we're imitating a bug pattern.
 jobject Java_MyClassNatives_instanceMethodThatShouldReturnClass(JNIEnv* env, jobject) {
@@ -943,39 +1319,59 @@
 
 void JniCompilerTest::UpcallReturnTypeChecking_InstanceImpl() {
   SetUpForTest(false, "instanceMethodThatShouldReturnClass", "()Ljava/lang/Class;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldReturnClass));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_instanceMethodThatShouldReturnClass));
 
   CheckJniAbortCatcher check_jni_abort_catcher;
   // This native method is bad, and tries to return a jstring as a jclass.
   env_->CallObjectMethod(jobj_, jmethod_);
-  check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
+  check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " +
+                                    "of java.lang.String from java.lang.Class " +
+                                    "MyClassNatives.instanceMethodThatShouldReturnClass" +
+                                    CurrentJniStringSuffix() + "()");
 
   // Here, we just call the method incorrectly; we should catch that too.
   env_->CallObjectMethod(jobj_, jmethod_);
-  check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
+  check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " +
+                                    "of java.lang.String from java.lang.Class " +
+                                    "MyClassNatives.instanceMethodThatShouldReturnClass" +
+                                    CurrentJniStringSuffix() + "()");
   env_->CallStaticObjectMethod(jklass_, jmethod_);
-  check_jni_abort_catcher.Check("calling non-static method java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass() with CallStaticObjectMethodV");
+  check_jni_abort_catcher.Check(std::string() + "calling non-static method " +
+                                    "java.lang.Class " +
+                                    "MyClassNatives.instanceMethodThatShouldReturnClass" +
+                                    CurrentJniStringSuffix() + "() with CallStaticObjectMethodV");
 }
 
-JNI_TEST(UpcallReturnTypeChecking_Instance)
+// TODO: Maybe support returning objects for @FastNative?
+JNI_TEST_NORMAL_ONLY(UpcallReturnTypeChecking_Instance)
 
 void JniCompilerTest::UpcallReturnTypeChecking_StaticImpl() {
   SetUpForTest(true, "staticMethodThatShouldReturnClass", "()Ljava/lang/Class;",
-               reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldReturnClass));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_staticMethodThatShouldReturnClass));
 
   CheckJniAbortCatcher check_jni_abort_catcher;
   // This native method is bad, and tries to return a jstring as a jclass.
   env_->CallStaticObjectMethod(jklass_, jmethod_);
-  check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
+  check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " +
+                                    "of java.lang.String from java.lang.Class " +
+                                    "MyClassNatives.staticMethodThatShouldReturnClass" +
+                                    CurrentJniStringSuffix() + "()");
 
   // Here, we just call the method incorrectly; we should catch that too.
   env_->CallStaticObjectMethod(jklass_, jmethod_);
-  check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
+  check_jni_abort_catcher.Check(std::string() + "attempt to return an instance " +
+                                    "of java.lang.String from java.lang.Class " +
+                                    "MyClassNatives.staticMethodThatShouldReturnClass" +
+                                    CurrentJniStringSuffix() + "()");
   env_->CallObjectMethod(jobj_, jmethod_);
-  check_jni_abort_catcher.Check("calling static method java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass() with CallObjectMethodV");
+  check_jni_abort_catcher.Check(std::string() + "calling static method " +
+                                    "java.lang.Class " +
+                                    "MyClassNatives.staticMethodThatShouldReturnClass" +
+                                    CurrentJniStringSuffix() + "() with CallObjectMethodV");
 }
 
-JNI_TEST(UpcallReturnTypeChecking_Static)
+// TODO: Maybe support returning objects for @FastNative?
+JNI_TEST_NORMAL_ONLY(UpcallReturnTypeChecking_Static)
 
 // This should take jclass, but we're imitating a bug pattern.
 void Java_MyClassNatives_instanceMethodThatShouldTakeClass(JNIEnv*, jobject, jclass) {
@@ -990,12 +1386,14 @@
   ScopedLogSeverity sls(LogSeverity::FATAL);
 
   SetUpForTest(false, "instanceMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldTakeClass));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_instanceMethodThatShouldTakeClass));
 
   CheckJniAbortCatcher check_jni_abort_catcher;
   // We deliberately pass a bad second argument here.
   env_->CallVoidMethod(jobj_, jmethod_, 123, env_->NewStringUTF("not a class!"));
-  check_jni_abort_catcher.Check("bad arguments passed to void MyClassNatives.instanceMethodThatShouldTakeClass(int, java.lang.Class)");
+  check_jni_abort_catcher.Check(std::string() + "bad arguments passed to void " +
+                                    "MyClassNatives.instanceMethodThatShouldTakeClass" +
+                                    CurrentJniStringSuffix() + "(int, java.lang.Class)");
 }
 
 JNI_TEST(UpcallArgumentTypeChecking_Instance)
@@ -1005,29 +1403,25 @@
   ScopedLogSeverity sls(LogSeverity::FATAL);
 
   SetUpForTest(true, "staticMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldTakeClass));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_staticMethodThatShouldTakeClass));
 
   CheckJniAbortCatcher check_jni_abort_catcher;
   // We deliberately pass a bad second argument here.
   env_->CallStaticVoidMethod(jklass_, jmethod_, 123, env_->NewStringUTF("not a class!"));
-  check_jni_abort_catcher.Check("bad arguments passed to void MyClassNatives.staticMethodThatShouldTakeClass(int, java.lang.Class)");
+  check_jni_abort_catcher.Check(std::string() + "bad arguments passed to void " +
+                                    "MyClassNatives.staticMethodThatShouldTakeClass" +
+                                    CurrentJniStringSuffix() + "(int, java.lang.Class)");
 }
 
 JNI_TEST(UpcallArgumentTypeChecking_Static)
 
-jfloat Java_MyClassNatives_checkFloats(JNIEnv* env, jobject thisObj, jfloat f1, jfloat f2) {
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+jfloat Java_MyClassNatives_checkFloats(JNIEnv*, jobject, jfloat f1, jfloat f2) {
   return f1 - f2;  // non-commutative operator
 }
 
 void JniCompilerTest::CompileAndRunFloatFloatMethodImpl() {
   SetUpForTest(false, "checkFloats", "(FF)F",
-               reinterpret_cast<void*>(&Java_MyClassNatives_checkFloats));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_checkFloats));
 
   jfloat result = env_->CallNonvirtualFloatMethod(jobj_, jklass_, jmethod_,
                                                     99.0F, 10.0F);
@@ -1042,28 +1436,22 @@
 
 void Java_MyClassNatives_checkParameterAlign(JNIEnv* env ATTRIBUTE_UNUSED,
                                              jobject thisObj ATTRIBUTE_UNUSED,
-                                             jint i1 ATTRIBUTE_UNUSED,
-                                             jlong l1 ATTRIBUTE_UNUSED) {
-//  EXPECT_EQ(kNative, Thread::Current()->GetState());
-//  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-//  EXPECT_TRUE(thisObj != nullptr);
-//  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-//  ScopedObjectAccess soa(Thread::Current());
-//  EXPECT_EQ(1U, Thread::Current()->NumStackReferences());
+                                             jint i1,
+                                             jlong l1) {
   EXPECT_EQ(i1, 1234);
   EXPECT_EQ(l1, INT64_C(0x12345678ABCDEF0));
 }
 
 void JniCompilerTest::CheckParameterAlignImpl() {
   SetUpForTest(false, "checkParameterAlign", "(IJ)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_checkParameterAlign));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_checkParameterAlign));
 
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_, 1234, INT64_C(0x12345678ABCDEF0));
 }
 
 JNI_TEST(CheckParameterAlign)
 
-void Java_MyClassNatives_maxParamNumber(JNIEnv* env, jobject thisObj,
+void Java_MyClassNatives_maxParamNumber(JNIEnv* env, jobject,
     jobject o0, jobject o1, jobject o2, jobject o3, jobject o4, jobject o5, jobject o6, jobject o7,
     jobject o8, jobject o9, jobject o10, jobject o11, jobject o12, jobject o13, jobject o14, jobject o15,
     jobject o16, jobject o17, jobject o18, jobject o19, jobject o20, jobject o21, jobject o22, jobject o23,
@@ -1096,13 +1484,6 @@
     jobject o232, jobject o233, jobject o234, jobject o235, jobject o236, jobject o237, jobject o238, jobject o239,
     jobject o240, jobject o241, jobject o242, jobject o243, jobject o244, jobject o245, jobject o246, jobject o247,
     jobject o248, jobject o249, jobject o250, jobject o251, jobject o252, jobject o253) {
-  EXPECT_EQ(kNative, Thread::Current()->GetState());
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
-  EXPECT_TRUE(thisObj != nullptr);
-  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
-  ScopedObjectAccess soa(Thread::Current());
-  EXPECT_GE(255U, Thread::Current()->NumStackReferences());
-
   // two tests possible
   if (o0 == nullptr) {
     // 1) everything is null
@@ -1470,7 +1851,7 @@
 
 void JniCompilerTest::MaxParamNumberImpl() {
   SetUpForTest(false, "maxParamNumber", longSig,
-               reinterpret_cast<void*>(&Java_MyClassNatives_maxParamNumber));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_maxParamNumber));
 
   jvalue args[254];
 
@@ -1497,7 +1878,7 @@
   // This will lead to error messages in the log.
   ScopedLogSeverity sls(LogSeverity::FATAL);
 
-  SetUpForTest(false, "withoutImplementation", "()V", nullptr);
+  SetUpForTest(false, "withoutImplementation", "()V", NORMAL_JNI_ONLY_NULLPTR);
 
   env_->CallVoidMethod(jobj_, jmethod_);
 
@@ -1505,13 +1886,18 @@
   EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
 }
 
-JNI_TEST(WithoutImplementation)
+// TODO: Don't test @FastNative here since it goes through a stub lookup (unsupported) which would
+// normally fail with an exception, but fails with an assert.
+JNI_TEST_NORMAL_ONLY(WithoutImplementation)
 
 void JniCompilerTest::WithoutImplementationRefReturnImpl() {
   // This will lead to error messages in the log.
   ScopedLogSeverity sls(LogSeverity::FATAL);
 
-  SetUpForTest(false, "withoutImplementationRefReturn", "()Ljava/lang/Object;", nullptr);
+  SetUpForTest(false,
+               "withoutImplementationRefReturn",
+               "()Ljava/lang/Object;",
+               NORMAL_JNI_ONLY_NULLPTR);
 
   env_->CallObjectMethod(jobj_, jmethod_);
 
@@ -1519,7 +1905,8 @@
   EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
 }
 
-JNI_TEST(WithoutImplementationRefReturn)
+// TODO: Should work for @FastNative too.
+JNI_TEST_NORMAL_ONLY(WithoutImplementationRefReturn)
 
 void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv*, jclass, jint i1, jint i2, jint i3,
                                             jint i4, jint i5, jint i6, jint i7, jint i8, jint i9,
@@ -1561,7 +1948,7 @@
 
 void JniCompilerTest::StackArgsIntsFirstImpl() {
   SetUpForTest(true, "stackArgsIntsFirst", "(IIIIIIIIIIFFFFFFFFFF)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsIntsFirst));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_stackArgsIntsFirst));
 
   jint i1 = 1;
   jint i2 = 2;
@@ -1589,7 +1976,7 @@
                              f3, f4, f5, f6, f7, f8, f9, f10);
 }
 
-JNI_TEST(StackArgsIntsFirst)
+JNI_TEST_CRITICAL(StackArgsIntsFirst)
 
 void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv*, jclass, jfloat f1, jfloat f2,
                                               jfloat f3, jfloat f4, jfloat f5, jfloat f6, jfloat f7,
@@ -1631,7 +2018,7 @@
 
 void JniCompilerTest::StackArgsFloatsFirstImpl() {
   SetUpForTest(true, "stackArgsFloatsFirst", "(FFFFFFFFFFIIIIIIIIII)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsFloatsFirst));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_stackArgsFloatsFirst));
 
   jint i1 = 1;
   jint i2 = 2;
@@ -1659,7 +2046,7 @@
                              i4, i5, i6, i7, i8, i9, i10);
 }
 
-JNI_TEST(StackArgsFloatsFirst)
+JNI_TEST_CRITICAL(StackArgsFloatsFirst)
 
 void Java_MyClassNatives_stackArgsMixed(JNIEnv*, jclass, jint i1, jfloat f1, jint i2,
                                         jfloat f2, jint i3, jfloat f3, jint i4, jfloat f4, jint i5,
@@ -1700,7 +2087,7 @@
 
 void JniCompilerTest::StackArgsMixedImpl() {
   SetUpForTest(true, "stackArgsMixed", "(IFIFIFIFIFIFIFIFIFIF)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsMixed));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_stackArgsMixed));
 
   jint i1 = 1;
   jint i2 = 2;
@@ -1728,7 +2115,7 @@
                              f7, i8, f8, i9, f9, i10, f10);
 }
 
-JNI_TEST(StackArgsMixed)
+JNI_TEST_CRITICAL(StackArgsMixed)
 
 void Java_MyClassNatives_stackArgsSignExtendedMips64(JNIEnv*, jclass, jint i1, jint i2, jint i3,
                                                      jint i4, jint i5, jint i6, jint i7, jint i8) {
@@ -1760,7 +2147,7 @@
 
 void JniCompilerTest::StackArgsSignExtendedMips64Impl() {
   SetUpForTest(true, "stackArgsSignExtendedMips64", "(IIIIIIII)V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsSignExtendedMips64));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_stackArgsSignExtendedMips64));
   jint i1 = 1;
   jint i2 = 2;
   jint i3 = 3;
@@ -1773,7 +2160,7 @@
   env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8);
 }
 
-JNI_TEST(StackArgsSignExtendedMips64)
+JNI_TEST_CRITICAL(StackArgsSignExtendedMips64)
 
 void Java_MyClassNatives_normalNative(JNIEnv*, jclass) {
   // Intentionally left empty.
@@ -1785,15 +2172,18 @@
   SetUpForTest(/* direct */ true,
                "normalNative",
                "()V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_normalNative));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_normalNative));
 
   ScopedObjectAccess soa(Thread::Current());
   ArtMethod* method = soa.DecodeMethod(jmethod_);
   ASSERT_TRUE(method != nullptr);
 
+  EXPECT_FALSE(method->IsAnnotatedWithCriticalNative());
   EXPECT_FALSE(method->IsAnnotatedWithFastNative());
 }
-JNI_TEST(NormalNative)
+
+// TODO: just rename the java functions  to the standard convention and remove duplicated tests
+JNI_TEST_NORMAL_ONLY(NormalNative)
 
 // Methods annotated with @FastNative are considered "fast native"
 // -- Check that the annotation lookup succeeds.
@@ -1805,14 +2195,53 @@
   SetUpForTest(/* direct */ true,
                "fastNative",
                "()V",
-               reinterpret_cast<void*>(&Java_MyClassNatives_fastNative));
+               CURRENT_JNI_WRAPPER(Java_MyClassNatives_fastNative));
 
   ScopedObjectAccess soa(Thread::Current());
   ArtMethod* method = soa.DecodeMethod(jmethod_);
   ASSERT_TRUE(method != nullptr);
 
+  EXPECT_FALSE(method->IsAnnotatedWithCriticalNative());
   EXPECT_TRUE(method->IsAnnotatedWithFastNative());
 }
-JNI_TEST(FastNative)
+
+// TODO: just rename the java functions  to the standard convention and remove duplicated tests
+JNI_TEST_NORMAL_ONLY(FastNative)
+
+int gJava_myClassNatives_criticalNative_calls[kJniKindCount] = {};
+// Methods annotated with @CriticalNative are considered "critical native"
+// -- Check that the annotation lookup succeeds.
+void Java_MyClassNatives_criticalNative() {
+  gJava_myClassNatives_criticalNative_calls[gCurrentJni]++;
+}
+
+void JniCompilerTest::CriticalNativeImpl() {
+  SetUpForTest(/* direct */ true,
+               // Important: Don't change the "current jni" yet to avoid a method name suffix.
+               "criticalNative",
+               "()V",
+               // TODO: Use CURRENT_JNI_WRAPPER instead which is more generic.
+               reinterpret_cast<void*>(&Java_MyClassNatives_criticalNative));
+
+  // TODO: remove this manual updating of the current JNI. Merge with the other tests.
+  UpdateCurrentJni(JniKind::kCritical);
+  ASSERT_TRUE(IsCurrentJniCritical());
+
+  ScopedObjectAccess soa(Thread::Current());
+  ArtMethod* method = soa.DecodeMethod(jmethod_);
+  ASSERT_TRUE(method != nullptr);
+
+  EXPECT_TRUE(method->IsAnnotatedWithCriticalNative());
+  EXPECT_FALSE(method->IsAnnotatedWithFastNative());
+
+  EXPECT_EQ(0, gJava_myClassNatives_criticalNative_calls[gCurrentJni]);
+  env_->CallStaticVoidMethod(jklass_, jmethod_);
+  EXPECT_EQ(1, gJava_myClassNatives_criticalNative_calls[gCurrentJni]);
+
+  gJava_myClassNatives_criticalNative_calls[gCurrentJni] = 0;
+}
+
+// TODO: just rename the java functions  to the standard convention and remove duplicated tests
+JNI_TEST_NORMAL_ONLY(CriticalNative)
 
 }  // namespace art
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index 0d16260..3f29ae5 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -24,15 +24,33 @@
 
 static_assert(kArmPointerSize == PointerSize::k32, "Unexpected ARM pointer size");
 
-// Used by hard float.
+//
+// JNI calling convention constants.
+//
+
+// List of parameters passed via registers for JNI.
+// JNI uses soft-float, so there is only a GPR list.
+static const Register kJniArgumentRegisters[] = {
+  R0, R1, R2, R3
+};
+
+static const size_t kJniArgumentRegisterCount = arraysize(kJniArgumentRegisters);
+
+//
+// Managed calling convention constants.
+//
+
+// Used by hard float. (General purpose registers.)
 static const Register kHFCoreArgumentRegisters[] = {
   R0, R1, R2, R3
 };
 
+// (VFP single-precision registers.)
 static const SRegister kHFSArgumentRegisters[] = {
   S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15
 };
 
+// (VFP double-precision registers.)
 static const DRegister kHFDArgumentRegisters[] = {
   D0, D1, D2, D3, D4, D5, D6, D7
 };
@@ -40,6 +58,10 @@
 static_assert(arraysize(kHFDArgumentRegisters) * 2 == arraysize(kHFSArgumentRegisters),
     "ks d argument registers mismatch");
 
+//
+// Shared managed+JNI calling convention constants.
+//
+
 static constexpr ManagedRegister kCalleeSaveRegisters[] = {
     // Core registers.
     ArmManagedRegister::FromCoreRegister(R5),
@@ -255,23 +277,95 @@
 }
 // JNI calling convention
 
-ArmJniCallingConvention::ArmJniCallingConvention(bool is_static, bool is_synchronized,
+ArmJniCallingConvention::ArmJniCallingConvention(bool is_static,
+                                                 bool is_synchronized,
+                                                 bool is_critical_native,
                                                  const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, shorty, kArmPointerSize) {
-  // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject
-  // or jclass for static methods and the JNIEnv. We start at the aligned register r2.
-  size_t padding = 0;
-  for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) {
+    : JniCallingConvention(is_static,
+                           is_synchronized,
+                           is_critical_native,
+                           shorty,
+                           kArmPointerSize) {
+  // AAPCS 4.1 specifies fundamental alignments for each type. All of our stack arguments are
+  // usually 4-byte aligned, however longs and doubles must be 8 bytes aligned. Add padding to
+  // maintain 8-byte alignment invariant.
+  //
+  // Compute padding to ensure longs and doubles are not split in AAPCS.
+  size_t shift = 0;
+
+  size_t cur_arg, cur_reg;
+  if (LIKELY(HasExtraArgumentsForJni())) {
+    // Ignore the 'this' jobject or jclass for static methods and the JNIEnv.
+    // We start at the aligned register r2.
+    //
+    // Ignore the first 2 parameters because they are guaranteed to be aligned.
+    cur_arg = NumImplicitArgs();  // skip the "this" arg.
+    cur_reg = 2;  // skip {r0=JNIEnv, r1=jobject} / {r0=JNIEnv, r1=jclass} parameters (start at r2).
+  } else {
+    // Check every parameter.
+    cur_arg = 0;
+    cur_reg = 0;
+  }
+
+  // TODO: Maybe should just use IsCurrentParamALongOrDouble instead to be cleaner?
+  // (this just seems like an unnecessary micro-optimization).
+
+  // Shift across a logical register mapping that looks like:
+  //
+  //   | r0 | r1 | r2 | r3 | SP | SP+4| SP+8 | SP+12 | ... | SP+n | SP+n+4 |
+  //
+  //   (where SP is some arbitrary stack pointer that our 0th stack arg would go into).
+  //
+  // Any time there would normally be a long/double in an odd logical register,
+  // we have to push out the rest of the mappings by 4 bytes to maintain an 8-byte alignment.
+  //
+  // This works for both physical register pairs {r0, r1}, {r2, r3} and for when
+  // the value is on the stack.
+  //
+  // For example:
+  // (a) long would normally go into r1, but we shift it into r2
+  //  | INT | (PAD) | LONG      |
+  //  | r0  |  r1   |  r2  | r3 |
+  //
+  // (b) long would normally go into r3, but we shift it into SP
+  //  | INT | INT | INT | (PAD) | LONG     |
+  //  | r0  |  r1 |  r2 |  r3   | SP+4 SP+8|
+  //
+  // where INT is any <=4 byte arg, and LONG is any 8-byte arg.
+  for (; cur_arg < NumArgs(); cur_arg++) {
     if (IsParamALongOrDouble(cur_arg)) {
-      if ((cur_reg & 1) != 0) {
-        padding += 4;
+      if ((cur_reg & 1) != 0) {  // check that it's in a logical contiguous register pair
+        shift += 4;
         cur_reg++;  // additional bump to ensure alignment
       }
-      cur_reg++;  // additional bump to skip extra long word
+      cur_reg += 2;  // bump the iterator twice for every long argument
+    } else {
+      cur_reg++;  // bump the iterator for every non-long argument
     }
-    cur_reg++;  // bump the iterator for every argument
   }
-  padding_ = padding;
+
+  if (cur_reg < kJniArgumentRegisterCount) {
+    // As a special case when, as a result of shifting (or not) there are no arguments on the stack,
+    // we actually have 0 stack padding.
+    //
+    // For example with @CriticalNative and:
+    // (int, long) -> shifts the long but doesn't need to pad the stack
+    //
+    //          shift
+    //           \/
+    //  | INT | (PAD) | LONG      | (EMPTY) ...
+    //  | r0  |  r1   |  r2  | r3 |   SP    ...
+    //                                /\
+    //                          no stack padding
+    padding_ = 0;
+  } else {
+    padding_ = shift;
+  }
+
+  // TODO: add some new JNI tests for @CriticalNative that introduced new edge cases
+  // (a) Using r0,r1 pair = f(long,...)
+  // (b) Shifting r1 long into r2,r3 pair = f(int, long, int, ...);
+  // (c) Shifting but not introducing a stack padding = f(int, long);
 }
 
 uint32_t ArmJniCallingConvention::CoreSpillMask() const {
@@ -289,15 +383,34 @@
 
 size_t ArmJniCallingConvention::FrameSize() {
   // Method*, LR and callee save area size, local reference segment state
-  size_t frame_data_size = static_cast<size_t>(kArmPointerSize)
-      + (2 + CalleeSaveRegisters().size()) * kFramePointerSize;
-  // References plus 2 words for HandleScope header
-  size_t handle_scope_size = HandleScope::SizeOf(kArmPointerSize, ReferenceCount());
+  const size_t method_ptr_size = static_cast<size_t>(kArmPointerSize);
+  const size_t lr_return_addr_size = kFramePointerSize;
+  const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
+  size_t frame_data_size = method_ptr_size + lr_return_addr_size + callee_save_area_size;
+
+  if (LIKELY(HasLocalReferenceSegmentState())) {
+    // local reference segment state
+    frame_data_size += kFramePointerSize;
+    // TODO: Probably better to use sizeof(IRTSegmentState) here...
+  }
+
+  // References plus link_ (pointer) and number_of_references_ (uint32_t) for HandleScope header
+  const size_t handle_scope_size = HandleScope::SizeOf(kArmPointerSize, ReferenceCount());
+
+  size_t total_size = frame_data_size;
+  if (LIKELY(HasHandleScope())) {
+    // HandleScope is sometimes excluded.
+    total_size += handle_scope_size;                                 // handle scope size
+  }
+
   // Plus return value spill area size
-  return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
+  total_size += SizeOfReturnValue();
+
+  return RoundUp(total_size, kStackAlignment);
 }
 
 size_t ArmJniCallingConvention::OutArgSize() {
+  // TODO: Identical to x86_64 except for also adding additional padding.
   return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_,
                  kStackAlignment);
 }
@@ -309,55 +422,70 @@
 // JniCallingConvention ABI follows AAPCS where longs and doubles must occur
 // in even register numbers and stack slots
 void ArmJniCallingConvention::Next() {
+  // Update the iterator by usual JNI rules.
   JniCallingConvention::Next();
-  size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-  if ((itr_args_ >= 2) &&
-      (arg_pos < NumArgs()) &&
-      IsParamALongOrDouble(arg_pos)) {
-    // itr_slots_ needs to be an even number, according to AAPCS.
-    if ((itr_slots_ & 0x1u) != 0) {
+
+  if (LIKELY(HasNext())) {  // Avoid CHECK failure for IsCurrentParam
+    // Ensure slot is 8-byte aligned for longs/doubles (AAPCS).
+    if (IsCurrentParamALongOrDouble() && ((itr_slots_ & 0x1u) != 0)) {
+      // itr_slots_ needs to be an even number, according to AAPCS.
       itr_slots_++;
     }
   }
 }
 
 bool ArmJniCallingConvention::IsCurrentParamInRegister() {
-  return itr_slots_ < 4;
+  return itr_slots_ < kJniArgumentRegisterCount;
 }
 
 bool ArmJniCallingConvention::IsCurrentParamOnStack() {
   return !IsCurrentParamInRegister();
 }
 
-static const Register kJniArgumentRegisters[] = {
-  R0, R1, R2, R3
-};
 ManagedRegister ArmJniCallingConvention::CurrentParamRegister() {
-  CHECK_LT(itr_slots_, 4u);
-  int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-  if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) {
-    CHECK_EQ(itr_slots_, 2u);
-    return ArmManagedRegister::FromRegisterPair(R2_R3);
+  CHECK_LT(itr_slots_, kJniArgumentRegisterCount);
+  if (IsCurrentParamALongOrDouble()) {
+    // AAPCS 5.1.1 requires 64-bit values to be in a consecutive register pair:
+    // "A double-word sized type is passed in two consecutive registers (e.g., r0 and r1, or r2 and
+    // r3). The content of the registers is as if the value had been loaded from memory
+    // representation with a single LDM instruction."
+    if (itr_slots_ == 0u) {
+      return ArmManagedRegister::FromRegisterPair(R0_R1);
+    } else if (itr_slots_ == 2u) {
+      return ArmManagedRegister::FromRegisterPair(R2_R3);
+    } else {
+      // The register can either be R0 (+R1) or R2 (+R3). Cannot be other values.
+      LOG(FATAL) << "Invalid iterator register position for a long/double " << itr_args_;
+      UNREACHABLE();
+    }
   } else {
-    return
-      ArmManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
+    // All other types can fit into one register.
+    return ArmManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
   }
 }
 
 FrameOffset ArmJniCallingConvention::CurrentParamStackOffset() {
-  CHECK_GE(itr_slots_, 4u);
+  CHECK_GE(itr_slots_, kJniArgumentRegisterCount);
   size_t offset =
-      displacement_.Int32Value() - OutArgSize() + ((itr_slots_ - 4) * kFramePointerSize);
+      displacement_.Int32Value()
+          - OutArgSize()
+          + ((itr_slots_ - kJniArgumentRegisterCount) * kFramePointerSize);
   CHECK_LT(offset, OutArgSize());
   return FrameOffset(offset);
 }
 
 size_t ArmJniCallingConvention::NumberOfOutgoingStackArgs() {
-  size_t static_args = IsStatic() ? 1 : 0;  // count jclass
+  size_t static_args = HasSelfClass() ? 1 : 0;  // count jclass
   // regular argument parameters and this
-  size_t param_args = NumArgs() + NumLongOrDoubleArgs();
+  size_t param_args = NumArgs() + NumLongOrDoubleArgs();  // twice count 8-byte args
+  // XX: Why is the long/ordouble counted twice but not JNIEnv* ???
   // count JNIEnv* less arguments in registers
-  return static_args + param_args + 1 - 4;
+  size_t internal_args = (HasJniEnv() ? 1 : 0 /* jni env */);
+  size_t total_args = static_args + param_args + internal_args;
+
+  return total_args - std::min(kJniArgumentRegisterCount, static_cast<size_t>(total_args));
+
+  // TODO: Very similar to x86_64 except for the return pc.
 }
 
 }  // namespace arm
diff --git a/compiler/jni/quick/arm/calling_convention_arm.h b/compiler/jni/quick/arm/calling_convention_arm.h
index 7c717cc..249f202 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.h
+++ b/compiler/jni/quick/arm/calling_convention_arm.h
@@ -52,7 +52,10 @@
 
 class ArmJniCallingConvention FINAL : public JniCallingConvention {
  public:
-  ArmJniCallingConvention(bool is_static, bool is_synchronized, const char* shorty);
+  ArmJniCallingConvention(bool is_static,
+                          bool is_synchronized,
+                          bool is_critical_native,
+                          const char* shorty);
   ~ArmJniCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index afa707d..3fb7b56 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -24,6 +24,13 @@
 
 static_assert(kArm64PointerSize == PointerSize::k64, "Unexpected ARM64 pointer size");
 
+// Up to how many float-like (float, double) args can be enregistered.
+// The rest of the args must go on the stack.
+constexpr size_t kMaxFloatOrDoubleRegisterArguments = 8u;
+// Up to how many integer-like (pointers, objects, longs, int, short, bool, etc) args can be
+// enregistered. The rest of the args must go on the stack.
+constexpr size_t kMaxIntLikeRegisterArguments = 8u;
+
 static const XRegister kXArgumentRegisters[] = {
   X0, X1, X2, X3, X4, X5, X6, X7
 };
@@ -211,9 +218,11 @@
 }
 
 // JNI calling convention
-Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_synchronized,
+Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static,
+                                                     bool is_synchronized,
+                                                     bool is_critical_native,
                                                      const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, shorty, kArm64PointerSize) {
+    : JniCallingConvention(is_static, is_synchronized, is_critical_native, shorty, kArm64PointerSize) {
 }
 
 uint32_t Arm64JniCallingConvention::CoreSpillMask() const {
@@ -230,38 +239,59 @@
 
 size_t Arm64JniCallingConvention::FrameSize() {
   // Method*, callee save area size, local reference segment state
-  size_t frame_data_size = kFramePointerSize +
-      CalleeSaveRegisters().size() * kFramePointerSize + sizeof(uint32_t);
+  //
+  // (Unlike x86_64, do not include return address, and the segment state is uint32
+  // instead of pointer).
+  size_t method_ptr_size = static_cast<size_t>(kFramePointerSize);
+  size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
+
+  size_t frame_data_size = method_ptr_size + callee_save_area_size;
+  if (LIKELY(HasLocalReferenceSegmentState())) {
+    frame_data_size += sizeof(uint32_t);
+  }
   // References plus 2 words for HandleScope header
   size_t handle_scope_size = HandleScope::SizeOf(kArm64PointerSize, ReferenceCount());
+
+  size_t total_size = frame_data_size;
+  if (LIKELY(HasHandleScope())) {
+    // HandleScope is sometimes excluded.
+    total_size += handle_scope_size;                                 // handle scope size
+  }
+
   // Plus return value spill area size
-  return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
+  total_size += SizeOfReturnValue();
+
+  return RoundUp(total_size, kStackAlignment);
 }
 
 size_t Arm64JniCallingConvention::OutArgSize() {
+  // Same as X86_64
   return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize, kStackAlignment);
 }
 
 ArrayRef<const ManagedRegister> Arm64JniCallingConvention::CalleeSaveRegisters() const {
+  // Same as X86_64
   return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
 }
 
 bool Arm64JniCallingConvention::IsCurrentParamInRegister() {
   if (IsCurrentParamAFloatOrDouble()) {
-    return (itr_float_and_doubles_ < 8);
+    return (itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments);
   } else {
-    return ((itr_args_ - itr_float_and_doubles_) < 8);
+    return ((itr_args_ - itr_float_and_doubles_) < kMaxIntLikeRegisterArguments);
   }
+  // TODO: Can we just call CurrentParamRegister to figure this out?
 }
 
 bool Arm64JniCallingConvention::IsCurrentParamOnStack() {
+  // Is this ever not the same for all the architectures?
   return !IsCurrentParamInRegister();
 }
 
 ManagedRegister Arm64JniCallingConvention::CurrentParamRegister() {
   CHECK(IsCurrentParamInRegister());
   if (IsCurrentParamAFloatOrDouble()) {
-    CHECK_LT(itr_float_and_doubles_, 8u);
+    CHECK_LT(itr_float_and_doubles_, kMaxFloatOrDoubleRegisterArguments);
     if (IsCurrentParamADouble()) {
       return Arm64ManagedRegister::FromDRegister(kDArgumentRegisters[itr_float_and_doubles_]);
     } else {
@@ -269,7 +299,7 @@
     }
   } else {
     int gp_reg = itr_args_ - itr_float_and_doubles_;
-    CHECK_LT(static_cast<unsigned int>(gp_reg), 8u);
+    CHECK_LT(static_cast<unsigned int>(gp_reg), kMaxIntLikeRegisterArguments);
     if (IsCurrentParamALong() || IsCurrentParamAReference() || IsCurrentParamJniEnv())  {
       return Arm64ManagedRegister::FromXRegister(kXArgumentRegisters[gp_reg]);
     } else {
@@ -281,20 +311,30 @@
 FrameOffset Arm64JniCallingConvention::CurrentParamStackOffset() {
   CHECK(IsCurrentParamOnStack());
   size_t args_on_stack = itr_args_
-                  - std::min(8u, itr_float_and_doubles_)
-                  - std::min(8u, (itr_args_ - itr_float_and_doubles_));
+                  - std::min(kMaxFloatOrDoubleRegisterArguments,
+                             static_cast<size_t>(itr_float_and_doubles_))
+                  - std::min(kMaxIntLikeRegisterArguments,
+                             static_cast<size_t>(itr_args_ - itr_float_and_doubles_));
   size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize);
   CHECK_LT(offset, OutArgSize());
   return FrameOffset(offset);
+  // TODO: Seems identical to X86_64 code.
 }
 
 size_t Arm64JniCallingConvention::NumberOfOutgoingStackArgs() {
   // all arguments including JNI args
   size_t all_args = NumArgs() + NumberOfExtraArgumentsForJni();
 
-  size_t all_stack_args = all_args -
-            std::min(8u, static_cast<unsigned int>(NumFloatOrDoubleArgs())) -
-            std::min(8u, static_cast<unsigned int>((all_args - NumFloatOrDoubleArgs())));
+  DCHECK_GE(all_args, NumFloatOrDoubleArgs());
+
+  size_t all_stack_args =
+      all_args
+      - std::min(kMaxFloatOrDoubleRegisterArguments,
+                 static_cast<size_t>(NumFloatOrDoubleArgs()))
+      - std::min(kMaxIntLikeRegisterArguments,
+                 static_cast<size_t>((all_args - NumFloatOrDoubleArgs())));
+
+  // TODO: Seems similar to X86_64 code except it doesn't count return pc.
 
   return all_stack_args;
 }
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.h b/compiler/jni/quick/arm64/calling_convention_arm64.h
index 90b12e5..5618942 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.h
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.h
@@ -52,7 +52,10 @@
 
 class Arm64JniCallingConvention FINAL : public JniCallingConvention {
  public:
-  Arm64JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty);
+  Arm64JniCallingConvention(bool is_static,
+                            bool is_synchronized,
+                            bool is_critical_native,
+                            const char* shorty);
   ~Arm64JniCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc
index c7ed9c9..9859b5d 100644
--- a/compiler/jni/quick/calling_convention.cc
+++ b/compiler/jni/quick/calling_convention.cc
@@ -149,19 +149,44 @@
 std::unique_ptr<JniCallingConvention> JniCallingConvention::Create(ArenaAllocator* arena,
                                                                    bool is_static,
                                                                    bool is_synchronized,
+                                                                   bool is_critical_native,
                                                                    const char* shorty,
                                                                    InstructionSet instruction_set) {
+  if (UNLIKELY(is_critical_native)) {
+    // Sanity check that the requested JNI instruction set
+    // is supported for critical natives. Not every one is.
+    switch (instruction_set) {
+      case kX86_64:
+      case kX86:
+      case kArm64:
+      case kArm:
+      case kThumb2:
+        break;
+      default:
+        is_critical_native = false;
+        LOG(WARNING) << "@CriticalNative support not implemented for " << instruction_set
+                     << "; will crash at runtime if trying to invoke such a method.";
+        // TODO: implement for MIPS/MIPS64
+    }
+  }
+
   switch (instruction_set) {
 #ifdef ART_ENABLE_CODEGEN_arm
     case kArm:
     case kThumb2:
       return std::unique_ptr<JniCallingConvention>(
-          new (arena) arm::ArmJniCallingConvention(is_static, is_synchronized, shorty));
+          new (arena) arm::ArmJniCallingConvention(is_static,
+                                                   is_synchronized,
+                                                   is_critical_native,
+                                                   shorty));
 #endif
 #ifdef ART_ENABLE_CODEGEN_arm64
     case kArm64:
       return std::unique_ptr<JniCallingConvention>(
-          new (arena) arm64::Arm64JniCallingConvention(is_static, is_synchronized, shorty));
+          new (arena) arm64::Arm64JniCallingConvention(is_static,
+                                                       is_synchronized,
+                                                       is_critical_native,
+                                                       shorty));
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips
     case kMips:
@@ -176,12 +201,18 @@
 #ifdef ART_ENABLE_CODEGEN_x86
     case kX86:
       return std::unique_ptr<JniCallingConvention>(
-          new (arena) x86::X86JniCallingConvention(is_static, is_synchronized, shorty));
+          new (arena) x86::X86JniCallingConvention(is_static,
+                                                   is_synchronized,
+                                                   is_critical_native,
+                                                   shorty));
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86_64
     case kX86_64:
       return std::unique_ptr<JniCallingConvention>(
-          new (arena) x86_64::X86_64JniCallingConvention(is_static, is_synchronized, shorty));
+          new (arena) x86_64::X86_64JniCallingConvention(is_static,
+                                                         is_synchronized,
+                                                         is_critical_native,
+                                                         shorty));
 #endif
     default:
       LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
@@ -199,27 +230,36 @@
 }
 
 FrameOffset JniCallingConvention::ReturnValueSaveLocation() const {
-  // Segment state is 4 bytes long
-  return FrameOffset(SavedLocalReferenceCookieOffset().Int32Value() + 4);
+  if (LIKELY(HasHandleScope())) {
+    // Initial offset already includes the displacement.
+    // -- Remove the additional local reference cookie offset if we don't have a handle scope.
+    const size_t saved_local_reference_cookie_offset =
+        SavedLocalReferenceCookieOffset().Int32Value();
+    // Segment state is 4 bytes long
+    const size_t segment_state_size = 4;
+    return FrameOffset(saved_local_reference_cookie_offset + segment_state_size);
+  } else {
+    // Include only the initial Method* as part of the offset.
+    CHECK_LT(displacement_.SizeValue(),
+             static_cast<size_t>(std::numeric_limits<int32_t>::max()));
+    return FrameOffset(displacement_.Int32Value() + static_cast<size_t>(frame_pointer_size_));
+  }
 }
 
 bool JniCallingConvention::HasNext() {
-  if (itr_args_ <= kObjectOrClass) {
+  if (IsCurrentArgExtraForJni()) {
     return true;
   } else {
-    unsigned int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
+    unsigned int arg_pos = GetIteratorPositionWithinShorty();
     return arg_pos < NumArgs();
   }
 }
 
 void JniCallingConvention::Next() {
   CHECK(HasNext());
-  if (itr_args_ > kObjectOrClass) {
-    int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-    if (IsParamALongOrDouble(arg_pos)) {
-      itr_longs_and_doubles_++;
-      itr_slots_++;
-    }
+  if (IsCurrentParamALong() || IsCurrentParamADouble()) {
+    itr_longs_and_doubles_++;
+    itr_slots_++;
   }
   if (IsCurrentParamAFloatOrDouble()) {
     itr_float_and_doubles_++;
@@ -227,63 +267,73 @@
   if (IsCurrentParamAReference()) {
     itr_refs_++;
   }
+  // This default/fallthrough case also covers the extra JNIEnv* argument,
+  // as well as any other single-slot primitives.
   itr_args_++;
   itr_slots_++;
 }
 
 bool JniCallingConvention::IsCurrentParamAReference() {
-  switch (itr_args_) {
-    case kJniEnv:
-      return false;  // JNIEnv*
-    case kObjectOrClass:
-      return true;   // jobject or jclass
-    default: {
-      int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-      return IsParamAReference(arg_pos);
-    }
+  bool return_value;
+  if (SwitchExtraJniArguments(itr_args_,
+                              false,  // JNIEnv*
+                              true,   // jobject or jclass
+                              /* out parameters */
+                              &return_value)) {
+    return return_value;
+  } else {
+    int arg_pos = GetIteratorPositionWithinShorty();
+    return IsParamAReference(arg_pos);
   }
 }
 
+
 bool JniCallingConvention::IsCurrentParamJniEnv() {
+  if (UNLIKELY(!HasJniEnv())) {
+    return false;
+  }
   return (itr_args_ == kJniEnv);
 }
 
 bool JniCallingConvention::IsCurrentParamAFloatOrDouble() {
-  switch (itr_args_) {
-    case kJniEnv:
-      return false;  // JNIEnv*
-    case kObjectOrClass:
-      return false;   // jobject or jclass
-    default: {
-      int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-      return IsParamAFloatOrDouble(arg_pos);
-    }
+  bool return_value;
+  if (SwitchExtraJniArguments(itr_args_,
+                              false,  // jnienv*
+                              false,  // jobject or jclass
+                              /* out parameters */
+                              &return_value)) {
+    return return_value;
+  } else {
+    int arg_pos = GetIteratorPositionWithinShorty();
+    return IsParamAFloatOrDouble(arg_pos);
   }
 }
 
 bool JniCallingConvention::IsCurrentParamADouble() {
-  switch (itr_args_) {
-    case kJniEnv:
-      return false;  // JNIEnv*
-    case kObjectOrClass:
-      return false;   // jobject or jclass
-    default: {
-      int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-      return IsParamADouble(arg_pos);
-    }
+  bool return_value;
+  if (SwitchExtraJniArguments(itr_args_,
+                              false,  // jnienv*
+                              false,  // jobject or jclass
+                              /* out parameters */
+                              &return_value)) {
+    return return_value;
+  } else {
+    int arg_pos = GetIteratorPositionWithinShorty();
+    return IsParamADouble(arg_pos);
   }
 }
 
 bool JniCallingConvention::IsCurrentParamALong() {
-  switch (itr_args_) {
-    case kJniEnv:
-      return false;  // JNIEnv*
-    case kObjectOrClass:
-      return false;   // jobject or jclass
-    default: {
-      int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
-      return IsParamALong(arg_pos);
-    }
+  bool return_value;
+  if (SwitchExtraJniArguments(itr_args_,
+                              false,  // jnienv*
+                              false,  // jobject or jclass
+                              /* out parameters */
+                              &return_value)) {
+    return return_value;
+  } else {
+    int arg_pos = GetIteratorPositionWithinShorty();
+    return IsParamALong(arg_pos);
   }
 }
 
@@ -297,19 +347,93 @@
   return FrameOffset(result);
 }
 
-size_t JniCallingConvention::CurrentParamSize() {
-  if (itr_args_ <= kObjectOrClass) {
+size_t JniCallingConvention::CurrentParamSize() const {
+  if (IsCurrentArgExtraForJni()) {
     return static_cast<size_t>(frame_pointer_size_);  // JNIEnv or jobject/jclass
   } else {
-    int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
+    int arg_pos = GetIteratorPositionWithinShorty();
     return ParamSize(arg_pos);
   }
 }
 
-size_t JniCallingConvention::NumberOfExtraArgumentsForJni() {
-  // The first argument is the JNIEnv*.
-  // Static methods have an extra argument which is the jclass.
-  return IsStatic() ? 2 : 1;
+size_t JniCallingConvention::NumberOfExtraArgumentsForJni() const {
+  if (LIKELY(HasExtraArgumentsForJni())) {
+    // The first argument is the JNIEnv*.
+    // Static methods have an extra argument which is the jclass.
+    return IsStatic() ? 2 : 1;
+  } else {
+    // Critical natives exclude the JNIEnv and the jclass/this parameters.
+    return 0;
+  }
 }
 
+bool JniCallingConvention::HasHandleScope() const {
+  // Exclude HandleScope for @CriticalNative methods for optimization speed.
+  return is_critical_native_ == false;
+}
+
+bool JniCallingConvention::HasLocalReferenceSegmentState() const {
+  // Exclude local reference segment states for @CriticalNative methods for optimization speed.
+  return is_critical_native_ == false;
+}
+
+bool JniCallingConvention::HasJniEnv() const {
+  // Exclude "JNIEnv*" parameter for @CriticalNative methods.
+  return HasExtraArgumentsForJni();
+}
+
+bool JniCallingConvention::HasSelfClass() const {
+  if (!IsStatic()) {
+    // Virtual functions: There is never an implicit jclass parameter.
+    return false;
+  } else {
+    // Static functions: There is an implicit jclass parameter unless it's @CriticalNative.
+    return HasExtraArgumentsForJni();
+  }
+}
+
+bool JniCallingConvention::HasExtraArgumentsForJni() const {
+  // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters.
+  return is_critical_native_ == false;
+}
+
+unsigned int JniCallingConvention::GetIteratorPositionWithinShorty() const {
+  // We need to subtract out the extra JNI arguments if we want to use this iterator position
+  // with the inherited CallingConvention member functions, which rely on scanning the shorty.
+  // Note that our shorty does *not* include the JNIEnv, jclass/jobject parameters.
+  DCHECK_GE(itr_args_, NumberOfExtraArgumentsForJni());
+  return itr_args_ - NumberOfExtraArgumentsForJni();
+}
+
+bool JniCallingConvention::IsCurrentArgExtraForJni() const {
+  if (UNLIKELY(!HasExtraArgumentsForJni())) {
+    return false;  // If there are no extra args, we can never be an extra.
+  }
+  // Only parameters kJniEnv and kObjectOrClass are considered extra.
+  return itr_args_ <= kObjectOrClass;
+}
+
+bool JniCallingConvention::SwitchExtraJniArguments(size_t switch_value,
+                                                   bool case_jni_env,
+                                                   bool case_object_or_class,
+                                                   /* out parameters */
+                                                   bool* return_value) const {
+  DCHECK(return_value != nullptr);
+  if (UNLIKELY(!HasExtraArgumentsForJni())) {
+    return false;
+  }
+
+  switch (switch_value) {
+    case kJniEnv:
+      *return_value = case_jni_env;
+      return true;
+    case kObjectOrClass:
+      *return_value = case_object_or_class;
+      return true;
+    default:
+      return false;
+  }
+}
+
+
 }  // namespace art
diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h
index 995fa51..3d89146 100644
--- a/compiler/jni/quick/calling_convention.h
+++ b/compiler/jni/quick/calling_convention.h
@@ -161,6 +161,12 @@
   size_t NumArgs() const {
     return num_args_;
   }
+  // Implicit argument count: 1 for instance functions, 0 for static functions.
+  // (The implicit argument is only relevant to the shorty, i.e.
+  // the 0th arg is not in the shorty if it's implicit).
+  size_t NumImplicitArgs() const {
+    return IsStatic() ? 0 : 1;
+  }
   size_t NumLongOrDoubleArgs() const {
     return num_long_or_double_args_;
   }
@@ -281,6 +287,7 @@
   static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* arena,
                                                       bool is_static,
                                                       bool is_synchronized,
+                                                      bool is_critical_native,
                                                       const char* shorty,
                                                       InstructionSet instruction_set);
 
@@ -288,7 +295,8 @@
   // always at the bottom of a frame, but this doesn't work for outgoing
   // native args). Includes alignment.
   virtual size_t FrameSize() = 0;
-  // Size of outgoing arguments, including alignment
+  // Size of outgoing arguments (stack portion), including alignment.
+  // -- Arguments that are passed via registers are excluded from this size.
   virtual size_t OutArgSize() = 0;
   // Number of references in stack indirect reference table
   size_t ReferenceCount() const;
@@ -319,8 +327,11 @@
   bool IsCurrentParamAFloatOrDouble();
   bool IsCurrentParamADouble();
   bool IsCurrentParamALong();
+  bool IsCurrentParamALongOrDouble() {
+    return IsCurrentParamALong() || IsCurrentParamADouble();
+  }
   bool IsCurrentParamJniEnv();
-  size_t CurrentParamSize();
+  size_t CurrentParamSize() const;
   virtual bool IsCurrentParamInRegister() = 0;
   virtual bool IsCurrentParamOnStack() = 0;
   virtual ManagedRegister CurrentParamRegister() = 0;
@@ -359,18 +370,62 @@
     kObjectOrClass = 1
   };
 
+  // TODO: remove this constructor once all are changed to the below one.
   JniCallingConvention(bool is_static,
                        bool is_synchronized,
                        const char* shorty,
                        PointerSize frame_pointer_size)
-      : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
+      : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
+        is_critical_native_(false) {}
+
+  JniCallingConvention(bool is_static,
+                       bool is_synchronized,
+                       bool is_critical_native,
+                       const char* shorty,
+                       PointerSize frame_pointer_size)
+      : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
+        is_critical_native_(is_critical_native) {}
 
   // Number of stack slots for outgoing arguments, above which the handle scope is
   // located
   virtual size_t NumberOfOutgoingStackArgs() = 0;
 
  protected:
-  size_t NumberOfExtraArgumentsForJni();
+  size_t NumberOfExtraArgumentsForJni() const;
+
+  // Does the transition have a StackHandleScope?
+  bool HasHandleScope() const;
+  // Does the transition have a local reference segment state?
+  bool HasLocalReferenceSegmentState() const;
+  // Has a JNIEnv* parameter implicitly?
+  bool HasJniEnv() const;
+  // Has a 'jclass' parameter implicitly?
+  bool HasSelfClass() const;
+
+  // Are there extra JNI arguments (JNIEnv* and maybe jclass)?
+  bool HasExtraArgumentsForJni() const;
+
+  // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments.
+  unsigned int GetIteratorPositionWithinShorty() const;
+
+  // Is the current argument (at the iterator) an extra argument for JNI?
+  bool IsCurrentArgExtraForJni() const;
+
+  const bool is_critical_native_;
+
+ private:
+  // Shorthand for switching on the switch value but only IF there are extra JNI arguments.
+  //
+  // Puts the case value into return_value.
+  // * (switch_value == kJniEnv) => case_jni_env
+  // * (switch_value == kObjectOrClass) => case_object_or_class
+  //
+  // Returns false otherwise (or if there are no extra JNI arguments).
+  bool SwitchExtraJniArguments(size_t switch_value,
+                               bool case_jni_env,
+                               bool case_object_or_class,
+                               /* out parameters */
+                               bool* return_value) const;
 };
 
 }  // namespace art
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index d092c3f..7e58d78 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -90,8 +90,10 @@
   const InstructionSetFeatures* instruction_set_features = driver->GetInstructionSetFeatures();
 
   // i.e. if the method was annotated with @FastNative
-  const bool is_fast_native =
-      (static_cast<uint32_t>(optimization_flags) & Compiler::kFastNative) != 0;
+  const bool is_fast_native = (optimization_flags == Compiler::kFastNative);
+
+  // i.e. if the method was annotated with @CriticalNative
+  bool is_critical_native = (optimization_flags == Compiler::kCriticalNative);
 
   VLOG(jni) << "JniCompile: Method :: "
               << art::PrettyMethod(method_idx, dex_file, /* with signature */ true)
@@ -102,12 +104,50 @@
               << art::PrettyMethod(method_idx, dex_file, /* with signature */ true);
   }
 
+  if (UNLIKELY(is_critical_native)) {
+    VLOG(jni) << "JniCompile: Critical native method detected :: "
+              << art::PrettyMethod(method_idx, dex_file, /* with signature */ true);
+  }
+
+  if (kIsDebugBuild) {
+    // Don't allow both @FastNative and @CriticalNative. They are mutually exclusive.
+    if (UNLIKELY(is_fast_native && is_critical_native)) {
+      LOG(FATAL) << "JniCompile: Method cannot be both @CriticalNative and @FastNative"
+                 << art::PrettyMethod(method_idx, dex_file, /* with_signature */ true);
+    }
+
+    // @CriticalNative - extra checks:
+    // -- Don't allow virtual criticals
+    // -- Don't allow synchronized criticals
+    // -- Don't allow any objects as parameter or return value
+    if (UNLIKELY(is_critical_native)) {
+      CHECK(is_static)
+          << "@CriticalNative functions cannot be virtual since that would"
+          << "require passing a reference parameter (this), which is illegal "
+          << art::PrettyMethod(method_idx, dex_file, /* with_signature */ true);
+      CHECK(!is_synchronized)
+          << "@CriticalNative functions cannot be synchronized since that would"
+          << "require passing a (class and/or this) reference parameter, which is illegal "
+          << art::PrettyMethod(method_idx, dex_file, /* with_signature */ true);
+      for (size_t i = 0; i < strlen(shorty); ++i) {
+        CHECK_NE(Primitive::kPrimNot, Primitive::GetType(shorty[i]))
+            << "@CriticalNative methods' shorty types must not have illegal references "
+            << art::PrettyMethod(method_idx, dex_file, /* with_signature */ true);
+      }
+    }
+  }
+
   ArenaPool pool;
   ArenaAllocator arena(&pool);
 
   // Calling conventions used to iterate over parameters to method
-  std::unique_ptr<JniCallingConvention> main_jni_conv(
-      JniCallingConvention::Create(&arena, is_static, is_synchronized, shorty, instruction_set));
+  std::unique_ptr<JniCallingConvention> main_jni_conv =
+      JniCallingConvention::Create(&arena,
+                                   is_static,
+                                   is_synchronized,
+                                   is_critical_native,
+                                   shorty,
+                                   instruction_set);
   bool reference_return = main_jni_conv->IsReturnAReference();
 
   std::unique_ptr<ManagedRuntimeCallingConvention> mr_conv(
@@ -127,8 +167,13 @@
     jni_end_shorty = "V";
   }
 
-  std::unique_ptr<JniCallingConvention> end_jni_conv(JniCallingConvention::Create(
-      &arena, is_static, is_synchronized, jni_end_shorty, instruction_set));
+  std::unique_ptr<JniCallingConvention> end_jni_conv(
+      JniCallingConvention::Create(&arena,
+                                   is_static,
+                                   is_synchronized,
+                                   is_critical_native,
+                                   jni_end_shorty,
+                                   instruction_set));
 
   // Assembler that holds generated instructions
   std::unique_ptr<JNIMacroAssembler<kPointerSize>> jni_asm =
@@ -141,75 +186,89 @@
   const Offset monitor_enter(OFFSETOF_MEMBER(JNINativeInterface, MonitorEnter));
   const Offset monitor_exit(OFFSETOF_MEMBER(JNINativeInterface, MonitorExit));
 
-  // 1. Build the frame saving all callee saves
-  const size_t frame_size(main_jni_conv->FrameSize());
+  // 1. Build the frame saving all callee saves, Method*, and PC return address.
+  const size_t frame_size(main_jni_conv->FrameSize());  // Excludes outgoing args.
   ArrayRef<const ManagedRegister> callee_save_regs = main_jni_conv->CalleeSaveRegisters();
   __ BuildFrame(frame_size, mr_conv->MethodRegister(), callee_save_regs, mr_conv->EntrySpills());
   DCHECK_EQ(jni_asm->cfi().GetCurrentCFAOffset(), static_cast<int>(frame_size));
 
-  // 2. Set up the HandleScope
-  mr_conv->ResetIterator(FrameOffset(frame_size));
-  main_jni_conv->ResetIterator(FrameOffset(0));
-  __ StoreImmediateToFrame(main_jni_conv->HandleScopeNumRefsOffset(),
-                           main_jni_conv->ReferenceCount(),
-                           mr_conv->InterproceduralScratchRegister());
+  if (LIKELY(!is_critical_native)) {
+    // NOTE: @CriticalNative methods don't have a HandleScope
+    //       because they can't have any reference parameters or return values.
 
-  __ CopyRawPtrFromThread(main_jni_conv->HandleScopeLinkOffset(),
-                          Thread::TopHandleScopeOffset<kPointerSize>(),
-                          mr_conv->InterproceduralScratchRegister());
-  __ StoreStackOffsetToThread(Thread::TopHandleScopeOffset<kPointerSize>(),
-                              main_jni_conv->HandleScopeOffset(),
-                              mr_conv->InterproceduralScratchRegister());
+    // 2. Set up the HandleScope
+    mr_conv->ResetIterator(FrameOffset(frame_size));
+    main_jni_conv->ResetIterator(FrameOffset(0));
+    __ StoreImmediateToFrame(main_jni_conv->HandleScopeNumRefsOffset(),
+                             main_jni_conv->ReferenceCount(),
+                             mr_conv->InterproceduralScratchRegister());
 
-  // 3. Place incoming reference arguments into handle scope
-  main_jni_conv->Next();  // Skip JNIEnv*
-  // 3.5. Create Class argument for static methods out of passed method
-  if (is_static) {
-    FrameOffset handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
-    // Check handle scope offset is within frame
-    CHECK_LT(handle_scope_offset.Uint32Value(), frame_size);
-    // Note this LoadRef() doesn't need heap unpoisoning since it's from the ArtMethod.
-    // Note this LoadRef() does not include read barrier. It will be handled below.
-    __ LoadRef(main_jni_conv->InterproceduralScratchRegister(),
-               mr_conv->MethodRegister(), ArtMethod::DeclaringClassOffset(), false);
-    __ VerifyObject(main_jni_conv->InterproceduralScratchRegister(), false);
-    __ StoreRef(handle_scope_offset, main_jni_conv->InterproceduralScratchRegister());
-    main_jni_conv->Next();  // in handle scope so move to next argument
-  }
-  while (mr_conv->HasNext()) {
-    CHECK(main_jni_conv->HasNext());
-    bool ref_param = main_jni_conv->IsCurrentParamAReference();
-    CHECK(!ref_param || mr_conv->IsCurrentParamAReference());
-    // References need placing in handle scope and the entry value passing
-    if (ref_param) {
-      // Compute handle scope entry, note null is placed in the handle scope but its boxed value
-      // must be null.
+    __ CopyRawPtrFromThread(main_jni_conv->HandleScopeLinkOffset(),
+                            Thread::TopHandleScopeOffset<kPointerSize>(),
+                            mr_conv->InterproceduralScratchRegister());
+    __ StoreStackOffsetToThread(Thread::TopHandleScopeOffset<kPointerSize>(),
+                                main_jni_conv->HandleScopeOffset(),
+                                mr_conv->InterproceduralScratchRegister());
+
+    // 3. Place incoming reference arguments into handle scope
+    main_jni_conv->Next();  // Skip JNIEnv*
+    // 3.5. Create Class argument for static methods out of passed method
+    if (is_static) {
       FrameOffset handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
-      // Check handle scope offset is within frame and doesn't run into the saved segment state.
+      // Check handle scope offset is within frame
       CHECK_LT(handle_scope_offset.Uint32Value(), frame_size);
-      CHECK_NE(handle_scope_offset.Uint32Value(),
-               main_jni_conv->SavedLocalReferenceCookieOffset().Uint32Value());
-      bool input_in_reg = mr_conv->IsCurrentParamInRegister();
-      bool input_on_stack = mr_conv->IsCurrentParamOnStack();
-      CHECK(input_in_reg || input_on_stack);
-
-      if (input_in_reg) {
-        ManagedRegister in_reg  =  mr_conv->CurrentParamRegister();
-        __ VerifyObject(in_reg, mr_conv->IsCurrentArgPossiblyNull());
-        __ StoreRef(handle_scope_offset, in_reg);
-      } else if (input_on_stack) {
-        FrameOffset in_off  = mr_conv->CurrentParamStackOffset();
-        __ VerifyObject(in_off, mr_conv->IsCurrentArgPossiblyNull());
-        __ CopyRef(handle_scope_offset, in_off,
-                   mr_conv->InterproceduralScratchRegister());
-      }
+      // Note this LoadRef() doesn't need heap unpoisoning since it's from the ArtMethod.
+      // Note this LoadRef() does not include read barrier. It will be handled below.
+      //
+      // scratchRegister = *method[DeclaringClassOffset()];
+      __ LoadRef(main_jni_conv->InterproceduralScratchRegister(),
+                 mr_conv->MethodRegister(), ArtMethod::DeclaringClassOffset(), false);
+      __ VerifyObject(main_jni_conv->InterproceduralScratchRegister(), false);
+      // *handleScopeOffset = scratchRegister
+      __ StoreRef(handle_scope_offset, main_jni_conv->InterproceduralScratchRegister());
+      main_jni_conv->Next();  // in handle scope so move to next argument
     }
-    mr_conv->Next();
-    main_jni_conv->Next();
-  }
+    // Place every reference into the handle scope (ignore other parameters).
+    while (mr_conv->HasNext()) {
+      CHECK(main_jni_conv->HasNext());
+      bool ref_param = main_jni_conv->IsCurrentParamAReference();
+      CHECK(!ref_param || mr_conv->IsCurrentParamAReference());
+      // References need placing in handle scope and the entry value passing
+      if (ref_param) {
+        // Compute handle scope entry, note null is placed in the handle scope but its boxed value
+        // must be null.
+        FrameOffset handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
+        // Check handle scope offset is within frame and doesn't run into the saved segment state.
+        CHECK_LT(handle_scope_offset.Uint32Value(), frame_size);
+        CHECK_NE(handle_scope_offset.Uint32Value(),
+                 main_jni_conv->SavedLocalReferenceCookieOffset().Uint32Value());
+        bool input_in_reg = mr_conv->IsCurrentParamInRegister();
+        bool input_on_stack = mr_conv->IsCurrentParamOnStack();
+        CHECK(input_in_reg || input_on_stack);
 
-  // 4. Write out the end of the quick frames.
-  __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+        if (input_in_reg) {
+          ManagedRegister in_reg  =  mr_conv->CurrentParamRegister();
+          __ VerifyObject(in_reg, mr_conv->IsCurrentArgPossiblyNull());
+          __ StoreRef(handle_scope_offset, in_reg);
+        } else if (input_on_stack) {
+          FrameOffset in_off  = mr_conv->CurrentParamStackOffset();
+          __ VerifyObject(in_off, mr_conv->IsCurrentArgPossiblyNull());
+          __ CopyRef(handle_scope_offset, in_off,
+                     mr_conv->InterproceduralScratchRegister());
+        }
+      }
+      mr_conv->Next();
+      main_jni_conv->Next();
+    }
+
+    // 4. Write out the end of the quick frames.
+    __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+
+    // NOTE: @CriticalNative does not need to store the stack pointer to the thread
+    //       because garbage collections are disabled within the execution of a
+    //       @CriticalNative method.
+    //       (TODO: We could probably disable it for @FastNative too).
+  }  // if (!is_critical_native)
 
   // 5. Move frame down to allow space for out going args.
   const size_t main_out_arg_size = main_jni_conv->OutArgSize();
@@ -218,7 +277,9 @@
 
   // Call the read barrier for the declaring class loaded from the method for a static call.
   // Note that we always have outgoing param space available for at least two params.
-  if (kUseReadBarrier && is_static) {
+  if (kUseReadBarrier && is_static && !is_critical_native) {
+    // XX: Why is this necessary only for the jclass? Why not for every single object ref?
+    // Skip this for @CriticalNative because we didn't build a HandleScope to begin with.
     ThreadOffset<kPointerSize> read_barrier = QUICK_ENTRYPOINT_OFFSET(kPointerSize,
                                                                       pReadBarrierJni);
     main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
@@ -255,46 +316,56 @@
   //    can occur. The result is the saved JNI local state that is restored by the exit call. We
   //    abuse the JNI calling convention here, that is guaranteed to support passing 2 pointer
   //    arguments.
-  ThreadOffset<kPointerSize> jni_start =
-      is_synchronized
-          ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStartSynchronized)
-          : (is_fast_native
-                 ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastStart)
-                 : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart));
+  FrameOffset locked_object_handle_scope_offset(0xBEEFDEAD);
+  if (LIKELY(!is_critical_native)) {
+    // Skip this for @CriticalNative methods. They do not call JniMethodStart.
+    ThreadOffset<kPointerSize> jni_start =
+        is_synchronized
+            ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStartSynchronized)
+            : (is_fast_native
+                   ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastStart)
+                   : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart));
 
-  main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
-  FrameOffset locked_object_handle_scope_offset(0);
-  if (is_synchronized) {
-    // Pass object for locking.
-    main_jni_conv->Next();  // Skip JNIEnv.
-    locked_object_handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
     main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
-    if (main_jni_conv->IsCurrentParamOnStack()) {
-      FrameOffset out_off = main_jni_conv->CurrentParamStackOffset();
-      __ CreateHandleScopeEntry(out_off, locked_object_handle_scope_offset,
-                                mr_conv->InterproceduralScratchRegister(), false);
-    } else {
-      ManagedRegister out_reg = main_jni_conv->CurrentParamRegister();
-      __ CreateHandleScopeEntry(out_reg, locked_object_handle_scope_offset,
-                                ManagedRegister::NoRegister(), false);
+    locked_object_handle_scope_offset = FrameOffset(0);
+    if (is_synchronized) {
+      // Pass object for locking.
+      main_jni_conv->Next();  // Skip JNIEnv.
+      locked_object_handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
+      main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
+      if (main_jni_conv->IsCurrentParamOnStack()) {
+        FrameOffset out_off = main_jni_conv->CurrentParamStackOffset();
+        __ CreateHandleScopeEntry(out_off, locked_object_handle_scope_offset,
+                                  mr_conv->InterproceduralScratchRegister(), false);
+      } else {
+        ManagedRegister out_reg = main_jni_conv->CurrentParamRegister();
+        __ CreateHandleScopeEntry(out_reg, locked_object_handle_scope_offset,
+                                  ManagedRegister::NoRegister(), false);
+      }
+      main_jni_conv->Next();
     }
-    main_jni_conv->Next();
+    if (main_jni_conv->IsCurrentParamInRegister()) {
+      __ GetCurrentThread(main_jni_conv->CurrentParamRegister());
+      __ Call(main_jni_conv->CurrentParamRegister(),
+              Offset(jni_start),
+              main_jni_conv->InterproceduralScratchRegister());
+    } else {
+      __ GetCurrentThread(main_jni_conv->CurrentParamStackOffset(),
+                          main_jni_conv->InterproceduralScratchRegister());
+      __ CallFromThread(jni_start, main_jni_conv->InterproceduralScratchRegister());
+    }
+    if (is_synchronized) {  // Check for exceptions from monitor enter.
+      __ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), main_out_arg_size);
+    }
   }
-  if (main_jni_conv->IsCurrentParamInRegister()) {
-    __ GetCurrentThread(main_jni_conv->CurrentParamRegister());
-    __ Call(main_jni_conv->CurrentParamRegister(),
-            Offset(jni_start),
-            main_jni_conv->InterproceduralScratchRegister());
-  } else {
-    __ GetCurrentThread(main_jni_conv->CurrentParamStackOffset(),
-                        main_jni_conv->InterproceduralScratchRegister());
-    __ CallFromThread(jni_start, main_jni_conv->InterproceduralScratchRegister());
+
+  // Store into stack_frame[saved_cookie_offset] the return value of JniMethodStart.
+  FrameOffset saved_cookie_offset(
+      FrameOffset(0xDEADBEEFu));  // @CriticalNative - use obviously bad value for debugging
+  if (LIKELY(!is_critical_native)) {
+    saved_cookie_offset = main_jni_conv->SavedLocalReferenceCookieOffset();
+    __ Store(saved_cookie_offset, main_jni_conv->IntReturnRegister(), 4 /* sizeof cookie */);
   }
-  if (is_synchronized) {  // Check for exceptions from monitor enter.
-    __ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), main_out_arg_size);
-  }
-  FrameOffset saved_cookie_offset = main_jni_conv->SavedLocalReferenceCookieOffset();
-  __ Store(saved_cookie_offset, main_jni_conv->IntReturnRegister(), 4);
 
   // 7. Iterate over arguments placing values from managed calling convention in
   //    to the convention required for a native call (shuffling). For references
@@ -315,9 +386,13 @@
   for (uint32_t i = 0; i < args_count; ++i) {
     mr_conv->ResetIterator(FrameOffset(frame_size + main_out_arg_size));
     main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
-    main_jni_conv->Next();  // Skip JNIEnv*.
-    if (is_static) {
-      main_jni_conv->Next();  // Skip Class for now.
+
+    // Skip the extra JNI parameters for now.
+    if (LIKELY(!is_critical_native)) {
+      main_jni_conv->Next();    // Skip JNIEnv*.
+      if (is_static) {
+        main_jni_conv->Next();  // Skip Class for now.
+      }
     }
     // Skip to the argument we're interested in.
     for (uint32_t j = 0; j < args_count - i - 1; ++j) {
@@ -326,7 +401,7 @@
     }
     CopyParameter(jni_asm.get(), mr_conv.get(), main_jni_conv.get(), frame_size, main_out_arg_size);
   }
-  if (is_static) {
+  if (is_static && !is_critical_native) {
     // Create argument for Class
     mr_conv->ResetIterator(FrameOffset(frame_size + main_out_arg_size));
     main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
@@ -344,24 +419,30 @@
     }
   }
 
-  // 8. Create 1st argument, the JNI environment ptr.
+  // Set the iterator back to the incoming Method*.
   main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
-  // Register that will hold local indirect reference table
-  if (main_jni_conv->IsCurrentParamInRegister()) {
-    ManagedRegister jni_env = main_jni_conv->CurrentParamRegister();
-    DCHECK(!jni_env.Equals(main_jni_conv->InterproceduralScratchRegister()));
-    __ LoadRawPtrFromThread(jni_env, Thread::JniEnvOffset<kPointerSize>());
-  } else {
-    FrameOffset jni_env = main_jni_conv->CurrentParamStackOffset();
-    __ CopyRawPtrFromThread(jni_env,
-                            Thread::JniEnvOffset<kPointerSize>(),
-                            main_jni_conv->InterproceduralScratchRegister());
+  if (LIKELY(!is_critical_native)) {
+    // 8. Create 1st argument, the JNI environment ptr.
+    // Register that will hold local indirect reference table
+    if (main_jni_conv->IsCurrentParamInRegister()) {
+      ManagedRegister jni_env = main_jni_conv->CurrentParamRegister();
+      DCHECK(!jni_env.Equals(main_jni_conv->InterproceduralScratchRegister()));
+      __ LoadRawPtrFromThread(jni_env, Thread::JniEnvOffset<kPointerSize>());
+    } else {
+      FrameOffset jni_env = main_jni_conv->CurrentParamStackOffset();
+      __ CopyRawPtrFromThread(jni_env,
+                              Thread::JniEnvOffset<kPointerSize>(),
+                              main_jni_conv->InterproceduralScratchRegister());
+    }
   }
 
   // 9. Plant call to native code associated with method.
-  MemberOffset jni_entrypoint_offset = ArtMethod::EntryPointFromJniOffset(
-      InstructionSetPointerSize(instruction_set));
-  __ Call(main_jni_conv->MethodStackOffset(), jni_entrypoint_offset,
+  MemberOffset jni_entrypoint_offset =
+      ArtMethod::EntryPointFromJniOffset(InstructionSetPointerSize(instruction_set));
+  // FIXME: Not sure if MethodStackOffset will work here. What does it even do?
+  __ Call(main_jni_conv->MethodStackOffset(),
+          jni_entrypoint_offset,
+          // XX: Why not the jni conv scratch register?
           mr_conv->InterproceduralScratchRegister());
 
   // 10. Fix differences in result widths.
@@ -377,20 +458,45 @@
     }
   }
 
-  // 11. Save return value
+  // 11. Process return value
   FrameOffset return_save_location = main_jni_conv->ReturnValueSaveLocation();
   if (main_jni_conv->SizeOfReturnValue() != 0 && !reference_return) {
-    if ((instruction_set == kMips || instruction_set == kMips64) &&
-        main_jni_conv->GetReturnType() == Primitive::kPrimDouble &&
-        return_save_location.Uint32Value() % 8 != 0) {
-      // Ensure doubles are 8-byte aligned for MIPS
-      return_save_location = FrameOffset(return_save_location.Uint32Value()
-                                             + static_cast<size_t>(kMipsPointerSize));
+    if (LIKELY(!is_critical_native)) {
+      // For normal JNI, store the return value on the stack because the call to
+      // JniMethodEnd will clobber the return value. It will be restored in (13).
+      if ((instruction_set == kMips || instruction_set == kMips64) &&
+          main_jni_conv->GetReturnType() == Primitive::kPrimDouble &&
+          return_save_location.Uint32Value() % 8 != 0) {
+        // Ensure doubles are 8-byte aligned for MIPS
+        return_save_location = FrameOffset(return_save_location.Uint32Value()
+                                               + static_cast<size_t>(kMipsPointerSize));
+        // TODO: refactor this into the JniCallingConvention code
+        // as a return value alignment requirement.
+      }
+      CHECK_LT(return_save_location.Uint32Value(), frame_size + main_out_arg_size);
+      __ Store(return_save_location,
+               main_jni_conv->ReturnRegister(),
+               main_jni_conv->SizeOfReturnValue());
+    } else {
+      // For @CriticalNative only,
+      // move the JNI return register into the managed return register (if they don't match).
+      ManagedRegister jni_return_reg = main_jni_conv->ReturnRegister();
+      ManagedRegister mr_return_reg = mr_conv->ReturnRegister();
+
+      // Check if the JNI return register matches the managed return register.
+      // If they differ, only then do we have to do anything about it.
+      // Otherwise the return value is already in the right place when we return.
+      if (!jni_return_reg.Equals(mr_return_reg)) {
+        // This is typically only necessary on ARM32 due to native being softfloat
+        // while managed is hardfloat.
+        // -- For example VMOV {r0, r1} -> D0; VMOV r0 -> S0.
+        __ Move(mr_return_reg, jni_return_reg, main_jni_conv->SizeOfReturnValue());
+      } else if (jni_return_reg.IsNoRegister() && mr_return_reg.IsNoRegister()) {
+        // Sanity check: If the return value is passed on the stack for some reason,
+        // then make sure the size matches.
+        CHECK_EQ(main_jni_conv->SizeOfReturnValue(), mr_conv->SizeOfReturnValue());
+      }
     }
-    CHECK_LT(return_save_location.Uint32Value(), frame_size + main_out_arg_size);
-    __ Store(return_save_location,
-             main_jni_conv->ReturnRegister(),
-             main_jni_conv->SizeOfReturnValue());
   }
 
   // Increase frame size for out args if needed by the end_jni_conv.
@@ -398,6 +504,8 @@
   if (end_out_arg_size > current_out_arg_size) {
     size_t out_arg_size_diff = end_out_arg_size - current_out_arg_size;
     current_out_arg_size = end_out_arg_size;
+    // TODO: This is redundant for @CriticalNative but we need to
+    // conditionally do __DecreaseFrameSize below.
     __ IncreaseFrameSize(out_arg_size_diff);
     saved_cookie_offset = FrameOffset(saved_cookie_offset.SizeValue() + out_arg_size_diff);
     locked_object_handle_scope_offset =
@@ -407,65 +515,71 @@
   //     thread.
   end_jni_conv->ResetIterator(FrameOffset(end_out_arg_size));
 
-  ThreadOffset<kPointerSize> jni_end(-1);
-  if (reference_return) {
-    // Pass result.
-    jni_end = is_synchronized
-                  ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndWithReferenceSynchronized)
-                  : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndWithReference);
-    SetNativeParameter(jni_asm.get(), end_jni_conv.get(), end_jni_conv->ReturnRegister());
-    end_jni_conv->Next();
-  } else {
-    jni_end = is_synchronized
-                  ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndSynchronized)
-                  : (is_fast_native
-                         ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastEnd)
-                         : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEnd));
-  }
-  // Pass saved local reference state.
-  if (end_jni_conv->IsCurrentParamOnStack()) {
-    FrameOffset out_off = end_jni_conv->CurrentParamStackOffset();
-    __ Copy(out_off, saved_cookie_offset, end_jni_conv->InterproceduralScratchRegister(), 4);
-  } else {
-    ManagedRegister out_reg = end_jni_conv->CurrentParamRegister();
-    __ Load(out_reg, saved_cookie_offset, 4);
-  }
-  end_jni_conv->Next();
-  if (is_synchronized) {
-    // Pass object for unlocking.
+  if (LIKELY(!is_critical_native)) {
+    // 12. Call JniMethodEnd
+    ThreadOffset<kPointerSize> jni_end(-1);
+    if (reference_return) {
+      // Pass result.
+      jni_end = is_synchronized
+                    ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndWithReferenceSynchronized)
+                    : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndWithReference);
+      SetNativeParameter(jni_asm.get(), end_jni_conv.get(), end_jni_conv->ReturnRegister());
+      end_jni_conv->Next();
+    } else {
+      jni_end = is_synchronized
+                    ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEndSynchronized)
+                    : (is_fast_native
+                           ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastEnd)
+                           : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEnd));
+    }
+    // Pass saved local reference state.
     if (end_jni_conv->IsCurrentParamOnStack()) {
       FrameOffset out_off = end_jni_conv->CurrentParamStackOffset();
-      __ CreateHandleScopeEntry(out_off, locked_object_handle_scope_offset,
-                         end_jni_conv->InterproceduralScratchRegister(),
-                         false);
+      __ Copy(out_off, saved_cookie_offset, end_jni_conv->InterproceduralScratchRegister(), 4);
     } else {
       ManagedRegister out_reg = end_jni_conv->CurrentParamRegister();
-      __ CreateHandleScopeEntry(out_reg, locked_object_handle_scope_offset,
-                         ManagedRegister::NoRegister(), false);
+      __ Load(out_reg, saved_cookie_offset, 4);
     }
     end_jni_conv->Next();
-  }
-  if (end_jni_conv->IsCurrentParamInRegister()) {
-    __ GetCurrentThread(end_jni_conv->CurrentParamRegister());
-    __ Call(end_jni_conv->CurrentParamRegister(),
-            Offset(jni_end),
-            end_jni_conv->InterproceduralScratchRegister());
-  } else {
-    __ GetCurrentThread(end_jni_conv->CurrentParamStackOffset(),
-                        end_jni_conv->InterproceduralScratchRegister());
-    __ CallFromThread(jni_end, end_jni_conv->InterproceduralScratchRegister());
-  }
+    if (is_synchronized) {
+      // Pass object for unlocking.
+      if (end_jni_conv->IsCurrentParamOnStack()) {
+        FrameOffset out_off = end_jni_conv->CurrentParamStackOffset();
+        __ CreateHandleScopeEntry(out_off, locked_object_handle_scope_offset,
+                           end_jni_conv->InterproceduralScratchRegister(),
+                           false);
+      } else {
+        ManagedRegister out_reg = end_jni_conv->CurrentParamRegister();
+        __ CreateHandleScopeEntry(out_reg, locked_object_handle_scope_offset,
+                           ManagedRegister::NoRegister(), false);
+      }
+      end_jni_conv->Next();
+    }
+    if (end_jni_conv->IsCurrentParamInRegister()) {
+      __ GetCurrentThread(end_jni_conv->CurrentParamRegister());
+      __ Call(end_jni_conv->CurrentParamRegister(),
+              Offset(jni_end),
+              end_jni_conv->InterproceduralScratchRegister());
+    } else {
+      __ GetCurrentThread(end_jni_conv->CurrentParamStackOffset(),
+                          end_jni_conv->InterproceduralScratchRegister());
+      __ CallFromThread(jni_end, end_jni_conv->InterproceduralScratchRegister());
+    }
 
-  // 13. Reload return value
-  if (main_jni_conv->SizeOfReturnValue() != 0 && !reference_return) {
-    __ Load(mr_conv->ReturnRegister(), return_save_location, mr_conv->SizeOfReturnValue());
-  }
+    // 13. Reload return value
+    if (main_jni_conv->SizeOfReturnValue() != 0 && !reference_return) {
+      __ Load(mr_conv->ReturnRegister(), return_save_location, mr_conv->SizeOfReturnValue());
+      // NIT: If it's @CriticalNative then we actually only need to do this IF
+      // the calling convention's native return register doesn't match the managed convention's
+      // return register.
+    }
+  }  // if (!is_critical_native)
 
   // 14. Move frame up now we're done with the out arg space.
   __ DecreaseFrameSize(current_out_arg_size);
 
   // 15. Process pending exceptions from JNI call or monitor exit.
-  __ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), 0);
+  __ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), 0 /* stack_adjust */);
 
   // 16. Remove activation - need to restore callee save registers since the GC may have changed
   //     them.
@@ -497,7 +611,8 @@
 static void CopyParameter(JNIMacroAssembler<kPointerSize>* jni_asm,
                           ManagedRuntimeCallingConvention* mr_conv,
                           JniCallingConvention* jni_conv,
-                          size_t frame_size, size_t out_arg_size) {
+                          size_t frame_size,
+                          size_t out_arg_size) {
   bool input_in_reg = mr_conv->IsCurrentParamInRegister();
   bool output_in_reg = jni_conv->IsCurrentParamInRegister();
   FrameOffset handle_scope_offset(0);
diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc
index 1d06f26..0bfcc3f 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.cc
+++ b/compiler/jni/quick/x86/calling_convention_x86.cc
@@ -24,6 +24,7 @@
 namespace x86 {
 
 static_assert(kX86PointerSize == PointerSize::k32, "Unexpected x86 pointer size");
+static_assert(kStackAlignment >= 16u, "IA-32 cdecl requires at least 16 byte stack alignment");
 
 static constexpr ManagedRegister kCalleeSaveRegisters[] = {
     // Core registers.
@@ -190,9 +191,15 @@
 
 // JNI calling convention
 
-X86JniCallingConvention::X86JniCallingConvention(bool is_static, bool is_synchronized,
+X86JniCallingConvention::X86JniCallingConvention(bool is_static,
+                                                 bool is_synchronized,
+                                                 bool is_critical_native,
                                                  const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, shorty, kX86PointerSize) {
+    : JniCallingConvention(is_static,
+                           is_synchronized,
+                           is_critical_native,
+                           shorty,
+                           kX86PointerSize) {
 }
 
 uint32_t X86JniCallingConvention::CoreSpillMask() const {
@@ -204,13 +211,31 @@
 }
 
 size_t X86JniCallingConvention::FrameSize() {
-  // Method*, return address and callee save area size, local reference segment state
-  size_t frame_data_size = static_cast<size_t>(kX86PointerSize) +
-      (2 + CalleeSaveRegisters().size()) * kFramePointerSize;
-  // References plus 2 words for HandleScope header
-  size_t handle_scope_size = HandleScope::SizeOf(kX86PointerSize, ReferenceCount());
+  // Method*, PC return address and callee save area size, local reference segment state
+  const size_t method_ptr_size = static_cast<size_t>(kX86PointerSize);
+  const size_t pc_return_addr_size = kFramePointerSize;
+  const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
+  size_t frame_data_size = method_ptr_size + pc_return_addr_size + callee_save_area_size;
+
+  if (LIKELY(HasLocalReferenceSegmentState())) {                     // local ref. segment state
+    // Local reference segment state is sometimes excluded.
+    frame_data_size += kFramePointerSize;
+  }
+
+  // References plus link_ (pointer) and number_of_references_ (uint32_t) for HandleScope header
+  const size_t handle_scope_size = HandleScope::SizeOf(kX86PointerSize, ReferenceCount());
+
+  size_t total_size = frame_data_size;
+  if (LIKELY(HasHandleScope())) {
+    // HandleScope is sometimes excluded.
+    total_size += handle_scope_size;                                 // handle scope size
+  }
+
   // Plus return value spill area size
-  return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
+  total_size += SizeOfReturnValue();
+
+  return RoundUp(total_size, kStackAlignment);
+  // TODO: Same thing as x64 except using different pointer size. Refactor?
 }
 
 size_t X86JniCallingConvention::OutArgSize() {
@@ -239,11 +264,13 @@
 }
 
 size_t X86JniCallingConvention::NumberOfOutgoingStackArgs() {
-  size_t static_args = IsStatic() ? 1 : 0;  // count jclass
+  size_t static_args = HasSelfClass() ? 1 : 0;  // count jclass
   // regular argument parameters and this
   size_t param_args = NumArgs() + NumLongOrDoubleArgs();
   // count JNIEnv* and return pc (pushed after Method*)
-  size_t total_args = static_args + param_args + 2;
+  size_t internal_args = 1 /* return pc */ + (HasJniEnv() ? 1 : 0 /* jni env */);
+  // No register args.
+  size_t total_args = static_args + param_args + internal_args;
   return total_args;
 }
 
diff --git a/compiler/jni/quick/x86/calling_convention_x86.h b/compiler/jni/quick/x86/calling_convention_x86.h
index ff92fc9..be83cda 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.h
+++ b/compiler/jni/quick/x86/calling_convention_x86.h
@@ -52,9 +52,13 @@
   DISALLOW_COPY_AND_ASSIGN(X86ManagedRuntimeCallingConvention);
 };
 
+// Implements the x86 cdecl calling convention.
 class X86JniCallingConvention FINAL : public JniCallingConvention {
  public:
-  X86JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty);
+  X86JniCallingConvention(bool is_static,
+                          bool is_synchronized,
+                          bool is_critical_native,
+                          const char* shorty);
   ~X86JniCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
index cbf10bd..8ca0ffe 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -25,8 +25,16 @@
 namespace x86_64 {
 
 constexpr size_t kFramePointerSize = static_cast<size_t>(PointerSize::k64);
-
 static_assert(kX86_64PointerSize == PointerSize::k64, "Unexpected x86_64 pointer size");
+static_assert(kStackAlignment >= 16u, "System V AMD64 ABI requires at least 16 byte stack alignment");
+
+// XMM0..XMM7 can be used to pass the first 8 floating args. The rest must go on the stack.
+// -- Managed and JNI calling conventions.
+constexpr size_t kMaxFloatOrDoubleRegisterArguments = 8u;
+// Up to how many integer-like (pointers, objects, longs, int, short, bool, etc) args can be
+// enregistered. The rest of the args must go on the stack.
+// -- JNI calling convention only (Managed excludes RDI, so it's actually 5).
+constexpr size_t kMaxIntLikeRegisterArguments = 6u;
 
 static constexpr ManagedRegister kCalleeSaveRegisters[] = {
     // Core registers.
@@ -130,7 +138,7 @@
     case 3: res = X86_64ManagedRegister::FromCpuRegister(R8); break;
     case 4: res = X86_64ManagedRegister::FromCpuRegister(R9); break;
     }
-  } else if (itr_float_and_doubles_ < 8) {
+  } else if (itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments) {
     // First eight float parameters are passed via XMM0..XMM7
     res = X86_64ManagedRegister::FromXmmRegister(
                                  static_cast<FloatRegister>(XMM0 + itr_float_and_doubles_));
@@ -165,9 +173,15 @@
 
 // JNI calling convention
 
-X86_64JniCallingConvention::X86_64JniCallingConvention(bool is_static, bool is_synchronized,
+X86_64JniCallingConvention::X86_64JniCallingConvention(bool is_static,
+                                                       bool is_synchronized,
+                                                       bool is_critical_native,
                                                        const char* shorty)
-    : JniCallingConvention(is_static, is_synchronized, shorty, kX86_64PointerSize) {
+    : JniCallingConvention(is_static,
+                           is_synchronized,
+                           is_critical_native,
+                           shorty,
+                           kX86_64PointerSize) {
 }
 
 uint32_t X86_64JniCallingConvention::CoreSpillMask() const {
@@ -179,13 +193,30 @@
 }
 
 size_t X86_64JniCallingConvention::FrameSize() {
-  // Method*, return address and callee save area size, local reference segment state
-  size_t frame_data_size = static_cast<size_t>(kX86_64PointerSize) +
-      (2 + CalleeSaveRegisters().size()) * kFramePointerSize;
+  // Method*, PC return address and callee save area size, local reference segment state
+  const size_t method_ptr_size = static_cast<size_t>(kX86_64PointerSize);
+  const size_t pc_return_addr_size = kFramePointerSize;
+  const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
+  size_t frame_data_size = method_ptr_size + pc_return_addr_size + callee_save_area_size;
+
+  if (LIKELY(HasLocalReferenceSegmentState())) {                     // local ref. segment state
+    // Local reference segment state is sometimes excluded.
+    frame_data_size += kFramePointerSize;
+  }
+
   // References plus link_ (pointer) and number_of_references_ (uint32_t) for HandleScope header
-  size_t handle_scope_size = HandleScope::SizeOf(kX86_64PointerSize, ReferenceCount());
+  const size_t handle_scope_size = HandleScope::SizeOf(kX86_64PointerSize, ReferenceCount());
+
+  size_t total_size = frame_data_size;
+  if (LIKELY(HasHandleScope())) {
+    // HandleScope is sometimes excluded.
+    total_size += handle_scope_size;                                 // handle scope size
+  }
+
   // Plus return value spill area size
-  return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
+  total_size += SizeOfReturnValue();
+
+  return RoundUp(total_size, kStackAlignment);
 }
 
 size_t X86_64JniCallingConvention::OutArgSize() {
@@ -214,8 +245,9 @@
     case 3: res = X86_64ManagedRegister::FromCpuRegister(RCX); break;
     case 4: res = X86_64ManagedRegister::FromCpuRegister(R8); break;
     case 5: res = X86_64ManagedRegister::FromCpuRegister(R9); break;
+    static_assert(5u == kMaxIntLikeRegisterArguments - 1, "Missing case statement(s)");
     }
-  } else if (itr_float_and_doubles_ < 8) {
+  } else if (itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments) {
     // First eight float parameters are passed via XMM0..XMM7
     res = X86_64ManagedRegister::FromXmmRegister(
                                  static_cast<FloatRegister>(XMM0 + itr_float_and_doubles_));
@@ -224,24 +256,35 @@
 }
 
 FrameOffset X86_64JniCallingConvention::CurrentParamStackOffset() {
-  size_t offset = itr_args_
-      - std::min(8U, itr_float_and_doubles_)               // Float arguments passed through Xmm0..Xmm7
-      - std::min(6U, itr_args_ - itr_float_and_doubles_);  // Integer arguments passed through GPR
-  return FrameOffset(displacement_.Int32Value() - OutArgSize() + (offset * kFramePointerSize));
+  CHECK(IsCurrentParamOnStack());
+  size_t args_on_stack = itr_args_
+      - std::min(kMaxFloatOrDoubleRegisterArguments,
+                 static_cast<size_t>(itr_float_and_doubles_))
+          // Float arguments passed through Xmm0..Xmm7
+      - std::min(kMaxIntLikeRegisterArguments,
+                 static_cast<size_t>(itr_args_ - itr_float_and_doubles_));
+          // Integer arguments passed through GPR
+  size_t offset = displacement_.Int32Value() - OutArgSize() + (args_on_stack * kFramePointerSize);
+  CHECK_LT(offset, OutArgSize());
+  return FrameOffset(offset);
 }
 
+// TODO: Calling this "NumberArgs" is misleading.
+// It's really more like NumberSlots (like itr_slots_)
+// because doubles/longs get counted twice.
 size_t X86_64JniCallingConvention::NumberOfOutgoingStackArgs() {
-  size_t static_args = IsStatic() ? 1 : 0;  // count jclass
+  size_t static_args = HasSelfClass() ? 1 : 0;  // count jclass
   // regular argument parameters and this
   size_t param_args = NumArgs() + NumLongOrDoubleArgs();
   // count JNIEnv* and return pc (pushed after Method*)
-  size_t total_args = static_args + param_args + 2;
+  size_t internal_args = 1 /* return pc */ + (HasJniEnv() ? 1 : 0 /* jni env */);
+  size_t total_args = static_args + param_args + internal_args;
 
   // Float arguments passed through Xmm0..Xmm7
   // Other (integer) arguments passed through GPR (RDI, RSI, RDX, RCX, R8, R9)
   size_t total_stack_args = total_args
-                            - std::min(8U, static_cast<unsigned int>(NumFloatOrDoubleArgs()))
-                            - std::min(6U, static_cast<unsigned int>(NumArgs() - NumFloatOrDoubleArgs()));
+                            - std::min(kMaxFloatOrDoubleRegisterArguments, static_cast<size_t>(NumFloatOrDoubleArgs()))
+                            - std::min(kMaxIntLikeRegisterArguments, static_cast<size_t>(NumArgs() - NumFloatOrDoubleArgs()));
 
   return total_stack_args;
 }
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.h b/compiler/jni/quick/x86_64/calling_convention_x86_64.h
index b98f505..cdba334 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.h
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.h
@@ -48,7 +48,10 @@
 
 class X86_64JniCallingConvention FINAL : public JniCallingConvention {
  public:
-  X86_64JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty);
+  X86_64JniCallingConvention(bool is_static,
+                             bool is_synchronized,
+                             bool is_critical_native,
+                             const char* shorty);
   ~X86_64JniCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index b1e3811..78e9ca9 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -125,7 +125,8 @@
                                               /* profile_compilation_info */ nullptr));
   }
 
-  bool WriteElf(File* file,
+  bool WriteElf(File* vdex_file,
+                File* oat_file,
                 const std::vector<const DexFile*>& dex_files,
                 SafeMap<std::string, std::string>& key_value_store,
                 bool verify) {
@@ -141,10 +142,11 @@
         return false;
       }
     }
-    return DoWriteElf(file, oat_writer, key_value_store, verify);
+    return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify);
   }
 
-  bool WriteElf(File* file,
+  bool WriteElf(File* vdex_file,
+                File* oat_file,
                 const std::vector<const char*>& dex_filenames,
                 SafeMap<std::string, std::string>& key_value_store,
                 bool verify) {
@@ -155,10 +157,11 @@
         return false;
       }
     }
-    return DoWriteElf(file, oat_writer, key_value_store, verify);
+    return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify);
   }
 
-  bool WriteElf(File* file,
+  bool WriteElf(File* vdex_file,
+                File* oat_file,
                 File&& zip_fd,
                 const char* location,
                 SafeMap<std::string, std::string>& key_value_store,
@@ -168,10 +171,11 @@
     if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
       return false;
     }
-    return DoWriteElf(file, oat_writer, key_value_store, verify);
+    return DoWriteElf(vdex_file, oat_file, oat_writer, key_value_store, verify);
   }
 
-  bool DoWriteElf(File* file,
+  bool DoWriteElf(File* vdex_file,
+                  File* oat_file,
                   OatWriter& oat_writer,
                   SafeMap<std::string, std::string>& key_value_store,
                   bool verify) {
@@ -179,13 +183,13 @@
         compiler_driver_->GetInstructionSet(),
         compiler_driver_->GetInstructionSetFeatures(),
         &compiler_driver_->GetCompilerOptions(),
-        file);
+        oat_file);
     elf_writer->Start();
-    OutputStream* rodata = elf_writer->StartRoData();
+    OutputStream* oat_rodata = elf_writer->StartRoData();
     std::unique_ptr<MemMap> opened_dex_files_map;
     std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-    if (!oat_writer.WriteAndOpenDexFiles(rodata,
-                                         file,
+    if (!oat_writer.WriteAndOpenDexFiles(kIsVdexEnabled ? vdex_file : oat_file,
+                                         oat_rodata,
                                          compiler_driver_->GetInstructionSet(),
                                          compiler_driver_->GetInstructionSetFeatures(),
                                          &key_value_store,
@@ -206,13 +210,13 @@
                                             instruction_set_features_.get());
     oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files, &patcher);
     size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
-    size_t text_size = oat_writer.GetSize() - rodata_size;
+    size_t text_size = oat_writer.GetOatSize() - rodata_size;
     elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize());
 
-    if (!oat_writer.WriteRodata(rodata)) {
+    if (!oat_writer.WriteRodata(oat_rodata)) {
       return false;
     }
-    elf_writer->EndRoData(rodata);
+    elf_writer->EndRoData(oat_rodata);
 
     OutputStream* text = elf_writer->StartText();
     if (!oat_writer.WriteCode(text)) {
@@ -366,17 +370,21 @@
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
   }
 
-  ScratchFile tmp;
+  ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
-  bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store, false);
+  bool success = WriteElf(tmp_vdex.GetFile(),
+                          tmp_oat.GetFile(),
+                          class_linker->GetBootClassPath(),
+                          key_value_store,
+                          false);
   ASSERT_TRUE(success);
 
   if (kCompile) {  // OatWriter strips the code, regenerate to compare
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
-                                                  tmp.GetFilename(),
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+                                                  tmp_oat.GetFilename(),
                                                   nullptr,
                                                   nullptr,
                                                   false,
@@ -498,14 +506,14 @@
   compiler_driver_->SetDexFilesForOatFile(dex_files);
   compiler_driver_->CompileAll(class_loader, dex_files, &timings);
 
-  ScratchFile tmp;
+  ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
-  bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store, false);
+  bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), dex_files, key_value_store, false);
   ASSERT_TRUE(success);
 
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
-                                                  tmp.GetFilename(),
+  std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
+                                                  tmp_oat.GetFilename(),
                                                   nullptr,
                                                   nullptr,
                                                   false,
@@ -513,7 +521,8 @@
                                                   nullptr,
                                                   &error_msg));
   ASSERT_TRUE(oat_file != nullptr);
-  EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength()));
+  EXPECT_LT(static_cast<size_t>(oat_file->Size()),
+            static_cast<size_t>(tmp_oat.GetFile()->GetLength()));
 }
 
 static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>& data) {
@@ -559,10 +568,14 @@
   ASSERT_TRUE(success);
   input_filenames.push_back(dex_file2.GetFilename().c_str());
 
-  ScratchFile oat_file;
+  ScratchFile oat_file, vdex_file(oat_file, ".vdex");
   SafeMap<std::string, std::string> key_value_store;
   key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
-  success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
+  success = WriteElf(vdex_file.GetFile(),
+                     oat_file.GetFile(),
+                     input_filenames,
+                     key_value_store,
+                     verify);
 
   // In verify mode, we expect failure.
   if (verify) {
@@ -668,8 +681,9 @@
     // Test using the AddDexFileSource() interface with the zip file.
     std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() };  // NOLINT [readability/braces] [4]
 
-    ScratchFile oat_file;
-    success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
+    ScratchFile oat_file, vdex_file(oat_file, ".vdex");
+    success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(),
+                       input_filenames, key_value_store, verify);
 
     if (verify) {
       ASSERT_FALSE(success);
@@ -713,8 +727,9 @@
     File zip_fd(dup(zip_file.GetFd()), /* check_usage */ false);
     ASSERT_NE(-1, zip_fd.Fd());
 
-    ScratchFile oat_file;
-    success = WriteElf(oat_file.GetFile(),
+    ScratchFile oat_file, vdex_file(oat_file, ".vdex");
+    success = WriteElf(vdex_file.GetFile(),
+                       oat_file.GetFile(),
                        std::move(zip_fd),
                        zip_file.GetFilename().c_str(),
                        key_value_store,
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index c9c5d24..43e01d5 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -39,6 +39,8 @@
 #include "gc/space/space.h"
 #include "handle_scope-inl.h"
 #include "image_writer.h"
+#include "linker/buffered_output_stream.h"
+#include "linker/file_output_stream.h"
 #include "linker/multi_oat_relative_patcher.h"
 #include "linker/output_stream.h"
 #include "mirror/array.h"
@@ -51,6 +53,7 @@
 #include "scoped_thread_state_change.h"
 #include "type_lookup_table.h"
 #include "utils/dex_cache_arrays_layout-inl.h"
+#include "vdex_file.h"
 #include "verifier/method_verifier.h"
 #include "zip_archive.h"
 
@@ -283,10 +286,13 @@
     image_writer_(nullptr),
     compiling_boot_image_(compiling_boot_image),
     dex_files_(nullptr),
-    size_(0u),
+    vdex_size_(0u),
+    vdex_dex_files_offset_(0u),
+    oat_size_(0u),
     bss_size_(0u),
     oat_data_offset_(0u),
     oat_header_(nullptr),
+    size_vdex_header_(0),
     size_dex_file_alignment_(0),
     size_executable_offset_alignment_(0),
     size_oat_header_(0),
@@ -421,8 +427,8 @@
 }
 
 bool OatWriter::WriteAndOpenDexFiles(
-    OutputStream* rodata,
-    File* file,
+    File* vdex_file,
+    OutputStream* oat_rodata,
     InstructionSet instruction_set,
     const InstructionSetFeatures* instruction_set_features,
     SafeMap<std::string, std::string>* key_value_store,
@@ -431,37 +437,67 @@
     /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
   CHECK(write_state_ == WriteState::kAddingDexFileSources);
 
-  size_t offset = InitOatHeader(instruction_set,
-                                instruction_set_features,
-                                dchecked_integral_cast<uint32_t>(oat_dex_files_.size()),
-                                key_value_store);
-  size_ = InitOatDexFiles(offset);
+  // Record the ELF rodata section offset, i.e. the beginning of the OAT data.
+  if (!RecordOatDataOffset(oat_rodata)) {
+     return false;
+  }
 
   std::unique_ptr<MemMap> dex_files_map;
   std::vector<std::unique_ptr<const DexFile>> dex_files;
-  if (!WriteDexFiles(rodata, file) ||
-      !OpenDexFiles(file, verify, &dex_files_map, &dex_files)) {
-    return false;
+
+  // Initialize VDEX and OAT headers.
+  if (kIsVdexEnabled) {
+    size_vdex_header_ = sizeof(VdexFile::Header);
+    vdex_size_ = size_vdex_header_;
+  }
+  size_t oat_data_offset = InitOatHeader(instruction_set,
+                                        instruction_set_features,
+                                        dchecked_integral_cast<uint32_t>(oat_dex_files_.size()),
+                                        key_value_store);
+  oat_size_ = InitOatDexFiles(oat_data_offset);
+
+  ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get());
+
+  if (kIsVdexEnabled) {
+    std::unique_ptr<BufferedOutputStream> vdex_out(
+        MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(vdex_file)));
+
+    // Write DEX files into VDEX, mmap and open them.
+    if (!WriteDexFiles(vdex_out.get(), vdex_file) ||
+        !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
+      return false;
+    }
+
+    // VDEX is finalized. Seek to the beginning of the file and write the header.
+    if (!WriteVdexHeader(vdex_out.get())) {
+      return false;
+    }
+  } else {
+    // Write DEX files into OAT, mmap and open them.
+    if (!WriteDexFiles(oat_rodata, vdex_file) ||
+        !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) {
+      return false;
+    }
+
+    // Do a bulk checksum update for Dex[]. Doing it piece by piece would be
+    // difficult because we're not using the OutputStream directly.
+    if (!oat_dex_files_.empty()) {
+      size_t size = oat_size_ - oat_dex_files_[0].dex_file_offset_;
+      oat_header_->UpdateChecksum(dex_files_map->Begin(), size);
+    }
   }
 
-  // Do a bulk checksum update for Dex[]. Doing it piece by piece would be
-  // difficult because we're not using the OutputStream directly.
-  if (!oat_dex_files_.empty()) {
-    size_t size = size_ - oat_dex_files_[0].dex_file_offset_;
-    oat_header_->UpdateChecksum(dex_files_map->Begin(), size);
-  }
-
-  ChecksumUpdatingOutputStream checksum_updating_rodata(rodata, oat_header_.get());
-
+  // Write TypeLookupTables into OAT.
   if (!WriteTypeLookupTables(&checksum_updating_rodata, dex_files)) {
     return false;
   }
 
-  // Reserve space for class offsets and update class_offsets_offset_.
+  // Reserve space for class offsets in OAT and update class_offsets_offset_.
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
     oat_dex_file.ReserveClassOffsets(this);
   }
 
+  // Write OatDexFiles into OAT. Needs to be done last, once offsets are collected.
   if (!WriteOatDexFiles(&checksum_updating_rodata)) {
     return false;
   }
@@ -490,7 +526,7 @@
   InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
   CHECK_EQ(instruction_set, oat_header_->GetInstructionSet());
 
-  uint32_t offset = size_;
+  uint32_t offset = oat_size_;
   {
     TimingLogger::ScopedTiming split("InitOatClasses", timings_);
     offset = InitOatClasses(offset);
@@ -507,11 +543,11 @@
     TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_);
     offset = InitOatCodeDexFiles(offset);
   }
-  size_ = offset;
+  oat_size_ = offset;
 
   if (!HasBootImage()) {
     // Allocate space for app dex cache arrays in the .bss section.
-    size_t bss_start = RoundUp(size_, kPageSize);
+    size_t bss_start = RoundUp(oat_size_, kPageSize);
     PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set);
     bss_size_ = 0u;
     for (const DexFile* dex_file : *dex_files_) {
@@ -1587,6 +1623,7 @@
       VLOG(compiler) << #x "=" << PrettySize(x) << " (" << (x) << "B)"; \
       size_total += (x);
 
+    DO_STAT(size_vdex_header_);
     DO_STAT(size_dex_file_alignment_);
     DO_STAT(size_executable_offset_alignment_);
     DO_STAT(size_oat_header_);
@@ -1622,13 +1659,14 @@
     DO_STAT(size_oat_class_method_offsets_);
     #undef DO_STAT
 
-    VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
-    CHECK_EQ(file_offset + size_total, static_cast<size_t>(oat_end_file_offset));
-    CHECK_EQ(size_, size_total);
+    VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)";
+
+    CHECK_EQ(vdex_size_ + oat_size_, size_total);
+    CHECK_EQ(file_offset + size_total - vdex_size_, static_cast<size_t>(oat_end_file_offset));
   }
 
-  CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset));
-  CHECK_EQ(size_, relative_offset);
+  CHECK_EQ(file_offset + oat_size_, static_cast<size_t>(oat_end_file_offset));
+  CHECK_EQ(oat_size_, relative_offset);
 
   write_state_ = WriteState::kWriteHeader;
   return true;
@@ -1831,17 +1869,14 @@
   return true;
 }
 
-bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) {
-  TimingLogger::ScopedTiming split("WriteDexFiles", timings_);
+bool OatWriter::WriteDexFiles(OutputStream* out, File* file) {
+  TimingLogger::ScopedTiming split("Write Dex files", timings_);
 
-  // Get the elf file offset of the oat file.
-  if (!RecordOatDataOffset(rodata)) {
-    return false;
-  }
+  vdex_dex_files_offset_ = vdex_size_;
 
   // Write dex files.
   for (OatDexFile& oat_dex_file : oat_dex_files_) {
-    if (!WriteDexFile(rodata, file, &oat_dex_file)) {
+    if (!WriteDexFile(out, file, &oat_dex_file)) {
       return false;
     }
   }
@@ -1856,45 +1891,50 @@
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file) {
-  if (!SeekToDexFile(rodata, file, oat_dex_file)) {
+bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) {
+  if (!SeekToDexFile(out, file, oat_dex_file)) {
     return false;
   }
   if (oat_dex_file->source_.IsZipEntry()) {
-    if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
+    if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) {
       return false;
     }
   } else if (oat_dex_file->source_.IsRawFile()) {
-    if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
+    if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) {
       return false;
     }
   } else {
     DCHECK(oat_dex_file->source_.IsRawData());
-    if (!WriteDexFile(rodata, oat_dex_file, oat_dex_file->source_.GetRawData())) {
+    if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData())) {
       return false;
     }
   }
 
   // Update current size and account for the written data.
-  DCHECK_EQ(size_, oat_dex_file->dex_file_offset_);
-  size_ += oat_dex_file->dex_file_size_;
+  if (kIsVdexEnabled) {
+    DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_);
+    vdex_size_ += oat_dex_file->dex_file_size_;
+  } else {
+    DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_);
+    oat_size_ += oat_dex_file->dex_file_size_;
+  }
   size_dex_file_ += oat_dex_file->dex_file_size_;
   return true;
 }
 
 bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) {
   // Dex files are required to be 4 byte aligned.
-  size_t original_offset = size_;
-  size_t offset = RoundUp(original_offset, 4);
-  size_dex_file_alignment_ += offset - original_offset;
+  size_t initial_offset = kIsVdexEnabled ? vdex_size_ : oat_size_;
+  size_t start_offset = RoundUp(initial_offset, 4);
+  size_t file_offset = kIsVdexEnabled ? start_offset : (oat_data_offset_ + start_offset);
+  size_dex_file_alignment_ += start_offset - initial_offset;
 
   // Seek to the start of the dex file and flush any pending operations in the stream.
   // Verify that, after flushing the stream, the file is at the same offset as the stream.
-  uint32_t start_offset = oat_data_offset_ + offset;
-  off_t actual_offset = out->Seek(start_offset, kSeekSet);
-  if (actual_offset != static_cast<off_t>(start_offset)) {
+  off_t actual_offset = out->Seek(file_offset, kSeekSet);
+  if (actual_offset != static_cast<off_t>(file_offset)) {
     PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset
-                << " Expected: " << start_offset
+                << " Expected: " << file_offset
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
   }
@@ -1904,24 +1944,28 @@
     return false;
   }
   actual_offset = lseek(file->Fd(), 0, SEEK_CUR);
-  if (actual_offset != static_cast<off_t>(start_offset)) {
+  if (actual_offset != static_cast<off_t>(file_offset)) {
     PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset
-                << " Expected: " << start_offset
+                << " Expected: " << file_offset
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
   }
 
-  size_ = offset;
-  oat_dex_file->dex_file_offset_ = offset;
+  if (kIsVdexEnabled) {
+    vdex_size_ = start_offset;
+  } else {
+    oat_size_ = start_offset;
+  }
+  oat_dex_file->dex_file_offset_ = start_offset;
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* rodata,
+bool OatWriter::WriteDexFile(OutputStream* out,
                              File* file,
                              OatDexFile* oat_dex_file,
                              ZipEntry* dex_file) {
-  size_t start_offset = oat_data_offset_ + size_;
-  DCHECK_EQ(static_cast<off_t>(start_offset), rodata->Seek(0, kSeekCurrent));
+  size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_;
+  DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent));
 
   // Extract the dex file and get the extracted size.
   std::string error_msg;
@@ -1984,13 +2028,13 @@
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
   }
-  actual_offset = rodata->Seek(end_offset, kSeekSet);
+  actual_offset = out->Seek(end_offset, kSeekSet);
   if (actual_offset != static_cast<off_t>(end_offset)) {
     PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset
                 << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation();
     return false;
   }
-  if (!rodata->Flush()) {
+  if (!out->Flush()) {
     PLOG(ERROR) << "Failed to flush stream after seeking over dex file."
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
@@ -2000,7 +2044,8 @@
   if (extracted_size > oat_dex_file->dex_file_size_) {
     if (file->SetLength(end_offset) != 0) {
       PLOG(ERROR) << "Failed to truncate excessive dex file length."
-                  << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
+                  << " File: " << oat_dex_file->GetLocation()
+                  << " Output: " << file->GetPath();
       return false;
     }
   }
@@ -2008,12 +2053,12 @@
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* rodata,
+bool OatWriter::WriteDexFile(OutputStream* out,
                              File* file,
                              OatDexFile* oat_dex_file,
                              File* dex_file) {
-  size_t start_offset = oat_data_offset_ + size_;
-  DCHECK_EQ(static_cast<off_t>(start_offset), rodata->Seek(0, kSeekCurrent));
+  size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_;
+  DCHECK_EQ(static_cast<off_t>(start_offset), out->Seek(0, kSeekCurrent));
 
   off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET);
   if (input_offset != static_cast<off_t>(0)) {
@@ -2047,13 +2092,13 @@
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
   }
-  actual_offset = rodata->Seek(end_offset, kSeekSet);
+  actual_offset = out->Seek(end_offset, kSeekSet);
   if (actual_offset != static_cast<off_t>(end_offset)) {
     PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset
                 << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation();
     return false;
   }
-  if (!rodata->Flush()) {
+  if (!out->Flush()) {
     PLOG(ERROR) << "Failed to flush stream after seeking over dex file."
                 << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath();
     return false;
@@ -2062,7 +2107,7 @@
   return true;
 }
 
-bool OatWriter::WriteDexFile(OutputStream* rodata,
+bool OatWriter::WriteDexFile(OutputStream* out,
                              OatDexFile* oat_dex_file,
                              const uint8_t* dex_file) {
   // Note: The raw data has already been checked to contain the header
@@ -2071,12 +2116,12 @@
   DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation()));
   const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file);
 
-  if (!rodata->WriteFully(dex_file, header->file_size_)) {
+  if (!out->WriteFully(dex_file, header->file_size_)) {
     PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation()
-                << " to " << rodata->GetLocation();
+                << " to " << out->GetLocation();
     return false;
   }
-  if (!rodata->Flush()) {
+  if (!out->Flush()) {
     PLOG(ERROR) << "Failed to flush stream after writing dex file."
                 << " File: " << oat_dex_file->GetLocation();
     return false;
@@ -2146,16 +2191,18 @@
   }
 
   size_t map_offset = oat_dex_files_[0].dex_file_offset_;
-  size_t length = size_ - map_offset;
+  size_t length = kIsVdexEnabled ? (vdex_size_ - map_offset) : (oat_size_ - map_offset);
+
   std::string error_msg;
-  std::unique_ptr<MemMap> dex_files_map(MemMap::MapFile(length,
-                                                        PROT_READ | PROT_WRITE,
-                                                        MAP_SHARED,
-                                                        file->Fd(),
-                                                        oat_data_offset_ + map_offset,
-                                                        /* low_4gb */ false,
-                                                        file->GetPath().c_str(),
-                                                        &error_msg));
+  std::unique_ptr<MemMap> dex_files_map(MemMap::MapFile(
+      length,
+      PROT_READ | PROT_WRITE,
+      MAP_SHARED,
+      file->Fd(),
+      kIsVdexEnabled ? map_offset : (oat_data_offset_ + map_offset),
+      /* low_4gb */ false,
+      file->GetPath().c_str(),
+      &error_msg));
   if (dex_files_map == nullptr) {
     LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath()
                << " error: " << error_msg;
@@ -2210,10 +2257,18 @@
 }
 
 bool OatWriter::WriteTypeLookupTables(
-    OutputStream* rodata,
+    OutputStream* oat_rodata,
     const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) {
   TimingLogger::ScopedTiming split("WriteTypeLookupTables", timings_);
 
+  uint32_t expected_offset = oat_data_offset_ + oat_size_;
+  off_t actual_offset = oat_rodata->Seek(expected_offset, kSeekSet);
+  if (static_cast<uint32_t>(actual_offset) != expected_offset) {
+    PLOG(ERROR) << "Failed to seek to TypeLookupTable section. Actual: " << actual_offset
+                << " Expected: " << expected_offset << " File: " << oat_rodata->GetLocation();
+    return false;
+  }
+
   DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size());
   for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) {
     OatDexFile* oat_dex_file = &oat_dex_files_[i];
@@ -2235,41 +2290,58 @@
     TypeLookupTable* table = opened_dex_files[i]->GetTypeLookupTable();
 
     // Type tables are required to be 4 byte aligned.
-    size_t original_offset = size_;
-    size_t rodata_offset = RoundUp(original_offset, 4);
-    size_t padding_size = rodata_offset - original_offset;
+    size_t initial_offset = oat_size_;
+    size_t rodata_offset = RoundUp(initial_offset, 4);
+    size_t padding_size = rodata_offset - initial_offset;
 
     if (padding_size != 0u) {
       std::vector<uint8_t> buffer(padding_size, 0u);
-      if (!rodata->WriteFully(buffer.data(), padding_size)) {
+      if (!oat_rodata->WriteFully(buffer.data(), padding_size)) {
         PLOG(ERROR) << "Failed to write lookup table alignment padding."
                     << " File: " << oat_dex_file->GetLocation()
-                    << " Output: " << rodata->GetLocation();
+                    << " Output: " << oat_rodata->GetLocation();
         return false;
       }
     }
 
     DCHECK_EQ(oat_data_offset_ + rodata_offset,
-              static_cast<size_t>(rodata->Seek(0u, kSeekCurrent)));
+              static_cast<size_t>(oat_rodata->Seek(0u, kSeekCurrent)));
     DCHECK_EQ(table_size, table->RawDataLength());
 
-    if (!rodata->WriteFully(table->RawData(), table_size)) {
+    if (!oat_rodata->WriteFully(table->RawData(), table_size)) {
       PLOG(ERROR) << "Failed to write lookup table."
                   << " File: " << oat_dex_file->GetLocation()
-                  << " Output: " << rodata->GetLocation();
+                  << " Output: " << oat_rodata->GetLocation();
       return false;
     }
 
     oat_dex_file->lookup_table_offset_ = rodata_offset;
 
-    size_ += padding_size + table_size;
+    oat_size_ += padding_size + table_size;
     size_oat_lookup_table_ += table_size;
     size_oat_lookup_table_alignment_ += padding_size;
   }
 
-  if (!rodata->Flush()) {
+  if (!oat_rodata->Flush()) {
     PLOG(ERROR) << "Failed to flush stream after writing type lookup tables."
-                << " File: " << rodata->GetLocation();
+                << " File: " << oat_rodata->GetLocation();
+    return false;
+  }
+
+  return true;
+}
+
+bool OatWriter::WriteVdexHeader(OutputStream* vdex_out) {
+  off_t actual_offset = vdex_out->Seek(0, kSeekSet);
+  if (actual_offset != 0) {
+    PLOG(ERROR) << "Failed to seek to the beginning of vdex file. Actual: " << actual_offset
+                << " File: " << vdex_out->GetLocation();
+    return false;
+  }
+
+  VdexFile::Header vdex_header;
+  if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) {
+    PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation();
     return false;
   }
 
@@ -2329,11 +2401,11 @@
   DCHECK_EQ(class_offsets_offset_, 0u);
   if (!class_offsets_.empty()) {
     // Class offsets are required to be 4 byte aligned.
-    size_t original_offset = oat_writer->size_;
-    size_t offset = RoundUp(original_offset, 4);
-    oat_writer->size_oat_class_offsets_alignment_ += offset - original_offset;
+    size_t initial_offset = oat_writer->oat_size_;
+    size_t offset = RoundUp(initial_offset, 4);
+    oat_writer->size_oat_class_offsets_alignment_ += offset - initial_offset;
     class_offsets_offset_ = offset;
-    oat_writer->size_ = offset + GetClassOffsetsRawSize();
+    oat_writer->oat_size_ = offset + GetClassOffsetsRawSize();
   }
 }
 
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 93e2e44..77525f1 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -57,11 +57,6 @@
 // ...
 // OatDexFile[D]
 //
-// Dex[0]            one variable sized DexFile for each OatDexFile.
-// Dex[1]            these are literal copies of the input .dex files.
-// ...
-// Dex[D]
-//
 // TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile.
 // TypeLookupTable[1]
 // ...
@@ -142,11 +137,12 @@
       CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
   dchecked_vector<const char*> GetSourceLocations() const;
 
-  // Write raw dex files to the .rodata section and open them from the oat file. The verify
-  // setting dictates whether the dex file verifier should check the dex files. This is generally
-  // the case, and should only be false for tests.
-  bool WriteAndOpenDexFiles(OutputStream* rodata,
-                            File* file,
+  // Write raw dex files to the vdex file, mmap the file and open the dex files from it.
+  // Supporting data structures are written into the .rodata section of the oat file.
+  // The `verify` setting dictates whether the dex file verifier should check the dex files.
+  // This is generally the case, and should only be false for tests.
+  bool WriteAndOpenDexFiles(File* vdex_file,
+                            OutputStream* oat_rodata,
                             InstructionSet instruction_set,
                             const InstructionSetFeatures* instruction_set_features,
                             SafeMap<std::string, std::string>* key_value_store,
@@ -183,8 +179,8 @@
     return *oat_header_;
   }
 
-  size_t GetSize() const {
-    return size_;
+  size_t GetOatSize() const {
+    return oat_size_;
   }
 
   size_t GetBssSize() const {
@@ -236,6 +232,25 @@
   // with a given DexMethodVisitor.
   bool VisitDexMethods(DexMethodVisitor* visitor);
 
+  bool WriteVdexHeader(OutputStream* vdex_out);
+
+  bool WriteDexFiles(OutputStream* out, File* file);
+  bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
+  bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file);
+  bool WriteDexFile(OutputStream* out,
+                    File* file,
+                    OatDexFile* oat_dex_file,
+                    ZipEntry* dex_file);
+  bool WriteDexFile(OutputStream* out,
+                    File* file,
+                    OatDexFile* oat_dex_file,
+                    File* dex_file);
+  bool WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, const uint8_t* dex_file);
+  bool OpenDexFiles(File* file,
+                    bool verify,
+                    /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
+                    /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
+
   size_t InitOatHeader(InstructionSet instruction_set,
                        const InstructionSetFeatures* instruction_set_features,
                        uint32_t num_dex_files,
@@ -253,20 +268,10 @@
   size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
 
   bool RecordOatDataOffset(OutputStream* out);
-  bool ReadDexFileHeader(File* file, OatDexFile* oat_dex_file);
+  bool ReadDexFileHeader(File* oat_file, OatDexFile* oat_dex_file);
   bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location);
-  bool WriteDexFiles(OutputStream* rodata, File* file);
-  bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file);
-  bool SeekToDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file);
-  bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file);
-  bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, File* dex_file);
-  bool WriteDexFile(OutputStream* rodata, OatDexFile* oat_dex_file, const uint8_t* dex_file);
-  bool WriteOatDexFiles(OutputStream* rodata);
-  bool OpenDexFiles(File* file,
-                    bool verify,
-                    /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
-                    /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
-  bool WriteTypeLookupTables(OutputStream* rodata,
+  bool WriteOatDexFiles(OutputStream* oat_rodata);
+  bool WriteTypeLookupTables(OutputStream* oat_rodata,
                              const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
   bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
   void SetMultiOatRelativePatcherAdjustment();
@@ -300,8 +305,14 @@
   // note OatFile does not take ownership of the DexFiles
   const std::vector<const DexFile*>* dex_files_;
 
+  // Size required for Vdex data structures.
+  size_t vdex_size_;
+
+  // Offset of section holding Dex files inside Vdex.
+  size_t vdex_dex_files_offset_;
+
   // Size required for Oat data structures.
-  size_t size_;
+  size_t oat_size_;
 
   // The size of the required .bss section holding the DexCache data.
   size_t bss_size_;
@@ -324,6 +335,7 @@
   std::unique_ptr<const std::vector<uint8_t>> quick_to_interpreter_bridge_;
 
   // output stats
+  uint32_t size_vdex_header_;
   uint32_t size_dex_file_alignment_;
   uint32_t size_executable_offset_alignment_;
   uint32_t size_oat_header_;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index b0de964..2087888 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -283,8 +283,7 @@
 }
 
 void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
-                                             size_t maximum_number_of_live_core_registers,
-                                             size_t maximum_number_of_live_fpu_registers,
+                                             size_t maximum_safepoint_spill_size,
                                              size_t number_of_out_slots,
                                              const ArenaVector<HBasicBlock*>& block_order) {
   block_order_ = &block_order;
@@ -298,14 +297,12 @@
       && !HasAllocatedCalleeSaveRegisters()
       && IsLeafMethod()
       && !RequiresCurrentMethod()) {
-    DCHECK_EQ(maximum_number_of_live_core_registers, 0u);
-    DCHECK_EQ(maximum_number_of_live_fpu_registers, 0u);
+    DCHECK_EQ(maximum_safepoint_spill_size, 0u);
     SetFrameSize(CallPushesPC() ? GetWordSize() : 0);
   } else {
     SetFrameSize(RoundUp(
         first_register_slot_in_slow_path_
-        + maximum_number_of_live_core_registers * GetWordSize()
-        + maximum_number_of_live_fpu_registers * GetFloatingPointSpillSlotSize()
+        + maximum_safepoint_spill_size
         + FrameEntrySpillSize(),
         kStackAlignment));
   }
@@ -765,21 +762,16 @@
   LocationSummary* locations = instruction->GetLocations();
 
   uint32_t register_mask = locations->GetRegisterMask();
-  if (instruction->IsSuspendCheck()) {
-    // Suspend check has special ABI that saves the caller-save registers in callee,
-    // so we want to emit stack maps containing the registers.
-    // TODO: Register allocator still reserves space for the caller-save registers.
-    // We should add slow-path-specific caller-save information into LocationSummary
-    // and refactor the code here as well as in the register allocator to use it.
+  DCHECK_EQ(register_mask & ~locations->GetLiveRegisters()->GetCoreRegisters(), 0u);
+  if (locations->OnlyCallsOnSlowPath()) {
+    // In case of slow path, we currently set the location of caller-save registers
+    // to register (instead of their stack location when pushed before the slow-path
+    // call). Therefore register_mask contains both callee-save and caller-save
+    // registers that hold objects. We must remove the spilled caller-save from the
+    // mask, since they will be overwritten by the callee.
+    uint32_t spills = GetSlowPathSpills(locations, /* core_registers */ true);
+    register_mask &= ~spills;
   } else {
-    if (locations->OnlyCallsOnSlowPath()) {
-      // In case of slow path, we currently set the location of caller-save registers
-      // to register (instead of their stack location when pushed before the slow-path
-      // call). Therefore register_mask contains both callee-save and caller-save
-      // registers that hold objects. We must remove the caller-save from the mask, since
-      // they will be overwritten by the callee.
-      register_mask &= core_callee_save_mask_;
-    }
     // The register mask must be a subset of callee-save registers.
     DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask);
   }
@@ -1236,58 +1228,44 @@
 }
 
 void SlowPathCode::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
-  RegisterSet* live_registers = locations->GetLiveRegisters();
   size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
 
-  for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
-    if (!codegen->IsCoreCalleeSaveRegister(i)) {
-      if (live_registers->ContainsCoreRegister(i)) {
-        // If the register holds an object, update the stack mask.
-        if (locations->RegisterContainsObject(i)) {
-          locations->SetStackBit(stack_offset / kVRegSize);
-        }
-        DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-        DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-        saved_core_stack_offsets_[i] = stack_offset;
-        stack_offset += codegen->SaveCoreRegister(stack_offset, i);
-      }
+  const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
+  for (uint32_t i : LowToHighBits(core_spills)) {
+    // If the register holds an object, update the stack mask.
+    if (locations->RegisterContainsObject(i)) {
+      locations->SetStackBit(stack_offset / kVRegSize);
     }
+    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+    saved_core_stack_offsets_[i] = stack_offset;
+    stack_offset += codegen->SaveCoreRegister(stack_offset, i);
   }
 
-  for (size_t i = 0, e = codegen->GetNumberOfFloatingPointRegisters(); i < e; ++i) {
-    if (!codegen->IsFloatingPointCalleeSaveRegister(i)) {
-      if (live_registers->ContainsFloatingPointRegister(i)) {
-        DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-        DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-        saved_fpu_stack_offsets_[i] = stack_offset;
-        stack_offset += codegen->SaveFloatingPointRegister(stack_offset, i);
-      }
-    }
+  const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
+  for (size_t i : LowToHighBits(fp_spills)) {
+    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+    saved_fpu_stack_offsets_[i] = stack_offset;
+    stack_offset += codegen->SaveFloatingPointRegister(stack_offset, i);
   }
 }
 
 void SlowPathCode::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
-  RegisterSet* live_registers = locations->GetLiveRegisters();
   size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
 
-  for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
-    if (!codegen->IsCoreCalleeSaveRegister(i)) {
-      if (live_registers->ContainsCoreRegister(i)) {
-        DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-        DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-        stack_offset += codegen->RestoreCoreRegister(stack_offset, i);
-      }
-    }
+  const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
+  for (uint32_t i : LowToHighBits(core_spills)) {
+    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+    stack_offset += codegen->RestoreCoreRegister(stack_offset, i);
   }
 
-  for (size_t i = 0, e = codegen->GetNumberOfFloatingPointRegisters(); i < e; ++i) {
-    if (!codegen->IsFloatingPointCalleeSaveRegister(i)) {
-      if (live_registers->ContainsFloatingPointRegister(i)) {
-        DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
-        DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
-        stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, i);
-      }
-    }
+  const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
+  for (size_t i : LowToHighBits(fp_spills)) {
+    DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+    DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+    stack_offset += codegen->RestoreFloatingPointRegister(stack_offset, i);
   }
 }
 
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 78a8afb..0c60a98 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -22,6 +22,7 @@
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
 #include "base/bit_field.h"
+#include "base/bit_utils.h"
 #include "base/enums.h"
 #include "globals.h"
 #include "graph_visualizer.h"
@@ -211,8 +212,7 @@
   virtual size_t GetFloatingPointSpillSlotSize() const = 0;
   virtual uintptr_t GetAddressOf(HBasicBlock* block) = 0;
   void InitializeCodeGeneration(size_t number_of_spill_slots,
-                                size_t maximum_number_of_live_core_registers,
-                                size_t maximum_number_of_live_fpu_registers,
+                                size_t maximum_safepoint_spill_size,
                                 size_t number_of_out_slots,
                                 const ArenaVector<HBasicBlock*>& block_order);
   // Backends can override this as necessary. For most, no special alignment is required.
@@ -278,6 +278,30 @@
     return (fpu_callee_save_mask_ & (1 << reg)) != 0;
   }
 
+  uint32_t GetSlowPathSpills(LocationSummary* locations, bool core_registers) const {
+    DCHECK(locations->OnlyCallsOnSlowPath() ||
+           (locations->Intrinsified() && locations->CallsOnMainAndSlowPath() &&
+               !locations->HasCustomSlowPathCallingConvention()));
+    uint32_t live_registers = core_registers
+        ? locations->GetLiveRegisters()->GetCoreRegisters()
+        : locations->GetLiveRegisters()->GetFloatingPointRegisters();
+    if (locations->HasCustomSlowPathCallingConvention()) {
+      // Save only the live registers that the custom calling convention wants us to save.
+      uint32_t caller_saves = core_registers
+          ? locations->GetCustomSlowPathCallerSaves().GetCoreRegisters()
+          : locations->GetCustomSlowPathCallerSaves().GetFloatingPointRegisters();
+      return live_registers & caller_saves;
+    } else {
+      // Default ABI, we need to spill non-callee-save live registers.
+      uint32_t callee_saves = core_registers ? core_callee_save_mask_ : fpu_callee_save_mask_;
+      return live_registers & ~callee_saves;
+    }
+  }
+
+  size_t GetNumberOfSlowPathSpills(LocationSummary* locations, bool core_registers) const {
+    return POPCOUNT(GetSlowPathSpills(locations, core_registers));
+  }
+
   // Record native to dex mapping for a suspend point.  Required by runtime.
   void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
   // Check whether we have already recorded mapping at this PC.
@@ -600,7 +624,7 @@
     return POPCOUNT(core_spill_mask_) * GetWordSize();
   }
 
-  bool HasAllocatedCalleeSaveRegisters() const {
+  virtual bool HasAllocatedCalleeSaveRegisters() const {
     // We check the core registers against 1 because it always comprises the return PC.
     return (POPCOUNT(allocated_registers_.GetCoreRegisters() & core_callee_save_mask_) != 1)
       || (POPCOUNT(allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_) != 0);
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 16072d9..e76190f 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -247,35 +247,6 @@
   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM);
 };
 
-class LoadStringSlowPathARM : public SlowPathCode {
- public:
-  explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCode(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-
-    CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
-    __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index);
-    arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-    arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
-
-    RestoreLiveRegisters(codegen, locations);
-    __ b(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM);
-};
-
 class TypeCheckSlowPathARM : public SlowPathCode {
  public:
   TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal)
@@ -344,7 +315,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, instruction_->GetLocations());
     arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickDeoptimize, void, void>();
   }
@@ -1532,6 +1502,7 @@
 void LocationsBuilderARM::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -3959,6 +3930,9 @@
                                                    object_field_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
 
   bool volatile_for_double = field_info.IsVolatile()
@@ -4435,6 +4409,9 @@
                                                    object_array_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -4621,7 +4598,6 @@
   }
   if (needs_write_barrier) {
     // Temporary registers for the write barrier.
-    // These registers may be used for Baker read barriers too.
     locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
     locations->AddTemp(Location::RequiresRegister());
   }
@@ -4739,127 +4715,42 @@
           __ Bind(&non_zero);
         }
 
-        if (kEmitCompilerReadBarrier) {
-          if (!kUseBakerReadBarrier) {
-            // When (non-Baker) read barriers are enabled, the type
-            // checking instrumentation requires two read barriers
-            // generated by CodeGeneratorARM::GenerateReadBarrierSlow:
-            //
-            //   __ Mov(temp2, temp1);
-            //   // /* HeapReference<Class> */ temp1 = temp1->component_type_
-            //   __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp1_loc, temp1_loc, temp2_loc, component_offset);
-            //
-            //   // /* HeapReference<Class> */ temp2 = value->klass_
-            //   __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc);
-            //
-            //   __ cmp(temp1, ShifterOperand(temp2));
-            //
-            // However, the second read barrier may trash `temp`, as it
-            // is a temporary register, and as such would not be saved
-            // along with live registers before calling the runtime (nor
-            // restored afterwards).  So in this case, we bail out and
-            // delegate the work to the array set slow path.
-            //
-            // TODO: Extend the register allocator to support a new
-            // "(locally) live temp" location so as to avoid always
-            // going into the slow path when read barriers are enabled?
-            //
-            // There is no such problem with Baker read barriers (see below).
-            __ b(slow_path->GetEntryLabel());
-          } else {
-            Register temp3 = IP;
-            Location temp3_loc = Location::RegisterLocation(temp3);
+        // Note that when read barriers are enabled, the type checks
+        // are performed without read barriers.  This is fine, even in
+        // the case where a class object is in the from-space after
+        // the flip, as a comparison involving such a type would not
+        // produce a false positive; it may of course produce a false
+        // negative, in which case we would take the ArraySet slow
+        // path.
 
-            // Note: `temp3` (scratch register IP) cannot be used as
-            // `ref` argument of GenerateFieldLoadWithBakerReadBarrier
-            // calls below (see ReadBarrierMarkSlowPathARM for more
-            // details).
+        // /* HeapReference<Class> */ temp1 = array->klass_
+        __ LoadFromOffset(kLoadWord, temp1, array, class_offset);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ MaybeUnpoisonHeapReference(temp1);
 
-            // /* HeapReference<Class> */ temp1 = array->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                            temp1_loc,
-                                                            array,
-                                                            class_offset,
-                                                            temp3_loc,
-                                                            /* needs_null_check */ true);
+        // /* HeapReference<Class> */ temp1 = temp1->component_type_
+        __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
+        // /* HeapReference<Class> */ temp2 = value->klass_
+        __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
+        // If heap poisoning is enabled, no need to unpoison `temp1`
+        // nor `temp2`, as we are comparing two poisoned references.
+        __ cmp(temp1, ShifterOperand(temp2));
 
-            // /* HeapReference<Class> */ temp1 = temp1->component_type_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                            temp1_loc,
-                                                            temp1,
-                                                            component_offset,
-                                                            temp3_loc,
-                                                            /* needs_null_check */ false);
-            // Register `temp1` is not trashed by the read barrier
-            // emitted by GenerateFieldLoadWithBakerReadBarrier below,
-            // as that method produces a call to a ReadBarrierMarkRegX
-            // entry point, which saves all potentially live registers,
-            // including temporaries such a `temp1`.
-            // /* HeapReference<Class> */ temp2 = value->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                            temp2_loc,
-                                                            value,
-                                                            class_offset,
-                                                            temp3_loc,
-                                                            /* needs_null_check */ false);
-            // If heap poisoning is enabled, `temp1` and `temp2` have
-            // been unpoisoned by the the previous calls to
-            // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
-            __ cmp(temp1, ShifterOperand(temp2));
-
-            if (instruction->StaticTypeOfArrayIsObjectArray()) {
-              Label do_put;
-              __ b(&do_put, EQ);
-              // We do not need to emit a read barrier for the
-              // following heap reference load, as `temp1` is only used
-              // in a comparison with null below, and this reference
-              // is not kept afterwards.
-              // /* HeapReference<Class> */ temp1 = temp1->super_class_
-              __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
-              // If heap poisoning is enabled, no need to unpoison
-              // `temp`, as we are comparing against null below.
-              __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
-              __ Bind(&do_put);
-            } else {
-              __ b(slow_path->GetEntryLabel(), NE);
-            }
-          }
-        } else {
-          // Non read barrier code.
-
-          // /* HeapReference<Class> */ temp1 = array->klass_
-          __ LoadFromOffset(kLoadWord, temp1, array, class_offset);
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
+        if (instruction->StaticTypeOfArrayIsObjectArray()) {
+          Label do_put;
+          __ b(&do_put, EQ);
+          // If heap poisoning is enabled, the `temp1` reference has
+          // not been unpoisoned yet; unpoison it now.
           __ MaybeUnpoisonHeapReference(temp1);
 
-          // /* HeapReference<Class> */ temp1 = temp1->component_type_
-          __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
-          // /* HeapReference<Class> */ temp2 = value->klass_
-          __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
-          // If heap poisoning is enabled, no need to unpoison `temp1`
-          // nor `temp2`, as we are comparing two poisoned references.
-          __ cmp(temp1, ShifterOperand(temp2));
-
-          if (instruction->StaticTypeOfArrayIsObjectArray()) {
-            Label do_put;
-            __ b(&do_put, EQ);
-            // If heap poisoning is enabled, the `temp1` reference has
-            // not been unpoisoned yet; unpoison it now.
-            __ MaybeUnpoisonHeapReference(temp1);
-
-            // /* HeapReference<Class> */ temp1 = temp1->super_class_
-            __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
-            // If heap poisoning is enabled, no need to unpoison
-            // `temp1`, as we are comparing against null below.
-            __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
-            __ Bind(&do_put);
-          } else {
-            __ b(slow_path->GetEntryLabel(), NE);
-          }
+          // /* HeapReference<Class> */ temp1 = temp1->super_class_
+          __ LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
+          // If heap poisoning is enabled, no need to unpoison
+          // `temp1`, as we are comparing against null below.
+          __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
+          __ Bind(&do_put);
+        } else {
+          __ b(slow_path->GetEntryLabel(), NE);
         }
       }
 
@@ -5054,7 +4945,9 @@
 }
 
 void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) {
-  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5329,17 +5222,6 @@
 
 HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -5381,10 +5263,15 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
+
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
@@ -5406,6 +5293,7 @@
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
 
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -5413,18 +5301,21 @@
       DCHECK(!cls->MustGenerateClinitCheck());
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       Register current_method = locations->InAt(0).AsRegister<Register>();
-      GenerateGcRootFieldLoad(
-          cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              current_method,
+                              ArtMethod::DeclaringClassOffset().Int32Value(),
+                              requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
                                                                     cls->GetTypeIndex()));
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       CodeGeneratorARM::PcRelativePatchInfo* labels =
           codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
       __ BindTrackedLabel(&labels->movw_label);
@@ -5436,7 +5327,7 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
@@ -5456,7 +5347,7 @@
       uint32_t offset = address & MaxInt<uint32_t>(offset_bits);
       __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address));
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)
-      GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+      GenerateGcRootFieldLoad(cls, out_loc, out, offset, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5465,7 +5356,7 @@
       HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
       int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
       // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
-      GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset);
+      GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5479,7 +5370,7 @@
                         ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
       // /* GcRoot<mirror::Class> */ out = out[type_index]
       size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
-      GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+      GenerateGcRootFieldLoad(cls, out_loc, out, offset, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
     }
   }
@@ -5531,17 +5422,6 @@
 
 HLoadString::LoadKind CodeGeneratorARM::GetSupportedLoadStringKind(
     HLoadString::LoadKind desired_string_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_string_load_kind) {
-      case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadString::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadString::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
       DCHECK(!GetCompilerOptions().GetCompilePic());
@@ -5571,32 +5451,34 @@
 }
 
 void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? LocationSummary::kCallOnSlowPath
+  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+      ? LocationSummary::kCallOnMainOnly
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
+
   HLoadString::LoadKind load_kind = load->GetLoadKind();
-  if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
-      load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
+  DCHECK(load_kind != HLoadString::LoadKind::kDexCachePcRelative) << "Not supported";
+  if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
     locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetOut(Location::RegisterLocation(R0));
+  } else {
+    locations->SetOut(Location::RequiresRegister());
   }
-  locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
   LocationSummary* locations = load->GetLocations();
   Location out_loc = locations->Out();
   Register out = out_loc.AsRegister<Register>();
+  HLoadString::LoadKind load_kind = load->GetLoadKind();
 
-  switch (load->GetLoadKind()) {
+  switch (load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       __ LoadLiteral(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
                                                                       load->GetStringIndex()));
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
       CodeGeneratorARM::PcRelativePatchInfo* labels =
           codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
       __ BindTrackedLabel(&labels->movw_label);
@@ -5608,7 +5490,6 @@
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       DCHECK_NE(load->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
       __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
@@ -5618,11 +5499,12 @@
       break;
   }
 
-  // TODO: Re-add the compiler code to do string dex cache lookup again.
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
-  codegen_->AddSlowPath(slow_path);
-  __ b(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  // TODO: Consider re-adding the compiler code to do string dex cache lookup again.
+  DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
+  InvokeRuntimeCallingConvention calling_convention;
+  __ LoadImmediate(calling_convention.GetRegisterAt(0), load->GetStringIndex());
+  codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
 }
 
 static int32_t GetExceptionTlsOffset() {
@@ -5672,6 +5554,7 @@
 void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) {
   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -5679,6 +5562,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind =
           kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
+      baker_read_barrier_slow_path = kUseBakerReadBarrier;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -5688,6 +5572,9 @@
   }
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // The "out" register is used as a temporary, so it overlaps with the inputs.
@@ -5858,6 +5745,7 @@
   bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
 
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -5866,6 +5754,7 @@
       call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
           LocationSummary::kCallOnSlowPath :
           LocationSummary::kNoCall;  // In fact, call on a fatal (non-returning) slow path.
+      baker_read_barrier_slow_path = kUseBakerReadBarrier && !throws_into_catch;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -5875,6 +5764,9 @@
   }
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Note that TypeCheckSlowPathARM uses this "temp" register too.
@@ -6377,9 +6269,11 @@
 void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction,
                                                           Location root,
                                                           Register obj,
-                                                          uint32_t offset) {
+                                                          uint32_t offset,
+                                                          bool requires_read_barrier) {
   Register root_reg = root.AsRegister<Register>();
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index ac10e23..ce9d7e6 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -271,11 +271,12 @@
   //
   //   root <- *(obj + offset)
   //
-  // while honoring read barriers (if any).
+  // while honoring read barriers if requires_read_barrier is true.
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                Register obj,
-                               uint32_t offset);
+                               uint32_t offset,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
   void GenerateTestAndBranch(HInstruction* instruction,
                              size_t condition_input_index,
                              Label* true_target,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 933f3e6..64231ad 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -336,36 +336,6 @@
   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64);
 };
 
-class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
- public:
-  explicit LoadStringSlowPathARM64(HLoadString* instruction) : SlowPathCodeARM64(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
-
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
-    __ Mov(calling_convention.GetRegisterAt(0).W(), string_index);
-    arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-    Primitive::Type type = instruction_->GetType();
-    arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type);
-
-    RestoreLiveRegisters(codegen, locations);
-    __ B(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64);
-};
-
 class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
  public:
   explicit NullCheckSlowPathARM64(HNullCheck* instr) : SlowPathCodeARM64(instr) {}
@@ -494,7 +464,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, instruction_->GetLocations());
     arm64_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickDeoptimize, void, void>();
   }
@@ -1638,6 +1607,9 @@
                                                    object_field_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
     locations->SetOut(Location::RequiresFpuRegister());
@@ -2063,6 +2035,9 @@
                                                    object_array_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -2173,11 +2148,6 @@
   } else {
     locations->SetInAt(2, Location::RequiresRegister());
   }
-  if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && (value_type == Primitive::kPrimNot)) {
-    // Additional temporary registers for a Baker read barrier.
-    locations->AddTemp(Location::RequiresRegister());
-    locations->AddTemp(Location::RequiresRegister());
-  }
 }
 
 void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
@@ -2264,144 +2234,44 @@
           __ Bind(&non_zero);
         }
 
-        if (kEmitCompilerReadBarrier) {
-          if (!kUseBakerReadBarrier) {
-            // When (non-Baker) read barriers are enabled, the type
-            // checking instrumentation requires two read barriers
-            // generated by CodeGeneratorARM64::GenerateReadBarrierSlow:
-            //
-            //   __ Mov(temp2, temp);
-            //   // /* HeapReference<Class> */ temp = temp->component_type_
-            //   __ Ldr(temp, HeapOperand(temp, component_offset));
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp_loc, temp_loc, temp2_loc, component_offset);
-            //
-            //   // /* HeapReference<Class> */ temp2 = value->klass_
-            //   __ Ldr(temp2, HeapOperand(Register(value), class_offset));
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp_loc);
-            //
-            //   __ Cmp(temp, temp2);
-            //
-            // However, the second read barrier may trash `temp`, as it
-            // is a temporary register, and as such would not be saved
-            // along with live registers before calling the runtime (nor
-            // restored afterwards).  So in this case, we bail out and
-            // delegate the work to the array set slow path.
-            //
-            // TODO: Extend the register allocator to support a new
-            // "(locally) live temp" location so as to avoid always
-            // going into the slow path when read barriers are enabled?
-            //
-            // There is no such problem with Baker read barriers (see below).
-            __ B(slow_path->GetEntryLabel());
-          } else {
-            // Note that we cannot use `temps` (instance of VIXL's
-            // UseScratchRegisterScope) to allocate `temp2` because
-            // the Baker read barriers generated by
-            // GenerateFieldLoadWithBakerReadBarrier below also use
-            // that facility to allocate a temporary register, thus
-            // making VIXL's scratch register pool empty.
-            Location temp2_loc = locations->GetTemp(0);
-            Register temp2 = WRegisterFrom(temp2_loc);
+        // Note that when Baker read barriers are enabled, the type
+        // checks are performed without read barriers.  This is fine,
+        // even in the case where a class object is in the from-space
+        // after the flip, as a comparison involving such a type would
+        // not produce a false positive; it may of course produce a
+        // false negative, in which case we would take the ArraySet
+        // slow path.
 
-            // Note: Because it is acquired from VIXL's scratch register
-            // pool, `temp` might be IP0, and thus cannot be used as
-            // `ref` argument of GenerateFieldLoadWithBakerReadBarrier
-            // calls below (see ReadBarrierMarkSlowPathARM64 for more
-            // details).
+        Register temp2 = temps.AcquireSameSizeAs(array);
+        // /* HeapReference<Class> */ temp = array->klass_
+        __ Ldr(temp, HeapOperand(array, class_offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        GetAssembler()->MaybeUnpoisonHeapReference(temp);
 
-            // /* HeapReference<Class> */ temp2 = array->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                            temp2_loc,
-                                                            array,
-                                                            class_offset,
-                                                            temp,
-                                                            /* needs_null_check */ true,
-                                                            /* use_load_acquire */ false);
+        // /* HeapReference<Class> */ temp = temp->component_type_
+        __ Ldr(temp, HeapOperand(temp, component_offset));
+        // /* HeapReference<Class> */ temp2 = value->klass_
+        __ Ldr(temp2, HeapOperand(Register(value), class_offset));
+        // If heap poisoning is enabled, no need to unpoison `temp`
+        // nor `temp2`, as we are comparing two poisoned references.
+        __ Cmp(temp, temp2);
+        temps.Release(temp2);
 
-            // /* HeapReference<Class> */ temp2 = temp2->component_type_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                            temp2_loc,
-                                                            temp2,
-                                                            component_offset,
-                                                            temp,
-                                                            /* needs_null_check */ false,
-                                                            /* use_load_acquire */ false);
-            // For the same reason that we request `temp2` from the
-            // register allocator above, we cannot get `temp3` from
-            // VIXL's scratch register pool.
-            Location temp3_loc = locations->GetTemp(1);
-            Register temp3 = WRegisterFrom(temp3_loc);
-            // Register `temp2` is not trashed by the read barrier
-            // emitted by GenerateFieldLoadWithBakerReadBarrier below,
-            // as that method produces a call to a ReadBarrierMarkRegX
-            // entry point, which saves all potentially live registers,
-            // including temporaries such a `temp2`.
-            // /* HeapReference<Class> */ temp3 = register_value->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
-                                                            temp3_loc,
-                                                            value.W(),
-                                                            class_offset,
-                                                            temp,
-                                                            /* needs_null_check */ false,
-                                                            /* use_load_acquire */ false);
-            // If heap poisoning is enabled, `temp2` and `temp3` have
-            // been unpoisoned by the the previous calls to
-            // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier.
-            __ Cmp(temp2, temp3);
-
-            if (instruction->StaticTypeOfArrayIsObjectArray()) {
-              vixl::aarch64::Label do_put;
-              __ B(eq, &do_put);
-              // We do not need to emit a read barrier for the
-              // following heap reference load, as `temp2` is only used
-              // in a comparison with null below, and this reference
-              // is not kept afterwards.
-              // /* HeapReference<Class> */ temp = temp2->super_class_
-              __ Ldr(temp, HeapOperand(temp2, super_offset));
-              // If heap poisoning is enabled, no need to unpoison
-              // `temp`, as we are comparing against null below.
-              __ Cbnz(temp, slow_path->GetEntryLabel());
-              __ Bind(&do_put);
-            } else {
-              __ B(ne, slow_path->GetEntryLabel());
-            }
-          }
-        } else {
-          // Non read barrier code.
-
-          Register temp2 = temps.AcquireSameSizeAs(array);
-          // /* HeapReference<Class> */ temp = array->klass_
-          __ Ldr(temp, HeapOperand(array, class_offset));
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
+        if (instruction->StaticTypeOfArrayIsObjectArray()) {
+          vixl::aarch64::Label do_put;
+          __ B(eq, &do_put);
+          // If heap poisoning is enabled, the `temp` reference has
+          // not been unpoisoned yet; unpoison it now.
           GetAssembler()->MaybeUnpoisonHeapReference(temp);
 
-          // /* HeapReference<Class> */ temp = temp->component_type_
-          __ Ldr(temp, HeapOperand(temp, component_offset));
-          // /* HeapReference<Class> */ temp2 = value->klass_
-          __ Ldr(temp2, HeapOperand(Register(value), class_offset));
-          // If heap poisoning is enabled, no need to unpoison `temp`
-          // nor `temp2`, as we are comparing two poisoned references.
-          __ Cmp(temp, temp2);
-          temps.Release(temp2);
-
-          if (instruction->StaticTypeOfArrayIsObjectArray()) {
-            vixl::aarch64::Label do_put;
-            __ B(eq, &do_put);
-            // If heap poisoning is enabled, the `temp` reference has
-            // not been unpoisoned yet; unpoison it now.
-            GetAssembler()->MaybeUnpoisonHeapReference(temp);
-
-            // /* HeapReference<Class> */ temp = temp->super_class_
-            __ Ldr(temp, HeapOperand(temp, super_offset));
-            // If heap poisoning is enabled, no need to unpoison
-            // `temp`, as we are comparing against null below.
-            __ Cbnz(temp, slow_path->GetEntryLabel());
-            __ Bind(&do_put);
-          } else {
-            __ B(ne, slow_path->GetEntryLabel());
-          }
+          // /* HeapReference<Class> */ temp = temp->super_class_
+          __ Ldr(temp, HeapOperand(temp, super_offset));
+          // If heap poisoning is enabled, no need to unpoison
+          // `temp`, as we are comparing against null below.
+          __ Cbnz(temp, slow_path->GetEntryLabel());
+          __ Bind(&do_put);
+        } else {
+          __ B(ne, slow_path->GetEntryLabel());
         }
       }
 
@@ -3054,6 +2924,7 @@
 void LocationsBuilderARM64::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -3187,6 +3058,7 @@
 void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -3194,6 +3066,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind =
           kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
+      baker_read_barrier_slow_path = kUseBakerReadBarrier;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -3203,6 +3076,9 @@
   }
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // The "out" register is used as a temporary, so it overlaps with the inputs.
@@ -3374,6 +3250,7 @@
   bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
 
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -3382,6 +3259,7 @@
       call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
           LocationSummary::kCallOnSlowPath :
           LocationSummary::kNoCall;  // In fact, call on a fatal (non-returning) slow path.
+      baker_read_barrier_slow_path = kUseBakerReadBarrier && !throws_into_catch;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -3391,6 +3269,9 @@
   }
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   // Note that TypeCheckSlowPathARM64 uses this "temp" register too.
@@ -4028,17 +3909,6 @@
 
 HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -4073,10 +3943,15 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
+
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
@@ -4096,6 +3971,7 @@
   Location out_loc = cls->GetLocations()->Out();
   Register out = OutputRegister(cls);
 
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -4103,17 +3979,21 @@
       DCHECK(!cls->MustGenerateClinitCheck());
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       Register current_method = InputRegisterAt(cls, 0);
-      GenerateGcRootFieldLoad(
-          cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              current_method,
+                              ArtMethod::DeclaringClassOffset().Int32Value(),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
                                                             cls->GetTypeIndex()));
       break;
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       // Add ADRP with its PC-relative type patch.
       const DexFile& dex_file = cls->GetDexFile();
       uint32_t type_index = cls->GetTypeIndex();
@@ -4134,7 +4014,7 @@
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress()));
       __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress()));
       break;
@@ -4152,7 +4032,12 @@
       uint32_t offset = cls->GetAddress() & MaxInt<uint64_t>(offset_bits);
       __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address));
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)
-      GenerateGcRootFieldLoad(cls, out_loc, out.X(), offset);
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              out.X(),
+                              offset,
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -4171,7 +4056,12 @@
       vixl::aarch64::Label* ldr_label =
           codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
       // /* GcRoot<mirror::Class> */ out = *(base_address + offset)  /* PC-relative */
-      GenerateGcRootFieldLoad(cls, out_loc, out.X(), /* offset placeholder */ 0, ldr_label);
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              out.X(),
+                              /* offset placeholder */ 0,
+                              ldr_label,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -4183,8 +4073,12 @@
       Register current_method = InputRegisterAt(cls, 0);
       __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      GenerateGcRootFieldLoad(
-          cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              out.X(),
+                              CodeGenerator::GetCacheOffset(cls->GetTypeIndex()),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -4230,17 +4124,6 @@
 
 HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
     HLoadString::LoadKind desired_string_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_string_load_kind) {
-      case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadString::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadString::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
       DCHECK(!GetCompilerOptions().GetCompilePic());
@@ -4263,14 +4146,17 @@
 }
 
 void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? LocationSummary::kCallOnSlowPath
+  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+      ? LocationSummary::kCallOnMainOnly
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
     locations->SetInAt(0, Location::RequiresRegister());
+    InvokeRuntimeCallingConvention calling_convention;
+    locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
+  } else {
+    locations->SetOut(Location::RequiresRegister());
   }
-  locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
@@ -4278,12 +4164,10 @@
 
   switch (load->GetLoadKind()) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      DCHECK(!kEmitCompilerReadBarrier);
       __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
                                                               load->GetStringIndex()));
       return;  // No dex cache slow path.
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
       // Add ADRP with its PC-relative String patch.
       const DexFile& dex_file = load->GetDexFile();
       uint32_t string_index = load->GetStringIndex();
@@ -4304,7 +4188,6 @@
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       DCHECK(load->GetAddress() != 0u && IsUint<32>(load->GetAddress()));
       __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(load->GetAddress()));
       return;  // No dex cache slow path.
@@ -4314,10 +4197,10 @@
   }
 
   // TODO: Re-add the compiler code to do string dex cache lookup again.
-  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
-  codegen_->AddSlowPath(slow_path);
-  __ B(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  InvokeRuntimeCallingConvention calling_convention;
+  __ Mov(calling_convention.GetRegisterAt(0).W(), load->GetStringIndex());
+  codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
 }
 
 void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
@@ -4797,7 +4680,9 @@
 }
 
 void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
-  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5080,9 +4965,11 @@
                                                             Location root,
                                                             Register obj,
                                                             uint32_t offset,
-                                                            vixl::aarch64::Label* fixup_label) {
+                                                            vixl::aarch64::Label* fixup_label,
+                                                            bool requires_read_barrier) {
   Register root_reg = RegisterFrom(root, Primitive::kPrimNot);
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 78db803..f0d7910 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -294,7 +294,8 @@
                                Location root,
                                vixl::aarch64::Register obj,
                                uint32_t offset,
-                               vixl::aarch64::Label* fixup_label = nullptr);
+                               vixl::aarch64::Label* fixup_label = nullptr,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
 
   // Generate a floating-point comparison.
   void GenerateFcmp(HInstruction* instruction);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index fe6069c..f07f8a0 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -418,7 +418,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, instruction_->GetLocations());
     mips_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickDeoptimize, void, void>();
   }
@@ -666,16 +665,17 @@
   if ((fpu_spill_mask_ != 0) && (POPCOUNT(core_spill_mask_) % 2 != 0)) {
     core_spill_mask_ |= (1 << ZERO);
   }
+}
+
+bool CodeGeneratorMIPS::HasAllocatedCalleeSaveRegisters() const {
   // If RA is clobbered by PC-relative operations on R2 and it's the only spilled register
-  // (this can happen in leaf methods), artificially spill the ZERO register in order to
-  // force explicit saving and restoring of RA. RA isn't saved/restored when it's the only
-  // spilled register.
+  // (this can happen in leaf methods), force CodeGenerator::InitializeCodeGeneration()
+  // into the path that creates a stack frame so that RA can be explicitly saved and restored.
+  // RA can't otherwise be saved/restored when it's the only spilled register.
   // TODO: Can this be improved? It causes creation of a stack frame (while RA might be
   // saved in an unused temporary register) and saving of RA and the current method pointer
   // in the frame.
-  if (clobbered_ra_ && core_spill_mask_ == (1u << RA) && fpu_spill_mask_ == 0) {
-    core_spill_mask_ |= (1 << ZERO);
-  }
+  return CodeGenerator::HasAllocatedCalleeSaveRegisters() || clobbered_ra_;
 }
 
 static dwarf::Reg DWARFReg(Register reg) {
@@ -698,6 +698,9 @@
   }
 
   if (HasEmptyFrame()) {
+    CHECK_EQ(fpu_spill_mask_, 0u);
+    CHECK_EQ(core_spill_mask_, 1u << RA);
+    CHECK(!clobbered_ra_);
     return;
   }
 
@@ -731,6 +734,7 @@
   }
 
   // Store the current method pointer.
+  // TODO: can we not do this if RequiresCurrentMethod() returns false?
   __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset);
 }
 
@@ -2374,13 +2378,8 @@
 
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      // TODO: don't use branches.
-      GenerateFpCompareAndBranch(instruction->GetCondition(),
-                                 instruction->IsGtBias(),
-                                 type,
-                                 locations,
-                                 &true_label);
-      break;
+      GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations);
+      return;
   }
 
   // Convert the branches into the result.
@@ -3173,6 +3172,230 @@
   }
 }
 
+void InstructionCodeGeneratorMIPS::GenerateFpCompare(IfCondition cond,
+                                                     bool gt_bias,
+                                                     Primitive::Type type,
+                                                     LocationSummary* locations) {
+  Register dst = locations->Out().AsRegister<Register>();
+  FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
+  FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
+  bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+  if (type == Primitive::kPrimFloat) {
+    if (isR6) {
+      switch (cond) {
+        case kCondEQ:
+          __ CmpEqS(FTMP, lhs, rhs);
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondNE:
+          __ CmpEqS(FTMP, lhs, rhs);
+          __ Mfc1(dst, FTMP);
+          __ Addiu(dst, dst, 1);
+          break;
+        case kCondLT:
+          if (gt_bias) {
+            __ CmpLtS(FTMP, lhs, rhs);
+          } else {
+            __ CmpUltS(FTMP, lhs, rhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondLE:
+          if (gt_bias) {
+            __ CmpLeS(FTMP, lhs, rhs);
+          } else {
+            __ CmpUleS(FTMP, lhs, rhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondGT:
+          if (gt_bias) {
+            __ CmpUltS(FTMP, rhs, lhs);
+          } else {
+            __ CmpLtS(FTMP, rhs, lhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondGE:
+          if (gt_bias) {
+            __ CmpUleS(FTMP, rhs, lhs);
+          } else {
+            __ CmpLeS(FTMP, rhs, lhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+          UNREACHABLE();
+      }
+    } else {
+      switch (cond) {
+        case kCondEQ:
+          __ CeqS(0, lhs, rhs);
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondNE:
+          __ CeqS(0, lhs, rhs);
+          __ LoadConst32(dst, 1);
+          __ Movt(dst, ZERO, 0);
+          break;
+        case kCondLT:
+          if (gt_bias) {
+            __ ColtS(0, lhs, rhs);
+          } else {
+            __ CultS(0, lhs, rhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondLE:
+          if (gt_bias) {
+            __ ColeS(0, lhs, rhs);
+          } else {
+            __ CuleS(0, lhs, rhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondGT:
+          if (gt_bias) {
+            __ CultS(0, rhs, lhs);
+          } else {
+            __ ColtS(0, rhs, lhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondGE:
+          if (gt_bias) {
+            __ CuleS(0, rhs, lhs);
+          } else {
+            __ ColeS(0, rhs, lhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+          UNREACHABLE();
+      }
+    }
+  } else {
+    DCHECK_EQ(type, Primitive::kPrimDouble);
+    if (isR6) {
+      switch (cond) {
+        case kCondEQ:
+          __ CmpEqD(FTMP, lhs, rhs);
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondNE:
+          __ CmpEqD(FTMP, lhs, rhs);
+          __ Mfc1(dst, FTMP);
+          __ Addiu(dst, dst, 1);
+          break;
+        case kCondLT:
+          if (gt_bias) {
+            __ CmpLtD(FTMP, lhs, rhs);
+          } else {
+            __ CmpUltD(FTMP, lhs, rhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondLE:
+          if (gt_bias) {
+            __ CmpLeD(FTMP, lhs, rhs);
+          } else {
+            __ CmpUleD(FTMP, lhs, rhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondGT:
+          if (gt_bias) {
+            __ CmpUltD(FTMP, rhs, lhs);
+          } else {
+            __ CmpLtD(FTMP, rhs, lhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        case kCondGE:
+          if (gt_bias) {
+            __ CmpUleD(FTMP, rhs, lhs);
+          } else {
+            __ CmpLeD(FTMP, rhs, lhs);
+          }
+          __ Mfc1(dst, FTMP);
+          __ Andi(dst, dst, 1);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+          UNREACHABLE();
+      }
+    } else {
+      switch (cond) {
+        case kCondEQ:
+          __ CeqD(0, lhs, rhs);
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondNE:
+          __ CeqD(0, lhs, rhs);
+          __ LoadConst32(dst, 1);
+          __ Movt(dst, ZERO, 0);
+          break;
+        case kCondLT:
+          if (gt_bias) {
+            __ ColtD(0, lhs, rhs);
+          } else {
+            __ CultD(0, lhs, rhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondLE:
+          if (gt_bias) {
+            __ ColeD(0, lhs, rhs);
+          } else {
+            __ CuleD(0, lhs, rhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondGT:
+          if (gt_bias) {
+            __ CultD(0, rhs, lhs);
+          } else {
+            __ ColtD(0, rhs, lhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        case kCondGE:
+          if (gt_bias) {
+            __ CuleD(0, rhs, lhs);
+          } else {
+            __ ColeD(0, rhs, lhs);
+          }
+          __ LoadConst32(dst, 1);
+          __ Movf(dst, ZERO, 0);
+          break;
+        default:
+          LOG(FATAL) << "Unexpected non-floating-point condition " << cond;
+          UNREACHABLE();
+      }
+    }
+  }
+}
+
 void InstructionCodeGeneratorMIPS::GenerateFpCompareAndBranch(IfCondition cond,
                                                               bool gt_bias,
                                                               Primitive::Type type,
@@ -3465,6 +3688,7 @@
 void LocationsBuilderMIPS::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -5150,7 +5374,9 @@
 }
 
 void LocationsBuilderMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
-  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorMIPS::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 4ce54b6..0039981 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -243,6 +243,10 @@
   void GenerateLongCompareAndBranch(IfCondition cond,
                                     LocationSummary* locations,
                                     MipsLabel* label);
+  void GenerateFpCompare(IfCondition cond,
+                         bool gt_bias,
+                         Primitive::Type type,
+                         LocationSummary* locations);
   void GenerateFpCompareAndBranch(IfCondition cond,
                                   bool gt_bias,
                                   Primitive::Type type,
@@ -274,6 +278,7 @@
   virtual ~CodeGeneratorMIPS() {}
 
   void ComputeSpillMask() OVERRIDE;
+  bool HasAllocatedCalleeSaveRegisters() const OVERRIDE;
   void GenerateFrameEntry() OVERRIDE;
   void GenerateFrameExit() OVERRIDE;
 
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 71d35bd..664d498 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -376,7 +376,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, instruction_->GetLocations());
     mips64_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickDeoptimize, void, void>();
   }
@@ -2631,6 +2630,7 @@
 void LocationsBuilderMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
@@ -3746,7 +3746,9 @@
 }
 
 void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
-  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 1369e5f..72ab615 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -213,35 +213,6 @@
   DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86);
 };
 
-class LoadStringSlowPathX86 : public SlowPathCode {
- public:
-  explicit LoadStringSlowPathX86(HLoadString* instruction): SlowPathCode(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-
-    CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
-    __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index));
-    x86_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
-    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-    x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX));
-    RestoreLiveRegisters(codegen, locations);
-
-    __ jmp(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86);
-};
-
 class LoadClassSlowPathX86 : public SlowPathCode {
  public:
   LoadClassSlowPathX86(HLoadClass* cls,
@@ -369,7 +340,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, instruction_->GetLocations());
     x86_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickDeoptimize, void, void>();
   }
@@ -1499,6 +1469,7 @@
 void LocationsBuilderX86::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::Any());
   }
@@ -4568,6 +4539,9 @@
                                                    kEmitCompilerReadBarrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
 
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -5032,6 +5006,9 @@
                                                    object_array_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -5232,7 +5209,6 @@
   }
   if (needs_write_barrier) {
     // Temporary registers for the write barrier.
-    // These registers may be used for Baker read barriers too.
     locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
     // Ensure the card is in a byte register.
     locations->AddTemp(Location::RegisterLocation(ECX));
@@ -5322,105 +5298,40 @@
           __ Bind(&not_null);
         }
 
-        if (kEmitCompilerReadBarrier) {
-          if (!kUseBakerReadBarrier) {
-            // When (non-Baker) read barriers are enabled, the type
-            // checking instrumentation requires two read barriers
-            // generated by CodeGeneratorX86::GenerateReadBarrierSlow:
-            //
-            //   __ movl(temp2, temp);
-            //   // /* HeapReference<Class> */ temp = temp->component_type_
-            //   __ movl(temp, Address(temp, component_offset));
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp_loc, temp_loc, temp2_loc, component_offset);
-            //
-            //   // /* HeapReference<Class> */ temp2 = register_value->klass_
-            //   __ movl(temp2, Address(register_value, class_offset));
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp2_loc, temp2_loc, value, class_offset, temp_loc);
-            //
-            //   __ cmpl(temp, temp2);
-            //
-            // However, the second read barrier may trash `temp`, as it
-            // is a temporary register, and as such would not be saved
-            // along with live registers before calling the runtime (nor
-            // restored afterwards).  So in this case, we bail out and
-            // delegate the work to the array set slow path.
-            //
-            // TODO: Extend the register allocator to support a new
-            // "(locally) live temp" location so as to avoid always
-            // going into the slow path when read barriers are enabled?
-            //
-            // There is no such problem with Baker read barriers (see below).
-            __ jmp(slow_path->GetEntryLabel());
-          } else {
-            Location temp2_loc = locations->GetTemp(1);
-            Register temp2 = temp2_loc.AsRegister<Register>();
-            // /* HeapReference<Class> */ temp = array->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(
-                instruction, temp_loc, array, class_offset, /* needs_null_check */ true);
+        // Note that when Baker read barriers are enabled, the type
+        // checks are performed without read barriers.  This is fine,
+        // even in the case where a class object is in the from-space
+        // after the flip, as a comparison involving such a type would
+        // not produce a false positive; it may of course produce a
+        // false negative, in which case we would take the ArraySet
+        // slow path.
 
-            // /* HeapReference<Class> */ temp = temp->component_type_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(
-                instruction, temp_loc, temp, component_offset, /* needs_null_check */ false);
-            // Register `temp` is not trashed by the read barrier
-            // emitted by GenerateFieldLoadWithBakerReadBarrier below,
-            // as that method produces a call to a ReadBarrierMarkRegX
-            // entry point, which saves all potentially live registers,
-            // including temporaries such a `temp`.
-            // /* HeapReference<Class> */ temp2 = register_value->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(
-                instruction, temp2_loc, register_value, class_offset, /* needs_null_check */ false);
-            // If heap poisoning is enabled, `temp` and `temp2` have
-            // been unpoisoned by the the previous calls to
-            // CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier.
-            __ cmpl(temp, temp2);
+        // /* HeapReference<Class> */ temp = array->klass_
+        __ movl(temp, Address(array, class_offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ MaybeUnpoisonHeapReference(temp);
 
-            if (instruction->StaticTypeOfArrayIsObjectArray()) {
-              __ j(kEqual, &do_put);
-              // We do not need to emit a read barrier for the
-              // following heap reference load, as `temp` is only used
-              // in a comparison with null below, and this reference
-              // is not kept afterwards. Also, if heap poisoning is
-              // enabled, there is no need to unpoison that heap
-              // reference for the same reason (comparison with null).
-              __ cmpl(Address(temp, super_offset), Immediate(0));
-              __ j(kNotEqual, slow_path->GetEntryLabel());
-              __ Bind(&do_put);
-            } else {
-              __ j(kNotEqual, slow_path->GetEntryLabel());
-            }
-          }
-        } else {
-          // Non read barrier code.
+        // /* HeapReference<Class> */ temp = temp->component_type_
+        __ movl(temp, Address(temp, component_offset));
+        // If heap poisoning is enabled, no need to unpoison `temp`
+        // nor the object reference in `register_value->klass`, as
+        // we are comparing two poisoned references.
+        __ cmpl(temp, Address(register_value, class_offset));
 
-          // /* HeapReference<Class> */ temp = array->klass_
-          __ movl(temp, Address(array, class_offset));
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
+        if (instruction->StaticTypeOfArrayIsObjectArray()) {
+          __ j(kEqual, &do_put);
+          // If heap poisoning is enabled, the `temp` reference has
+          // not been unpoisoned yet; unpoison it now.
           __ MaybeUnpoisonHeapReference(temp);
 
-          // /* HeapReference<Class> */ temp = temp->component_type_
-          __ movl(temp, Address(temp, component_offset));
-          // If heap poisoning is enabled, no need to unpoison `temp`
-          // nor the object reference in `register_value->klass`, as
-          // we are comparing two poisoned references.
-          __ cmpl(temp, Address(register_value, class_offset));
-
-          if (instruction->StaticTypeOfArrayIsObjectArray()) {
-            __ j(kEqual, &do_put);
-            // If heap poisoning is enabled, the `temp` reference has
-            // not been unpoisoned yet; unpoison it now.
-            __ MaybeUnpoisonHeapReference(temp);
-
-            // If heap poisoning is enabled, no need to unpoison the
-            // heap reference loaded below, as it is only used for a
-            // comparison with null.
-            __ cmpl(Address(temp, super_offset), Immediate(0));
-            __ j(kNotEqual, slow_path->GetEntryLabel());
-            __ Bind(&do_put);
-          } else {
-            __ j(kNotEqual, slow_path->GetEntryLabel());
-          }
+          // If heap poisoning is enabled, no need to unpoison the
+          // heap reference loaded below, as it is only used for a
+          // comparison with null.
+          __ cmpl(Address(temp, super_offset), Immediate(0));
+          __ j(kNotEqual, slow_path->GetEntryLabel());
+          __ Bind(&do_put);
+        } else {
+          __ j(kNotEqual, slow_path->GetEntryLabel());
         }
       }
 
@@ -5642,7 +5553,9 @@
 }
 
 void LocationsBuilderX86::VisitSuspendCheck(HSuspendCheck* instruction) {
-  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5953,17 +5866,6 @@
 
 HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -6005,10 +5907,15 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
+
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
@@ -6032,6 +5939,7 @@
   Register out = out_loc.AsRegister<Register>();
 
   bool generate_null_check = false;
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
       DCHECK(!cls->CanCallRuntime());
@@ -6039,24 +5947,28 @@
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       Register current_method = locations->InAt(0).AsRegister<Register>();
       GenerateGcRootFieldLoad(
-          cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+          cls,
+          out_loc,
+          Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()),
+          /*fixup_label*/ nullptr,
+          requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ movl(out, Immediate(/* placeholder */ 0));
       codegen_->RecordTypePatch(cls);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       Register method_address = locations->InAt(0).AsRegister<Register>();
       __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
       codegen_->RecordTypePatch(cls);
       break;
     }
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       __ movl(out, Immediate(address));
@@ -6067,7 +5979,11 @@
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       // /* GcRoot<mirror::Class> */ out = *address
-      GenerateGcRootFieldLoad(cls, out_loc, Address::Absolute(address));
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              Address::Absolute(address),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6076,8 +5992,11 @@
       uint32_t offset = cls->GetDexCacheElementOffset();
       Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset);
       // /* GcRoot<mirror::Class> */ out = *(base + offset)  /* PC-relative */
-      GenerateGcRootFieldLoad(
-          cls, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label);
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              Address(base_reg, CodeGeneratorX86::kDummy32BitOffset),
+                              fixup_label,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6088,8 +6007,11 @@
       __ movl(out, Address(current_method,
                            ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
       // /* GcRoot<mirror::Class> */ out = out[type_index]
-      GenerateGcRootFieldLoad(
-          cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+      GenerateGcRootFieldLoad(cls,
+                              out_loc,
+                              Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
+                              /*fixup_label*/ nullptr,
+                              requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -6143,17 +6065,6 @@
 
 HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
     HLoadString::LoadKind desired_string_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_string_load_kind) {
-      case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadString::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadString::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
       DCHECK(!GetCompilerOptions().GetCompilePic());
@@ -6184,7 +6095,7 @@
 
 void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
   LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? LocationSummary::kCallOnSlowPath
+      ? LocationSummary::kCallOnMainOnly
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   HLoadString::LoadKind load_kind = load->GetLoadKind();
@@ -6193,7 +6104,11 @@
       load_kind == HLoadString::LoadKind::kDexCachePcRelative) {
     locations->SetInAt(0, Location::RequiresRegister());
   }
-  locations->SetOut(Location::RequiresRegister());
+  if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
+    locations->SetOut(Location::RegisterLocation(EAX));
+  } else {
+    locations->SetOut(Location::RequiresRegister());
+  }
 }
 
 void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
@@ -6203,20 +6118,17 @@
 
   switch (load->GetLoadKind()) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       __ movl(out, Immediate(/* placeholder */ 0));
       codegen_->RecordStringPatch(load);
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
       Register method_address = locations->InAt(0).AsRegister<Register>();
       __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
       codegen_->RecordStringPatch(load);
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       DCHECK_NE(load->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
       __ movl(out, Immediate(address));
@@ -6228,10 +6140,10 @@
   }
 
   // TODO: Re-add the compiler code to do string dex cache lookup again.
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
-  codegen_->AddSlowPath(slow_path);
-  __ jmp(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  InvokeRuntimeCallingConvention calling_convention;
+  __ movl(calling_convention.GetRegisterAt(0), Immediate(load->GetStringIndex()));
+  codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
 }
 
 static Address GetExceptionTlsAddress() {
@@ -6279,6 +6191,7 @@
 void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {
   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -6286,6 +6199,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind =
           kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
+      baker_read_barrier_slow_path = kUseBakerReadBarrier;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -6295,6 +6209,9 @@
   }
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   // Note that TypeCheckSlowPathX86 uses this "out" register too.
@@ -6492,6 +6409,7 @@
   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
   bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -6500,6 +6418,7 @@
       call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
           LocationSummary::kCallOnSlowPath :
           LocationSummary::kNoCall;  // In fact, call on a fatal (non-returning) slow path.
+      baker_read_barrier_slow_path = kUseBakerReadBarrier && !throws_into_catch;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -6508,6 +6427,9 @@
       break;
   }
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   // Note that TypeCheckSlowPathX86 uses this "temp" register too.
@@ -6912,9 +6834,11 @@
 void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(HInstruction* instruction,
                                                           Location root,
                                                           const Address& address,
-                                                          Label* fixup_label) {
+                                                          Label* fixup_label,
+                                                          bool requires_read_barrier) {
   Register root_reg = root.AsRegister<Register>();
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index df65fa2..e225098 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -263,7 +263,8 @@
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                const Address& address,
-                               Label* fixup_label = nullptr);
+                               Label* fixup_label = nullptr,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
 
   // Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
   // `is_wide` specifies whether it is long/double or not.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index aef4cd7..cbb5799 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -288,37 +288,6 @@
   DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathX86_64);
 };
 
-class LoadStringSlowPathX86_64 : public SlowPathCode {
- public:
-  explicit LoadStringSlowPathX86_64(HLoadString* instruction) : SlowPathCode(instruction) {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    LocationSummary* locations = instruction_->GetLocations();
-    DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
-
-    CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
-    __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, locations);
-
-    InvokeRuntimeCallingConvention calling_convention;
-    const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
-    __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(string_index));
-    x86_64_codegen->InvokeRuntime(kQuickResolveString,
-                                  instruction_,
-                                  instruction_->GetDexPc(),
-                                  this);
-    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-    x86_64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX));
-    RestoreLiveRegisters(codegen, locations);
-    __ jmp(GetExitLabel());
-  }
-
-  const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86_64"; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86_64);
-};
-
 class TypeCheckSlowPathX86_64 : public SlowPathCode {
  public:
   TypeCheckSlowPathX86_64(HInstruction* instruction, bool is_fatal)
@@ -388,7 +357,6 @@
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
     CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
     __ Bind(GetEntryLabel());
-    SaveLiveRegisters(codegen, instruction_->GetLocations());
     x86_64_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
     CheckEntrypointTypes<kQuickDeoptimize, void, void>();
   }
@@ -1563,6 +1531,7 @@
 void LocationsBuilderX86_64::VisitDeoptimize(HDeoptimize* deoptimize) {
   LocationSummary* locations = new (GetGraph()->GetArena())
       LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
   if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
     locations->SetInAt(0, Location::Any());
   }
@@ -4114,6 +4083,9 @@
                                                    object_field_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
     locations->SetOut(Location::RequiresFpuRegister());
@@ -4543,6 +4515,9 @@
                                                    object_array_get_with_read_barrier ?
                                                        LocationSummary::kCallOnSlowPath :
                                                        LocationSummary::kNoCall);
+  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
   if (Primitive::IsFloatingPointType(instruction->GetType())) {
@@ -4726,7 +4701,6 @@
 
   if (needs_write_barrier) {
     // Temporary registers for the write barrier.
-    // These registers may be used for Baker read barriers too.
     locations->AddTemp(Location::RequiresRegister());  // Possibly used for ref. poisoning too.
     locations->AddTemp(Location::RequiresRegister());
   }
@@ -4816,105 +4790,40 @@
           __ Bind(&not_null);
         }
 
-        if (kEmitCompilerReadBarrier) {
-          if (!kUseBakerReadBarrier) {
-            // When (non-Baker) read barriers are enabled, the type
-            // checking instrumentation requires two read barriers
-            // generated by CodeGeneratorX86_64::GenerateReadBarrierSlow:
-            //
-            //   __ movl(temp2, temp);
-            //   // /* HeapReference<Class> */ temp = temp->component_type_
-            //   __ movl(temp, Address(temp, component_offset));
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp_loc, temp_loc, temp2_loc, component_offset);
-            //
-            //   // /* HeapReference<Class> */ temp2 = register_value->klass_
-            //   __ movl(temp2, Address(register_value, class_offset));
-            //   codegen_->GenerateReadBarrierSlow(
-            //       instruction, temp2_loc, temp2_loc, value, class_offset, temp_loc);
-            //
-            //   __ cmpl(temp, temp2);
-            //
-            // However, the second read barrier may trash `temp`, as it
-            // is a temporary register, and as such would not be saved
-            // along with live registers before calling the runtime (nor
-            // restored afterwards).  So in this case, we bail out and
-            // delegate the work to the array set slow path.
-            //
-            // TODO: Extend the register allocator to support a new
-            // "(locally) live temp" location so as to avoid always
-            // going into the slow path when read barriers are enabled?
-            //
-            // There is no such problem with Baker read barriers (see below).
-            __ jmp(slow_path->GetEntryLabel());
-          } else {
-            Location temp2_loc = locations->GetTemp(1);
-            CpuRegister temp2 = temp2_loc.AsRegister<CpuRegister>();
-            // /* HeapReference<Class> */ temp = array->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(
-                instruction, temp_loc, array, class_offset, /* needs_null_check */ true);
+        // Note that when Baker read barriers are enabled, the type
+        // checks are performed without read barriers.  This is fine,
+        // even in the case where a class object is in the from-space
+        // after the flip, as a comparison involving such a type would
+        // not produce a false positive; it may of course produce a
+        // false negative, in which case we would take the ArraySet
+        // slow path.
 
-            // /* HeapReference<Class> */ temp = temp->component_type_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(
-                instruction, temp_loc, temp, component_offset, /* needs_null_check */ false);
-            // Register `temp` is not trashed by the read barrier
-            // emitted by GenerateFieldLoadWithBakerReadBarrier below,
-            // as that method produces a call to a ReadBarrierMarkRegX
-            // entry point, which saves all potentially live registers,
-            // including temporaries such a `temp`.
-            // /* HeapReference<Class> */ temp2 = register_value->klass_
-            codegen_->GenerateFieldLoadWithBakerReadBarrier(
-                instruction, temp2_loc, register_value, class_offset, /* needs_null_check */ false);
-            // If heap poisoning is enabled, `temp` and `temp2` have
-            // been unpoisoned by the the previous calls to
-            // CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier.
-            __ cmpl(temp, temp2);
+        // /* HeapReference<Class> */ temp = array->klass_
+        __ movl(temp, Address(array, class_offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ MaybeUnpoisonHeapReference(temp);
 
-            if (instruction->StaticTypeOfArrayIsObjectArray()) {
-              __ j(kEqual, &do_put);
-              // We do not need to emit a read barrier for the
-              // following heap reference load, as `temp` is only used
-              // in a comparison with null below, and this reference
-              // is not kept afterwards. Also, if heap poisoning is
-              // enabled, there is no need to unpoison that heap
-              // reference for the same reason (comparison with null).
-              __ cmpl(Address(temp, super_offset), Immediate(0));
-              __ j(kNotEqual, slow_path->GetEntryLabel());
-              __ Bind(&do_put);
-            } else {
-              __ j(kNotEqual, slow_path->GetEntryLabel());
-            }
-          }
-        } else {
-          // Non read barrier code.
+        // /* HeapReference<Class> */ temp = temp->component_type_
+        __ movl(temp, Address(temp, component_offset));
+        // If heap poisoning is enabled, no need to unpoison `temp`
+        // nor the object reference in `register_value->klass`, as
+        // we are comparing two poisoned references.
+        __ cmpl(temp, Address(register_value, class_offset));
 
-          // /* HeapReference<Class> */ temp = array->klass_
-          __ movl(temp, Address(array, class_offset));
-          codegen_->MaybeRecordImplicitNullCheck(instruction);
+        if (instruction->StaticTypeOfArrayIsObjectArray()) {
+          __ j(kEqual, &do_put);
+          // If heap poisoning is enabled, the `temp` reference has
+          // not been unpoisoned yet; unpoison it now.
           __ MaybeUnpoisonHeapReference(temp);
 
-          // /* HeapReference<Class> */ temp = temp->component_type_
-          __ movl(temp, Address(temp, component_offset));
-          // If heap poisoning is enabled, no need to unpoison `temp`
-          // nor the object reference in `register_value->klass`, as
-          // we are comparing two poisoned references.
-          __ cmpl(temp, Address(register_value, class_offset));
-
-          if (instruction->StaticTypeOfArrayIsObjectArray()) {
-            __ j(kEqual, &do_put);
-            // If heap poisoning is enabled, the `temp` reference has
-            // not been unpoisoned yet; unpoison it now.
-            __ MaybeUnpoisonHeapReference(temp);
-
-            // If heap poisoning is enabled, no need to unpoison the
-            // heap reference loaded below, as it is only used for a
-            // comparison with null.
-            __ cmpl(Address(temp, super_offset), Immediate(0));
-            __ j(kNotEqual, slow_path->GetEntryLabel());
-            __ Bind(&do_put);
-          } else {
-            __ j(kNotEqual, slow_path->GetEntryLabel());
-          }
+          // If heap poisoning is enabled, no need to unpoison the
+          // heap reference loaded below, as it is only used for a
+          // comparison with null.
+          __ cmpl(Address(temp, super_offset), Immediate(0));
+          __ j(kNotEqual, slow_path->GetEntryLabel());
+          __ Bind(&do_put);
+        } else {
+          __ j(kNotEqual, slow_path->GetEntryLabel());
         }
       }
 
@@ -5140,7 +5049,9 @@
 }
 
 void LocationsBuilderX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
-  new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+  locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
 }
 
 void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -5400,17 +5311,6 @@
 
 HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
     HLoadClass::LoadKind desired_class_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_class_load_kind) {
-      case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadClass::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadClass::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_class_load_kind) {
     case HLoadClass::LoadKind::kReferrersClass:
       break;
@@ -5446,10 +5346,15 @@
     return;
   }
 
-  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+  LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
       ? LocationSummary::kCallOnSlowPath
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+  if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
+
   HLoadClass::LoadKind load_kind = cls->GetLoadKind();
   if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
       load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
@@ -5470,6 +5375,7 @@
   Location out_loc = locations->Out();
   CpuRegister out = out_loc.AsRegister<CpuRegister>();
 
+  const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
   bool generate_null_check = false;
   switch (cls->GetLoadKind()) {
     case HLoadClass::LoadKind::kReferrersClass: {
@@ -5478,16 +5384,20 @@
       // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
       CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
       GenerateGcRootFieldLoad(
-          cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+          cls,
+          out_loc,
+          Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()),
+          /*fixup_label*/nullptr,
+          requires_read_barrier);
       break;
     }
     case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
       codegen_->RecordTypePatch(cls);
       break;
     case HLoadClass::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
+      DCHECK(!requires_read_barrier);
       DCHECK_NE(cls->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
       __ movl(out, Immediate(address));  // Zero-extended.
@@ -5499,11 +5409,19 @@
       // /* GcRoot<mirror::Class> */ out = *address
       if (IsUint<32>(cls->GetAddress())) {
         Address address = Address::Absolute(cls->GetAddress(), /* no_rip */ true);
-        GenerateGcRootFieldLoad(cls, out_loc, address);
+        GenerateGcRootFieldLoad(cls,
+                                out_loc,
+                                address,
+                                /*fixup_label*/nullptr,
+                                requires_read_barrier);
       } else {
         // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address).
         __ movq(out, Immediate(cls->GetAddress()));
-        GenerateGcRootFieldLoad(cls, out_loc, Address(out, 0));
+        GenerateGcRootFieldLoad(cls,
+                                out_loc,
+                                Address(out, 0),
+                                /*fixup_label*/nullptr,
+                                requires_read_barrier);
       }
       generate_null_check = !cls->IsInDexCache();
       break;
@@ -5514,7 +5432,7 @@
       Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
                                           /* no_rip */ false);
       // /* GcRoot<mirror::Class> */ out = *address  /* PC-relative */
-      GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label);
+      GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5527,7 +5445,11 @@
                       ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
       // /* GcRoot<mirror::Class> */ out = out[type_index]
       GenerateGcRootFieldLoad(
-          cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+          cls,
+          out_loc,
+          Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
+          /*fixup_label*/nullptr,
+          requires_read_barrier);
       generate_null_check = !cls->IsInDexCache();
       break;
     }
@@ -5573,17 +5495,6 @@
 
 HLoadString::LoadKind CodeGeneratorX86_64::GetSupportedLoadStringKind(
     HLoadString::LoadKind desired_string_load_kind) {
-  if (kEmitCompilerReadBarrier) {
-    switch (desired_string_load_kind) {
-      case HLoadString::LoadKind::kBootImageLinkTimeAddress:
-      case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
-      case HLoadString::LoadKind::kBootImageAddress:
-        // TODO: Implement for read barrier.
-        return HLoadString::LoadKind::kDexCacheViaMethod;
-      default:
-        break;
-    }
-  }
   switch (desired_string_load_kind) {
     case HLoadString::LoadKind::kBootImageLinkTimeAddress:
       DCHECK(!GetCompilerOptions().GetCompilePic());
@@ -5607,14 +5518,16 @@
 }
 
 void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
-  LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
-      ? LocationSummary::kCallOnSlowPath
+  LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+      ? LocationSummary::kCallOnMainOnly
       : LocationSummary::kNoCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
   if (load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) {
     locations->SetInAt(0, Location::RequiresRegister());
+    locations->SetOut(Location::RegisterLocation(RAX));
+  } else {
+    locations->SetOut(Location::RequiresRegister());
   }
-  locations->SetOut(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
@@ -5624,13 +5537,11 @@
 
   switch (load->GetLoadKind()) {
     case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
-      DCHECK(!kEmitCompilerReadBarrier);
       __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
       codegen_->RecordStringPatch(load);
       return;  // No dex cache slow path.
     }
     case HLoadString::LoadKind::kBootImageAddress: {
-      DCHECK(!kEmitCompilerReadBarrier);
       DCHECK_NE(load->GetAddress(), 0u);
       uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
       __ movl(out, Immediate(address));  // Zero-extended.
@@ -5642,10 +5553,13 @@
   }
 
   // TODO: Re-add the compiler code to do string dex cache lookup again.
-  SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
-  codegen_->AddSlowPath(slow_path);
-  __ jmp(slow_path->GetEntryLabel());
-  __ Bind(slow_path->GetExitLabel());
+  InvokeRuntimeCallingConvention calling_convention;
+  __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
+          Immediate(load->GetStringIndex()));
+  codegen_->InvokeRuntime(kQuickResolveString,
+                          load,
+                          load->GetDexPc());
+  CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
 }
 
 static Address GetExceptionTlsAddress() {
@@ -5694,6 +5608,7 @@
 void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -5701,6 +5616,7 @@
     case TypeCheckKind::kArrayObjectCheck:
       call_kind =
           kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
+      baker_read_barrier_slow_path = kUseBakerReadBarrier;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -5710,6 +5626,9 @@
   }
 
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   // Note that TypeCheckSlowPathX86_64 uses this "out" register too.
@@ -5912,6 +5831,7 @@
   LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
   bool throws_into_catch = instruction->CanThrowIntoCatchBlock();
   TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
+  bool baker_read_barrier_slow_path = false;
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
     case TypeCheckKind::kAbstractClassCheck:
@@ -5920,6 +5840,7 @@
       call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ?
           LocationSummary::kCallOnSlowPath :
           LocationSummary::kNoCall;  // In fact, call on a fatal (non-returning) slow path.
+      baker_read_barrier_slow_path = kUseBakerReadBarrier && !throws_into_catch;
       break;
     case TypeCheckKind::kArrayCheck:
     case TypeCheckKind::kUnresolvedCheck:
@@ -5928,6 +5849,9 @@
       break;
   }
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+  if (baker_read_barrier_slow_path) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::Any());
   // Note that TypeCheckSlowPathX86_64 uses this "temp" register too.
@@ -6361,9 +6285,11 @@
 void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instruction,
                                                              Location root,
                                                              const Address& address,
-                                                             Label* fixup_label) {
+                                                             Label* fixup_label,
+                                                             bool requires_read_barrier) {
   CpuRegister root_reg = root.AsRegister<CpuRegister>();
-  if (kEmitCompilerReadBarrier) {
+  if (requires_read_barrier) {
+    DCHECK(kEmitCompilerReadBarrier);
     if (kUseBakerReadBarrier) {
       // Fast path implementation of art::ReadBarrier::BarrierForRoot when
       // Baker's read barrier are used:
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index f23bff5..d939083 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -257,7 +257,8 @@
   void GenerateGcRootFieldLoad(HInstruction* instruction,
                                Location root,
                                const Address& address,
-                               Label* fixup_label = nullptr);
+                               Label* fixup_label = nullptr,
+                               bool requires_read_barrier = kEmitCompilerReadBarrier);
 
   void PushOntoFPStack(Location source, uint32_t temp_offset,
                        uint32_t stack_adjustment, bool is_float);
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index fe6c0a3..d00a786 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -991,4 +991,67 @@
   }
 }
 
+#ifdef ART_ENABLE_CODEGEN_mips
+TEST_F(CodegenTest, MipsClobberRA) {
+  std::unique_ptr<const MipsInstructionSetFeatures> features_mips(
+      MipsInstructionSetFeatures::FromCppDefines());
+  if (!CanExecute(kMips) || features_mips->IsR6()) {
+    // HMipsComputeBaseMethodAddress and the NAL instruction behind it
+    // should only be generated on non-R6.
+    return;
+  }
+
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = CreateGraph(&allocator);
+
+  HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry_block);
+  graph->SetEntryBlock(entry_block);
+  entry_block->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+
+  HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit_block);
+  graph->SetExitBlock(exit_block);
+  exit_block->AddInstruction(new (&allocator) HExit());
+
+  entry_block->AddSuccessor(block);
+  block->AddSuccessor(exit_block);
+
+  // To simplify matters, don't create PC-relative HLoadClass or HLoadString.
+  // Instead, generate HMipsComputeBaseMethodAddress directly.
+  HMipsComputeBaseMethodAddress* base = new (&allocator) HMipsComputeBaseMethodAddress();
+  block->AddInstruction(base);
+  // HMipsComputeBaseMethodAddress is defined as int, so just make the
+  // compiled method return it.
+  block->AddInstruction(new (&allocator) HReturn(base));
+
+  graph->BuildDominatorTree();
+
+  mips::CodeGeneratorMIPS codegenMIPS(graph, *features_mips.get(), CompilerOptions());
+  // Since there isn't HLoadClass or HLoadString, we need to manually indicate
+  // that RA is clobbered and the method entry code should generate a stack frame
+  // and preserve RA in it. And this is what we're testing here.
+  codegenMIPS.ClobberRA();
+  // Without ClobberRA() the code would be:
+  //   nal              # Sets RA to point to the jr instruction below
+  //   move  v0, ra     # and the CPU falls into an infinite loop.
+  //   jr    ra
+  //   nop
+  // The expected code is:
+  //   addiu sp, sp, -16
+  //   sw    ra, 12(sp)
+  //   sw    a0, 0(sp)
+  //   nal              # Sets RA to point to the lw instruction below.
+  //   move  v0, ra
+  //   lw    ra, 12(sp)
+  //   jr    ra
+  //   addiu sp, sp, 16
+  RunCode(&codegenMIPS, graph, [](HGraph*) {}, false, 0);
+}
+#endif
+
 }  // namespace art
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index cea4a7e..eda0971 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -38,7 +38,7 @@
 static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32),
               "Unexpected values for register codes.");
 
-static inline int VIXLRegCodeFromART(int code) {
+inline int VIXLRegCodeFromART(int code) {
   if (code == SP) {
     return vixl::aarch64::kSPRegInternalCode;
   }
@@ -48,7 +48,7 @@
   return code;
 }
 
-static inline int ARTRegCodeFromVIXL(int code) {
+inline int ARTRegCodeFromVIXL(int code) {
   if (code == vixl::aarch64::kSPRegInternalCode) {
     return SP;
   }
@@ -58,85 +58,85 @@
   return code;
 }
 
-static inline vixl::aarch64::Register XRegisterFrom(Location location) {
+inline vixl::aarch64::Register XRegisterFrom(Location location) {
   DCHECK(location.IsRegister()) << location;
   return vixl::aarch64::Register::GetXRegFromCode(VIXLRegCodeFromART(location.reg()));
 }
 
-static inline vixl::aarch64::Register WRegisterFrom(Location location) {
+inline vixl::aarch64::Register WRegisterFrom(Location location) {
   DCHECK(location.IsRegister()) << location;
   return vixl::aarch64::Register::GetWRegFromCode(VIXLRegCodeFromART(location.reg()));
 }
 
-static inline vixl::aarch64::Register RegisterFrom(Location location, Primitive::Type type) {
+inline vixl::aarch64::Register RegisterFrom(Location location, Primitive::Type type) {
   DCHECK(type != Primitive::kPrimVoid && !Primitive::IsFloatingPointType(type)) << type;
   return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
 }
 
-static inline vixl::aarch64::Register OutputRegister(HInstruction* instr) {
+inline vixl::aarch64::Register OutputRegister(HInstruction* instr) {
   return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
 }
 
-static inline vixl::aarch64::Register InputRegisterAt(HInstruction* instr, int input_index) {
+inline vixl::aarch64::Register InputRegisterAt(HInstruction* instr, int input_index) {
   return RegisterFrom(instr->GetLocations()->InAt(input_index),
                       instr->InputAt(input_index)->GetType());
 }
 
-static inline vixl::aarch64::FPRegister DRegisterFrom(Location location) {
+inline vixl::aarch64::FPRegister DRegisterFrom(Location location) {
   DCHECK(location.IsFpuRegister()) << location;
   return vixl::aarch64::FPRegister::GetDRegFromCode(location.reg());
 }
 
-static inline vixl::aarch64::FPRegister SRegisterFrom(Location location) {
+inline vixl::aarch64::FPRegister SRegisterFrom(Location location) {
   DCHECK(location.IsFpuRegister()) << location;
   return vixl::aarch64::FPRegister::GetSRegFromCode(location.reg());
 }
 
-static inline vixl::aarch64::FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
+inline vixl::aarch64::FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
   DCHECK(Primitive::IsFloatingPointType(type)) << type;
   return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
 }
 
-static inline vixl::aarch64::FPRegister OutputFPRegister(HInstruction* instr) {
+inline vixl::aarch64::FPRegister OutputFPRegister(HInstruction* instr) {
   return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
 }
 
-static inline vixl::aarch64::FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
+inline vixl::aarch64::FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
   return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
                         instr->InputAt(input_index)->GetType());
 }
 
-static inline vixl::aarch64::CPURegister CPURegisterFrom(Location location, Primitive::Type type) {
+inline vixl::aarch64::CPURegister CPURegisterFrom(Location location, Primitive::Type type) {
   return Primitive::IsFloatingPointType(type)
       ? vixl::aarch64::CPURegister(FPRegisterFrom(location, type))
       : vixl::aarch64::CPURegister(RegisterFrom(location, type));
 }
 
-static inline vixl::aarch64::CPURegister OutputCPURegister(HInstruction* instr) {
+inline vixl::aarch64::CPURegister OutputCPURegister(HInstruction* instr) {
   return Primitive::IsFloatingPointType(instr->GetType())
       ? static_cast<vixl::aarch64::CPURegister>(OutputFPRegister(instr))
       : static_cast<vixl::aarch64::CPURegister>(OutputRegister(instr));
 }
 
-static inline vixl::aarch64::CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
+inline vixl::aarch64::CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
   return Primitive::IsFloatingPointType(instr->InputAt(index)->GetType())
       ? static_cast<vixl::aarch64::CPURegister>(InputFPRegisterAt(instr, index))
       : static_cast<vixl::aarch64::CPURegister>(InputRegisterAt(instr, index));
 }
 
-static inline vixl::aarch64::CPURegister InputCPURegisterOrZeroRegAt(HInstruction* instr,
+inline vixl::aarch64::CPURegister InputCPURegisterOrZeroRegAt(HInstruction* instr,
                                                                      int index) {
   HInstruction* input = instr->InputAt(index);
   Primitive::Type input_type = input->GetType();
   if (input->IsConstant() && input->AsConstant()->IsZeroBitPattern()) {
     return (Primitive::ComponentSize(input_type) >= vixl::aarch64::kXRegSizeInBytes)
-        ?  vixl::aarch64::xzr
+        ? vixl::aarch64::xzr
         : vixl::aarch64::wzr;
   }
   return InputCPURegisterAt(instr, index);
 }
 
-static inline int64_t Int64ConstantFrom(Location location) {
+inline int64_t Int64ConstantFrom(Location location) {
   HConstant* instr = location.GetConstant();
   if (instr->IsIntConstant()) {
     return instr->AsIntConstant()->GetValue();
@@ -148,7 +148,7 @@
   }
 }
 
-static inline vixl::aarch64::Operand OperandFrom(Location location, Primitive::Type type) {
+inline vixl::aarch64::Operand OperandFrom(Location location, Primitive::Type type) {
   if (location.IsRegister()) {
     return vixl::aarch64::Operand(RegisterFrom(location, type));
   } else {
@@ -156,23 +156,23 @@
   }
 }
 
-static inline vixl::aarch64::Operand InputOperandAt(HInstruction* instr, int input_index) {
+inline vixl::aarch64::Operand InputOperandAt(HInstruction* instr, int input_index) {
   return OperandFrom(instr->GetLocations()->InAt(input_index),
                      instr->InputAt(input_index)->GetType());
 }
 
-static inline vixl::aarch64::MemOperand StackOperandFrom(Location location) {
+inline vixl::aarch64::MemOperand StackOperandFrom(Location location) {
   return vixl::aarch64::MemOperand(vixl::aarch64::sp, location.GetStackIndex());
 }
 
-static inline vixl::aarch64::MemOperand HeapOperand(const vixl::aarch64::Register& base,
+inline vixl::aarch64::MemOperand HeapOperand(const vixl::aarch64::Register& base,
                                                     size_t offset = 0) {
   // A heap reference must be 32bit, so fit in a W register.
   DCHECK(base.IsW());
   return vixl::aarch64::MemOperand(base.X(), offset);
 }
 
-static inline vixl::aarch64::MemOperand HeapOperand(const vixl::aarch64::Register& base,
+inline vixl::aarch64::MemOperand HeapOperand(const vixl::aarch64::Register& base,
                                                     const vixl::aarch64::Register& regoffset,
                                                     vixl::aarch64::Shift shift = vixl::aarch64::LSL,
                                                     unsigned shift_amount = 0) {
@@ -181,24 +181,24 @@
   return vixl::aarch64::MemOperand(base.X(), regoffset, shift, shift_amount);
 }
 
-static inline vixl::aarch64::MemOperand HeapOperand(const vixl::aarch64::Register& base,
+inline vixl::aarch64::MemOperand HeapOperand(const vixl::aarch64::Register& base,
                                                     Offset offset) {
   return HeapOperand(base, offset.SizeValue());
 }
 
-static inline vixl::aarch64::MemOperand HeapOperandFrom(Location location, Offset offset) {
+inline vixl::aarch64::MemOperand HeapOperandFrom(Location location, Offset offset) {
   return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset);
 }
 
-static inline Location LocationFrom(const vixl::aarch64::Register& reg) {
+inline Location LocationFrom(const vixl::aarch64::Register& reg) {
   return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.GetCode()));
 }
 
-static inline Location LocationFrom(const vixl::aarch64::FPRegister& fpreg) {
+inline Location LocationFrom(const vixl::aarch64::FPRegister& fpreg) {
   return Location::FpuRegisterLocation(fpreg.GetCode());
 }
 
-static inline vixl::aarch64::Operand OperandFromMemOperand(
+inline vixl::aarch64::Operand OperandFromMemOperand(
     const vixl::aarch64::MemOperand& mem_op) {
   if (mem_op.IsImmediateOffset()) {
     return vixl::aarch64::Operand(mem_op.GetOffset());
@@ -219,7 +219,7 @@
   }
 }
 
-static bool CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) {
+inline bool CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) {
   DCHECK(constant->IsIntConstant() || constant->IsLongConstant() || constant->IsNullConstant())
       << constant->DebugName();
 
@@ -258,7 +258,7 @@
   }
 }
 
-static inline Location ARM64EncodableConstantOrRegister(HInstruction* constant,
+inline Location ARM64EncodableConstantOrRegister(HInstruction* constant,
                                                         HInstruction* instr) {
   if (constant->IsConstant()
       && CanEncodeConstantAsImmediate(constant->AsConstant(), instr)) {
@@ -272,7 +272,7 @@
 // codes are same, we can initialize vixl register list simply by the register masks. Currently,
 // only SP/WSP and ZXR/WZR codes are different between art and vixl.
 // Note: This function is only used for debug checks.
-static inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers,
+inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers,
                                                    size_t num_core,
                                                    uint32_t art_fpu_registers,
                                                    size_t num_fpu) {
@@ -290,7 +290,7 @@
   return true;
 }
 
-static inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
+inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
   switch (op_kind) {
     case HArm64DataProcWithShifterOp::kASR: return vixl::aarch64::ASR;
     case HArm64DataProcWithShifterOp::kLSL: return vixl::aarch64::LSL;
@@ -302,7 +302,7 @@
   }
 }
 
-static inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
+inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
   switch (op_kind) {
     case HArm64DataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB;
     case HArm64DataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH;
@@ -317,7 +317,7 @@
   }
 }
 
-static inline bool CanFitInShifterOperand(HInstruction* instruction) {
+inline bool CanFitInShifterOperand(HInstruction* instruction) {
   if (instruction->IsTypeConversion()) {
     HTypeConversion* conversion = instruction->AsTypeConversion();
     Primitive::Type result_type = conversion->GetResultType();
@@ -332,7 +332,7 @@
   }
 }
 
-static inline bool HasShifterOperand(HInstruction* instr) {
+inline bool HasShifterOperand(HInstruction* instr) {
   // `neg` instructions are an alias of `sub` using the zero register as the
   // first register input.
   bool res = instr->IsAdd() || instr->IsAnd() || instr->IsNeg() ||
@@ -340,7 +340,7 @@
   return res;
 }
 
-static inline bool ShifterOperandSupportsExtension(HInstruction* instruction) {
+inline bool ShifterOperandSupportsExtension(HInstruction* instruction) {
   DCHECK(HasShifterOperand(instruction));
   // Although the `neg` instruction is an alias of the `sub` instruction, `HNeg`
   // does *not* support extension. This is because the `extended register` form
@@ -351,7 +351,7 @@
   return instruction->IsAdd() || instruction->IsSub();
 }
 
-static inline bool IsConstantZeroBitPattern(const HInstruction* instruction) {
+inline bool IsConstantZeroBitPattern(const HInstruction* instruction) {
   return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern();
 }
 
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 1e5f0b6..ce53134 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -452,7 +452,8 @@
                                                                is_referrer,
                                                                invoke_instruction->GetDexPc(),
                                                                /* needs_access_check */ false,
-                                                               /* is_in_dex_cache */ true);
+                                                               /* is_in_dex_cache */ true,
+                                                               /* is_in_boot_image */ false);
 
   HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
   // TODO: Extend reference type propagation to understand the guard.
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 453068b..d7e4c53 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -935,7 +935,8 @@
       IsOutermostCompilingClass(type_index),
       dex_pc,
       needs_access_check,
-      /* is_in_dex_cache */ false);
+      /* is_in_dex_cache */ false,
+      /* is_in_boot_image */ false);
 
   AppendInstruction(load_class);
   HInstruction* cls = load_class;
@@ -1026,7 +1027,8 @@
         is_outer_class,
         dex_pc,
         /*needs_access_check*/ false,
-        /* is_in_dex_cache */ false);
+        /* is_in_dex_cache */ false,
+        /* is_in_boot_image */ false);
     AppendInstruction(load_class);
     clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
     AppendInstruction(clinit_check);
@@ -1384,7 +1386,8 @@
                                                  is_outer_class,
                                                  dex_pc,
                                                  /*needs_access_check*/ false,
-                                                 /* is_in_dex_cache */ false);
+                                                 /* is_in_dex_cache */ false,
+                                                 /* is_in_boot_image */ false);
   AppendInstruction(constant);
 
   HInstruction* cls = constant;
@@ -1545,8 +1548,6 @@
 
 void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
   HInstruction* array = LoadNullCheckedLocal(instruction.VRegA_31t(), dex_pc);
-  HInstruction* length = new (arena_) HArrayLength(array, dex_pc);
-  AppendInstruction(length);
 
   int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
   const Instruction::ArrayDataPayload* payload =
@@ -1554,6 +1555,14 @@
   const uint8_t* data = payload->data;
   uint32_t element_count = payload->element_count;
 
+  if (element_count == 0u) {
+    // For empty payload we emit only the null check above.
+    return;
+  }
+
+  HInstruction* length = new (arena_) HArrayLength(array, dex_pc);
+  AppendInstruction(length);
+
   // Implementation of this DEX instruction seems to be that the bounds check is
   // done before doing any stores.
   HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
@@ -1653,7 +1662,8 @@
       IsOutermostCompilingClass(type_index),
       dex_pc,
       !can_access,
-      /* is_in_dex_cache */ false);
+      /* is_in_dex_cache */ false,
+      /* is_in_boot_image */ false);
   AppendInstruction(cls);
 
   TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -2628,7 +2638,8 @@
           IsOutermostCompilingClass(type_index),
           dex_pc,
           !can_access,
-          /* is_in_dex_cache */ false));
+          /* is_in_dex_cache */ false,
+          /* is_in_boot_image */ false));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index a1fc7c3..67640a1 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -656,6 +656,9 @@
                                                                LocationSummary::kCallOnSlowPath :
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
+  if (can_call && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetInAt(2, Location::RequiresRegister());
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 7482057..082076d 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -894,6 +894,9 @@
                                                                LocationSummary::kCallOnSlowPath :
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
+  if (can_call && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetInAt(2, Location::RequiresRegister());
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index ef4d0f1..d17f85e 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1976,6 +1976,9 @@
                                                                LocationSummary::kCallOnSlowPath :
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
+  if (can_call && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetInAt(2, Location::RequiresRegister());
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index c7e3007..f8f30d9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2109,6 +2109,9 @@
                                                                LocationSummary::kCallOnSlowPath :
                                                                LocationSummary::kNoCall,
                                                            kIntrinsified);
+  if (can_call && kUseBakerReadBarrier) {
+    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
+  }
   locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
   locations->SetInAt(1, Location::RequiresRegister());
   locations->SetInAt(2, Location::RequiresRegister());
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 83596da..1b1b3a7 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -27,12 +27,14 @@
     : inputs_(instruction->InputCount(),
               instruction->GetBlock()->GetGraph()->GetArena()->Adapter(kArenaAllocLocationSummary)),
       temps_(instruction->GetBlock()->GetGraph()->GetArena()->Adapter(kArenaAllocLocationSummary)),
-      output_overlaps_(Location::kOutputOverlap),
       call_kind_(call_kind),
+      intrinsified_(intrinsified),
+      has_custom_slow_path_calling_convention_(false),
+      output_overlaps_(Location::kOutputOverlap),
       stack_mask_(nullptr),
       register_mask_(0),
       live_registers_(),
-      intrinsified_(intrinsified) {
+      custom_slow_path_caller_saves_() {
   instruction->SetLocations(this);
 
   if (NeedsSafepoint()) {
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 5fdfb9b..4384042 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -20,6 +20,7 @@
 #include "base/arena_containers.h"
 #include "base/arena_object.h"
 #include "base/bit_field.h"
+#include "base/bit_utils.h"
 #include "base/bit_vector.h"
 #include "base/value_object.h"
 
@@ -452,7 +453,7 @@
   }
 
   size_t GetNumberOfRegisters() const {
-    return __builtin_popcount(core_registers_) + __builtin_popcount(floating_point_registers_);
+    return POPCOUNT(core_registers_) + POPCOUNT(floating_point_registers_);
   }
 
   uint32_t GetCoreRegisters() const {
@@ -466,8 +467,6 @@
  private:
   uint32_t core_registers_;
   uint32_t floating_point_registers_;
-
-  DISALLOW_COPY_AND_ASSIGN(RegisterSet);
 };
 
 static constexpr bool kIntrinsified = true;
@@ -569,6 +568,21 @@
     return CanCall();
   }
 
+  void SetCustomSlowPathCallerSaves(const RegisterSet& caller_saves) {
+    DCHECK(OnlyCallsOnSlowPath());
+    has_custom_slow_path_calling_convention_ = true;
+    custom_slow_path_caller_saves_ = caller_saves;
+  }
+
+  bool HasCustomSlowPathCallingConvention() const {
+    return has_custom_slow_path_calling_convention_;
+  }
+
+  const RegisterSet& GetCustomSlowPathCallerSaves() const {
+    DCHECK(HasCustomSlowPathCallingConvention());
+    return custom_slow_path_caller_saves_;
+  }
+
   void SetStackBit(uint32_t index) {
     stack_mask_->SetBit(index);
   }
@@ -628,18 +642,18 @@
     return intrinsified_;
   }
 
-  void SetIntrinsified(bool intrinsified) {
-    intrinsified_ = intrinsified;
-  }
-
  private:
   ArenaVector<Location> inputs_;
   ArenaVector<Location> temps_;
+  const CallKind call_kind_;
+  // Whether these are locations for an intrinsified call.
+  const bool intrinsified_;
+  // Whether the slow path has default or custom calling convention.
+  bool has_custom_slow_path_calling_convention_;
   // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot
   // share the same register as the inputs.
   Location::OutputOverlap output_overlaps_;
   Location output_;
-  const CallKind call_kind_;
 
   // Mask of objects that live in the stack.
   BitVector* stack_mask_;
@@ -650,8 +664,8 @@
   // Registers that are in use at this position.
   RegisterSet live_registers_;
 
-  // Whether these are locations for an intrinsified call.
-  bool intrinsified_;
+  // Custom slow path caller saves. Valid only if indicated by slow_path_calling_convention_.
+  RegisterSet custom_slow_path_caller_saves_;
 
   friend class RegisterAllocatorTest;
   DISALLOW_COPY_AND_ASSIGN(LocationSummary);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 19e499b..149a71d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5461,7 +5461,8 @@
              bool is_referrers_class,
              uint32_t dex_pc,
              bool needs_access_check,
-             bool is_in_dex_cache)
+             bool is_in_dex_cache,
+             bool is_in_boot_image)
       : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
@@ -5475,6 +5476,7 @@
         is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
     SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
     SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
+    SetPackedFlag<kFlagIsInBootImage>(is_in_boot_image);
     SetPackedFlag<kFlagGenerateClInitCheck>(false);
   }
 
@@ -5565,6 +5567,7 @@
   bool IsReferrersClass() const { return GetLoadKind() == LoadKind::kReferrersClass; }
   bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
   bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+  bool IsInBootImage() const { return GetPackedFlag<kFlagIsInBootImage>(); }
   bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); }
 
   void MarkInDexCache() {
@@ -5574,6 +5577,10 @@
     SetSideEffects(SideEffects::None());
   }
 
+  void MarkInBootImage() {
+    SetPackedFlag<kFlagIsInBootImage>(true);
+  }
+
   void AddSpecialInput(HInstruction* special_input);
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
@@ -5591,9 +5598,10 @@
  private:
   static constexpr size_t kFlagNeedsAccessCheck    = kNumberOfGenericPackedBits;
   static constexpr size_t kFlagIsInDexCache        = kFlagNeedsAccessCheck + 1;
+  static constexpr size_t kFlagIsInBootImage       = kFlagIsInDexCache + 1;
   // Whether this instruction must generate the initialization check.
   // Used for code generation.
-  static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1;
+  static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInBootImage + 1;
   static constexpr size_t kFieldLoadKind           = kFlagGenerateClInitCheck + 1;
   static constexpr size_t kFieldLoadKindSize =
       MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc
index 3450286..ad7a8a3 100644
--- a/compiler/optimizing/register_allocation_resolver.cc
+++ b/compiler/optimizing/register_allocation_resolver.cc
@@ -28,8 +28,7 @@
         codegen_(codegen),
         liveness_(liveness) {}
 
-void RegisterAllocationResolver::Resolve(size_t max_safepoint_live_core_regs,
-                                         size_t max_safepoint_live_fp_regs,
+void RegisterAllocationResolver::Resolve(ArrayRef<HInstruction* const> safepoints,
                                          size_t reserved_out_slots,
                                          size_t int_spill_slots,
                                          size_t long_spill_slots,
@@ -43,10 +42,13 @@
                      + double_spill_slots
                      + catch_phi_spill_slots;
 
+  // Update safepoints and calculate the size of the spills.
+  UpdateSafepointLiveRegisters();
+  size_t maximum_safepoint_spill_size = CalculateMaximumSafepointSpillSize(safepoints);
+
   // Computes frame size and spill mask.
   codegen_->InitializeCodeGeneration(spill_slots,
-                                     max_safepoint_live_core_regs,
-                                     max_safepoint_live_fp_regs,
+                                     maximum_safepoint_spill_size,
                                      reserved_out_slots,  // Includes slot(s) for the art method.
                                      codegen_->GetGraph()->GetLinearOrder());
 
@@ -135,8 +137,7 @@
   // Connect siblings and resolve inputs.
   for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
     HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
-    ConnectSiblings(instruction->GetLiveInterval(),
-                    max_safepoint_live_core_regs + max_safepoint_live_fp_regs);
+    ConnectSiblings(instruction->GetLiveInterval());
   }
 
   // Resolve non-linear control flow across branches. Order does not matter.
@@ -222,8 +223,73 @@
   }
 }
 
-void RegisterAllocationResolver::ConnectSiblings(LiveInterval* interval,
-                                                 size_t max_safepoint_live_regs) {
+void RegisterAllocationResolver::UpdateSafepointLiveRegisters() {
+  for (size_t i = 0, e = liveness_.GetNumberOfSsaValues(); i < e; ++i) {
+    HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i);
+    for (LiveInterval* current = instruction->GetLiveInterval();
+         current != nullptr;
+         current = current->GetNextSibling()) {
+      if (!current->HasRegister()) {
+        continue;
+      }
+      Location source = current->ToLocation();
+      for (SafepointPosition* safepoint_position = current->GetFirstSafepoint();
+           safepoint_position != nullptr;
+           safepoint_position = safepoint_position->GetNext()) {
+        DCHECK(current->CoversSlow(safepoint_position->GetPosition()));
+        LocationSummary* locations = safepoint_position->GetLocations();
+        switch (source.GetKind()) {
+          case Location::kRegister:
+          case Location::kFpuRegister: {
+            locations->AddLiveRegister(source);
+            break;
+          }
+          case Location::kRegisterPair:
+          case Location::kFpuRegisterPair: {
+            locations->AddLiveRegister(source.ToLow());
+            locations->AddLiveRegister(source.ToHigh());
+            break;
+          }
+          case Location::kStackSlot:  // Fall-through
+          case Location::kDoubleStackSlot:  // Fall-through
+          case Location::kConstant: {
+            // Nothing to do.
+            break;
+          }
+          default: {
+            LOG(FATAL) << "Unexpected location for object";
+          }
+        }
+      }
+    }
+  }
+}
+
+size_t RegisterAllocationResolver::CalculateMaximumSafepointSpillSize(
+    ArrayRef<HInstruction* const> safepoints) {
+  size_t core_register_spill_size = codegen_->GetWordSize();
+  size_t fp_register_spill_size = codegen_->GetFloatingPointSpillSlotSize();
+  size_t maximum_safepoint_spill_size = 0u;
+  for (HInstruction* instruction : safepoints) {
+    LocationSummary* locations = instruction->GetLocations();
+    if (locations->OnlyCallsOnSlowPath()) {
+      size_t core_spills =
+          codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ true);
+      size_t fp_spills =
+          codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ false);
+      size_t spill_size =
+          core_register_spill_size * core_spills + fp_register_spill_size * fp_spills;
+      maximum_safepoint_spill_size = std::max(maximum_safepoint_spill_size, spill_size);
+    } else if (locations->CallsOnMainAndSlowPath()) {
+      // Nothing to spill on the slow path if the main path already clobbers caller-saves.
+      DCHECK_EQ(0u, codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ true));
+      DCHECK_EQ(0u, codegen_->GetNumberOfSlowPathSpills(locations, /* core_registers */ false));
+    }
+  }
+  return maximum_safepoint_spill_size;
+}
+
+void RegisterAllocationResolver::ConnectSiblings(LiveInterval* interval) {
   LiveInterval* current = interval;
   if (current->HasSpillSlot()
       && current->HasRegister()
@@ -306,48 +372,16 @@
          safepoint_position = safepoint_position->GetNext()) {
       DCHECK(current->CoversSlow(safepoint_position->GetPosition()));
 
-      LocationSummary* locations = safepoint_position->GetLocations();
-      if ((current->GetType() == Primitive::kPrimNot) && current->GetParent()->HasSpillSlot()) {
+      if (current->GetType() == Primitive::kPrimNot) {
         DCHECK(interval->GetDefinedBy()->IsActualObject())
             << interval->GetDefinedBy()->DebugName()
             << "@" << safepoint_position->GetInstruction()->DebugName();
-        locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
-      }
-
-      switch (source.GetKind()) {
-        case Location::kRegister: {
-          locations->AddLiveRegister(source);
-          if (kIsDebugBuild && locations->OnlyCallsOnSlowPath()) {
-            DCHECK_LE(locations->GetNumberOfLiveRegisters(),
-                      max_safepoint_live_regs);
-          }
-          if (current->GetType() == Primitive::kPrimNot) {
-            DCHECK(interval->GetDefinedBy()->IsActualObject())
-                << interval->GetDefinedBy()->DebugName()
-                << "@" << safepoint_position->GetInstruction()->DebugName();
-            locations->SetRegisterBit(source.reg());
-          }
-          break;
+        LocationSummary* locations = safepoint_position->GetLocations();
+        if (current->GetParent()->HasSpillSlot()) {
+          locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
         }
-        case Location::kFpuRegister: {
-          locations->AddLiveRegister(source);
-          break;
-        }
-
-        case Location::kRegisterPair:
-        case Location::kFpuRegisterPair: {
-          locations->AddLiveRegister(source.ToLow());
-          locations->AddLiveRegister(source.ToHigh());
-          break;
-        }
-        case Location::kStackSlot:  // Fall-through
-        case Location::kDoubleStackSlot:  // Fall-through
-        case Location::kConstant: {
-          // Nothing to do.
-          break;
-        }
-        default: {
-          LOG(FATAL) << "Unexpected location for object";
+        if (source.GetKind() == Location::kRegister) {
+          locations->SetRegisterBit(source.reg());
         }
       }
     }
diff --git a/compiler/optimizing/register_allocation_resolver.h b/compiler/optimizing/register_allocation_resolver.h
index 6ceb9bc..a70ceae 100644
--- a/compiler/optimizing/register_allocation_resolver.h
+++ b/compiler/optimizing/register_allocation_resolver.h
@@ -20,6 +20,7 @@
 #include "base/arena_containers.h"
 #include "base/value_object.h"
 #include "primitive.h"
+#include "utils/array_ref.h"
 
 namespace art {
 
@@ -43,8 +44,7 @@
                              CodeGenerator* codegen,
                              const SsaLivenessAnalysis& liveness);
 
-  void Resolve(size_t max_safepoint_live_core_regs,
-               size_t max_safepoint_live_fp_regs,
+  void Resolve(ArrayRef<HInstruction* const> safepoints,
                size_t reserved_out_slots,  // Includes slot(s) for the art method.
                size_t int_spill_slots,
                size_t long_spill_slots,
@@ -54,10 +54,14 @@
                const ArenaVector<LiveInterval*>& temp_intervals);
 
  private:
+  // Update live registers of safepoint location summary.
+  void UpdateSafepointLiveRegisters();
+
+  // Calculate the maximum size of the spill area for safepoints.
+  size_t CalculateMaximumSafepointSpillSize(ArrayRef<HInstruction* const> safepoints);
+
   // Connect adjacent siblings within blocks, and resolve inputs along the way.
-  // Uses max_safepoint_live_regs to check that we did not underestimate the
-  // number of live registers at safepoints.
-  void ConnectSiblings(LiveInterval* interval, size_t max_safepoint_live_regs);
+  void ConnectSiblings(LiveInterval* interval);
 
   // Connect siblings between block entries and exits.
   void ConnectSplitSiblings(LiveInterval* interval, HBasicBlock* from, HBasicBlock* to) const;
diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc
index a21595f..7178399 100644
--- a/compiler/optimizing/register_allocator_graph_color.cc
+++ b/compiler/optimizing/register_allocator_graph_color.cc
@@ -438,8 +438,7 @@
   // track of live intervals across safepoints.
   // TODO: Should build safepoints elsewhere.
   void BuildInterferenceGraph(const ArenaVector<LiveInterval*>& intervals,
-                              const ArenaVector<InterferenceNode*>& physical_nodes,
-                              ArenaVector<InterferenceNode*>* safepoints);
+                              const ArenaVector<InterferenceNode*>& physical_nodes);
 
   // Add coalesce opportunities to interference nodes.
   void FindCoalesceOpportunities();
@@ -566,11 +565,7 @@
         num_long_spill_slots_(0),
         catch_phi_spill_slot_counter_(0),
         reserved_art_method_slots_(ComputeReservedArtMethodSlots(*codegen)),
-        reserved_out_slots_(codegen->GetGraph()->GetMaximumNumberOfOutVRegs()),
-        number_of_globally_blocked_core_regs_(0),
-        number_of_globally_blocked_fp_regs_(0),
-        max_safepoint_live_core_regs_(0),
-        max_safepoint_live_fp_regs_(0) {
+        reserved_out_slots_(codegen->GetGraph()->GetMaximumNumberOfOutVRegs()) {
   // Before we ask for blocked registers, set them up in the code generator.
   codegen->SetupBlockedRegisters();
 
@@ -584,7 +579,6 @@
     physical_core_nodes_[i]->stage = NodeStage::kPrecolored;
     core_intervals_.push_back(interval);
     if (codegen_->IsBlockedCoreRegister(i)) {
-      ++number_of_globally_blocked_core_regs_;
       interval->AddRange(0, liveness.GetMaxLifetimePosition());
     }
   }
@@ -597,7 +591,6 @@
     physical_fp_nodes_[i]->stage = NodeStage::kPrecolored;
     fp_intervals_.push_back(interval);
     if (codegen_->IsBlockedFloatingPointRegister(i)) {
-      ++number_of_globally_blocked_fp_regs_;
       interval->AddRange(0, liveness.GetMaxLifetimePosition());
     }
   }
@@ -638,7 +631,7 @@
       ArenaVector<InterferenceNode*>& physical_nodes = processing_core_regs
           ? physical_core_nodes_
           : physical_fp_nodes_;
-      iteration.BuildInterferenceGraph(intervals, physical_nodes, &safepoints);
+      iteration.BuildInterferenceGraph(intervals, physical_nodes);
 
       // (3) Add coalesce opportunities.
       //     If we have tried coloring the graph a suspiciously high number of times, give
@@ -667,19 +660,6 @@
         // Assign spill slots.
         AllocateSpillSlots(iteration.GetPrunableNodes());
 
-        // Compute the maximum number of live registers across safepoints.
-        // Notice that we do not count globally blocked registers, such as the stack pointer.
-        if (safepoints.size() > 0) {
-          size_t max_safepoint_live_regs = ComputeMaxSafepointLiveRegisters(safepoints);
-          if (processing_core_regs) {
-            max_safepoint_live_core_regs_ =
-                max_safepoint_live_regs - number_of_globally_blocked_core_regs_;
-          } else {
-            max_safepoint_live_fp_regs_=
-                max_safepoint_live_regs - number_of_globally_blocked_fp_regs_;
-          }
-        }
-
         // Tell the code generator which registers were allocated.
         // We only look at prunable_nodes because we already told the code generator about
         // fixed intervals while processing instructions. We also ignore the fixed intervals
@@ -711,8 +691,7 @@
 
   // (6) Resolve locations and deconstruct SSA form.
   RegisterAllocationResolver(allocator_, codegen_, liveness_)
-      .Resolve(max_safepoint_live_core_regs_,
-               max_safepoint_live_fp_regs_,
+      .Resolve(ArrayRef<HInstruction* const>(safepoints_),
                reserved_art_method_slots_ + reserved_out_slots_,
                num_int_spill_slots_,
                num_long_spill_slots_,
@@ -989,24 +968,9 @@
 
 void RegisterAllocatorGraphColor::CheckForSafepoint(HInstruction* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  size_t position = instruction->GetLifetimePosition();
 
   if (locations->NeedsSafepoint()) {
     safepoints_.push_back(instruction);
-    if (locations->OnlyCallsOnSlowPath()) {
-      // We add a synthesized range at this position to record the live registers
-      // at this position. Ideally, we could just update the safepoints when locations
-      // are updated, but we currently need to know the full stack size before updating
-      // locations (because of parameters and the fact that we don't have a frame pointer).
-      // And knowing the full stack size requires to know the maximum number of live
-      // registers at calls in slow paths.
-      // By adding the following interval in the algorithm, we can compute this
-      // maximum before updating locations.
-      LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction);
-      interval->AddRange(position, position + 1);
-      core_intervals_.push_back(interval);
-      fp_intervals_.push_back(interval);
-    }
   }
 }
 
@@ -1110,11 +1074,6 @@
                                                  bool both_directions) {
   if (from->IsPrecolored()) {
     // We save space by ignoring outgoing edges from fixed nodes.
-  } else if (to->GetInterval()->IsSlowPathSafepoint()) {
-    // Safepoint intervals are only there to count max live registers,
-    // so no need to give them incoming interference edges.
-    // This is also necessary for correctness, because we don't want nodes
-    // to remove themselves from safepoint adjacency sets when they're pruned.
   } else if (to->IsPrecolored()) {
     // It is important that only a single node represents a given fixed register in the
     // interference graph. We retrieve that node here.
@@ -1200,8 +1159,7 @@
 
 void ColoringIteration::BuildInterferenceGraph(
     const ArenaVector<LiveInterval*>& intervals,
-    const ArenaVector<InterferenceNode*>& physical_nodes,
-    ArenaVector<InterferenceNode*>* safepoints) {
+    const ArenaVector<InterferenceNode*>& physical_nodes) {
   DCHECK(interval_node_map_.Empty() && prunable_nodes_.empty());
   // Build the interference graph efficiently by ordering range endpoints
   // by position and doing a linear sweep to find interferences. (That is, we
@@ -1236,11 +1194,6 @@
           node->SetAlias(physical_node);
           DCHECK_EQ(node->GetInterval()->GetRegister(),
                     physical_node->GetInterval()->GetRegister());
-        } else if (sibling->IsSlowPathSafepoint()) {
-          // Safepoint intervals are synthesized to count max live registers.
-          // They will be processed separately after coloring.
-          node->stage = NodeStage::kSafepoint;
-          safepoints->push_back(node);
         } else {
           node->stage = NodeStage::kPrunable;
           prunable_nodes_.push_back(node);
@@ -1494,7 +1447,6 @@
   // filled by FindCoalesceOpportunities().
   for (InterferenceNode* node : prunable_nodes_) {
     DCHECK(!node->IsPrecolored()) << "Fixed nodes should never be pruned";
-    DCHECK(!node->GetInterval()->IsSlowPathSafepoint()) << "Safepoint nodes should never be pruned";
     if (IsLowDegreeNode(node, num_regs_)) {
       if (node->GetCoalesceOpportunities().empty()) {
         // Simplify Worklist.
@@ -1577,8 +1529,6 @@
   pruned_nodes_.push(node);
 
   for (InterferenceNode* adj : node->GetAdjacentNodes()) {
-    DCHECK(!adj->GetInterval()->IsSlowPathSafepoint())
-        << "Nodes should never interfere with synthesized safepoint nodes";
     DCHECK_NE(adj->stage, NodeStage::kPruned) << "Should be no interferences with pruned nodes";
 
     if (adj->IsPrecolored()) {
@@ -1938,18 +1888,6 @@
   return successful;
 }
 
-size_t RegisterAllocatorGraphColor::ComputeMaxSafepointLiveRegisters(
-    const ArenaVector<InterferenceNode*>& safepoints) {
-  size_t max_safepoint_live_regs = 0;
-  for (InterferenceNode* safepoint : safepoints) {
-    DCHECK(safepoint->GetInterval()->IsSlowPathSafepoint());
-    std::bitset<kMaxNumRegs> conflict_mask = BuildConflictMask(safepoint->GetAdjacentNodes());
-    size_t live_regs = conflict_mask.count();
-    max_safepoint_live_regs = std::max(max_safepoint_live_regs, live_regs);
-  }
-  return max_safepoint_live_regs;
-}
-
 void RegisterAllocatorGraphColor::AllocateSpillSlots(const ArenaVector<InterferenceNode*>& nodes) {
   // The register allocation resolver will organize the stack based on value type,
   // so we assign stack slots for each value type separately.
diff --git a/compiler/optimizing/register_allocator_graph_color.h b/compiler/optimizing/register_allocator_graph_color.h
index ed12561..548687f 100644
--- a/compiler/optimizing/register_allocator_graph_color.h
+++ b/compiler/optimizing/register_allocator_graph_color.h
@@ -140,10 +140,6 @@
 
   bool IsCallerSave(size_t reg, bool processing_core_regs);
 
-  // Return the maximum number of registers live at safepoints,
-  // based on the outgoing interference edges of safepoint nodes.
-  size_t ComputeMaxSafepointLiveRegisters(const ArenaVector<InterferenceNode*>& safepoints);
-
   // Assigns stack slots to a list of intervals, ensuring that interfering intervals are not
   // assigned the same stack slot.
   void ColorSpillSlots(ArenaVector<LiveInterval*>* nodes,
@@ -187,14 +183,6 @@
   // Number of stack slots needed for outgoing arguments.
   const size_t reserved_out_slots_;
 
-  // The number of globally blocked core and floating point registers, such as the stack pointer.
-  size_t number_of_globally_blocked_core_regs_;
-  size_t number_of_globally_blocked_fp_regs_;
-
-  // The maximum number of registers live at safe points. Needed by the code generator.
-  size_t max_safepoint_live_core_regs_;
-  size_t max_safepoint_live_fp_regs_;
-
   friend class ColoringIteration;
 
   DISALLOW_COPY_AND_ASSIGN(RegisterAllocatorGraphColor);
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index 768ed2d..6910c71 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -63,9 +63,7 @@
         registers_array_(nullptr),
         blocked_core_registers_(codegen->GetBlockedCoreRegisters()),
         blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()),
-        reserved_out_slots_(0),
-        maximum_number_of_live_core_registers_(0),
-        maximum_number_of_live_fp_registers_(0) {
+        reserved_out_slots_(0) {
   temp_intervals_.reserve(4);
   int_spill_slots_.reserve(kDefaultNumberOfSpillSlots);
   long_spill_slots_.reserve(kDefaultNumberOfSpillSlots);
@@ -92,8 +90,7 @@
 void RegisterAllocatorLinearScan::AllocateRegisters() {
   AllocateRegistersInternal();
   RegisterAllocationResolver(allocator_, codegen_, liveness_)
-      .Resolve(maximum_number_of_live_core_registers_,
-               maximum_number_of_live_fp_registers_,
+      .Resolve(ArrayRef<HInstruction* const>(safepoints_),
                reserved_out_slots_,
                int_spill_slots_.size(),
                long_spill_slots_.size(),
@@ -283,20 +280,6 @@
       return;
     }
     safepoints_.push_back(instruction);
-    if (locations->OnlyCallsOnSlowPath()) {
-      // We add a synthesized range at this position to record the live registers
-      // at this position. Ideally, we could just update the safepoints when locations
-      // are updated, but we currently need to know the full stack size before updating
-      // locations (because of parameters and the fact that we don't have a frame pointer).
-      // And knowing the full stack size requires to know the maximum number of live
-      // registers at calls in slow paths.
-      // By adding the following interval in the algorithm, we can compute this
-      // maximum before updating locations.
-      LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction);
-      interval->AddRange(position, position + 1);
-      AddSorted(&unhandled_core_intervals_, interval);
-      AddSorted(&unhandled_fp_intervals_, interval);
-    }
   }
 
   if (locations->WillCall()) {
@@ -569,20 +552,6 @@
         });
     inactive_.erase(inactive_kept_end, inactive_to_handle_end);
 
-    if (current->IsSlowPathSafepoint()) {
-      // Synthesized interval to record the maximum number of live registers
-      // at safepoints. No need to allocate a register for it.
-      if (processing_core_registers_) {
-        maximum_number_of_live_core_registers_ =
-          std::max(maximum_number_of_live_core_registers_, active_.size());
-      } else {
-        maximum_number_of_live_fp_registers_ =
-          std::max(maximum_number_of_live_fp_registers_, active_.size());
-      }
-      DCHECK(unhandled_->empty() || unhandled_->back()->GetStart() > current->GetStart());
-      continue;
-    }
-
     if (current->IsHighInterval() && !current->GetLowInterval()->HasRegister()) {
       DCHECK(!current->HasRegister());
       // Allocating the low part was unsucessful. The splitted interval for the high part
@@ -685,7 +654,7 @@
   // the next intersection with `current`.
   for (LiveInterval* inactive : inactive_) {
     // Temp/Slow-path-safepoint interval has no holes.
-    DCHECK(!inactive->IsTemp() && !inactive->IsSlowPathSafepoint());
+    DCHECK(!inactive->IsTemp());
     if (!current->IsSplit() && !inactive->IsFixed()) {
       // Neither current nor inactive are fixed.
       // Thanks to SSA, a non-split interval starting in a hole of an
@@ -933,7 +902,7 @@
   // start of current.
   for (LiveInterval* inactive : inactive_) {
     // Temp/Slow-path-safepoint interval has no holes.
-    DCHECK(!inactive->IsTemp() && !inactive->IsSlowPathSafepoint());
+    DCHECK(!inactive->IsTemp());
     if (!current->IsSplit() && !inactive->IsFixed()) {
       // Neither current nor inactive are fixed.
       // Thanks to SSA, a non-split interval starting in a hole of an
@@ -1085,12 +1054,6 @@
     if (current->StartsAfter(interval) && !current->IsHighInterval()) {
       insert_at = i;
       break;
-    } else if ((current->GetStart() == interval->GetStart()) && current->IsSlowPathSafepoint()) {
-      // Ensure the slow path interval is the last to be processed at its location: we want the
-      // interval to know all live registers at this location.
-      DCHECK(i == 1 || (*array)[i - 2u]->StartsAfter(current));
-      insert_at = i;
-      break;
     }
   }
 
diff --git a/compiler/optimizing/register_allocator_linear_scan.h b/compiler/optimizing/register_allocator_linear_scan.h
index 1a643a0..b3834f4 100644
--- a/compiler/optimizing/register_allocator_linear_scan.h
+++ b/compiler/optimizing/register_allocator_linear_scan.h
@@ -171,12 +171,6 @@
   // Slots reserved for out arguments.
   size_t reserved_out_slots_;
 
-  // The maximum live core registers at safepoints.
-  size_t maximum_number_of_live_core_registers_;
-
-  // The maximum live FP registers at safepoints.
-  size_t maximum_number_of_live_fp_registers_;
-
   ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
   ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
 
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 81163e2..b8e1379 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -176,6 +176,7 @@
   uint32_t type_index = load_class->GetTypeIndex();
 
   bool is_in_dex_cache = false;
+  bool is_in_boot_image = false;
   HLoadClass::LoadKind desired_load_kind;
   uint64_t address = 0u;  // Class or dex cache element address.
   {
@@ -192,45 +193,42 @@
       // Compiling boot image. Check if the class is a boot image class.
       DCHECK(!runtime->UseJitCompilation());
       if (!compiler_driver_->GetSupportBootImageFixup()) {
-        // MIPS/MIPS64 or compiler_driver_test. Do not sharpen.
+        // MIPS64 or compiler_driver_test. Do not sharpen.
         desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+      } else if ((klass != nullptr) && compiler_driver_->IsImageClass(
+          dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
+        is_in_boot_image = true;
+        is_in_dex_cache = true;
+        desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
+            ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
+            : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
       } else {
-        if (klass != nullptr &&
-            compiler_driver_->IsImageClass(
-                dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
-          is_in_dex_cache = true;
-          desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
-              ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
-              : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
-        } else {
-          // Not a boot image class. We must go through the dex cache.
-          DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
-          desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
-        }
-      }
-    } else if (runtime->UseJitCompilation()) {
-      // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
-      // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
-      is_in_dex_cache = (klass != nullptr);
-      if (klass != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(klass)) {
-        // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
-        desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
-        address = reinterpret_cast64<uint64_t>(klass);
-      } else {
-        // Note: If the class is not in the dex cache or isn't initialized, the
-        // instruction needs environment and will not be inlined across dex files.
-        // Within a dex file, the slow-path helper loads the correct class and
-        // inlined frames are used correctly for OOM stack trace.
-        // TODO: Write a test for this. Bug: 29416588
-        desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress;
-        void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index];
-        address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
+        // Not a boot image class. We must go through the dex cache.
+        DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
+        desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
       }
     } else {
-      // AOT app compilation. Check if the class is in the boot image.
-      if ((klass != nullptr) &&
-          runtime->GetHeap()->ObjectIsInBootImageSpace(klass) &&
-          !codegen_->GetCompilerOptions().GetCompilePic()) {
+      is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
+      if (runtime->UseJitCompilation()) {
+        // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+        // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+        is_in_dex_cache = (klass != nullptr);
+        if (is_in_boot_image) {
+          // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
+          desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+          address = reinterpret_cast64<uint64_t>(klass);
+        } else {
+          // Note: If the class is not in the dex cache or isn't initialized, the
+          // instruction needs environment and will not be inlined across dex files.
+          // Within a dex file, the slow-path helper loads the correct class and
+          // inlined frames are used correctly for OOM stack trace.
+          // TODO: Write a test for this. Bug: 29416588
+          desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress;
+          void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index];
+          address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
+        }
+        // AOT app compilation. Check if the class is in the boot image.
+      } else if (is_in_boot_image && !codegen_->GetCompilerOptions().GetCompilePic()) {
         desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
         address = reinterpret_cast64<uint64_t>(klass);
       } else {
@@ -247,6 +245,9 @@
   if (is_in_dex_cache) {
     load_class->MarkInDexCache();
   }
+  if (is_in_boot_image) {
+    load_class->MarkInBootImage();
+  }
 
   HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind);
   switch (load_kind) {
@@ -296,7 +297,15 @@
       DCHECK(!runtime->UseJitCompilation());
       mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
       CHECK(string != nullptr);
-      // TODO: In follow up CL, add PcRelative and Address back in.
+      if (compiler_driver_->GetSupportBootImageFixup()) {
+        DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
+        desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
+            ? HLoadString::LoadKind::kBootImageLinkTimePcRelative
+            : HLoadString::LoadKind::kBootImageLinkTimeAddress;
+      } else {
+        // MIPS64 or compiler_driver_test. Do not sharpen.
+        DCHECK_EQ(desired_load_kind, HLoadString::LoadKind::kDexCacheViaMethod);
+      }
     } else if (runtime->UseJitCompilation()) {
       // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
       // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 9f94c83..0be1611 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -208,11 +208,6 @@
     return new (allocator) LiveInterval(allocator, type, instruction);
   }
 
-  static LiveInterval* MakeSlowPathInterval(ArenaAllocator* allocator, HInstruction* instruction) {
-    return new (allocator) LiveInterval(
-        allocator, Primitive::kPrimVoid, instruction, false, kNoRegister, false, true);
-  }
-
   static LiveInterval* MakeFixedInterval(ArenaAllocator* allocator, int reg, Primitive::Type type) {
     return new (allocator) LiveInterval(allocator, type, nullptr, true, reg, false);
   }
@@ -223,7 +218,6 @@
 
   bool IsFixed() const { return is_fixed_; }
   bool IsTemp() const { return is_temp_; }
-  bool IsSlowPathSafepoint() const { return is_slow_path_safepoint_; }
   // This interval is the result of a split.
   bool IsSplit() const { return parent_ != this; }
 
@@ -790,7 +784,7 @@
     DCHECK(!HasHighInterval());
     DCHECK(!HasLowInterval());
     high_or_low_interval_ = new (allocator_) LiveInterval(
-        allocator_, type_, defined_by_, false, kNoRegister, is_temp, false, true);
+        allocator_, type_, defined_by_, false, kNoRegister, is_temp, true);
     high_or_low_interval_->high_or_low_interval_ = this;
     if (first_range_ != nullptr) {
       high_or_low_interval_->first_range_ = first_range_->Dup(allocator_);
@@ -919,7 +913,6 @@
                bool is_fixed = false,
                int reg = kNoRegister,
                bool is_temp = false,
-               bool is_slow_path_safepoint = false,
                bool is_high_interval = false)
       : allocator_(allocator),
         first_range_(nullptr),
@@ -936,7 +929,6 @@
         spill_slot_(kNoSpillSlot),
         is_fixed_(is_fixed),
         is_temp_(is_temp),
-        is_slow_path_safepoint_(is_slow_path_safepoint),
         is_high_interval_(is_high_interval),
         high_or_low_interval_(nullptr),
         defined_by_(defined_by) {}
@@ -1087,9 +1079,6 @@
   // Whether the interval is for a temporary.
   const bool is_temp_;
 
-  // Whether the interval is for a safepoint that calls on slow path.
-  const bool is_slow_path_safepoint_;
-
   // Whether this interval is a synthesized interval for register pair.
   const bool is_high_interval_;
 
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc
index e0bfa12..cf7a4d1 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm.cc
@@ -367,11 +367,21 @@
       CHECK(src.IsCoreRegister()) << src;
       __ mov(dst.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
     } else if (dst.IsDRegister()) {
-      CHECK(src.IsDRegister()) << src;
-      __ vmovd(dst.AsDRegister(), src.AsDRegister());
+      if (src.IsDRegister()) {
+        __ vmovd(dst.AsDRegister(), src.AsDRegister());
+      } else {
+        // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi})
+        CHECK(src.IsRegisterPair()) << src;
+        __ vmovdrr(dst.AsDRegister(), src.AsRegisterPairLow(), src.AsRegisterPairHigh());
+      }
     } else if (dst.IsSRegister()) {
-      CHECK(src.IsSRegister()) << src;
-      __ vmovs(dst.AsSRegister(), src.AsSRegister());
+      if (src.IsSRegister()) {
+        __ vmovs(dst.AsSRegister(), src.AsSRegister());
+      } else {
+        // VMOV Sn, Rn  (Sn = Rn)
+        CHECK(src.IsCoreRegister()) << src;
+        __ vmovsr(dst.AsSRegister(), src.AsCoreRegister());
+      }
     } else {
       CHECK(dst.IsRegisterPair()) << dst;
       CHECK(src.IsRegisterPair()) << src;
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 719fe7f..a03dd74 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -73,12 +73,11 @@
   cfi().RelOffsetForMany(DWARFReg(r0), 0, core_spill_mask, kFramePointerSize);
   if (fp_spill_mask != 0) {
     uint32_t first = CTZ(fp_spill_mask);
-    uint32_t last  = first + POPCOUNT(fp_spill_mask) - 1;
 
     // Check that list is contiguous.
     DCHECK_EQ(fp_spill_mask >> CTZ(fp_spill_mask), ~0u >> (32 - POPCOUNT(fp_spill_mask)));
 
-    ___ Vpush(SRegisterList(vixl32::SRegister(first), vixl32::SRegister(last)));
+    ___ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fp_spill_mask)));
     cfi().AdjustCFAOffset(POPCOUNT(fp_spill_mask) * kFramePointerSize);
     cfi().RelOffsetForMany(DWARFReg(s0), 0, fp_spill_mask, kFramePointerSize);
   }
@@ -136,11 +135,10 @@
 
   if (fp_spill_mask != 0) {
     uint32_t first = CTZ(fp_spill_mask);
-    uint32_t last  = first + POPCOUNT(fp_spill_mask) - 1;
     // Check that list is contiguous.
      DCHECK_EQ(fp_spill_mask >> CTZ(fp_spill_mask), ~0u >> (32 - POPCOUNT(fp_spill_mask)));
 
-    ___ Vpop(SRegisterList(vixl32::SRegister(first), vixl32::SRegister(last)));
+    ___ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fp_spill_mask)));
     cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(fp_spill_mask));
     cfi().RestoreMany(DWARFReg(s0), fp_spill_mask);
   }
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 367ed97..3b05173 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1661,13 +1661,19 @@
 TEST_F(ArmVIXLAssemblerTest, VixlJniHelpers) {
   const bool is_static = true;
   const bool is_synchronized = false;
+  const bool is_critical_native = false;
   const char* shorty = "IIFII";
 
   ArenaPool pool;
   ArenaAllocator arena(&pool);
 
   std::unique_ptr<JniCallingConvention> jni_conv(
-      JniCallingConvention::Create(&arena, is_static, is_synchronized, shorty, kThumb2));
+      JniCallingConvention::Create(&arena,
+                                   is_static,
+                                   is_synchronized,
+                                   is_critical_native,
+                                   shorty,
+                                   kThumb2));
   std::unique_ptr<ManagedRuntimeCallingConvention> mr_conv(
       ManagedRuntimeCallingConvention::Create(&arena, is_static, is_synchronized, shorty, kThumb2));
   const int frame_size(jni_conv->FrameSize());
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index f2ef41f..cd30872 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1708,6 +1708,13 @@
 }
 
 
+void X86Assembler::repne_scasb() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF2);
+  EmitUint8(0xAE);
+}
+
+
 void X86Assembler::repne_scasw() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
@@ -1716,6 +1723,13 @@
 }
 
 
+void X86Assembler::repe_cmpsb() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF2);
+  EmitUint8(0xA6);
+}
+
+
 void X86Assembler::repe_cmpsw() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
@@ -1731,6 +1745,13 @@
 }
 
 
+void X86Assembler::rep_movsb() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF3);
+  EmitUint8(0xA4);
+}
+
+
 void X86Assembler::rep_movsw() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 2ddcd76..9738784 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -592,9 +592,12 @@
   void jmp(Label* label);
   void jmp(NearLabel* label);
 
+  void repne_scasb();
   void repne_scasw();
+  void repe_cmpsb();
   void repe_cmpsw();
   void repe_cmpsl();
+  void rep_movsb();
   void rep_movsw();
 
   X86Assembler* lock();
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 61d70d7..9bae6c2 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -207,12 +207,24 @@
   DriverStr(expected, "FPUIntegerStore");
 }
 
+TEST_F(AssemblerX86Test, Repnescasb) {
+  GetAssembler()->repne_scasb();
+  const char* expected = "repne scasb\n";
+  DriverStr(expected, "Repnescasb");
+}
+
 TEST_F(AssemblerX86Test, Repnescasw) {
   GetAssembler()->repne_scasw();
   const char* expected = "repne scasw\n";
   DriverStr(expected, "Repnescasw");
 }
 
+TEST_F(AssemblerX86Test, Repecmpsb) {
+  GetAssembler()->repe_cmpsb();
+  const char* expected = "repe cmpsb\n";
+  DriverStr(expected, "Repecmpsb");
+}
+
 TEST_F(AssemblerX86Test, Repecmpsw) {
   GetAssembler()->repe_cmpsw();
   const char* expected = "repe cmpsw\n";
@@ -225,10 +237,10 @@
   DriverStr(expected, "Repecmpsl");
 }
 
-TEST_F(AssemblerX86Test, RepneScasw) {
-  GetAssembler()->repne_scasw();
-  const char* expected = "repne scasw\n";
-  DriverStr(expected, "repne_scasw");
+TEST_F(AssemblerX86Test, RepMovsb) {
+  GetAssembler()->rep_movsb();
+  const char* expected = "rep movsb\n";
+  DriverStr(expected, "rep_movsb");
 }
 
 TEST_F(AssemblerX86Test, RepMovsw) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 1f73aa7..e9a0607 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -2325,6 +2325,12 @@
   EmitOperand(dst.LowBits(), src);
 }
 
+void X86_64Assembler::repne_scasb() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF2);
+  EmitUint8(0xAE);
+}
+
 void X86_64Assembler::repne_scasw() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
@@ -2332,7 +2338,6 @@
   EmitUint8(0xAF);
 }
 
-
 void X86_64Assembler::repe_cmpsw() {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 3a4bfca..fdd3aa9 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -670,6 +670,7 @@
   void rolq(CpuRegister reg, const Immediate& imm);
   void rolq(CpuRegister operand, CpuRegister shifter);
 
+  void repne_scasb();
   void repne_scasw();
   void repe_cmpsw();
   void repe_cmpsl();
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 48a1876..ff01429 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -956,6 +956,12 @@
   DriverStr(expected, "xorq");
 }
 
+TEST_F(AssemblerX86_64Test, RepneScasb) {
+  GetAssembler()->repne_scasb();
+  const char* expected = "repne scasb\n";
+  DriverStr(expected, "repne_scasb");
+}
+
 TEST_F(AssemblerX86_64Test, RepneScasw) {
   GetAssembler()->repne_scasw();
   const char* expected = "repne scasw\n";
diff --git a/dalvikvm/Android.bp b/dalvikvm/Android.bp
new file mode 100644
index 0000000..ab645bb
--- /dev/null
+++ b/dalvikvm/Android.bp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2013 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.
+//
+
+art_cc_binary {
+    name: "dalvikvm",
+    host_supported: true,
+    compile_multilib: "both",
+
+    srcs: ["dalvikvm.cc"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    include_dirs: ["art/runtime"],
+    shared_libs: [
+        "libnativehelper",
+    ],
+    whole_static_libs: ["libsigchain"],
+    target: {
+        host: {
+            host_ldlibs: [
+                "-ldl",
+                "-lpthread",
+            ],
+        },
+        android: {
+            shared_libs: [
+                "libdl",
+                "liblog",
+            ],
+            ldflags: ["-Wl,--export-dynamic"],
+        },
+        linux: {
+            ldflags: ["-Wl,--export-dynamic"],
+        },
+    },
+
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    // Create symlink for the primary version target.
+    symlink_preferred_arch: true,
+}
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
deleted file mode 100644
index 6c0bcb1..0000000
--- a/dalvikvm/Android.mk
+++ /dev/null
@@ -1,87 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include art/build/Android.common.mk
-
-dalvikvm_cflags := -Wall -Werror -Wextra
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := dalvikvm
-LOCAL_MODULE_TAGS := optional
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc
-LOCAL_CFLAGS := $(dalvikvm_cflags)
-LOCAL_C_INCLUDES := art/runtime
-LOCAL_SHARED_LIBRARIES := libdl liblog libnativehelper
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
-LOCAL_LDFLAGS := -Wl,--export-dynamic
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := dalvikvm32
-LOCAL_MODULE_STEM_64 := dalvikvm64
-LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
-include $(BUILD_EXECUTABLE)
-
-# Create symlink for the primary version target.
-include  $(BUILD_SYSTEM)/executable_prefer_symlink.mk
-
-ART_TARGET_EXECUTABLES += $(TARGET_OUT_EXECUTABLES)/$(LOCAL_MODULE)
-ART_TARGET_EXECUTABLES += $(TARGET_OUT_EXECUTABLES)/$(LOCAL_MODULE)$(ART_PHONY_TEST_TARGET_SUFFIX)
-ifdef 2ND_ART_PHONY_TEST_TARGET_SUFFIX
-  ART_TARGET_EXECUTABLES += $(TARGET_OUT_EXECUTABLES)/$(LOCAL_MODULE)$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
-endif
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := dalvikvm
-LOCAL_MODULE_TAGS := optional
-LOCAL_CLANG := true
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := dalvikvm.cc
-LOCAL_CFLAGS := $(dalvikvm_cflags)
-LOCAL_C_INCLUDES := art/runtime
-LOCAL_SHARED_LIBRARIES := libnativehelper
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
-LOCAL_LDFLAGS := -ldl -lpthread
-# Mac OS linker doesn't understand --export-dynamic.
-ifneq ($(HOST_OS),darwin)
-  LOCAL_LDFLAGS += -Wl,--export-dynamic
-endif
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common.mk
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MULTILIB := both
-ifdef ART_MULTILIB_OVERRIDE_host
-  LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
-endif
-ifeq ($(LOCAL_MULTILIB),both)
-LOCAL_MODULE_STEM_32 := dalvikvm32
-LOCAL_MODULE_STEM_64 := dalvikvm64
-endif
-LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
-include $(BUILD_HOST_EXECUTABLE)
-# Create symlink for the primary version target.
-ifeq ($(LOCAL_MULTILIB),both)
-include  $(BUILD_SYSTEM)/executable_prefer_symlink.mk
-
-ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)$(ART_PHONY_TEST_HOST_SUFFIX)
-ifdef 2ND_ART_PHONY_TEST_HOST_SUFFIX
-  ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
-endif
-endif
-ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 8a179c1..32424ca 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -55,35 +55,19 @@
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain,art/compiler,target,debug,$(dex2oat_target_arch)))
 endif
 
-# Note: the order is important because of static linking resolution.
-DEX2OAT_STATIC_DEPENDENCIES := \
-  libziparchive \
-  libnativehelper \
-  libnativebridge \
-  libnativeloader \
-  libsigchain_dummy \
-  liblog \
-  libz \
-  libbacktrace \
-  libcutils \
-  libunwindbacktrace \
-  libutils \
-  libbase \
-  liblz4 \
-  liblzma
-
-# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+# 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 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 libvixl-arm libvixl-arm64 $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static))
+    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libvixl-arm libvixl-arm64 $(ART_STATIC_DEPENDENCIES),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 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 libvixld-arm libvixld-arm64 $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
+    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libvixld-arm libvixld-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
   endif
 endif
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index febfb63..1dd9132 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -507,6 +507,7 @@
       thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
       start_ns_(NanoTime()),
       oat_fd_(-1),
+      vdex_fd_(-1),
       zip_fd_(-1),
       image_base_(0U),
       image_classes_zip_filename_(nullptr),
@@ -557,6 +558,9 @@
       for (std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
         map.release();
       }
+      for (std::unique_ptr<File>& vdex_file : vdex_files_) {
+        vdex_file.release();
+      }
       for (std::unique_ptr<File>& oat_file : oat_files_) {
         oat_file.release();
       }
@@ -578,6 +582,10 @@
     ParseUintOption(option, "--zip-fd", &zip_fd_, Usage);
   }
 
+  void ParseVdexFd(const StringPiece& option) {
+    ParseUintOption(option, "--vdex-fd", &vdex_fd_, Usage);
+  }
+
   void ParseOatFd(const StringPiece& option) {
     ParseUintOption(option, "--oat-fd", &oat_fd_, Usage);
   }
@@ -693,6 +701,11 @@
       Usage("--oat-file should not be used with --oat-fd");
     }
 
+    if ((vdex_fd_ == -1) != (oat_fd_ == -1)) {
+      Usage("VDEX and OAT output must be specified either with one --oat-filename "
+            "or with --oat-fd and --vdex-fd file descriptors");
+    }
+
     if (!parser_options->oat_symbols.empty() && oat_fd_ != -1) {
       Usage("--oat-symbols should not be used with --oat-fd");
     }
@@ -701,6 +714,10 @@
       Usage("--oat-symbols should not be used with --host");
     }
 
+    if (vdex_fd_ != -1 && !image_filenames_.empty()) {
+      Usage("--vdex-fd should not be used with --image");
+    }
+
     if (oat_fd_ != -1 && !image_filenames_.empty()) {
       Usage("--oat-fd should not be used with --image");
     }
@@ -1074,20 +1091,22 @@
         ParseZipFd(option);
       } else if (option.starts_with("--zip-location=")) {
         zip_location_ = option.substr(strlen("--zip-location=")).data();
+      } else if (option.starts_with("--vdex-fd=")) {
+        ParseVdexFd(option);
       } else if (option.starts_with("--oat-file=")) {
         oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data());
       } else if (option.starts_with("--oat-symbols=")) {
         parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data());
       } else if (option.starts_with("--oat-fd=")) {
         ParseOatFd(option);
+      } else if (option.starts_with("--oat-location=")) {
+        oat_location_ = option.substr(strlen("--oat-location=")).data();
       } else if (option == "--watch-dog") {
         parser_options->watch_dog_enabled = true;
       } else if (option == "--no-watch-dog") {
         parser_options->watch_dog_enabled = false;
       } else if (option.starts_with("-j")) {
         ParseJ(option);
-      } else if (option.starts_with("--oat-location=")) {
-        oat_location_ = option.substr(strlen("--oat-location=")).data();
       } else if (option.starts_with("--image=")) {
         image_filenames_.push_back(option.substr(strlen("--image=")).data());
       } else if (option.starts_with("--image-classes=")) {
@@ -1199,41 +1218,66 @@
       ExpandOatAndImageFilenames();
     }
 
-    bool create_file = oat_fd_ == -1;  // as opposed to using open file descriptor
-    if (create_file) {
+    // OAT and VDEX file handling
+
+    if (oat_fd_ == -1) {
+      DCHECK(!oat_filenames_.empty());
       for (const char* oat_filename : oat_filenames_) {
         std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_filename));
         if (oat_file.get() == nullptr) {
           PLOG(ERROR) << "Failed to create oat file: " << oat_filename;
           return false;
         }
-        if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
+        if (fchmod(oat_file->Fd(), 0644) != 0) {
           PLOG(ERROR) << "Failed to make oat file world readable: " << oat_filename;
           oat_file->Erase();
           return false;
         }
         oat_files_.push_back(std::move(oat_file));
+
+        DCHECK_EQ(vdex_fd_, -1);
+        std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
+        std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
+        if (vdex_file.get() == nullptr) {
+          PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename;
+          return false;
+        }
+        if (fchmod(vdex_file->Fd(), 0644) != 0) {
+          PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename;
+          vdex_file->Erase();
+          return false;
+        }
+        vdex_files_.push_back(std::move(vdex_file));
       }
     } else {
-      std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, true));
-      oat_file->DisableAutoClose();
-      if (oat_file->SetLength(0) != 0) {
-        PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
-      }
+      std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true));
       if (oat_file.get() == nullptr) {
         PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
         return false;
       }
-      if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
-        PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
-        oat_file->Erase();
+      oat_file->DisableAutoClose();
+      if (oat_file->SetLength(0) != 0) {
+        PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
+      }
+      oat_files_.push_back(std::move(oat_file));
+
+      DCHECK_NE(vdex_fd_, -1);
+      std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
+      std::unique_ptr<File> vdex_file(new File(vdex_fd_, vdex_location, /* check_usage */ true));
+      if (vdex_file.get() == nullptr) {
+        PLOG(ERROR) << "Failed to create vdex file: " << vdex_location;
         return false;
       }
+      vdex_file->DisableAutoClose();
+      if (vdex_file->SetLength(0) != 0) {
+        PLOG(WARNING) << "Truncating vdex file " << vdex_location << " failed.";
+      }
+      vdex_files_.push_back(std::move(vdex_file));
+
       oat_filenames_.push_back(oat_location_.c_str());
-      oat_files_.push_back(std::move(oat_file));
     }
 
-    // Swap file handling.
+    // Swap file handling
     //
     // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file
     // that we can use for swap.
@@ -1256,11 +1300,14 @@
     return true;
   }
 
-  void EraseOatFiles() {
-    for (size_t i = 0; i < oat_files_.size(); ++i) {
-      DCHECK(oat_files_[i].get() != nullptr);
-      oat_files_[i]->Erase();
-      oat_files_[i].reset();
+  void EraseOutputFiles() {
+    for (auto& files : { &vdex_files_, &oat_files_ }) {
+      for (size_t i = 0; i < files->size(); ++i) {
+        if ((*files)[i].get() != nullptr) {
+          (*files)[i]->Erase();
+          (*files)[i].reset();
+        }
+      }
     }
   }
 
@@ -1399,14 +1446,15 @@
         // Unzip or copy dex files straight to the oat file.
         std::unique_ptr<MemMap> opened_dex_files_map;
         std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-        if (!oat_writers_[i]->WriteAndOpenDexFiles(rodata_.back(),
-                                                   oat_files_[i].get(),
-                                                   instruction_set_,
-                                                   instruction_set_features_.get(),
-                                                   key_value_store_.get(),
-                                                   /* verify */ true,
-                                                   &opened_dex_files_map,
-                                                   &opened_dex_files)) {
+        if (!oat_writers_[i]->WriteAndOpenDexFiles(
+            kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(),
+            rodata_.back(),
+            instruction_set_,
+            instruction_set_features_.get(),
+            key_value_store_.get(),
+            /* verify */ true,
+            &opened_dex_files_map,
+            &opened_dex_files)) {
           return false;
         }
         dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files));
@@ -1652,7 +1700,7 @@
   // ImageWriter, if necessary.
   // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure
   //       case (when the file will be explicitly erased).
-  bool WriteOatFiles() {
+  bool WriteOutputFiles() {
     TimingLogger::ScopedTiming t("dex2oat Oat", timings_);
 
     // Sync the data to the file, in case we did dex2dex transformations.
@@ -1709,7 +1757,7 @@
         oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files, &patcher);
 
         size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
-        size_t text_size = oat_writer->GetSize() - rodata_size;
+        size_t text_size = oat_writer->GetOatSize() - rodata_size;
         elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize());
 
         if (IsImage()) {
@@ -1719,7 +1767,7 @@
           image_writer_->UpdateOatFileLayout(i,
                                              elf_writer->GetLoadedSize(),
                                              oat_writer->GetOatDataOffset(),
-                                             oat_writer->GetSize());
+                                             oat_writer->GetOatSize());
         }
       }
 
@@ -1774,12 +1822,8 @@
           return false;
         }
 
-        // Flush the oat file.
-        if (oat_files_[i] != nullptr) {
-          if (oat_files_[i]->Flush() != 0) {
-            PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i];
-            return false;
-          }
+        if (!FlushOutputFile(&vdex_files_[i]) || !FlushOutputFile(&oat_files_[i])) {
+          return false;
         }
 
         VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i];
@@ -1812,7 +1856,7 @@
       if (strcmp(oat_unstripped_[i], oat_filenames_[i]) != 0) {
         // If the oat file is still open, flush it.
         if (oat_files_[i].get() != nullptr && oat_files_[i]->IsOpened()) {
-          if (!FlushCloseOatFile(i)) {
+          if (!FlushCloseOutputFile(&oat_files_[i])) {
             return false;
           }
         }
@@ -1840,13 +1884,32 @@
     return true;
   }
 
-  bool FlushOatFiles() {
-    TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
-    for (size_t i = 0; i < oat_files_.size(); ++i) {
-      if (oat_files_[i].get() != nullptr) {
-        if (oat_files_[i]->Flush() != 0) {
-          PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i];
-          oat_files_[i]->Erase();
+  bool FlushOutputFile(std::unique_ptr<File>* file) {
+    if (file->get() != nullptr) {
+      if (file->get()->Flush() != 0) {
+        PLOG(ERROR) << "Failed to flush output file: " << file->get()->GetPath();
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool FlushCloseOutputFile(std::unique_ptr<File>* file) {
+    if (file->get() != nullptr) {
+      std::unique_ptr<File> tmp(file->release());
+      if (tmp->FlushCloseOrErase() != 0) {
+        PLOG(ERROR) << "Failed to flush and close output file: " << tmp->GetPath();
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool FlushOutputFiles() {
+    TimingLogger::ScopedTiming t2("dex2oat Flush Output Files", timings_);
+    for (auto& files : { &vdex_files_, &oat_files_ }) {
+      for (size_t i = 0; i < files->size(); ++i) {
+        if (!FlushOutputFile(&(*files)[i])) {
           return false;
         }
       }
@@ -1854,21 +1917,12 @@
     return true;
   }
 
-  bool FlushCloseOatFile(size_t i) {
-    if (oat_files_[i].get() != nullptr) {
-      std::unique_ptr<File> tmp(oat_files_[i].release());
-      if (tmp->FlushCloseOrErase() != 0) {
-        PLOG(ERROR) << "Failed to flush and close oat file: " << oat_filenames_[i];
-        return false;
-      }
-    }
-    return true;
-  }
-
-  bool FlushCloseOatFiles() {
+  bool FlushCloseOutputFiles() {
     bool result = true;
-    for (size_t i = 0; i < oat_files_.size(); ++i) {
-      result &= FlushCloseOatFile(i);
+    for (auto& files : { &vdex_files_, &oat_files_ }) {
+      for (size_t i = 0; i < files->size(); ++i) {
+        result &= FlushCloseOutputFile(&(*files)[i]);
+      }
     }
     return result;
   }
@@ -2503,10 +2557,12 @@
   uint64_t start_ns_;
   std::unique_ptr<WatchDog> watchdog_;
   std::vector<std::unique_ptr<File>> oat_files_;
+  std::vector<std::unique_ptr<File>> vdex_files_;
   std::string oat_location_;
   std::vector<const char*> oat_filenames_;
   std::vector<const char*> oat_unstripped_;
   int oat_fd_;
+  int vdex_fd_;
   std::vector<const char*> dex_filenames_;
   std::vector<const char*> dex_locations_;
   int zip_fd_;
@@ -2603,8 +2659,8 @@
   dex2oat.LoadClassProfileDescriptors();
   dex2oat.Compile();
 
-  if (!dex2oat.WriteOatFiles()) {
-    dex2oat.EraseOatFiles();
+  if (!dex2oat.WriteOutputFiles()) {
+    dex2oat.EraseOutputFiles();
     return EXIT_FAILURE;
   }
 
@@ -2612,10 +2668,11 @@
   // unstripped name. Do not close the file if we are compiling the image with an oat fd since the
   // image writer will require this fd to generate the image.
   if (dex2oat.ShouldKeepOatFileOpen()) {
-    if (!dex2oat.FlushOatFiles()) {
+    if (!dex2oat.FlushOutputFiles()) {
+      dex2oat.EraseOutputFiles();
       return EXIT_FAILURE;
     }
-  } else if (!dex2oat.FlushCloseOatFiles()) {
+  } else if (!dex2oat.FlushCloseOutputFiles()) {
     return EXIT_FAILURE;
   }
 
@@ -2636,7 +2693,7 @@
   }
 
   // FlushClose again, as stripping might have re-opened the oat files.
-  if (!dex2oat.FlushCloseOatFiles()) {
+  if (!dex2oat.FlushCloseOutputFiles()) {
     return EXIT_FAILURE;
   }
 
@@ -2647,8 +2704,8 @@
 static int CompileApp(Dex2Oat& dex2oat) {
   dex2oat.Compile();
 
-  if (!dex2oat.WriteOatFiles()) {
-    dex2oat.EraseOatFiles();
+  if (!dex2oat.WriteOutputFiles()) {
+    dex2oat.EraseOutputFiles();
     return EXIT_FAILURE;
   }
 
@@ -2657,7 +2714,7 @@
 
   // When given --host, finish early without stripping.
   if (dex2oat.IsHost()) {
-    if (!dex2oat.FlushCloseOatFiles()) {
+    if (!dex2oat.FlushCloseOutputFiles()) {
       return EXIT_FAILURE;
     }
 
@@ -2672,7 +2729,7 @@
   }
 
   // Flush and close the files.
-  if (!dex2oat.FlushCloseOatFiles()) {
+  if (!dex2oat.FlushCloseOutputFiles()) {
     return EXIT_FAILURE;
   }
 
@@ -2721,7 +2778,7 @@
   }
 
   if (!dex2oat->Setup()) {
-    dex2oat->EraseOatFiles();
+    dex2oat->EraseOutputFiles();
     return EXIT_FAILURE;
   }
 
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
new file mode 100644
index 0000000..e77f809
--- /dev/null
+++ b/dexdump/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+// TODO(ajcbik): rename dexdump2 into dexdump when Dalvik version is removed
+
+art_cc_binary {
+    name: "dexdump2",
+    host_supported: true,
+    srcs: [
+        "dexdump_main.cc",
+        "dexdump.cc",
+    ],
+    cflags: ["-Wall"],
+    shared_libs: ["libart"],
+}
diff --git a/dexdump/Android.mk b/dexdump/Android.mk
deleted file mode 100755
index ec2529e..0000000
--- a/dexdump/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-# 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.
-
-# TODO(ajcbik): Art-i-fy this makefile
-
-# TODO(ajcbik): rename dexdump2 into dexdump when Dalvik version is removed
-
-LOCAL_PATH:= $(call my-dir)
-
-dexdump_src_files := dexdump_main.cc dexdump.cc
-dexdump_c_includes := art/runtime
-dexdump_libraries := libart
-
-##
-## Build the device command line tool dexdump.
-##
-
-ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := $(dexdump_src_files)
-LOCAL_C_INCLUDES := $(dexdump_c_includes)
-LOCAL_CFLAGS += -Wall
-LOCAL_SHARED_LIBRARIES += $(dexdump_libraries)
-LOCAL_MODULE := dexdump2
-include $(BUILD_EXECUTABLE)
-endif # !SDK_ONLY
-
-##
-## Build the host command line tool dexdump.
-##
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := $(dexdump_src_files)
-LOCAL_C_INCLUDES := $(dexdump_c_includes)
-LOCAL_CFLAGS += -Wall
-LOCAL_SHARED_LIBRARIES += $(dexdump_libraries)
-LOCAL_MODULE := dexdump2
-LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
new file mode 100644
index 0000000..852f6c2
--- /dev/null
+++ b/dexlayout/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+art_cc_binary {
+    name: "dexlayout",
+    host_supported: true,
+    srcs: [
+        "dexlayout_main.cc",
+        "dexlayout.cc",
+        "dex_ir_builder.cc",
+    ],
+    cflags: ["-Wall"],
+    shared_libs: ["libart"],
+}
diff --git a/dexlayout/Android.mk b/dexlayout/Android.mk
deleted file mode 100755
index 3095866..0000000
--- a/dexlayout/Android.mk
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# TODO(sehr): Art-i-fy this makefile
-
-LOCAL_PATH:= $(call my-dir)
-
-dexlayout_src_files := dexlayout_main.cc dexlayout.cc dex_ir.cc
-dexlayout_c_includes := art/runtime
-dexlayout_libraries := libart
-
-##
-## Build the device command line tool dexlayout.
-##
-
-ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := $(dexlayout_src_files)
-LOCAL_C_INCLUDES := $(dexlayout_c_includes)
-LOCAL_CFLAGS += -Wall
-LOCAL_SHARED_LIBRARIES += $(dexlayout_libraries)
-LOCAL_MODULE := dexlayout
-include $(BUILD_EXECUTABLE)
-endif # !SDK_ONLY
-
-##
-## Build the host command line tool dexlayout.
-##
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := $(dexlayout_src_files)
-LOCAL_C_INCLUDES := $(dexlayout_c_includes)
-LOCAL_CFLAGS += -Wall
-LOCAL_SHARED_LIBRARIES += $(dexlayout_libraries)
-LOCAL_MODULE := dexlayout
-LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
deleted file mode 100644
index 0ed040e..0000000
--- a/dexlayout/dex_ir.cc
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Implementation file of the dex file intermediate representation.
- *
- * Utilities for reading dex files into an internal representation,
- * manipulating them, and writing them out.
- */
-
-#include "dex_ir.h"
-
-#include <map>
-#include <vector>
-
-#include "dex_file.h"
-#include "dex_file-inl.h"
-#include "utils.h"
-
-namespace art {
-namespace dex_ir {
-
-namespace {
-static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) {
-  uint64_t value = 0;
-  for (uint32_t i = 0; i <= length; i++) {
-    value |= static_cast<uint64_t>(*(*data)++) << (i * 8);
-  }
-  if (sign_extend) {
-    int shift = (7 - length) * 8;
-    return (static_cast<int64_t>(value) << shift) >> shift;
-  }
-  return value;
-}
-
-static bool GetPositionsCb(void* context, const DexFile::PositionInfo& entry) {
-  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
-  std::vector<std::unique_ptr<PositionInfo>>& positions = debug_info->GetPositionInfo();
-  positions.push_back(std::unique_ptr<PositionInfo>(new PositionInfo(entry.address_, entry.line_)));
-  return false;
-}
-
-static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
-  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
-  std::vector<std::unique_ptr<LocalInfo>>& locals = debug_info->GetLocalInfo();
-  const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
-  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
-  locals.push_back(std::unique_ptr<LocalInfo>(
-      new LocalInfo(name, entry.descriptor_, signature, entry.start_address_,
-                    entry.end_address_, entry.reg_)));
-}
-}  // namespace
-
-Header::Header(const DexFile& dex_file) : dex_file_(dex_file) {
-  const DexFile::Header& disk_header = dex_file.GetHeader();
-  memcpy(magic_, disk_header.magic_, sizeof(magic_));
-  checksum_ = disk_header.checksum_;
-  // TODO(sehr): clearly the signature will need to be recomputed before dumping.
-  memcpy(signature_, disk_header.signature_, sizeof(signature_));
-  endian_tag_ = disk_header.endian_tag_;
-  file_size_ = disk_header.file_size_;
-  header_size_ = disk_header.header_size_;
-  link_size_ = disk_header.link_size_;
-  link_offset_ = disk_header.link_off_;
-  data_size_ = disk_header.data_size_;
-  data_offset_ = disk_header.data_off_;
-  // Walk the rest of the header fields.
-  string_ids_.SetOffset(disk_header.string_ids_off_);
-  for (uint32_t i = 0; i < dex_file_.NumStringIds(); ++i) {
-    string_ids_.AddWithPosition(i, new StringId(dex_file_.GetStringId(i), *this));
-  }
-  type_ids_.SetOffset(disk_header.type_ids_off_);
-  for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) {
-    type_ids_.AddWithPosition(i, new TypeId(dex_file_.GetTypeId(i), *this));
-  }
-  proto_ids_.SetOffset(disk_header.proto_ids_off_);
-  for (uint32_t i = 0; i < dex_file_.NumProtoIds(); ++i) {
-    proto_ids_.AddWithPosition(i, new ProtoId(dex_file_.GetProtoId(i), *this));
-  }
-  field_ids_.SetOffset(disk_header.field_ids_off_);
-  for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) {
-    field_ids_.AddWithPosition(i, new FieldId(dex_file_.GetFieldId(i), *this));
-  }
-  method_ids_.SetOffset(disk_header.method_ids_off_);
-  for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) {
-    method_ids_.AddWithPosition(i, new MethodId(dex_file_.GetMethodId(i), *this));
-  }
-  class_defs_.SetOffset(disk_header.class_defs_off_);
-  for (uint32_t i = 0; i < dex_file_.NumClassDefs(); ++i) {
-    class_defs_.AddWithPosition(i, new ClassDef(dex_file_.GetClassDef(i), *this));
-  }
-}
-
-ArrayItem::ArrayItem(Header& header, const uint8_t** data, uint8_t type, uint8_t length) {
-  Read(header, data, type, length);
-}
-
-ArrayItem::ArrayItem(Header& header, const uint8_t** data) {
-  const uint8_t encoded_value = *(*data)++;
-  Read(header, data, encoded_value & 0x1f, encoded_value >> 5);
-}
-
-void ArrayItem::Read(Header& header, const uint8_t** data, uint8_t type, uint8_t length) {
-  type_ = type;
-  switch (type_) {
-    case DexFile::kDexAnnotationByte:
-      item_.byte_val_ = static_cast<int8_t>(ReadVarWidth(data, length, false));
-      break;
-    case DexFile::kDexAnnotationShort:
-      item_.short_val_ = static_cast<int16_t>(ReadVarWidth(data, length, true));
-      break;
-    case DexFile::kDexAnnotationChar:
-      item_.char_val_ = static_cast<uint16_t>(ReadVarWidth(data, length, false));
-      break;
-    case DexFile::kDexAnnotationInt:
-      item_.int_val_ = static_cast<int32_t>(ReadVarWidth(data, length, true));
-      break;
-    case DexFile::kDexAnnotationLong:
-      item_.long_val_ = static_cast<int64_t>(ReadVarWidth(data, length, true));
-      break;
-    case DexFile::kDexAnnotationFloat: {
-      // Fill on right.
-      union {
-        float f;
-        uint32_t data;
-      } conv;
-      conv.data = static_cast<uint32_t>(ReadVarWidth(data, length, false)) << (3 - length) * 8;
-      item_.float_val_ = conv.f;
-      break;
-    }
-    case DexFile::kDexAnnotationDouble: {
-      // Fill on right.
-      union {
-        double d;
-        uint64_t data;
-      } conv;
-      conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8;
-      item_.double_val_ = conv.d;
-      break;
-    }
-    case DexFile::kDexAnnotationString: {
-      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item_.string_val_ = header.StringIds()[string_index].get();
-      break;
-    }
-    case DexFile::kDexAnnotationType: {
-      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item_.string_val_ = header.TypeIds()[string_index]->GetStringId();
-      break;
-    }
-    case DexFile::kDexAnnotationField:
-    case DexFile::kDexAnnotationEnum: {
-      const uint32_t field_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item_.field_val_ = header.FieldIds()[field_index].get();
-      break;
-    }
-    case DexFile::kDexAnnotationMethod: {
-      const uint32_t method_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
-      item_.method_val_ = header.MethodIds()[method_index].get();
-      break;
-    }
-    case DexFile::kDexAnnotationArray: {
-      item_.annotation_array_val_ = new std::vector<std::unique_ptr<ArrayItem>>();
-      // Decode all elements.
-      const uint32_t size = DecodeUnsignedLeb128(data);
-      for (uint32_t i = 0; i < size; i++) {
-        item_.annotation_array_val_->push_back(
-            std::unique_ptr<ArrayItem>(new ArrayItem(header, data)));
-      }
-      break;
-    }
-    case DexFile::kDexAnnotationAnnotation: {
-      const uint32_t type_idx = DecodeUnsignedLeb128(data);
-      item_.annotation_annotation_val_.string_ = header.TypeIds()[type_idx]->GetStringId();
-      item_.annotation_annotation_val_.array_ = new std::vector<std::unique_ptr<NameValuePair>>();
-      // Decode all name=value pairs.
-      const uint32_t size = DecodeUnsignedLeb128(data);
-      for (uint32_t i = 0; i < size; i++) {
-        const uint32_t name_index = DecodeUnsignedLeb128(data);
-        item_.annotation_annotation_val_.array_->push_back(std::unique_ptr<NameValuePair>(
-            new NameValuePair(header.StringIds()[name_index].get(), new ArrayItem(header, data))));
-      }
-      break;
-    }
-    case DexFile::kDexAnnotationNull:
-      break;
-    case DexFile::kDexAnnotationBoolean:
-      item_.bool_val_ = (length != 0);
-      break;
-    default:
-      break;
-  }
-}
-
-ClassDef::ClassDef(const DexFile::ClassDef& disk_class_def, Header& header) {
-  class_type_ = header.TypeIds()[disk_class_def.class_idx_].get();
-  access_flags_ = disk_class_def.access_flags_;
-  superclass_ = header.GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
-
-  const DexFile::TypeList* type_list = header.GetDexFile().GetInterfacesList(disk_class_def);
-  interfaces_offset_ = disk_class_def.interfaces_off_;
-  if (type_list != nullptr) {
-    for (uint32_t index = 0; index < type_list->Size(); ++index) {
-      interfaces_.push_back(header.TypeIds()[type_list->GetTypeItem(index).type_idx_].get());
-    }
-  }
-  source_file_ = header.GetStringIdOrNullPtr(disk_class_def.source_file_idx_);
-  // Annotations.
-  const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item =
-      header.GetDexFile().GetAnnotationsDirectory(disk_class_def);
-  if (disk_annotations_directory_item == nullptr) {
-    annotations_.reset(nullptr);
-  } else {
-    annotations_.reset(new AnnotationsDirectoryItem(disk_annotations_directory_item, header));
-    annotations_->SetOffset(disk_class_def.annotations_off_);
-  }
-  // Static field initializers.
-  static_values_ = nullptr;
-  const uint8_t* static_data = header.GetDexFile().GetEncodedStaticFieldValuesArray(disk_class_def);
-  if (static_data != nullptr) {
-    uint32_t static_value_count = static_data == nullptr ? 0 : DecodeUnsignedLeb128(&static_data);
-    if (static_value_count > 0) {
-      static_values_ = new std::vector<std::unique_ptr<ArrayItem>>();
-      for (uint32_t i = 0; i < static_value_count; ++i) {
-        static_values_->push_back(std::unique_ptr<ArrayItem>(new ArrayItem(header, &static_data)));
-      }
-    }
-  }
-  // Read the fields and methods defined by the class, resolving the circular reference from those
-  // to classes by setting class at the same time.
-  const uint8_t* encoded_data = header.GetDexFile().GetClassData(disk_class_def);
-  class_data_.SetOffset(disk_class_def.class_data_off_);
-  if (encoded_data != nullptr) {
-    ClassDataItemIterator cdii(header.GetDexFile(), encoded_data);
-    // Static fields.
-    for (uint32_t i = 0; cdii.HasNextStaticField(); i++, cdii.Next()) {
-      FieldId* field_item = header.FieldIds()[cdii.GetMemberIndex()].get();
-      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      class_data_.StaticFields().push_back(
-          std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
-    }
-    // Instance fields.
-    for (uint32_t i = 0; cdii.HasNextInstanceField(); i++, cdii.Next()) {
-      FieldId* field_item = header.FieldIds()[cdii.GetMemberIndex()].get();
-      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      class_data_.InstanceFields().push_back(
-          std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
-    }
-    // Direct methods.
-    for (uint32_t i = 0; cdii.HasNextDirectMethod(); i++, cdii.Next()) {
-      class_data_.DirectMethods().push_back(
-          std::unique_ptr<MethodItem>(GenerateMethodItem(header, cdii)));
-    }
-    // Virtual methods.
-    for (uint32_t i = 0; cdii.HasNextVirtualMethod(); i++, cdii.Next()) {
-      class_data_.VirtualMethods().push_back(
-          std::unique_ptr<MethodItem>(GenerateMethodItem(header, cdii)));
-    }
-  }
-}
-
-MethodItem* ClassDef::GenerateMethodItem(Header& header, ClassDataItemIterator& cdii) {
-  MethodId* method_item = header.MethodIds()[cdii.GetMemberIndex()].get();
-  uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-  const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
-  CodeItem* code_item = nullptr;
-  DebugInfoItem* debug_info = nullptr;
-  if (disk_code_item != nullptr) {
-    code_item = new CodeItem(*disk_code_item, header);
-    code_item->SetOffset(cdii.GetMethodCodeItemOffset());
-    debug_info = code_item->DebugInfo();
-  }
-  if (debug_info != nullptr) {
-    bool is_static = (access_flags & kAccStatic) != 0;
-    header.GetDexFile().DecodeDebugLocalInfo(
-        disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
-    header.GetDexFile().DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
-  }
-  return new MethodItem(access_flags, method_item, code_item);
-}
-
-CodeItem::CodeItem(const DexFile::CodeItem& disk_code_item, Header& header) {
-  registers_size_ = disk_code_item.registers_size_;
-  ins_size_ = disk_code_item.ins_size_;
-  outs_size_ = disk_code_item.outs_size_;
-  tries_size_ = disk_code_item.tries_size_;
-
-  const uint8_t* debug_info_stream = header.GetDexFile().GetDebugInfoStream(&disk_code_item);
-  if (debug_info_stream != nullptr) {
-    debug_info_.reset(new DebugInfoItem());
-  } else {
-    debug_info_.reset(nullptr);
-  }
-
-  insns_size_ = disk_code_item.insns_size_in_code_units_;
-  insns_.reset(new uint16_t[insns_size_]);
-  memcpy(insns_.get(), disk_code_item.insns_, insns_size_ * sizeof(uint16_t));
-
-  if (tries_size_ > 0) {
-    tries_ = new std::vector<std::unique_ptr<const TryItem>>();
-    for (uint32_t i = 0; i < tries_size_; ++i) {
-      const DexFile::TryItem* disk_try_item = header.GetDexFile().GetTryItems(disk_code_item, i);
-      tries_->push_back(std::unique_ptr<const TryItem>(
-          new TryItem(*disk_try_item, disk_code_item, header)));
-    }
-  } else {
-    tries_ = nullptr;
-  }
-}
-
-AnnotationSetItem::AnnotationSetItem(const DexFile::AnnotationSetItem& disk_annotations_item,
-                                     Header& header) {
-  if (disk_annotations_item.size_ == 0) {
-    return;
-  }
-  for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
-    const DexFile::AnnotationItem* annotation =
-        header.GetDexFile().GetAnnotationItem(&disk_annotations_item, i);
-    if (annotation == nullptr) {
-      continue;
-    }
-    uint8_t visibility = annotation->visibility_;
-    const uint8_t* annotation_data = annotation->annotation_;
-    ArrayItem* array_item =
-        new ArrayItem(header, &annotation_data, DexFile::kDexAnnotationAnnotation, 0);
-    items_.push_back(std::unique_ptr<AnnotationItem>(new AnnotationItem(visibility, array_item)));
-  }
-}
-
-AnnotationsDirectoryItem::AnnotationsDirectoryItem(
-    const DexFile::AnnotationsDirectoryItem* disk_annotations_item, Header& header) {
-  const DexFile::AnnotationSetItem* class_set_item =
-      header.GetDexFile().GetClassAnnotationSet(disk_annotations_item);
-  if (class_set_item == nullptr) {
-    class_annotation_.reset(nullptr);
-  } else {
-    class_annotation_.reset(new AnnotationSetItem(*class_set_item, header));
-  }
-  const DexFile::FieldAnnotationsItem* fields =
-      header.GetDexFile().GetFieldAnnotations(disk_annotations_item);
-  if (fields != nullptr) {
-    for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) {
-      FieldId* field_id = header.FieldIds()[fields[i].field_idx_].get();
-      const DexFile::AnnotationSetItem* field_set_item =
-          header.GetDexFile().GetFieldAnnotationSetItem(fields[i]);
-      dex_ir::AnnotationSetItem* annotation_set_item =
-          new AnnotationSetItem(*field_set_item, header);
-      field_annotations_.push_back(std::unique_ptr<FieldAnnotation>(
-          new FieldAnnotation(field_id, annotation_set_item)));
-    }
-  }
-  const DexFile::MethodAnnotationsItem* methods =
-      header.GetDexFile().GetMethodAnnotations(disk_annotations_item);
-  if (methods != nullptr) {
-    for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) {
-      MethodId* method_id = header.MethodIds()[methods[i].method_idx_].get();
-      const DexFile::AnnotationSetItem* method_set_item =
-          header.GetDexFile().GetMethodAnnotationSetItem(methods[i]);
-      dex_ir::AnnotationSetItem* annotation_set_item =
-          new AnnotationSetItem(*method_set_item, header);
-      method_annotations_.push_back(std::unique_ptr<MethodAnnotation>(
-          new MethodAnnotation(method_id, annotation_set_item)));
-    }
-  }
-  const DexFile::ParameterAnnotationsItem* parameters =
-      header.GetDexFile().GetParameterAnnotations(disk_annotations_item);
-  if (parameters != nullptr) {
-    for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) {
-      MethodId* method_id = header.MethodIds()[parameters[i].method_idx_].get();
-      const DexFile::AnnotationSetRefList* list =
-          header.GetDexFile().GetParameterAnnotationSetRefList(&parameters[i]);
-      parameter_annotations_.push_back(std::unique_ptr<ParameterAnnotation>(
-          new ParameterAnnotation(method_id, list, header)));
-    }
-  }
-}
-
-}  // namespace dex_ir
-}  // namespace art
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index fcd3ab0..cbb4404 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -19,12 +19,10 @@
 #ifndef ART_DEXLAYOUT_DEX_IR_H_
 #define ART_DEXLAYOUT_DEX_IR_H_
 
-#include <iostream>
-#include <map>
 #include <vector>
 #include <stdint.h>
 
-#include "dex_file.h"
+#include "dex_file-inl.h"
 
 namespace art {
 namespace dex_ir {
@@ -106,19 +104,39 @@
 class Item {
  public:
   virtual ~Item() { }
+
   uint32_t GetOffset() const { return offset_; }
   void SetOffset(uint32_t offset) { offset_ = offset; }
+
  protected:
   uint32_t offset_ = 0;
 };
 
 class Header : public Item {
  public:
-  explicit Header(const DexFile& dex_file);
+  Header(const uint8_t* magic,
+         uint32_t checksum,
+         const uint8_t* signature,
+         uint32_t endian_tag,
+         uint32_t file_size,
+         uint32_t header_size,
+         uint32_t link_size,
+         uint32_t link_offset,
+         uint32_t data_size,
+         uint32_t data_offset)
+      : checksum_(checksum),
+        endian_tag_(endian_tag),
+        file_size_(file_size),
+        header_size_(header_size),
+        link_size_(link_size),
+        link_offset_(link_offset),
+        data_size_(data_size),
+        data_offset_(data_offset) {
+    memcpy(magic_, magic, sizeof(magic_));
+    memcpy(signature_, signature, sizeof(signature_));
+  }
   ~Header() OVERRIDE { }
 
-  const DexFile& GetDexFile() const { return dex_file_; }
-
   const uint8_t* Magic() const { return magic_; }
   uint32_t Checksum() const { return checksum_; }
   const uint8_t* Signature() const { return signature_; }
@@ -178,7 +196,6 @@
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  const DexFile& dex_file_;
   uint8_t magic_[8];
   uint32_t checksum_;
   uint8_t signature_[DexFile::kSha1DigestSize];
@@ -201,9 +218,7 @@
 
 class StringId : public Item {
  public:
-  StringId(const DexFile::StringId& disk_string_id, Header& header) :
-    data_(strdup(header.GetDexFile().GetStringData(disk_string_id))) {
-  }
+  explicit StringId(const char* data) : data_(strdup(data)) { }
   ~StringId() OVERRIDE { }
 
   const char* Data() const { return data_.get(); }
@@ -217,9 +232,7 @@
 
 class TypeId : public Item {
  public:
-  TypeId(const DexFile::TypeId& disk_type_id, Header& header) :
-    string_id_(header.StringIds()[disk_type_id.descriptor_idx_].get()) {
-  }
+  explicit TypeId(StringId* string_id) : string_id_(string_id) { }
   ~TypeId() OVERRIDE { }
 
   StringId* GetStringId() const { return string_id_; }
@@ -231,39 +244,31 @@
   DISALLOW_COPY_AND_ASSIGN(TypeId);
 };
 
+using TypeIdVector = std::vector<const TypeId*>;
+
 class ProtoId : public Item {
  public:
-  ProtoId(const DexFile::ProtoId& disk_proto_id, Header& header) {
-    shorty_ = header.StringIds()[disk_proto_id.shorty_idx_].get();
-    return_type_ = header.TypeIds()[disk_proto_id.return_type_idx_].get();
-    DexFileParameterIterator dfpi(header.GetDexFile(), disk_proto_id);
-    while (dfpi.HasNext()) {
-      parameters_.push_back(header.TypeIds()[dfpi.GetTypeIdx()].get());
-      dfpi.Next();
-    }
-  }
+  ProtoId(const StringId* shorty, const TypeId* return_type, TypeIdVector* parameters)
+      : shorty_(shorty), return_type_(return_type), parameters_(parameters) { }
   ~ProtoId() OVERRIDE { }
 
   const StringId* Shorty() const { return shorty_; }
   const TypeId* ReturnType() const { return return_type_; }
-  const std::vector<const TypeId*>& Parameters() const { return parameters_; }
+  const std::vector<const TypeId*>& Parameters() const { return *parameters_; }
 
   void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); }
 
  private:
   const StringId* shorty_;
   const TypeId* return_type_;
-  std::vector<const TypeId*> parameters_;
+  std::unique_ptr<TypeIdVector> parameters_;
   DISALLOW_COPY_AND_ASSIGN(ProtoId);
 };
 
 class FieldId : public Item {
  public:
-  FieldId(const DexFile::FieldId& disk_field_id, Header& header) {
-    class_ = header.TypeIds()[disk_field_id.class_idx_].get();
-    type_ = header.TypeIds()[disk_field_id.type_idx_].get();
-    name_ = header.StringIds()[disk_field_id.name_idx_].get();
-  }
+  FieldId(const TypeId* klass, const TypeId* type, const StringId* name)
+      : class_(klass), type_(type), name_(name) { }
   ~FieldId() OVERRIDE { }
 
   const TypeId* Class() const { return class_; }
@@ -281,11 +286,8 @@
 
 class MethodId : public Item {
  public:
-  MethodId(const DexFile::MethodId& disk_method_id, Header& header) {
-    class_ = header.TypeIds()[disk_method_id.class_idx_].get();
-    proto_ = header.ProtoIds()[disk_method_id.proto_idx_].get();
-    name_ = header.StringIds()[disk_method_id.name_idx_].get();
-  }
+  MethodId(const TypeId* klass, const ProtoId* proto, const StringId* name)
+      : class_(klass), proto_(proto), name_(name) { }
   ~MethodId() OVERRIDE { }
 
   const TypeId* Class() const { return class_; }
@@ -303,8 +305,8 @@
 
 class FieldItem : public Item {
  public:
-  FieldItem(uint32_t access_flags, const FieldId* field_id) :
-    access_flags_(access_flags), field_id_(field_id) { }
+  FieldItem(uint32_t access_flags, const FieldId* field_id)
+      : access_flags_(access_flags), field_id_(field_id) { }
   ~FieldItem() OVERRIDE { }
 
   uint32_t GetAccessFlags() const { return access_flags_; }
@@ -318,10 +320,12 @@
   DISALLOW_COPY_AND_ASSIGN(FieldItem);
 };
 
+using FieldItemVector = std::vector<std::unique_ptr<FieldItem>>;
+
 class MethodItem : public Item {
  public:
-  MethodItem(uint32_t access_flags, const MethodId* method_id, const CodeItem* code) :
-    access_flags_(access_flags), method_id_(method_id), code_(code) { }
+  MethodItem(uint32_t access_flags, const MethodId* method_id, const CodeItem* code)
+      : access_flags_(access_flags), method_id_(method_id), code_(code) { }
   ~MethodItem() OVERRIDE { }
 
   uint32_t GetAccessFlags() const { return access_flags_; }
@@ -337,12 +341,14 @@
   DISALLOW_COPY_AND_ASSIGN(MethodItem);
 };
 
+using MethodItemVector = std::vector<std::unique_ptr<MethodItem>>;
+
 class ArrayItem : public Item {
  public:
   class NameValuePair {
    public:
-    NameValuePair(StringId* name, ArrayItem* value) :
-      name_(name), value_(value) { }
+    NameValuePair(StringId* name, ArrayItem* value)
+        : name_(name), value_(value) { }
 
     StringId* Name() const { return name_; }
     ArrayItem* Value() const { return value_.get(); }
@@ -353,92 +359,125 @@
     DISALLOW_COPY_AND_ASSIGN(NameValuePair);
   };
 
-  ArrayItem(Header& header, const uint8_t** data, uint8_t type, uint8_t length);
-  ArrayItem(Header& header, const uint8_t** data);
+  struct ArrayItemVariant {
+   public:
+    union {
+      bool bool_val_;
+      int8_t byte_val_;
+      int16_t short_val_;
+      uint16_t char_val_;
+      int32_t int_val_;
+      int64_t long_val_;
+      float float_val_;
+      double double_val_;
+      StringId* string_val_;
+      FieldId* field_val_;
+      MethodId* method_val_;
+    } u_;
+    std::unique_ptr<std::vector<std::unique_ptr<ArrayItem>>> annotation_array_val_;
+    struct {
+      StringId* string_;
+      std::unique_ptr<std::vector<std::unique_ptr<NameValuePair>>> array_;
+    } annotation_annotation_val_;
+  };
+
+  explicit ArrayItem(uint8_t type) : type_(type) { }
   ~ArrayItem() OVERRIDE { }
 
   int8_t Type() const { return type_; }
-  bool GetBoolean() const { return item_.bool_val_; }
-  int8_t GetByte() const { return item_.byte_val_; }
-  int16_t GetShort() const { return item_.short_val_; }
-  uint16_t GetChar() const { return item_.char_val_; }
-  int32_t GetInt() const { return item_.int_val_; }
-  int64_t GetLong() const { return item_.long_val_; }
-  float GetFloat() const { return item_.float_val_; }
-  double GetDouble() const { return item_.double_val_; }
-  StringId* GetStringId() const { return item_.string_val_; }
-  FieldId* GetFieldId() const { return item_.field_val_; }
-  MethodId* GetMethodId() const { return item_.method_val_; }
+  bool GetBoolean() const { return item_.u_.bool_val_; }
+  int8_t GetByte() const { return item_.u_.byte_val_; }
+  int16_t GetShort() const { return item_.u_.short_val_; }
+  uint16_t GetChar() const { return item_.u_.char_val_; }
+  int32_t GetInt() const { return item_.u_.int_val_; }
+  int64_t GetLong() const { return item_.u_.long_val_; }
+  float GetFloat() const { return item_.u_.float_val_; }
+  double GetDouble() const { return item_.u_.double_val_; }
+  StringId* GetStringId() const { return item_.u_.string_val_; }
+  FieldId* GetFieldId() const { return item_.u_.field_val_; }
+  MethodId* GetMethodId() const { return item_.u_.method_val_; }
   std::vector<std::unique_ptr<ArrayItem>>* GetAnnotationArray() const {
-    return item_.annotation_array_val_;
+    return item_.annotation_array_val_.get();
   }
   StringId* GetAnnotationAnnotationString() const {
     return item_.annotation_annotation_val_.string_;
   }
   std::vector<std::unique_ptr<NameValuePair>>* GetAnnotationAnnotationNameValuePairArray() const {
-    return item_.annotation_annotation_val_.array_;
+    return item_.annotation_annotation_val_.array_.get();
   }
+  // Used to construct the item union.  Ugly, but necessary.
+  ArrayItemVariant* GetArrayItemVariant() { return &item_; }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  void Read(Header& header, const uint8_t** data, uint8_t type, uint8_t length);
   uint8_t type_;
-  union {
-    bool bool_val_;
-    int8_t byte_val_;
-    int16_t short_val_;
-    uint16_t char_val_;
-    int32_t int_val_;
-    int64_t long_val_;
-    float float_val_;
-    double double_val_;
-    StringId* string_val_;
-    FieldId* field_val_;
-    MethodId* method_val_;
-    std::vector<std::unique_ptr<ArrayItem>>* annotation_array_val_;
-    struct {
-      StringId* string_;
-      std::vector<std::unique_ptr<NameValuePair>>* array_;
-    } annotation_annotation_val_;
-  } item_;
+  ArrayItemVariant item_;
   DISALLOW_COPY_AND_ASSIGN(ArrayItem);
 };
 
+using ArrayItemVector = std::vector<std::unique_ptr<ArrayItem>>;
+
 class ClassData : public Item {
  public:
-  ClassData() = default;
+  ClassData(FieldItemVector* static_fields,
+            FieldItemVector* instance_fields,
+            MethodItemVector* direct_methods,
+            MethodItemVector* virtual_methods)
+      : static_fields_(static_fields),
+        instance_fields_(instance_fields),
+        direct_methods_(direct_methods),
+        virtual_methods_(virtual_methods) { }
+
   ~ClassData() OVERRIDE = default;
-  std::vector<std::unique_ptr<FieldItem>>& StaticFields() { return static_fields_; }
-  std::vector<std::unique_ptr<FieldItem>>& InstanceFields() { return instance_fields_; }
-  std::vector<std::unique_ptr<MethodItem>>& DirectMethods() { return direct_methods_; }
-  std::vector<std::unique_ptr<MethodItem>>& VirtualMethods() { return virtual_methods_; }
+  FieldItemVector* StaticFields() { return static_fields_.get(); }
+  FieldItemVector* InstanceFields() { return instance_fields_.get(); }
+  MethodItemVector* DirectMethods() { return direct_methods_.get(); }
+  MethodItemVector* VirtualMethods() { return virtual_methods_.get(); }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  std::vector<std::unique_ptr<FieldItem>> static_fields_;
-  std::vector<std::unique_ptr<FieldItem>> instance_fields_;
-  std::vector<std::unique_ptr<MethodItem>> direct_methods_;
-  std::vector<std::unique_ptr<MethodItem>> virtual_methods_;
+  std::unique_ptr<FieldItemVector> static_fields_;
+  std::unique_ptr<FieldItemVector> instance_fields_;
+  std::unique_ptr<MethodItemVector> direct_methods_;
+  std::unique_ptr<MethodItemVector> virtual_methods_;
   DISALLOW_COPY_AND_ASSIGN(ClassData);
 };
 
 class ClassDef : public Item {
  public:
-  ClassDef(const DexFile::ClassDef& disk_class_def, Header& header);
+  ClassDef(const TypeId* class_type,
+           uint32_t access_flags,
+           const TypeId* superclass,
+           TypeIdVector* interfaces,
+           uint32_t interfaces_offset,
+           const StringId* source_file,
+           AnnotationsDirectoryItem* annotations,
+           ArrayItemVector* static_values,
+           ClassData* class_data)
+      : class_type_(class_type),
+        access_flags_(access_flags),
+        superclass_(superclass),
+        interfaces_(interfaces),
+        interfaces_offset_(interfaces_offset),
+        source_file_(source_file),
+        annotations_(annotations),
+        static_values_(static_values),
+        class_data_(class_data) { }
+
   ~ClassDef() OVERRIDE { }
 
   const TypeId* ClassType() const { return class_type_; }
   uint32_t GetAccessFlags() const { return access_flags_; }
   const TypeId* Superclass() const { return superclass_; }
-  std::vector<TypeId*>* Interfaces() { return &interfaces_; }
+  TypeIdVector* Interfaces() { return interfaces_.get(); }
   uint32_t InterfacesOffset() const { return interfaces_offset_; }
   void SetInterfacesOffset(uint32_t new_offset) { interfaces_offset_ = new_offset; }
   const StringId* SourceFile() const { return source_file_; }
   AnnotationsDirectoryItem* Annotations() const { return annotations_.get(); }
-  std::vector<std::unique_ptr<ArrayItem>>* StaticValues() { return static_values_; }
-  ClassData* GetClassData() { return &class_data_; }
+  ArrayItemVector* StaticValues() { return static_values_.get(); }
+  ClassData* GetClassData() { return class_data_.get(); }
 
   MethodItem* GenerateMethodItem(Header& header, ClassDataItemIterator& cdii);
 
@@ -448,28 +487,78 @@
   const TypeId* class_type_;
   uint32_t access_flags_;
   const TypeId* superclass_;
-  std::vector<TypeId*> interfaces_;
+  std::unique_ptr<TypeIdVector> interfaces_;
   uint32_t interfaces_offset_;
   const StringId* source_file_;
   std::unique_ptr<AnnotationsDirectoryItem> annotations_;
-  std::vector<std::unique_ptr<ArrayItem>>* static_values_;
-  ClassData class_data_;
+  std::unique_ptr<ArrayItemVector> static_values_;
+  std::unique_ptr<ClassData> class_data_;
   DISALLOW_COPY_AND_ASSIGN(ClassDef);
 };
 
+class CatchHandler {
+ public:
+  CatchHandler(const TypeId* type_id, uint32_t address) : type_id_(type_id), address_(address) { }
+
+  const TypeId* GetTypeId() const { return type_id_; }
+  uint32_t GetAddress() const { return address_; }
+
+ private:
+  const TypeId* type_id_;
+  uint32_t address_;
+  DISALLOW_COPY_AND_ASSIGN(CatchHandler);
+};
+
+using CatchHandlerVector = std::vector<std::unique_ptr<const CatchHandler>>;
+
+class TryItem : public Item {
+ public:
+  TryItem(uint32_t start_addr, uint16_t insn_count, CatchHandlerVector* handlers)
+      : start_addr_(start_addr), insn_count_(insn_count), handlers_(handlers) { }
+  ~TryItem() OVERRIDE { }
+
+  uint32_t StartAddr() const { return start_addr_; }
+  uint16_t InsnCount() const { return insn_count_; }
+  const CatchHandlerVector& GetHandlers() const { return *handlers_.get(); }
+
+  void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
+
+ private:
+  uint32_t start_addr_;
+  uint16_t insn_count_;
+  std::unique_ptr<CatchHandlerVector> handlers_;
+  DISALLOW_COPY_AND_ASSIGN(TryItem);
+};
+
+using TryItemVector = std::vector<std::unique_ptr<const TryItem>>;
+
 class CodeItem : public Item {
  public:
-  CodeItem(const DexFile::CodeItem& disk_code_item, Header& header);
+  CodeItem(uint16_t registers_size,
+           uint16_t ins_size,
+           uint16_t outs_size,
+           DebugInfoItem* debug_info,
+           uint32_t insns_size,
+           uint16_t* insns,
+           TryItemVector* tries)
+      : registers_size_(registers_size),
+        ins_size_(ins_size),
+        outs_size_(outs_size),
+        debug_info_(debug_info),
+        insns_size_(insns_size),
+        insns_(insns),
+        tries_(tries) { }
+
   ~CodeItem() OVERRIDE { }
 
   uint16_t RegistersSize() const { return registers_size_; }
   uint16_t InsSize() const { return ins_size_; }
   uint16_t OutsSize() const { return outs_size_; }
-  uint16_t TriesSize() const { return tries_size_; }
+  uint16_t TriesSize() const { return tries_ == nullptr ? 0 : tries_->size(); }
   DebugInfoItem* DebugInfo() const { return debug_info_.get(); }
   uint32_t InsnsSize() const { return insns_size_; }
   uint16_t* Insns() const { return insns_.get(); }
-  std::vector<std::unique_ptr<const TryItem>>* Tries() const { return tries_; }
+  TryItemVector* Tries() const { return tries_.get(); }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
@@ -477,56 +566,13 @@
   uint16_t registers_size_;
   uint16_t ins_size_;
   uint16_t outs_size_;
-  uint16_t tries_size_;
   std::unique_ptr<DebugInfoItem> debug_info_;
   uint32_t insns_size_;
   std::unique_ptr<uint16_t[]> insns_;
-  std::vector<std::unique_ptr<const TryItem>>* tries_;
+  std::unique_ptr<TryItemVector> tries_;
   DISALLOW_COPY_AND_ASSIGN(CodeItem);
 };
 
-class TryItem : public Item {
- public:
-  class CatchHandler {
-   public:
-    CatchHandler(const TypeId* type_id, uint32_t address) : type_id_(type_id), address_(address) { }
-
-    const TypeId* GetTypeId() const { return type_id_; }
-    uint32_t GetAddress() const { return address_; }
-
-   private:
-    const TypeId* type_id_;
-    uint32_t address_;
-    DISALLOW_COPY_AND_ASSIGN(CatchHandler);
-  };
-
-  TryItem(const DexFile::TryItem& disk_try_item,
-          const DexFile::CodeItem& disk_code_item,
-          Header& header) {
-    start_addr_ = disk_try_item.start_addr_;
-    insn_count_ = disk_try_item.insn_count_;
-    for (CatchHandlerIterator it(disk_code_item, disk_try_item); it.HasNext(); it.Next()) {
-      const uint16_t type_index = it.GetHandlerTypeIndex();
-      const TypeId* type_id = header.GetTypeIdOrNullPtr(type_index);
-      handlers_.push_back(std::unique_ptr<const CatchHandler>(
-          new CatchHandler(type_id, it.GetHandlerAddress())));
-    }
-  }
-  ~TryItem() OVERRIDE { }
-
-  uint32_t StartAddr() const { return start_addr_; }
-  uint16_t InsnCount() const { return insn_count_; }
-  const std::vector<std::unique_ptr<const CatchHandler>>& GetHandlers() const { return handlers_; }
-
-  void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
-
- private:
-  uint32_t start_addr_;
-  uint16_t insn_count_;
-  std::vector<std::unique_ptr<const CatchHandler>> handlers_;
-  DISALLOW_COPY_AND_ASSIGN(TryItem);
-};
-
 
 struct PositionInfo {
   PositionInfo(uint32_t address, uint32_t line) : address_(address), line_(line) { }
@@ -535,11 +581,21 @@
   uint32_t line_;
 };
 
+using PositionInfoVector = std::vector<std::unique_ptr<PositionInfo>>;
+
 struct LocalInfo {
-  LocalInfo(const char* name, const char* descriptor, const char* signature, uint32_t start_address,
-            uint32_t end_address, uint16_t reg) :
-    name_(name), descriptor_(descriptor), signature_(signature), start_address_(start_address),
-    end_address_(end_address), reg_(reg) { }
+  LocalInfo(const char* name,
+            const char* descriptor,
+            const char* signature,
+            uint32_t start_address,
+            uint32_t end_address,
+            uint16_t reg)
+      : name_(name),
+        descriptor_(descriptor),
+        signature_(signature),
+        start_address_(start_address),
+        end_address_(end_address),
+        reg_(reg) { }
 
   std::string name_;
   std::string descriptor_;
@@ -549,124 +605,123 @@
   uint16_t reg_;
 };
 
+using LocalInfoVector = std::vector<std::unique_ptr<LocalInfo>>;
+
 class DebugInfoItem : public Item {
  public:
   DebugInfoItem() = default;
 
-  std::vector<std::unique_ptr<PositionInfo>>& GetPositionInfo() { return positions_; }
-  std::vector<std::unique_ptr<LocalInfo>>& GetLocalInfo() { return locals_; }
+  PositionInfoVector& GetPositionInfo() { return positions_; }
+  LocalInfoVector& GetLocalInfo() { return locals_; }
 
  private:
-  std::vector<std::unique_ptr<PositionInfo>> positions_;
-  std::vector<std::unique_ptr<LocalInfo>> locals_;
+  PositionInfoVector positions_;
+  LocalInfoVector locals_;
   DISALLOW_COPY_AND_ASSIGN(DebugInfoItem);
 };
 
+class AnnotationItem {
+ public:
+  AnnotationItem(uint8_t visibility, ArrayItem* item) : visibility_(visibility), item_(item) { }
+
+  uint8_t GetVisibility() const { return visibility_; }
+  ArrayItem* GetItem() const { return item_.get(); }
+
+ private:
+  uint8_t visibility_;
+  std::unique_ptr<ArrayItem> item_;
+  DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
+};
+
+using AnnotationItemVector = std::vector<std::unique_ptr<AnnotationItem>>;
+
 class AnnotationSetItem : public Item {
  public:
-  class AnnotationItem {
-   public:
-    AnnotationItem(uint8_t visibility, ArrayItem* item) :
-      visibility_(visibility), item_(item) { }
-
-    uint8_t GetVisibility() const { return visibility_; }
-    ArrayItem* GetItem() const { return item_.get(); }
-
-   private:
-    uint8_t visibility_;
-    std::unique_ptr<ArrayItem> item_;
-    DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
-  };
-
-  AnnotationSetItem(const DexFile::AnnotationSetItem& disk_annotations_item, Header& header);
+  explicit AnnotationSetItem(AnnotationItemVector* items) : items_(items) { }
   ~AnnotationSetItem() OVERRIDE { }
 
-  std::vector<std::unique_ptr<AnnotationItem>>& GetItems() { return items_; }
+  AnnotationItemVector* GetItems() { return items_.get(); }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  std::vector<std::unique_ptr<AnnotationItem>> items_;
+  std::unique_ptr<AnnotationItemVector> items_;
   DISALLOW_COPY_AND_ASSIGN(AnnotationSetItem);
 };
 
+using AnnotationSetItemVector = std::vector<std::unique_ptr<AnnotationSetItem>>;
+
+class FieldAnnotation {
+ public:
+  FieldAnnotation(FieldId* field_id, AnnotationSetItem* annotation_set_item)
+      : field_id_(field_id), annotation_set_item_(annotation_set_item) { }
+
+  FieldId* GetFieldId() const { return field_id_; }
+  AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_.get(); }
+
+ private:
+  FieldId* field_id_;
+  std::unique_ptr<AnnotationSetItem> annotation_set_item_;
+  DISALLOW_COPY_AND_ASSIGN(FieldAnnotation);
+};
+
+using FieldAnnotationVector = std::vector<std::unique_ptr<FieldAnnotation>>;
+
+class MethodAnnotation {
+ public:
+  MethodAnnotation(MethodId* method_id, AnnotationSetItem* annotation_set_item)
+      : method_id_(method_id), annotation_set_item_(annotation_set_item) { }
+
+  MethodId* GetMethodId() const { return method_id_; }
+  AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_.get(); }
+
+ private:
+  MethodId* method_id_;
+  std::unique_ptr<AnnotationSetItem> annotation_set_item_;
+  DISALLOW_COPY_AND_ASSIGN(MethodAnnotation);
+};
+
+using MethodAnnotationVector = std::vector<std::unique_ptr<MethodAnnotation>>;
+
+class ParameterAnnotation {
+ public:
+  ParameterAnnotation(MethodId* method_id, AnnotationSetItemVector* annotations)
+      : method_id_(method_id), annotations_(annotations) { }
+
+  MethodId* GetMethodId() const { return method_id_; }
+  AnnotationSetItemVector* GetAnnotations() { return annotations_.get(); }
+
+ private:
+  MethodId* method_id_;
+  std::unique_ptr<AnnotationSetItemVector> annotations_;
+  DISALLOW_COPY_AND_ASSIGN(ParameterAnnotation);
+};
+
+using ParameterAnnotationVector = std::vector<std::unique_ptr<ParameterAnnotation>>;
+
 class AnnotationsDirectoryItem : public Item {
  public:
-  class FieldAnnotation {
-   public:
-    FieldAnnotation(FieldId* field_id, AnnotationSetItem* annotation_set_item) :
-      field_id_(field_id), annotation_set_item_(annotation_set_item) { }
-
-    FieldId* GetFieldId() const { return field_id_; }
-    AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_.get(); }
-
-   private:
-    FieldId* field_id_;
-    std::unique_ptr<AnnotationSetItem> annotation_set_item_;
-    DISALLOW_COPY_AND_ASSIGN(FieldAnnotation);
-  };
-
-  class MethodAnnotation {
-   public:
-    MethodAnnotation(MethodId* method_id, AnnotationSetItem* annotation_set_item) :
-      method_id_(method_id), annotation_set_item_(annotation_set_item) { }
-
-    MethodId* GetMethodId() const { return method_id_; }
-    AnnotationSetItem* GetAnnotationSetItem() const { return annotation_set_item_.get(); }
-
-   private:
-    MethodId* method_id_;
-    std::unique_ptr<AnnotationSetItem> annotation_set_item_;
-    DISALLOW_COPY_AND_ASSIGN(MethodAnnotation);
-  };
-
-  class ParameterAnnotation {
-   public:
-    ParameterAnnotation(MethodId* method_id,
-                        const DexFile::AnnotationSetRefList* annotation_set_ref_list,
-                        Header& header) :
-      method_id_(method_id) {
-      for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
-        const DexFile::AnnotationSetItem* annotation_set_item =
-            header.GetDexFile().GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
-        annotations_.push_back(std::unique_ptr<AnnotationSetItem>(
-            new AnnotationSetItem(*annotation_set_item, header)));
-      }
-    }
-
-    MethodId* GetMethodId() const { return method_id_; }
-    std::vector<std::unique_ptr<AnnotationSetItem>>& GetAnnotations() { return annotations_; }
-
-   private:
-    MethodId* method_id_;
-    std::vector<std::unique_ptr<AnnotationSetItem>> annotations_;
-    DISALLOW_COPY_AND_ASSIGN(ParameterAnnotation);
-  };
-
-  AnnotationsDirectoryItem(const DexFile::AnnotationsDirectoryItem* disk_annotations_item,
-                           Header& header);
+  AnnotationsDirectoryItem(AnnotationSetItem* class_annotation,
+                           FieldAnnotationVector* field_annotations,
+                           MethodAnnotationVector* method_annotations,
+                           ParameterAnnotationVector* parameter_annotations)
+      : class_annotation_(class_annotation),
+        field_annotations_(field_annotations),
+        method_annotations_(method_annotations),
+        parameter_annotations_(parameter_annotations) { }
 
   AnnotationSetItem* GetClassAnnotation() const { return class_annotation_.get(); }
-
-  std::vector<std::unique_ptr<FieldAnnotation>>& GetFieldAnnotations() {
-    return field_annotations_;
-  }
-
-  std::vector<std::unique_ptr<MethodAnnotation>>& GetMethodAnnotations() {
-    return method_annotations_;
-  }
-
-  std::vector<std::unique_ptr<ParameterAnnotation>>& GetParameterAnnotations() {
-    return parameter_annotations_;
-  }
+  FieldAnnotationVector* GetFieldAnnotations() { return field_annotations_.get(); }
+  MethodAnnotationVector* GetMethodAnnotations() { return method_annotations_.get(); }
+  ParameterAnnotationVector* GetParameterAnnotations() { return parameter_annotations_.get(); }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
   std::unique_ptr<AnnotationSetItem> class_annotation_;
-  std::vector<std::unique_ptr<FieldAnnotation>> field_annotations_;
-  std::vector<std::unique_ptr<MethodAnnotation>> method_annotations_;
-  std::vector<std::unique_ptr<ParameterAnnotation>> parameter_annotations_;
+  std::unique_ptr<FieldAnnotationVector> field_annotations_;
+  std::unique_ptr<MethodAnnotationVector> method_annotations_;
+  std::unique_ptr<ParameterAnnotationVector> parameter_annotations_;
   DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem);
 };
 
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
new file mode 100644
index 0000000..30f57d9
--- /dev/null
+++ b/dexlayout/dex_ir_builder.cc
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Header file of an in-memory representation of DEX files.
+ */
+
+#include <stdint.h>
+#include <vector>
+
+#include "dex_ir_builder.h"
+
+namespace art {
+namespace dex_ir {
+
+namespace {
+
+static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) {
+  uint64_t value = 0;
+  for (uint32_t i = 0; i <= length; i++) {
+    value |= static_cast<uint64_t>(*(*data)++) << (i * 8);
+  }
+  if (sign_extend) {
+    int shift = (7 - length) * 8;
+    return (static_cast<int64_t>(value) << shift) >> shift;
+  }
+  return value;
+}
+
+// Prototype to break cyclic dependency.
+void ReadArrayItemVariant(Header& header,
+                          const uint8_t** data,
+                          uint8_t type,
+                          uint8_t length,
+                          ArrayItem::ArrayItemVariant* item);
+
+ArrayItem* ReadArrayItem(Header& header, const uint8_t** data, uint8_t type, uint8_t length) {
+  ArrayItem* item = new ArrayItem(type);
+  ReadArrayItemVariant(header, data, type, length, item->GetArrayItemVariant());
+  return item;
+}
+
+ArrayItem* ReadArrayItem(Header& header, const uint8_t** data) {
+  const uint8_t encoded_value = *(*data)++;
+  const uint8_t type = encoded_value & 0x1f;
+  ArrayItem* item = new ArrayItem(type);
+  ReadArrayItemVariant(header, data, type, encoded_value >> 5, item->GetArrayItemVariant());
+  return item;
+}
+
+void ReadArrayItemVariant(Header& header,
+                          const uint8_t** data,
+                          uint8_t type,
+                          uint8_t length,
+                          ArrayItem::ArrayItemVariant* item) {
+  switch (type) {
+    case DexFile::kDexAnnotationByte:
+      item->u_.byte_val_ = static_cast<int8_t>(ReadVarWidth(data, length, false));
+      break;
+    case DexFile::kDexAnnotationShort:
+      item->u_.short_val_ = static_cast<int16_t>(ReadVarWidth(data, length, true));
+      break;
+    case DexFile::kDexAnnotationChar:
+      item->u_.char_val_ = static_cast<uint16_t>(ReadVarWidth(data, length, false));
+      break;
+    case DexFile::kDexAnnotationInt:
+      item->u_.int_val_ = static_cast<int32_t>(ReadVarWidth(data, length, true));
+      break;
+    case DexFile::kDexAnnotationLong:
+      item->u_.long_val_ = static_cast<int64_t>(ReadVarWidth(data, length, true));
+      break;
+    case DexFile::kDexAnnotationFloat: {
+      // Fill on right.
+      union {
+        float f;
+        uint32_t data;
+      } conv;
+      conv.data = static_cast<uint32_t>(ReadVarWidth(data, length, false)) << (3 - length) * 8;
+      item->u_.float_val_ = conv.f;
+      break;
+    }
+    case DexFile::kDexAnnotationDouble: {
+      // Fill on right.
+      union {
+        double d;
+        uint64_t data;
+      } conv;
+      conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8;
+      item->u_.double_val_ = conv.d;
+      break;
+    }
+    case DexFile::kDexAnnotationString: {
+      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->u_.string_val_ = header.StringIds()[string_index].get();
+      break;
+    }
+    case DexFile::kDexAnnotationType: {
+      const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->u_.string_val_ = header.TypeIds()[string_index]->GetStringId();
+      break;
+    }
+    case DexFile::kDexAnnotationField:
+    case DexFile::kDexAnnotationEnum: {
+      const uint32_t field_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->u_.field_val_ = header.FieldIds()[field_index].get();
+      break;
+    }
+    case DexFile::kDexAnnotationMethod: {
+      const uint32_t method_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
+      item->u_.method_val_ = header.MethodIds()[method_index].get();
+      break;
+    }
+    case DexFile::kDexAnnotationArray: {
+      item->annotation_array_val_.reset(new ArrayItemVector());
+      // Decode all elements.
+      const uint32_t size = DecodeUnsignedLeb128(data);
+      for (uint32_t i = 0; i < size; i++) {
+        item->annotation_array_val_->push_back(
+            std::unique_ptr<ArrayItem>(ReadArrayItem(header, data)));
+      }
+      break;
+    }
+    case DexFile::kDexAnnotationAnnotation: {
+      const uint32_t type_idx = DecodeUnsignedLeb128(data);
+      item->annotation_annotation_val_.string_ = header.TypeIds()[type_idx]->GetStringId();
+      item->annotation_annotation_val_.array_.reset(
+          new std::vector<std::unique_ptr<ArrayItem::NameValuePair>>());
+      // Decode all name=value pairs.
+      const uint32_t size = DecodeUnsignedLeb128(data);
+      for (uint32_t i = 0; i < size; i++) {
+        const uint32_t name_index = DecodeUnsignedLeb128(data);
+        item->annotation_annotation_val_.array_->push_back(
+            std::unique_ptr<ArrayItem::NameValuePair>(
+                new ArrayItem::NameValuePair(header.StringIds()[name_index].get(),
+                                             ReadArrayItem(header, data))));
+      }
+      break;
+    }
+    case DexFile::kDexAnnotationNull:
+      break;
+    case DexFile::kDexAnnotationBoolean:
+      item->u_.bool_val_ = (length != 0);
+      break;
+    default:
+      break;
+  }
+}
+
+static bool GetPositionsCb(void* context, const DexFile::PositionInfo& entry) {
+  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
+  PositionInfoVector& positions = debug_info->GetPositionInfo();
+  positions.push_back(std::unique_ptr<PositionInfo>(new PositionInfo(entry.address_, entry.line_)));
+  return false;
+}
+
+static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
+  DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
+  LocalInfoVector& locals = debug_info->GetLocalInfo();
+  const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
+  const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
+  locals.push_back(std::unique_ptr<LocalInfo>(
+      new LocalInfo(name, entry.descriptor_, signature, entry.start_address_,
+                    entry.end_address_, entry.reg_)));
+}
+
+CodeItem* ReadCodeItem(const DexFile& dex_file,
+                       const DexFile::CodeItem& disk_code_item,
+                       Header& header) {
+  uint16_t registers_size = disk_code_item.registers_size_;
+  uint16_t ins_size = disk_code_item.ins_size_;
+  uint16_t outs_size = disk_code_item.outs_size_;
+  uint32_t tries_size = disk_code_item.tries_size_;
+
+  const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
+  DebugInfoItem* debug_info = nullptr;
+  if (debug_info_stream != nullptr) {
+    debug_info = new DebugInfoItem();
+  }
+
+  uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
+  uint16_t* insns = new uint16_t[insns_size];
+  memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t));
+
+  TryItemVector* tries = nullptr;
+  if (tries_size > 0) {
+    tries = new TryItemVector();
+    for (uint32_t i = 0; i < tries_size; ++i) {
+      const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
+      uint32_t start_addr = disk_try_item->start_addr_;
+      uint16_t insn_count = disk_try_item->insn_count_;
+      CatchHandlerVector* handlers = new CatchHandlerVector();
+      for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
+        const uint16_t type_index = it.GetHandlerTypeIndex();
+        const TypeId* type_id = header.GetTypeIdOrNullPtr(type_index);
+        handlers->push_back(std::unique_ptr<const CatchHandler>(
+            new CatchHandler(type_id, it.GetHandlerAddress())));
+      }
+      TryItem* try_item = new TryItem(start_addr, insn_count, handlers);
+      tries->push_back(std::unique_ptr<const TryItem>(try_item));
+    }
+  }
+  return new CodeItem(registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries);
+}
+
+MethodItem* GenerateMethodItem(const DexFile& dex_file,
+                               dex_ir::Header& header,
+                               ClassDataItemIterator& cdii) {
+  MethodId* method_item = header.MethodIds()[cdii.GetMemberIndex()].get();
+  uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+  const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
+  CodeItem* code_item = nullptr;
+  DebugInfoItem* debug_info = nullptr;
+  if (disk_code_item != nullptr) {
+    code_item = ReadCodeItem(dex_file, *disk_code_item, header);
+    code_item->SetOffset(cdii.GetMethodCodeItemOffset());
+    debug_info = code_item->DebugInfo();
+  }
+  if (debug_info != nullptr) {
+    bool is_static = (access_flags & kAccStatic) != 0;
+    dex_file.DecodeDebugLocalInfo(
+        disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
+    dex_file.DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
+  }
+  return new MethodItem(access_flags, method_item, code_item);
+}
+
+AnnotationSetItem* ReadAnnotationSetItem(const DexFile& dex_file,
+                                         const DexFile::AnnotationSetItem& disk_annotations_item,
+                                         Header& header) {
+  if (disk_annotations_item.size_ == 0) {
+    return nullptr;
+  }
+  AnnotationItemVector* items = new AnnotationItemVector();
+  for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
+    const DexFile::AnnotationItem* annotation =
+        dex_file.GetAnnotationItem(&disk_annotations_item, i);
+    if (annotation == nullptr) {
+      continue;
+    }
+    uint8_t visibility = annotation->visibility_;
+    const uint8_t* annotation_data = annotation->annotation_;
+    ArrayItem* array_item =
+        ReadArrayItem(header, &annotation_data, DexFile::kDexAnnotationAnnotation, 0);
+    items->push_back(std::unique_ptr<AnnotationItem>(new AnnotationItem(visibility, array_item)));
+  }
+  return new AnnotationSetItem(items);
+}
+
+ParameterAnnotation* ReadParameterAnnotation(
+    const DexFile& dex_file,
+    MethodId* method_id,
+    const DexFile::AnnotationSetRefList* annotation_set_ref_list,
+    Header& header) {
+  AnnotationSetItemVector* annotations = new AnnotationSetItemVector();
+  for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
+    const DexFile::AnnotationSetItem* annotation_set_item =
+        dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
+    annotations->push_back(std::unique_ptr<AnnotationSetItem>(
+        ReadAnnotationSetItem(dex_file, *annotation_set_item, header)));
+  }
+  return new ParameterAnnotation(method_id, annotations);
+}
+
+AnnotationsDirectoryItem* ReadAnnotationsDirectoryItem(
+    const DexFile& dex_file,
+    const DexFile::AnnotationsDirectoryItem* disk_annotations_item,
+    Header& header) {
+  const DexFile::AnnotationSetItem* class_set_item =
+      dex_file.GetClassAnnotationSet(disk_annotations_item);
+  AnnotationSetItem* class_annotation = nullptr;
+  if (class_set_item != nullptr) {
+    class_annotation = ReadAnnotationSetItem(dex_file, *class_set_item, header);
+  }
+  const DexFile::FieldAnnotationsItem* fields =
+      dex_file.GetFieldAnnotations(disk_annotations_item);
+  FieldAnnotationVector* field_annotations = nullptr;
+  if (fields != nullptr) {
+    field_annotations = new FieldAnnotationVector();
+    for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) {
+      FieldId* field_id = header.FieldIds()[fields[i].field_idx_].get();
+      const DexFile::AnnotationSetItem* field_set_item =
+          dex_file.GetFieldAnnotationSetItem(fields[i]);
+      AnnotationSetItem* annotation_set_item =
+          ReadAnnotationSetItem(dex_file, *field_set_item, header);
+      field_annotations->push_back(std::unique_ptr<FieldAnnotation>(
+          new FieldAnnotation(field_id, annotation_set_item)));
+    }
+  }
+  const DexFile::MethodAnnotationsItem* methods =
+      dex_file.GetMethodAnnotations(disk_annotations_item);
+  MethodAnnotationVector* method_annotations = nullptr;
+  if (methods != nullptr) {
+    method_annotations = new MethodAnnotationVector();
+    for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) {
+      MethodId* method_id = header.MethodIds()[methods[i].method_idx_].get();
+      const DexFile::AnnotationSetItem* method_set_item =
+          dex_file.GetMethodAnnotationSetItem(methods[i]);
+      AnnotationSetItem* annotation_set_item =
+          ReadAnnotationSetItem(dex_file, *method_set_item, header);
+      method_annotations->push_back(std::unique_ptr<MethodAnnotation>(
+          new MethodAnnotation(method_id, annotation_set_item)));
+    }
+  }
+  const DexFile::ParameterAnnotationsItem* parameters =
+      dex_file.GetParameterAnnotations(disk_annotations_item);
+  ParameterAnnotationVector* parameter_annotations = nullptr;
+  if (parameters != nullptr) {
+    parameter_annotations = new ParameterAnnotationVector();
+    for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) {
+      MethodId* method_id = header.MethodIds()[parameters[i].method_idx_].get();
+      const DexFile::AnnotationSetRefList* list =
+          dex_file.GetParameterAnnotationSetRefList(&parameters[i]);
+      parameter_annotations->push_back(std::unique_ptr<ParameterAnnotation>(
+          ReadParameterAnnotation(dex_file, method_id, list, header)));
+    }
+  }
+
+  return new AnnotationsDirectoryItem(class_annotation,
+                                      field_annotations,
+                                      method_annotations,
+                                      parameter_annotations);
+}
+
+ClassDef* ReadClassDef(const DexFile& dex_file,
+                       const DexFile::ClassDef& disk_class_def,
+                       Header& header) {
+  const TypeId* class_type = header.TypeIds()[disk_class_def.class_idx_].get();
+  uint32_t access_flags = disk_class_def.access_flags_;
+  const TypeId* superclass = header.GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
+
+  TypeIdVector* interfaces = nullptr;
+  const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def);
+  uint32_t interfaces_offset = disk_class_def.interfaces_off_;
+  if (type_list != nullptr) {
+    interfaces = new TypeIdVector();
+    for (uint32_t index = 0; index < type_list->Size(); ++index) {
+      interfaces->push_back(header.TypeIds()[type_list->GetTypeItem(index).type_idx_].get());
+    }
+  }
+  const StringId* source_file = header.GetStringIdOrNullPtr(disk_class_def.source_file_idx_);
+  // Annotations.
+  AnnotationsDirectoryItem* annotations = nullptr;
+  const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item =
+      dex_file.GetAnnotationsDirectory(disk_class_def);
+  if (disk_annotations_directory_item != nullptr) {
+    annotations = ReadAnnotationsDirectoryItem(dex_file, disk_annotations_directory_item, header);
+    annotations->SetOffset(disk_class_def.annotations_off_);
+  }
+  // Static field initializers.
+  ArrayItemVector* static_values = nullptr;
+  const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def);
+  if (static_data != nullptr) {
+    uint32_t static_value_count = static_data == nullptr ? 0 : DecodeUnsignedLeb128(&static_data);
+    if (static_value_count > 0) {
+      static_values = new ArrayItemVector();
+      for (uint32_t i = 0; i < static_value_count; ++i) {
+        static_values->push_back(std::unique_ptr<ArrayItem>(ReadArrayItem(header, &static_data)));
+      }
+    }
+  }
+  // Read the fields and methods defined by the class, resolving the circular reference from those
+  // to classes by setting class at the same time.
+  const uint8_t* encoded_data = dex_file.GetClassData(disk_class_def);
+  ClassData* class_data = nullptr;
+  if (encoded_data != nullptr) {
+    uint32_t offset = disk_class_def.class_data_off_;
+    ClassDataItemIterator cdii(dex_file, encoded_data);
+    // Static fields.
+    FieldItemVector* static_fields = new FieldItemVector();
+    for (uint32_t i = 0; cdii.HasNextStaticField(); i++, cdii.Next()) {
+      FieldId* field_item = header.FieldIds()[cdii.GetMemberIndex()].get();
+      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+      static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+    }
+    // Instance fields.
+    FieldItemVector* instance_fields = new FieldItemVector();
+    for (uint32_t i = 0; cdii.HasNextInstanceField(); i++, cdii.Next()) {
+      FieldId* field_item = header.FieldIds()[cdii.GetMemberIndex()].get();
+      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+      instance_fields->push_back(
+          std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+    }
+    // Direct methods.
+    MethodItemVector* direct_methods = new MethodItemVector();
+    for (uint32_t i = 0; cdii.HasNextDirectMethod(); i++, cdii.Next()) {
+      direct_methods->push_back(
+          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, header, cdii)));
+    }
+    // Virtual methods.
+    MethodItemVector* virtual_methods = new MethodItemVector();
+    for (uint32_t i = 0; cdii.HasNextVirtualMethod(); i++, cdii.Next()) {
+      virtual_methods->push_back(
+          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, header, cdii)));
+    }
+    class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
+    class_data->SetOffset(offset);
+  }
+  return new ClassDef(class_type,
+                      access_flags,
+                      superclass,
+                      interfaces,
+                      interfaces_offset,
+                      source_file,
+                      annotations,
+                      static_values,
+                      class_data);
+}
+
+}  // namespace
+
+Header* DexIrBuilder(const DexFile& dex_file) {
+  const DexFile::Header& disk_header = dex_file.GetHeader();
+  Header* header = new Header(disk_header.magic_,
+                              disk_header.checksum_,
+                              disk_header.signature_,
+                              disk_header.endian_tag_,
+                              disk_header.file_size_,
+                              disk_header.header_size_,
+                              disk_header.link_size_,
+                              disk_header.link_off_,
+                              disk_header.data_size_,
+                              disk_header.data_off_);
+  // Walk the rest of the header fields.
+  // StringId table.
+  std::vector<std::unique_ptr<StringId>>& string_ids = header->StringIds();
+  header->SetStringIdsOffset(disk_header.string_ids_off_);
+  for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) {
+    const DexFile::StringId& disk_string_id = dex_file.GetStringId(i);
+    StringId* string_id = new StringId(dex_file.GetStringData(disk_string_id));
+    string_id->SetOffset(i);
+    string_ids.push_back(std::unique_ptr<StringId>(string_id));
+  }
+  // TypeId table.
+  std::vector<std::unique_ptr<TypeId>>& type_ids = header->TypeIds();
+  header->SetTypeIdsOffset(disk_header.type_ids_off_);
+  for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
+    const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(i);
+    TypeId* type_id = new TypeId(header->StringIds()[disk_type_id.descriptor_idx_].get());
+    type_id->SetOffset(i);
+    type_ids.push_back(std::unique_ptr<TypeId>(type_id));
+  }
+  // ProtoId table.
+  std::vector<std::unique_ptr<ProtoId>>& proto_ids = header->ProtoIds();
+  header->SetProtoIdsOffset(disk_header.proto_ids_off_);
+  for (uint32_t i = 0; i < dex_file.NumProtoIds(); ++i) {
+    const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
+    // Build the parameter type vector.
+    TypeIdVector* parameters = new TypeIdVector();
+    DexFileParameterIterator dfpi(dex_file, disk_proto_id);
+    while (dfpi.HasNext()) {
+      parameters->push_back(header->TypeIds()[dfpi.GetTypeIdx()].get());
+      dfpi.Next();
+    }
+    ProtoId* proto_id = new ProtoId(header->StringIds()[disk_proto_id.shorty_idx_].get(),
+                                    header->TypeIds()[disk_proto_id.return_type_idx_].get(),
+                                    parameters);
+    proto_id->SetOffset(i);
+    proto_ids.push_back(std::unique_ptr<ProtoId>(proto_id));
+  }
+  // FieldId table.
+  std::vector<std::unique_ptr<FieldId>>& field_ids = header->FieldIds();
+  header->SetFieldIdsOffset(disk_header.field_ids_off_);
+  for (uint32_t i = 0; i < dex_file.NumFieldIds(); ++i) {
+    const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i);
+    FieldId* field_id = new FieldId(header->TypeIds()[disk_field_id.class_idx_].get(),
+                                    header->TypeIds()[disk_field_id.type_idx_].get(),
+                                    header->StringIds()[disk_field_id.name_idx_].get());
+    field_id->SetOffset(i);
+    field_ids.push_back(std::unique_ptr<FieldId>(field_id));
+  }
+  // MethodId table.
+  std::vector<std::unique_ptr<MethodId>>& method_ids = header->MethodIds();
+  header->SetMethodIdsOffset(disk_header.method_ids_off_);
+  for (uint32_t i = 0; i < dex_file.NumMethodIds(); ++i) {
+    const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
+    MethodId* method_id = new MethodId(header->TypeIds()[disk_method_id.class_idx_].get(),
+                                       header->ProtoIds()[disk_method_id.proto_idx_].get(),
+                                       header->StringIds()[disk_method_id.name_idx_].get());
+    method_id->SetOffset(i);
+    method_ids.push_back(std::unique_ptr<MethodId>(method_id));
+  }
+  // ClassDef table.
+  std::vector<std::unique_ptr<ClassDef>>& class_defs = header->ClassDefs();
+  header->SetClassDefsOffset(disk_header.class_defs_off_);
+  for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
+    const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i);
+    ClassDef* class_def = ReadClassDef(dex_file, disk_class_def, *header);
+    class_def->SetOffset(i);
+    class_defs.push_back(std::unique_ptr<ClassDef>(class_def));
+  }
+
+  return header;
+}
+
+}  // namespace dex_ir
+}  // namespace art
diff --git a/dexlayout/dex_ir_builder.h b/dexlayout/dex_ir_builder.h
new file mode 100644
index 0000000..c53157b
--- /dev/null
+++ b/dexlayout/dex_ir_builder.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Header file of an in-memory representation of DEX files.
+ */
+
+#ifndef ART_DEXLAYOUT_DEX_IR_BUILDER_H_
+#define ART_DEXLAYOUT_DEX_IR_BUILDER_H_
+
+#include "dex_ir.h"
+
+namespace art {
+namespace dex_ir {
+
+dex_ir::Header* DexIrBuilder(const DexFile& dex_file);
+
+}  // namespace dex_ir
+}  // namespace art
+
+#endif  // ART_DEXLAYOUT_DEX_IR_BUILDER_H_
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 0b31614..3a3f417 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -30,7 +30,7 @@
 #include <sstream>
 #include <vector>
 
-#include "dex_ir.h"
+#include "dex_ir_builder.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "utils.h"
@@ -501,20 +501,33 @@
   }
   fprintf(out_file_, "annotations_off     : %d (0x%06x)\n",
           annotations_offset, annotations_offset);
-  fprintf(out_file_, "class_data_off      : %d (0x%06x)\n",
-          class_def->GetClassData()->GetOffset(), class_def->GetClassData()->GetOffset());
+  if (class_def->GetClassData() == nullptr) {
+    fprintf(out_file_, "class_data_off      : %d (0x%06x)\n", 0, 0);
+  } else {
+    fprintf(out_file_, "class_data_off      : %d (0x%06x)\n",
+            class_def->GetClassData()->GetOffset(), class_def->GetClassData()->GetOffset());
+  }
 
   // Fields and methods.
   dex_ir::ClassData* class_data = class_def->GetClassData();
-  if (class_data != nullptr) {
-    fprintf(out_file_, "static_fields_size  : %zu\n", class_data->StaticFields().size());
-    fprintf(out_file_, "instance_fields_size: %zu\n", class_data->InstanceFields().size());
-    fprintf(out_file_, "direct_methods_size : %zu\n", class_data->DirectMethods().size());
-    fprintf(out_file_, "virtual_methods_size: %zu\n", class_data->VirtualMethods().size());
+  if (class_data != nullptr && class_data->StaticFields() != nullptr) {
+    fprintf(out_file_, "static_fields_size  : %zu\n", class_data->StaticFields()->size());
   } else {
     fprintf(out_file_, "static_fields_size  : 0\n");
+  }
+  if (class_data != nullptr && class_data->InstanceFields() != nullptr) {
+    fprintf(out_file_, "instance_fields_size: %zu\n", class_data->InstanceFields()->size());
+  } else {
     fprintf(out_file_, "instance_fields_size: 0\n");
+  }
+  if (class_data != nullptr && class_data->DirectMethods() != nullptr) {
+    fprintf(out_file_, "direct_methods_size : %zu\n", class_data->DirectMethods()->size());
+  } else {
     fprintf(out_file_, "direct_methods_size : 0\n");
+  }
+  if (class_data != nullptr && class_data->VirtualMethods() != nullptr) {
+    fprintf(out_file_, "virtual_methods_size: %zu\n", class_data->VirtualMethods()->size());
+  } else {
     fprintf(out_file_, "virtual_methods_size: 0\n");
   }
   fprintf(out_file_, "\n");
@@ -524,12 +537,11 @@
  * Dumps an annotation set item.
  */
 static void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item) {
-  if (set_item == nullptr || set_item->GetItems().size() == 0) {
+  if (set_item == nullptr || set_item->GetItems()->size() == 0) {
     fputs("  empty-annotation-set\n", out_file_);
     return;
   }
-  for (std::unique_ptr<dex_ir::AnnotationSetItem::AnnotationItem>& annotation :
-       set_item->GetItems()) {
+  for (std::unique_ptr<dex_ir::AnnotationItem>& annotation : *set_item->GetItems()) {
     if (annotation == nullptr) {
       continue;
     }
@@ -561,12 +573,9 @@
   fprintf(out_file_, "Class #%d annotations:\n", idx);
 
   dex_ir::AnnotationSetItem* class_set_item = annotations_directory->GetClassAnnotation();
-  std::vector<std::unique_ptr<dex_ir::AnnotationsDirectoryItem::FieldAnnotation>>& fields =
-      annotations_directory->GetFieldAnnotations();
-  std::vector<std::unique_ptr<dex_ir::AnnotationsDirectoryItem::MethodAnnotation>>& methods =
-      annotations_directory->GetMethodAnnotations();
-  std::vector<std::unique_ptr<dex_ir::AnnotationsDirectoryItem::ParameterAnnotation>>& parameters =
-      annotations_directory->GetParameterAnnotations();
+  dex_ir::FieldAnnotationVector* fields = annotations_directory->GetFieldAnnotations();
+  dex_ir::MethodAnnotationVector* methods = annotations_directory->GetMethodAnnotations();
+  dex_ir::ParameterAnnotationVector* parameters = annotations_directory->GetParameterAnnotations();
 
   // Annotations on the class itself.
   if (class_set_item != nullptr) {
@@ -575,34 +584,40 @@
   }
 
   // Annotations on fields.
-  for (auto& field : fields) {
-    const dex_ir::FieldId* field_id = field->GetFieldId();
-    const uint32_t field_idx = field_id->GetOffset();
-    const char* field_name = field_id->Name()->Data();
-    fprintf(out_file_, "Annotations on field #%u '%s'\n", field_idx, field_name);
-    DumpAnnotationSetItem(field->GetAnnotationSetItem());
+  if (fields != nullptr) {
+    for (auto& field : *fields) {
+      const dex_ir::FieldId* field_id = field->GetFieldId();
+      const uint32_t field_idx = field_id->GetOffset();
+      const char* field_name = field_id->Name()->Data();
+      fprintf(out_file_, "Annotations on field #%u '%s'\n", field_idx, field_name);
+      DumpAnnotationSetItem(field->GetAnnotationSetItem());
+    }
   }
 
   // Annotations on methods.
-  for (auto& method : methods) {
-    const dex_ir::MethodId* method_id = method->GetMethodId();
-    const uint32_t method_idx = method_id->GetOffset();
-    const char* method_name = method_id->Name()->Data();
-    fprintf(out_file_, "Annotations on method #%u '%s'\n", method_idx, method_name);
-    DumpAnnotationSetItem(method->GetAnnotationSetItem());
+  if (methods != nullptr) {
+    for (auto& method : *methods) {
+      const dex_ir::MethodId* method_id = method->GetMethodId();
+      const uint32_t method_idx = method_id->GetOffset();
+      const char* method_name = method_id->Name()->Data();
+      fprintf(out_file_, "Annotations on method #%u '%s'\n", method_idx, method_name);
+      DumpAnnotationSetItem(method->GetAnnotationSetItem());
+    }
   }
 
   // Annotations on method parameters.
-  for (auto& parameter : parameters) {
-    const dex_ir::MethodId* method_id = parameter->GetMethodId();
-    const uint32_t method_idx = method_id->GetOffset();
-    const char* method_name = method_id->Name()->Data();
-    fprintf(out_file_, "Annotations on method #%u '%s' parameters\n", method_idx, method_name);
-    uint32_t j = 0;
-    for (auto& annotation : parameter->GetAnnotations()) {
-      fprintf(out_file_, "#%u\n", j);
-      DumpAnnotationSetItem(annotation.get());
-      ++j;
+  if (parameters != nullptr) {
+    for (auto& parameter : *parameters) {
+      const dex_ir::MethodId* method_id = parameter->GetMethodId();
+      const uint32_t method_idx = method_id->GetOffset();
+      const char* method_name = method_id->Name()->Data();
+      fprintf(out_file_, "Annotations on method #%u '%s' parameters\n", method_idx, method_name);
+      uint32_t j = 0;
+      for (auto& annotation : *parameter->GetAnnotations()) {
+        fprintf(out_file_, "#%u\n", j);
+        DumpAnnotationSetItem(annotation.get());
+        ++j;
+      }
     }
   }
 
@@ -612,7 +627,7 @@
 /*
  * Dumps an interface that a class declares to implement.
  */
-static void DumpInterface(dex_ir::TypeId* type_item, int i) {
+static void DumpInterface(const dex_ir::TypeId* type_item, int i) {
   const char* interface_name = type_item->GetStringId()->Data();
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "    #%d              : '%s'\n", i, interface_name);
@@ -752,10 +767,10 @@
       if (index < header->MethodIdsSize()) {
         dex_ir::MethodId* method_id = header->MethodIds()[index].get();
         const char* name = method_id->Name()->Data();
-        char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str());
+        std::string type_descriptor = GetSignatureForProtoId(method_id->Proto());
         const char* back_descriptor = method_id->Class()->GetStringId()->Data();
         outSize = snprintf(buf.get(), buf_size, "%s.%s:%s // method@%0*x",
-                           back_descriptor, name, type_descriptor, width, index);
+                           back_descriptor, name, type_descriptor.c_str(), width, index);
       } else {
         outSize = snprintf(buf.get(), buf_size, "<method?> // method@%0*x", width, index);
       }
@@ -1015,13 +1030,13 @@
                           const dex_ir::CodeItem* code, uint32_t code_offset) {
   dex_ir::MethodId* method_id = header->MethodIds()[idx].get();
   const char* name = method_id->Name()->Data();
-  const char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str());
+  std::string type_descriptor = GetSignatureForProtoId(method_id->Proto());
   const char* back_descriptor = method_id->Class()->GetStringId()->Data();
 
   // Generate header.
   std::string dot(DescriptorToDotWrapper(back_descriptor));
   fprintf(out_file_, "%06x:                                        |[%06x] %s.%s:%s\n",
-          code_offset, code_offset, dot.c_str(), name, type_descriptor);
+          code_offset, code_offset, dot.c_str(), name, type_descriptor.c_str());
 
   // Iterate over all instructions.
   const uint16_t* insns = code->Insns();
@@ -1260,8 +1275,8 @@
   }
   while (it.HasNextVirtualMethod()) {
     DumpCFG(dex_file,
-                it.GetMemberIndex(),
-                it.GetMethodCodeItem());
+            it.GetMemberIndex(),
+            it.GetMethodCodeItem());
     it.Next();
   }
 }
@@ -1274,7 +1289,10 @@
  * If "*last_package" is nullptr or does not match the current class' package,
  * the value will be replaced with a newly-allocated string.
  */
-static void DumpClass(dex_ir::Header* header, int idx, char** last_package) {
+static void DumpClass(const DexFile* dex_file,
+                      dex_ir::Header* header,
+                      int idx,
+                      char** last_package) {
   dex_ir::ClassDef* class_def = header->ClassDefs()[idx].get();
   // Omitting non-public class.
   if (options_.exports_only_ && (class_def->GetAccessFlags() & kAccPublic) == 0) {
@@ -1290,7 +1308,7 @@
   }
 
   if (options_.show_cfg_) {
-    DumpCFG(&header->GetDexFile(), idx);
+    DumpCFG(dex_file, idx);
     return;
   }
 
@@ -1368,10 +1386,12 @@
   }
 
   // Interfaces.
-  std::vector<dex_ir::TypeId*>* interfaces = class_def->Interfaces();
-  for (uint32_t i = 0; i < interfaces->size(); i++) {
-    DumpInterface((*interfaces)[i], i);
-  }  // for
+  dex_ir::TypeIdVector* interfaces = class_def->Interfaces();
+  if (interfaces != nullptr) {
+    for (uint32_t i = 0; i < interfaces->size(); i++) {
+      DumpInterface((*interfaces)[i], i);
+    }  // for
+  }
 
   // Fields and methods.
   dex_ir::ClassData* class_data = class_def->GetClassData();
@@ -1383,52 +1403,68 @@
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "  Static fields     -\n");
   }
-  std::vector<std::unique_ptr<dex_ir::FieldItem>>& static_fields = class_data->StaticFields();
-  for (uint32_t i = 0; i < static_fields.size(); i++) {
-    DumpSField(header,
-               static_fields[i]->GetFieldId()->GetOffset(),
-               static_fields[i]->GetAccessFlags(),
-               i,
-               i < static_values_size ? (*static_values)[i].get() : nullptr);
-  }  // for
+  if (class_data != nullptr) {
+    dex_ir::FieldItemVector* static_fields = class_data->StaticFields();
+    if (static_fields != nullptr) {
+      for (uint32_t i = 0; i < static_fields->size(); i++) {
+        DumpSField(header,
+                   (*static_fields)[i]->GetFieldId()->GetOffset(),
+                   (*static_fields)[i]->GetAccessFlags(),
+                   i,
+                   i < static_values_size ? (*static_values)[i].get() : nullptr);
+      }  // for
+    }
+  }
 
   // Instance fields.
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "  Instance fields   -\n");
   }
-  std::vector<std::unique_ptr<dex_ir::FieldItem>>& instance_fields = class_data->InstanceFields();
-  for (uint32_t i = 0; i < instance_fields.size(); i++) {
-    DumpIField(header,
-               instance_fields[i]->GetFieldId()->GetOffset(),
-               instance_fields[i]->GetAccessFlags(),
-               i);
-  }  // for
+  if (class_data != nullptr) {
+    dex_ir::FieldItemVector* instance_fields = class_data->InstanceFields();
+    if (instance_fields != nullptr) {
+      for (uint32_t i = 0; i < instance_fields->size(); i++) {
+        DumpIField(header,
+                   (*instance_fields)[i]->GetFieldId()->GetOffset(),
+                   (*instance_fields)[i]->GetAccessFlags(),
+                   i);
+      }  // for
+    }
+  }
 
   // Direct methods.
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "  Direct methods    -\n");
   }
-  std::vector<std::unique_ptr<dex_ir::MethodItem>>& direct_methods = class_data->DirectMethods();
-  for (uint32_t i = 0; i < direct_methods.size(); i++) {
-    DumpMethod(header,
-               direct_methods[i]->GetMethodId()->GetOffset(),
-               direct_methods[i]->GetAccessFlags(),
-               direct_methods[i]->GetCodeItem(),
-               i);
-  }  // for
+  if (class_data != nullptr) {
+    dex_ir::MethodItemVector* direct_methods = class_data->DirectMethods();
+    if (direct_methods != nullptr) {
+      for (uint32_t i = 0; i < direct_methods->size(); i++) {
+        DumpMethod(header,
+                   (*direct_methods)[i]->GetMethodId()->GetOffset(),
+                   (*direct_methods)[i]->GetAccessFlags(),
+                   (*direct_methods)[i]->GetCodeItem(),
+                 i);
+      }  // for
+    }
+  }
 
   // Virtual methods.
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "  Virtual methods   -\n");
   }
-  std::vector<std::unique_ptr<dex_ir::MethodItem>>& virtual_methods = class_data->VirtualMethods();
-  for (uint32_t i = 0; i < virtual_methods.size(); i++) {
-    DumpMethod(header,
-               virtual_methods[i]->GetMethodId()->GetOffset(),
-               virtual_methods[i]->GetAccessFlags(),
-               virtual_methods[i]->GetCodeItem(),
-               i);
-  }  // for
+  if (class_data != nullptr) {
+    dex_ir::MethodItemVector* virtual_methods = class_data->VirtualMethods();
+    if (virtual_methods != nullptr) {
+      for (uint32_t i = 0; i < virtual_methods->size(); i++) {
+        DumpMethod(header,
+                   (*virtual_methods)[i]->GetMethodId()->GetOffset(),
+                   (*virtual_methods)[i]->GetAccessFlags(),
+                   (*virtual_methods)[i]->GetCodeItem(),
+                   i);
+      }  // for
+    }
+  }
 
   // End of class.
   if (options_.output_format_ == kOutputPlain) {
@@ -1454,11 +1490,11 @@
     fprintf(out_file_, "Opened '%s', DEX version '%.3s'\n",
             file_name, dex_file->GetHeader().magic_ + 4);
   }
-  dex_ir::Header header(*dex_file);
+  std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
 
   // Headers.
   if (options_.show_file_headers_) {
-    DumpFileHeader(&header);
+    DumpFileHeader(header.get());
   }
 
   // Open XML context.
@@ -1468,9 +1504,9 @@
 
   // Iterate over all classes.
   char* package = nullptr;
-  const uint32_t class_defs_size = header.ClassDefsSize();
+  const uint32_t class_defs_size = header->ClassDefsSize();
   for (uint32_t i = 0; i < class_defs_size; i++) {
-    DumpClass(&header, i, &package);
+    DumpClass(dex_file, header.get(), i, &package);
   }  // for
 
   // Free the last package allocated.
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
new file mode 100644
index 0000000..8e3c91d
--- /dev/null
+++ b/dexlist/Android.bp
@@ -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.
+
+art_cc_binary {
+    name: "dexlist",
+    host_supported: true,
+    srcs: ["dexlist.cc"],
+    cflags: ["-Wall"],
+    shared_libs: ["libart"],
+}
diff --git a/dexlist/Android.mk b/dexlist/Android.mk
deleted file mode 100755
index 6ec6c97..0000000
--- a/dexlist/Android.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-# 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.
-
-# TODO(ajcbik): Art-i-fy this makefile
-
-LOCAL_PATH:= $(call my-dir)
-
-dexlist_src_files := dexlist.cc
-dexlist_c_includes := art/runtime
-dexlist_libraries := libart
-
-##
-## Build the device command line tool dexlist.
-##
-
-ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := $(dexlist_src_files)
-LOCAL_C_INCLUDES := $(dexlist_c_includes)
-LOCAL_CFLAGS += -Wall
-LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
-LOCAL_MODULE := dexlist
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-include $(BUILD_EXECUTABLE)
-endif # !SDK_ONLY
-
-##
-## Build the host command line tool dexlist.
-##
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := $(dexlist_src_files)
-LOCAL_C_INCLUDES := $(dexlist_c_includes)
-LOCAL_CFLAGS += -Wall
-LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
-LOCAL_MODULE := dexlist
-LOCAL_MULTILIB := $(ART_MULTILIB_OVERRIDE_host)
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
new file mode 100644
index 0000000..d06e4de
--- /dev/null
+++ b/disassembler/Android.bp
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2012 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.
+//
+
+art_cc_defaults {
+    name: "libart-disassembler-defaults",
+    defaults: ["art_defaults"],
+    host_supported: true,
+    clang: true,
+    srcs: [
+        "disassembler.cc",
+        "disassembler_arm.cc",
+        "disassembler_arm64.cc",
+        "disassembler_mips.cc",
+        "disassembler_x86.cc",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libbase",
+    ],
+    export_include_dirs: ["."],
+}
+
+art_cc_library {
+    name: "libart-disassembler",
+    defaults: ["libart-disassembler-defaults"],
+    shared_libs: [
+        "libart",
+        // For disassembler_arm64.
+        "libvixld-arm64",
+    ],
+}
+
+art_cc_library {
+    name: "libartd-disassembler",
+    defaults: [
+        "libart-disassembler-defaults",
+        "art_debug_defaults",
+    ],
+    shared_libs: [
+        "libartd",
+        // For disassembler_arm64.
+        "libvixld-arm64",
+    ],
+}
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
deleted file mode 100644
index db327fc..0000000
--- a/disassembler/Android.mk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright (C) 2012 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include art/build/Android.common_build.mk
-
-LIBART_DISASSEMBLER_SRC_FILES := \
-	disassembler.cc \
-	disassembler_arm.cc \
-	disassembler_arm64.cc \
-	disassembler_mips.cc \
-	disassembler_x86.cc
-
-# $(1): target or host
-# $(2): ndebug or debug
-define build-libart-disassembler
-  ifneq ($(1),target)
-    ifneq ($(1),host)
-      $$(error expected target or host for argument 1, received $(1))
-    endif
-  endif
-  ifneq ($(2),ndebug)
-    ifneq ($(2),debug)
-      $$(error expected ndebug or debug for argument 2, received $(2))
-    endif
-  endif
-
-  art_target_or_host := $(1)
-  art_ndebug_or_debug := $(2)
-
-  include $(CLEAR_VARS)
-  ifeq ($$(art_target_or_host),host)
-     LOCAL_IS_HOST_MODULE := true
-  endif
-  LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
-  ifeq ($$(art_ndebug_or_debug),ndebug)
-    LOCAL_MODULE := libart-disassembler
-  else # debug
-    LOCAL_MODULE := libartd-disassembler
-  endif
-
-  LOCAL_MODULE_TAGS := optional
-  LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-
-  LOCAL_SRC_FILES := $$(LIBART_DISASSEMBLER_SRC_FILES)
-
-  ifeq ($$(art_target_or_host),target)
-    LOCAL_CLANG := $(ART_TARGET_CLANG)
-    $(call set-target-local-cflags-vars,$(2))
-  else # host
-    LOCAL_CLANG := $(ART_HOST_CLANG)
-    LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
-    LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
-    ifeq ($$(art_ndebug_or_debug),debug)
-      LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
-      LOCAL_ASFLAGS += $(ART_HOST_DEBUG_ASFLAGS)
-    else
-      LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
-      LOCAL_ASFLAGS += $(ART_HOST_NON_DEBUG_ASFLAGS)
-    endif
-  endif
-
-  LOCAL_SHARED_LIBRARIES += liblog
-  ifeq ($$(art_ndebug_or_debug),debug)
-    LOCAL_SHARED_LIBRARIES += libartd
-  else
-    LOCAL_SHARED_LIBRARIES += libart
-  endif
-
-  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
-  LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-  LOCAL_MULTILIB := both
-
-  LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
-  LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
-  # For disassembler_arm64.
-  ifeq ($$(art_ndebug_or_debug),debug)
-    LOCAL_SHARED_LIBRARIES += libvixld-arm64
-  else
-    LOCAL_SHARED_LIBRARIES += libvixl-arm64
-  endif
-  ifeq ($$(art_target_or_host),target)
-    include $(BUILD_SHARED_LIBRARY)
-  else # host
-    include $(BUILD_HOST_SHARED_LIBRARY)
-  endif
-endef
-
-ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-libart-disassembler,target,ndebug))
-endif
-ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-libart-disassembler,target,debug))
-endif
-# 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-libart-disassembler,host,ndebug))
-endif
-ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-libart-disassembler,host,debug))
-endif
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index bcd0d16..8eecc62 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -18,15 +18,23 @@
 
 #include <ostream>
 
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
 #include "disassembler_arm.h"
 #include "disassembler_arm64.h"
 #include "disassembler_mips.h"
 #include "disassembler_x86.h"
 
+using android::base::StringPrintf;
+
 namespace art {
 
+Disassembler::Disassembler(DisassemblerOptions* disassembler_options)
+    : disassembler_options_(disassembler_options) {
+  CHECK(disassembler_options_ != nullptr);
+}
+
 Disassembler* Disassembler::Create(InstructionSet instruction_set, DisassemblerOptions* options) {
   if (instruction_set == kArm || instruction_set == kThumb2) {
     return new arm::DisassemblerArm(options);
@@ -39,7 +47,7 @@
   } else if (instruction_set == kX86_64) {
     return new x86::DisassemblerX86(options, true);
   } else {
-    UNIMPLEMENTED(FATAL) << "no disassembler for " << instruction_set;
+    UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set);
     return nullptr;
   }
 }
diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h
index 86793cc..1ef456c 100644
--- a/disassembler/disassembler.h
+++ b/disassembler/disassembler.h
@@ -21,8 +21,9 @@
 
 #include <iosfwd>
 
+#include "android-base/macros.h"
+
 #include "arch/instruction_set.h"
-#include "base/macros.h"
 
 namespace art {
 
@@ -81,10 +82,7 @@
   }
 
  protected:
-  explicit Disassembler(DisassemblerOptions* disassembler_options)
-      : disassembler_options_(disassembler_options) {
-    CHECK(disassembler_options_ != nullptr);
-  }
+  explicit Disassembler(DisassemblerOptions* disassembler_options);
 
   std::string FormatInstructionPointer(const uint8_t* begin);
 
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index a47b6ad..c3e288d 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -21,10 +21,13 @@
 #include <ostream>
 #include <sstream>
 
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
 #include "arch/arm/registers_arm.h"
 #include "base/bit_utils.h"
-#include "base/logging.h"
-#include "base/stringprintf.h"
+
+using android::base::StringPrintf;
 
 namespace art {
 namespace arm {
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index 80bacb2..49b9623 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -20,8 +20,10 @@
 
 #include <sstream>
 
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
 
 using namespace vixl::aarch64;  // NOLINT(build/namespaces)
 
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 02c6d71..9a73f29 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -19,8 +19,10 @@
 #include <ostream>
 #include <sstream>
 
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
 
 namespace art {
 namespace mips {
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 2ca84e5..9f49ec6 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -21,8 +21,10 @@
 #include <ostream>
 #include <sstream>
 
-#include "base/logging.h"
-#include "base/stringprintf.h"
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
 
 namespace art {
 namespace x86 {
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
new file mode 100644
index 0000000..b01bf51
--- /dev/null
+++ b/oatdump/Android.bp
@@ -0,0 +1,89 @@
+//
+// Copyright (C) 2011 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.
+//
+
+cc_defaults {
+    name: "oatdump-defaults",
+    defaults: ["art_defaults"],
+    host_supported: true,
+    srcs: ["oatdump.cc"],
+    target: {
+        android: {
+            shared_libs: ["libcutils"],
+        },
+    },
+    include_dirs: ["art/cmdline"],
+}
+
+art_cc_binary {
+    name: "oatdump",
+    defaults: ["oatdump-defaults"],
+    shared_libs: [
+        "libart",
+        "libart-compiler",
+        "libart-disassembler",
+    ],
+}
+
+art_cc_binary {
+    name: "oatdumpd",
+    defaults: [
+        "art_debug_defaults",
+        "oatdump-defaults",
+    ],
+    shared_libs: [
+        "libartd",
+        "libartd-compiler",
+        "libartd-disassembler",
+    ],
+}
+
+art_cc_binary {
+    name: "oatdumps",
+    defaults: ["oatdump-defaults"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    static_libs: [
+        "libart",
+        "libart-compiler",
+        "libart-disassembler",
+        "libvixl-arm",
+        "libvixl-arm64",
+    ] + art_static_dependencies,
+}
+
+art_cc_binary {
+    name: "oatdumpds",
+    defaults: [
+        "art_debug_defaults",
+        "oatdump-defaults",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    static_libs: [
+        "libartd",
+        "libartd-compiler",
+        "libartd-disassembler",
+        "libvixld-arm",
+        "libvixld-arm64",
+    ] + art_static_dependencies,
+}
+
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index 5c75f20..d80df70 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -16,14 +16,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-include art/build/Android.executable.mk
-
-OATDUMP_SRC_FILES := \
-	oatdump.cc
-
-# Build variants {target,host} x {debug,ndebug}
-$(eval $(call build-art-multi-executable,oatdump,$(OATDUMP_SRC_FILES),libart-compiler libart-disassembler,libcutils,,art/compiler art/disassembler))
-
 ########################################################################
 # oatdump targets
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index c87a18b..db6a709 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -719,10 +719,12 @@
     os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
     os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
 
-    // Print embedded dex file data range.
     const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin();
+    const uint8_t* const vdex_file_begin = oat_dex_file.GetOatFile()->DexBegin();
+
+    // Print data range of the dex file embedded inside the corresponding vdex file.
     const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
-    uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - oat_file_begin);
+    uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - vdex_file_begin);
     os << StringPrintf("dex-file: 0x%08x..0x%08x\n",
                        dex_offset,
                        dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index db97055..22db818 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -42,13 +42,22 @@
     core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA);
   }
 
+  // Linking flavor.
+  enum Flavor {
+    kDynamic,  // oatdump(d)
+    kStatic,   // oatdump(d)s
+  };
+
   // Returns path to the oatdump binary.
-  std::string GetOatDumpFilePath() {
+  std::string GetOatDumpFilePath(Flavor flavor) {
     std::string root = GetTestAndroidRoot();
     root += "/bin/oatdump";
     if (kIsDebugBuild) {
       root += "d";
     }
+    if (flavor == kStatic) {
+      root += "s";
+    }
     return root;
   }
 
@@ -58,12 +67,19 @@
     kModeSymbolize,
   };
 
+  // Display style.
+  enum Display {
+    kListOnly,
+    kListAndCode
+  };
+
   // Run the test with custom arguments.
-  bool Exec(Mode mode,
+  bool Exec(Flavor flavor,
+            Mode mode,
             const std::vector<std::string>& args,
-            bool list_only,
+            Display display,
             std::string* error_msg) {
-    std::string file_path = GetOatDumpFilePath();
+    std::string file_path = GetOatDumpFilePath(flavor);
 
     EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
 
@@ -81,7 +97,7 @@
       expected_prefixes.push_back("LOCATION:");
       expected_prefixes.push_back("MAGIC:");
       expected_prefixes.push_back("DEX FILE COUNT:");
-      if (!list_only) {
+      if (display == kListAndCode) {
         // Code and dex code do not show up if list only.
         expected_prefixes.push_back("DEX CODE:");
         expected_prefixes.push_back("CODE:");
@@ -205,37 +221,73 @@
 #if !defined(__arm__) && !defined(__mips__)
 TEST_F(OatDumpTest, TestImage) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestImageStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestOatImage) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeOat, {}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestOatImageStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestNoDumpVmap) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--no-dump:vmap"}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestNoDumpVmapStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestNoDisassemble) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--no-disassemble"}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg))
+      << error_msg;
+}
+TEST_F(OatDumpTest, TestNoDisassembleStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestListClasses) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--list-classes"}, /*list_only*/ true, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestListClassesStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestListMethods) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--list-methods"}, /*list_only*/ true, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestListMethodsStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestSymbolize) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeSymbolize, {}, /*list_only*/ true, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestSymbolizeStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
 }
 #endif
 }  // namespace art
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index b7ce02c..5240011 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -31,6 +31,7 @@
 #include "base/stringpiece.h"
 #include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
+#include "base/unix_file/random_access_file_utils.h"
 #include "elf_utils.h"
 #include "elf_file.h"
 #include "elf_file_impl.h"
@@ -151,6 +152,28 @@
   }
 }
 
+static bool SymlinkFile(const std::string& input_filename, const std::string& output_filename) {
+  if (input_filename == output_filename) {
+    // Input and output are the same, nothing to do.
+    return true;
+  }
+
+  // Unlink the original filename, since we are overwriting it.
+  unlink(output_filename.c_str());
+
+  // Create a symlink from the source file to the target path.
+  if (symlink(input_filename.c_str(), output_filename.c_str()) < 0) {
+    PLOG(ERROR) << "Failed to create symlink " << output_filename << " -> " << input_filename;
+    return false;
+  }
+
+  if (kIsDebugBuild) {
+    LOG(INFO) << "Created symlink " << output_filename << " -> " << input_filename;
+  }
+
+  return true;
+}
+
 bool PatchOat::Patch(const std::string& image_location,
                      off_t delta,
                      const std::string& output_directory,
@@ -230,9 +253,13 @@
     space_to_memmap_map.emplace(space, std::move(image));
   }
 
+  // Do a first pass over the image spaces. Symlink PIC oat and vdex files, and
+  // prepare PatchOat instances for the rest.
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
     std::string input_image_filename = space->GetImageFilename();
+    std::string input_vdex_filename =
+        ImageHeader::GetVdexLocationFromImageLocation(input_image_filename);
     std::string input_oat_filename =
         ImageHeader::GetOatLocationFromImageLocation(input_image_filename);
     std::unique_ptr<File> input_oat_file(OS::OpenFileForReading(input_oat_filename.c_str()));
@@ -261,13 +288,16 @@
       std::string output_image_filename = output_directory +
                                           (StartsWith(converted_image_filename, "/") ? "" : "/") +
                                           converted_image_filename;
+      std::string output_vdex_filename =
+          ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
       std::string output_oat_filename =
           ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
 
       if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(),
                                      output_oat_filename,
                                      false,
-                                     true)) {
+                                     true) ||
+          !SymlinkFile(input_vdex_filename, output_vdex_filename)) {
         // Errors already logged by above call.
         return false;
       }
@@ -301,9 +331,13 @@
     space_to_skip_patching_map.emplace(space, skip_patching_oat);
   }
 
+  // Do a second pass over the image spaces. Patch image files, non-PIC oat files
+  // and symlink their corresponding vdex files.
   for (size_t i = 0; i < spaces.size(); ++i) {
     gc::space::ImageSpace* space = spaces[i];
     std::string input_image_filename = space->GetImageFilename();
+    std::string input_vdex_filename =
+        ImageHeader::GetVdexLocationFromImageLocation(input_image_filename);
 
     t.NewTiming("Writing files");
     std::string converted_image_filename = space->GetImageLocation();
@@ -329,8 +363,11 @@
 
     bool skip_patching_oat = space_to_skip_patching_map.find(space)->second;
     if (!skip_patching_oat) {
+      std::string output_vdex_filename =
+          ImageHeader::GetVdexLocationFromImageLocation(output_image_filename);
       std::string output_oat_filename =
           ImageHeader::GetOatLocationFromImageLocation(output_image_filename);
+
       std::unique_ptr<File>
           output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
       if (output_oat_file.get() == nullptr) {
@@ -339,6 +376,9 @@
       }
       success = p.WriteElf(output_oat_file.get());
       success = FinishFile(output_oat_file.get(), success);
+      if (success) {
+        success = SymlinkFile(input_vdex_filename, output_vdex_filename);
+      }
       if (!success) {
         return false;
       }
@@ -921,6 +961,9 @@
   UsageError("  --input-oat-fd=<file-descriptor>: Specifies the file-descriptor of the oat file");
   UsageError("      to be patched.");
   UsageError("");
+  UsageError("  --input-vdex-fd=<file-descriptor>: Specifies the file-descriptor of the vdex file");
+  UsageError("      associated with the oat file.");
+  UsageError("");
   UsageError("  --input-oat-location=<file.oat>: Specifies the 'location' to read the patched");
   UsageError("      oat file from. If used one must also supply the --instruction-set");
   UsageError("");
@@ -932,7 +975,10 @@
   UsageError("      file to.");
   UsageError("");
   UsageError("  --output-oat-fd=<file-descriptor>: Specifies the file-descriptor to write the");
-  UsageError("      the patched oat file to.");
+  UsageError("      patched oat file to.");
+  UsageError("");
+  UsageError("  --output-vdex-fd=<file-descriptor>: Specifies the file-descriptor to copy the");
+  UsageError("      the vdex file associated with the patch oat file to.");
   UsageError("");
   UsageError("  --output-image-file=<file.art>: Specifies the exact file to write the patched");
   UsageError("      image file to.");
@@ -1029,10 +1075,12 @@
                         off_t base_delta,
                         bool base_delta_set,
                         int input_oat_fd,
+                        int input_vdex_fd,
                         const std::string& input_oat_location,
                         std::string input_oat_filename,
                         bool have_input_oat,
                         int output_oat_fd,
+                        int output_vdex_fd,
                         std::string output_oat_filename,
                         bool have_output_oat,
                         bool lock_output,
@@ -1062,6 +1110,12 @@
     }
   }
 
+  if ((input_oat_fd == -1) != (input_vdex_fd == -1)) {
+    Usage("Either both input oat and vdex have to be passed as file descriptors or none of them");
+  } else if ((output_oat_fd == -1) != (output_vdex_fd == -1)) {
+    Usage("Either both output oat and vdex have to be passed as file descriptors or none of them");
+  }
+
   bool match_delta = false;
   if (!patched_image_location.empty()) {
     std::string system_filename;
@@ -1102,8 +1156,24 @@
     Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
   }
 
+  // We can symlink VDEX only if we have both input and output specified as filenames.
+  // Store that piece of information before we possibly create bogus filenames for
+  // files passed as file descriptors.
+  bool symlink_vdex = !input_oat_filename.empty() && !output_oat_filename.empty();
+
+  // Infer names of VDEX files.
+  std::string input_vdex_filename;
+  std::string output_vdex_filename;
+  if (!input_oat_filename.empty()) {
+    input_vdex_filename = ReplaceFileExtension(input_oat_filename, "vdex");
+  }
+  if (!output_oat_filename.empty()) {
+    output_vdex_filename = ReplaceFileExtension(output_oat_filename, "vdex");
+  }
+
   // Do we need to cleanup output files if we fail?
   bool new_oat_out = false;
+  bool new_vdex_out = false;
 
   std::unique_ptr<File> input_oat;
   std::unique_ptr<File> output_oat;
@@ -1162,13 +1232,52 @@
     }
   }
 
+  // Open VDEX files if we are not symlinking them.
+  std::unique_ptr<File> input_vdex;
+  std::unique_ptr<File> output_vdex;
+  if (symlink_vdex) {
+    new_vdex_out = !OS::FileExists(output_vdex_filename.c_str());
+  } else {
+    if (input_vdex_fd != -1) {
+      input_vdex.reset(new File(input_vdex_fd, input_vdex_filename, true));
+      if (input_vdex == nullptr) {
+        // Unlikely, but ensure exhaustive logging in non-0 exit code case
+        LOG(ERROR) << "Failed to open input vdex file by its FD" << input_vdex_fd;
+      }
+    } else {
+      input_vdex.reset(OS::OpenFileForReading(input_vdex_filename.c_str()));
+      if (input_vdex == nullptr) {
+        PLOG(ERROR) << "Failed to open input vdex file " << input_vdex_filename;
+        return EXIT_FAILURE;
+      }
+    }
+    if (output_vdex_fd != -1) {
+      output_vdex.reset(new File(output_vdex_fd, output_vdex_filename, true));
+      if (output_vdex == nullptr) {
+        // Unlikely, but ensure exhaustive logging in non-0 exit code case
+        LOG(ERROR) << "Failed to open output vdex file by its FD" << output_vdex_fd;
+      }
+    } else {
+      output_vdex.reset(CreateOrOpen(output_vdex_filename.c_str(), &new_vdex_out));
+      if (output_vdex == nullptr) {
+        PLOG(ERROR) << "Failed to open output vdex file " << output_vdex_filename;
+        return EXIT_FAILURE;
+      }
+    }
+  }
+
   // TODO: get rid of this.
-  auto cleanup = [&output_oat_filename, &new_oat_out](bool success) {
+  auto cleanup = [&output_oat_filename, &output_vdex_filename, &new_oat_out, &new_vdex_out]
+                 (bool success) {
     if (!success) {
       if (new_oat_out) {
         CHECK(!output_oat_filename.empty());
         unlink(output_oat_filename.c_str());
       }
+      if (new_vdex_out) {
+        CHECK(!output_vdex_filename.empty());
+        unlink(output_vdex_filename.c_str());
+      }
     }
 
     if (kIsDebugBuild) {
@@ -1220,6 +1329,14 @@
                              new_oat_out);
   ret = FinishFile(output_oat.get(), ret);
 
+  if (ret) {
+    if (symlink_vdex) {
+      ret = SymlinkFile(input_vdex_filename, output_vdex_filename);
+    } else {
+      ret = unix_file::CopyFile(*input_vdex.get(), output_vdex.get());
+    }
+  }
+
   if (kIsDebugBuild) {
     LOG(INFO) << "Exiting with return ... " << ret;
   }
@@ -1227,6 +1344,18 @@
   return ret ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
+static int ParseFd(const StringPiece& option, const char* cmdline_arg) {
+  int fd;
+  const char* fd_str = option.substr(strlen(cmdline_arg)).data();
+  if (!ParseInt(fd_str, &fd)) {
+    Usage("Failed to parse %d argument '%s' as an integer", cmdline_arg, fd_str);
+  }
+  if (fd < 0) {
+    Usage("%s pass a negative value %d", cmdline_arg, fd);
+  }
+  return fd;
+}
+
 static int patchoat(int argc, char **argv) {
   InitLogging(argv);
   MemMap::Init();
@@ -1253,10 +1382,12 @@
   std::string input_oat_filename;
   std::string input_oat_location;
   int input_oat_fd = -1;
+  int input_vdex_fd = -1;
   bool have_input_oat = false;
   std::string input_image_location;
   std::string output_oat_filename;
   int output_oat_fd = -1;
+  int output_vdex_fd = -1;
   bool have_output_oat = false;
   std::string output_image_filename;
   off_t base_delta = 0;
@@ -1296,13 +1427,9 @@
         Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used.");
       }
       have_input_oat = true;
-      const char* oat_fd_str = option.substr(strlen("--input-oat-fd=")).data();
-      if (!ParseInt(oat_fd_str, &input_oat_fd)) {
-        Usage("Failed to parse --input-oat-fd argument '%s' as an integer", oat_fd_str);
-      }
-      if (input_oat_fd < 0) {
-        Usage("--input-oat-fd pass a negative value %d", input_oat_fd);
-      }
+      input_oat_fd = ParseFd(option, "--input-oat-fd=");
+    } else if (option.starts_with("--input-vdex-fd=")) {
+      input_vdex_fd = ParseFd(option, "--input-vdex-fd=");
     } else if (option.starts_with("--input-image-location=")) {
       input_image_location = option.substr(strlen("--input-image-location=")).data();
     } else if (option.starts_with("--output-oat-file=")) {
@@ -1316,13 +1443,9 @@
         Usage("Only one of --output-oat-file, --output-oat-fd may be used.");
       }
       have_output_oat = true;
-      const char* oat_fd_str = option.substr(strlen("--output-oat-fd=")).data();
-      if (!ParseInt(oat_fd_str, &output_oat_fd)) {
-        Usage("Failed to parse --output-oat-fd argument '%s' as an integer", oat_fd_str);
-      }
-      if (output_oat_fd < 0) {
-        Usage("--output-oat-fd pass a negative value %d", output_oat_fd);
-      }
+      output_oat_fd = ParseFd(option, "--output-oat-fd=");
+    } else if (option.starts_with("--output-vdex-fd=")) {
+      output_vdex_fd = ParseFd(option, "--output-vdex-fd=");
     } else if (option.starts_with("--output-image-file=")) {
       output_image_filename = option.substr(strlen("--output-image-file=")).data();
     } else if (option.starts_with("--base-offset-delta=")) {
@@ -1367,10 +1490,12 @@
                        base_delta,
                        base_delta_set,
                        input_oat_fd,
+                       input_vdex_fd,
                        input_oat_location,
                        input_oat_filename,
                        have_input_oat,
                        output_oat_fd,
+                       output_vdex_fd,
                        output_oat_filename,
                        have_output_oat,
                        lock_output,
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 22d79cb..a884505 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -97,7 +97,6 @@
         "intern_table.cc",
         "interpreter/interpreter.cc",
         "interpreter/interpreter_common.cc",
-        "interpreter/interpreter_goto_table_impl.cc",
         "interpreter/interpreter_switch_impl.cc",
         "interpreter/unstarted_runtime.cc",
         "java_vm_ext.cc",
@@ -191,6 +190,7 @@
         "type_lookup_table.cc",
         "utf.cc",
         "utils.cc",
+        "vdex_file.cc",
         "verifier/instruction_flags.cc",
         "verifier/method_verifier.cc",
         "verifier/reg_type.cc",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index c51c336..a3f053b 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1544,7 +1544,7 @@
      */
     .extern artDeoptimizeFromCompiledCode
 ENTRY art_quick_deoptimize_from_compiled_code
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0
+    SETUP_SAVE_EVERYTHING_FRAME r0
     mov    r0, r9                         @ Set up args.
     blx    artDeoptimizeFromCompiledCode  @ artDeoptimizeFromCompiledCode(Thread*)
 END art_quick_deoptimize_from_compiled_code
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 03768af..25aa8ce 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -19,6 +19,52 @@
 #include "arch/quick_alloc_entrypoints.S"
 
 
+.macro INCREASE_FRAME frame_adjustment
+    sub sp, sp, #(\frame_adjustment)
+    .cfi_adjust_cfa_offset (\frame_adjustment)
+.endm
+
+.macro DECREASE_FRAME frame_adjustment
+    add sp, sp, #(\frame_adjustment)
+    .cfi_adjust_cfa_offset -(\frame_adjustment)
+.endm
+
+.macro SAVE_REG reg, offset
+    str \reg, [sp, #(\offset)]
+    .cfi_rel_offset \reg, (\offset)
+.endm
+
+.macro RESTORE_REG reg, offset
+    ldr \reg, [sp, #(\offset)]
+    .cfi_restore \reg
+.endm
+
+.macro SAVE_TWO_REGS reg1, reg2, offset
+    stp \reg1, \reg2, [sp, #(\offset)]
+    .cfi_rel_offset \reg1, (\offset)
+    .cfi_rel_offset \reg2, (\offset) + 8
+.endm
+
+.macro RESTORE_TWO_REGS reg1, reg2, offset
+    ldp \reg1, \reg2, [sp, #(\offset)]
+    .cfi_restore \reg1
+    .cfi_restore \reg2
+.endm
+
+.macro SAVE_TWO_REGS_INCREASE_FRAME reg1, reg2, frame_adjustment
+    stp \reg1, \reg2, [sp, #-(\frame_adjustment)]!
+    .cfi_adjust_cfa_offset (\frame_adjustment)
+    .cfi_rel_offset \reg1, 0
+    .cfi_rel_offset \reg2, 8
+.endm
+
+.macro RESTORE_TWO_REGS_DECREASE_FRAME reg1, reg2, frame_adjustment
+    ldp \reg1, \reg2, [sp], #(\frame_adjustment)
+    .cfi_restore \reg1
+    .cfi_restore \reg2
+    .cfi_adjust_cfa_offset -(\frame_adjustment)
+.endm
+
     /*
      * Macro that sets up the callee save frame to conform with
      * Runtime::CreateCalleeSaveMethod(kSaveAllCalleeSaves)
@@ -34,8 +80,7 @@
     // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveAllCalleeSaves];
     ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET]
 
-    sub sp, sp, #176
-    .cfi_adjust_cfa_offset 176
+    INCREASE_FRAME 176
 
     // Ugly compile-time check, but we only have the preprocessor.
 #if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVES != 176)
@@ -50,29 +95,12 @@
     stp d14, d15, [sp, #64]
 
     // GP callee-saves
-    stp x19, x20, [sp, #80]
-    .cfi_rel_offset x19, 80
-    .cfi_rel_offset x20, 88
-
-    stp x21, x22, [sp, #96]
-    .cfi_rel_offset x21, 96
-    .cfi_rel_offset x22, 104
-
-    stp x23, x24, [sp, #112]
-    .cfi_rel_offset x23, 112
-    .cfi_rel_offset x24, 120
-
-    stp x25, x26, [sp, #128]
-    .cfi_rel_offset x25, 128
-    .cfi_rel_offset x26, 136
-
-    stp x27, x28, [sp, #144]
-    .cfi_rel_offset x27, 144
-    .cfi_rel_offset x28, 152
-
-    stp x29, xLR, [sp, #160]
-    .cfi_rel_offset x29, 160
-    .cfi_rel_offset x30, 168
+    SAVE_TWO_REGS x19, x20, 80
+    SAVE_TWO_REGS x21, x22, 96
+    SAVE_TWO_REGS x23, x24, 112
+    SAVE_TWO_REGS x25, x26, 128
+    SAVE_TWO_REGS x27, x28, 144
+    SAVE_TWO_REGS x29, xLR, 160
 
     // Store ArtMethod* Runtime::callee_save_methods_[kSaveAllCalleeSaves].
     str xIP0, [sp]
@@ -96,8 +124,7 @@
     // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveRefOnly];
     ldr xIP0, [xIP0, RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET]
 
-    sub sp, sp, #96
-    .cfi_adjust_cfa_offset 96
+    INCREASE_FRAME 96
 
     // Ugly compile-time check, but we only have the preprocessor.
 #if (FRAME_SIZE_SAVE_REFS_ONLY != 96)
@@ -106,25 +133,11 @@
 
     // GP callee-saves.
     // x20 paired with ArtMethod* - see below.
-    stp x21, x22, [sp, #16]
-    .cfi_rel_offset x21, 16
-    .cfi_rel_offset x22, 24
-
-    stp x23, x24, [sp, #32]
-    .cfi_rel_offset x23, 32
-    .cfi_rel_offset x24, 40
-
-    stp x25, x26, [sp, #48]
-    .cfi_rel_offset x25, 48
-    .cfi_rel_offset x26, 56
-
-    stp x27, x28, [sp, #64]
-    .cfi_rel_offset x27, 64
-    .cfi_rel_offset x28, 72
-
-    stp x29, xLR, [sp, #80]
-    .cfi_rel_offset x29, 80
-    .cfi_rel_offset x30, 88
+    SAVE_TWO_REGS x21, x22, 16
+    SAVE_TWO_REGS x23, x24, 32
+    SAVE_TWO_REGS x25, x26, 48
+    SAVE_TWO_REGS x27, x28, 64
+    SAVE_TWO_REGS x29, xLR, 80
 
     // Store ArtMethod* Runtime::callee_save_methods_[kSaveRefsOnly].
     stp xIP0, x20, [sp]
@@ -138,36 +151,18 @@
 // TODO: Probably no need to restore registers preserved by aapcs64.
 .macro RESTORE_SAVE_REFS_ONLY_FRAME
     // Callee-saves.
-    ldr x20, [sp, #8]
-    .cfi_restore x20
+    RESTORE_REG x20, 8
+    RESTORE_TWO_REGS x21, x22, 16
+    RESTORE_TWO_REGS x23, x24, 32
+    RESTORE_TWO_REGS x25, x26, 48
+    RESTORE_TWO_REGS x27, x28, 64
+    RESTORE_TWO_REGS x29, xLR, 80
 
-    ldp x21, x22, [sp, #16]
-    .cfi_restore x21
-    .cfi_restore x22
-
-    ldp x23, x24, [sp, #32]
-    .cfi_restore x23
-    .cfi_restore x24
-
-    ldp x25, x26, [sp, #48]
-    .cfi_restore x25
-    .cfi_restore x26
-
-    ldp x27, x28, [sp, #64]
-    .cfi_restore x27
-    .cfi_restore x28
-
-    ldp x29, xLR, [sp, #80]
-    .cfi_restore x29
-    .cfi_restore x30
-
-    add sp, sp, #96
-    .cfi_adjust_cfa_offset -96
+    DECREASE_FRAME 96
 .endm
 
 .macro POP_SAVE_REFS_ONLY_FRAME
-    add sp, sp, #96
-    .cfi_adjust_cfa_offset - 96
+    DECREASE_FRAME 96
 .endm
 
 .macro RESTORE_SAVE_REFS_ONLY_FRAME_AND_RETURN
@@ -177,8 +172,7 @@
 
 
 .macro SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL
-    sub sp, sp, #224
-    .cfi_adjust_cfa_offset 224
+    INCREASE_FRAME 224
 
     // Ugly compile-time check, but we only have the preprocessor.
 #if (FRAME_SIZE_SAVE_REFS_AND_ARGS != 224)
@@ -193,43 +187,19 @@
     stp d6, d7, [sp, #64]
 
     // Core args.
-    stp x1, x2, [sp, #80]
-    .cfi_rel_offset x1, 80
-    .cfi_rel_offset x2, 88
-
-    stp x3, x4, [sp, #96]
-    .cfi_rel_offset x3, 96
-    .cfi_rel_offset x4, 104
-
-    stp x5, x6, [sp, #112]
-    .cfi_rel_offset x5, 112
-    .cfi_rel_offset x6, 120
+    SAVE_TWO_REGS x1, x2, 80
+    SAVE_TWO_REGS x3, x4, 96
+    SAVE_TWO_REGS x5, x6, 112
 
     // x7, Callee-saves.
-    stp x7, x20, [sp, #128]
-    .cfi_rel_offset x7, 128
-    .cfi_rel_offset x20, 136
-
-    stp x21, x22, [sp, #144]
-    .cfi_rel_offset x21, 144
-    .cfi_rel_offset x22, 152
-
-    stp x23, x24, [sp, #160]
-    .cfi_rel_offset x23, 160
-    .cfi_rel_offset x24, 168
-
-    stp x25, x26, [sp, #176]
-    .cfi_rel_offset x25, 176
-    .cfi_rel_offset x26, 184
-
-    stp x27, x28, [sp, #192]
-    .cfi_rel_offset x27, 192
-    .cfi_rel_offset x28, 200
+    SAVE_TWO_REGS x7, x20, 128
+    SAVE_TWO_REGS x21, x22, 144
+    SAVE_TWO_REGS x23, x24, 160
+    SAVE_TWO_REGS x25, x26, 176
+    SAVE_TWO_REGS x27, x28, 192
 
     // x29(callee-save) and LR.
-    stp x29, xLR, [sp, #208]
-    .cfi_rel_offset x29, 208
-    .cfi_rel_offset x30, 216
+    SAVE_TWO_REGS x29, xLR, 208
 
 .endm
 
@@ -275,46 +245,21 @@
     ldp d6, d7, [sp, #64]
 
     // Core args.
-    ldp x1, x2, [sp, #80]
-    .cfi_restore x1
-    .cfi_restore x2
-
-    ldp x3, x4, [sp, #96]
-    .cfi_restore x3
-    .cfi_restore x4
-
-    ldp x5, x6, [sp, #112]
-    .cfi_restore x5
-    .cfi_restore x6
+    RESTORE_TWO_REGS x1, x2, 80
+    RESTORE_TWO_REGS x3, x4, 96
+    RESTORE_TWO_REGS x5, x6, 112
 
     // x7, Callee-saves.
-    ldp x7, x20, [sp, #128]
-    .cfi_restore x7
-    .cfi_restore x20
-
-    ldp x21, x22, [sp, #144]
-    .cfi_restore x21
-    .cfi_restore x22
-
-    ldp x23, x24, [sp, #160]
-    .cfi_restore x23
-    .cfi_restore x24
-
-    ldp x25, x26, [sp, #176]
-    .cfi_restore x25
-    .cfi_restore x26
-
-    ldp x27, x28, [sp, #192]
-    .cfi_restore x27
-    .cfi_restore x28
+    RESTORE_TWO_REGS x7, x20, 128
+    RESTORE_TWO_REGS x21, x22, 144
+    RESTORE_TWO_REGS x23, x24, 160
+    RESTORE_TWO_REGS x25, x26, 176
+    RESTORE_TWO_REGS x27, x28, 192
 
     // x29(callee-save) and LR.
-    ldp x29, xLR, [sp, #208]
-    .cfi_restore x29
-    .cfi_restore x30
+    RESTORE_TWO_REGS x29, xLR, 208
 
-    add sp, sp, #224
-    .cfi_adjust_cfa_offset -224
+    DECREASE_FRAME 224
 .endm
 
     /*
@@ -322,8 +267,7 @@
      * Runtime::CreateCalleeSaveMethod(kSaveEverything)
      */
 .macro SETUP_SAVE_EVERYTHING_FRAME
-    sub sp, sp, #512
-    .cfi_adjust_cfa_offset 512
+    INCREASE_FRAME 512
 
     // Ugly compile-time check, but we only have the preprocessor.
 #if (FRAME_SIZE_SAVE_EVERYTHING != 512)
@@ -351,68 +295,22 @@
     str d31,      [sp, #256]
 
     // Save core registers.
-    str x0,       [sp, #264]
-    .cfi_rel_offset x0, 264
-
-    stp x1, x2,   [sp, #272]
-    .cfi_rel_offset x1, 272
-    .cfi_rel_offset x2, 280
-
-    stp x3, x4,   [sp, #288]
-    .cfi_rel_offset x3, 288
-    .cfi_rel_offset x4, 296
-
-    stp x5, x6,   [sp, #304]
-    .cfi_rel_offset x5, 304
-    .cfi_rel_offset x6, 312
-
-    stp x7, x8,   [sp, #320]
-    .cfi_rel_offset x7, 320
-    .cfi_rel_offset x8, 328
-
-    stp x9, x10,  [sp, #336]
-    .cfi_rel_offset x9, 336
-    .cfi_rel_offset x10, 344
-
-    stp x11, x12, [sp, #352]
-    .cfi_rel_offset x11, 352
-    .cfi_rel_offset x12, 360
-
-    stp x13, x14, [sp, #368]
-    .cfi_rel_offset x13, 368
-    .cfi_rel_offset x14, 376
-
-    stp x15, x16, [sp, #384]
-    .cfi_rel_offset x15, 384
-    .cfi_rel_offset x16, 392
-
-    stp x17, x18, [sp, #400]
-    .cfi_rel_offset x17, 400
-    .cfi_rel_offset x18, 408
-
-    stp x19, x20, [sp, #416]
-    .cfi_rel_offset x19, 416
-    .cfi_rel_offset x20, 424
-
-    stp x21, x22, [sp, #432]
-    .cfi_rel_offset x21, 432
-    .cfi_rel_offset x22, 440
-
-    stp x23, x24, [sp, #448]
-    .cfi_rel_offset x23, 448
-    .cfi_rel_offset x24, 456
-
-    stp x25, x26, [sp, #464]
-    .cfi_rel_offset x25, 464
-    .cfi_rel_offset x26, 472
-
-    stp x27, x28, [sp, #480]
-    .cfi_rel_offset x27, 480
-    .cfi_rel_offset x28, 488
-
-    stp x29, xLR, [sp, #496]
-    .cfi_rel_offset x29, 496
-    .cfi_rel_offset x30, 504
+    SAVE_REG            x0, 264
+    SAVE_TWO_REGS  x1,  x2, 272
+    SAVE_TWO_REGS  x3,  x4, 288
+    SAVE_TWO_REGS  x5,  x6, 304
+    SAVE_TWO_REGS  x7,  x8, 320
+    SAVE_TWO_REGS  x9, x10, 336
+    SAVE_TWO_REGS x11, x12, 352
+    SAVE_TWO_REGS x13, x14, 368
+    SAVE_TWO_REGS x15, x16, 384
+    SAVE_TWO_REGS x17, x18, 400
+    SAVE_TWO_REGS x19, x20, 416
+    SAVE_TWO_REGS x21, x22, 432
+    SAVE_TWO_REGS x23, x24, 448
+    SAVE_TWO_REGS x25, x26, 464
+    SAVE_TWO_REGS x27, x28, 480
+    SAVE_TWO_REGS x29, xLR, 496
 
     // art::Runtime** xIP0 = &art::Runtime::instance_
     adrp xIP0, :got:_ZN3art7Runtime9instance_E
@@ -452,71 +350,24 @@
     ldr d31,      [sp, #256]
 
     // Restore core registers.
-    ldr x0,       [sp, #264]
-    .cfi_restore x0
+    RESTORE_REG            x0, 264
+    RESTORE_TWO_REGS  x1,  x2, 272
+    RESTORE_TWO_REGS  x3,  x4, 288
+    RESTORE_TWO_REGS  x5,  x6, 304
+    RESTORE_TWO_REGS  x7,  x8, 320
+    RESTORE_TWO_REGS  x9, x10, 336
+    RESTORE_TWO_REGS x11, x12, 352
+    RESTORE_TWO_REGS x13, x14, 368
+    RESTORE_TWO_REGS x15, x16, 384
+    RESTORE_TWO_REGS x17, x18, 400
+    RESTORE_TWO_REGS x19, x20, 416
+    RESTORE_TWO_REGS x21, x22, 432
+    RESTORE_TWO_REGS x23, x24, 448
+    RESTORE_TWO_REGS x25, x26, 464
+    RESTORE_TWO_REGS x27, x28, 480
+    RESTORE_TWO_REGS x29, xLR, 496
 
-    ldp x1, x2,   [sp, #272]
-    .cfi_restore x1
-    .cfi_restore x2
-
-    ldp x3, x4,   [sp, #288]
-    .cfi_restore x3
-    .cfi_restore x4
-
-    ldp x5, x6,   [sp, #304]
-    .cfi_restore x5
-    .cfi_restore x6
-
-    ldp x7, x8,   [sp, #320]
-    .cfi_restore x7
-    .cfi_restore x8
-
-    ldp x9, x10,  [sp, #336]
-    .cfi_restore x9
-    .cfi_restore x10
-
-    ldp x11, x12, [sp, #352]
-    .cfi_restore x11
-    .cfi_restore x12
-
-    ldp x13, x14, [sp, #368]
-    .cfi_restore x13
-    .cfi_restore x14
-
-    ldp x15, x16, [sp, #384]
-    .cfi_restore x15
-    .cfi_restore x16
-
-    ldp x17, x18, [sp, #400]
-    .cfi_restore x17
-    .cfi_restore x18
-
-    ldp x19, x20, [sp, #416]
-    .cfi_restore x19
-    .cfi_restore x20
-
-    ldp x21, x22, [sp, #432]
-    .cfi_restore x21
-    .cfi_restore x22
-
-    ldp x23, x24, [sp, #448]
-    .cfi_restore x23
-    .cfi_restore x24
-
-    ldp x25, x26, [sp, #464]
-    .cfi_restore x25
-    .cfi_restore x26
-
-    ldp x27, x28, [sp, #480]
-    .cfi_restore x27
-    .cfi_restore x28
-
-    ldp x29, xLR, [sp, #496]
-    .cfi_restore x29
-    .cfi_restore x30
-
-    add sp, sp, #512
-    .cfi_adjust_cfa_offset -512
+    DECREASE_FRAME 512
 .endm
 
 .macro RETURN_IF_RESULT_IS_ZERO
@@ -1409,12 +1260,8 @@
 ENTRY art_quick_check_cast
     // Store arguments and link register
     // Stack needs to be 16B aligned on calls.
-    stp x0, x1, [sp,#-32]!
-    .cfi_adjust_cfa_offset 32
-    .cfi_rel_offset x0, 0
-    .cfi_rel_offset x1, 8
-    str xLR, [sp, #24]
-    .cfi_rel_offset x30, 24
+    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
+    SAVE_REG xLR, 24
 
     // Call runtime code
     bl artIsAssignableFromCode
@@ -1423,24 +1270,16 @@
     cbz x0, .Lthrow_class_cast_exception
 
     // Restore and return
-    ldr xLR, [sp, #24]
-    .cfi_restore x30
-    ldp x0, x1, [sp], #32
-    .cfi_restore x0
-    .cfi_restore x1
-    .cfi_adjust_cfa_offset -32
+    .cfi_remember_state
+    RESTORE_REG xLR, 24
+    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
     ret
-
-    .cfi_adjust_cfa_offset 32         // Reset unwind info so following code unwinds.
+    .cfi_restore_state                // Reset unwind info so following code unwinds.
 
 .Lthrow_class_cast_exception:
     // Restore
-    ldr xLR, [sp, #24]
-    .cfi_restore x30
-    ldp x0, x1, [sp], #32
-    .cfi_restore x0
-    .cfi_restore x1
-    .cfi_adjust_cfa_offset -32
+    RESTORE_REG xLR, 24
+    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
 
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
     mov x2, xSELF                     // pass Thread::Current
@@ -1492,16 +1331,9 @@
 #endif
 .Lrb_slowpath\number:
     // Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned.
-    stp x0, x1, [sp, #-48]!
-    .cfi_adjust_cfa_offset 48
-    .cfi_rel_offset x0, 0
-    .cfi_rel_offset x1, 8
-    stp x2, x3, [sp, #16]
-    .cfi_rel_offset x2, 16
-    .cfi_rel_offset x3, 24
-    stp x4, xLR, [sp, #32]
-    .cfi_rel_offset x4, 32
-    .cfi_rel_offset x30, 40
+    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 48
+    SAVE_TWO_REGS x2, x3, 16
+    SAVE_TWO_REGS x4, xLR, 32
 
     // mov x0, \xRef                // pass ref in x0 (no-op for now since parameter ref is unused)
     .ifnc \xObj, x1
@@ -1520,10 +1352,8 @@
     POP_REG_NE x2, 16, \xDest
     POP_REG_NE x3, 24, \xDest
     POP_REG_NE x4, 32, \xDest
-    ldr xLR, [sp, #40]
-    .cfi_restore x30
-    add sp, sp, #48
-    .cfi_adjust_cfa_offset -48
+    RESTORE_REG xLR, 40
+    DECREASE_FRAME 48
 .Lrb_exit\number:
 #else
     ldr \wDest, [\xObj, #\offset]   // Heap reference = 32b. This also zero-extends to \xDest.
@@ -1587,13 +1417,8 @@
     ret
 .Lcheck_assignability:
     // Store arguments and link register
-    stp x0, x1, [sp,#-32]!
-    .cfi_adjust_cfa_offset 32
-    .cfi_rel_offset x0, 0
-    .cfi_rel_offset x1, 8
-    stp x2, xLR, [sp, #16]
-    .cfi_rel_offset x2, 16
-    .cfi_rel_offset x30, 24
+    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
+    SAVE_TWO_REGS x2, xLR, 16
 
     // Call runtime code
     mov x0, x3              // Heap reference, 32b, "uncompress" = do nothing, already zero-extended
@@ -1604,13 +1429,9 @@
     cbz x0, .Lthrow_array_store_exception
 
     // Restore
-    ldp x2, x30, [sp, #16]
-    .cfi_restore x2
-    .cfi_restore x30
-    ldp x0, x1, [sp], #32
-    .cfi_restore x0
-    .cfi_restore x1
-    .cfi_adjust_cfa_offset -32
+    .cfi_remember_state
+    RESTORE_TWO_REGS x2, xLR, 16
+    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
 
     add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
                                                           // "Compress" = do nothing
@@ -1620,15 +1441,10 @@
     lsr x0, x0, #7
     strb w3, [x3, x0]
     ret
-    .cfi_adjust_cfa_offset 32  // 4 restores after cbz for unwinding.
+    .cfi_restore_state            // Reset unwind info so following code unwinds.
 .Lthrow_array_store_exception:
-    ldp x2, x30, [sp, #16]
-    .cfi_restore x2
-    .cfi_restore x30
-    ldp x0, x1, [sp], #32
-    .cfi_restore x0
-    .cfi_restore x1
-    .cfi_adjust_cfa_offset -32
+    RESTORE_TWO_REGS x2, xLR, 16
+    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
 
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
     mov x1, x2                    // Pass value.
@@ -1821,15 +1637,9 @@
     ldr   x3, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
     tbnz  x3, #LOCK_WORD_MARK_BIT_SHIFT, .Lart_quick_resolve_string_no_rb
     // Save LR so that we can return, also x1 for alignment purposes.
-    stp    x1, xLR, [sp, #-16]!                     // Save x1, LR.
-    .cfi_adjust_cfa_offset 16
-    .cfi_rel_offset x1, 0
-    .cfi_rel_offset xLR, 8
+    SAVE_TWO_REGS_INCREASE_FRAME x1, xLR, 16        // Save x1, LR.
     bl     artReadBarrierMark                       // Get the marked string back.
-    ldp    x1, xLR, [sp], #16                       // Restore registers.
-    .cfi_restore xLR
-    .cfi_restore x1
-    .cfi_adjust_cfa_offset -16
+    RESTORE_TWO_REGS_DECREASE_FRAME x1, xLR, 16     // Restore registers.
 .Lart_quick_resolve_string_no_rb:
     ret
 
@@ -2104,22 +1914,13 @@
     tbnz    w3, #LOCK_WORD_MARK_BIT_SHIFT, .Ldo_allocation\name
                                                               // The read barrier slow path. Mark
                                                               // the class.
-    stp    x0, x1, [sp, #-32]!                                // Save registers (x0, x1, lr).
-    .cfi_adjust_cfa_offset 32
-    .cfi_rel_offset x0, 0
-    .cfi_rel_offset x1, 8
-    str    xLR, [sp, #16]                                     // Align sp by 16 bytes.
-    .cfi_rel_offset xLR, 16
+    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32                   // Save registers (x0, x1, lr).
+    SAVE_REG xLR, 24                                          // Align sp by 16 bytes.
     mov    x0, x2                                             // Pass the class as the first param.
     bl     artReadBarrierMark
     mov    x2, x0                                             // Get the (marked) class back.
-    ldp    x0, x1, [sp, #0]                                   // Restore registers.
-    .cfi_restore x0
-    .cfi_restore x1
-    ldr    xLR, [sp, #16]
-    .cfi_restore xLR
-    add    sp, sp, #32
-    .cfi_adjust_cfa_offset -32
+    RESTORE_REG xLR, 24
+    RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32                // Restore registers.
     b      .Ldo_allocation\name
 .endif
 .Lslow_path\name:
@@ -2503,7 +2304,7 @@
     mov   xLR, x1             // r1 is holding link register if we're to bounce to deoptimize
 
     ldr   d0, [sp, #8]        // Restore floating-point result.
-    ldr   x0, [sp], 16        // Restore integer result, and drop stack area.
+    ldr   x0, [sp], #16       // Restore integer result, and drop stack area.
     .cfi_adjust_cfa_offset 16
 
     POP_SAVE_REFS_ONLY_FRAME
@@ -2529,7 +2330,7 @@
      */
     .extern artDeoptimizeFromCompiledCode
 ENTRY art_quick_deoptimize_from_compiled_code
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+    SETUP_SAVE_EVERYTHING_FRAME
     mov    x0, xSELF                      // Pass thread.
     bl     artDeoptimizeFromCompiledCode  // artDeoptimizeFromCompiledCode(Thread*)
     brk 0
@@ -2661,37 +2462,16 @@
     ret
 .Lslow_path_rb_\name:
     // Save all potentially live caller-save core registers.
-    stp   x0, x1,   [sp, #-368]!
-    .cfi_adjust_cfa_offset 368
-    .cfi_rel_offset x0, 0
-    .cfi_rel_offset x1, 8
-    stp   x2, x3,   [sp, #16]
-    .cfi_rel_offset x2, 16
-    .cfi_rel_offset x3, 24
-    stp   x4, x5,   [sp, #32]
-    .cfi_rel_offset x4, 32
-    .cfi_rel_offset x5, 40
-    stp   x6, x7,   [sp, #48]
-    .cfi_rel_offset x6, 48
-    .cfi_rel_offset x7, 56
-    stp   x8, x9,   [sp, #64]
-    .cfi_rel_offset x8, 64
-    .cfi_rel_offset x9, 72
-    stp   x10, x11, [sp, #80]
-    .cfi_rel_offset x10, 80
-    .cfi_rel_offset x11, 88
-    stp   x12, x13, [sp, #96]
-    .cfi_rel_offset x12, 96
-    .cfi_rel_offset x13, 104
-    stp   x14, x15, [sp, #112]
-    .cfi_rel_offset x14, 112
-    .cfi_rel_offset x15, 120
-    stp   x16, x17, [sp, #128]
-    .cfi_rel_offset x16, 128
-    .cfi_rel_offset x17, 136
-    stp   x18, x19, [sp, #144]
-    .cfi_rel_offset x18, 144
-    .cfi_rel_offset x19, 152
+    SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 368
+    SAVE_TWO_REGS  x2,  x3, 16
+    SAVE_TWO_REGS  x4,  x5, 32
+    SAVE_TWO_REGS  x6,  x7, 48
+    SAVE_TWO_REGS  x8,  x9, 64
+    SAVE_TWO_REGS x10, x11, 80
+    SAVE_TWO_REGS x12, x13, 96
+    SAVE_TWO_REGS x14, x15, 112
+    SAVE_TWO_REGS x16, x17, 128
+    SAVE_TWO_REGS x18, x19, 144
     // Save all potentially live caller-save floating-point registers.
     stp   d0, d1,   [sp, #160]
     stp   d2, d3,   [sp, #176]
@@ -2706,9 +2486,8 @@
     stp   d28, d29, [sp, #320]
     stp   d30, d31, [sp, #336]
     // Save return address.
-    str   xLR,      [sp, #352]
-    .cfi_rel_offset x30, 352
-    // (sp + #360 is a padding slot)
+    // (sp + #352 is a padding slot)
+    SAVE_REG xLR, 360
 
     .ifnc \wreg, w0
       mov   w0, \wreg                   // Pass arg1 - obj from `wreg`
@@ -2744,10 +2523,8 @@
     ldp   d28, d29, [sp, #320]
     ldp   d30, d31, [sp, #336]
     // Restore return address and remove padding.
-    ldr   xLR,      [sp, #352]
-    .cfi_restore x30
-    add sp, sp, #368
-    .cfi_adjust_cfa_offset -368
+    RESTORE_REG xLR, 360
+    DECREASE_FRAME 368
 .Lret_rb_\name:
     ret
 END \name
diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc
index b35e088..8f64dcd 100644
--- a/runtime/arch/instruction_set.cc
+++ b/runtime/arch/instruction_set.cc
@@ -19,11 +19,31 @@
 // Explicitly include our own elf.h to avoid Linux and other dependencies.
 #include "../elf.h"
 #include "base/bit_utils.h"
+#include "base/logging.h"
 #include "globals.h"
 
 namespace art {
 
-const char* GetInstructionSetString(const InstructionSet isa) {
+void InstructionSetAbort(InstructionSet isa) {
+  switch (isa) {
+    case kArm:
+    case kThumb2:
+    case kArm64:
+    case kX86:
+    case kX86_64:
+    case kMips:
+    case kMips64:
+    case kNone:
+      LOG(FATAL) << "Unsupported instruction set " << isa;
+      UNREACHABLE();
+
+    default:
+      LOG(FATAL) << "Unknown ISA " << isa;
+      UNREACHABLE();
+  }
+}
+
+const char* GetInstructionSetString(InstructionSet isa) {
   switch (isa) {
     case kArm:
     case kThumb2:
diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h
index 917acc9..4a8bea4 100644
--- a/runtime/arch/instruction_set.h
+++ b/runtime/arch/instruction_set.h
@@ -21,7 +21,7 @@
 #include <string>
 
 #include "base/enums.h"
-#include "base/logging.h"  // Logging is required for FATAL in the helper functions.
+#include "base/macros.h"
 
 namespace art {
 
@@ -75,7 +75,6 @@
 // X86 instruction alignment. This is the recommended alignment for maximum performance.
 static constexpr size_t kX86Alignment = 16;
 
-
 const char* GetInstructionSetString(InstructionSet isa);
 
 // Note: Returns kNone when the string cannot be parsed to a known value.
@@ -83,6 +82,9 @@
 
 InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags);
 
+// Fatal logging out of line to keep the header clean of logging.h.
+NO_RETURN void InstructionSetAbort(InstructionSet isa);
+
 static inline PointerSize GetInstructionSetPointerSize(InstructionSet isa) {
   switch (isa) {
     case kArm:
@@ -99,12 +101,8 @@
       return kMipsPointerSize;
     case kMips64:
       return kMips64PointerSize;
-    case kNone:
-      LOG(FATAL) << "ISA kNone does not have pointer size.";
-      UNREACHABLE();
     default:
-      LOG(FATAL) << "Unknown ISA " << isa;
-      UNREACHABLE();
+      InstructionSetAbort(isa);
   }
 }
 
@@ -139,12 +137,8 @@
     case kMips64:
       return true;
 
-    case kNone:
-      LOG(FATAL) << "ISA kNone does not have bit width.";
-      UNREACHABLE();
     default:
-      LOG(FATAL) << "Unknown ISA " << isa;
-      UNREACHABLE();
+      InstructionSetAbort(isa);
   }
 }
 
@@ -168,12 +162,9 @@
       return 4;
     case kMips64:
       return 8;
-    case kNone:
-      LOG(FATAL) << "ISA kNone does not have spills.";
-      UNREACHABLE();
+
     default:
-      LOG(FATAL) << "Unknown ISA " << isa;
-      UNREACHABLE();
+      InstructionSetAbort(isa);
   }
 }
 
@@ -193,12 +184,9 @@
       return 4;
     case kMips64:
       return 8;
-    case kNone:
-      LOG(FATAL) << "ISA kNone does not have spills.";
-      UNREACHABLE();
+
     default:
-      LOG(FATAL) << "Unknown ISA " << isa;
-      UNREACHABLE();
+      InstructionSetAbort(isa);
   }
 }
 
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index fb38b47..7bbc709 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -22,6 +22,7 @@
 #include "cutils/properties.h"
 #endif
 
+#include "base/logging.h"
 #include "base/stringprintf.h"
 
 namespace art {
diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h
index 120dc1c..2d54988 100644
--- a/runtime/arch/mips/instruction_set_features_mips.h
+++ b/runtime/arch/mips/instruction_set_features_mips.h
@@ -18,6 +18,8 @@
 #define ART_RUNTIME_ARCH_MIPS_INSTRUCTION_SET_FEATURES_MIPS_H_
 
 #include "arch/instruction_set_features.h"
+#include "base/logging.h"
+#include "base/macros.h"
 
 namespace art {
 
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3d393f6..4bd1314 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2094,7 +2094,7 @@
      */
     .extern artDeoptimizeFromCompiledCode
 ENTRY art_quick_deoptimize_from_compiled_code
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+    SETUP_SAVE_EVERYTHING_FRAME
     la       $t9, artDeoptimizeFromCompiledCode
     jalr     $t9                            # artDeoptimizeFromCompiledCode(Thread*)
                                             # Returns caller method's frame size.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 9774eb9..26717ad 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1487,6 +1487,7 @@
     ld     $a1, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
     jal    artGet32StaticFromCode        # (uint32_t field_idx, const Method* referrer, Thread*)
     move   $a2, rSELF                    # pass Thread::Current
+    sll    $v0, $v0, 0                   # sign-extend result
     RETURN_IF_NO_EXCEPTION
 END art_quick_get32_static
 
@@ -1571,6 +1572,7 @@
     ld     $a2, FRAME_SIZE_SAVE_REFS_ONLY($sp)  # pass referrer's Method*
     jal    artGet32InstanceFromCode      # (field_idx, Object*, referrer, Thread*)
     move   $a3, rSELF                    # pass Thread::Current
+    sll    $v0, $v0, 0                   # sign-extend result
     RETURN_IF_NO_EXCEPTION
 END art_quick_get32_instance
 
@@ -2138,8 +2140,8 @@
      * will long jump to the upcall with a special exception of -1.
      */
     .extern artDeoptimizeFromCompiledCode
-ENTRY art_quick_deoptimize_from_compiled_code
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ENTRY_NO_GP art_quick_deoptimize_from_compiled_code
+    SETUP_SAVE_EVERYTHING_FRAME
     jal      artDeoptimizeFromCompiledCode    # artDeoptimizeFromCompiledCode(Thread*, SP)
                                               # Returns caller method's frame size.
     move     $a0, rSELF                       # pass Thread::current
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 67ebf50..646a80c 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1930,7 +1930,7 @@
      * will long jump to the interpreter bridge.
      */
 DEFINE_FUNCTION art_quick_deoptimize_from_compiled_code
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx
+    SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
     subl LITERAL(12), %esp                      // Align stack.
     CFI_ADJUST_CFA_OFFSET(12)
     pushl %fs:THREAD_SELF_OFFSET                // Pass Thread::Current().
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index b805703..5ea58af 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2053,7 +2053,7 @@
      * will long jump to the interpreter bridge.
      */
 DEFINE_FUNCTION art_quick_deoptimize_from_compiled_code
-    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+    SETUP_SAVE_EVERYTHING_FRAME
                                                 // Stack should be aligned now.
     movq %gs:THREAD_SELF_OFFSET, %rdi           // Pass Thread.
     call SYMBOL(artDeoptimizeFromCompiledCode)  // artDeoptimizeFromCompiledCode(Thread*)
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index f9bc249..1392399 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -335,20 +335,30 @@
 }
 
 bool ArtMethod::IsAnnotatedWithFastNative() {
+  return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_FastNative,
+                         DexFile::kDexVisibilityBuild);
+}
+
+bool ArtMethod::IsAnnotatedWithCriticalNative() {
+  return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_CriticalNative,
+                         DexFile::kDexVisibilityBuild);
+}
+
+bool ArtMethod::IsAnnotatedWith(jclass klass, uint32_t visibility) {
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   StackHandleScope<1> shs(self);
 
   const DexFile& dex_file = GetDeclaringClass()->GetDexFile();
 
-  mirror::Class* fast_native_annotation =
-      soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_annotation_optimization_FastNative);
-  Handle<mirror::Class> fast_native_handle(shs.NewHandle(fast_native_annotation));
+  mirror::Class* annotation = soa.Decode<mirror::Class*>(klass);
+  DCHECK(annotation->IsAnnotation());
+  Handle<mirror::Class> annotation_handle(shs.NewHandle(annotation));
 
   // Note: Resolves any method annotations' classes as a side-effect.
   // -- This seems allowed by the spec since it says we can preload any classes
   //    referenced by another classes's constant pool table.
-  return dex_file.IsMethodAnnotationPresent(this, fast_native_handle, DexFile::kDexVisibilityBuild);
+  return dex_file.IsMethodAnnotationPresent(this, annotation_handle, visibility);
 }
 
 bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> params) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index b25087c..8051a1f 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -378,6 +378,10 @@
   // -- Independent of kAccFastNative access flags.
   bool IsAnnotatedWithFastNative();
 
+  // Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative
+  // -- Unrelated to the GC notion of "critical".
+  bool IsAnnotatedWithCriticalNative();
+
   // Returns true if this method could be overridden by a default method.
   bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -776,6 +780,8 @@
   } ptr_sized_fields_;
 
  private:
+  bool IsAnnotatedWith(jclass klass, uint32_t visibility);
+
   static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) {
     // Round up to pointer size for padding field. Tested in art_method.cc.
     return RoundUp(offsetof(ArtMethod, hotness_count_) + sizeof(hotness_count_),
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 378371d..d2f0fdb 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -21,7 +21,12 @@
 #include <limits>
 #include <type_traits>
 
+// This header is used in the disassembler with libbase's logging. Only include ART logging
+// when no other logging macros are available. b/15436106, b/31338270
+#ifndef CHECK
 #include "base/logging.h"
+#endif
+
 #include "base/iteration_range.h"
 #include "base/stl_util.h"
 
diff --git a/runtime/base/enums.h b/runtime/base/enums.h
index 51b86ea..52cab2a 100644
--- a/runtime/base/enums.h
+++ b/runtime/base/enums.h
@@ -20,9 +20,6 @@
 #include <cstddef>
 #include <ostream>
 
-#include "base/logging.h"
-#include "base/macros.h"
-
 namespace art {
 
 enum class PointerSize : size_t {
@@ -35,16 +32,6 @@
                                                        ? PointerSize::k64
                                                        : PointerSize::k32;
 
-template <typename T>
-static constexpr PointerSize ConvertToPointerSize(T any) {
-  if (any == 4 || any == 8) {
-    return static_cast<PointerSize>(any);
-  } else {
-    LOG(FATAL);
-    UNREACHABLE();
-  }
-}
-
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_ENUMS_H_
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 28352cb..529c391 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -139,13 +139,10 @@
 class LogMessageData {
  public:
   LogMessageData(const char* file, unsigned int line, LogSeverity severity, int error)
-      : file_(file),
+      : file_(GetFilenameBase(file)),
         line_number_(line),
         severity_(severity),
-        error_(error) {
-    const char* last_slash = strrchr(file, '/');
-    file = (last_slash == nullptr) ? file : last_slash + 1;
-  }
+        error_(error) {}
 
   const char * GetFile() const {
     return file_;
@@ -178,6 +175,11 @@
   const LogSeverity severity_;
   const int error_;
 
+  static const char* GetFilenameBase(const char* file) {
+    const char* last_slash = strrchr(file, '/');
+    return (last_slash == nullptr) ? file : last_slash + 1;
+  }
+
   DISALLOW_COPY_AND_ASSIGN(LogMessageData);
 };
 
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index a53dcea..a4cf249 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -20,7 +20,11 @@
 #include <algorithm>
 #include <sstream>
 
+// This header is used in the disassembler with libbase's logging. Only include ART logging
+// when no other logging macros are available. b/15436106, b/31338270
+#ifndef CHECK
 #include "base/logging.h"
+#endif
 
 namespace art {
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 726e897..8edb1b4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6533,39 +6533,90 @@
   return true;
 }
 
-// Finds the method with a name/signature that matches cmp in the given list of methods. The list of
-// methods must be unique.
+// Finds the method with a name/signature that matches cmp in the given lists of methods. The list
+// of methods must be unique.
+static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp ATTRIBUTE_UNUSED) {
+  return nullptr;
+}
+
+template <typename ... Types>
 static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp,
-                                           const ScopedArenaVector<ArtMethod*>& list)
+                                           const ScopedArenaVector<ArtMethod*>& list,
+                                           const Types& ... rest)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   for (ArtMethod* method : list) {
     if (cmp.HasSameNameAndSignature(method)) {
       return method;
     }
   }
-  return nullptr;
+  return FindSameNameAndSignature(cmp, rest...);
 }
 
-static void SanityCheckVTable(Handle<mirror::Class> klass, PointerSize pointer_size)
+// Check that all vtable entries are present in this class's virtuals or are the same as a
+// superclasses vtable entry.
+static void CheckClassOwnsVTableEntries(Thread* self,
+                                        Handle<mirror::Class> klass,
+                                        PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::PointerArray* check_vtable = klass->GetVTableDuringLinking();
-  mirror::Class* superclass = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr;
-  int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0;
+  StackHandleScope<2> hs(self);
+  Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
+  mirror::Class* super_temp = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr;
+  Handle<mirror::Class> superclass(hs.NewHandle(super_temp));
+  int32_t super_vtable_length = (superclass.Get() != nullptr) ? superclass->GetVTableLength() : 0;
   for (int32_t i = 0; i < check_vtable->GetLength(); ++i) {
     ArtMethod* m = check_vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
     CHECK(m != nullptr);
 
+    CHECK_EQ(m->GetMethodIndexDuringLinking(), i)
+        << PrettyMethod(m) << " has an unexpected method index for its spot in the vtable for class"
+        << PrettyClass(klass.Get());
     ArraySlice<ArtMethod> virtuals = klass->GetVirtualMethodsSliceUnchecked(pointer_size);
     auto is_same_method = [m] (const ArtMethod& meth) {
       return &meth == m;
     };
     CHECK((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) ||
           std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end())
-        << "While linking class '" << PrettyClass(klass.Get()) << "' unable to find owning class "
-        << "of '" << PrettyMethod(m) << "' (vtable index: " << i << ").";
+        << PrettyMethod(m) << " does not seem to be owned by current class "
+        << PrettyClass(klass.Get()) << " or any of its superclasses!";
   }
 }
 
+// Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
+// method is overridden in a subclass.
+static void CheckVTableHasNoDuplicates(Thread* self,
+                                       Handle<mirror::Class> klass,
+                                       PointerSize pointer_size)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
+  int32_t num_entries = vtable->GetLength();
+  for (int32_t i = 0; i < num_entries; i++) {
+    ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
+    // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+    if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+                                vtable_entry->GetAccessFlags())) {
+      continue;
+    }
+    MethodNameAndSignatureComparator name_comparator(
+        vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
+    for (int32_t j = i+1; j < num_entries; j++) {
+      ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
+      CHECK(vtable_entry != other_entry &&
+            !name_comparator.HasSameNameAndSignature(
+                other_entry->GetInterfaceMethodIfProxy(pointer_size)))
+          << "vtable entries " << i << " and " << j << " are identical for "
+          << PrettyClass(klass.Get()) << " in method " << PrettyMethod(vtable_entry) << " and "
+          << PrettyMethod(other_entry);
+    }
+  }
+}
+
+static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, PointerSize pointer_size)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  CheckClassOwnsVTableEntries(self, klass, pointer_size);
+  CheckVTableHasNoDuplicates(self, klass, pointer_size);
+}
+
 void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
                                         ArtMethod* unimplemented_method,
                                         ArtMethod* imt_conflict_method,
@@ -6624,8 +6675,10 @@
   ScopedArenaAllocator allocator(&stack);
 
   ScopedArenaVector<ArtMethod*> default_conflict_methods(allocator.Adapter());
+  ScopedArenaVector<ArtMethod*> overriding_default_conflict_methods(allocator.Adapter());
   ScopedArenaVector<ArtMethod*> miranda_methods(allocator.Adapter());
   ScopedArenaVector<ArtMethod*> default_methods(allocator.Adapter());
+  ScopedArenaVector<ArtMethod*> overriding_default_methods(allocator.Adapter());
 
   MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
   ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
@@ -6829,8 +6882,10 @@
               default_conflict_method = vtable_impl;
             } else {
               // See if we already have a conflict method for this method.
-              ArtMethod* preexisting_conflict = FindSameNameAndSignature(interface_name_comparator,
-                                                                         default_conflict_methods);
+              ArtMethod* preexisting_conflict = FindSameNameAndSignature(
+                  interface_name_comparator,
+                  default_conflict_methods,
+                  overriding_default_conflict_methods);
               if (LIKELY(preexisting_conflict != nullptr)) {
                 // We already have another conflict we can reuse.
                 default_conflict_method = preexisting_conflict;
@@ -6841,7 +6896,13 @@
                 default_conflict_method =
                     reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
                 new(default_conflict_method) ArtMethod(interface_method, image_pointer_size_);
-                default_conflict_methods.push_back(default_conflict_method);
+                if (vtable_impl == nullptr) {
+                  // Save the conflict method. We need to add it to the vtable.
+                  default_conflict_methods.push_back(default_conflict_method);
+                } else {
+                  // Save the conflict method but it is already in the vtable.
+                  overriding_default_conflict_methods.push_back(default_conflict_method);
+                }
               }
             }
             current_method = default_conflict_method;
@@ -6861,11 +6922,18 @@
               // TODO It might be worthwhile to copy default methods on interfaces anyway since it
               //      would make lookup for interface super much faster. (We would only need to scan
               //      the iftable to find if there is a NSME or AME.)
-              ArtMethod* old = FindSameNameAndSignature(interface_name_comparator, default_methods);
+              ArtMethod* old = FindSameNameAndSignature(interface_name_comparator,
+                                                        default_methods,
+                                                        overriding_default_methods);
               if (old == nullptr) {
                 // We found a default method implementation and there were no conflicts.
-                // Save the default method. We need to add it to the vtable.
-                default_methods.push_back(current_method);
+                if (vtable_impl == nullptr) {
+                  // Save the default method. We need to add it to the vtable.
+                  default_methods.push_back(current_method);
+                } else {
+                  // Save the default method but it is already in the vtable.
+                  overriding_default_methods.push_back(current_method);
+                }
               } else {
                 CHECK(old == current_method) << "Multiple default implementations selected!";
               }
@@ -6920,6 +6988,8 @@
   }  // For each interface.
   const bool has_new_virtuals = !(miranda_methods.empty() &&
                                   default_methods.empty() &&
+                                  overriding_default_methods.empty() &&
+                                  overriding_default_conflict_methods.empty() &&
                                   default_conflict_methods.empty());
   // TODO don't extend virtuals of interface unless necessary (when is it?).
   if (has_new_virtuals) {
@@ -6927,11 +6997,16 @@
         << "Interfaces should only have default-conflict methods appended to them.";
     VLOG(class_linker) << PrettyClass(klass.Get()) << ": miranda_methods=" << miranda_methods.size()
                        << " default_methods=" << default_methods.size()
-                       << " default_conflict_methods=" << default_conflict_methods.size();
+                       << " overriding_default_methods=" << overriding_default_methods.size()
+                       << " default_conflict_methods=" << default_conflict_methods.size()
+                       << " overriding_default_conflict_methods="
+                       << overriding_default_conflict_methods.size();
     const size_t old_method_count = klass->NumMethods();
     const size_t new_method_count = old_method_count +
                                     miranda_methods.size() +
                                     default_methods.size() +
+                                    overriding_default_conflict_methods.size() +
+                                    overriding_default_methods.size() +
                                     default_conflict_methods.size();
     // Attempt to realloc to save RAM if possible.
     LengthPrefixedArray<ArtMethod>* old_methods = klass->GetMethodsPtr();
@@ -6986,36 +7061,42 @@
     // interface but will have different ArtMethod*s for them. This also means we cannot compare a
     // default method found on a class with one found on the declaring interface directly and must
     // look at the declaring class to determine if they are the same.
-    for (ArtMethod* def_method : default_methods) {
-      ArtMethod& new_method = *out;
-      new_method.CopyFrom(def_method, image_pointer_size_);
-      // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been verified
-      // yet it shouldn't have methods that are skipping access checks.
-      // TODO This is rather arbitrary. We should maybe support classes where only some of its
-      // methods are skip_access_checks.
-      constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
-      constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
-      new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
-      move_table.emplace(def_method, &new_method);
-      ++out;
+    for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods,
+                                                             overriding_default_methods}) {
+      for (ArtMethod* def_method : methods_vec) {
+        ArtMethod& new_method = *out;
+        new_method.CopyFrom(def_method, image_pointer_size_);
+        // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been
+        // verified yet it shouldn't have methods that are skipping access checks.
+        // TODO This is rather arbitrary. We should maybe support classes where only some of its
+        // methods are skip_access_checks.
+        constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
+        constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
+        new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
+        move_table.emplace(def_method, &new_method);
+        ++out;
+      }
     }
-    for (ArtMethod* conf_method : default_conflict_methods) {
-      ArtMethod& new_method = *out;
-      new_method.CopyFrom(conf_method, image_pointer_size_);
-      // This is a type of default method (there are default method impls, just a conflict) so mark
-      // this as a default, non-abstract method, since thats what it is. Also clear the
-      // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
-      // methods that are skipping access checks.
-      constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
-      constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
-      new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
-      DCHECK(new_method.IsDefaultConflicting());
-      // The actual method might or might not be marked abstract since we just copied it from a
-      // (possibly default) interface method. We need to set it entry point to be the bridge so that
-      // the compiler will not invoke the implementation of whatever method we copied from.
-      EnsureThrowsInvocationError(&new_method);
-      move_table.emplace(conf_method, &new_method);
-      ++out;
+    for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_conflict_methods,
+                                                             overriding_default_conflict_methods}) {
+      for (ArtMethod* conf_method : methods_vec) {
+        ArtMethod& new_method = *out;
+        new_method.CopyFrom(conf_method, image_pointer_size_);
+        // This is a type of default method (there are default method impls, just a conflict) so
+        // mark this as a default, non-abstract method, since thats what it is. Also clear the
+        // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
+        // methods that are skipping access checks.
+        constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
+        constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
+        new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
+        DCHECK(new_method.IsDefaultConflicting());
+        // The actual method might or might not be marked abstract since we just copied it from a
+        // (possibly default) interface method. We need to set it entry point to be the bridge so
+        // that the compiler will not invoke the implementation of whatever method we copied from.
+        EnsureThrowsInvocationError(&new_method);
+        move_table.emplace(conf_method, &new_method);
+        ++out;
+      }
     }
     methods->SetSize(new_method_count);
     UpdateClassMethods(klass.Get(), methods);
@@ -7031,22 +7112,31 @@
                                       miranda_methods.size() +
                                       default_methods.size() +
                                       default_conflict_methods.size();
+
       vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
       if (UNLIKELY(vtable.Get() == nullptr)) {
         self->AssertPendingOOMException();
         return false;
       }
-      out = methods->begin(method_size, method_alignment) + old_method_count;
       size_t vtable_pos = old_vtable_count;
       // Update all the newly copied method's indexes so they denote their placement in the vtable.
-      for (size_t i = old_method_count; i < new_method_count; ++i) {
-        // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_
-        // fields are references into the dex file the method was defined in. Since the ArtMethod
-        // does not store that information it uses declaring_class_->dex_cache_.
-        out->SetMethodIndex(0xFFFF & vtable_pos);
-        vtable->SetElementPtrSize(vtable_pos, &*out, image_pointer_size_);
-        ++out;
-        ++vtable_pos;
+      for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods,
+                                                               default_conflict_methods,
+                                                               miranda_methods}) {
+        // These are the functions that are not already in the vtable!
+        for (ArtMethod* new_method : methods_vec) {
+          auto translated_method_it = move_table.find(new_method);
+          CHECK(translated_method_it != move_table.end())
+              << "We must have a translation for methods added to the classes methods_ array! We "
+              << "could not find the ArtMethod added for " << PrettyMethod(new_method);
+          ArtMethod* new_vtable_method = translated_method_it->second;
+          // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_
+          // fields are references into the dex file the method was defined in. Since the ArtMethod
+          // does not store that information it uses declaring_class_->dex_cache_.
+          new_vtable_method->SetMethodIndex(0xFFFF & vtable_pos);
+          vtable->SetElementPtrSize(vtable_pos, new_vtable_method, image_pointer_size_);
+          ++vtable_pos;
+        }
       }
       CHECK_EQ(vtable_pos, new_vtable_count);
       // Update old vtable methods. We use the default_translations map to figure out what each
@@ -7062,8 +7152,10 @@
             // Find which conflict method we are to use for this method.
             MethodNameAndSignatureComparator old_method_comparator(
                 translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
-            ArtMethod* new_conflict_method = FindSameNameAndSignature(old_method_comparator,
-                                                                      default_conflict_methods);
+            // We only need to look through overriding_default_conflict_methods since this is an
+            // overridden method we are fixing up here.
+            ArtMethod* new_conflict_method = FindSameNameAndSignature(
+                old_method_comparator, overriding_default_conflict_methods);
             CHECK(new_conflict_method != nullptr) << "Expected a conflict method!";
             translated_method = new_conflict_method;
           } else if (translation_it->second.IsAbstract()) {
@@ -7071,7 +7163,7 @@
             MethodNameAndSignatureComparator old_method_comparator(
                 translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
             ArtMethod* miranda_method = FindSameNameAndSignature(old_method_comparator,
-                                                                miranda_methods);
+                                                                 miranda_methods);
             DCHECK(miranda_method != nullptr);
             translated_method = miranda_method;
           } else {
@@ -7086,6 +7178,14 @@
         if (it != move_table.end()) {
           auto* new_method = it->second;
           DCHECK(new_method != nullptr);
+          // Make sure the new_methods index is set.
+          if (new_method->GetMethodIndexDuringLinking() != i) {
+            DCHECK_LE(reinterpret_cast<uintptr_t>(&*methods->begin(method_size, method_alignment)),
+                      reinterpret_cast<uintptr_t>(new_method));
+            DCHECK_LT(reinterpret_cast<uintptr_t>(new_method),
+                      reinterpret_cast<uintptr_t>(&*methods->end(method_size, method_alignment)));
+            new_method->SetMethodIndex(0xFFFF & i);
+          }
           vtable->SetElementPtrSize(i, new_method, image_pointer_size_);
         } else {
           // If it was not going to be updated we wouldn't have put it into the default_translations
@@ -7149,7 +7249,7 @@
     self->EndAssertNoThreadSuspension(old_cause);
   }
   if (kIsDebugBuild && !is_interface) {
-    SanityCheckVTable(klass, image_pointer_size_);
+    SanityCheckVTable(self, klass, image_pointer_size_);
   }
   return true;
 }
diff --git a/runtime/code_simulator_container.h b/runtime/code_simulator_container.h
index 655a247..10178ba 100644
--- a/runtime/code_simulator_container.h
+++ b/runtime/code_simulator_container.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_CODE_SIMULATOR_CONTAINER_H_
 
 #include "arch/instruction_set.h"
+#include "base/logging.h"
 #include "simulator/code_simulator.h"
 
 namespace art {
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 741b682..dba0a81 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -608,6 +608,10 @@
   EXPECT_TRUE(actual_.empty()) << actual_;
 }
 
+void CheckJniAbortCatcher::Check(const std::string& expected_text) {
+  Check(expected_text.c_str());
+}
+
 void CheckJniAbortCatcher::Check(const char* expected_text) {
   EXPECT_TRUE(actual_.find(expected_text) != std::string::npos) << "\n"
       << "Expected to find: " << expected_text << "\n"
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index c07c03e..b2090b7 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -184,6 +184,7 @@
 
   ~CheckJniAbortCatcher();
 
+  void Check(const std::string& expected_text);
   void Check(const char* expected_text);
 
  private:
@@ -219,6 +220,12 @@
     return; \
   }
 
+#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \
+  if (!kHostStaticBuildEnabled) { \
+    printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \
+    return; \
+  }
+
 }  // namespace art
 
 namespace std {
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index fc04f30..47e6ca3 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -265,9 +265,9 @@
   RecordFree(ObjectBytePair(from_objects - to_objects, from_bytes - to_bytes));
   // Clear and protect the from space.
   from_space_->Clear();
-  // b/31172841. Temporarily disable the from-space protection under gcstress mode with debug build
+  // b/31172841. Temporarily disable the from-space protection with host debug build
   // due to some protection issue in the build server.
-  if (kProtectFromSpace && !(kIsDebugBuild && heap_->gc_stress_mode_)) {
+  if (kProtectFromSpace && !(kIsDebugBuild && !kIsTargetBuild)) {
     if (!from_space_->IsRosAllocSpace()) {
       // Protect with PROT_NONE.
       VLOG(heap) << "Protecting from_space_ : " << *from_space_;
@@ -794,9 +794,9 @@
 
 void SemiSpace::FinishPhase() {
   TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
-  // b/31172841. Temporarily disable the from-space protection under gcstress mode with debug build
+  // b/31172841. Temporarily disable the from-space protection with host debug build
   // due to some protection issue in the build server.
-  if (kProtectFromSpace && !(kIsDebugBuild && heap_->gc_stress_mode_)) {
+  if (kProtectFromSpace && !(kIsDebugBuild && !kIsTargetBuild)) {
     if (from_space_->IsRosAllocSpace()) {
       VLOG(heap) << "Protecting from_space_ with PROT_NONE : " << *from_space_;
       from_space_->GetMemMap()->Protect(PROT_NONE);
diff --git a/runtime/globals.h b/runtime/globals.h
index 9045d40..691bf55 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -67,22 +67,30 @@
 #if defined(ART_TARGET)
 // Useful in conditionals where ART_TARGET isn't.
 static constexpr bool kIsTargetBuild = true;
-#if defined(ART_TARGET_LINUX)
+# if defined(ART_TARGET_LINUX)
 static constexpr bool kIsTargetLinux = true;
-#elif defined(ART_TARGET_ANDROID)
+# elif defined(ART_TARGET_ANDROID)
 static constexpr bool kIsTargetLinux = false;
-#else
-#error "Either ART_TARGET_LINUX or ART_TARGET_ANDROID needs to be defined for target builds."
-#endif
+# else
+# error "Either ART_TARGET_LINUX or ART_TARGET_ANDROID needs to be defined for target builds."
+# endif
 #else
 static constexpr bool kIsTargetBuild = false;
-#if defined(ART_TARGET_LINUX)
-#error "ART_TARGET_LINUX defined for host build."
-#elif defined(ART_TARGET_ANDROID)
-#error "ART_TARGET_ANDROID defined for host build."
-#else
+# if defined(ART_TARGET_LINUX)
+# error "ART_TARGET_LINUX defined for host build."
+# elif defined(ART_TARGET_ANDROID)
+# error "ART_TARGET_ANDROID defined for host build."
+# else
 static constexpr bool kIsTargetLinux = false;
+# endif
 #endif
+
+// Are additional statically-linked ART host binaries (dex2oats,
+// oatdumps, etc.) built and available?
+#if !defined(ART_TARGET) && defined(ART_BUILD_HOST_STATIC)
+static constexpr bool kHostStaticBuildEnabled = true;
+#else
+static constexpr bool kHostStaticBuildEnabled = false;
 #endif
 
 // Garbage collector constants.
@@ -158,6 +166,12 @@
 
 static constexpr bool kArm32QuickCodeUseSoftFloat = false;
 
+#ifdef ART_ENABLE_VDEX
+static constexpr bool kIsVdexEnabled = true;
+#else
+static constexpr bool kIsVdexEnabled = false;
+#endif
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_GLOBALS_H_
diff --git a/runtime/image.cc b/runtime/image.cc
index 6888183..7e6790a 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -20,6 +20,7 @@
 #include "mirror/object_array.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
+#include "utils.h"
 
 namespace art {
 
@@ -179,4 +180,8 @@
   }
 }
 
+PointerSize ImageHeader::GetPointerSize() const {
+  return ConvertToPointerSize(pointer_size_);
+}
+
 }  // namespace art
diff --git a/runtime/image.h b/runtime/image.h
index 8cd94bb..da9976a 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -157,9 +157,7 @@
     return reinterpret_cast<uint8_t*>(oat_file_end_);
   }
 
-  PointerSize GetPointerSize() const {
-    return ConvertToPointerSize(pointer_size_);
-  }
+  PointerSize GetPointerSize() const;
 
   uint32_t GetPointerSizeUnchecked() const {
     return pointer_size_;
@@ -170,13 +168,11 @@
   }
 
   static std::string GetOatLocationFromImageLocation(const std::string& image) {
-    std::string oat_filename = image;
-    if (oat_filename.length() <= 3) {
-      oat_filename += ".oat";
-    } else {
-      oat_filename.replace(oat_filename.length() - 3, 3, "oat");
-    }
-    return oat_filename;
+    return GetLocationFromImageLocation(image, "oat");
+  }
+
+  static std::string GetVdexLocationFromImageLocation(const std::string& image) {
+    return GetLocationFromImageLocation(image, "vdex");
   }
 
   enum ImageMethod {
@@ -301,6 +297,17 @@
   static const uint8_t kImageMagic[4];
   static const uint8_t kImageVersion[4];
 
+  static std::string GetLocationFromImageLocation(const std::string& image,
+                                                  const std::string& extension) {
+    std::string filename = image;
+    if (filename.length() <= 3) {
+      filename += "." + extension;
+    } else {
+      filename.replace(filename.length() - 3, 3, extension);
+    }
+    return filename;
+  }
+
   uint8_t magic_[4];
   uint8_t version_[4];
 
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 277bda4..0003e72 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -20,7 +20,6 @@
 
 #include "common_throws.h"
 #include "interpreter_common.h"
-#include "interpreter_goto_table_impl.h"
 #include "interpreter_mterp_impl.h"
 #include "interpreter_switch_impl.h"
 #include "mirror/string-inl.h"
@@ -231,15 +230,12 @@
 
 enum InterpreterImplKind {
   kSwitchImplKind,        // Switch-based interpreter implementation.
-  kComputedGotoImplKind,  // Computed-goto-based interpreter implementation.
   kMterpImplKind          // Assembly interpreter
 };
 static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs) {
   os << ((rhs == kSwitchImplKind)
               ? "Switch-based interpreter"
-              : (rhs == kComputedGotoImplKind)
-                  ? "Computed-goto-based interpreter"
-                  : "Asm interpreter");
+              : "Asm interpreter");
   return os;
 }
 
@@ -323,7 +319,8 @@
           }
         }
       }
-    } else if (kInterpreterImplKind == kSwitchImplKind) {
+    } else {
+      DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
       if (transaction_active) {
         return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
                                               false);
@@ -331,13 +328,6 @@
         return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
                                                false);
       }
-    } else {
-      DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
-      if (transaction_active) {
-        return ExecuteGotoImpl<false, true>(self, code_item, shadow_frame, result_register);
-      } else {
-        return ExecuteGotoImpl<false, false>(self, code_item, shadow_frame, result_register);
-      }
     }
   } else {
     // Enter the "with access check" interpreter.
@@ -350,7 +340,8 @@
         return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register,
                                               false);
       }
-    } else if (kInterpreterImplKind == kSwitchImplKind) {
+    } else {
+      DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
       if (transaction_active) {
         return ExecuteSwitchImpl<true, true>(self, code_item, shadow_frame, result_register,
                                              false);
@@ -358,13 +349,6 @@
         return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register,
                                               false);
       }
-    } else {
-      DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
-      if (transaction_active) {
-        return ExecuteGotoImpl<true, true>(self, code_item, shadow_frame, result_register);
-      } else {
-        return ExecuteGotoImpl<true, false>(self, code_item, shadow_frame, result_register);
-      }
     }
   }
 }
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
deleted file mode 100644
index 37dd63b..0000000
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ /dev/null
@@ -1,2607 +0,0 @@
-/*
- * Copyright (C) 2012 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 "interpreter_goto_table_impl.h"
-
-// Common includes
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "stack.h"
-#include "thread.h"
-
-// Clang compiles the GOTO interpreter very slowly. So we skip it. These are the implementation
-// details only necessary when compiling it.
-#if !defined(__clang__)
-#include "experimental_flags.h"
-#include "interpreter_common.h"
-#include "jit/jit.h"
-#include "safe_math.h"
-#endif
-
-namespace art {
-namespace interpreter {
-
-#if !defined(__clang__)
-
-// In the following macros, we expect the following local variables exist:
-// - "self": the current Thread*.
-// - "inst" : the current Instruction*.
-// - "inst_data" : the current instruction's first 16 bits.
-// - "dex_pc": the current pc.
-// - "shadow_frame": the current shadow frame.
-// - "currentHandlersTable": the current table of pointer to each instruction handler.
-
-// Advance to the next instruction and updates interpreter state.
-#define ADVANCE(_offset)                                                    \
-  do {                                                                      \
-    int32_t disp = static_cast<int32_t>(_offset);                           \
-    inst = inst->RelativeAt(disp);                                          \
-    dex_pc = static_cast<uint32_t>(static_cast<int32_t>(dex_pc) + disp);    \
-    shadow_frame.SetDexPC(dex_pc);                                          \
-    TraceExecution(shadow_frame, inst, dex_pc);                             \
-    inst_data = inst->Fetch16(0);                                           \
-    goto *currentHandlersTable[inst->Opcode(inst_data)];                    \
-  } while (false)
-
-#define HANDLE_PENDING_EXCEPTION() goto exception_pending_label
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _offset)   \
-  do {                                                                      \
-    if (UNLIKELY(_is_exception_pending)) {                                  \
-      HANDLE_PENDING_EXCEPTION();                                           \
-    } else {                                                                \
-      ADVANCE(_offset);                                                     \
-    }                                                                       \
-  } while (false)
-
-#define UPDATE_HANDLER_TABLE() \
-  currentHandlersTable = handlersTable[ \
-      Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()]
-
-#define BRANCH_INSTRUMENTATION(offset)                                                          \
-  do {                                                                                          \
-    if (UNLIKELY(instrumentation->HasBranchListeners())) {                                      \
-      instrumentation->Branch(self, method, dex_pc, offset);                                    \
-    }                                                                                           \
-    JValue result;                                                                              \
-    if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) {           \
-      return result;                                                                            \
-    }                                                                                           \
-  } while (false)
-
-#define HOTNESS_UPDATE()                                                                       \
-  do {                                                                                         \
-    if (jit != nullptr) {                                                                      \
-      jit->AddSamples(self, method, 1, /*with_backedges*/ true);                               \
-    }                                                                                          \
-  } while (false)
-
-#define UNREACHABLE_CODE_CHECK()                \
-  do {                                          \
-    if (kIsDebugBuild) {                        \
-      LOG(FATAL) << "We should not be here !";  \
-      UNREACHABLE();                            \
-    }                                           \
-  } while (false)
-
-#define HANDLE_INSTRUCTION_START(opcode) op_##opcode:  // NOLINT(whitespace/labels)
-#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()
-
-#define HANDLE_MONITOR_CHECKS()                                                                   \
-  if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {                       \
-    HANDLE_PENDING_EXCEPTION();                                                                   \
-  }
-
-/**
- * Interpreter based on computed goto tables.
- *
- * Each instruction is associated to a handler. This handler is responsible for executing the
- * instruction and jump to the next instruction's handler.
- * In order to limit the cost of instrumentation, we have two handler tables:
- * - the "main" handler table: it contains handlers for normal execution of each instruction without
- * handling of instrumentation.
- * - the "alternative" handler table: it contains alternative handlers which first handle
- * instrumentation before jumping to the corresponding "normal" instruction's handler.
- *
- * When instrumentation is active, the interpreter uses the "alternative" handler table. Otherwise
- * it uses the "main" handler table.
- *
- * The current handler table is the handler table being used by the interpreter. It is updated:
- * - on backward branch (goto, if and switch instructions)
- * - after invoke
- * - when an exception is thrown.
- * This allows to support an attaching debugger to an already running application for instance.
- *
- * For a fast handler table update, handler tables are stored in an array of handler tables. Each
- * handler table is represented by the InterpreterHandlerTable enum which allows to associate it
- * to an index in this array of handler tables ((see Instrumentation::GetInterpreterHandlerTable).
- *
- * Here's the current layout of this array of handler tables:
- *
- * ---------------------+---------------+
- *                      |     NOP       | (handler for NOP instruction)
- *                      +---------------+
- *       "main"         |     MOVE      | (handler for MOVE instruction)
- *    handler table     +---------------+
- *                      |      ...      |
- *                      +---------------+
- *                      |   UNUSED_FF   | (handler for UNUSED_FF instruction)
- * ---------------------+---------------+
- *                      |     NOP       | (alternative handler for NOP instruction)
- *                      +---------------+
- *    "alternative"     |     MOVE      | (alternative handler for MOVE instruction)
- *    handler table     +---------------+
- *                      |      ...      |
- *                      +---------------+
- *                      |   UNUSED_FF   | (alternative handler for UNUSED_FF instruction)
- * ---------------------+---------------+
- *
- */
-template<bool do_access_check, bool transaction_active>
-JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame,
-                       JValue result_register) {
-  // Define handler tables:
-  // - The main handler table contains execution handlers for each instruction.
-  // - The alternative handler table contains prelude handlers which check for thread suspend and
-  //   manage instrumentation before jumping to the execution handler.
-  static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
-    {
-    // Main handler table.
-#define INSTRUCTION_HANDLER(o, code, n, f, i, a, v) &&op_##code,
-#include "dex_instruction_list.h"
-      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_HANDLER
-    }, {
-    // Alternative handler table.
-#define INSTRUCTION_HANDLER(o, code, n, f, i, a, v) &&alt_op_##code,
-#include "dex_instruction_list.h"
-      DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUCTION_HANDLER
-    }
-  };
-
-  constexpr bool do_assignability_check = do_access_check;
-  if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
-    LOG(FATAL) << "Invalid shadow frame for interpreter use";
-    return JValue();
-  }
-  self->VerifyStack();
-
-  uint32_t dex_pc = shadow_frame.GetDexPC();
-  const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
-  uint16_t inst_data;
-  const void* const* currentHandlersTable;
-  UPDATE_HANDLER_TABLE();
-  const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
-  ArtMethod* method = shadow_frame.GetMethod();
-  jit::Jit* jit = Runtime::Current()->GetJit();
-
-  // Jump to first instruction.
-  ADVANCE(0);
-  UNREACHABLE_CODE_CHECK();
-
-  HANDLE_INSTRUCTION_START(NOP)
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE)
-    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_FROM16)
-    shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_16)
-    shadow_frame.SetVReg(inst->VRegA_32x(),
-                         shadow_frame.GetVReg(inst->VRegB_32x()));
-    ADVANCE(3);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_WIDE)
-    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_WIDE_FROM16)
-    shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_22x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_WIDE_16)
-    shadow_frame.SetVRegLong(inst->VRegA_32x(),
-                             shadow_frame.GetVRegLong(inst->VRegB_32x()));
-    ADVANCE(3);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_OBJECT)
-    shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
-                                  shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_OBJECT_FROM16)
-    shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
-                                  shadow_frame.GetVRegReference(inst->VRegB_22x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_OBJECT_16)
-    shadow_frame.SetVRegReference(inst->VRegA_32x(),
-                                  shadow_frame.GetVRegReference(inst->VRegB_32x()));
-    ADVANCE(3);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_RESULT)
-    shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_RESULT_WIDE)
-    shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_RESULT_OBJECT)
-    shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) {
-    Throwable* exception = self->GetException();
-    DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
-    shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
-    self->ClearException();
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RETURN_VOID_NO_BARRIER) {
-    JValue result;
-    self->AllowThreadSuspension();
-    HANDLE_MONITOR_CHECKS();
-    if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-      instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                       shadow_frame.GetMethod(), dex_pc,
-                                       result);
-    }
-    return result;
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RETURN_VOID) {
-    QuasiAtomic::ThreadFenceForConstructor();
-    JValue result;
-    self->AllowThreadSuspension();
-    HANDLE_MONITOR_CHECKS();
-    if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-      instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                       shadow_frame.GetMethod(), dex_pc,
-                                       result);
-    }
-    return result;
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RETURN) {
-    JValue result;
-    result.SetJ(0);
-    result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
-    self->AllowThreadSuspension();
-    HANDLE_MONITOR_CHECKS();
-    if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-      instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                       shadow_frame.GetMethod(), dex_pc,
-                                       result);
-    }
-    return result;
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RETURN_WIDE) {
-    JValue result;
-    result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
-    self->AllowThreadSuspension();
-    HANDLE_MONITOR_CHECKS();
-    if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-      instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                       shadow_frame.GetMethod(), dex_pc,
-                                       result);
-    }
-    return result;
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RETURN_OBJECT) {
-    JValue result;
-    self->AllowThreadSuspension();
-    HANDLE_MONITOR_CHECKS();
-    const uint8_t vreg_index = inst->VRegA_11x(inst_data);
-    Object* obj_result = shadow_frame.GetVRegReference(vreg_index);
-    if (do_assignability_check && obj_result != nullptr) {
-      size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-      Class* return_type = shadow_frame.GetMethod()->GetReturnType(true /* resolve */,
-                                                                   pointer_size);
-      obj_result = shadow_frame.GetVRegReference(vreg_index);
-      if (return_type == nullptr) {
-        // Return the pending exception.
-        HANDLE_PENDING_EXCEPTION();
-      }
-      if (!obj_result->VerifierInstanceOf(return_type)) {
-        // This should never happen.
-        std::string temp1, temp2;
-        self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
-                                 "Returning '%s' that is not instance of return type '%s'",
-                                 obj_result->GetClass()->GetDescriptor(&temp1),
-                                 return_type->GetDescriptor(&temp2));
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-    result.SetL(obj_result);
-    if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-      instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                       shadow_frame.GetMethod(), dex_pc,
-                                       result);
-    }
-    return result;
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_4) {
-    uint32_t dst = inst->VRegA_11n(inst_data);
-    int32_t val = inst->VRegB_11n(inst_data);
-    shadow_frame.SetVReg(dst, val);
-    if (val == 0) {
-      shadow_frame.SetVRegReference(dst, nullptr);
-    }
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_16) {
-    uint32_t dst = inst->VRegA_21s(inst_data);
-    int32_t val = inst->VRegB_21s();
-    shadow_frame.SetVReg(dst, val);
-    if (val == 0) {
-      shadow_frame.SetVRegReference(dst, nullptr);
-    }
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST) {
-    uint32_t dst = inst->VRegA_31i(inst_data);
-    int32_t val = inst->VRegB_31i();
-    shadow_frame.SetVReg(dst, val);
-    if (val == 0) {
-      shadow_frame.SetVRegReference(dst, nullptr);
-    }
-    ADVANCE(3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_HIGH16) {
-    uint32_t dst = inst->VRegA_21h(inst_data);
-    int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
-    shadow_frame.SetVReg(dst, val);
-    if (val == 0) {
-      shadow_frame.SetVRegReference(dst, nullptr);
-    }
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_WIDE_16)
-    shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_WIDE_32)
-    shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
-    ADVANCE(3);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_WIDE)
-    shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
-    ADVANCE(5);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_WIDE_HIGH16)
-    shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
-                             static_cast<uint64_t>(inst->VRegB_21h()) << 48);
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_STRING) {
-    String* s = ResolveString(self, shadow_frame, inst->VRegB_21c());
-    if (UNLIKELY(s == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_STRING_JUMBO) {
-    String* s = ResolveString(self, shadow_frame, inst->VRegB_31c());
-    if (UNLIKELY(s == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
-      ADVANCE(3);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CONST_CLASS) {
-    Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
-                                      self, false, do_access_check);
-    if (UNLIKELY(c == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MONITOR_ENTER) {
-    Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
-    if (UNLIKELY(obj == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      DoMonitorEnter<do_access_check>(self, &shadow_frame, obj);
-      POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MONITOR_EXIT) {
-    Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
-    if (UNLIKELY(obj == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      DoMonitorExit<do_access_check>(self, &shadow_frame, obj);
-      POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CHECK_CAST) {
-    Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
-                                      self, false, do_access_check);
-    if (UNLIKELY(c == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
-      if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
-        ThrowClassCastException(c, obj->GetClass());
-        HANDLE_PENDING_EXCEPTION();
-      } else {
-        ADVANCE(2);
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INSTANCE_OF) {
-    Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
-                                      self, false, do_access_check);
-    if (UNLIKELY(c == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
-      shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ARRAY_LENGTH) {
-    Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
-    if (UNLIKELY(array == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
-      ADVANCE(1);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NEW_INSTANCE) {
-    Object* obj = nullptr;
-    Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
-                                      self, false, do_access_check);
-    if (LIKELY(c != nullptr)) {
-      if (UNLIKELY(c->IsStringClass())) {
-        gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
-        obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
-      } else {
-        obj = AllocObjectFromCode<do_access_check, true>(
-            inst->VRegB_21c(), shadow_frame.GetMethod(), self,
-            Runtime::Current()->GetHeap()->GetCurrentAllocator());
-      }
-    }
-    if (UNLIKELY(obj == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      obj->GetClass()->AssertInitializedOrInitializingInThread(self);
-      // Don't allow finalizable objects to be allocated during a transaction since these can't be
-      // finalized without a started runtime.
-      if (transaction_active && obj->GetClass()->IsFinalizable()) {
-        AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
-                          PrettyTypeOf(obj).c_str());
-        HANDLE_PENDING_EXCEPTION();
-      }
-      shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NEW_ARRAY) {
-    int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
-    Object* obj = AllocArrayFromCode<do_access_check, true>(
-        inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
-        Runtime::Current()->GetHeap()->GetCurrentAllocator());
-    if (UNLIKELY(obj == nullptr)) {
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) {
-    bool success =
-        DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame,
-                                                                     self, &result_register);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) {
-    bool success =
-        DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
-                                                                    self, &result_register);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(FILL_ARRAY_DATA) {
-    Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
-    const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
-    const Instruction::ArrayDataPayload* payload =
-        reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
-    bool success = FillArrayData(obj, payload);
-    if (transaction_active && success) {
-      RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
-    }
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(THROW) {
-    Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
-    if (UNLIKELY(exception == nullptr)) {
-      ThrowNullPointerException("throw with null exception");
-    } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
-      // This should never happen.
-      std::string temp;
-      self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
-                               "Throwing '%s' that is not instance of Throwable",
-                               exception->GetClass()->GetDescriptor(&temp));
-    } else {
-      self->SetException(exception->AsThrowable());
-    }
-    HANDLE_PENDING_EXCEPTION();
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(GOTO) {
-    int8_t offset = inst->VRegA_10t(inst_data);
-    BRANCH_INSTRUMENTATION(offset);
-    if (IsBackwardBranch(offset)) {
-      HOTNESS_UPDATE();
-      if (UNLIKELY(self->TestAllFlags())) {
-        self->CheckSuspend();
-        UPDATE_HANDLER_TABLE();
-      }
-    }
-    ADVANCE(offset);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(GOTO_16) {
-    int16_t offset = inst->VRegA_20t();
-    BRANCH_INSTRUMENTATION(offset);
-    if (IsBackwardBranch(offset)) {
-      HOTNESS_UPDATE();
-      if (UNLIKELY(self->TestAllFlags())) {
-        self->CheckSuspend();
-        UPDATE_HANDLER_TABLE();
-      }
-    }
-    ADVANCE(offset);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(GOTO_32) {
-    int32_t offset = inst->VRegA_30t();
-    BRANCH_INSTRUMENTATION(offset);
-    if (IsBackwardBranch(offset)) {
-      HOTNESS_UPDATE();
-      if (UNLIKELY(self->TestAllFlags())) {
-        self->CheckSuspend();
-        UPDATE_HANDLER_TABLE();
-      }
-    }
-    ADVANCE(offset);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(PACKED_SWITCH) {
-    int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
-    BRANCH_INSTRUMENTATION(offset);
-    if (IsBackwardBranch(offset)) {
-      HOTNESS_UPDATE();
-      if (UNLIKELY(self->TestAllFlags())) {
-        self->CheckSuspend();
-        UPDATE_HANDLER_TABLE();
-      }
-    }
-    ADVANCE(offset);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPARSE_SWITCH) {
-    int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
-    BRANCH_INSTRUMENTATION(offset);
-    if (IsBackwardBranch(offset)) {
-      HOTNESS_UPDATE();
-      if (UNLIKELY(self->TestAllFlags())) {
-        self->CheckSuspend();
-        UPDATE_HANDLER_TABLE();
-      }
-    }
-    ADVANCE(offset);
-  }
-  HANDLE_INSTRUCTION_END();
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wfloat-equal"
-#endif
-
-  HANDLE_INSTRUCTION_START(CMPL_FLOAT) {
-    float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
-    float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
-    int32_t result;
-    if (val1 > val2) {
-      result = 1;
-    } else if (val1 == val2) {
-      result = 0;
-    } else {
-      result = -1;
-    }
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CMPG_FLOAT) {
-    float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
-    float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
-    int32_t result;
-    if (val1 < val2) {
-      result = -1;
-    } else if (val1 == val2) {
-      result = 0;
-    } else {
-      result = 1;
-    }
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CMPL_DOUBLE) {
-    double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
-    double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
-    int32_t result;
-    if (val1 > val2) {
-      result = 1;
-    } else if (val1 == val2) {
-      result = 0;
-    } else {
-      result = -1;
-    }
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(CMPG_DOUBLE) {
-    double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
-    double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
-    int32_t result;
-    if (val1 < val2) {
-      result = -1;
-    } else if (val1 == val2) {
-      result = 0;
-    } else {
-      result = 1;
-    }
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-  HANDLE_INSTRUCTION_START(CMP_LONG) {
-    int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
-    int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
-    int32_t result;
-    if (val1 > val2) {
-      result = 1;
-    } else if (val1 == val2) {
-      result = 0;
-    } else {
-      result = -1;
-    }
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
-    ADVANCE(2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_EQ) {
-    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-      int16_t offset = inst->VRegC_22t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_NE) {
-    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) !=
-        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-      int16_t offset = inst->VRegC_22t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_LT) {
-    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <
-        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-      int16_t offset = inst->VRegC_22t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_GE) {
-    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >=
-        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-      int16_t offset = inst->VRegC_22t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_GT) {
-    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >
-    shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-      int16_t offset = inst->VRegC_22t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_LE) {
-    if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <=
-        shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
-      int16_t offset = inst->VRegC_22t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_EQZ) {
-    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
-      int16_t offset = inst->VRegB_21t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_NEZ) {
-    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
-      int16_t offset = inst->VRegB_21t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_LTZ) {
-    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
-      int16_t offset = inst->VRegB_21t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_GEZ) {
-    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
-      int16_t offset = inst->VRegB_21t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_GTZ) {
-    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
-      int16_t offset = inst->VRegB_21t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IF_LEZ)  {
-    if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
-      int16_t offset = inst->VRegB_21t();
-      BRANCH_INSTRUMENTATION(offset);
-      if (IsBackwardBranch(offset)) {
-        HOTNESS_UPDATE();
-        if (UNLIKELY(self->TestAllFlags())) {
-          self->CheckSuspend();
-          UPDATE_HANDLER_TABLE();
-        }
-      }
-      ADVANCE(offset);
-    } else {
-      BRANCH_INSTRUMENTATION(2);
-      ADVANCE(2);
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET_BOOLEAN) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      BooleanArray* array = a->AsBooleanArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET_BYTE) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      ByteArray* array = a->AsByteArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET_CHAR) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      CharArray* array = a->AsCharArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET_SHORT) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      ShortArray* array = a->AsShortArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      DCHECK(a->IsIntArray() || a->IsFloatArray()) << PrettyTypeOf(a);
-      auto* array = down_cast<IntArray*>(a);
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET_WIDE)  {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      DCHECK(a->IsLongArray() || a->IsDoubleArray()) << PrettyTypeOf(a);
-      auto* array = down_cast<LongArray*>(a);
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AGET_OBJECT) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      ObjectArray<Object>* array = a->AsObjectArray<Object>();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT_BOOLEAN) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      BooleanArray* array = a->AsBooleanArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT_BYTE) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      ByteArray* array = a->AsByteArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT_CHAR) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      CharArray* array = a->AsCharArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT_SHORT) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      ShortArray* array = a->AsShortArray();
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      DCHECK(a->IsIntArray() || a->IsFloatArray()) << PrettyTypeOf(a);
-      auto* array = down_cast<IntArray*>(a);
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT_WIDE) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      DCHECK(a->IsLongArray() || a->IsDoubleArray()) << PrettyTypeOf(a);
-      auto* array = down_cast<LongArray*>(a);
-      if (LIKELY(array->CheckIsValidIndex(index))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(APUT_OBJECT) {
-    Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
-    if (UNLIKELY(a == nullptr)) {
-      ThrowNullPointerExceptionFromInterpreter();
-      HANDLE_PENDING_EXCEPTION();
-    } else {
-      int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
-      Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
-      ObjectArray<Object>* array = a->AsObjectArray<Object>();
-      if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) {
-        array->SetWithoutChecks<transaction_active>(index, val);
-        ADVANCE(2);
-      } else {
-        HANDLE_PENDING_EXCEPTION();
-      }
-    }
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_BOOLEAN) {
-    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_BYTE) {
-    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_CHAR) {
-    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_SHORT) {
-    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET) {
-    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_WIDE) {
-    bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_OBJECT) {
-    bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_BOOLEAN_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_BYTE_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_CHAR_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_SHORT_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_WIDE_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IGET_OBJECT_QUICK) {
-    bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET_BOOLEAN) {
-    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET_BYTE) {
-    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET_CHAR) {
-    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET_SHORT) {
-    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET) {
-    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET_WIDE) {
-    bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SGET_OBJECT) {
-    bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(
-        self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_BYTE) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_CHAR) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_SHORT) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_WIDE) {
-    bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_OBJECT) {
-    bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_BOOLEAN_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_BYTE_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_CHAR_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_SHORT_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) {
-    bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(
-        shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT_BYTE) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT_CHAR) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT_SHORT) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT_WIDE) {
-    bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SPUT_OBJECT) {
-    bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check,
-        transaction_active>(self, shadow_frame, inst, inst_data);
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL) {
-    bool success = DoInvoke<kVirtual, false, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE) {
-    bool success = DoInvoke<kVirtual, true, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_SUPER) {
-    bool success = DoInvoke<kSuper, false, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_SUPER_RANGE) {
-    bool success = DoInvoke<kSuper, true, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_DIRECT) {
-    bool success = DoInvoke<kDirect, false, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_DIRECT_RANGE) {
-    bool success = DoInvoke<kDirect, true, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_INTERFACE) {
-    bool success = DoInvoke<kInterface, false, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_INTERFACE_RANGE) {
-    bool success = DoInvoke<kInterface, true, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_STATIC) {
-    bool success = DoInvoke<kStatic, false, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_STATIC_RANGE) {
-    bool success = DoInvoke<kStatic, true, do_access_check>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_QUICK) {
-    bool success = DoInvokeVirtualQuick<false>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE_QUICK) {
-    bool success = DoInvokeVirtualQuick<true>(
-        self, shadow_frame, inst, inst_data, &result_register);
-    UPDATE_HANDLER_TABLE();
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NEG_INT)
-    shadow_frame.SetVReg(
-        inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NOT_INT)
-    shadow_frame.SetVReg(
-        inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NEG_LONG)
-    shadow_frame.SetVRegLong(
-        inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NOT_LONG)
-    shadow_frame.SetVRegLong(
-        inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NEG_FLOAT)
-    shadow_frame.SetVRegFloat(
-        inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(NEG_DOUBLE)
-    shadow_frame.SetVRegDouble(
-        inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INT_TO_LONG)
-    shadow_frame.SetVRegLong(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INT_TO_FLOAT)
-    shadow_frame.SetVRegFloat(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INT_TO_DOUBLE)
-    shadow_frame.SetVRegDouble(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(LONG_TO_INT)
-    shadow_frame.SetVReg(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(LONG_TO_FLOAT)
-    shadow_frame.SetVRegFloat(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(LONG_TO_DOUBLE)
-    shadow_frame.SetVRegDouble(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(FLOAT_TO_INT) {
-    float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
-    int32_t result = art_float_to_integral<int32_t, float>(val);
-    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(FLOAT_TO_LONG) {
-    float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
-    int64_t result = art_float_to_integral<int64_t, float>(val);
-    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(FLOAT_TO_DOUBLE)
-    shadow_frame.SetVRegDouble(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DOUBLE_TO_INT) {
-    double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
-    int32_t result = art_float_to_integral<int32_t, double>(val);
-    shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DOUBLE_TO_LONG) {
-    double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
-    int64_t result = art_float_to_integral<int64_t, double>(val);
-    shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DOUBLE_TO_FLOAT)
-    shadow_frame.SetVRegFloat(
-        inst->VRegA_12x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INT_TO_BYTE)
-    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
-                         static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INT_TO_CHAR)
-    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
-                         static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(INT_TO_SHORT)
-    shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
-                         static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
-                                 shadow_frame.GetVReg(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
-                                 shadow_frame.GetVReg(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
-                                 shadow_frame.GetVReg(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_INT) {
-    bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
-                               shadow_frame.GetVReg(inst->VRegB_23x()),
-                               shadow_frame.GetVReg(inst->VRegC_23x()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_INT) {
-    bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
-                                  shadow_frame.GetVReg(inst->VRegB_23x()),
-                                  shadow_frame.GetVReg(inst->VRegC_23x()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHL_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) <<
-                         (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHR_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) >>
-                         (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(USHR_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
-                         (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AND_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) &
-                         shadow_frame.GetVReg(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(OR_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) |
-                         shadow_frame.GetVReg(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(XOR_INT)
-    shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_23x()) ^
-                         shadow_frame.GetVReg(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                     shadow_frame.GetVRegLong(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_LONG) {
-    bool success = DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
-                                shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                shadow_frame.GetVRegLong(inst->VRegC_23x()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_LONG) {
-    bool success = DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
-                                   shadow_frame.GetVRegLong(inst->VRegB_23x()),
-                                   shadow_frame.GetVRegLong(inst->VRegC_23x()));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AND_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) &
-                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(OR_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) |
-                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(XOR_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
-                             shadow_frame.GetVRegLong(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHL_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
-                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHR_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
-                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(USHR_LONG)
-    shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
-                             static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
-                             (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_FLOAT)
-    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
-                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_FLOAT)
-    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
-                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_FLOAT)
-    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
-                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_FLOAT)
-    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                              shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
-                              shadow_frame.GetVRegFloat(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_FLOAT)
-    shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
-                              fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
-                                    shadow_frame.GetVRegFloat(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_DOUBLE)
-    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
-                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_DOUBLE)
-    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
-                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_DOUBLE)
-    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
-                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_DOUBLE)
-    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                               shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
-                               shadow_frame.GetVRegDouble(inst->VRegC_23x()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_DOUBLE)
-    shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
-                               fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
-                                    shadow_frame.GetVRegDouble(inst->VRegC_23x())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         SafeAdd(shadow_frame.GetVReg(vregA),
-                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         SafeSub(shadow_frame.GetVReg(vregA),
-                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         SafeMul(shadow_frame.GetVReg(vregA),
-                                 shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
-                               shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
-                                  shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHL_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) <<
-                         (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHR_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) >>
-                         (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(USHR_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
-                         (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AND_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) &
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(OR_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) |
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(XOR_INT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVReg(vregA,
-                         shadow_frame.GetVReg(vregA) ^
-                         shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             SafeAdd(shadow_frame.GetVRegLong(vregA),
-                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             SafeSub(shadow_frame.GetVRegLong(vregA),
-                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             SafeMul(shadow_frame.GetVRegLong(vregA),
-                                     shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    bool success = DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
-                                shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    bool success = DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
-                                   shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AND_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) &
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(OR_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) |
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(XOR_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) ^
-                             shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHL_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) <<
-                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHR_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             shadow_frame.GetVRegLong(vregA) >>
-                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(USHR_LONG_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegLong(vregA,
-                             static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
-                             (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_FLOAT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegFloat(vregA,
-                              shadow_frame.GetVRegFloat(vregA) +
-                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_FLOAT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegFloat(vregA,
-                              shadow_frame.GetVRegFloat(vregA) -
-                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_FLOAT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegFloat(vregA,
-                              shadow_frame.GetVRegFloat(vregA) *
-                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_FLOAT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegFloat(vregA,
-                              shadow_frame.GetVRegFloat(vregA) /
-                              shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_FLOAT_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegFloat(vregA,
-                              fmodf(shadow_frame.GetVRegFloat(vregA),
-                                    shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_DOUBLE_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegDouble(vregA,
-                               shadow_frame.GetVRegDouble(vregA) +
-                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SUB_DOUBLE_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegDouble(vregA,
-                               shadow_frame.GetVRegDouble(vregA) -
-                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_DOUBLE_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegDouble(vregA,
-                               shadow_frame.GetVRegDouble(vregA) *
-                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_DOUBLE_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegDouble(vregA,
-                               shadow_frame.GetVRegDouble(vregA) /
-                               shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_DOUBLE_2ADDR) {
-    uint32_t vregA = inst->VRegA_12x(inst_data);
-    shadow_frame.SetVRegDouble(vregA,
-                               fmod(shadow_frame.GetVRegDouble(vregA),
-                                    shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
-    ADVANCE(1);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_INT_LIT16)
-    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-                                 inst->VRegC_22s()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RSUB_INT)
-    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         SafeSub(inst->VRegC_22s(),
-                                 shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_INT_LIT16)
-    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-                                 inst->VRegC_22s()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_INT_LIT16) {
-    bool success = DoIntDivide(
-        shadow_frame, inst->VRegA_22s(inst_data), shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-        inst->VRegC_22s());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_INT_LIT16) {
-    bool success = DoIntRemainder(
-        shadow_frame, inst->VRegA_22s(inst_data), shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
-        inst->VRegC_22s());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AND_INT_LIT16)
-    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
-                         inst->VRegC_22s());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(OR_INT_LIT16)
-    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
-                         inst->VRegC_22s());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(XOR_INT_LIT16)
-    shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
-                         inst->VRegC_22s());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(ADD_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()),
-                                 inst->VRegC_22b()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(RSUB_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         SafeSub(inst->VRegC_22b(),
-                                 shadow_frame.GetVReg(inst->VRegB_22b())));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(MUL_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()),
-                                 inst->VRegC_22b()));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(DIV_INT_LIT8) {
-    bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
-                               shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(REM_INT_LIT8) {
-    bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
-                                  shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
-    POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
-  }
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(AND_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) &
-                         inst->VRegC_22b());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(OR_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) |
-                         inst->VRegC_22b());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(XOR_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) ^
-                         inst->VRegC_22b());
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHL_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) <<
-                         (inst->VRegC_22b() & 0x1f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(SHR_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         shadow_frame.GetVReg(inst->VRegB_22b()) >>
-                         (inst->VRegC_22b() & 0x1f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(USHR_INT_LIT8)
-    shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
-                         static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
-                         (inst->VRegC_22b() & 0x1f));
-    ADVANCE(2);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_3E)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_3F)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_40)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_41)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_42)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_43)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_79)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_7A)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F3)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F4)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F5)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F6)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F7)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F8)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F9)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_FA)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_FB)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_FC)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_FD)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_FE)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_FF)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  exception_pending_label: {
-    CHECK(self->IsExceptionPending());
-    if (UNLIKELY(self->TestAllFlags())) {
-      self->CheckSuspend();
-      UPDATE_HANDLER_TABLE();
-    }
-    uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc,
-                                                                  instrumentation);
-    if (found_dex_pc == DexFile::kDexNoIndex) {
-      // Structured locking is to be enforced for abnormal termination, too.
-      DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);
-      return JValue(); /* Handled in caller. */
-    } else {
-      int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc);
-      ADVANCE(displacement);
-    }
-  }
-
-// Create alternative instruction handlers dedicated to instrumentation.
-// Return instructions must not call Instrumentation::DexPcMovedEvent since they already call
-// Instrumentation::MethodExited. This is to avoid posting debugger events twice for this location.
-// Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The
-// compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to
-// a constant condition that would remove the "if" statement so the test is free.
-#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, i, a, v)                        \
-  alt_op_##code: {                                                                            \
-    if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                     \
-      Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);                 \
-      instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc);  \
-    }                                                                                         \
-    UPDATE_HANDLER_TABLE();                                                                   \
-    goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code];               \
-  }
-#include "dex_instruction_list.h"
-      DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)
-#undef DEX_INSTRUCTION_LIST
-#undef INSTRUMENTATION_INSTRUCTION_HANDLER
-}  // NOLINT(readability/fn_size)
-
-// Explicit definitions of ExecuteGotoImpl.
-template HOT_ATTR
-JValue ExecuteGotoImpl<true, false>(Thread* self, const DexFile::CodeItem* code_item,
-                                    ShadowFrame& shadow_frame, JValue result_register);
-template HOT_ATTR
-JValue ExecuteGotoImpl<false, false>(Thread* self, const DexFile::CodeItem* code_item,
-                                     ShadowFrame& shadow_frame, JValue result_register);
-template
-JValue ExecuteGotoImpl<true, true>(Thread* self, const DexFile::CodeItem* code_item,
-                                   ShadowFrame& shadow_frame, JValue result_register);
-template
-JValue ExecuteGotoImpl<false, true>(Thread* self, const DexFile::CodeItem* code_item,
-                                    ShadowFrame& shadow_frame, JValue result_register);
-
-#else
-
-template<bool do_access_check, bool transaction_active>
-JValue ExecuteGotoImpl(Thread*, const DexFile::CodeItem*, ShadowFrame&, JValue) {
-  LOG(FATAL) << "UNREACHABLE";
-  UNREACHABLE();
-}
-// Explicit definitions of ExecuteGotoImpl.
-template<>
-JValue ExecuteGotoImpl<true, false>(Thread* self, const DexFile::CodeItem* code_item,
-                                    ShadowFrame& shadow_frame, JValue result_register);
-template<>
-JValue ExecuteGotoImpl<false, false>(Thread* self, const DexFile::CodeItem* code_item,
-                                     ShadowFrame& shadow_frame, JValue result_register);
-template<>
-JValue ExecuteGotoImpl<true, true>(Thread* self,  const DexFile::CodeItem* code_item,
-                                   ShadowFrame& shadow_frame, JValue result_register);
-template<>
-JValue ExecuteGotoImpl<false, true>(Thread* self, const DexFile::CodeItem* code_item,
-                                    ShadowFrame& shadow_frame, JValue result_register);
-#endif
-
-}  // namespace interpreter
-}  // namespace art
diff --git a/runtime/interpreter/interpreter_goto_table_impl.h b/runtime/interpreter/interpreter_goto_table_impl.h
deleted file mode 100644
index c54746d..0000000
--- a/runtime/interpreter/interpreter_goto_table_impl.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_GOTO_TABLE_IMPL_H_
-#define ART_RUNTIME_INTERPRETER_INTERPRETER_GOTO_TABLE_IMPL_H_
-
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "dex_file.h"
-#include "jvalue.h"
-
-namespace art {
-
-class ShadowFrame;
-class Thread;
-
-namespace interpreter {
-
-template<bool do_access_check, bool transaction_active>
-JValue ExecuteGotoImpl(Thread* self,
-                       const DexFile::CodeItem* code_item,
-                       ShadowFrame& shadow_frame,
-                       JValue result_register) REQUIRES_SHARED(Locks::mutator_lock_);
-
-}  // namespace interpreter
-}  // namespace art
-
-#endif  // ART_RUNTIME_INTERPRETER_INTERPRETER_GOTO_TABLE_IMPL_H_
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index d1d8caa..5a5f717 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -371,8 +371,21 @@
     // class rather than the declaring class itself.
     DexCache* referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
     uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
-    // The referenced class has already been resolved with the field, get it from the dex cache.
-    Class* dex_access_to = referrer_dex_cache->GetResolvedType(class_idx);
+    // The referenced class has already been resolved with the field, but may not be in the dex
+    // cache. Using ResolveType here without handles in the caller should be safe since there
+    // should be no thread suspension due to the class being resolved.
+    // TODO: Clean this up to use handles in the caller.
+    Class* dex_access_to;
+    {
+      StackHandleScope<2> hs(Thread::Current());
+      Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer_dex_cache));
+      Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(access_to->GetClassLoader()));
+      dex_access_to = Runtime::Current()->GetClassLinker()->ResolveType(
+          *referrer_dex_cache->GetDexFile(),
+          class_idx,
+          h_dex_cache,
+          h_class_loader);
+    }
     DCHECK(dex_access_to != nullptr);
     if (UNLIKELY(!this->CanAccess(dex_access_to))) {
       if (throw_on_failure) {
@@ -401,8 +414,21 @@
     // class rather than the declaring class itself.
     DexCache* referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
     uint32_t class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
-    // The referenced class has already been resolved with the method, get it from the dex cache.
-    Class* dex_access_to = referrer_dex_cache->GetResolvedType(class_idx);
+    // The referenced class has already been resolved with the method, but may not be in the dex
+    // cache. Using ResolveType here without handles in the caller should be safe since there
+    // should be no thread suspension due to the class being resolved.
+    // TODO: Clean this up to use handles in the caller.
+    Class* dex_access_to;
+    {
+      StackHandleScope<2> hs(Thread::Current());
+      Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer_dex_cache));
+      Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(access_to->GetClassLoader()));
+      dex_access_to = Runtime::Current()->GetClassLinker()->ResolveType(
+          *referrer_dex_cache->GetDexFile(),
+          class_idx,
+          h_dex_cache,
+          h_class_loader);
+    }
     DCHECK(dex_access_to != nullptr);
     if (UNLIKELY(!this->CanAccess(dex_access_to))) {
       if (throw_on_failure) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 99b7769..548087e 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1505,6 +1505,7 @@
   // java.lang.Class
   static GcRoot<Class> java_lang_Class_;
 
+  ART_FRIEND_TEST(DexCacheTest, TestResolvedFieldAccess);  // For ResolvedFieldAccessTest
   friend struct art::ClassOffsets;  // for verifying offset information
   friend class Object;  // For VisitReferences
   DISALLOW_IMPLICIT_CONSTRUCTORS(Class);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index a3071b7..220979a 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -44,13 +44,29 @@
 
 inline void DexCache::SetResolvedString(uint32_t string_idx, mirror::String* resolved) {
   DCHECK_LT(string_idx % NumStrings(), NumStrings());
-  // TODO default transaction support.
-  StringDexCachePair idx_ptr;
-  idx_ptr.string_index = string_idx;
-  idx_ptr.string_pointer = GcRoot<String>(resolved);
-  GetStrings()[string_idx % NumStrings()].store(idx_ptr, std::memory_order_relaxed);
+  GetStrings()[string_idx % NumStrings()].store(
+      StringDexCachePair(resolved, string_idx),
+      std::memory_order_relaxed);
+  Runtime* const runtime = Runtime::Current();
+  if (UNLIKELY(runtime->IsActiveTransaction())) {
+    DCHECK(runtime->IsAotCompiler());
+    runtime->RecordResolveString(this, string_idx);
+  }
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
-  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+  runtime->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
+inline void DexCache::ClearString(uint32_t string_idx) {
+  const uint32_t slot_idx = string_idx % NumStrings();
+  DCHECK(Runtime::Current()->IsAotCompiler());
+  StringDexCacheType* slot = &GetStrings()[slot_idx];
+  // This is racy but should only be called from the transactional interpreter.
+  if (slot->load(std::memory_order_relaxed).string_index == string_idx) {
+    StringDexCachePair cleared(
+        nullptr,
+        StringDexCachePair::InvalidStringIndexForSlot(slot_idx));
+    slot->store(cleared, std::memory_order_relaxed);
+  }
 }
 
 inline Class* DexCache::GetResolvedType(uint32_t type_idx) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index caf00c2..7d4021f 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -56,12 +56,20 @@
   // it's always non-null if the string id branch succeeds (except for the 0th string id).
   // Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
   // the lookup string id == stored id branch.
+  StringDexCachePair(String* string, uint32_t string_idx)
+      : string_pointer(string),
+        string_index(string_idx) {}
+  StringDexCachePair() = default;
+  StringDexCachePair(const StringDexCachePair&) = default;
+  StringDexCachePair& operator=(const StringDexCachePair&) = default;
+
   static void Initialize(StringDexCacheType* strings) {
     mirror::StringDexCachePair first_elem;
     first_elem.string_pointer = GcRoot<String>(nullptr);
-    first_elem.string_index = 1;
+    first_elem.string_index = InvalidStringIndexForSlot(0);
     strings[0].store(first_elem, std::memory_order_relaxed);
   }
+
   static GcRoot<String> LookupString(StringDexCacheType* dex_cache,
                                      uint32_t string_idx,
                                      uint32_t cache_size) {
@@ -71,10 +79,15 @@
     DCHECK(!index_string.string_pointer.IsNull());
     return index_string.string_pointer;
   }
+
+  static uint32_t InvalidStringIndexForSlot(uint32_t slot) {
+    // Since the cache size is a power of two, 0 will always map to slot 0.
+    // Use 1 for slot 0 and 0 for all other slots.
+    return (slot == 0) ? 1u : 0u;
+  }
 };
 using StringDexCacheType = std::atomic<StringDexCachePair>;
 
-
 // C++ mirror of java.lang.DexCache.
 class MANAGED DexCache FINAL : public Object {
  public:
@@ -164,6 +177,10 @@
   void SetResolvedString(uint32_t string_idx, mirror::String* resolved) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Clear a string for a string_idx, used to undo string intern transactions to make sure
+  // the string isn't kept live.
+  void ClearString(uint32_t string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
   Class* GetResolvedType(uint32_t type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetResolvedType(uint32_t type_idx, Class* resolved) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 175997c..43ba362 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -64,5 +64,33 @@
   EXPECT_TRUE(linear_alloc->Contains(klass->GetDexCache()->GetResolvedMethods()));
 }
 
+TEST_F(DexCacheTest, TestResolvedFieldAccess) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader(LoadDex("Packages"));
+  ASSERT_TRUE(jclass_loader != nullptr);
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* const class_linker = runtime->GetClassLinker();
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+  Handle<mirror::Class> klass1 =
+      hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader));
+  ASSERT_TRUE(klass1.Get() != nullptr);
+  Handle<mirror::Class> klass2 =
+      hs.NewHandle(class_linker->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader));
+  ASSERT_TRUE(klass2.Get() != nullptr);
+  EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache());
+
+  EXPECT_NE(klass1->NumStaticFields(), 0u);
+  for (ArtField& field : klass2->GetSFields()) {
+    EXPECT_FALSE((
+        klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false,
+            /*use_referrers_cache*/ false>(klass2.Get(),
+                                           &field,
+                                           field.GetDexFieldIndex(),
+                                           klass1->GetDexCache())));
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/oat.h b/runtime/oat.h
index 7c84fe9..35d0c92 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '8', '6', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '8', '7', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 5752fd9..cbc5d3c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -83,7 +83,8 @@
   virtual ~OatFileBase() {}
 
   template <typename kOatFileBaseSubType>
-  static OatFileBase* OpenOatFile(const std::string& elf_filename,
+  static OatFileBase* OpenOatFile(const std::string& vdex_filename,
+                                  const std::string& elf_filename,
                                   const std::string& location,
                                   uint8_t* requested_base,
                                   uint8_t* oat_file_begin,
@@ -101,6 +102,11 @@
 
   virtual void PreLoad() = 0;
 
+  bool LoadVdex(const std::string& vdex_filename,
+                bool writable,
+                bool low_4gb,
+                std::string* error_msg);
+
   virtual bool Load(const std::string& elf_filename,
                     uint8_t* oat_file_begin,
                     bool writable,
@@ -131,7 +137,8 @@
 };
 
 template <typename kOatFileBaseSubType>
-OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename,
+OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename,
+                                      const std::string& elf_filename,
                                       const std::string& location,
                                       uint8_t* requested_base,
                                       uint8_t* oat_file_begin,
@@ -144,6 +151,10 @@
 
   ret->PreLoad();
 
+  if (kIsVdexEnabled && !ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) {
+    return nullptr;
+  }
+
   if (!ret->Load(elf_filename,
                  oat_file_begin,
                  writable,
@@ -166,6 +177,20 @@
   return ret.release();
 }
 
+bool OatFileBase::LoadVdex(const std::string& vdex_filename,
+                           bool writable,
+                           bool low_4gb,
+                           std::string* error_msg) {
+  vdex_.reset(VdexFile::Open(vdex_filename, writable, low_4gb, error_msg));
+  if (vdex_.get() == nullptr) {
+    *error_msg = StringPrintf("Failed to load vdex file '%s' %s",
+                              vdex_filename.c_str(),
+                              error_msg->c_str());
+    return false;
+  }
+  return true;
+}
+
 bool OatFileBase::ComputeFields(uint8_t* requested_base,
                                 const std::string& file_path,
                                 std::string* error_msg) {
@@ -321,29 +346,29 @@
                                 dex_file_location.c_str());
       return false;
     }
-    if (UNLIKELY(dex_file_offset > Size())) {
+    if (UNLIKELY(dex_file_offset > DexSize())) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
                                     "offset %u > %zu",
                                 GetLocation().c_str(),
                                 i,
                                 dex_file_location.c_str(),
                                 dex_file_offset,
-                                Size());
+                                DexSize());
       return false;
     }
-    if (UNLIKELY(Size() - dex_file_offset < sizeof(DexFile::Header))) {
+    if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
                                     "offset %u of %zu but the size of dex file header is %zu",
                                 GetLocation().c_str(),
                                 i,
                                 dex_file_location.c_str(),
                                 dex_file_offset,
-                                Size(),
+                                DexSize(),
                                 sizeof(DexFile::Header));
       return false;
     }
 
-    const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
+    const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset;
     if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid "
                                     "dex file magic '%s'",
@@ -363,7 +388,7 @@
       return false;
     }
     const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
-    if (Size() - dex_file_offset < header->file_size_) {
+    if (DexSize() - dex_file_offset < header->file_size_) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file "
                                     "offset %u and size %u truncated at %zu",
                                 GetLocation().c_str(),
@@ -371,7 +396,7 @@
                                 dex_file_location.c_str(),
                                 dex_file_offset,
                                 header->file_size_,
-                                Size());
+                                DexSize());
       return false;
     }
 
@@ -942,31 +967,37 @@
       : nullptr;
 }
 
-OatFile* OatFile::Open(const std::string& filename,
-                       const std::string& location,
+OatFile* OatFile::Open(const std::string& oat_filename,
+                       const std::string& oat_location,
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
                        bool low_4gb,
                        const char* abs_dex_location,
                        std::string* error_msg) {
-  ScopedTrace trace("Open oat file " + location);
-  CHECK(!filename.empty()) << location;
-  CheckLocation(location);
+  ScopedTrace trace("Open oat file " + oat_location);
+  CHECK(!oat_filename.empty()) << oat_location;
+  CheckLocation(oat_location);
 
-  // Check that the file even exists, fast-fail.
-  if (!OS::FileExists(filename.c_str())) {
-    *error_msg = StringPrintf("File %s does not exist.", filename.c_str());
+  std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
+
+  // Check that the files even exist, fast-fail.
+  if (kIsVdexEnabled && !OS::FileExists(vdex_filename.c_str())) {
+    *error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str());
+    return nullptr;
+  } else if (!OS::FileExists(oat_filename.c_str())) {
+    *error_msg = StringPrintf("File %s does not exist.", oat_filename.c_str());
     return nullptr;
   }
 
   // Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is
   // disabled.
-  OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(filename,
-                                                                 location,
+  OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(vdex_filename,
+                                                                 oat_filename,
+                                                                 oat_location,
                                                                  requested_base,
                                                                  oat_file_begin,
-                                                                 false,
+                                                                 false /* writable */,
                                                                  executable,
                                                                  low_4gb,
                                                                  abs_dex_location,
@@ -975,7 +1006,7 @@
     return with_dlopen;
   }
   if (kPrintDlOpenErrorMessage) {
-    LOG(ERROR) << "Failed to dlopen: " << filename << " with error " << *error_msg;
+    LOG(ERROR) << "Failed to dlopen: " << oat_filename << " with error " << *error_msg;
   }
   // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
   //
@@ -990,11 +1021,12 @@
   //
   // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually
   // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN.
-  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(filename,
-                                                                location,
+  OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(vdex_filename,
+                                                                oat_filename,
+                                                                oat_location,
                                                                 requested_base,
                                                                 oat_file_begin,
-                                                                false,
+                                                                false /* writable */,
                                                                 executable,
                                                                 low_4gb,
                                                                 abs_dex_location,
@@ -1036,6 +1068,7 @@
 
 OatFile::OatFile(const std::string& location, bool is_executable)
     : location_(location),
+      vdex_(nullptr),
       begin_(nullptr),
       end_(nullptr),
       bss_begin_(nullptr),
@@ -1071,6 +1104,14 @@
   return bss_end_;
 }
 
+const uint8_t* OatFile::DexBegin() const {
+  return kIsVdexEnabled ? vdex_->Begin() : Begin();
+}
+
+const uint8_t* OatFile::DexEnd() const {
+  return kIsVdexEnabled ? vdex_->End() : End();
+}
+
 const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
                                                   const uint32_t* dex_location_checksum,
                                                   std::string* error_msg) const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index f5ab9dc..96e651e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -30,6 +30,7 @@
 #include "oat.h"
 #include "os.h"
 #include "utils.h"
+#include "vdex_file.h"
 
 namespace art {
 
@@ -46,6 +47,14 @@
 }  // namespace collector
 }  // namespace gc
 
+// Runtime representation of the OAT file format which holds compiler output.
+// The class opens an OAT file from storage and maps it to memory, typically with
+// dlopen and provides access to its internal data structures (see OatWriter for
+// for more details about the OAT format).
+// In the process of loading OAT, the class also loads the associated VDEX file
+// with the input DEX files (see VdexFile for details about the VDEX format).
+// The raw DEX data are accessible transparently through the OatDexFile objects.
+
 class OatFile {
  public:
   // Special classpath that skips shared library check.
@@ -240,12 +249,19 @@
     return BssEnd() - BssBegin();
   }
 
+  size_t DexSize() const {
+    return DexEnd() - DexBegin();
+  }
+
   const uint8_t* Begin() const;
   const uint8_t* End() const;
 
   const uint8_t* BssBegin() const;
   const uint8_t* BssEnd() const;
 
+  const uint8_t* DexBegin() const;
+  const uint8_t* DexEnd() const;
+
   // Returns the absolute dex location for the encoded relative dex location.
   //
   // If not null, abs_dex_location is used to resolve the absolute dex
@@ -279,6 +295,9 @@
   // The image will embed this to link its associated oat file.
   const std::string location_;
 
+  // Pointer to the Vdex file with the Dex files for this Oat file.
+  std::unique_ptr<VdexFile> vdex_;
+
   // Pointer to OatHeader.
   const uint8_t* begin_;
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index fe6332d..415f991 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -345,17 +345,6 @@
   return odex_.CompilerFilter();
 }
 
-static std::string ArtFileName(const OatFile* oat_file) {
-  const std::string oat_file_location = oat_file->GetLocation();
-  // Replace extension with .art
-  const size_t last_ext = oat_file_location.find_last_of('.');
-  if (last_ext == std::string::npos) {
-    LOG(ERROR) << "No extension in oat file " << oat_file_location;
-    return std::string();
-  }
-  return oat_file_location.substr(0, last_ext) + ".art";
-}
-
 const std::string* OatFileAssistant::OatFileName() {
   return oat_.Filename();
 }
@@ -565,6 +554,7 @@
     return kUpdateNotAttempted;
   }
   const std::string& oat_file_name = *oat_.Filename();
+  const std::string& vdex_file_name = ReplaceFileExtension(oat_file_name, "vdex");
 
   // dex2oat ignores missing dex files and doesn't report an error.
   // Check explicitly here so we can detect the error properly.
@@ -574,8 +564,22 @@
     return kUpdateNotAttempted;
   }
 
-  std::unique_ptr<File> oat_file;
-  oat_file.reset(OS::CreateEmptyFile(oat_file_name.c_str()));
+  std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_file_name.c_str()));
+  if (vdex_file.get() == nullptr) {
+    *error_msg = "Generation of oat file " + oat_file_name
+      + " not attempted because the vdex file " + vdex_file_name
+      + " could not be opened.";
+    return kUpdateNotAttempted;
+  }
+
+  if (fchmod(vdex_file->Fd(), 0644) != 0) {
+    *error_msg = "Generation of oat file " + oat_file_name
+      + " not attempted because the vdex file " + vdex_file_name
+      + " could not be made world readable.";
+    return kUpdateNotAttempted;
+  }
+
+  std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_file_name.c_str()));
   if (oat_file.get() == nullptr) {
     *error_msg = "Generation of oat file " + oat_file_name
       + " not attempted because the oat file could not be created.";
@@ -591,17 +595,26 @@
 
   std::vector<std::string> args;
   args.push_back("--dex-file=" + dex_location_);
+  args.push_back("--vdex-fd=" + std::to_string(vdex_file->Fd()));
   args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
   args.push_back("--oat-location=" + oat_file_name);
 
   if (!Dex2Oat(args, error_msg)) {
-    // Manually delete the file. This ensures there is no garbage left over if
-    // the process unexpectedly died.
+    // Manually delete the oat and vdex files. This ensures there is no garbage
+    // left over if the process unexpectedly died.
+    vdex_file->Erase();
+    unlink(vdex_file_name.c_str());
     oat_file->Erase();
     unlink(oat_file_name.c_str());
     return kUpdateFailed;
   }
 
+  if (vdex_file->FlushCloseOrErase() != 0) {
+    *error_msg = "Unable to close vdex file " + vdex_file_name;
+    unlink(vdex_file_name.c_str());
+    return kUpdateFailed;
+  }
+
   if (oat_file->FlushCloseOrErase() != 0) {
     *error_msg = "Unable to close oat file " + oat_file_name;
     unlink(oat_file_name.c_str());
@@ -830,7 +843,7 @@
 
 std::unique_ptr<gc::space::ImageSpace> OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
   DCHECK(oat_file != nullptr);
-  std::string art_file = ArtFileName(oat_file);
+  std::string art_file = ReplaceFileExtension(oat_file->GetLocation(), "art");
   if (art_file.empty()) {
     return nullptr;
   }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a365a73..ba12d33 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1929,6 +1929,12 @@
   preinitialization_transaction_->RecordWeakStringRemoval(s);
 }
 
+void Runtime::RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) const {
+  DCHECK(IsAotCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction_->RecordResolveString(dex_cache, string_idx);
+}
+
 void Runtime::SetFaultMessage(const std::string& message) {
   MutexLock mu(Thread::Current(), fault_message_lock_);
   fault_message_ = message;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 44f765a..dc14c04 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -55,8 +55,9 @@
 }  // namespace jit
 
 namespace mirror {
-  class ClassLoader;
   class Array;
+  class ClassLoader;
+  class DexCache;
   template<class T> class ObjectArray;
   template<class T> class PrimitiveArray;
   typedef PrimitiveArray<int8_t> ByteArray;
@@ -508,6 +509,8 @@
       REQUIRES(Locks::intern_table_lock_);
   void RecordWeakStringRemoval(mirror::String* s) const
       REQUIRES(Locks::intern_table_lock_);
+  void RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   void SetFaultMessage(const std::string& message) REQUIRES(!fault_message_lock_);
   // Only read by the signal handler, NO_THREAD_SAFETY_ANALYSIS to prevent lock order violations
diff --git a/runtime/simulator/Android.bp b/runtime/simulator/Android.bp
index ec0b49e..49322fc 100644
--- a/runtime/simulator/Android.bp
+++ b/runtime/simulator/Android.bp
@@ -16,6 +16,9 @@
 
 cc_defaults {
     name: "libart_simulator_defaults",
+    host_supported: true,
+    device_supported: false,
+
     defaults: ["art_defaults"],
     srcs: [
         "code_simulator.cc",
@@ -29,7 +32,7 @@
     include_dirs: ["art/runtime"],
 }
 
-cc_library_host_shared {
+art_cc_library {
     name: "libart-simulator",
     defaults: ["libart_simulator_defaults"],
     shared_libs: [
@@ -38,7 +41,7 @@
     ],
 }
 
-cc_library_host_shared {
+art_cc_library {
     name: "libartd-simulator",
     defaults: [
         "art_debug_defaults",
diff --git a/runtime/simulator/code_simulator_arm64.cc b/runtime/simulator/code_simulator_arm64.cc
index 897d4f5..c7ad1fd 100644
--- a/runtime/simulator/code_simulator_arm64.cc
+++ b/runtime/simulator/code_simulator_arm64.cc
@@ -16,6 +16,8 @@
 
 #include "simulator/code_simulator_arm64.h"
 
+#include "base/logging.h"
+
 using namespace vixl::aarch64;  // NOLINT(build/namespaces)
 
 namespace art {
diff --git a/runtime/stack.cc b/runtime/stack.cc
index ec492ed..3b5360c 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -319,8 +319,19 @@
 bool StackVisitor::GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const {
   const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
 
-  // X86 float registers are 64-bit and the logic below does not apply.
-  DCHECK(!is_float || kRuntimeISA != InstructionSet::kX86);
+  if (kRuntimeISA == InstructionSet::kX86 && is_float) {
+    // X86 float registers are 64-bit and each XMM register is provided as two separate
+    // 32-bit registers by the context.
+    reg = (kind == kDoubleHiVReg) ? (2 * reg + 1) : (2 * reg);
+  }
+
+  // MIPS32 float registers are used as 64-bit (for MIPS32r2 it is pair
+  // F(2n)-F(2n+1), and for MIPS32r6 it is 64-bit register F(2n)). When
+  // accessing upper 32-bits from double, reg + 1 should be used.
+  if ((kRuntimeISA == InstructionSet::kMips) && (kind == kDoubleHiVReg)) {
+    DCHECK_ALIGNED(reg, 2);
+    reg++;
+  }
 
   if (!IsAccessibleRegister(reg, is_float)) {
     return false;
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index d91860b..9f8d981 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -49,13 +49,15 @@
     for (auto it : array_logs_) {
       array_values_count += it.second.Size();
     }
-    size_t string_count = intern_string_logs_.size();
+    size_t intern_string_count = intern_string_logs_.size();
+    size_t resolve_string_count = resolve_string_logs_.size();
     LOG(INFO) << "Transaction::~Transaction"
               << ": objects_count=" << objects_count
               << ", field_values_count=" << field_values_count
               << ", array_count=" << array_count
               << ", array_values_count=" << array_values_count
-              << ", string_count=" << string_count;
+              << ", intern_string_count=" << intern_string_count
+              << ", resolve_string_count=" << resolve_string_count;
   }
 }
 
@@ -165,6 +167,13 @@
   array_log.LogValue(index, value);
 }
 
+void Transaction::RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) {
+  DCHECK(dex_cache != nullptr);
+  DCHECK_LT(string_idx, dex_cache->GetDexFile()->NumStringIds());
+  MutexLock mu(Thread::Current(), log_lock_);
+  resolve_string_logs_.push_back(ResolveStringLog(dex_cache, string_idx));
+}
+
 void Transaction::RecordStrongStringInsertion(mirror::String* s) {
   InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kInsert);
   LogInternedString(log);
@@ -200,6 +209,7 @@
   UndoObjectModifications();
   UndoArrayModifications();
   UndoInternStringTableModifications();
+  UndoResolveStringModifications();
 }
 
 void Transaction::UndoObjectModifications() {
@@ -230,11 +240,19 @@
   intern_string_logs_.clear();
 }
 
+void Transaction::UndoResolveStringModifications() {
+  for (ResolveStringLog& string_log : resolve_string_logs_) {
+    string_log.Undo();
+  }
+  resolve_string_logs_.clear();
+}
+
 void Transaction::VisitRoots(RootVisitor* visitor) {
   MutexLock mu(Thread::Current(), log_lock_);
   VisitObjectLogs(visitor);
   VisitArrayLogs(visitor);
-  VisitStringLogs(visitor);
+  VisitInternStringLogs(visitor);
+  VisitResolveStringLogs(visitor);
 }
 
 void Transaction::VisitObjectLogs(RootVisitor* visitor) {
@@ -292,12 +310,18 @@
   }
 }
 
-void Transaction::VisitStringLogs(RootVisitor* visitor) {
+void Transaction::VisitInternStringLogs(RootVisitor* visitor) {
   for (InternStringLog& log : intern_string_logs_) {
     log.VisitRoots(visitor);
   }
 }
 
+void Transaction::VisitResolveStringLogs(RootVisitor* visitor) {
+  for (ResolveStringLog& log : resolve_string_logs_) {
+    log.VisitRoots(visitor);
+  }
+}
+
 void Transaction::ObjectLog::LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile) {
   LogValue(ObjectLog::kBoolean, offset, value, is_volatile);
 }
@@ -481,6 +505,21 @@
   visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&str_), RootInfo(kRootInternedString));
 }
 
+void Transaction::ResolveStringLog::Undo() {
+  dex_cache_.Read()->ClearString(string_idx_);
+}
+
+Transaction::ResolveStringLog::ResolveStringLog(mirror::DexCache* dex_cache, uint32_t string_idx)
+    : dex_cache_(dex_cache),
+      string_idx_(string_idx) {
+  DCHECK(dex_cache != nullptr);
+  DCHECK_LT(string_idx_, dex_cache->GetDexFile()->NumStringIds());
+}
+
+void Transaction::ResolveStringLog::VisitRoots(RootVisitor* visitor) {
+  dex_cache_.VisitRoot(visitor, RootInfo(kRootVMInternal));
+}
+
 void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
   auto it = array_values_.find(index);
   if (it == array_values_.end()) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index bc9c640..584dfb8 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -32,6 +32,7 @@
 namespace art {
 namespace mirror {
 class Array;
+class DexCache;
 class Object;
 class String;
 }
@@ -95,6 +96,11 @@
       REQUIRES(Locks::intern_table_lock_)
       REQUIRES(!log_lock_);
 
+  // Record resolve string.
+  void RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!log_lock_);
+
   // Abort transaction by undoing all recorded changes.
   void Rollback()
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -192,6 +198,19 @@
     const StringOp string_op_;
   };
 
+  class ResolveStringLog : public ValueObject {
+   public:
+    ResolveStringLog(mirror::DexCache* dex_cache, uint32_t string_idx);
+
+    void Undo() REQUIRES_SHARED(Locks::mutator_lock_);
+
+    void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+   private:
+    GcRoot<mirror::DexCache> dex_cache_;
+    const uint32_t string_idx_;
+  };
+
   void LogInternedString(const InternStringLog& log)
       REQUIRES(Locks::intern_table_lock_)
       REQUIRES(!log_lock_);
@@ -206,6 +225,9 @@
       REQUIRES(Locks::intern_table_lock_)
       REQUIRES(log_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  void UndoResolveStringModifications()
+      REQUIRES(log_lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   void VisitObjectLogs(RootVisitor* visitor)
       REQUIRES(log_lock_)
@@ -213,7 +235,10 @@
   void VisitArrayLogs(RootVisitor* visitor)
       REQUIRES(log_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void VisitStringLogs(RootVisitor* visitor)
+  void VisitInternStringLogs(RootVisitor* visitor)
+      REQUIRES(log_lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  void VisitResolveStringLogs(RootVisitor* visitor)
       REQUIRES(log_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -223,6 +248,7 @@
   std::map<mirror::Object*, ObjectLog> object_logs_ GUARDED_BY(log_lock_);
   std::map<mirror::Array*, ArrayLog> array_logs_  GUARDED_BY(log_lock_);
   std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_);
+  std::list<ResolveStringLog> resolve_string_logs_ GUARDED_BY(log_lock_);
   bool aborted_ GUARDED_BY(log_lock_);
   std::string abort_message_ GUARDED_BY(log_lock_);
 
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 8279a26..82e529c 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -20,11 +20,14 @@
 #include "art_method-inl.h"
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
+#include "dex_file.h"
 #include "mirror/array-inl.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
 
+static const size_t kDexNoIndex = DexFile::kDexNoIndex;  // Make copy to prevent linking errors.
+
 class TransactionTest : public CommonRuntimeTest {
  public:
   // Tests failing class initialization due to native call with transaction rollback.
@@ -482,6 +485,55 @@
   EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr);
 }
 
+// Tests rolling back interned strings and resolved strings.
+TEST_F(TransactionTest, ResolveString) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))));
+  ASSERT_TRUE(class_loader.Get() != nullptr);
+
+  Handle<mirror::Class> h_klass(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$ResolveString;",
+                                            class_loader)));
+  ASSERT_TRUE(h_klass.Get() != nullptr);
+
+  Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(h_klass->GetDexCache()));
+  ASSERT_TRUE(h_dex_cache.Get() != nullptr);
+  const DexFile* const dex_file = h_dex_cache->GetDexFile();
+  ASSERT_TRUE(dex_file != nullptr);
+
+  // Go search the dex file to find the string id of our string.
+  static const char* kResolvedString = "ResolvedString";
+  const DexFile::StringId* string_id = dex_file->FindStringId(kResolvedString);
+  ASSERT_TRUE(string_id != nullptr);
+  uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
+  ASSERT_NE(string_idx, kDexNoIndex);
+  // String should only get resolved by the initializer.
+  EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache) == nullptr);
+  EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr);
+  // Do the transaction, then roll back.
+  Transaction transaction;
+  Runtime::Current()->EnterTransactionMode(&transaction);
+  bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(h_klass->IsInitialized());
+  // Make sure the string got resolved by the transaction.
+  {
+    mirror::String* s = class_linker_->LookupString(*dex_file, string_idx, h_dex_cache);
+    ASSERT_TRUE(s != nullptr);
+    EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString);
+    EXPECT_EQ(s, h_dex_cache->GetResolvedString(string_idx));
+  }
+  Runtime::Current()->ExitTransactionMode();
+  transaction.Rollback();
+  // Check that the string did not stay resolved.
+  EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache) == nullptr);
+  EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr);
+  ASSERT_FALSE(h_klass->IsInitialized());
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
 // Tests successful class initialization without class initializer.
 TEST_F(TransactionTest, EmptyClass) {
   ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 313190c..d48edcf 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1213,6 +1213,15 @@
   return buffer.st_size > 0;
 }
 
+std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension) {
+  const size_t last_ext = filename.find_last_of('.');
+  if (last_ext == std::string::npos) {
+    return filename + "." + new_extension;
+  } else {
+    return filename.substr(0, last_ext + 1) + new_extension;
+  }
+}
+
 std::string PrettyDescriptor(Primitive::Type type) {
   return PrettyDescriptor(Primitive::Descriptor(type));
 }
diff --git a/runtime/utils.h b/runtime/utils.h
index 2389ce7..f3284e8 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -279,6 +279,13 @@
 bool FileExists(const std::string& filename);
 bool FileExistsAndNotEmpty(const std::string& filename);
 
+// Returns `filename` with the text after the last occurrence of '.' replaced with
+// `extension`. If `filename` does not contain a period, returns a string containing `filename`,
+// a period, and `new_extension`.
+// Example: ReplaceFileExtension("foo.bar", "abc") == "foo.abc"
+//          ReplaceFileExtension("foo", "abc") == "foo.abc"
+std::string ReplaceFileExtension(const std::string& filename, const std::string& new_extension);
+
 class VoidFunctor {
  public:
   template <typename A>
@@ -386,6 +393,16 @@
   __builtin___clear_cache(begin, end);
 }
 
+template <typename T>
+constexpr PointerSize ConvertToPointerSize(T any) {
+  if (any == 4 || any == 8) {
+    return static_cast<PointerSize>(any);
+  } else {
+    LOG(FATAL);
+    UNREACHABLE();
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index 4c63156..a85d033 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -55,7 +55,8 @@
 
 template <typename T>
 static constexpr PointerSize GcRootAsPointerSize() {
-  return ConvertToPointerSize(sizeof(GcRoot<T>));
+  static_assert(sizeof(GcRoot<T>) == 4U, "Unexpected GcRoot size");
+  return PointerSize::k32;
 }
 
 inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
new file mode 100644
index 0000000..12bc451
--- /dev/null
+++ b/runtime/vdex_file.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vdex_file.h"
+
+#include <memory>
+
+#include "base/logging.h"
+
+namespace art {
+
+constexpr uint8_t VdexFile::Header::kVdexMagic[4];
+constexpr uint8_t VdexFile::Header::kVdexVersion[4];
+
+bool VdexFile::Header::IsMagicValid() const {
+  return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0);
+}
+
+bool VdexFile::Header::IsVersionValid() const {
+  return (memcmp(version_, kVdexVersion, sizeof(kVdexVersion)) == 0);
+}
+
+VdexFile::Header::Header() {
+  memcpy(magic_, kVdexMagic, sizeof(kVdexMagic));
+  memcpy(version_, kVdexVersion, sizeof(kVdexVersion));
+  DCHECK(IsMagicValid());
+  DCHECK(IsVersionValid());
+}
+
+VdexFile* VdexFile::Open(const std::string& vdex_filename,
+                         bool writable,
+                         bool low_4gb,
+                         std::string* error_msg) {
+  if (!OS::FileExists(vdex_filename.c_str())) {
+    *error_msg = "File " + vdex_filename + " does not exist.";
+    return nullptr;
+  }
+
+  std::unique_ptr<File> vdex_file;
+  if (writable) {
+    vdex_file.reset(OS::OpenFileReadWrite(vdex_filename.c_str()));
+  } else {
+    vdex_file.reset(OS::OpenFileForReading(vdex_filename.c_str()));
+  }
+  if (vdex_file == nullptr) {
+    *error_msg = "Could not open file " + vdex_filename +
+                 (writable ? " for read/write" : "for reading");
+    return nullptr;
+  }
+
+  int64_t vdex_length = vdex_file->GetLength();
+  if (vdex_length == -1) {
+    *error_msg = "Could not read the length of file " + vdex_filename;
+    return nullptr;
+  }
+
+  std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
+                                               writable ? PROT_READ | PROT_WRITE : PROT_READ,
+                                               MAP_SHARED,
+                                               vdex_file->Fd(),
+                                               0 /* start offset */,
+                                               low_4gb,
+                                               vdex_filename.c_str(),
+                                               error_msg));
+  if (mmap == nullptr) {
+    *error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg;
+    return nullptr;
+  }
+
+  *error_msg = "Success";
+  return new VdexFile(vdex_file.release(), mmap.release());
+}
+
+}  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
new file mode 100644
index 0000000..e381eb7
--- /dev/null
+++ b/runtime/vdex_file.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_VDEX_FILE_H_
+#define ART_RUNTIME_VDEX_FILE_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "base/macros.h"
+#include "base/unix_file/fd_file.h"
+#include "mem_map.h"
+#include "os.h"
+
+namespace art {
+
+// VDEX files contain extracted DEX files. The VdexFile class maps the file to
+// memory and provides tools for accessing its individual sections.
+//
+// File format:
+//   VdexFile::Header    fixed-length header
+//
+//   DEX[0]              array of the input DEX files
+//   DEX[1]              the bytecode may have been quickened
+//   ...
+//   DEX[D]
+//
+
+class VdexFile {
+ public:
+  struct Header {
+   public:
+    Header();
+
+    bool IsMagicValid() const;
+    bool IsVersionValid() const;
+
+   private:
+    static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
+    static constexpr uint8_t kVdexVersion[] = { '0', '0', '0', '\0' };
+
+    uint8_t magic_[4];
+    uint8_t version_[4];
+  };
+
+  static VdexFile* Open(const std::string& vdex_filename,
+                        bool writable,
+                        bool low_4gb,
+                        std::string* error_msg);
+
+  const uint8_t* Begin() const { return mmap_->Begin(); }
+  const uint8_t* End() const { return mmap_->End(); }
+  size_t Size() const { return mmap_->Size(); }
+
+ private:
+  VdexFile(File* file, MemMap* mmap) : file_(file), mmap_(mmap) {}
+
+  std::unique_ptr<File> file_;
+  std::unique_ptr<MemMap> mmap_;
+
+  DISALLOW_COPY_AND_ASSIGN(VdexFile);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_VDEX_FILE_H_
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 2c99275..5f5fbc8 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -30,6 +30,7 @@
 namespace art {
 
 jclass WellKnownClasses::com_android_dex_Dex;
+jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
 jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
 jclass WellKnownClasses::dalvik_system_DexFile;
 jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -216,6 +217,8 @@
 
 void WellKnownClasses::Init(JNIEnv* env) {
   com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
+  dalvik_annotation_optimization_CriticalNative =
+      CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
   dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
   dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
   dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 25c9424..ce710ff 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -41,6 +41,7 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   static jclass com_android_dex_Dex;
+  static jclass dalvik_annotation_optimization_CriticalNative;
   static jclass dalvik_annotation_optimization_FastNative;
   static jclass dalvik_system_DexFile;
   static jclass dalvik_system_DexPathList;
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index bb18a70..81be531 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -28,9 +28,11 @@
 static JavaVM* jvm = nullptr;
 
 static jint Java_Main_intFastNativeMethod(JNIEnv*, jclass, jint a, jint b, jint c);
+static jint Java_Main_intCriticalNativeMethod(jint a, jint b, jint c);
 
 static JNINativeMethod sMainMethods[] = {
-  {"intFastNativeMethod", "(III)I", reinterpret_cast<void*>(Java_Main_intFastNativeMethod) }
+  {"intFastNativeMethod", "(III)I", reinterpret_cast<void*>(Java_Main_intFastNativeMethod) },
+  {"intCriticalNativeMethod", "(III)I", reinterpret_cast<void*>(Java_Main_intCriticalNativeMethod) },
 };
 
 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void*) {
@@ -766,5 +768,12 @@
   return a + b + c;
 }
 
+// Annotated with @CriticalNative in Java code. Doesn't need to be explicitly registered with "!".
+// NOTE: Has to be registered explicitly to avoid mutator lock check failures.
+static jint Java_Main_intCriticalNativeMethod(jint a, jint b, jint c) {
+  // Note that unlike a "Fast Native" method this excludes JNIEnv and the jclass parameters.
+  return a + b + c;
+}
+
 }  // namespace art
 
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 573afdb..bb098e4 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -18,6 +18,7 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
 public class Main {
@@ -49,6 +50,7 @@
 
         registerNativesJniTest();
         testFastNativeMethods();
+        testCriticalNativeMethods();
     }
 
     private static native boolean registerNativesJniTest();
@@ -288,7 +290,23 @@
       }
     }
 
+    // Smoke test for @CriticalNative
+    // TODO: Way more thorough tests since it involved quite a bit of changes.
 
+    // Return sum of a+b+c.
+    @CriticalNative
+    static native int intCriticalNativeMethod(int a, int b, int c);
+
+    private static void testCriticalNativeMethods() {
+      int returns[] = { 3, 6, 9, 12, 15 };
+      for (int i = 0; i < returns.length; i++) {
+        int result = intCriticalNativeMethod(i, i+1, i+2);
+        if (returns[i] != result) {
+          System.out.println("CriticalNative Int Run " + i + " with " + returns[i] + " vs " + result);
+          throw new AssertionError();
+        }
+      }
+    }
 }
 
 @FunctionalInterface
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 45251b8..9f1499e 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -29,6 +29,7 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/stringprintf.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "oat_file.h"
@@ -55,7 +56,7 @@
   // Keep pausing.
   printf("Going to sleep\n");
   for (;;) {
-    pause();
+    sleep(1);
   }
 }
 
@@ -86,6 +87,19 @@
 
   return false;
 }
+
+static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
+  printf("Secondary pid is %d\n", pid);
+
+  PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ERROR);
+
+  if (sig_quit_on_fail) {
+    int res = kill(pid, SIGQUIT);
+    if (res != 0) {
+      PLOG(ERROR) << "Failed to send signal";
+    }
+  }
+}
 #endif
 
 // Currently we have to fall back to our own loader for the boot image when it's compiled PIC
@@ -254,10 +268,20 @@
     result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
   }
 
+  constexpr bool kSigQuitOnFail = true;
+  if (!result) {
+    MoreErrorInfo(pid, kSigQuitOnFail);
+  }
+
   if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
     PLOG(ERROR) << "Detach failed";
   }
 
+  // If we failed to unwind and induced an ANR dump, give the child some time (20s).
+  if (!result && kSigQuitOnFail) {
+    sleep(20);
+  }
+
   // Kill the other process once we are done with it.
   kill(pid, SIGKILL);
 
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
index 5cfe33d..1ec7072 100644
--- a/test/137-cfi/src/Main.java
+++ b/test/137-cfi/src/Main.java
@@ -101,9 +101,10 @@
           }
 
           // Wait until the forked process had time to run until its sleep phase.
+          BufferedReader lineReader;
           try {
               InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8");
-              BufferedReader lineReader = new BufferedReader(stdout);
+              lineReader = new BufferedReader(stdout);
               while (!lineReader.readLine().contains("Going to sleep")) {
               }
           } catch (Exception e) {
@@ -112,6 +113,26 @@
 
           if (!unwindOtherProcess(fullSignatures, pid)) {
               System.out.println("Unwinding other process failed.");
+
+              // In this case, log all the output.
+              // Note: this is potentially non-terminating code, if the secondary is totally stuck.
+              //       We rely on the run-test timeout infrastructure to terminate the primary in
+              //       such a case.
+              try {
+                  String tmp;
+                  System.out.println("Output from the secondary:");
+                  while ((tmp = lineReader.readLine()) != null) {
+                      System.out.println("Secondary: " + tmp);
+                  }
+              } catch (Exception e) {
+                  e.printStackTrace(System.out);
+              }
+          }
+
+          try {
+              lineReader.close();
+          } catch (Exception e) {
+              e.printStackTrace(System.out);
           }
       } finally {
           // Kill the forked process if it is not already dead.
diff --git a/test/412-new-array/smali/fill_array_data.smali b/test/412-new-array/smali/fill_array_data.smali
index 2b24e56..f163084 100644
--- a/test/412-new-array/smali/fill_array_data.smali
+++ b/test/412-new-array/smali/fill_array_data.smali
@@ -2,6 +2,18 @@
 
 .super Ljava/lang/Object;
 
+.method public static emptyIntArray([I)V
+   .registers 1
+
+   fill-array-data v0, :ArrayData
+   return-void
+
+:ArrayData
+    .array-data 4
+    .end array-data
+
+.end method
+
 .method public static intArray([I)V
    .registers 1
 
diff --git a/test/412-new-array/src/Main.java b/test/412-new-array/src/Main.java
index d95d2c5..fb348ba 100644
--- a/test/412-new-array/src/Main.java
+++ b/test/412-new-array/src/Main.java
@@ -220,6 +220,38 @@
   public static void testSmaliFillArrayData() throws Exception {
     Class<?> c = Class.forName("FillArrayData");
     {
+      Method m = c.getMethod("emptyIntArray", int[].class);
+      int[] array = new int[0];
+      Object[] args = { array };
+      m.invoke(null, args);
+      assertEquals(0, array.length);
+
+      array = new int[2];
+      args[0] = array;
+      m.invoke(null, args);
+      // Test that nothing has been written to the array.
+      assertEquals(0, array[0]);
+      assertEquals(0, array[1]);
+
+      array = new int[] { 42, -42 };
+      args[0] = array;
+      m.invoke(null, args);
+      // Test that nothing has been written to the array.
+      assertEquals(42, array[0]);
+      assertEquals(-42, array[1]);
+
+      Throwable exception = null;
+      args[0] = null;
+      try {
+        m.invoke(null, args);
+      } catch (InvocationTargetException e) {
+        exception = e.getCause();
+        assertTrue(exception instanceof NullPointerException);
+      }
+      assertNotNull(exception);
+    }
+
+    {
       Method m = c.getMethod("intArray", int[].class);
       int[] array = new int[7];
       Object[] args = { array };
@@ -235,7 +267,7 @@
 
       array = new int[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
@@ -274,7 +306,7 @@
 
       array = new int[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
@@ -313,7 +345,7 @@
 
       array = new short[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
@@ -352,7 +384,7 @@
 
       array = new long[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
@@ -391,7 +423,7 @@
 
       array = new char[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
@@ -430,7 +462,7 @@
 
       array = new byte[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
@@ -467,7 +499,7 @@
 
       array = new boolean[2];
       args[0] = array;
-      Throwable exception  = null;
+      Throwable exception = null;
       try {
         m.invoke(null, args);
       } catch (InvocationTargetException e) {
diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java
index 7b5cbc1..5fd51e1 100644
--- a/test/529-checker-unresolved/src/Main.java
+++ b/test/529-checker-unresolved/src/Main.java
@@ -122,6 +122,16 @@
     expectEquals(123456789123456789f, c.instanceFloat);
     expectEquals(123456789123456789d, c.instanceDouble);
     expectEquals(o, c.instanceObject);
+
+    // Check "large" values.
+
+    c.instanceByte = (byte)-1;
+    c.instanceChar = (char)32768;
+    c.instanceInt = -1;
+
+    expectEquals((byte)-1, c.instanceByte);
+    expectEquals((char)32768, c.instanceChar);
+    expectEquals(-1, c.instanceInt);
   }
 
   /// CHECK-START: void Main.callUnresolvedNull(UnresolvedClass) register (before)
diff --git a/test/534-checker-bce-deoptimization/expected.txt b/test/534-checker-bce-deoptimization/expected.txt
index 3823a29..b9a1e27 100644
--- a/test/534-checker-bce-deoptimization/expected.txt
+++ b/test/534-checker-bce-deoptimization/expected.txt
@@ -1 +1,5 @@
+array[0]=2.5f
+array[1]=2.625f
+array[0]=3.5
+array[1]=3.625
 finish
diff --git a/test/534-checker-bce-deoptimization/src/Main.java b/test/534-checker-bce-deoptimization/src/Main.java
index 8cd20f6..c4e4cbf 100644
--- a/test/534-checker-bce-deoptimization/src/Main.java
+++ b/test/534-checker-bce-deoptimization/src/Main.java
@@ -17,6 +17,8 @@
 public class Main {
     public static void main(String[] args) {
         new Main().run();
+        testPreserveFloat();
+        testPreserveDouble();
         System.out.println("finish");
     }
 
@@ -53,5 +55,77 @@
             b[i + 1] += c * b[i + 1];
         }
     }
+
+    /*
+     * Test that we correctly preserve floating point registers when we deoptimize.
+     *
+     * Note: These tests rely on the deoptimization happening before the loop,
+     * so that the loop is interpreted and fills the provided arrays. However,
+     * the BCE transformation can be modified to execute the loop as many times
+     * as the compiler can guarantee no AIOOBE and only deoptimize thereafter,
+     * just before the throwing iteration. Then the floating point registers
+     * would no longer be used after the deoptimization and another approach
+     * would be needed to test this.
+     */
+
+    static public void testPreserveFloat() {
+        float[] array = new float[2];
+        try {
+            $noinline$FloatFill(1.125f, 2.5f, array, 3);
+            throw new Error();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            System.out.println("array[0]=" + array[0] + "f");
+            System.out.println("array[1]=" + array[1] + "f");
+        }
+    }
+
+    /// CHECK-START: void Main.$noinline$FloatFill(float, float, float[], int) BCE (after)
+    /// CHECK-DAG:          Deoptimize
+    /// CHECK-DAG:          Deoptimize
+    /// CHECK-DAG:          Deoptimize
+    /// CHECK-NOT:          Deoptimize
+
+    /// CHECK-START: void Main.$noinline$FloatFill(float, float, float[], int) BCE (after)
+    /// CHECK-NOT:          BoundsCheck
+
+    public static void $noinline$FloatFill(float f1, float f2, float[] array, int n) {
+        if (doThrow) { throw new Error(); }
+        for (int i = 0; i < n; ++i) {
+            array[i] = ((i & 1) == 1) ? f1 : f2;
+            f1 += 1.5f;
+            f2 += 2.25f;
+        }
+    }
+
+    static public void testPreserveDouble() {
+        double[] array = new double[2];
+        try {
+            $noinline$DoubleFill(2.125, 3.5, array, 3);
+            throw new Error();
+        } catch (ArrayIndexOutOfBoundsException expected) {
+            System.out.println("array[0]=" + array[0]);
+            System.out.println("array[1]=" + array[1]);
+        }
+    }
+
+    /// CHECK-START: void Main.$noinline$DoubleFill(double, double, double[], int) BCE (after)
+    /// CHECK-DAG:          Deoptimize
+    /// CHECK-DAG:          Deoptimize
+    /// CHECK-DAG:          Deoptimize
+    /// CHECK-NOT:          Deoptimize
+
+    /// CHECK-START: void Main.$noinline$DoubleFill(double, double, double[], int) BCE (after)
+    /// CHECK-NOT:          BoundsCheck
+
+    public static void $noinline$DoubleFill(double d1, double d2, double[] array, int n) {
+        if (doThrow) { throw new Error(); }
+        for (int i = 0; i < n; ++i) {
+            array[i] = ((i & 1) == 1) ? d1 : d2;
+            d1 += 1.5;
+            d2 += 2.25;
+        }
+    }
+
+    public static boolean doThrow = false;
 }
 
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index f6713a2..04a12fa 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -102,12 +102,12 @@
 
   /// CHECK-START-ARM: long Main.and255(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
   /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #255
   /// CHECK-DAG:            movs {{r\d+}}, #0
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
 
   public static long and255(long arg) {
     return arg & 255L;
@@ -115,12 +115,13 @@
 
   /// CHECK-START-ARM: long Main.and511(long) disassembly (after)
   /// CHECK:                movw {{r\d+}}, #511
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
-  /// CHECK-DAG:            and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
-  /// CHECK-DAG:            movs {{r\d+}}, #0
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NEXT:           movs {{r\d+}}, #0
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
+  /// CHECK:                and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+  /// CHECK-NEXT:           and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
 
   public static long and511(long arg) {
     return arg & 511L;
@@ -128,11 +129,11 @@
 
   /// CHECK-START-ARM: long Main.andNot15(long) disassembly (after)
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
   /// CHECK:                bic {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
 
   public static long andNot15(long arg) {
     return arg & ~15L;
@@ -141,12 +142,12 @@
   /// CHECK-START-ARM: long Main.and0xfffffff00000000f(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #15
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
   /// CHECK-DAG:            and {{r\d+}}, {{r\d+}}, #15
   /// CHECK-DAG:            bic {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-NOT:            and
-  /// CHECK-NOT:            bic
+  /// CHECK-NOT:            and{{(\.w)?}}
+  /// CHECK-NOT:            bic{{(\.w)?}}
 
   public static long and0xfffffff00000000f(long arg) {
     return arg & 0xfffffff00000000fL;
@@ -154,10 +155,10 @@
 
   /// CHECK-START-ARM: long Main.or255(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK-NOT:            orr
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
   /// CHECK:                orr {{r\d+}}, {{r\d+}}, #255
-  /// CHECK-NOT:            orr
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
   public static long or255(long arg) {
@@ -166,10 +167,12 @@
 
   /// CHECK-START-ARM: long Main.or511(long) disassembly (after)
   /// CHECK:                movw {{r\d+}}, #511
-  /// CHECK-NOT:            orr
+  /// CHECK-NEXT:           movs {{r\d+}}, #0
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
   /// CHECK:                orr{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
-  /// CHECK-NOT:            orr
+  /// CHECK-NEXT:           orr{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
   public static long or511(long arg) {
@@ -178,11 +181,11 @@
 
   /// CHECK-START-ARM: long Main.orNot15(long) disassembly (after)
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK-NOT:            orr
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
   /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #15
   /// CHECK-DAG:            mvn {{r\d+}}, #0
-  /// CHECK-NOT:            orr
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
   public static long orNot15(long arg) {
@@ -192,11 +195,11 @@
   /// CHECK-START-ARM: long Main.or0xfffffff00000000f(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #15
   /// CHECK-NOT:            mvn {{r\d+}}, #15
-  /// CHECK-NOT:            orr
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
   /// CHECK-DAG:            orr {{r\d+}}, {{r\d+}}, #15
   /// CHECK-DAG:            orn {{r\d+}}, {{r\d+}}, #15
-  /// CHECK-NOT:            orr
+  /// CHECK-NOT:            orr{{(\.w)?}}
   /// CHECK-NOT:            orn
 
   public static long or0xfffffff00000000f(long arg) {
@@ -205,9 +208,9 @@
 
   /// CHECK-START-ARM: long Main.xor255(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #255
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
   /// CHECK:                eor {{r\d+}}, {{r\d+}}, #255
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor255(long arg) {
     return arg ^ 255L;
@@ -215,9 +218,11 @@
 
   /// CHECK-START-ARM: long Main.xor511(long) disassembly (after)
   /// CHECK:                movw {{r\d+}}, #511
-  /// CHECK-NOT:            eor
-  /// CHECK-DAG:            eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
-  /// CHECK-NOT:            eor
+  /// CHECK-NEXT:           movs {{r\d+}}, #0
+  /// CHECK-NOT:            eor{{(\.w)?}}
+  /// CHECK:                eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+  /// CHECK-NEXT:           eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
+  /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor511(long arg) {
     return arg ^ 511L;
@@ -226,10 +231,10 @@
   /// CHECK-START-ARM: long Main.xorNot15(long) disassembly (after)
   /// CHECK-DAG:            mvn {{r\d+}}, #15
   /// CHECK-DAG:            mov.w {{r\d+}}, #-1
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
   /// CHECK-DAG:            eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
   /// CHECK-DAG:            eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xorNot15(long arg) {
     return arg ^ ~15L;
@@ -239,10 +244,10 @@
   /// CHECK-START-ARM: long Main.xor0xfffffff00000000f(long) disassembly (after)
   /// CHECK-DAG:            movs {{r\d+}}, #15
   /// CHECK-DAG:            mvn {{r\d+}}, #15
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
   /// CHECK-DAG:            eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
   /// CHECK-DAG:            eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}}
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor0xfffffff00000000f(long arg) {
     return arg ^ 0xfffffff00000000fL;
@@ -251,10 +256,10 @@
   /// CHECK-START-ARM: long Main.xor0xf00000000000000f(long) disassembly (after)
   /// CHECK-NOT:            movs {{r\d+}}, #15
   /// CHECK-NOT:            mov.w {{r\d+}}, #-268435456
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
   /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #15
   /// CHECK-DAG:            eor {{r\d+}}, {{r\d+}}, #-268435456
-  /// CHECK-NOT:            eor
+  /// CHECK-NOT:            eor{{(\.w)?}}
 
   public static long xor0xf00000000000000f(long arg) {
     return arg ^ 0xf00000000000000fL;
diff --git a/test/960-default-smali/expected.txt b/test/960-default-smali/expected.txt
index f3db93f..8153d7d 100644
--- a/test/960-default-smali/expected.txt
+++ b/test/960-default-smali/expected.txt
@@ -98,3 +98,34 @@
 M-virtual           L.bar()='BAZ!'
 M-virtual           M.bar()='BAZ!'
 End testing for type M
+Testing for type N
+N-interface       Foo.bar()='foobar'
+N-virtual           N.bar()='foobar'
+End testing for type N
+Testing for type O
+O-interface       Foo.bar()='foobar foobar'
+O-interface      Foo2.bar()='foobar foobar'
+O-virtual           N.bar()='foobar foobar'
+O-virtual           O.bar()='foobar foobar'
+End testing for type O
+Testing for type P
+P-interface       Foo.bar()='not foobar!'
+P-interface      Foo2.bar()='not foobar!'
+P-virtual           N.bar()='not foobar!'
+P-virtual           O.bar()='not foobar!'
+P-virtual           P.bar()='not foobar!'
+End testing for type P
+Testing for type Q
+Q-interface on Foo: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-interface on Foo2: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-interface on Foo3: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-virtual on N: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-virtual on O: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-virtual on Q: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+End testing for type Q
diff --git a/test/960-default-smali/src/Foo2.java b/test/960-default-smali/src/Foo2.java
new file mode 100644
index 0000000..2a1bbc0
--- /dev/null
+++ b/test/960-default-smali/src/Foo2.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+interface Foo2 extends Foo {
+  @Override
+  public default String bar() {
+    return "foobar foobar";
+  }
+}
diff --git a/test/960-default-smali/src/Foo3.java b/test/960-default-smali/src/Foo3.java
new file mode 100644
index 0000000..4c00425
--- /dev/null
+++ b/test/960-default-smali/src/Foo3.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+interface Foo3 extends Foo {
+  default void doNothing() {}
+}
diff --git a/test/960-default-smali/src/N.java b/test/960-default-smali/src/N.java
new file mode 100644
index 0000000..9d33320
--- /dev/null
+++ b/test/960-default-smali/src/N.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class N implements Foo {
+}
diff --git a/test/960-default-smali/src/O.java b/test/960-default-smali/src/O.java
new file mode 100644
index 0000000..55126af
--- /dev/null
+++ b/test/960-default-smali/src/O.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class O extends N implements Foo2 {
+}
diff --git a/test/960-default-smali/src/P.java b/test/960-default-smali/src/P.java
new file mode 100644
index 0000000..1ee6c26
--- /dev/null
+++ b/test/960-default-smali/src/P.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class P extends O implements Foo2 {
+  @Override
+  public String bar() {
+    return "not foobar!";
+  }
+}
diff --git a/test/960-default-smali/src/Q.java b/test/960-default-smali/src/Q.java
new file mode 100644
index 0000000..bc1e164
--- /dev/null
+++ b/test/960-default-smali/src/Q.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class Q extends O implements Foo2, Foo3 {
+}
diff --git a/test/960-default-smali/src/classes.xml b/test/960-default-smali/src/classes.xml
index f3e50c5..c66d35b 100644
--- a/test/960-default-smali/src/classes.xml
+++ b/test/960-default-smali/src/classes.xml
@@ -102,6 +102,37 @@
         <method>bar</method>
       </methods>
     </class>
+
+    <class name="N" super="java/lang/Object">
+      <implements>
+        <item>Foo</item>
+      </implements>
+      <methods> </methods>
+    </class>
+
+    <class name="O" super="N">
+      <implements>
+        <item>Foo2</item>
+      </implements>
+      <methods> </methods>
+    </class>
+
+    <class name="P" super="O">
+      <implements>
+        <item>Foo2</item>
+      </implements>
+      <methods>
+        <method>bar</method>
+      </methods>
+    </class>
+
+    <class name="Q" super="O">
+      <implements>
+        <item>Foo2</item>
+        <item>Foo3</item>
+      </implements>
+      <methods> </methods>
+    </class>
   </classes>
 
   <interfaces>
@@ -153,6 +184,24 @@
       </methods>
     </interface>
 
+    <interface name="Foo2" super="java/lang/Object">
+      <implements>
+        <item>Foo</item>
+      </implements>
+      <methods>
+        <method type="default">bar</method>
+      </methods>
+    </interface>
+
+    <interface name="Foo3" super="java/lang/Object">
+      <implements>
+        <item>Foo</item>
+      </implements>
+      <methods>
+        <method type="default">bar</method>
+      </methods>
+    </interface>
+
     <interface name="Fooer" super="java/lang/Object">
       <implements>
         <item>Foo</item>
diff --git a/test/960-default-smali/src2/Foo.java b/test/960-default-smali/src2/Foo.java
new file mode 100644
index 0000000..ed5b35f
--- /dev/null
+++ b/test/960-default-smali/src2/Foo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+interface Foo {
+  public default String bar() {
+    return "foobar";
+  }
+}
diff --git a/test/960-default-smali/src2/Foo3.java b/test/960-default-smali/src2/Foo3.java
new file mode 100644
index 0000000..e96f98a
--- /dev/null
+++ b/test/960-default-smali/src2/Foo3.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+interface Foo3 extends Foo {
+  @Override
+  public default String bar() {
+    return "I'm in conflict";
+  }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 75c4f34..d6957fc 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -338,7 +338,9 @@
 TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
 
 # Temporarily disable some broken tests when forcing access checks in interpreter b/22414682
+# 004-JniTest is disabled because @CriticalNative is unsupported by generic JNI b/31400248
 TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := \
+  004-JniTest \
   137-cfi
 
 ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
@@ -397,9 +399,11 @@
 # 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.
+# 004-JniTest is disabled because @CriticalNative is unsupported by generic JNI b/31400248
 # 147-stripped-dex-fallback is disabled because it requires --prebuild.
 # 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 := \
+  004-JniTest \
   116-nodex2oat \
   117-nopatchoat \
   118-noimage-dex2oat \
@@ -473,7 +477,9 @@
 # Known broken tests for the JIT.
 # CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
 # also uses Generic JNI instead of the JNI compiler.
+# 004-JniTest is disabled because @CriticalNative is unsupported by generic JNI b/31400248
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
+  004-JniTest \
   137-cfi
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
@@ -500,7 +506,6 @@
 
 # Known broken tests for the mips32 optimizing compiler backend.
 TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \
-    510-checker-try-catch \
 
 ifeq (mips,$(TARGET_ARCH))
   ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES)))
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 45cfd0f..3cb1f23 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -14,40 +14,77 @@
  * limitations under the License.
  */
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+/*
+ * AUTOMATICALLY GENERATED FROM art/tools/mako-source-generator/...../MyClassNatives.java.mako
+ *
+ * !!! DO NOT EDIT DIRECTLY !!!
+ *
+ */
 class MyClassNatives {
-    native void throwException();
-    native void foo();
-    native int bar(int count);
-    static native int sbar(int count);
-    native int fooI(int x);
-    native int fooII(int x, int y);
-    native long fooJJ(long x, long y);
-    native Object fooO(Object x);
-    native double fooDD(double x, double y);
-    synchronized native long fooJJ_synchronized(long x, long y);
-    native Object fooIOO(int x, Object y, Object z);
-    static native Object fooSIOO(int x, Object y, Object z);
-    static native int fooSII(int x, int y);
-    static native double fooSDD(double x, double y);
-    static synchronized native Object fooSSIOO(int x, Object y, Object z);
-    static native void arraycopy(Object src, int src_pos, Object dst, int dst_pos, int length);
-    native boolean compareAndSwapInt(Object obj, long offset, int expected, int newval);
-    static native int getText(long val1, Object obj1, long val2, Object obj2);
-    synchronized native Object []getSinkPropertiesNative(String path);
 
+    // Normal native
+    native void throwException();
+    // Normal native
+    native void foo();
+    // Normal native
+    native int bar(int count);
+    // Normal native
+    static native int sbar(int count);
+    // Normal native
+    native int fooI(int x);
+    // Normal native
+    native int fooII(int x, int y);
+    // Normal native
+    native long fooJJ(long x, long y);
+    // Normal native
+    native Object fooO(Object x);
+    // Normal native
+    native double fooDD(double x, double y);
+    // Normal native
+    synchronized native long fooJJ_synchronized(long x, long y);
+    // Normal native
+    native Object fooIOO(int x, Object y, Object z);
+    // Normal native
+    static native Object fooSIOO(int x, Object y, Object z);
+    // Normal native
+    static native int fooSII(int x, int y);
+    // Normal native
+    static native double fooSDD(double x, double y);
+    // Normal native
+    static synchronized native Object fooSSIOO(int x, Object y, Object z);
+    // Normal native
+    static native void arraycopy(Object src, int src_pos, Object dst, int dst_pos, int length);
+    // Normal native
+    native boolean compareAndSwapInt(Object obj, long offset, int expected, int newval);
+    // Normal native
+    static native int getText(long val1, Object obj1, long val2, Object obj2);
+    // Normal native
+    synchronized native Object[] getSinkPropertiesNative(String path);
+
+    // Normal native
     native Class<?> instanceMethodThatShouldReturnClass();
+    // Normal native
     static native Class<?> staticMethodThatShouldReturnClass();
 
+    // Normal native
     native void instanceMethodThatShouldTakeClass(int i, Class<?> c);
+    // Normal native
     static native void staticMethodThatShouldTakeClass(int i, Class<?> c);
 
+    // TODO: These 3 seem like they could work for @CriticalNative as well if they were static.
+    // Normal native
     native float checkFloats(float f1, float f2);
+    // Normal native
     native void forceStackParameters(int i1, int i2, int i3, int i4, int i5, int i6, int i8, int i9,
                                      float f1, float f2, float f3, float f4, float f5, float f6,
                                      float f7, float f8, float f9);
+    // Normal native
     native void checkParameterAlign(int i1, long l1);
+
+    // Normal native
     native void maxParamNumber(Object o0, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7,
         Object o8, Object o9, Object o10, Object o11, Object o12, Object o13, Object o14, Object o15,
         Object o16, Object o17, Object o18, Object o19, Object o20, Object o21, Object o22, Object o23,
@@ -81,32 +118,228 @@
         Object o240, Object o241, Object o242, Object o243, Object o244, Object o245, Object o246, Object o247,
         Object o248, Object o249, Object o250, Object o251, Object o252, Object o253);
 
+    // Normal native
     native void withoutImplementation();
+    // Normal native
     native Object withoutImplementationRefReturn();
 
+    // Normal native
     native static void stackArgsIntsFirst(int i1, int i2, int i3, int i4, int i5, int i6, int i7,
         int i8, int i9, int i10, float f1, float f2, float f3, float f4, float f5, float f6,
         float f7, float f8, float f9, float f10);
 
+    // Normal native
     native static void stackArgsFloatsFirst(float f1, float f2, float f3, float f4, float f5,
         float f6, float f7, float f8, float f9, float f10, int i1, int i2, int i3, int i4, int i5,
         int i6, int i7, int i8, int i9, int i10);
 
+    // Normal native
     native static void stackArgsMixed(int i1, float f1, int i2, float f2, int i3, float f3, int i4,
         float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9,
         float f9, int i10, float f10);
 
+    // Normal native
     native static void stackArgsSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6,
         int i7, int i8);
 
+    // Normal native
     static native double logD(double d);
+    // Normal native
     static native float logF(float f);
+    // Normal native
     static native boolean returnTrue();
+    // Normal native
     static native boolean returnFalse();
+    // Normal native
     static native int returnInt();
+    // Normal native
+    static native double returnDouble();
+    // Normal native
+    static native long returnLong();
 
-    // Check for @FastNative annotation presence [or lack of presence].
+
+
+    @FastNative
+    native void throwException_Fast();
+    @FastNative
+    native void foo_Fast();
+    @FastNative
+    native int bar_Fast(int count);
+    @FastNative
+    static native int sbar_Fast(int count);
+    @FastNative
+    native int fooI_Fast(int x);
+    @FastNative
+    native int fooII_Fast(int x, int y);
+    @FastNative
+    native long fooJJ_Fast(long x, long y);
+    @FastNative
+    native Object fooO_Fast(Object x);
+    @FastNative
+    native double fooDD_Fast(double x, double y);
+    @FastNative
+    synchronized native long fooJJ_synchronized_Fast(long x, long y);
+    @FastNative
+    native Object fooIOO_Fast(int x, Object y, Object z);
+    @FastNative
+    static native Object fooSIOO_Fast(int x, Object y, Object z);
+    @FastNative
+    static native int fooSII_Fast(int x, int y);
+    @FastNative
+    static native double fooSDD_Fast(double x, double y);
+    @FastNative
+    static synchronized native Object fooSSIOO_Fast(int x, Object y, Object z);
+    @FastNative
+    static native void arraycopy_Fast(Object src, int src_pos, Object dst, int dst_pos, int length);
+    @FastNative
+    native boolean compareAndSwapInt_Fast(Object obj, long offset, int expected, int newval);
+    @FastNative
+    static native int getText_Fast(long val1, Object obj1, long val2, Object obj2);
+    @FastNative
+    synchronized native Object[] getSinkPropertiesNative_Fast(String path);
+
+    @FastNative
+    native Class<?> instanceMethodThatShouldReturnClass_Fast();
+    @FastNative
+    static native Class<?> staticMethodThatShouldReturnClass_Fast();
+
+    @FastNative
+    native void instanceMethodThatShouldTakeClass_Fast(int i, Class<?> c);
+    @FastNative
+    static native void staticMethodThatShouldTakeClass_Fast(int i, Class<?> c);
+
+    // TODO: These 3 seem like they could work for @CriticalNative as well if they were static.
+    @FastNative
+    native float checkFloats_Fast(float f1, float f2);
+    @FastNative
+    native void forceStackParameters_Fast(int i1, int i2, int i3, int i4, int i5, int i6, int i8, int i9,
+                                     float f1, float f2, float f3, float f4, float f5, float f6,
+                                     float f7, float f8, float f9);
+    @FastNative
+    native void checkParameterAlign_Fast(int i1, long l1);
+
+    @FastNative
+    native void maxParamNumber_Fast(Object o0, Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7,
+        Object o8, Object o9, Object o10, Object o11, Object o12, Object o13, Object o14, Object o15,
+        Object o16, Object o17, Object o18, Object o19, Object o20, Object o21, Object o22, Object o23,
+        Object o24, Object o25, Object o26, Object o27, Object o28, Object o29, Object o30, Object o31,
+        Object o32, Object o33, Object o34, Object o35, Object o36, Object o37, Object o38, Object o39,
+        Object o40, Object o41, Object o42, Object o43, Object o44, Object o45, Object o46, Object o47,
+        Object o48, Object o49, Object o50, Object o51, Object o52, Object o53, Object o54, Object o55,
+        Object o56, Object o57, Object o58, Object o59, Object o60, Object o61, Object o62, Object o63,
+        Object o64, Object o65, Object o66, Object o67, Object o68, Object o69, Object o70, Object o71,
+        Object o72, Object o73, Object o74, Object o75, Object o76, Object o77, Object o78, Object o79,
+        Object o80, Object o81, Object o82, Object o83, Object o84, Object o85, Object o86, Object o87,
+        Object o88, Object o89, Object o90, Object o91, Object o92, Object o93, Object o94, Object o95,
+        Object o96, Object o97, Object o98, Object o99, Object o100, Object o101, Object o102, Object o103,
+        Object o104, Object o105, Object o106, Object o107, Object o108, Object o109, Object o110, Object o111,
+        Object o112, Object o113, Object o114, Object o115, Object o116, Object o117, Object o118, Object o119,
+        Object o120, Object o121, Object o122, Object o123, Object o124, Object o125, Object o126, Object o127,
+        Object o128, Object o129, Object o130, Object o131, Object o132, Object o133, Object o134, Object o135,
+        Object o136, Object o137, Object o138, Object o139, Object o140, Object o141, Object o142, Object o143,
+        Object o144, Object o145, Object o146, Object o147, Object o148, Object o149, Object o150, Object o151,
+        Object o152, Object o153, Object o154, Object o155, Object o156, Object o157, Object o158, Object o159,
+        Object o160, Object o161, Object o162, Object o163, Object o164, Object o165, Object o166, Object o167,
+        Object o168, Object o169, Object o170, Object o171, Object o172, Object o173, Object o174, Object o175,
+        Object o176, Object o177, Object o178, Object o179, Object o180, Object o181, Object o182, Object o183,
+        Object o184, Object o185, Object o186, Object o187, Object o188, Object o189, Object o190, Object o191,
+        Object o192, Object o193, Object o194, Object o195, Object o196, Object o197, Object o198, Object o199,
+        Object o200, Object o201, Object o202, Object o203, Object o204, Object o205, Object o206, Object o207,
+        Object o208, Object o209, Object o210, Object o211, Object o212, Object o213, Object o214, Object o215,
+        Object o216, Object o217, Object o218, Object o219, Object o220, Object o221, Object o222, Object o223,
+        Object o224, Object o225, Object o226, Object o227, Object o228, Object o229, Object o230, Object o231,
+        Object o232, Object o233, Object o234, Object o235, Object o236, Object o237, Object o238, Object o239,
+        Object o240, Object o241, Object o242, Object o243, Object o244, Object o245, Object o246, Object o247,
+        Object o248, Object o249, Object o250, Object o251, Object o252, Object o253);
+
+    @FastNative
+    native void withoutImplementation_Fast();
+    @FastNative
+    native Object withoutImplementationRefReturn_Fast();
+
+    @FastNative
+    native static void stackArgsIntsFirst_Fast(int i1, int i2, int i3, int i4, int i5, int i6, int i7,
+        int i8, int i9, int i10, float f1, float f2, float f3, float f4, float f5, float f6,
+        float f7, float f8, float f9, float f10);
+
+    @FastNative
+    native static void stackArgsFloatsFirst_Fast(float f1, float f2, float f3, float f4, float f5,
+        float f6, float f7, float f8, float f9, float f10, int i1, int i2, int i3, int i4, int i5,
+        int i6, int i7, int i8, int i9, int i10);
+
+    @FastNative
+    native static void stackArgsMixed_Fast(int i1, float f1, int i2, float f2, int i3, float f3, int i4,
+        float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9,
+        float f9, int i10, float f10);
+
+    @FastNative
+    native static void stackArgsSignExtendedMips64_Fast(int i1, int i2, int i3, int i4, int i5, int i6,
+        int i7, int i8);
+
+    @FastNative
+    static native double logD_Fast(double d);
+    @FastNative
+    static native float logF_Fast(float f);
+    @FastNative
+    static native boolean returnTrue_Fast();
+    @FastNative
+    static native boolean returnFalse_Fast();
+    @FastNative
+    static native int returnInt_Fast();
+    @FastNative
+    static native double returnDouble_Fast();
+    @FastNative
+    static native long returnLong_Fast();
+
+
+
+    @CriticalNative
+    static native int sbar_Critical(int count);
+    @CriticalNative
+    static native int fooSII_Critical(int x, int y);
+    @CriticalNative
+    static native double fooSDD_Critical(double x, double y);
+
+    @CriticalNative
+    native static void stackArgsIntsFirst_Critical(int i1, int i2, int i3, int i4, int i5, int i6, int i7,
+        int i8, int i9, int i10, float f1, float f2, float f3, float f4, float f5, float f6,
+        float f7, float f8, float f9, float f10);
+
+    @CriticalNative
+    native static void stackArgsFloatsFirst_Critical(float f1, float f2, float f3, float f4, float f5,
+        float f6, float f7, float f8, float f9, float f10, int i1, int i2, int i3, int i4, int i5,
+        int i6, int i7, int i8, int i9, int i10);
+
+    @CriticalNative
+    native static void stackArgsMixed_Critical(int i1, float f1, int i2, float f2, int i3, float f3, int i4,
+        float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9,
+        float f9, int i10, float f10);
+
+    @CriticalNative
+    native static void stackArgsSignExtendedMips64_Critical(int i1, int i2, int i3, int i4, int i5, int i6,
+        int i7, int i8);
+
+    @CriticalNative
+    static native double logD_Critical(double d);
+    @CriticalNative
+    static native float logF_Critical(float f);
+    @CriticalNative
+    static native boolean returnTrue_Critical();
+    @CriticalNative
+    static native boolean returnFalse_Critical();
+    @CriticalNative
+    static native int returnInt_Critical();
+    @CriticalNative
+    static native double returnDouble_Critical();
+    @CriticalNative
+    static native long returnLong_Critical();
+
+
+
+    // Check for @FastNative/@CriticalNative annotation presence [or lack of presence].
     public static native void normalNative();
     @FastNative
     public static native void fastNative();
+    @CriticalNative
+    public static native void criticalNative();
 }
diff --git a/test/Packages/Package1.java b/test/Packages/Package1.java
new file mode 100644
index 0000000..6d58246
--- /dev/null
+++ b/test/Packages/Package1.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package package1;
+class Package1 {
+    static int someField;
+}
diff --git a/test/Packages/Package2.java b/test/Packages/Package2.java
new file mode 100644
index 0000000..9ae370a
--- /dev/null
+++ b/test/Packages/Package2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package package2;
+class Package2 {
+    static int someField;
+}
diff --git a/test/Transaction/Transaction.java b/test/Transaction/Transaction.java
index 00e1fbb..e7085c1 100644
--- a/test/Transaction/Transaction.java
+++ b/test/Transaction/Transaction.java
@@ -18,6 +18,10 @@
     static class EmptyStatic {
     }
 
+    static class ResolveString {
+      static String s = "ResolvedString";
+    }
+
     static class StaticFieldClass {
       public static int intField;
       static {
diff --git a/test/utils/python/generate_java_main.py b/test/utils/python/generate_java_main.py
index f66d0dd..1c76b06 100755
--- a/test/utils/python/generate_java_main.py
+++ b/test/utils/python/generate_java_main.py
@@ -175,7 +175,11 @@
       return;
     }} catch (Error e) {{
       System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s);
-      e.printStackTrace(System.out);
+      if (e instanceof IncompatibleClassChangeError) {{
+        System.out.printf("Exception is of type %s\\n", e.getClass().getName());
+      }} else {{
+        e.printStackTrace(System.out);
+      }}
     }}
   }}
 """
diff --git a/tools/art b/tools/art
index d91b451..1a3bba7 100644
--- a/tools/art
+++ b/tools/art
@@ -87,7 +87,7 @@
 fi
 
 if [ z"$PERF" != z ]; then
-  invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
+  invoke_with="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
   DEBUG_OPTION="-Xcompiler-option --generate-debug-info"
 fi
 
diff --git a/tools/bisection-search/README.md b/tools/bisection-search/README.md
deleted file mode 100644
index 857c930..0000000
--- a/tools/bisection-search/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-Bisection Bug Search
-====================
-
-Bisection Bug Search is a tool for finding compiler optimizations bugs. It
-accepts a program which exposes a bug by producing incorrect output and expected
-output for the program. It then attempts to narrow down the issue to a single
-method and optimization pass under the assumption that interpreter is correct.
-
-Given methods in order M0..Mn finds smallest i such that compiling Mi and
-interpreting all other methods produces incorrect output. Then, given ordered
-optimization passes P0..Pl, finds smallest j such that compiling Mi with passes
-P0..Pj-1 produces expected output and compiling Mi with passes P0..Pj produces
-incorrect output. Prints Mi and Pj.
-
-How to run Bisection Bug Search
-===============================
-
-    bisection_search.py [-h] -cp CLASSPATH
-                        [--expected-output EXPECTED_OUTPUT] [--device]
-                        [--lib LIB] [--64]
-                        [--dalvikvm-option [OPTION [OPTION ...]]]
-                        [--arg [TEST_ARGS [TEST_ARGS ...]]] [--image IMAGE]
-                        [--verbose]
-                        classname
-
-    positional arguments:
-      classname             name of class to run
-
-    optional arguments:
-      -h, --help            show this help message and exit
-      -cp CLASSPATH, --classpath CLASSPATH
-                            classpath
-      --expected-output EXPECTED_OUTPUT
-                            file containing expected output
-      --device              run on device
-      --lib LIB             lib to use, default: libart.so
-      --64                  x64 mode
-      --dalvikvm-option [OPTION [OPTION ...]]
-                            additional dalvikvm option
-      --arg [TEST_ARGS [TEST_ARGS ...]]
-                            argument to pass to program
-      --image IMAGE         path to image
-      --verbose             enable verbose output
diff --git a/tools/bisection-search/bisection_search.py b/tools/bisection-search/bisection_search.py
deleted file mode 100755
index d6c1749..0000000
--- a/tools/bisection-search/bisection_search.py
+++ /dev/null
@@ -1,300 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Performs bisection bug search on methods and optimizations.
-
-See README.md.
-
-Example usage:
-./bisection-search.py -cp classes.dex --expected-output output Test
-"""
-
-import argparse
-import re
-import sys
-
-from common import DeviceTestEnv
-from common import FatalError
-from common import GetEnvVariableOrError
-from common import HostTestEnv
-
-# Passes that are never disabled during search process because disabling them
-# would compromise correctness.
-MANDATORY_PASSES = ['dex_cache_array_fixups_arm',
-                    'dex_cache_array_fixups_mips',
-                    'instruction_simplifier$before_codegen',
-                    'pc_relative_fixups_x86',
-                    'pc_relative_fixups_mips',
-                    'x86_memory_operand_generation']
-
-# Passes that show up as optimizations in compiler verbose output but aren't
-# driven by run-passes mechanism. They are mandatory and will always run, we
-# never pass them to --run-passes.
-NON_PASSES = ['builder', 'prepare_for_register_allocation',
-              'liveness', 'register']
-
-
-class Dex2OatWrapperTestable(object):
-  """Class representing a testable compilation.
-
-  Accepts filters on compiled methods and optimization passes.
-  """
-
-  def __init__(self, base_cmd, test_env, class_name, args,
-               expected_output=None, verbose=False):
-    """Constructor.
-
-    Args:
-      base_cmd: list of strings, base command to run.
-      test_env: ITestEnv.
-      class_name: string, name of class to run.
-      args: list of strings, program arguments to pass.
-      expected_output: string, expected output to compare against or None.
-      verbose: bool, enable verbose output.
-    """
-    self._base_cmd = base_cmd
-    self._test_env = test_env
-    self._class_name = class_name
-    self._args = args
-    self._expected_output = expected_output
-    self._compiled_methods_path = self._test_env.CreateFile('compiled_methods')
-    self._passes_to_run_path = self._test_env.CreateFile('run_passes')
-    self._verbose = verbose
-
-  def Test(self, compiled_methods, passes_to_run=None):
-    """Tests compilation with compiled_methods and run_passes switches active.
-
-    If compiled_methods is None then compiles all methods.
-    If passes_to_run is None then runs default passes.
-
-    Args:
-      compiled_methods: list of strings representing methods to compile or None.
-      passes_to_run: list of strings representing passes to run or None.
-
-    Returns:
-      True if test passes with given settings. False otherwise.
-    """
-    if self._verbose:
-      print('Testing methods: {0} passes:{1}.'.format(
-          compiled_methods, passes_to_run))
-    cmd = self._PrepareCmd(compiled_methods=compiled_methods,
-                           passes_to_run=passes_to_run,
-                           verbose_compiler=True)
-    (output, _, ret_code) = self._test_env.RunCommand(cmd)
-    res = ret_code == 0 and (self._expected_output is None
-                             or output == self._expected_output)
-    if self._verbose:
-      print('Test passed: {0}.'.format(res))
-    return res
-
-  def GetAllMethods(self):
-    """Get methods compiled during the test.
-
-    Returns:
-      List of strings representing methods compiled during the test.
-
-    Raises:
-      FatalError: An error occurred when retrieving methods list.
-    """
-    cmd = self._PrepareCmd(verbose_compiler=True)
-    (_, err_output, _) = self._test_env.RunCommand(cmd)
-    match_methods = re.findall(r'Building ([^\n]+)\n', err_output)
-    if not match_methods:
-      raise FatalError('Failed to retrieve methods list. '
-                       'Not recognized output format.')
-    return match_methods
-
-  def GetAllPassesForMethod(self, compiled_method):
-    """Get all optimization passes ran for a method during the test.
-
-    Args:
-      compiled_method: string representing method to compile.
-
-    Returns:
-      List of strings representing passes ran for compiled_method during test.
-
-    Raises:
-      FatalError: An error occurred when retrieving passes list.
-    """
-    cmd = self._PrepareCmd(compiled_methods=[compiled_method],
-                           verbose_compiler=True)
-    (_, err_output, _) = self._test_env.RunCommand(cmd)
-    match_passes = re.findall(r'Starting pass: ([^\n]+)\n', err_output)
-    if not match_passes:
-      raise FatalError('Failed to retrieve passes list. '
-                       'Not recognized output format.')
-    return [p for p in match_passes if p not in NON_PASSES]
-
-  def _PrepareCmd(self, compiled_methods=None, passes_to_run=None,
-                  verbose_compiler=False):
-    """Prepare command to run."""
-    cmd = list(self._base_cmd)
-    if compiled_methods is not None:
-      self._test_env.WriteLines(self._compiled_methods_path, compiled_methods)
-      cmd += ['-Xcompiler-option', '--compiled-methods={0}'.format(
-          self._compiled_methods_path)]
-    if passes_to_run is not None:
-      self._test_env.WriteLines(self._passes_to_run_path, passes_to_run)
-      cmd += ['-Xcompiler-option', '--run-passes={0}'.format(
-          self._passes_to_run_path)]
-    if verbose_compiler:
-      cmd += ['-Xcompiler-option', '--runtime-arg', '-Xcompiler-option',
-              '-verbose:compiler']
-    cmd += ['-classpath', self._test_env.classpath, self._class_name]
-    cmd += self._args
-    return cmd
-
-
-def BinarySearch(start, end, test):
-  """Binary search integers using test function to guide the process."""
-  while start < end:
-    mid = (start + end) // 2
-    if test(mid):
-      start = mid + 1
-    else:
-      end = mid
-  return start
-
-
-def FilterPasses(passes, cutoff_idx):
-  """Filters passes list according to cutoff_idx but keeps mandatory passes."""
-  return [opt_pass for idx, opt_pass in enumerate(passes)
-          if opt_pass in MANDATORY_PASSES or idx < cutoff_idx]
-
-
-def BugSearch(testable):
-  """Find buggy (method, optimization pass) pair for a given testable.
-
-  Args:
-    testable: Dex2OatWrapperTestable.
-
-  Returns:
-    (string, string) tuple. First element is name of method which when compiled
-    exposes test failure. Second element is name of optimization pass such that
-    for aforementioned method running all passes up to and excluding the pass
-    results in test passing but running all passes up to and including the pass
-    results in test failing.
-
-    (None, None) if test passes when compiling all methods.
-    (string, None) if a method is found which exposes the failure, but the
-      failure happens even when running just mandatory passes.
-
-  Raises:
-    FatalError: Testable fails with no methods compiled.
-    AssertionError: Method failed for all passes when bisecting methods, but
-    passed when bisecting passes. Possible sporadic failure.
-  """
-  all_methods = testable.GetAllMethods()
-  faulty_method_idx = BinarySearch(
-      0,
-      len(all_methods),
-      lambda mid: testable.Test(all_methods[0:mid]))
-  if faulty_method_idx == len(all_methods):
-    return (None, None)
-  if faulty_method_idx == 0:
-    raise FatalError('Testable fails with no methods compiled. '
-                     'Perhaps issue lies outside of compiler.')
-  faulty_method = all_methods[faulty_method_idx - 1]
-  all_passes = testable.GetAllPassesForMethod(faulty_method)
-  faulty_pass_idx = BinarySearch(
-      0,
-      len(all_passes),
-      lambda mid: testable.Test([faulty_method],
-                                FilterPasses(all_passes, mid)))
-  if faulty_pass_idx == 0:
-    return (faulty_method, None)
-  assert faulty_pass_idx != len(all_passes), 'Method must fail for some passes.'
-  faulty_pass = all_passes[faulty_pass_idx - 1]
-  return (faulty_method, faulty_pass)
-
-
-def PrepareParser():
-  """Prepares argument parser."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '-cp', '--classpath', required=True, type=str, help='classpath')
-  parser.add_argument('--expected-output', type=str,
-                      help='file containing expected output')
-  parser.add_argument(
-      '--device', action='store_true', default=False, help='run on device')
-  parser.add_argument('classname', type=str, help='name of class to run')
-  parser.add_argument('--lib', dest='lib', type=str, default='libart.so',
-                      help='lib to use, default: libart.so')
-  parser.add_argument('--64', dest='x64', action='store_true',
-                      default=False, help='x64 mode')
-  parser.add_argument('--dalvikvm-option', dest='dalvikvm_opts',
-                      metavar='OPTION', nargs='*', default=[],
-                      help='additional dalvikvm option')
-  parser.add_argument('--arg', dest='test_args', nargs='*', default=[],
-                      help='argument to pass to program')
-  parser.add_argument('--image', type=str, help='path to image')
-  parser.add_argument('--verbose', action='store_true',
-                      default=False, help='enable verbose output')
-  return parser
-
-
-def main():
-  # Parse arguments
-  parser = PrepareParser()
-  args = parser.parse_args()
-
-  # Prepare environment
-  if args.expected_output is not None:
-    with open(args.expected_output, 'r') as f:
-      expected_output = f.read()
-  else:
-    expected_output = None
-  if args.device:
-    run_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
-    test_env = DeviceTestEnv(args.classpath)
-  else:
-    run_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
-    run_cmd += ['-XXlib:{0}'.format(args.lib)]
-    if not args.image:
-      image_path = '{0}/framework/core-optimizing-pic.art'.format(
-          GetEnvVariableOrError('ANDROID_HOST_OUT'))
-    else:
-      image_path = args.image
-    run_cmd += ['-Ximage:{0}'.format(image_path)]
-    if args.dalvikvm_opts:
-      run_cmd += args.dalvikvm_opts
-    test_env = HostTestEnv(args.classpath, args.x64)
-
-  # Perform the search
-  try:
-    testable = Dex2OatWrapperTestable(run_cmd, test_env, args.classname,
-                                      args.test_args, expected_output,
-                                      args.verbose)
-    (method, opt_pass) = BugSearch(testable)
-  except Exception as e:
-    print('Error. Refer to logfile: {0}'.format(test_env.logfile.name))
-    test_env.logfile.write('Exception: {0}\n'.format(e))
-    raise
-
-  # Report results
-  if method is None:
-    print('Couldn\'t find any bugs.')
-  elif opt_pass is None:
-    print('Faulty method: {0}. Fails with just mandatory passes.'.format(
-        method))
-  else:
-    print('Faulty method and pass: {0}, {1}.'.format(method, opt_pass))
-  print('Logfile: {0}'.format(test_env.logfile.name))
-  sys.exit(0)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/tools/bisection_search/README.md b/tools/bisection_search/README.md
new file mode 100644
index 0000000..a7485c2
--- /dev/null
+++ b/tools/bisection_search/README.md
@@ -0,0 +1,43 @@
+Bisection Bug Search
+====================
+
+Bisection Bug Search is a tool for finding compiler optimizations bugs. It
+accepts a program which exposes a bug by producing incorrect output and expected
+output for the program. It then attempts to narrow down the issue to a single
+method and optimization pass under the assumption that interpreter is correct.
+
+Given methods in order M0..Mn finds smallest i such that compiling Mi and
+interpreting all other methods produces incorrect output. Then, given ordered
+optimization passes P0..Pl, finds smallest j such that compiling Mi with passes
+P0..Pj-1 produces expected output and compiling Mi with passes P0..Pj produces
+incorrect output. Prints Mi and Pj.
+
+How to run Bisection Bug Search
+===============================
+
+    bisection_search.py [-h] [-cp CLASSPATH] [--class CLASSNAME] [--lib LIB]
+                               [--dalvikvm-option [OPT [OPT ...]]] [--arg [ARG [ARG ...]]]
+                               [--image IMAGE] [--raw-cmd RAW_CMD]
+                               [--64] [--device] [--expected-output EXPECTED_OUTPUT]
+                               [--check-script CHECK_SCRIPT] [--verbose]
+
+    Tool for finding compiler bugs. Either --raw-cmd or both -cp and --class are required.
+
+    optional arguments:
+      -h, --help                            show this help message and exit
+
+    dalvikvm command options:
+      -cp CLASSPATH, --classpath CLASSPATH  classpath
+      --class CLASSNAME                     name of main class
+      --lib LIB                             lib to use, default: libart.so
+      --dalvikvm-option [OPT [OPT ...]]     additional dalvikvm option
+      --arg [ARG [ARG ...]]                 argument passed to test
+      --image IMAGE                         path to image
+      --raw-cmd RAW_CMD                     bisect with this command, ignore other command options
+
+    bisection options:
+      --64                                  x64 mode
+      --device                              run on device
+      --expected-output EXPECTED_OUTPUT     file containing expected output
+      --check-script CHECK_SCRIPT           script comparing output and expected output
+      --verbose                             enable verbose output
diff --git a/tools/bisection_search/__init__.py b/tools/bisection_search/__init__.py
new file mode 100644
index 0000000..0a42789
--- /dev/null
+++ b/tools/bisection_search/__init__.py
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This file is intentionally left empty. It indicates that the directory is a Python package.
diff --git a/tools/bisection_search/bisection_search.py b/tools/bisection_search/bisection_search.py
new file mode 100755
index 0000000..110ef82
--- /dev/null
+++ b/tools/bisection_search/bisection_search.py
@@ -0,0 +1,396 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Performs bisection bug search on methods and optimizations.
+
+See README.md.
+
+Example usage:
+./bisection-search.py -cp classes.dex --expected-output output Test
+"""
+
+import abc
+import argparse
+import re
+import shlex
+from subprocess import call
+import sys
+from tempfile import NamedTemporaryFile
+
+from common import DeviceTestEnv
+from common import FatalError
+from common import GetEnvVariableOrError
+from common import HostTestEnv
+
+
+# Passes that are never disabled during search process because disabling them
+# would compromise correctness.
+MANDATORY_PASSES = ['dex_cache_array_fixups_arm',
+                    'dex_cache_array_fixups_mips',
+                    'instruction_simplifier$before_codegen',
+                    'pc_relative_fixups_x86',
+                    'pc_relative_fixups_mips',
+                    'x86_memory_operand_generation']
+
+# Passes that show up as optimizations in compiler verbose output but aren't
+# driven by run-passes mechanism. They are mandatory and will always run, we
+# never pass them to --run-passes.
+NON_PASSES = ['builder', 'prepare_for_register_allocation',
+              'liveness', 'register']
+
+
+class Dex2OatWrapperTestable(object):
+  """Class representing a testable compilation.
+
+  Accepts filters on compiled methods and optimization passes.
+  """
+
+  def __init__(self, base_cmd, test_env, output_checker=None, verbose=False):
+    """Constructor.
+
+    Args:
+      base_cmd: list of strings, base command to run.
+      test_env: ITestEnv.
+      output_checker: IOutputCheck, output checker.
+      verbose: bool, enable verbose output.
+    """
+    self._base_cmd = base_cmd
+    self._test_env = test_env
+    self._output_checker = output_checker
+    self._compiled_methods_path = self._test_env.CreateFile('compiled_methods')
+    self._passes_to_run_path = self._test_env.CreateFile('run_passes')
+    self._verbose = verbose
+
+  def Test(self, compiled_methods, passes_to_run=None):
+    """Tests compilation with compiled_methods and run_passes switches active.
+
+    If compiled_methods is None then compiles all methods.
+    If passes_to_run is None then runs default passes.
+
+    Args:
+      compiled_methods: list of strings representing methods to compile or None.
+      passes_to_run: list of strings representing passes to run or None.
+
+    Returns:
+      True if test passes with given settings. False otherwise.
+    """
+    if self._verbose:
+      print('Testing methods: {0} passes: {1}.'.format(
+          compiled_methods, passes_to_run))
+    cmd = self._PrepareCmd(compiled_methods=compiled_methods,
+                           passes_to_run=passes_to_run,
+                           verbose_compiler=False)
+    (output, ret_code) = self._test_env.RunCommand(
+        cmd, {'ANDROID_LOG_TAGS': '*:e'})
+    res = ((self._output_checker is None and ret_code == 0)
+           or self._output_checker.Check(output))
+    if self._verbose:
+      print('Test passed: {0}.'.format(res))
+    return res
+
+  def GetAllMethods(self):
+    """Get methods compiled during the test.
+
+    Returns:
+      List of strings representing methods compiled during the test.
+
+    Raises:
+      FatalError: An error occurred when retrieving methods list.
+    """
+    cmd = self._PrepareCmd(verbose_compiler=True)
+    (output, _) = self._test_env.RunCommand(cmd, {'ANDROID_LOG_TAGS': '*:i'})
+    match_methods = re.findall(r'Building ([^\n]+)\n', output)
+    if not match_methods:
+      raise FatalError('Failed to retrieve methods list. '
+                       'Not recognized output format.')
+    return match_methods
+
+  def GetAllPassesForMethod(self, compiled_method):
+    """Get all optimization passes ran for a method during the test.
+
+    Args:
+      compiled_method: string representing method to compile.
+
+    Returns:
+      List of strings representing passes ran for compiled_method during test.
+
+    Raises:
+      FatalError: An error occurred when retrieving passes list.
+    """
+    cmd = self._PrepareCmd(compiled_methods=[compiled_method],
+                           verbose_compiler=True)
+    (output, _) = self._test_env.RunCommand(cmd, {'ANDROID_LOG_TAGS': '*:i'})
+    match_passes = re.findall(r'Starting pass: ([^\n]+)\n', output)
+    if not match_passes:
+      raise FatalError('Failed to retrieve passes list. '
+                       'Not recognized output format.')
+    return [p for p in match_passes if p not in NON_PASSES]
+
+  def _PrepareCmd(self, compiled_methods=None, passes_to_run=None,
+                  verbose_compiler=False):
+    """Prepare command to run."""
+    cmd = [self._base_cmd[0]]
+    # insert additional arguments
+    if compiled_methods is not None:
+      self._test_env.WriteLines(self._compiled_methods_path, compiled_methods)
+      cmd += ['-Xcompiler-option', '--compiled-methods={0}'.format(
+          self._compiled_methods_path)]
+    if passes_to_run is not None:
+      self._test_env.WriteLines(self._passes_to_run_path, passes_to_run)
+      cmd += ['-Xcompiler-option', '--run-passes={0}'.format(
+          self._passes_to_run_path)]
+    if verbose_compiler:
+      cmd += ['-Xcompiler-option', '--runtime-arg', '-Xcompiler-option',
+              '-verbose:compiler', '-Xcompiler-option', '-j1']
+    cmd += self._base_cmd[1:]
+    return cmd
+
+
+class IOutputCheck(object):
+  """Abstract output checking class.
+
+  Checks if output is correct.
+  """
+  __meta_class__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def Check(self, output):
+    """Check if output is correct.
+
+    Args:
+      output: string, output to check.
+
+    Returns:
+      boolean, True if output is correct, False otherwise.
+    """
+
+
+class EqualsOutputCheck(IOutputCheck):
+  """Concrete output checking class checking for equality to expected output."""
+
+  def __init__(self, expected_output):
+    """Constructor.
+
+    Args:
+      expected_output: string, expected output.
+    """
+    self._expected_output = expected_output
+
+  def Check(self, output):
+    """See base class."""
+    return self._expected_output == output
+
+
+class ExternalScriptOutputCheck(IOutputCheck):
+  """Concrete output checking class calling an external script.
+
+  The script should accept two arguments, path to expected output and path to
+  program output. It should exit with 0 return code if outputs are equivalent
+  and with different return code otherwise.
+  """
+
+  def __init__(self, script_path, expected_output_path, logfile):
+    """Constructor.
+
+    Args:
+      script_path: string, path to checking script.
+      expected_output_path: string, path to file with expected output.
+      logfile: file handle, logfile.
+    """
+    self._script_path = script_path
+    self._expected_output_path = expected_output_path
+    self._logfile = logfile
+
+  def Check(self, output):
+    """See base class."""
+    ret_code = None
+    with NamedTemporaryFile(mode='w', delete=False) as temp_file:
+      temp_file.write(output)
+      temp_file.flush()
+      ret_code = call(
+          [self._script_path, self._expected_output_path, temp_file.name],
+          stdout=self._logfile, stderr=self._logfile, universal_newlines=True)
+    return ret_code == 0
+
+
+def BinarySearch(start, end, test):
+  """Binary search integers using test function to guide the process."""
+  while start < end:
+    mid = (start + end) // 2
+    if test(mid):
+      start = mid + 1
+    else:
+      end = mid
+  return start
+
+
+def FilterPasses(passes, cutoff_idx):
+  """Filters passes list according to cutoff_idx but keeps mandatory passes."""
+  return [opt_pass for idx, opt_pass in enumerate(passes)
+          if opt_pass in MANDATORY_PASSES or idx < cutoff_idx]
+
+
+def BugSearch(testable):
+  """Find buggy (method, optimization pass) pair for a given testable.
+
+  Args:
+    testable: Dex2OatWrapperTestable.
+
+  Returns:
+    (string, string) tuple. First element is name of method which when compiled
+    exposes test failure. Second element is name of optimization pass such that
+    for aforementioned method running all passes up to and excluding the pass
+    results in test passing but running all passes up to and including the pass
+    results in test failing.
+
+    (None, None) if test passes when compiling all methods.
+    (string, None) if a method is found which exposes the failure, but the
+      failure happens even when running just mandatory passes.
+
+  Raises:
+    FatalError: Testable fails with no methods compiled.
+    AssertionError: Method failed for all passes when bisecting methods, but
+    passed when bisecting passes. Possible sporadic failure.
+  """
+  all_methods = testable.GetAllMethods()
+  faulty_method_idx = BinarySearch(
+      0,
+      len(all_methods) + 1,
+      lambda mid: testable.Test(all_methods[0:mid]))
+  if faulty_method_idx == len(all_methods) + 1:
+    return (None, None)
+  if faulty_method_idx == 0:
+    raise FatalError('Testable fails with no methods compiled. '
+                     'Perhaps issue lies outside of compiler.')
+  faulty_method = all_methods[faulty_method_idx - 1]
+  all_passes = testable.GetAllPassesForMethod(faulty_method)
+  faulty_pass_idx = BinarySearch(
+      0,
+      len(all_passes) + 1,
+      lambda mid: testable.Test([faulty_method],
+                                FilterPasses(all_passes, mid)))
+  if faulty_pass_idx == 0:
+    return (faulty_method, None)
+  assert faulty_pass_idx != len(all_passes) + 1, ('Method must fail for some '
+                                                  'passes.')
+  faulty_pass = all_passes[faulty_pass_idx - 1]
+  return (faulty_method, faulty_pass)
+
+
+def PrepareParser():
+  """Prepares argument parser."""
+  parser = argparse.ArgumentParser(
+      description='Tool for finding compiler bugs. Either --raw-cmd or both '
+                  '-cp and --class are required.')
+  command_opts = parser.add_argument_group('dalvikvm command options')
+  command_opts.add_argument('-cp', '--classpath', type=str, help='classpath')
+  command_opts.add_argument('--class', dest='classname', type=str,
+                            help='name of main class')
+  command_opts.add_argument('--lib', dest='lib', type=str, default='libart.so',
+                            help='lib to use, default: libart.so')
+  command_opts.add_argument('--dalvikvm-option', dest='dalvikvm_opts',
+                            metavar='OPT', nargs='*', default=[],
+                            help='additional dalvikvm option')
+  command_opts.add_argument('--arg', dest='test_args', nargs='*', default=[],
+                            metavar='ARG', help='argument passed to test')
+  command_opts.add_argument('--image', type=str, help='path to image')
+  command_opts.add_argument('--raw-cmd', dest='raw_cmd', type=str,
+                            help='bisect with this command, ignore other '
+                                 'command options')
+  bisection_opts = parser.add_argument_group('bisection options')
+  bisection_opts.add_argument('--64', dest='x64', action='store_true',
+                              default=False, help='x64 mode')
+  bisection_opts.add_argument(
+      '--device', action='store_true', default=False, help='run on device')
+  bisection_opts.add_argument('--expected-output', type=str,
+                              help='file containing expected output')
+  bisection_opts.add_argument(
+      '--check-script', dest='check_script', type=str,
+      help='script comparing output and expected output')
+  bisection_opts.add_argument('--verbose', action='store_true',
+                              default=False, help='enable verbose output')
+  return parser
+
+
+def PrepareBaseCommand(args, classpath):
+  """Prepares base command used to run test."""
+  if args.raw_cmd:
+    return shlex.split(args.raw_cmd)
+  else:
+    base_cmd = ['dalvikvm64'] if args.x64 else ['dalvikvm32']
+    if not args.device:
+      base_cmd += ['-XXlib:{0}'.format(args.lib)]
+      if not args.image:
+        image_path = '{0}/framework/core-optimizing-pic.art'.format(
+            GetEnvVariableOrError('ANDROID_HOST_OUT'))
+      else:
+        image_path = args.image
+      base_cmd += ['-Ximage:{0}'.format(image_path)]
+    if args.dalvikvm_opts:
+      base_cmd += args.dalvikvm_opts
+    base_cmd += ['-cp', classpath, args.classname] + args.test_args
+  return base_cmd
+
+
+def main():
+  # Parse arguments
+  parser = PrepareParser()
+  args = parser.parse_args()
+  if not args.raw_cmd and (not args.classpath or not args.classname):
+    parser.error('Either --raw-cmd or both -cp and --class are required')
+
+  # Prepare environment
+  classpath = args.classpath
+  if args.device:
+    test_env = DeviceTestEnv()
+    if classpath:
+      classpath = test_env.PushClasspath(classpath)
+  else:
+    test_env = HostTestEnv(args.x64)
+  base_cmd = PrepareBaseCommand(args, classpath)
+  output_checker = None
+  if args.expected_output:
+    if args.check_script:
+      output_checker = ExternalScriptOutputCheck(
+          args.check_script, args.expected_output, test_env.logfile)
+    else:
+      with open(args.expected_output, 'r') as expected_output_file:
+        output_checker = EqualsOutputCheck(expected_output_file.read())
+
+  # Perform the search
+  try:
+    testable = Dex2OatWrapperTestable(base_cmd, test_env, output_checker,
+                                      args.verbose)
+    (method, opt_pass) = BugSearch(testable)
+  except Exception as e:
+    print('Error. Refer to logfile: {0}'.format(test_env.logfile.name))
+    test_env.logfile.write('Exception: {0}\n'.format(e))
+    raise
+
+  # Report results
+  if method is None:
+    print('Couldn\'t find any bugs.')
+  elif opt_pass is None:
+    print('Faulty method: {0}. Fails with just mandatory passes.'.format(
+        method))
+  else:
+    print('Faulty method and pass: {0}, {1}.'.format(method, opt_pass))
+  print('Logfile: {0}'.format(test_env.logfile.name))
+  sys.exit(0)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/bisection-search/bisection_test.py b/tools/bisection_search/bisection_test.py
similarity index 100%
rename from tools/bisection-search/bisection_test.py
rename to tools/bisection_search/bisection_test.py
diff --git a/tools/bisection-search/common.py b/tools/bisection_search/common.py
similarity index 80%
rename from tools/bisection-search/common.py
rename to tools/bisection_search/common.py
index 8361fc9..d5029bb 100755
--- a/tools/bisection-search/common.py
+++ b/tools/bisection_search/common.py
@@ -23,6 +23,7 @@
 from subprocess import check_call
 from subprocess import PIPE
 from subprocess import Popen
+from subprocess import STDOUT
 from subprocess import TimeoutExpired
 
 from tempfile import mkdtemp
@@ -81,19 +82,20 @@
   Returns:
    tuple (string, string, int) stdout output, stderr output, return code.
   """
-  proc = Popen(cmd, stderr=PIPE, stdout=PIPE, env=env, universal_newlines=True)
+  proc = Popen(cmd, stderr=STDOUT, stdout=PIPE, env=env,
+               universal_newlines=True)
   timeouted = False
   try:
-    (output, err_output) = proc.communicate(timeout=timeout)
+    (output, _) = proc.communicate(timeout=timeout)
   except TimeoutExpired:
     timeouted = True
     proc.kill()
-    (output, err_output) = proc.communicate()
-  logfile.write('Command:\n{0}\n{1}{2}\nReturn code: {3}\n'.format(
-      _CommandListToCommandString(cmd), err_output, output,
+    (output, _) = proc.communicate()
+  logfile.write('Command:\n{0}\n{1}\nReturn code: {2}\n'.format(
+      _CommandListToCommandString(cmd), output,
       'TIMEOUT' if timeouted else proc.returncode))
   ret_code = 1 if timeouted else proc.returncode
-  return (output, err_output, ret_code)
+  return (output, ret_code)
 
 
 def _CommandListToCommandString(cmd):
@@ -148,21 +150,18 @@
     """
 
   @abc.abstractmethod
-  def RunCommand(self, cmd):
-    """Runs command in environment.
+  def RunCommand(self, cmd, env_updates=None):
+    """Runs command in environment with updated environmental variables.
 
     Args:
-      cmd: string, command to run.
-
+      cmd: list of strings, command to run.
+      env_updates: dict, string to string, maps names of variables to their
+        updated values.
     Returns:
       tuple (string, string, int) stdout output, stderr output, return code.
     """
 
   @abc.abstractproperty
-  def classpath(self):
-    """Gets environment specific classpath with test class."""
-
-  @abc.abstractproperty
   def logfile(self):
     """Gets file handle to logfile residing on host."""
 
@@ -176,14 +175,12 @@
   For methods documentation see base class.
   """
 
-  def __init__(self, classpath, x64):
+  def __init__(self, x64):
     """Constructor.
 
     Args:
-      classpath: string, classpath with test class.
       x64: boolean, whether to setup in x64 mode.
     """
-    self._classpath = classpath
     self._env_path = mkdtemp(dir='/tmp/', prefix='bisection_search_')
     self._logfile = open('{0}/log'.format(self._env_path), 'w+')
     os.mkdir('{0}/dalvik-cache'.format(self._env_path))
@@ -197,6 +194,7 @@
     self._shell_env['ANDROID_DATA'] = self._env_path
     self._shell_env['ANDROID_ROOT'] = android_root
     self._shell_env['LD_LIBRARY_PATH'] = library_path
+    self._shell_env['DYLD_LIBRARY_PATH'] = library_path
     self._shell_env['PATH'] = (path + ':' + self._shell_env['PATH'])
     # Using dlopen requires load bias on the host.
     self._shell_env['LD_USE_LOAD_BIAS'] = '1'
@@ -213,13 +211,13 @@
       f.writelines('{0}\n'.format(line) for line in lines)
     return
 
-  def RunCommand(self, cmd):
+  def RunCommand(self, cmd, env_updates=None):
+    if not env_updates:
+      env_updates = {}
     self._EmptyDexCache()
-    return _RunCommandForOutputAndLog(cmd, self._shell_env, self._logfile)
-
-  @property
-  def classpath(self):
-    return self._classpath
+    env = self._shell_env.copy()
+    env.update(env_updates)
+    return _RunCommandForOutputAndLog(cmd, env, self._logfile)
 
   @property
   def logfile(self):
@@ -247,32 +245,18 @@
   For methods documentation see base class.
   """
 
-  def __init__(self, classpath):
-    """Constructor.
-
-    Args:
-      classpath: string, classpath with test class.
-    """
+  def __init__(self):
+    """Constructor."""
     self._host_env_path = mkdtemp(dir='/tmp/', prefix='bisection_search_')
     self._logfile = open('{0}/log'.format(self._host_env_path), 'w+')
     self._device_env_path = '{0}/{1}'.format(
         DEVICE_TMP_PATH, os.path.basename(self._host_env_path))
-    self._classpath = os.path.join(
-        self._device_env_path, os.path.basename(classpath))
-    self._shell_env = os.environ
+    self._shell_env = os.environ.copy()
 
     self._AdbMkdir('{0}/dalvik-cache'.format(self._device_env_path))
     for arch_cache_path in _DexArchCachePaths(self._device_env_path):
       self._AdbMkdir(arch_cache_path)
 
-    paths = classpath.split(':')
-    device_paths = []
-    for path in paths:
-      device_paths.append('{0}/{1}'.format(
-          self._device_env_path, os.path.basename(path)))
-      self._AdbPush(path, self._device_env_path)
-    self._classpath = ':'.join(device_paths)
-
   def CreateFile(self, name=None):
     with NamedTemporaryFile(mode='w') as temp_file:
       self._AdbPush(temp_file.name, self._device_env_path)
@@ -283,25 +267,47 @@
   def WriteLines(self, file_path, lines):
     with NamedTemporaryFile(mode='w') as temp_file:
       temp_file.writelines('{0}\n'.format(line) for line in lines)
+      temp_file.flush()
       self._AdbPush(temp_file.name, file_path)
     return
 
-  def RunCommand(self, cmd):
+  def RunCommand(self, cmd, env_updates=None):
+    if not env_updates:
+      env_updates = {}
     self._EmptyDexCache()
+    if 'ANDROID_DATA' not in env_updates:
+      env_updates['ANDROID_DATA'] = self._device_env_path
+    env_updates_cmd = ' '.join(['{0}={1}'.format(var, val) for var, val
+                                in env_updates.items()])
     cmd = _CommandListToCommandString(cmd)
-    cmd = ('adb shell "logcat -c && ANDROID_DATA={0} {1} && '
-           'logcat -d dex2oat:* *:S 1>&2"').format(self._device_env_path, cmd)
-    return _RunCommandForOutputAndLog(shlex.split(cmd), self._shell_env,
-                                      self._logfile)
-
-  @property
-  def classpath(self):
-    return self._classpath
+    cmd = ('adb shell "logcat -c && {0} {1} ; logcat -d -s dex2oat:* dex2oatd:*'
+           '| grep -v "^---------" 1>&2"').format(env_updates_cmd, cmd)
+    return _RunCommandForOutputAndLog(
+        shlex.split(cmd), self._shell_env, self._logfile)
 
   @property
   def logfile(self):
     return self._logfile
 
+  def PushClasspath(self, classpath):
+    """Push classpath to on-device test directory.
+
+    Classpath can contain multiple colon separated file paths, each file is
+    pushed. Returns analogous classpath with paths valid on device.
+
+    Args:
+      classpath: string, classpath in format 'a/b/c:d/e/f'.
+    Returns:
+      string, classpath valid on device.
+    """
+    paths = classpath.split(':')
+    device_paths = []
+    for path in paths:
+      device_paths.append('{0}/{1}'.format(
+          self._device_env_path, os.path.basename(path)))
+      self._AdbPush(path, self._device_env_path)
+    return ':'.join(device_paths)
+
   def _AdbPush(self, what, where):
     check_call(shlex.split('adb push "{0}" "{1}"'.format(what, where)),
                stdout=self._logfile, stderr=self._logfile)
diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp
new file mode 100644
index 0000000..d792e90
--- /dev/null
+++ b/tools/cpp-define-generator/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2014 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.
+//
+
+// Build a "data" binary which will hold all the symbol values that will be parsed by the other scripts.
+//
+// Builds are for host only, target-specific define generation is possibly but is trickier and would need extra tooling.
+//
+// In the future we may wish to parameterize this on (32,64)x(read_barrier,no_read_barrier).
+
+art_cc_binary {
+    name: "cpp-define-generator-data",
+    host_supported: true,
+    device_supported: false,
+    defaults: [
+        "art_debug_defaults",
+        "art_defaults",
+    ],
+    include_dirs: ["art/runtime"],
+    srcs: ["main.cc"],
+    shared_libs: [
+        "libbase",
+    ],
+}
diff --git a/tools/cpp-define-generator/Android.mk b/tools/cpp-define-generator/Android.mk
deleted file mode 100644
index 6ba643c..0000000
--- a/tools/cpp-define-generator/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include art/build/Android.executable.mk
-
-CPP_DEFINE_GENERATOR_SRC_FILES := \
-  main.cc
-
-CPP_DEFINE_GENERATOR_EXTRA_SHARED_LIBRARIES :=
-CPP_DEFINE_GENERATOR_EXTRA_INCLUDE :=
-CPP_DEFINE_GENERATOR_MULTILIB :=
-
-# Build a "data" binary which will hold all the symbol values that will be parsed by the other scripts.
-#
-# Builds are for host only, target-specific define generation is possibly but is trickier and would need extra tooling.
-#
-# In the future we may wish to parameterize this on (32,64)x(read_barrier,no_read_barrier).
-$(eval $(call build-art-executable,cpp-define-generator-data,$(CPP_DEFINE_GENERATOR_SRC_FILES),$(CPP_DEFINE_GENERATOR_EXTRA_SHARED_LIBRARIES),$(CPP_DEFINE_GENERATOR_EXTRA_INCLUDE),host,debug,$(CPP_DEFINE_GENERATOR_MULTILIB),shared))
-
diff --git a/tools/dmtracedump/Android.bp b/tools/dmtracedump/Android.bp
new file mode 100644
index 0000000..4f942bd
--- /dev/null
+++ b/tools/dmtracedump/Android.bp
@@ -0,0 +1,44 @@
+// 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.
+
+// Java method trace dump tool
+
+art_cc_binary {
+    name: "dmtracedump",
+    host_supported: true,
+    device_supported: false,
+    srcs: ["tracedump.cc"],
+    cflags: [
+        "-O0",
+        "-g",
+        "-Wall",
+    ],
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
+art_cc_binary {
+    name: "create_test_dmtrace",
+    host_supported: true,
+    device_supported: false,
+    srcs: ["createtesttrace.cc"],
+    cflags: [
+        "-O0",
+        "-g",
+        "-Wall",
+    ],
+}
diff --git a/tools/dmtracedump/Android.mk b/tools/dmtracedump/Android.mk
deleted file mode 100644
index da0d632..0000000
--- a/tools/dmtracedump/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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.
-
-# Java method trace dump tool
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := tracedump.cc
-LOCAL_CFLAGS += -O0 -g -Wall
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_MODULE := dmtracedump
-include $(BUILD_HOST_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := createtesttrace.cc
-LOCAL_CFLAGS += -O0 -g -Wall
-LOCAL_MODULE := create_test_dmtrace
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/javafuzz/__init__.py b/tools/javafuzz/__init__.py
new file mode 100644
index 0000000..3955c71
--- /dev/null
+++ b/tools/javafuzz/__init__.py
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This file is intentionally left empty. It indicates that the directory is a Python package.
\ No newline at end of file