Merge "Revert "Use implicit null checks inside try blocks.""
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 04a0344..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 \
@@ -244,6 +243,11 @@
   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.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 d41b407..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)
 
@@ -137,7 +141,26 @@
 	p.Target.Android.Cflags = deviceFlags(ctx)
 	p.Target.Host.Cflags = hostFlags(ctx)
 	ctx.AppendProperties(p)
+}
 
+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 {
@@ -153,22 +176,7 @@
 	}
 }
 
-type artGlobalDefaults struct{}
-
-func (a *artLinkerCustomizer) 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 artLinkerCustomizer struct{}
+type artPrefer32BitCustomizer struct{}
 
 func init() {
 	soong.RegisterModuleType("art_cc_library", artLibrary)
@@ -200,6 +208,7 @@
 	c := &codegenCustomizer{}
 	android.AddCustomizer(library, c)
 	props = append(props, &c.codegenProperties)
+
 	return module, props
 }
 
@@ -207,7 +216,8 @@
 	binary, _ := cc.NewBinary(android.HostAndDeviceSupported)
 	module, props := binary.Init()
 
-	android.AddCustomizer(binary, &artLinkerCustomizer{})
+	android.AddCustomizer(binary, &artCustomLinkerCustomizer{})
+	android.AddCustomizer(binary, &artPrefer32BitCustomizer{})
 	return module, props
 }
 
@@ -222,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_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 5301a6b..3cc2598 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -4598,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());
   }
@@ -4716,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);
         }
       }
 
@@ -5508,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,13 +5474,11 @@
 
   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);
@@ -5589,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));
@@ -5845,7 +5745,6 @@
   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:
@@ -5854,7 +5753,6 @@
       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:
@@ -5864,9 +5762,6 @@
   }
 
   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.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 36f7b4d..179bf76 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) {}
@@ -2178,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) {
@@ -2269,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());
         }
       }
 
@@ -3385,7 +3250,6 @@
   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:
@@ -3394,7 +3258,6 @@
       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:
@@ -3404,9 +3267,6 @@
   }
 
   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.
@@ -4259,17 +4119,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());
@@ -4292,18 +4141,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 (kUseBakerReadBarrier && !load->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
-  }
-
   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) {
@@ -4311,12 +4159,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();
@@ -4337,7 +4183,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.
@@ -4347,10 +4192,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) {
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 92e9cd9..f07f8a0 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2378,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.
@@ -3177,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,
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 7ba6c0d..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,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4689ccb..e18b366 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,
@@ -5238,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));
@@ -5328,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());
         }
       }
 
@@ -6160,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());
@@ -6201,20 +6095,20 @@
 
 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);
-  if (kUseBakerReadBarrier && !load->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
-  }
-
   HLoadString::LoadKind load_kind = load->GetLoadKind();
   if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod ||
       load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
       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) {
@@ -6224,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));
@@ -6249,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() {
@@ -6518,7 +6409,6 @@
   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:
@@ -6527,7 +6417,6 @@
       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:
@@ -6536,9 +6425,6 @@
       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.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index a21a09e..15307fe 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)
@@ -4732,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());
   }
@@ -4822,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());
         }
       }
 
@@ -5592,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());
@@ -5626,18 +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 (kUseBakerReadBarrier && !load->NeedsEnvironment()) {
-    locations->SetCustomSlowPathCallerSaves(RegisterSet());  // No caller-save registers.
-  }
-
   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) {
@@ -5647,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.
@@ -5665,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() {
@@ -5940,7 +5831,6 @@
   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:
@@ -5949,7 +5839,6 @@
       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:
@@ -5958,9 +5847,6 @@
       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.
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/sharpening.cc b/compiler/optimizing/sharpening.cc
index 8d4d143..b8e1379 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -297,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/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/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/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 de02580..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_builder.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/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 630f3e4..0000000
--- a/disassembler/Android.mk
+++ /dev/null
@@ -1,165 +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
-# $(3): static or shared (static is only valid for host)
-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
-  ifeq ($(3),static)
-    ifneq ($(1),host)
-      $$(error received static for argument 3, but argument 1 is not host)
-    endif
-  else
-    ifneq ($(3),shared)
-      $$(error expected static or shared for argument 3, received $(3))
-    endif
-  endif
-
-  art_target_or_host := $(1)
-  art_ndebug_or_debug := $(2)
-  art_static_or_shared := $(3)
-
-  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
-  ifeq ($$(art_static_or_shared),static)
-    LOCAL_MODULE_CLASS := STATIC_LIBRARIES
-  else # shared
-    LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-  endif
-
-  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
-
-  ifeq ($$(art_static_or_shared),static)
-    LOCAL_STATIC_LIBRARIES += liblog
-    ifeq ($$(art_ndebug_or_debug),debug)
-      LOCAL_STATIC_LIBRARIES += libartd
-    else
-      LOCAL_STATIC_LIBRARIES += libart
-    endif
-  else # shared
-    LOCAL_SHARED_LIBRARIES += liblog
-    ifeq ($$(art_ndebug_or_debug),debug)
-      LOCAL_SHARED_LIBRARIES += libartd
-    else
-      LOCAL_SHARED_LIBRARIES += libart
-    endif
-  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_static_or_shared),static)
-    ifeq ($$(art_ndebug_or_debug),debug)
-      LOCAL_STATIC_LIBRARIES += libvixld-arm64
-    else
-      LOCAL_STATIC_LIBRARIES += libvixl-arm64
-    endif
-    ifeq ($$(art_target_or_host),target)
-      $$(error libart-disassembler static builds for target are not supported)
-    else # host
-      include $(BUILD_HOST_STATIC_LIBRARY)
-    endif
-  else # shared
-    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
-  endif
-
-  # Clear out local variables now that we're done with them.
-  art_target_or_host :=
-  art_ndebug_or_debug :=
-  art_static_or_shared :=
-endef
-
-ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-libart-disassembler,target,ndebug,shared))
-endif
-ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-libart-disassembler,target,debug,shared))
-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,shared))
-  ifeq ($(ART_BUILD_HOST_STATIC),true)
-    $(eval $(call build-libart-disassembler,host,ndebug,static))
-  endif
-endif
-ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-libart-disassembler,host,debug,shared))
-  ifeq ($(ART_BUILD_HOST_STATIC),true)
-    $(eval $(call build-libart-disassembler,host,debug,static))
-  endif
-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 4b37c6a..d80df70 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -16,36 +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))
-
-# Static variants (only for host).
-ifeq ($(ART_BUILD_HOST_STATIC),true)
-  ifeq ($(HOST_PREFER_32_BIT),true)
-    # We need to explicitly restrict the host arch to 32-bit only, as
-    # giving 'both' would make build-art-executable generate a build
-    # rule for a 64-bit oatdump executable too.
-    oatdump_host_arch := 32
-  else
-    oatdump_host_arch := both
-  endif
-
-  ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-    $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart libart-compiler libart-disassembler libvixl-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler art/disassembler,host,ndebug,$(oatdump_host_arch),static))
-  endif
-  ifeq ($(ART_BUILD_HOST_DEBUG),true)
-    $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd libartd-compiler libartd-disassembler libvixld-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler art/disassembler,host,debug,$(oatdump_host_arch),static))
-  endif
-
-  # Clear locals now they've served their purpose.
-  oatdump_host_arch :=
-endif
-
 ########################################################################
 # 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/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/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/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 0bf2a35..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
 
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/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 00394e9..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:
diff --git a/runtime/globals.h b/runtime/globals.h
index aba5661..691bf55 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -166,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/intern_table.cc b/runtime/intern_table.cc
index 1940d67..be061be 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -316,8 +316,14 @@
 
 mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
   DCHECK(utf8_data != nullptr);
+  Thread* self = Thread::Current();
+  // Try to avoid allocation.
+  mirror::String* s = LookupStrong(self, utf16_length, utf8_data);
+  if (s != nullptr) {
+    return s;
+  }
   return InternStrong(mirror::String::AllocFromModifiedUtf8(
-      Thread::Current(), utf16_length, utf8_data));
+      self, utf16_length, utf8_data));
 }
 
 mirror::String* InternTable::InternStrong(const char* utf8_data) {
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_file.cc b/runtime/oat_file.cc
index 5752fd9..76b71a3 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,
@@ -126,12 +132,17 @@
     end_ = end;
   }
 
+  void SetVdex(VdexFile* vdex) {
+    vdex_.reset(vdex);
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(OatFileBase);
 };
 
 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 +155,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 +181,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 +350,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 +392,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 +400,7 @@
                                 dex_file_location.c_str(),
                                 dex_file_offset,
                                 header->file_size_,
-                                Size());
+                                DexSize());
       return false;
     }
 
@@ -768,6 +797,7 @@
                                  std::string* error_msg);
 
   bool InitializeFromElfFile(ElfFile* elf_file,
+                             VdexFile* vdex_file,
                              const char* abs_dex_location,
                              std::string* error_msg);
 
@@ -844,6 +874,7 @@
 }
 
 bool ElfOatFile::InitializeFromElfFile(ElfFile* elf_file,
+                                       VdexFile* vdex_file,
                                        const char* abs_dex_location,
                                        std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
@@ -852,6 +883,7 @@
     return false;
   }
   elf_file_.reset(elf_file);
+  SetVdex(vdex_file);
   uint64_t offset, size;
   bool has_section = elf_file->GetSectionOffsetAndSize(".rodata", &offset, &size);
   CHECK(has_section);
@@ -933,40 +965,47 @@
 }
 
 OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
+                                  VdexFile* vdex_file,
                                   const std::string& location,
                                   const char* abs_dex_location,
                                   std::string* error_msg) {
   std::unique_ptr<ElfOatFile> oat_file(new ElfOatFile(location, false /* executable */));
-  return oat_file->InitializeFromElfFile(elf_file, abs_dex_location, error_msg)
+  return oat_file->InitializeFromElfFile(elf_file, vdex_file, abs_dex_location, error_msg)
       ? oat_file.release()
       : 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 +1014,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 +1029,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 +1076,7 @@
 
 OatFile::OatFile(const std::string& location, bool is_executable)
     : location_(location),
+      vdex_(nullptr),
       begin_(nullptr),
       end_(nullptr),
       bss_begin_(nullptr),
@@ -1071,6 +1112,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..a48791e 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.
@@ -55,7 +64,9 @@
 
   // Opens an oat file contained within the given elf file. This is always opened as
   // non-executable at the moment.
-  static OatFile* OpenWithElfFile(ElfFile* elf_file, const std::string& location,
+  static OatFile* OpenWithElfFile(ElfFile* elf_file,
+                                  VdexFile* vdex_file,
+                                  const std::string& location,
                                   const char* abs_dex_location,
                                   std::string* error_msg);
   // Open an oat file. Returns null on failure.  Requested base can
@@ -240,12 +251,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 +297,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..d8e8573 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -829,6 +829,8 @@
 
     // We are falling back to non-executable use of the oat file because patching failed, presumably
     // due to lack of space.
+    std::string vdex_filename =
+        ImageHeader::GetVdexLocationFromImageLocation(system_filename.c_str());
     std::string oat_filename =
         ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str());
     std::string oat_location =
@@ -838,22 +840,34 @@
     if (EndsWith(oat_location, ".jar")) {
       oat_location.replace(oat_location.length() - 3, 3, "oat");
     }
+    std::string error_msg;
+
+    std::unique_ptr<VdexFile> vdex_file(VdexFile::Open(vdex_filename,
+                                                       false /* writable */,
+                                                       false /* low_4gb */,
+                                                       &error_msg));
+    if (vdex_file.get() == nullptr) {
+      return false;
+    }
 
     std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str()));
     if (file.get() == nullptr) {
       return false;
     }
-    std::string error_msg;
     std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(),
-                                                    false,
-                                                    false,
-                                                    /*low_4gb*/false,
+                                                    false /* writable */,
+                                                    false /* program_header_only */,
+                                                    false /* low_4gb */,
                                                     &error_msg));
     if (elf_file.get() == nullptr) {
       return false;
     }
     std::unique_ptr<const OatFile> oat_file(
-        OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg));
+        OatFile::OpenWithElfFile(elf_file.release(),
+                                 vdex_file.release(),
+                                 oat_location,
+                                 nullptr,
+                                 &error_msg));
     if (oat_file == nullptr) {
       LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg;
       return false;
@@ -1929,6 +1943,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 4678ac6..3b5360c 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -325,6 +325,14 @@
     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/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/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/bisection-search/README.md b/tools/bisection_search/README.md
similarity index 100%
rename from tools/bisection-search/README.md
rename to tools/bisection_search/README.md
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
similarity index 100%
rename from tools/bisection-search/bisection_search.py
rename to tools/bisection_search/bisection_search.py
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 100%
rename from tools/bisection-search/common.py
rename to tools/bisection_search/common.py
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