Merge "Fix ImageWriter::ComputeEagerResolvedStringsCallback()."
diff --git a/Android.mk b/Android.mk
index d11d011..76c3aa5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -101,9 +101,11 @@
 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)/dalvikvm/Android.mk
 include $(art_path)/tools/Android.mk
+include $(art_path)/tools/dexfuzz/Android.mk
 include $(art_path)/sigchainlib/Android.mk
 
 
@@ -313,11 +315,7 @@
 
 # $(1): input jar or apk target location
 define declare-oat-target-target
-ifneq (,$(filter $(1),$(addprefix system/app/,$(addsuffix .apk,$(PRODUCT_DEX_PREOPT_PACKAGES_IN_DATA)))))
-OUT_OAT_FILE := $(call dalvik-cache-out,$(1)/classes.dex)
-else
 OUT_OAT_FILE := $(PRODUCT_OUT)/$(basename $(1)).odex
-endif
 
 ifeq ($(ONE_SHOT_MAKEFILE),)
 # ONE_SHOT_MAKEFILE is empty for a top level build and we don't want
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 39e78fa..0f756ef 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -14,12 +14,27 @@
 # limitations under the License.
 #
 
-ifndef ANDROID_COMMON_MK
-ANDROID_COMMON_MK = true
+ifndef ART_ANDROID_COMMON_MK
+ART_ANDROID_COMMON_MK = true
 
-ART_TARGET_SUPPORTED_ARCH := arm arm64 mips x86 x86_64
+ART_TARGET_SUPPORTED_ARCH := arm arm64 mips mips64 x86 x86_64
 ART_HOST_SUPPORTED_ARCH := x86 x86_64
 
+ART_COVERAGE := false
+
+ifeq ($(ART_COVERAGE),true)
+# https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html
+GCOV_PREFIX := /data/local/tmp/gcov
+# GCOV_PREFIX_STRIP is an integer that defines how many levels should be
+# stripped off the beginning of the path. We want the paths in $GCOV_PREFIX to
+# be relative to $ANDROID_BUILD_TOP so we can just adb pull from the top and not
+# have to worry about placing things ourselves.
+GCOV_PREFIX_STRIP := $(shell echo $(ANDROID_BUILD_TOP) | grep -o / | wc -l)
+GCOV_ENV := GCOV_PREFIX=$(GCOV_PREFIX) GCOV_PREFIX_STRIP=$(GCOV_PREFIX_STRIP)
+else
+GCOV_ENV :=
+endif
+
 ifeq (,$(filter $(TARGET_ARCH),$(ART_TARGET_SUPPORTED_ARCH)))
 $(warning unsupported TARGET_ARCH=$(TARGET_ARCH))
 endif
@@ -81,4 +96,4 @@
   2ND_ART_HOST_OUT_SHARED_LIBRARIES := $(2ND_HOST_OUT_SHARED_LIBRARIES)
 endif
 
-endif # ANDROID_COMMON_MK
+endif # ART_ANDROID_COMMON_MK
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 5dd9f15..3000cdf 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -14,10 +14,11 @@
 # limitations under the License.
 #
 
-ifndef ANDROID_COMMON_BUILD_MK
-ANDROID_COMMON_BUILD_MK = true
+ifndef ART_ANDROID_COMMON_BUILD_MK
+ART_ANDROID_COMMON_BUILD_MK = true
 
 include art/build/Android.common.mk
+include art/build/Android.common_utils.mk
 
 # These can be overridden via the environment or by editing to
 # enable/disable certain build configuration.
@@ -59,42 +60,11 @@
 endif
 
 #
-# Used to enable SEA mode
-#
-ART_SEA_IR_MODE := false
-ifneq ($(wildcard art/SEA_IR_ART),)
-$(info Enabling ART_SEA_IR_MODE because of existence of art/SEA_IR_ART)
-ART_SEA_IR_MODE := true
-endif
-ifeq ($(WITH_ART_SEA_IR_MODE), true)
-ART_SEA_IR_MODE := true
-endif
-
-#
-# Used to enable portable mode
-#
-ART_USE_PORTABLE_COMPILER := false
-ifneq ($(wildcard art/USE_PORTABLE_COMPILER),)
-$(info Enabling ART_USE_PORTABLE_COMPILER because of existence of art/USE_PORTABLE_COMPILER)
-ART_USE_PORTABLE_COMPILER := true
-endif
-ifeq ($(WITH_ART_USE_PORTABLE_COMPILER),true)
-$(info Enabling ART_USE_PORTABLE_COMPILER because WITH_ART_USE_PORTABLE_COMPILER=true)
-ART_USE_PORTABLE_COMPILER := true
-endif
-
-#
 # Used to change the default GC. Valid values are CMS, SS, GSS. The default is CMS.
 #
 art_default_gc_type ?= CMS
 art_default_gc_type_cflags := -DART_DEFAULT_GC_TYPE_IS_$(art_default_gc_type)
 
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-  LLVM_ROOT_PATH := external/llvm
-  # Don't fail a dalvik minimal host build.
-  -include $(LLVM_ROOT_PATH)/llvm.mk
-endif
-
 ART_HOST_CFLAGS :=
 ART_TARGET_CFLAGS :=
 
@@ -113,9 +83,19 @@
 else
 ART_TARGET_CLANG := false
 endif
+
+ifeq ($(TARGET_ARCH)|$(ART_TARGET_CLANG),mips|true)
+  # b/18807290, Clang generated mips assembly code for array.cc
+  # cannot be compiled by gas.
+  # b/18789639, Clang assembler cannot compile inlined assembly code in
+  # valgrind_malloc_space-inl.h:192:5: error: used $at without ".set noat"
+  $(warning Clang is disabled for the mips target)
+endif
 ART_TARGET_CLANG_arm :=
 ART_TARGET_CLANG_arm64 :=
-ART_TARGET_CLANG_mips :=
+# TODO: Enable clang mips when b/18807290 and b/18789639 are fixed.
+ART_TARGET_CLANG_mips := false
+ART_TARGET_CLANG_mips64 := false
 ART_TARGET_CLANG_x86 :=
 ART_TARGET_CLANG_x86_64 :=
 
@@ -131,14 +111,13 @@
 ART_TARGET_CLANG_CFLAGS_arm :=
 ART_TARGET_CLANG_CFLAGS_arm64 :=
 ART_TARGET_CLANG_CFLAGS_mips :=
+ART_TARGET_CLANG_CFLAGS_mips64 :=
 ART_TARGET_CLANG_CFLAGS_x86 :=
 ART_TARGET_CLANG_CFLAGS_x86_64 :=
 
 # These are necessary for Clang ARM64 ART builds. TODO: remove.
 ART_TARGET_CLANG_CFLAGS_arm64  += \
-  -Wno-implicit-exception-spec-mismatch \
-  -DNVALGRIND \
-  -Wno-unused-value
+  -DNVALGRIND
 
 # FIXME: upstream LLVM has a vectorizer bug that needs to be fixed
 ART_TARGET_CLANG_CFLAGS_arm64 += \
@@ -168,7 +147,7 @@
 # Suggest final: Have to move to a more recent GCC.
 #                  -Wsuggest-final-types
 
-
+ART_TARGET_CLANG_CFLAGS := $(art_clang_cflags)
 ifeq ($(ART_HOST_CLANG),true)
   # Bug: 15446488. We don't omit the frame pointer to work around
   # clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
@@ -176,10 +155,14 @@
 else
   ART_HOST_CFLAGS += $(art_gcc_cflags)
 endif
-ifeq ($(ART_TARGET_CLANG),true)
-  ART_TARGET_CFLAGS += $(art_clang_cflags)
-else
+ifneq ($(ART_TARGET_CLANG),true)
   ART_TARGET_CFLAGS += $(art_gcc_cflags)
+else
+  # TODO: if we ever want to support GCC/Clang mix for multi-target products, this needs to be
+  #       split up.
+  ifeq ($(ART_TARGET_CLANG_$(TARGET_ARCH)),false)
+    ART_TARGET_CFLAGS += $(art_gcc_cflags)
+  endif
 endif
 
 # Clear local variables now their use has ended.
@@ -194,7 +177,6 @@
   external/valgrind/main \
   external/vixl/src \
   external/zlib \
-  frameworks/compile/mclinker/include
 
 # Base set of cflags used by all things ART.
 art_cflags := \
@@ -207,7 +189,6 @@
   -Wstrict-aliasing \
   -fstrict-aliasing \
   -Wunreachable-code \
-  -Wno-conversion-null \
   -Wredundant-decls \
   -Wshadow \
   -fvisibility=protected \
@@ -229,14 +210,18 @@
   art_cflags += -DART_SMALL_MODE=1
 endif
 
-ifeq ($(ART_SEA_IR_MODE),true)
-  art_cflags += -DART_SEA_IR_MODE=1
-endif
-
 ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
   art_cflags += -DART_USE_OPTIMIZING_COMPILER=1
 endif
 
+ifeq ($(ART_HEAP_POISONING),true)
+  art_cflags += -DART_HEAP_POISONING=1
+endif
+
+ifeq ($(ART_USE_READ_BARRIER),true)
+  art_cflags += -DART_USE_READ_BARRIER=1
+endif
+
 # Cflags for non-debug ART and ART tools.
 art_non_debug_cflags := \
   -O3
@@ -253,10 +238,14 @@
 
 ifeq ($(HOST_OS),linux)
   # Larger frame-size for host clang builds today
-  ifndef SANITIZE_HOST
-    art_host_non_debug_cflags += -Wframe-larger-than=2700
+  ifneq ($(ART_COVERAGE),true)
+    ifneq ($(NATIVE_COVERAGE),true)
+      ifndef SANITIZE_HOST
+        art_host_non_debug_cflags += -Wframe-larger-than=2700
+      endif
+      art_target_non_debug_cflags += -Wframe-larger-than=1728
+    endif
   endif
-  art_target_non_debug_cflags += -Wframe-larger-than=1728
 endif
 
 ifndef LIBART_IMG_HOST_BASE_ADDRESS
@@ -325,11 +314,9 @@
     LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS)
   endif
 
-  # TODO: Also set when ART_TARGET_CLANG_$(arch)!=false and ART_TARGET_CLANG==true
+  LOCAL_CLANG_CFLAGS := $(ART_TARGET_CLANG_CFLAGS)
   $(foreach arch,$(ART_SUPPORTED_ARCH),
-    ifeq ($$(ART_TARGET_CLANG_$(arch)),true)
-      LOCAL_CFLAGS_$(arch) += $$(ART_TARGET_CLANG_CFLAGS_$(arch))
-  endif)
+    LOCAL_CLANG_CFLAGS_$(arch) += $$(ART_TARGET_CLANG_CFLAGS_$(arch)))
 
   # Clear locally used variables.
   art_target_cflags_ndebug_or_debug :=
@@ -357,4 +344,4 @@
   ART_BUILD_DEBUG := true
 endif
 
-endif # ANDROID_COMMON_BUILD_MK
+endif # ART_ANDROID_COMMON_BUILD_MK
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 281d189..e0c0b0c 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -14,8 +14,8 @@
 # limitations under the License.
 #
 
-ifndef ANDROID_COMMON_PATH_MK
-ANDROID_COMMON_PATH_MK := true
+ifndef ART_ANDROID_COMMON_PATH_MK
+ART_ANDROID_COMMON_PATH_MK := true
 
 include art/build/Android.common.mk
 
@@ -88,4 +88,4 @@
 
 HOST_CORE_DEX_FILES   := $(foreach jar,$(HOST_CORE_JARS),  $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar)
 TARGET_CORE_DEX_FILES := $(foreach jar,$(TARGET_CORE_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar), ,COMMON)/javalib.jar)
-endif # ANDROID_COMMON_PATH_MK
+endif # ART_ANDROID_COMMON_PATH_MK
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 2493565..da50d53 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -14,8 +14,8 @@
 # limitations under the License.
 #
 
-ifndef ANDROID_COMMON_TEST_MK
-ANDROID_COMMON_TEST_MK = true
+ifndef ART_ANDROID_COMMON_TEST_MK
+ART_ANDROID_COMMON_TEST_MK = true
 
 include art/build/Android.common_path.mk
 
@@ -25,21 +25,7 @@
 
 # List of known broken tests that we won't attempt to execute. The test name must be the full
 # rule name such as test-art-host-oat-optimizing-HelloWorld64.
-ART_TEST_KNOWN_BROKEN := \
-  test-art-target-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
-  test-art-target-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
-  test-art-target-run-test-gcstress-default-prebuild-004-SignalTest32 \
-  test-art-target-run-test-gcstress-default-norelocate-004-SignalTest32 \
-  test-art-target-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
-  test-art-target-run-test-gcstress-default-relocate-004-SignalTest32 \
-  test-art-target-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
-  test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32 \
-  test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC32 \
-  test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC32 \
-  test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC32 \
-  test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC64 \
-  test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC64 \
-  test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC64
+ART_TEST_KNOWN_BROKEN :=
 
 # Failing valgrind tests.
 # Note: *all* 64b tests involving the runtime do not work currently. b/15170219.
@@ -197,4 +183,4 @@
   endif
 endef
 
-endif # ANDROID_COMMON_TEST_MK
+endif # ART_ANDROID_COMMON_TEST_MK
diff --git a/build/Android.common_utils.mk b/build/Android.common_utils.mk
new file mode 100644
index 0000000..8069c3a
--- /dev/null
+++ b/build/Android.common_utils.mk
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+ifndef ART_ANDROID_COMMON_UTILS_MK
+ART_ANDROID_COMMON_UTILS_MK = true
+
+#
+# Convert a string into an uppercase string.
+#
+# $(1): a string which should be made uppercase
+art-string-to-uppercase = $(shell echo $(1) | tr '[:lower:]' '[:upper:]')
+
+endif # ART_ANDROID_COMMON_UTILS_MK
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 86f445f..dfea6e1 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -20,9 +20,6 @@
 ART_TARGET_EXECUTABLES ?=
 
 ART_EXECUTABLES_CFLAGS :=
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-  ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
 
 # $(1): executable ("d" will be appended for debug version)
 # $(2): source
@@ -50,12 +47,13 @@
   art_target_or_host := $(5)
   art_ndebug_or_debug := $(6)
   art_multilib := $(7)
+  art_out_binary_name :=
 
   include $(CLEAR_VARS)
   LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
   LOCAL_MODULE_TAGS := optional
   LOCAL_SRC_FILES := $$(art_source)
-  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes)
+  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime art/cmdline $$(art_c_includes)
   LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries)
   LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
 
@@ -66,9 +64,9 @@
   endif
 
   LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS)
-  # Mac OS linker doesn't understand --export-dynamic/--version-script.
+  # Mac OS linker doesn't understand --export-dynamic.
   ifneq ($$(HOST_OS)-$$(art_target_or_host),darwin-host)
-    LOCAL_LDFLAGS := -Wl,--version-script,art/sigchainlib/version-script.txt -Wl,--export-dynamic
+    LOCAL_LDFLAGS := -Wl,--export-dynamic
   endif
 
   ifeq ($$(art_target_or_host),target)
@@ -94,21 +92,115 @@
   endif
 
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+  LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_utils.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.executable.mk
 
   ifeq ($$(art_target_or_host),target)
     LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
   endif
-  LOCAL_MULTILIB := $$(art_multilib)
 
-  include external/libcxx/libcxx.mk
+  LOCAL_MULTILIB := $$(art_multilib)
+  art_out_binary_name := $$(LOCAL_MODULE)
+
+  # If multilib=both (potentially building both 32-bit and 64-bit), need to provide stem.
+  ifeq ($$(art_multilib),both)
+    # Set up a 32-bit/64-bit stem if we are building both binaries.
+    # In this case, the 32-bit binary has an additional 32-bit suffix.
+    LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)32
+    LOCAL_MODULE_STEM_64 := $$(LOCAL_MODULE)
+
+    # Remember the binary names so we can add them to the global art executables list later.
+    art_out_binary_name := $$(LOCAL_MODULE_STEM_32) $$(LOCAL_MODULE_STEM_64)
+
+    # For single-architecture targets, remove any binary name suffixes.
+    ifeq ($$(art_target_or_host),target)
+      ifeq (,$(TARGET_2ND_ARCH))
+        LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)
+        art_out_binary_name := $$(LOCAL_MODULE)
+      endif
+    endif
+
+    # For single-architecture hosts, remove any binary name suffixes.
+    ifeq ($$(art_target_or_host),host)
+      ifeq (,$(HOST_2ND_ARCH))
+        LOCAL_MODULE_STEM_32 := $$(LOCAL_MODULE)
+        art_out_binary_name := $$(LOCAL_MODULE)
+      endif
+    endif
+  endif
+
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
   ifeq ($$(art_target_or_host),target)
     include $(BUILD_EXECUTABLE)
-    ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $(TARGET_OUT_EXECUTABLES)/$$(LOCAL_MODULE)
+    ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $$(foreach name,$$(art_out_binary_name),$(TARGET_OUT_EXECUTABLES)/$$(name))
   else # host
     LOCAL_IS_HOST_MODULE := true
     include $(BUILD_HOST_EXECUTABLE)
-    ART_HOST_EXECUTABLES := $(ART_HOST_EXECUTABLES) $(HOST_OUT_EXECUTABLES)/$$(LOCAL_MODULE)
+    ART_HOST_EXECUTABLES := $(ART_HOST_EXECUTABLES) $$(foreach name,$$(art_out_binary_name),$(HOST_OUT_EXECUTABLES)/$$(name))
   endif
 
+  # Clear out local variables now that we're done with them.
+  art_executable :=
+  art_source :=
+  art_shared_libraries :=
+  art_c_includes :=
+  art_target_or_host :=
+  art_ndebug_or_debug :=
+  art_multilib :=
+  art_out_binary_name :=
+
+endef
+
+#
+# Build many art executables from multiple variations (debug/ndebug, host/target, 32/64bit).
+# By default only either 32-bit or 64-bit is built (but not both -- see multilib arg).
+# All other variations are gated by ANDROID_BUILD_(TARGET|HOST)_[N]DEBUG.
+# The result must be eval-uated.
+#
+# $(1): executable name
+# $(2): source files
+# $(3): library dependencies (common); debug prefix is added on as necessary automatically.
+# $(4): library dependencies (target only)
+# $(5): library dependencies (host only)
+# $(6): extra include directories
+# $(7): multilib (default: empty), valid values: {,32,64,both})
+define build-art-multi-executable
+  $(foreach debug_flavor,ndebug debug,
+    $(foreach target_flavor,host target,
+      art-multi-binary-name := $(1)
+      art-multi-source-files := $(2)
+      art-multi-lib-dependencies := $(3)
+      art-multi-lib-dependencies-target := $(4)
+      art-multi-lib-dependencies-host := $(5)
+      art-multi-include-extra := $(6)
+      art-multi-multilib := $(7)
+
+      # Add either -host or -target specific lib dependencies to the lib dependencies.
+      art-multi-lib-dependencies += $$(art-multi-lib-dependencies-$(target_flavor))
+
+      # Replace libart- prefix with libartd- for debug flavor.
+      ifeq ($(debug_flavor),debug)
+        art-multi-lib-dependencies := $$(subst libart-,libartd-,$$(art-multi-lib-dependencies))
+      endif
+
+      # Build the env guard var name, e.g. ART_BUILD_HOST_NDEBUG.
+      art-multi-env-guard := $$(call art-string-to-uppercase,ART_BUILD_$(target_flavor)_$(debug_flavor))
+
+      # Build the art executable only if the corresponding env guard was set.
+      ifeq ($$($$(art-multi-env-guard)),true)
+        $$(eval $$(call build-art-executable,$$(art-multi-binary-name),$$(art-multi-source-files),$$(art-multi-lib-dependencies),$$(art-multi-include-extra),$(target_flavor),$(debug_flavor),$$(art-multi-multilib)))
+      endif
+
+      # Clear locals now they've served their purpose.
+      art-multi-binary-name :=
+      art-multi-source-files :=
+      art-multi-lib-dependencies :=
+      art-multi-lib-dependencies-target :=
+      art-multi-lib-dependencies-host :=
+      art-multi-include-extra :=
+      art-multi-multilib :=
+      art-multi-env-guard :=
+    )
+  )
 endef
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 10b0400..06d258d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -65,10 +65,24 @@
 # TODO: document why this is needed.
 ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
 
+# The imgdiag test has dependencies on core.oat since it needs to load it during the test.
+# For the host, also add the installed tool (in the base size, that should suffice). For the
+# target, just the module is fine, the sync will happen late enough.
+ART_GTEST_imgdiag_test_HOST_DEPS := \
+  $(HOST_CORE_IMAGE_default_no-pic_64) \
+  $(HOST_CORE_IMAGE_default_no-pic_32) \
+  $(HOST_OUT_EXECUTABLES)/imgdiagd
+ART_GTEST_imgdiag_test_TARGET_DEPS := \
+  $(TARGET_CORE_IMAGE_default_no-pic_64) \
+  $(TARGET_CORE_IMAGE_default_no-pic_32) \
+  imgdiagd
+
 # The path for which all the source files are relative, not actually the current directory.
 LOCAL_PATH := art
 
 RUNTIME_GTEST_COMMON_SRC_FILES := \
+  cmdline/cmdline_parser_test.cc \
+  imgdiag/imgdiag_test.cc \
   runtime/arch/arch_test.cc \
   runtime/arch/instruction_set_test.cc \
   runtime/arch/instruction_set_features_test.cc \
@@ -77,6 +91,7 @@
   runtime/arch/arm/instruction_set_features_arm_test.cc \
   runtime/arch/arm64/instruction_set_features_arm64_test.cc \
   runtime/arch/mips/instruction_set_features_mips_test.cc \
+  runtime/arch/mips64/instruction_set_features_mips64_test.cc \
   runtime/arch/x86/instruction_set_features_x86_test.cc \
   runtime/arch/x86_64/instruction_set_features_x86_64_test.cc \
   runtime/barrier_test.cc \
@@ -89,10 +104,8 @@
   runtime/base/scoped_flock_test.cc \
   runtime/base/stringprintf_test.cc \
   runtime/base/timing_logger_test.cc \
+  runtime/base/variant_map_test.cc \
   runtime/base/unix_file/fd_file_test.cc \
-  runtime/base/unix_file/null_file_test.cc \
-  runtime/base/unix_file/random_access_file_utils_test.cc \
-  runtime/base/unix_file/string_file_test.cc \
   runtime/class_linker_test.cc \
   runtime/dex_file_test.cc \
   runtime/dex_file_verifier_test.cc \
@@ -105,6 +118,7 @@
   runtime/gc/accounting/card_table_test.cc \
   runtime/gc/accounting/space_bitmap_test.cc \
   runtime/gc/heap_test.cc \
+  runtime/gc/reference_queue_test.cc \
   runtime/gc/space/dlmalloc_space_base_test.cc \
   runtime/gc/space/dlmalloc_space_static_test.cc \
   runtime/gc/space/dlmalloc_space_random_test.cc \
@@ -112,6 +126,7 @@
   runtime/gc/space/rosalloc_space_static_test.cc \
   runtime/gc/space/rosalloc_space_random_test.cc \
   runtime/gc/space/large_object_space_test.cc \
+  runtime/gc/task_processor_test.cc \
   runtime/gtest_test.cc \
   runtime/handle_scope_test.cc \
   runtime/indenter_test.cc \
@@ -147,6 +162,7 @@
   compiler/image_test.cc \
   compiler/jni/jni_compiler_test.cc \
   compiler/oat_test.cc \
+  compiler/optimizing/bounds_check_elimination_test.cc \
   compiler/optimizing/codegen_test.cc \
   compiler/optimizing/dead_code_elimination_test.cc \
   compiler/optimizing/constant_folding_test.cc \
@@ -169,18 +185,11 @@
   compiler/output_stream_test.cc \
   compiler/utils/arena_allocator_test.cc \
   compiler/utils/dedupe_set_test.cc \
+  compiler/utils/swap_space_test.cc \
   compiler/utils/arm/managed_register_arm_test.cc \
   compiler/utils/arm64/managed_register_arm64_test.cc \
   compiler/utils/x86/managed_register_x86_test.cc \
 
-ifeq ($(ART_SEA_IR_MODE),true)
-COMPILER_GTEST_COMMON_SRC_FILES += \
-  compiler/utils/scoped_hashtable_test.cc \
-  compiler/sea_ir/types/type_data_test.cc \
-  compiler/sea_ir/types/type_inference_visitor_test.cc \
-  compiler/sea_ir/ir/regions_test.cc
-endif
-
 RUNTIME_GTEST_TARGET_SRC_FILES := \
   $(RUNTIME_GTEST_COMMON_SRC_FILES)
 
@@ -199,23 +208,19 @@
   compiler/utils/x86_64/assembler_x86_64_test.cc
 
 ART_TEST_CFLAGS :=
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-  ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libart-gtest
 LOCAL_MODULE_TAGS := optional
 LOCAL_CPP_EXTENSION := cc
-LOCAL_CFLAGS := $(ART_TARGET_CFLAGS)
 LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
 LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler
 LOCAL_SHARED_LIBRARIES := libartd libartd-compiler libdl
 LOCAL_STATIC_LIBRARIES += libgtest
-LOCAL_CLANG := $(ART_TARGET_CLANG)
 LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
-include external/libcxx/libcxx.mk
+$(eval $(call set-target-local-clang-vars))
+$(eval $(call set-target-local-cflags-vars,debug))
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -232,7 +237,6 @@
 LOCAL_CLANG := $(ART_HOST_CLANG)
 LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
-include external/libcxx/libcxx.mk
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 # Variables holding collections of gtest pre-requisits used to run a number of gtests.
@@ -273,7 +277,7 @@
 	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
 	$(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+	  (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
 	    $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
 	  && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \
 	      && $$(call ART_TEST_PASSED,$$@)) \
@@ -364,7 +368,6 @@
   endif
 
   LOCAL_CFLAGS := $$(ART_TEST_CFLAGS)
-  include external/libcxx/libcxx.mk
   ifeq ($$(art_target_or_host),target)
     $$(eval $$(call set-target-local-clang-vars))
     $$(eval $$(call set-target-local-cflags-vars,debug))
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 523d143..8d49565 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -34,6 +34,8 @@
 # $(1): compiler - default, optimizing or interpreter.
 # $(2): pic/no-pic
 # $(3): 2ND_ or undefined, 2ND_ for 32-bit host builds.
+# $(4): wrapper, e.g., valgrind.
+# $(5): dex2oat suffix, e.g, valgrind requires 32 right now.
 # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
 # run-test --no-image
 define create-core-oat-host-rules
@@ -44,13 +46,17 @@
   core_pic_infix :=
   core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
 
+  # With the optimizing compiler, we want to rerun dex2oat whenever there is
+  # a dex2oat change to catch regressions early.
+  ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
+    core_dex2oat_dependency := $(DEX2OAT)
+  endif
+
   ifeq ($(1),default)
     core_compile_options += --compiler-backend=Quick
   endif
   ifeq ($(1),optimizing)
     core_compile_options += --compiler-backend=Optimizing
-    # With the optimizing compiler, we want to rerun dex2oat whenever there is
-    # a dex2oat change to catch regressions early.
     core_dex2oat_dependency := $(DEX2OAT)
     core_infix := -optimizing
   endif
@@ -78,25 +84,30 @@
     $$(error found $(2) expected pic or no-pic)
   endif
 
-  core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_IMG_SUFFIX)
-  core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_OAT_SUFFIX)
+  core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX)
 
   # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
   ifeq ($(3),)
-    HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+    $(4)HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
   else
-    HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+    $(4)HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
   endif
-  HOST_CORE_IMG_OUTS += $$(core_image_name)
-  HOST_CORE_OAT_OUTS += $$(core_oat_name)
+  $(4)HOST_CORE_IMG_OUTS += $$(core_image_name)
+  $(4)HOST_CORE_OAT_OUTS += $$(core_oat_name)
 
+  # If we have a wrapper, make the target phony.
+  ifneq ($(4),)
+.PHONY: $$(core_image_name)
+  endif
 $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
 $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
 	@echo "host dex2oat: $$@ ($$?)"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+	$$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
 	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
@@ -117,20 +128,29 @@
 endef  # create-core-oat-host-rules
 
 # $(1): compiler - default, optimizing or interpreter.
+# $(2): wrapper.
+# $(3): dex2oat suffix.
 define create-core-oat-host-rule-combination
-  $(call create-core-oat-host-rules,$(1),no-pic,)
-  $(call create-core-oat-host-rules,$(1),pic,)
+  $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3))
+  $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3))
 
   ifneq ($(HOST_PREFER_32_BIT),true)
-    $(call create-core-oat-host-rules,$(1),no-pic,2ND_)
-    $(call create-core-oat-host-rules,$(1),pic,2ND_)
+    $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3))
+    $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3))
   endif
 endef
 
-$(eval $(call create-core-oat-host-rule-combination,default))
-$(eval $(call create-core-oat-host-rule-combination,optimizing))
-$(eval $(call create-core-oat-host-rule-combination,interpreter))
+$(eval $(call create-core-oat-host-rule-combination,default,,))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,,))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,,))
 
+valgrindHOST_CORE_IMG_OUTS :=
+valgrindHOST_CORE_OAT_OUTS :=
+$(eval $(call create-core-oat-host-rule-combination,default,valgrind,32))
+$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32))
+$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32))
+
+valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS)
 
 define create-core-oat-target-rules
   core_compile_options :=
@@ -140,20 +160,18 @@
   core_pic_infix :=
   core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
 
+  # With the optimizing compiler, we want to rerun dex2oat whenever there is
+  # a dex2oat change to catch regressions early.
+  ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
+    core_dex2oat_dependency := $(DEX2OAT)
+  endif
+
   ifeq ($(1),default)
     core_compile_options += --compiler-backend=Quick
   endif
   ifeq ($(1),optimizing)
-    ifeq ($($(3)TARGET_ARCH),arm64)
-      # TODO: Enable image generation on arm64 once the backend
-      # is on par with other architectures.
-      core_compile_options += --compiler-backend=Quick
-    else
-      core_compile_options += --compiler-backend=Optimizing
-      # With the optimizing compiler, we want to rerun dex2oat whenever there is
-      # a dex2oat change to catch regressions early.
-      core_dex2oat_dependency := $(DEX2OAT)
-    endif
+    core_compile_options += --compiler-backend=Optimizing
+    core_dex2oat_dependency := $(DEX2OAT)
     core_infix := -optimizing
   endif
   ifeq ($(1),interpreter)
@@ -180,29 +198,34 @@
     $$(error found $(2) expected pic or no-pic)
   endif
 
-  core_image_name := $($(3)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_IMG_SUFFIX)
-  core_oat_name := $($(3)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(CORE_OAT_SUFFIX)
+  core_image_name := $($(3)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX)
+  core_oat_name := $($(3)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX)
 
   # Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
   ifeq ($(3),)
     ifdef TARGET_2ND_ARCH
-      TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+      $(4)TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
     else
-      TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+      $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
     endif
   else
-    TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+    $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
   endif
-  TARGET_CORE_IMG_OUTS += $$(core_image_name)
-  TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+  $(4)TARGET_CORE_IMG_OUTS += $$(core_image_name)
+  $(4)TARGET_CORE_OAT_OUTS += $$(core_oat_name)
 
+  # If we have a wrapper, make the target phony.
+  ifneq ($(4),)
+.PHONY: $$(core_image_name)
+  endif
 $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
 $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
 $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
 $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
 	@echo "target dex2oat: $$@ ($$?)"
 	@mkdir -p $$(dir $$@)
-	$$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
+	$$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+	  --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
 	  --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \
 	  $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
 	  --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
@@ -223,16 +246,28 @@
 endef  # create-core-oat-target-rules
 
 # $(1): compiler - default, optimizing or interpreter.
+# $(2): wrapper.
+# $(3): dex2oat suffix.
 define create-core-oat-target-rule-combination
-  $(call create-core-oat-target-rules,$(1),no-pic,)
-  $(call create-core-oat-target-rules,$(1),pic,)
+  $(call create-core-oat-target-rules,$(1),no-pic,,$(2),$(3))
+  $(call create-core-oat-target-rules,$(1),pic,,$(2),$(3))
 
   ifdef TARGET_2ND_ARCH
-    $(call create-core-oat-target-rules,$(1),no-pic,2ND_)
-    $(call create-core-oat-target-rules,$(1),pic,2ND_)
+    $(call create-core-oat-target-rules,$(1),no-pic,2ND_,$(2),$(3))
+    $(call create-core-oat-target-rules,$(1),pic,2ND_,$(2),$(3))
   endif
 endef
 
-$(eval $(call create-core-oat-target-rule-combination,default))
-$(eval $(call create-core-oat-target-rule-combination,optimizing))
-$(eval $(call create-core-oat-target-rule-combination,interpreter))
+$(eval $(call create-core-oat-target-rule-combination,default,,))
+$(eval $(call create-core-oat-target-rule-combination,optimizing,,))
+$(eval $(call create-core-oat-target-rule-combination,interpreter,,))
+
+valgrindTARGET_CORE_IMG_OUTS :=
+valgrindTARGET_CORE_OAT_OUTS :=
+$(eval $(call create-core-oat-target-rule-combination,default,valgrind,32))
+$(eval $(call create-core-oat-target-rule-combination,optimizing,valgrind,32))
+$(eval $(call create-core-oat-target-rule-combination,interpreter,valgrind,32))
+
+valgrind-test-art-host-dex2oat-target: $(valgrindTARGET_CORE_IMG_OUTS)
+
+valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-test-art-host-dex2oat-target
diff --git a/cmdline/README.md b/cmdline/README.md
new file mode 100644
index 0000000..8cac77f
--- /dev/null
+++ b/cmdline/README.md
@@ -0,0 +1,245 @@
+Cmdline
+===================
+
+Introduction
+-------------
+This directory contains the classes that do common command line tool initialization and parsing. The
+long term goal is eventually for all `art` command-line tools to be using these helpers.
+
+----------
+
+
+## Cmdline Parser
+-------------
+
+The `CmdlineParser` class provides a fluent interface using a domain-specific language to quickly
+generate a type-safe value parser that process a user-provided list of strings (`argv`). Currently,
+it can parse a string into a `VariantMap`, although in the future it might be desirable to parse
+into any struct of any field.
+
+To use, create a `CmdlineParser::Builder` and then chain the `Define` methods together with
+`WithType` and `IntoXX` methods.
+
+### Quick Start
+For example, to save the values into a user-defined variant map:
+
+```
+struct FruitVariantMap : VariantMap {
+  static const Key<int> Apple;
+  static const Key<double> Orange;
+  static const Key<bool> Help;
+};
+// Note that some template boilerplate has been avoided for clarity.
+// See variant_map_test.cc for how to completely define a custom map.
+
+using FruitParser = CmdlineParser<FruitVariantMap, FruitVariantMap::Key>;
+
+FruitParser MakeParser() {
+  auto&& builder = FruitParser::Builder();
+  builder.
+   .Define("--help")
+      .IntoKey(FruitVariantMap::Help)
+    Define("--apple:_")
+      .WithType<int>()
+      .IntoKey(FruitVariantMap::Apple)
+   .Define("--orange:_")
+      .WithType<double>()
+      .WithRange(0.0, 1.0)
+      .IntoKey(FruitVariantMap::Orange);
+
+  return builder.Build();
+}
+
+int main(char** argv, int argc) {
+  auto parser = MakeParser();
+  auto result = parser.parse(argv, argc));
+  if (result.isError()) {
+     std::cerr << result.getMessage() << std::endl;
+     return EXIT_FAILURE;
+  }
+  auto map = parser.GetArgumentsMap();
+  std::cout << "Help? " << map.GetOrDefault(FruitVariantMap::Help) << std::endl;
+  std::cout << "Apple? " << map.GetOrDefault(FruitVariantMap::Apple) << std::endl;
+  std::cout << "Orange? " << map.GetOrDefault(FruitVariantMap::Orange) << std::endl;
+
+  return EXIT_SUCCESS;
+}
+```
+
+In the above code sample, we define a parser which is capable of parsing something like `--help
+--apple:123 --orange:0.456` . It will error out automatically if invalid flags are given, or if the
+appropriate flags are given but of the the wrong type/range. So for example, `--foo` will not parse
+(invalid argument), neither will `--apple:fruit` (fruit is not an int) nor `--orange:1234` (1234 is
+out of range of [0.0, 1.0])
+
+### Argument Definitions in Detail
+#### Define method
+The 'Define' method takes one or more aliases for the argument. Common examples might be `{"-h",
+"--help"}` where both `--help` and `-h` are aliases for the same argument.
+
+The simplest kind of argument just tests for presence, but we often want to parse out a particular
+type of value (such as an int or double as in the above `FruitVariantMap` example). To do that, a
+_wildcard_ must be used to denote the location within the token that the type will be parsed out of.
+
+For example with `-orange:_` the parse would know to check all tokens in an `argv` list for the
+`-orange:` prefix and then strip it, leaving only the remains to be parsed.
+
+#### WithType method (optional)
+After an argument definition is provided, the parser builder needs to know what type the argument
+will be in order to provide the type safety and make sure the rest of the argument definition is
+correct as early as possible (in essence, everything but the parsing of the argument name is done at
+compile time).
+
+Everything that follows a `WithType<T>()` call is thus type checked to only take `T` values.
+
+If this call is omitted, the parser generator assumes you are building a `Unit` type (i.e. an
+argument that only cares about presence).
+
+#### WithRange method (optional)
+Some values will not make sense outside of a `[min, max]` range, so this is an option to quickly add
+a range check without writing custom code. The range check is performed after the main parsing
+happens and happens for any type implementing the `<=` operators.
+
+#### WithValueMap (optional)
+When parsing an enumeration, it might be very convenient to map a list of possible argument string
+values into its runtime value.
+
+With something like
+```
+    .Define("-hello:_")
+      .WithValueMap({"world", kWorld},
+                    {"galaxy", kGalaxy})
+```
+It will parse either `-hello:world` or `-hello:galaxy` only (and error out on other variations of
+`-hello:whatever`), converting it to the type-safe value of `kWorld` or `kGalaxy` respectively.
+
+This is meant to be another shorthand (like `WithRange`) to avoid writing a custom type parser. In
+general it takes a variadic number of `pair<const char* /*arg name*/, T /*value*/>`.
+
+#### WithValues (optional)
+When an argument definition has multiple aliases with no wildcards, it might be convenient to
+quickly map them into discrete values.
+
+For example:
+```
+  .Define({"-xinterpret", "-xnointerpret"})
+    .WithValues({true, false}
+```
+It will parse `-xinterpret` as `true` and `-xnointerpret` as `false`.
+
+In general, it uses the position of the argument alias to map into the WithValues position value.
+
+(Note that this method will not work when the argument definitions have a wildcard because there is
+no way to position-ally match that).
+
+#### AppendValues (optional)
+By default, the argument is assumed to appear exactly once, and if the user specifies it more than
+once, only the latest value is taken into account (and all previous occurrences of the argument are
+ignored).
+
+In some situations, we may want to accumulate the argument values instead of discarding the previous
+ones.
+
+For example
+```
+  .Define("-D")
+     .WithType<std::vector<std::string>)()
+     .AppendValues()
+```
+Will parse something like `-Dhello -Dworld -Dbar -Dbaz` into `std::vector<std::string>{"hello",
+"world", "bar", "baz"}`.
+
+### Setting an argument parse target (required)
+To complete an argument definition, the parser generator also needs to know where to save values.
+Currently, only `IntoKey` is supported, but that may change in the future.
+
+#### IntoKey (required)
+This specifies that when a value is parsed, it will get saved into a variant map using the specific
+key.
+
+For example,
+```
+   .Define("-help")
+     .IntoKey(Map::Help)
+```
+will save occurrences of the `-help` argument by doing a `Map.Set(Map::Help, ParsedValue("-help"))`
+where `ParsedValue` is an imaginary function that parses the `-help` argment into a specific type
+set by `WithType`.
+
+### Ignoring unknown arguments
+This is highly discouraged, but for compatibility with `JNI` which allows argument ignores, there is
+an option to ignore any argument tokens that are not known to the parser. This is done with the
+`Ignore` function which takes a list of argument definition names.
+
+It's semantically equivalent to making a series of argument definitions that map to `Unit` but don't
+get saved anywhere. Values will still get parsed as normal, so it will *not* ignore known arguments
+with invalid values, only user-arguments for which it could not find a matching argument definition.
+
+### Parsing custom types
+Any type can be parsed from a string by specializing the `CmdlineType` class and implementing the
+static interface provided by `CmdlineTypeParser`. It is recommended to inherit from
+`CmdlineTypeParser` since it already provides default implementations for every method.
+
+The `Parse` method should be implemented for most types. Some types will allow appending (such as an
+`std::vector<std::string>` and are meant to be used with `AppendValues` in which case the
+`ParseAndAppend` function should be implemented.
+
+For example:
+```
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+  Result Parse(const std::string& str) {
+    char* end = nullptr;
+    errno = 0;
+    double value = strtod(str.c_str(), &end);
+
+    if (*end != '\0') {
+      return Result::Failure("Failed to parse double from " + str);
+    }
+    if (errno == ERANGE) {
+      return Result::OutOfRange(
+          "Failed to parse double from " + str + "; overflow/underflow occurred");
+    }
+
+    return Result::Success(value);
+  }
+
+  static const char* Name() { return "double"; }
+  // note: Name() is just here for more user-friendly errors,
+  // but in the future we will use non-standard ways of getting the type name
+  // at compile-time and this will no longer be required
+};
+```
+Will parse any non-append argument definitions with a type of `double`.
+
+For an appending example:
+```
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+  Result ParseAndAppend(const std::string& args,
+                        std::vector<std::string>& existing_value) {
+    existing_value.push_back(args);
+    return Result::SuccessNoValue();
+  }
+  static const char* Name() { return "std::vector<std::string>"; }
+};
+```
+Will parse multiple instances of the same argument repeatedly into the `existing_value` (which will
+be default-constructed to `T{}` for the first occurrence of the argument).
+
+#### What is a `Result`?
+`Result` is a typedef for `CmdlineParseResult<T>` and it acts similar to a poor version of
+`Either<Left, Right>` in Haskell. In particular, it would be similar to `Either< int ErrorCode,
+Maybe<T> >`.
+
+There are helpers like `Result::Success(value)`, `Result::Failure(string message)` and so on to
+quickly construct these without caring about the type.
+
+When successfully parsing a single value, `Result::Success(value)` should be used, and when
+successfully parsing an appended value, use `Result::SuccessNoValue()` and write back the new value
+into `existing_value` as an out-parameter.
+
+When many arguments are parsed, the result is collapsed down to a `CmdlineResult` which acts as a
+`Either<int ErrorCode, Unit>` where the right side simply indicates success. When values are
+successfully stored, the parser will automatically save it into the target destination as a side
+effect.
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
new file mode 100644
index 0000000..2967e27
--- /dev/null
+++ b/cmdline/cmdline.h
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_H_
+#define ART_CMDLINE_CMDLINE_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "runtime.h"
+#include "base/stringpiece.h"
+#include "noop_compiler_callbacks.h"
+#include "base/logging.h"
+
+#if !defined(NDEBUG)
+#define DBG_LOG LOG(INFO)
+#else
+#define DBG_LOG LOG(DEBUG)
+#endif
+
+namespace art {
+
+// TODO: Move to <runtime/utils.h> and remove all copies of this function.
+static bool LocationToFilename(const std::string& location, InstructionSet isa,
+                               std::string* filename) {
+  bool has_system = false;
+  bool has_cache = false;
+  // image_location = /system/framework/boot.art
+  // system_image_filename = /system/framework/<image_isa>/boot.art
+  std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
+  if (OS::FileExists(system_filename.c_str())) {
+    has_system = true;
+  }
+
+  bool have_android_data = false;
+  bool dalvik_cache_exists = false;
+  bool is_global_cache = false;
+  std::string dalvik_cache;
+  GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
+                 &have_android_data, &dalvik_cache_exists, &is_global_cache);
+
+  std::string cache_filename;
+  if (have_android_data && dalvik_cache_exists) {
+    // Always set output location even if it does not exist,
+    // so that the caller knows where to create the image.
+    //
+    // image_location = /system/framework/boot.art
+    // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
+    std::string error_msg;
+    if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
+                               &cache_filename, &error_msg)) {
+      has_cache = true;
+    }
+  }
+  if (has_system) {
+    *filename = system_filename;
+    return true;
+  } else if (has_cache) {
+    *filename = cache_filename;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static Runtime* StartRuntime(const char* boot_image_location,
+                             InstructionSet instruction_set) {
+  CHECK(boot_image_location != nullptr);
+
+  RuntimeOptions options;
+
+  // We are more like a compiler than a run-time. We don't want to execute code.
+  {
+    static NoopCompilerCallbacks callbacks;
+    options.push_back(std::make_pair("compilercallbacks", &callbacks));
+  }
+
+  // Boot image location.
+  {
+    std::string boot_image_option;
+    boot_image_option += "-Ximage:";
+    boot_image_option += boot_image_location;
+    options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
+  }
+
+  // Instruction set.
+  options.push_back(
+      std::make_pair("imageinstructionset",
+                     reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
+
+  if (!Runtime::Create(options, false)) {
+    fprintf(stderr, "Failed to create runtime\n");
+    return nullptr;
+  }
+
+  // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
+  // give it away now and then switch to a more manageable ScopedObjectAccess.
+  Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+  return Runtime::Current();
+}
+
+struct CmdlineArgs {
+  enum ParseStatus {
+    kParseOk,               // Parse successful. Do not set the error message.
+    kParseUnknownArgument,  // Unknown argument. Do not set the error message.
+    kParseError,            // Parse ok, but failed elsewhere. Print the set error message.
+  };
+
+  bool Parse(int argc, char** argv) {
+    // Skip over argv[0].
+    argv++;
+    argc--;
+
+    if (argc == 0) {
+      fprintf(stderr, "No arguments specified\n");
+      PrintUsage();
+      return false;
+    }
+
+    std::string error_msg;
+    for (int i = 0; i < argc; i++) {
+      const StringPiece option(argv[i]);
+      if (option.starts_with("--boot-image=")) {
+        boot_image_location_ = option.substr(strlen("--boot-image=")).data();
+      } else if (option.starts_with("--instruction-set=")) {
+        StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
+        instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
+        if (instruction_set_ == kNone) {
+          fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
+          PrintUsage();
+          return false;
+        }
+      } else if (option.starts_with("--output=")) {
+        output_name_ = option.substr(strlen("--output=")).ToString();
+        const char* filename = output_name_.c_str();
+        out_.reset(new std::ofstream(filename));
+        if (!out_->good()) {
+          fprintf(stderr, "Failed to open output filename %s\n", filename);
+          PrintUsage();
+          return false;
+        }
+        os_ = out_.get();
+      } else {
+        ParseStatus parse_status = ParseCustom(option, &error_msg);
+
+        if (parse_status == kParseUnknownArgument) {
+          fprintf(stderr, "Unknown argument %s\n", option.data());
+        }
+
+        if (parse_status != kParseOk) {
+          fprintf(stderr, "%s\n", error_msg.c_str());
+          PrintUsage();
+          return false;
+        }
+      }
+    }
+
+    DBG_LOG << "will call parse checks";
+
+    {
+      ParseStatus checks_status = ParseChecks(&error_msg);
+      if (checks_status != kParseOk) {
+          fprintf(stderr, "%s\n", error_msg.c_str());
+          PrintUsage();
+          return false;
+      }
+    }
+
+    return true;
+  }
+
+  virtual std::string GetUsage() const {
+    std::string usage;
+
+    usage +=  // Required.
+        "  --boot-image=<file.art>: provide the image location for the boot class path.\n"
+        "      Do not include the arch as part of the name, it is added automatically.\n"
+        "      Example: --boot-image=/system/framework/boot.art\n"
+        "\n";
+    usage += StringPrintf(  // Optional.
+        "  --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
+        "      file based on the image location set.\n"
+        "      Example: --instruction-set=x86\n"
+        "      Default: %s\n"
+        "\n",
+        GetInstructionSetString(kRuntimeISA));
+    usage +=  // Optional.
+        "  --output=<file> may be used to send the output to a file.\n"
+        "      Example: --output=/tmp/oatdump.txt\n"
+        "\n";
+
+    return usage;
+  }
+
+  // Specified by --boot-image.
+  const char* boot_image_location_ = nullptr;
+  // Specified by --instruction-set.
+  InstructionSet instruction_set_ = kRuntimeISA;
+  // Specified by --output.
+  std::ostream* os_ = &std::cout;
+  std::unique_ptr<std::ofstream> out_;  // If something besides cout is used
+  std::string output_name_;
+
+  virtual ~CmdlineArgs() {}
+
+  bool ParseCheckBootImage(std::string* error_msg) {
+    if (boot_image_location_ == nullptr) {
+      *error_msg = "--boot-image must be specified";
+      return false;
+    }
+
+    DBG_LOG << "boot image location: " << boot_image_location_;
+
+    // Checks for --boot-image location.
+    {
+      std::string boot_image_location = boot_image_location_;
+      size_t file_name_idx = boot_image_location.rfind("/");
+      if (file_name_idx == std::string::npos) {  // Prevent a InsertIsaDirectory check failure.
+        *error_msg = "Boot image location must have a / in it";
+        return false;
+      }
+
+      // Don't let image locations with the 'arch' in it through, since it's not a location.
+      // This prevents a common error "Could not create an image space..." when initing the Runtime.
+      if (file_name_idx != std::string::npos) {
+        std::string no_file_name = boot_image_location.substr(0, file_name_idx);
+        size_t ancestor_dirs_idx = no_file_name.rfind("/");
+
+        std::string parent_dir_name;
+        if (ancestor_dirs_idx != std::string::npos) {
+          parent_dir_name = no_file_name.substr(ancestor_dirs_idx + 1);
+        } else {
+          parent_dir_name = no_file_name;
+        }
+
+        DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
+
+        if (GetInstructionSetFromString(parent_dir_name.c_str()) != kNone) {
+          *error_msg = "Do not specify the architecture as part of the boot image location";
+          return false;
+        }
+      }
+
+      // Check that the boot image location points to a valid file name.
+      std::string file_name;
+      if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
+        *error_msg = StringPrintf("No corresponding file for location '%s' exists",
+                                  file_name.c_str());
+        return false;
+      }
+
+      DBG_LOG << "boot_image_filename does exist: " << file_name;
+    }
+
+    return true;
+  }
+
+  void PrintUsage() {
+    fprintf(stderr, "%s", GetUsage().c_str());
+  }
+
+ protected:
+  virtual ParseStatus ParseCustom(const StringPiece& option ATTRIBUTE_UNUSED,
+                                  std::string* error_msg ATTRIBUTE_UNUSED) {
+    return kParseUnknownArgument;
+  }
+
+  virtual ParseStatus ParseChecks(std::string* error_msg ATTRIBUTE_UNUSED) {
+    return kParseOk;
+  }
+};
+
+template <typename Args = CmdlineArgs>
+struct CmdlineMain {
+  int Main(int argc, char** argv) {
+    InitLogging(argv);
+    std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
+    args_ = args.get();
+
+    DBG_LOG << "Try to parse";
+
+    if (args_ == nullptr || !args_->Parse(argc, argv)) {
+      return EXIT_FAILURE;
+    }
+
+    bool needs_runtime = NeedsRuntime();
+    std::unique_ptr<Runtime> runtime;
+
+
+    if (needs_runtime) {
+      std::string error_msg;
+      if (!args_->ParseCheckBootImage(&error_msg)) {
+        fprintf(stderr, "%s\n", error_msg.c_str());
+        args_->PrintUsage();
+        return EXIT_FAILURE;
+      }
+      runtime.reset(CreateRuntime(args.get()));
+      if (runtime == nullptr) {
+        return EXIT_FAILURE;
+      }
+      if (!ExecuteWithRuntime(runtime.get())) {
+        return EXIT_FAILURE;
+      }
+    } else {
+      if (!ExecuteWithoutRuntime()) {
+        return EXIT_FAILURE;
+      }
+    }
+
+    if (!ExecuteCommon()) {
+      return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+  }
+
+  // Override this function to create your own arguments.
+  // Usually will want to return a subtype of CmdlineArgs.
+  virtual Args* CreateArguments() {
+    return new Args();
+  }
+
+  // Override this function to do something else with the runtime.
+  virtual bool ExecuteWithRuntime(Runtime* runtime) {
+    CHECK(runtime != nullptr);
+    // Do nothing
+    return true;
+  }
+
+  // Does the code execution need a runtime? Sometimes it doesn't.
+  virtual bool NeedsRuntime() {
+    return true;
+  }
+
+  // Do execution without having created a runtime.
+  virtual bool ExecuteWithoutRuntime() {
+    return true;
+  }
+
+  // Continue execution after ExecuteWith[out]Runtime
+  virtual bool ExecuteCommon() {
+    return true;
+  }
+
+  virtual ~CmdlineMain() {}
+
+ protected:
+  Args* args_ = nullptr;
+
+ private:
+  Runtime* CreateRuntime(CmdlineArgs* args) {
+    CHECK(args != nullptr);
+
+    return StartRuntime(args->boot_image_location_, args->instruction_set_);
+  }
+};
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_H_
diff --git a/cmdline/cmdline_parse_result.h b/cmdline/cmdline_parse_result.h
new file mode 100644
index 0000000..d6ac341
--- /dev/null
+++ b/cmdline/cmdline_parse_result.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
+#define ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
+
+#include "cmdline_result.h"
+#include "detail/cmdline_parser_detail.h"
+
+namespace art {
+// Result of a type-parsing attempt. If successful holds the strongly-typed value,
+// otherwise it holds either a usage or a failure string message that should be displayed back
+// to the user.
+//
+// CmdlineType::Parse/CmdlineType::ParseAndAppend must return this type.
+template <typename T>
+struct CmdlineParseResult : CmdlineResult {
+  using CmdlineResult::CmdlineResult;
+
+  // Create an error result with the usage error code and the specified message.
+  static CmdlineParseResult Usage(const std::string& message) {
+    return CmdlineParseResult(kUsage, message);
+  }
+
+  // Create an error result with the failure error code and no message.
+  static CmdlineParseResult<T> Failure()  {
+    return CmdlineParseResult(kFailure);
+  }
+
+  // Create an error result with the failure error code and no message.
+  static CmdlineParseResult<T> Failure(const std::string& message) {
+    return CmdlineParseResult(kFailure, message);
+  }
+
+  // Create a successful result which holds the specified value.
+  static CmdlineParseResult<T> Success(const T& value) {
+    return CmdlineParseResult(value);
+  }
+
+  // Create a successful result, taking over the value.
+  static CmdlineParseResult<T> Success(T&& value) {
+    return CmdlineParseResult(std::forward<T>(value));
+  }
+
+  // Create succesful result, without any values. Used when a value was successfully appended
+  // into an existing object.
+  static CmdlineParseResult<T> SuccessNoValue() {
+    return CmdlineParseResult(T {});
+  }
+
+  // Create an error result with the OutOfRange error and the specified message.
+  static CmdlineParseResult<T> OutOfRange(const std::string& message) {
+    return CmdlineParseResult(kOutOfRange, message);
+  }
+
+  // Create an error result with the OutOfRange code and a custom message
+  // which is printed from the actual/min/max values.
+  // Values are converted to string using the ostream<< operator.
+  static CmdlineParseResult<T> OutOfRange(const T& value,
+                                          const T& min,
+                                          const T& max) {
+    return CmdlineParseResult(kOutOfRange,
+                              "actual: " + art::detail::ToStringAny(value) +
+                              ", min: " + art::detail::ToStringAny(min) +
+                              ", max: " + art::detail::ToStringAny(max));
+  }
+
+  // Get a read-only reference to the underlying value.
+  // The result must have been successful and must have a value.
+  const T& GetValue() const {
+    assert(IsSuccess());
+    assert(has_value_);
+    return value_;
+  }
+
+  // Get a mutable reference to the underlying value.
+  // The result must have been successful and must have a value.
+  T& GetValue() {
+    assert(IsSuccess());
+    assert(has_value_);
+    return value_;
+  }
+
+  // Take over the value.
+  // The result must have been successful and must have a value.
+  T&& ReleaseValue() {
+    assert(IsSuccess());
+    assert(has_value_);
+    return std::move(value_);
+  }
+
+  // Whether or not the result has a value (e.g. created with Result::Success).
+  // Error results never have values, success results commonly, but not always, have values.
+  bool HasValue() const {
+    return has_value_;
+  }
+
+  // Cast an error-result from type T2 to T1.
+  // Safe since error-results don't store a typed value.
+  template <typename T2>
+  static CmdlineParseResult<T> CastError(const CmdlineParseResult<T2>& other) {
+    assert(other.IsError());
+    return CmdlineParseResult<T>(other.GetStatus());
+  }
+
+  // Make sure copying is allowed
+  CmdlineParseResult(const CmdlineParseResult& other) = default;
+  // Make sure moving is cheap
+  CmdlineParseResult(CmdlineParseResult&& other) = default;
+
+ private:
+  explicit CmdlineParseResult(const T& value)
+    : CmdlineResult(kSuccess), value_(value), has_value_(true) {}
+  explicit CmdlineParseResult(T&& value)
+    : CmdlineResult(kSuccess), value_(std::forward<T>(value)), has_value_(true) {}
+  explicit CmdlineParseResult()
+    : CmdlineResult(kSuccess), value_(), has_value_(false) {}
+
+  T value_;
+  bool has_value_ = false;
+};
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
new file mode 100644
index 0000000..a555356
--- /dev/null
+++ b/cmdline/cmdline_parser.h
@@ -0,0 +1,635 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_PARSER_H_
+
+#define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
+
+#include "cmdline/detail/cmdline_parser_detail.h"
+#include "cmdline/detail/cmdline_parse_argument_detail.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+
+#include "cmdline_type_parser.h"
+#include "token_range.h"
+#include "cmdline_types.h"
+#include "cmdline_result.h"
+#include "cmdline_parse_result.h"
+
+#include "runtime/base/variant_map.h"
+#include "utils.h"
+
+#include <vector>
+#include <memory>
+
+namespace art {
+// Build a parser for command line arguments with a small domain specific language.
+// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
+// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser {
+  template <typename TArg>
+  struct ArgumentBuilder;
+
+  struct Builder;  // Build the parser.
+  struct UntypedArgumentBuilder;  // Build arguments which weren't yet given a type.
+
+ private:
+  // Forward declare some functions that we need to use before fully-defining structs.
+  template <typename TArg>
+  static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
+  static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
+
+  // Allow argument definitions to save their values when they are parsed,
+  // without having a dependency on CmdlineParser or any of the builders.
+  //
+  // A shared pointer to the save destination is saved into the load/save argument callbacks.
+  //
+  // This also allows the underlying storage (i.e. a variant map) to be released
+  // to the user, without having to recreate all of the callbacks.
+  struct SaveDestination {
+    SaveDestination() : variant_map_(new TVariantMap()) {}
+
+    // Save value to the variant map.
+    template <typename TArg>
+    void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
+      variant_map_->Set(key, value);
+    }
+
+    // Get the existing value from a map, creating the value if it did not already exist.
+    template <typename TArg>
+    TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
+      auto* ptr = variant_map_->Get(key);
+      if (ptr == nullptr) {
+        variant_map_->Set(key, TArg());
+        ptr = variant_map_->Get(key);
+        assert(ptr != nullptr);
+      }
+
+      return *ptr;
+    }
+
+   protected:
+    // Release the map, clearing it as a side-effect.
+    // Future saves will be distinct from previous saves.
+    TVariantMap&& ReleaseMap() {
+      return std::move(*variant_map_);
+    }
+
+    // Get a read-only reference to the variant map.
+    const TVariantMap& GetMap() {
+      return *variant_map_;
+    }
+
+    // Clear all potential save targets.
+    void Clear() {
+      variant_map_->Clear();
+    }
+
+   private:
+    // Don't try to copy or move this. Just don't.
+    SaveDestination(const SaveDestination&) = delete;
+    SaveDestination(SaveDestination&&) = delete;
+    SaveDestination& operator=(const SaveDestination&) = delete;
+    SaveDestination& operator=(SaveDestination&&) = delete;
+
+    std::shared_ptr<TVariantMap> variant_map_;
+
+    // Allow the parser to change the underlying pointers when we release the underlying storage.
+    friend struct CmdlineParser;
+  };
+
+ public:
+  // Builder for the argument definition of type TArg. Do not use this type directly,
+  // it is only a separate type to provide compile-time enforcement against doing
+  // illegal builds.
+  template <typename TArg>
+  struct ArgumentBuilder {
+    // Add a range check to this argument.
+    ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
+      argument_info_.has_range_ = true;
+      argument_info_.min_ = min;
+      argument_info_.max_ = max;
+
+      return *this;
+    }
+
+    // Map the list of names into the list of values. List of names must not have
+    // any wildcards '_' in it.
+    //
+    // Do not use if a value map has already been set.
+    ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
+      SetValuesInternal(value_list);
+      return *this;
+    }
+
+    // When used with a single alias, map the alias into this value.
+    // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+    ArgumentBuilder<TArg> WithValue(const TArg& value) {
+      return WithValues({ value });
+    }
+
+    // Map the parsed string values (from _) onto a concrete value. If no wildcard
+    // has been specified, then map the value directly from the arg name (i.e.
+    // if there are multiple aliases, then use the alias to do the mapping).
+    //
+    // Do not use if a values list has already been set.
+    ArgumentBuilder<TArg>& WithValueMap(
+        std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
+      assert(!argument_info_.has_value_list_);
+
+      argument_info_.has_value_map_ = true;
+      argument_info_.value_map_ = key_value_list;
+
+      return *this;
+    }
+
+    // If this argument is seen multiple times, successive arguments mutate the same value
+    // instead of replacing it with a new value.
+    ArgumentBuilder<TArg>& AppendValues() {
+      argument_info_.appending_values_ = true;
+
+      return *this;
+    }
+
+    // Convenience type alias for the variant map key type definition.
+    using MapKey = TVariantMapKey<TArg>;
+
+    // Write the results of this argument into the key.
+    // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
+    CmdlineParser::Builder& IntoKey(const MapKey& key) {
+      // Only capture save destination as a pointer.
+      // This allows the parser to later on change the specific save targets.
+      auto save_destination = save_destination_;
+      save_value_ = [save_destination, &key](TArg& value) {
+        save_destination->SaveToMap(key, value);
+        CMDLINE_DEBUG_LOG << "Saved value into map '"
+            << detail::ToStringAny(value) << "'" << std::endl;
+      };
+
+      load_value_ = [save_destination, &key]() -> TArg& {
+        TArg& value = save_destination->GetOrCreateFromMap(key);
+        CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
+            << std::endl;
+
+        return value;
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
+    // Ensure we always move this when returning a new builder.
+    ArgumentBuilder(ArgumentBuilder&&) = default;
+
+   protected:
+    // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
+    CmdlineParser::Builder& IntoIgnore() {
+      save_value_ = [](TArg& value) {
+        CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
+      };
+      load_value_ = []() -> TArg& {
+        assert(false && "Should not be appending values to ignored arguments");
+        return *reinterpret_cast<TArg*>(0);  // Blow up.
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
+    void SetValuesInternal(const std::vector<TArg>&& value_list) {
+      assert(!argument_info_.has_value_map_);
+
+      argument_info_.has_value_list_ = true;
+      argument_info_.value_list_ = value_list;
+    }
+
+    void SetNames(std::vector<const char*>&& names) {
+      argument_info_.names_ = names;
+    }
+
+    void SetNames(std::initializer_list<const char*> names) {
+      argument_info_.names_ = names;
+    }
+
+   private:
+    // Copying is bad. Move only.
+    ArgumentBuilder(const ArgumentBuilder&) = delete;
+
+    // Called by any function that doesn't chain back into this builder.
+    // Completes the argument builder and save the information into the main builder.
+    void CompleteArgument() {
+      assert(save_value_specified_ &&
+             "No Into... function called, nowhere to save parsed values to");
+      assert(load_value_specified_ &&
+             "No Into... function called, nowhere to load parsed values from");
+
+      argument_info_.CompleteArgument();
+
+      // Appending the completed argument is destructive. The object is no longer
+      // usable since all the useful information got moved out of it.
+      AppendCompletedArgument(parent_,
+                              new detail::CmdlineParseArgument<TArg>(
+                                  std::move(argument_info_),
+                                  std::move(save_value_),
+                                  std::move(load_value_)));
+    }
+
+    friend struct CmdlineParser;
+    friend struct CmdlineParser::Builder;
+    friend struct CmdlineParser::UntypedArgumentBuilder;
+
+    ArgumentBuilder(CmdlineParser::Builder& parser,
+                    std::shared_ptr<SaveDestination> save_destination)
+        : parent_(parser),
+          save_value_specified_(false),
+          load_value_specified_(false),
+          save_destination_(save_destination) {
+      save_value_ = [](TArg&) {
+        assert(false && "No save value function defined");
+      };
+
+      load_value_ = []() -> TArg& {
+        assert(false && "No load value function defined");
+        return *reinterpret_cast<TArg*>(0);  // Blow up.
+      };
+    }
+
+    CmdlineParser::Builder& parent_;
+    std::function<void(TArg&)> save_value_;
+    std::function<TArg&(void)> load_value_;
+    bool save_value_specified_;
+    bool load_value_specified_;
+    detail::CmdlineParserArgumentInfo<TArg> argument_info_;
+
+    std::shared_ptr<SaveDestination> save_destination_;
+  };
+
+  struct UntypedArgumentBuilder {
+    // Set a type for this argument. The specific subcommand parser is looked up by the type.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithType() {
+      return CreateTypedBuilder<TArg>();
+    }
+
+    // When used with multiple aliases, map the position of the alias to the value position.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
+      auto&& a = CreateTypedBuilder<TArg>();
+      a.WithValues(values);
+      return std::move(a);
+    }
+
+    // When used with a single alias, map the alias into this value.
+    // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithValue(const TArg& value) {
+      return WithValues({ value });
+    }
+
+    // Set the current building argument to target this key.
+    // When this command line argument is parsed, it can be fetched with this key.
+    Builder& IntoKey(const TVariantMapKey<Unit>& key) {
+      return CreateTypedBuilder<Unit>().IntoKey(key);
+    }
+
+    // Ensure we always move this when returning a new builder.
+    UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
+
+   protected:
+    void SetNames(std::vector<const char*>&& names) {
+      names_ = std::move(names);
+    }
+
+    void SetNames(std::initializer_list<const char*> names) {
+      names_ = names;
+    }
+
+   private:
+    // No copying. Move instead.
+    UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
+
+    template <typename TArg>
+    ArgumentBuilder<TArg> CreateTypedBuilder() {
+      auto&& b = CreateArgumentBuilder<TArg>(parent_);
+      InitializeTypedBuilder(&b);  // Type-specific initialization
+      b.SetNames(std::move(names_));
+      return std::move(b);
+    }
+
+    template <typename TArg = Unit>
+    typename std::enable_if<std::is_same<TArg, Unit>::value>::type
+    InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
+      // Every Unit argument implicitly maps to a runtime value of Unit{}
+      std::vector<Unit> values(names_.size(), Unit{});  // NOLINT [whitespace/braces] [5]
+      arg_builder->SetValuesInternal(std::move(values));
+    }
+
+    // No extra work for all other types
+    void InitializeTypedBuilder(void*) {}
+
+    template <typename TArg>
+    friend struct ArgumentBuilder;
+    friend struct Builder;
+
+    explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
+    // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
+
+    CmdlineParser::Builder& parent_;
+    std::vector<const char*> names_;
+  };
+
+  // Build a new parser given a chain of calls to define arguments.
+  struct Builder {
+    Builder() : save_destination_(new SaveDestination()) {}
+
+    // Define a single argument. The default type is Unit.
+    UntypedArgumentBuilder Define(const char* name) {
+      return Define({name});
+    }
+
+    // Define a single argument with multiple aliases.
+    UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
+      auto&& b = UntypedArgumentBuilder(*this);
+      b.SetNames(names);
+      return std::move(b);
+    }
+
+    // Whether the parser should give up on unrecognized arguments. Not recommended.
+    Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
+      ignore_unrecognized_ = ignore_unrecognized;
+      return *this;
+    }
+
+    // Provide a list of arguments to ignore for backwards compatibility.
+    Builder& Ignore(std::initializer_list<const char*> ignore_list) {
+      for (auto&& ignore_name : ignore_list) {
+        std::string ign = ignore_name;
+
+        // Ignored arguments are just like a regular definition which have very
+        // liberal parsing requirements (no range checks, no value checks).
+        // Unlike regular argument definitions, when a value gets parsed into its
+        // stronger type, we just throw it away.
+
+        if (ign.find("_") != std::string::npos) {  // Does the arg-def have a wildcard?
+          // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
+          auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
+          assert(&builder == this);
+          (void)builder;  // Ignore pointless unused warning, it's used in the assert.
+        } else {
+          // pretend this is a unit, e.g. -Xjitblocking
+          auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
+          assert(&builder == this);
+          (void)builder;  // Ignore pointless unused warning, it's used in the assert.
+        }
+      }
+      ignore_list_ = ignore_list;
+      return *this;
+    }
+
+    // Finish building the parser; performs sanity checks. Return value is moved, not copied.
+    // Do not call this more than once.
+    CmdlineParser Build() {
+      assert(!built_);
+      built_ = true;
+
+      auto&& p = CmdlineParser(ignore_unrecognized_,
+                               std::move(ignore_list_),
+                               save_destination_,
+                               std::move(completed_arguments_));
+
+      return std::move(p);
+    }
+
+   protected:
+    void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
+      auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
+      completed_arguments_.push_back(std::move(smart_ptr));
+    }
+
+   private:
+    // No copying now!
+    Builder(const Builder& other) = delete;
+
+    template <typename TArg>
+    friend struct ArgumentBuilder;
+    friend struct UntypedArgumentBuilder;
+    friend struct CmdlineParser;
+
+    bool built_ = false;
+    bool ignore_unrecognized_ = false;
+    std::vector<const char*> ignore_list_;
+    std::shared_ptr<SaveDestination> save_destination_;
+
+    std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+  };
+
+  CmdlineResult Parse(const std::string& argv) {
+    std::vector<std::string> tokenized;
+    Split(argv, ' ', &tokenized);
+
+    return Parse(TokenRange(std::move(tokenized)));
+  }
+
+  // Parse the arguments; storing results into the arguments map. Returns success value.
+  CmdlineResult Parse(const char* argv) {
+    return Parse(std::string(argv));
+  }
+
+  // Parse the arguments; storing the results into the arguments map. Returns success value.
+  // Assumes that argv[0] is a valid argument (i.e. not the program name).
+  CmdlineResult Parse(const std::vector<const char*>& argv) {
+    return Parse(TokenRange(argv.begin(), argv.end()));
+  }
+
+  // Parse the arguments; storing the results into the arguments map. Returns success value.
+  // Assumes that argv[0] is a valid argument (i.e. not the program name).
+  CmdlineResult Parse(const std::vector<std::string>& argv) {
+    return Parse(TokenRange(argv.begin(), argv.end()));
+  }
+
+  // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
+  // Assumes that argv[0] is the program name, and ignores it.
+  CmdlineResult Parse(const char* argv[], int argc) {
+    return Parse(TokenRange(&argv[1], argc - 1));  // ignore argv[0] because it's the program name
+  }
+
+  // Look up the arguments that have been parsed; use the target keys to lookup individual args.
+  const TVariantMap& GetArgumentsMap() const {
+    return save_destination_->GetMap();
+  }
+
+  // Release the arguments map that has been parsed; useful for move semantics.
+  TVariantMap&& ReleaseArgumentsMap() {
+    return save_destination_->ReleaseMap();
+  }
+
+  // How many arguments were defined?
+  size_t CountDefinedArguments() const {
+    return completed_arguments_.size();
+  }
+
+  // Ensure we have a default move constructor.
+  CmdlineParser(CmdlineParser&& other) = default;
+  // Ensure we have a default move assignment operator.
+  CmdlineParser& operator=(CmdlineParser&& other) = default;
+
+ private:
+  friend struct Builder;
+
+  // Construct a new parser from the builder. Move all the arguments.
+  explicit CmdlineParser(bool ignore_unrecognized,
+                         std::vector<const char*>&& ignore_list,
+                         std::shared_ptr<SaveDestination> save_destination,
+                         std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&&
+                             completed_arguments)
+    : ignore_unrecognized_(ignore_unrecognized),
+      ignore_list_(std::move(ignore_list)),
+      save_destination_(save_destination),
+      completed_arguments_(std::move(completed_arguments)) {
+    assert(save_destination != nullptr);
+  }
+
+  // Parse the arguments; storing results into the arguments map. Returns success value.
+  // The parsing will fail on the first non-success parse result and return that error.
+  //
+  // All previously-parsed arguments are cleared out.
+  // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
+  // A partial parse will result only in a partial save of the arguments.
+  CmdlineResult Parse(TokenRange&& arguments_list) {
+    save_destination_->Clear();
+
+    for (size_t i = 0; i < arguments_list.Size(); ) {
+      TokenRange possible_name = arguments_list.Slice(i);
+
+      size_t best_match_size = 0;  // How many tokens were matched in the best case.
+      size_t best_match_arg_idx = 0;
+      bool matched = false;  // At least one argument definition has been matched?
+
+      // Find the closest argument definition for the remaining token range.
+      size_t arg_idx = 0;
+      for (auto&& arg : completed_arguments_) {
+        size_t local_match = arg->MaybeMatches(possible_name);
+
+        if (local_match > best_match_size) {
+          best_match_size = local_match;
+          best_match_arg_idx = arg_idx;
+          matched = true;
+        }
+        arg_idx++;
+      }
+
+      // Saw some kind of unknown argument
+      if (matched == false) {
+        if (UNLIKELY(ignore_unrecognized_)) {  // This is usually off, we only need it for JNI.
+          // Consume 1 token and keep going, hopefully the next token is a good one.
+          ++i;
+          continue;
+        }
+        // Common case:
+        // Bail out on the first unknown argument with an error.
+        return CmdlineResult(CmdlineResult::kUnknown,
+                             std::string("Unknown argument: ") + possible_name[0]);
+      }
+
+      // Look at the best-matched argument definition and try to parse against that.
+      auto&& arg = completed_arguments_[best_match_arg_idx];
+
+      assert(arg->MaybeMatches(possible_name) == best_match_size);
+
+      // Try to parse the argument now, if we have enough tokens.
+      std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
+      size_t min_tokens;
+      size_t max_tokens;
+
+      std::tie(min_tokens, max_tokens) = num_tokens;
+
+      if ((i + min_tokens) > arguments_list.Size()) {
+        // expected longer command line but it was too short
+        // e.g. if the argv was only "-Xms" without specifying a memory option
+        CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
+            " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
+        return CmdlineResult(CmdlineResult::kFailure,
+                             std::string("Argument ") +
+                             possible_name[0] + ": incomplete command line arguments, expected "
+                             + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
+                             " more tokens");
+      }
+
+      if (best_match_size > max_tokens || best_match_size < min_tokens) {
+        // Even our best match was out of range, so parsing would fail instantly.
+        return CmdlineResult(CmdlineResult::kFailure,
+                             std::string("Argument ") + possible_name[0] + ": too few tokens "
+                             "matched " + std::to_string(best_match_size)
+                             + " but wanted " + std::to_string(num_tokens.first));
+      }
+
+      // We have enough tokens to begin exact parsing.
+      TokenRange exact_range = possible_name.Slice(0, max_tokens);
+
+      size_t consumed_tokens = 1;  // At least 1 if we ever want to try to resume parsing on error
+      CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
+
+      if (parse_attempt.IsError()) {
+        // We may also want to continue parsing the other tokens to gather more errors.
+        return parse_attempt;
+      }  // else the value has been successfully stored into the map
+
+      assert(consumed_tokens > 0);  // Don't hang in an infinite loop trying to parse
+      i += consumed_tokens;
+
+      // TODO: also handle ignoring arguments for backwards compatibility
+    }  // for
+
+    return CmdlineResult(CmdlineResult::kSuccess);
+  }
+
+  bool ignore_unrecognized_ = false;
+  std::vector<const char*> ignore_list_;
+  std::shared_ptr<SaveDestination> save_destination_;
+  std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+};
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+template <typename TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
+    CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
+  return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
+      parent, parent.save_destination_);
+}
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
+    CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
+    detail::CmdlineParseArgumentAny* arg) {
+  builder.AppendCompletedArgument(arg);
+}
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_PARSER_H_
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
new file mode 100644
index 0000000..288f7ac
--- /dev/null
+++ b/cmdline/cmdline_parser_test.cc
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cmdline_parser.h"
+#include "runtime/runtime_options.h"
+#include "runtime/parsed_options.h"
+
+#include "utils.h"
+#include <numeric>
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+                                        reinterpret_cast<void*>(NULL));
+
+namespace art {
+  bool UsuallyEquals(double expected, double actual);
+
+  // This has a gtest dependency, which is why it's in the gtest only.
+  bool operator==(const TestProfilerOptions& lhs, const TestProfilerOptions& rhs) {
+    return lhs.enabled_ == rhs.enabled_ &&
+        lhs.output_file_name_ == rhs.output_file_name_ &&
+        lhs.period_s_ == rhs.period_s_ &&
+        lhs.duration_s_ == rhs.duration_s_ &&
+        lhs.interval_us_ == rhs.interval_us_ &&
+        UsuallyEquals(lhs.backoff_coefficient_, rhs.backoff_coefficient_) &&
+        UsuallyEquals(lhs.start_immediately_, rhs.start_immediately_) &&
+        UsuallyEquals(lhs.top_k_threshold_, rhs.top_k_threshold_) &&
+        UsuallyEquals(lhs.top_k_change_threshold_, rhs.top_k_change_threshold_) &&
+        lhs.profile_type_ == rhs.profile_type_ &&
+        lhs.max_stack_depth_ == rhs.max_stack_depth_;
+  }
+
+  bool UsuallyEquals(double expected, double actual) {
+    using FloatingPoint = ::testing::internal::FloatingPoint<double>;
+
+    FloatingPoint exp(expected);
+    FloatingPoint act(actual);
+
+    // Compare with ULPs instead of comparing with ==
+    return exp.AlmostEquals(act);
+  }
+
+  template <typename T>
+  bool UsuallyEquals(const T& expected, const T& actual,
+                     typename std::enable_if<
+                         detail::SupportsEqualityOperator<T>::value>::type* = 0) {
+    return expected == actual;
+  }
+
+  // Try to use memcmp to compare simple plain-old-data structs.
+  //
+  // This should *not* generate false positives, but it can generate false negatives.
+  // This will mostly work except for fields like float which can have different bit patterns
+  // that are nevertheless equal.
+  // If a test is failing because the structs aren't "equal" when they really are
+  // then it's recommended to implement operator== for it instead.
+  template <typename T, typename ... Ignore>
+  bool UsuallyEquals(const T& expected, const T& actual,
+                     const Ignore& ... more ATTRIBUTE_UNUSED,
+                     typename std::enable_if<std::is_pod<T>::value>::type* = 0,
+                     typename std::enable_if<!detail::SupportsEqualityOperator<T>::value>::type* = 0
+                     ) {
+    return memcmp(std::addressof(expected), std::addressof(actual), sizeof(T)) == 0;
+  }
+
+  bool UsuallyEquals(const XGcOption& expected, const XGcOption& actual) {
+    return memcmp(std::addressof(expected), std::addressof(actual), sizeof(expected)) == 0;
+  }
+
+  bool UsuallyEquals(const char* expected, std::string actual) {
+    return std::string(expected) == actual;
+  }
+
+  template <typename TMap, typename TKey, typename T>
+  ::testing::AssertionResult IsExpectedKeyValue(const T& expected,
+                                                const TMap& map,
+                                                const TKey& key) {
+    auto* actual = map.Get(key);
+    if (actual != nullptr) {
+      if (!UsuallyEquals(expected, *actual)) {
+        return ::testing::AssertionFailure()
+          << "expected " << detail::ToStringAny(expected) << " but got "
+          << detail::ToStringAny(*actual);
+      }
+      return ::testing::AssertionSuccess();
+    }
+
+    return ::testing::AssertionFailure() << "key was not in the map";
+  }
+
+class CmdlineParserTest : public ::testing::Test {
+ public:
+  CmdlineParserTest() = default;
+  ~CmdlineParserTest() = default;
+
+ protected:
+  using M = RuntimeArgumentMap;
+  using RuntimeParser = ParsedOptions::RuntimeParser;
+
+  static void SetUpTestCase() {
+    art::InitLogging(nullptr);  // argv = null
+  }
+
+  virtual void SetUp() {
+    parser_ = ParsedOptions::MakeParser(false);  // do not ignore unrecognized options
+  }
+
+  static ::testing::AssertionResult IsResultSuccessful(CmdlineResult result) {
+    if (result.IsSuccess()) {
+      return ::testing::AssertionSuccess();
+    } else {
+      return ::testing::AssertionFailure()
+        << result.GetStatus() << " with: " << result.GetMessage();
+    }
+  }
+
+  static ::testing::AssertionResult IsResultFailure(CmdlineResult result,
+                                                    CmdlineResult::Status failure_status) {
+    if (result.IsSuccess()) {
+      return ::testing::AssertionFailure() << " got success but expected failure: "
+          << failure_status;
+    } else if (result.GetStatus() == failure_status) {
+      return ::testing::AssertionSuccess();
+    }
+
+    return ::testing::AssertionFailure() << " expected failure " << failure_status
+        << " but got " << result.GetStatus();
+  }
+
+  std::unique_ptr<RuntimeParser> parser_;
+};
+
+#define EXPECT_KEY_EXISTS(map, key) EXPECT_TRUE((map).Exists(key))
+#define EXPECT_KEY_VALUE(map, key, expected) EXPECT_TRUE(IsExpectedKeyValue(expected, map, key))
+
+#define EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv)               \
+  do {                                                        \
+    EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv)));    \
+    EXPECT_EQ(0u, parser_->GetArgumentsMap().Size());         \
+  } while (false)
+
+#define _EXPECT_SINGLE_PARSE_EXISTS(argv, key)                \
+  do {                                                        \
+    EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv)));    \
+    RuntimeArgumentMap args = parser_->ReleaseArgumentsMap(); \
+    EXPECT_EQ(1u, args.Size());                               \
+    EXPECT_KEY_EXISTS(args, key);                             \
+
+#define EXPECT_SINGLE_PARSE_EXISTS(argv, key)                 \
+    _EXPECT_SINGLE_PARSE_EXISTS(argv, key);                   \
+  } while (false)
+
+#define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key)        \
+    _EXPECT_SINGLE_PARSE_EXISTS(argv, key);                   \
+    EXPECT_KEY_VALUE(args, key, expected);                    \
+  } while (false)                                             // NOLINT [readability/namespace] [5]
+
+#define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key)    \
+  EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key)
+
+#define EXPECT_SINGLE_PARSE_FAIL(argv, failure_status)         \
+    do {                                                       \
+      EXPECT_TRUE(IsResultFailure(parser_->Parse(argv), failure_status));\
+      RuntimeArgumentMap args = parser_->ReleaseArgumentsMap();\
+      EXPECT_EQ(0u, args.Size());                              \
+    } while (false)
+
+TEST_F(CmdlineParserTest, TestSimpleSuccesses) {
+  auto& parser = *parser_;
+
+  EXPECT_LT(0u, parser.CountDefinedArguments());
+
+  {
+    // Test case 1: No command line arguments
+    EXPECT_TRUE(IsResultSuccessful(parser.Parse("")));
+    RuntimeArgumentMap args = parser.ReleaseArgumentsMap();
+    EXPECT_EQ(0u, args.Size());
+  }
+
+  EXPECT_SINGLE_PARSE_EXISTS("-Xzygote", M::Zygote);
+  EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+  EXPECT_SINGLE_PARSE_VALUE("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+  EXPECT_SINGLE_PARSE_VALUE(false, "-Xverify:none", M::Verify);
+  EXPECT_SINGLE_PARSE_VALUE(true, "-Xverify:remote", M::Verify);
+  EXPECT_SINGLE_PARSE_VALUE(true, "-Xverify:all", M::Verify);
+  EXPECT_SINGLE_PARSE_VALUE(Memory<1>(234), "-Xss234", M::StackSize);
+  EXPECT_SINGLE_PARSE_VALUE(MemoryKiB(1234*MB), "-Xms1234m", M::MemoryInitialSize);
+  EXPECT_SINGLE_PARSE_VALUE(true, "-XX:EnableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
+  EXPECT_SINGLE_PARSE_VALUE(false, "-XX:DisableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
+  EXPECT_SINGLE_PARSE_VALUE(0.5, "-XX:HeapTargetUtilization=0.5", M::HeapTargetUtilization);
+  EXPECT_SINGLE_PARSE_VALUE(5u, "-XX:ParallelGCThreads=5", M::ParallelGCThreads);
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestSimpleFailures) {
+  // Test argument is unknown to the parser
+  EXPECT_SINGLE_PARSE_FAIL("abcdefg^%@#*(@#", CmdlineResult::kUnknown);
+  // Test value map substitution fails
+  EXPECT_SINGLE_PARSE_FAIL("-Xverify:whatever", CmdlineResult::kFailure);
+  // Test value type parsing failures
+  EXPECT_SINGLE_PARSE_FAIL("-Xsswhatever", CmdlineResult::kFailure);  // invalid memory value
+  EXPECT_SINGLE_PARSE_FAIL("-Xms123", CmdlineResult::kFailure);       // memory value too small
+  EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=0.0", CmdlineResult::kOutOfRange);  // toosmal
+  EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=2.0", CmdlineResult::kOutOfRange);  // toolarg
+  EXPECT_SINGLE_PARSE_FAIL("-XX:ParallelGCThreads=-5", CmdlineResult::kOutOfRange);  // too small
+  EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage);  // not a valid suboption
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestLogVerbosity) {
+  {
+    const char* log_args = "-verbose:"
+        "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,startup,third-party-jni,"
+        "threads,verifier";
+
+    LogVerbosity log_verbosity = LogVerbosity();
+    log_verbosity.class_linker = true;
+    log_verbosity.compiler = true;
+    log_verbosity.gc = true;
+    log_verbosity.heap = true;
+    log_verbosity.jdwp = true;
+    log_verbosity.jni = true;
+    log_verbosity.monitor = true;
+    log_verbosity.profiler = true;
+    log_verbosity.signals = true;
+    log_verbosity.startup = true;
+    log_verbosity.third_party_jni = true;
+    log_verbosity.threads = true;
+    log_verbosity.verifier = true;
+
+    EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+  }
+
+  {
+    const char* log_args = "-verbose:"
+        "class,compiler,gc,heap,jdwp,jni,monitor";
+
+    LogVerbosity log_verbosity = LogVerbosity();
+    log_verbosity.class_linker = true;
+    log_verbosity.compiler = true;
+    log_verbosity.gc = true;
+    log_verbosity.heap = true;
+    log_verbosity.jdwp = true;
+    log_verbosity.jni = true;
+    log_verbosity.monitor = true;
+
+    EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+  }
+
+  EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage);  // invalid verbose opt
+}  // TEST_F
+
+// TODO: Enable this b/19274810
+TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) {
+  /*
+   * Test success
+   */
+  {
+    XGcOption option_all_true{};  // NOLINT [readability/braces] [4]
+    option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS;
+    option_all_true.verify_pre_gc_heap_ = true;
+    option_all_true.verify_pre_sweeping_heap_ = true;
+    option_all_true.verify_post_gc_heap_ = true;
+    option_all_true.verify_pre_gc_rosalloc_ = true;
+    option_all_true.verify_pre_sweeping_rosalloc_ = true;
+    option_all_true.verify_post_gc_rosalloc_ = true;
+
+    const char * xgc_args_all_true = "-Xgc:concurrent,"
+        "preverify,presweepingverify,postverify,"
+        "preverify_rosalloc,presweepingverify_rosalloc,"
+        "postverify_rosalloc,precise,"
+        "verifycardtable";
+
+    EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption);
+
+    XGcOption option_all_false{};  // NOLINT [readability/braces] [4]
+    option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS;
+    option_all_false.verify_pre_gc_heap_ = false;
+    option_all_false.verify_pre_sweeping_heap_ = false;
+    option_all_false.verify_post_gc_heap_ = false;
+    option_all_false.verify_pre_gc_rosalloc_ = false;
+    option_all_false.verify_pre_sweeping_rosalloc_ = false;
+    option_all_false.verify_post_gc_rosalloc_ = false;
+
+    const char* xgc_args_all_false = "-Xgc:nonconcurrent,"
+        "nopreverify,nopresweepingverify,nopostverify,nopreverify_rosalloc,"
+        "nopresweepingverify_rosalloc,nopostverify_rosalloc,noprecise,noverifycardtable";
+
+    EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption);
+
+    XGcOption option_all_default{};  // NOLINT [readability/braces] [4]
+
+    const char* xgc_args_blank = "-Xgc:";
+    EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption);
+  }
+
+  /*
+   * Test failures
+   */
+  EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage);  // invalid Xgc opt
+}  // TEST_F
+
+/*
+ * {"-Xrunjdwp:_", "-agentlib:jdwp=_"}
+ */
+TEST_F(CmdlineParserTest, TestJdwpOptions) {
+  /*
+   * Test success
+   */
+  {
+    /*
+     * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
+    opt.port = 8000;
+    opt.server = true;
+
+    const char *opt_args = "-Xrunjdwp:transport=dt_socket,address=8000,server=y";
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+  }
+
+  {
+    /*
+     * "Example: -agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n\n");
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
+    opt.host = "localhost";
+    opt.port = 6500;
+    opt.server = false;
+
+    const char *opt_args = "-agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n";
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+  }
+
+  /*
+   * Test failures
+   */
+  EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:help", CmdlineResult::kUsage);  // usage for help only
+  EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:blabla", CmdlineResult::kFailure);  // invalid subarg
+  EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=help", CmdlineResult::kUsage);  // usage for help only
+  EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=blabla", CmdlineResult::kFailure);  // invalid subarg
+}  // TEST_F
+
+/*
+ * -D_ -D_ -D_ ...
+ */
+TEST_F(CmdlineParserTest, TestPropertiesList) {
+  /*
+   * Test successes
+   */
+  {
+    std::vector<std::string> opt = {"hello"};
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello", M::PropertiesList);
+  }
+
+  {
+    std::vector<std::string> opt = {"hello", "world"};
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello -Dworld", M::PropertiesList);
+  }
+
+  {
+    std::vector<std::string> opt = {"one", "two", "three"};
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Done -Dtwo -Dthree", M::PropertiesList);
+  }
+}  // TEST_F
+
+/*
+* -Xcompiler-option foo -Xcompiler-option bar ...
+*/
+TEST_F(CmdlineParserTest, TestCompilerOption) {
+ /*
+  * Test successes
+  */
+  {
+    std::vector<std::string> opt = {"hello"};
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Xcompiler-option hello", M::CompilerOptions);
+  }
+
+  {
+    std::vector<std::string> opt = {"hello", "world"};
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xcompiler-option hello -Xcompiler-option world",
+                              M::CompilerOptions);
+  }
+
+  {
+    std::vector<std::string> opt = {"one", "two", "three"};
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xcompiler-option one -Xcompiler-option two -Xcompiler-option three",
+                              M::CompilerOptions);
+  }
+}  // TEST_F
+
+/*
+* -X-profile-*
+*/
+TEST_F(CmdlineParserTest, TestProfilerOptions) {
+ /*
+  * Test successes
+  */
+
+  {
+    TestProfilerOptions opt;
+    opt.enabled_ = true;
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xenable-profiler",
+                              M::ProfilerOpts);
+  }
+
+  {
+    TestProfilerOptions opt;
+    // also need to test 'enabled'
+    opt.output_file_name_ = "hello_world.txt";
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xprofile-filename:hello_world.txt ",
+                              M::ProfilerOpts);
+  }
+
+  {
+    TestProfilerOptions opt = TestProfilerOptions();
+    // also need to test 'enabled'
+    opt.output_file_name_ = "output.txt";
+    opt.period_s_ = 123u;
+    opt.duration_s_ = 456u;
+    opt.interval_us_ = 789u;
+    opt.backoff_coefficient_ = 2.0;
+    opt.start_immediately_ = true;
+    opt.top_k_threshold_ = 50.0;
+    opt.top_k_change_threshold_ = 60.0;
+    opt.profile_type_ = kProfilerMethod;
+    opt.max_stack_depth_ = 1337u;
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xprofile-filename:output.txt "
+                              "-Xprofile-period:123 "
+                              "-Xprofile-duration:456 "
+                              "-Xprofile-interval:789 "
+                              "-Xprofile-backoff:2.0 "
+                              "-Xprofile-start-immediately "
+                              "-Xprofile-top-k-threshold:50.0 "
+                              "-Xprofile-top-k-change-threshold:60.0 "
+                              "-Xprofile-type:method "
+                              "-Xprofile-max-stack-depth:1337",
+                              M::ProfilerOpts);
+  }
+
+  {
+    TestProfilerOptions opt = TestProfilerOptions();
+    opt.profile_type_ = kProfilerBoundedStack;
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xprofile-type:stack",
+                              M::ProfilerOpts);
+  }
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestIgnoreUnrecognized) {
+  RuntimeParser::Builder parserBuilder;
+
+  parserBuilder
+      .Define("-help")
+          .IntoKey(M::Help)
+      .IgnoreUnrecognized(true);
+
+  parser_.reset(new RuntimeParser(parserBuilder.Build()));
+
+  EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option");
+  EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option1 --non-existent-option-2");
+}  //  TEST_F
+
+TEST_F(CmdlineParserTest, TestIgnoredArguments) {
+  std::initializer_list<const char*> ignored_args = {
+      "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
+      "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:abdef",
+      "-Xdexopt:foobar", "-Xnoquithandler", "-Xjnigreflimit:ixnay", "-Xgenregmap", "-Xnogenregmap",
+      "-Xverifyopt:never", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:noop",
+      "-Xincludeselectedmethod", "-Xjitthreshold:123", "-Xjitcodecachesize:12345",
+      "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:nosuchluck", "-Xjitoffset:none",
+      "-Xjitconfig:yes", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
+      "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=1337"
+  };
+
+  // Check they are ignored when parsed one at a time
+  for (auto&& arg : ignored_args) {
+    SCOPED_TRACE(arg);
+    EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(arg);
+  }
+
+  // Check they are ignored when we pass it all together at once
+  std::vector<const char*> argv = ignored_args;
+  EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv);
+}  //  TEST_F
+
+TEST_F(CmdlineParserTest, MultipleArguments) {
+  EXPECT_TRUE(IsResultSuccessful(parser_->Parse(
+      "-help -XX:ForegroundHeapGrowthMultiplier=0.5 "
+      "-Xnodex2oat -Xmethod-trace -XX:LargeObjectSpace=map")));
+
+  auto&& map = parser_->ReleaseArgumentsMap();
+  EXPECT_EQ(5u, map.Size());
+  EXPECT_KEY_VALUE(map, M::Help, Unit{});  // NOLINT [whitespace/braces] [5]
+  EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5);
+  EXPECT_KEY_VALUE(map, M::Dex2Oat, false);
+  EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{});  // NOLINT [whitespace/braces] [5]
+  EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
+}  //  TEST_F
+}  // namespace art
diff --git a/cmdline/cmdline_result.h b/cmdline/cmdline_result.h
new file mode 100644
index 0000000..bf3a85d
--- /dev/null
+++ b/cmdline/cmdline_result.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_RESULT_H_
+#define ART_CMDLINE_CMDLINE_RESULT_H_
+
+#include <assert.h>
+#include <utils.h>
+
+namespace art {
+  // Result of an attempt to process the command line arguments. If fails, specifies
+  // the specific error code and an error message.
+  // Use the value-carrying CmdlineParseResult<T> to get an additional value out in a success case.
+  struct CmdlineResult {
+    enum Status {
+      kSuccess,
+      // Error codes:
+      kUsage,
+      kFailure,
+      kOutOfRange,
+      kUnknown,
+    };
+
+    // Short-hand for checking if the result was successful.
+    operator bool() const {
+      return IsSuccess();
+    }
+
+    // Check if the operation has succeeded.
+    bool IsSuccess() const { return status_ == kSuccess; }
+    // Check if the operation was not a success.
+    bool IsError() const { return status_ != kSuccess; }
+    // Get the specific status, regardless of whether it's failure or success.
+    Status GetStatus() const { return status_; }
+
+    // Get the error message, *must* only be called for error status results.
+    const std::string& GetMessage() const { assert(IsError()); return message_; }
+
+    // Constructor any status. No message.
+    explicit CmdlineResult(Status status) : status_(status) {}
+
+    // Constructor with an error status, copying the message.
+    CmdlineResult(Status status, const std::string& message)
+      : status_(status), message_(message) {
+      assert(status != kSuccess);
+    }
+
+    // Constructor with an error status, taking over the message.
+    CmdlineResult(Status status, std::string&& message)
+      : status_(status), message_(message) {
+      assert(status != kSuccess);
+    }
+
+    // Make sure copying exists
+    CmdlineResult(const CmdlineResult& other) = default;
+    // Make sure moving is cheap
+    CmdlineResult(CmdlineResult&& other) = default;
+
+  private:
+    const Status status_;
+    const std::string message_;
+  };
+
+  // TODO: code-generate this
+  static inline std::ostream& operator<<(std::ostream& stream, CmdlineResult::Status status) {
+    switch (status) {
+      case CmdlineResult::kSuccess:
+        stream << "kSuccess";
+        break;
+      case CmdlineResult::kUsage:
+        stream << "kUsage";
+        break;
+      case CmdlineResult::kFailure:
+        stream << "kFailure";
+        break;
+      case CmdlineResult::kOutOfRange:
+        stream << "kOutOfRange";
+        break;
+      case CmdlineResult::kUnknown:
+        stream << "kUnknown";
+        break;
+      default:
+        UNREACHABLE();
+    }
+    return stream;
+  }
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_RESULT_H_
diff --git a/cmdline/cmdline_type_parser.h b/cmdline/cmdline_type_parser.h
new file mode 100644
index 0000000..fa5cdaf
--- /dev/null
+++ b/cmdline/cmdline_type_parser.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
+
+#include "cmdline_parse_result.h"
+
+namespace art {
+
+// Base class for user-defined CmdlineType<T> specializations.
+//
+// Not strictly necessary, but if the specializations fail to Define all of these functions
+// the compilation will fail.
+template <typename T>
+struct CmdlineTypeParser {
+  // Return value of parsing attempts. Represents a Success(T value) or an Error(int code)
+  using Result = CmdlineParseResult<T>;
+
+  // Parse a single value for an argument definition out of the wildcard component.
+  //
+  // e.g. if the argument definition was "foo:_", and the user-provided input was "foo:bar",
+  // then args is "bar".
+  Result Parse(const std::string& args ATTRIBUTE_UNUSED) {
+    assert(false);
+    return Result::Failure("Missing type specialization and/or value map");
+  }
+
+  // Parse a value and append it into the existing value so far, for argument
+  // definitions which are marked with AppendValues().
+  //
+  // The value is parsed out of the wildcard component as in Parse.
+  //
+  // If the initial value does not exist yet, a default value is created by
+  // value-initializing with 'T()'.
+  Result ParseAndAppend(const std::string& args ATTRIBUTE_UNUSED,
+                        T& existing_value ATTRIBUTE_UNUSED) {
+    assert(false);
+    return Result::Failure("Missing type specialization and/or value map");
+  }
+
+  // Runtime type name of T, so that we can print more useful error messages.
+  static const char* Name() { assert(false); return "UnspecializedType"; }
+
+  // Whether or not your type can parse argument definitions defined without a "_"
+  // e.g. -Xenable-profiler just mutates the existing profiler struct in-place
+  // so it doesn't need to do any parsing other than token recognition.
+  //
+  // If this is false, then either the argument definition has a _, from which the parsing
+  // happens, or the tokens get mapped to a value list/map from which a 1:1 matching occurs.
+  //
+  // This should almost *always* be false!
+  static constexpr bool kCanParseBlankless = false;
+
+ protected:
+  // Don't accidentally initialize instances of this directly; they will assert at runtime.
+  CmdlineTypeParser() = default;
+};
+
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
new file mode 100644
index 0000000..45f9b56
--- /dev/null
+++ b/cmdline/cmdline_types.h
@@ -0,0 +1,837 @@
+/*
+ * 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.
+ */
+#ifndef ART_CMDLINE_CMDLINE_TYPES_H_
+#define ART_CMDLINE_CMDLINE_TYPES_H_
+
+#define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
+
+#include "cmdline/memory_representation.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+#include "cmdline_type_parser.h"
+
+// Includes for the types that are being specialized
+#include <string>
+#include "unit.h"
+#include "jdwp/jdwp.h"
+#include "runtime/base/logging.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+
+namespace art {
+
+// The default specialization will always fail parsing the type from a string.
+// Provide your own specialization that inherits from CmdlineTypeParser<T>
+// and implements either Parse or ParseAndAppend
+// (only if the argument was defined with ::AppendValues()) but not both.
+template <typename T>
+struct CmdlineType : CmdlineTypeParser<T> {
+};
+
+// Specializations for CmdlineType<T> follow:
+
+// Parse argument definitions for Unit-typed arguments.
+template <>
+struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
+  Result Parse(const std::string& args) {
+    if (args == "") {
+      return Result::Success(Unit{});  // NOLINT [whitespace/braces] [5]
+    }
+    return Result::Failure("Unexpected extra characters " + args);
+  }
+};
+
+template <>
+struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
+  /*
+   * Handle one of the JDWP name/value pairs.
+   *
+   * JDWP options are:
+   *  help: if specified, show help message and bail
+   *  transport: may be dt_socket or dt_shmem
+   *  address: for dt_socket, "host:port", or just "port" when listening
+   *  server: if "y", wait for debugger to attach; if "n", attach to debugger
+   *  timeout: how long to wait for debugger to connect / listen
+   *
+   * Useful with server=n (these aren't supported yet):
+   *  onthrow=<exception-name>: connect to debugger when exception thrown
+   *  onuncaught=y|n: connect to debugger when uncaught exception thrown
+   *  launch=<command-line>: launch the debugger itself
+   *
+   * The "transport" option is required, as is "address" if server=n.
+   */
+  Result Parse(const std::string& options) {
+    VLOG(jdwp) << "ParseJdwpOptions: " << options;
+
+    if (options == "help") {
+      return Result::Usage(
+          "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+          "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+    }
+
+    const std::string s;
+
+    std::vector<std::string> pairs;
+    Split(options, ',', &pairs);
+
+    JDWP::JdwpOptions jdwp_options;
+    std::stringstream error_stream;
+
+    for (const std::string& jdwp_option : pairs) {
+      std::string::size_type equals_pos = jdwp_option.find('=');
+      if (equals_pos == std::string::npos) {
+        return Result::Failure(s +
+            "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'");
+      }
+
+      if (!ParseJdwpOption(jdwp_option.substr(0, equals_pos),
+                           jdwp_option.substr(equals_pos + 1),
+                           error_stream,
+                           &jdwp_options)) {
+        return Result::Failure(error_stream.str());
+      }
+    }
+
+    if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
+      return Result::Failure(s + "Must specify JDWP transport: " + options);
+    }
+    if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
+      return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
+    }
+
+    return Result::Success(std::move(jdwp_options));
+  }
+
+  bool ParseJdwpOption(const std::string& name, const std::string& value,
+                       std::ostream& error_stream,
+                       JDWP::JdwpOptions* jdwp_options) {
+    if (name == "transport") {
+      if (value == "dt_socket") {
+        jdwp_options->transport = JDWP::kJdwpTransportSocket;
+      } else if (value == "dt_android_adb") {
+        jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
+      } else {
+        error_stream << "JDWP transport not supported: " << value;
+        return false;
+      }
+    } else if (name == "server") {
+      if (value == "n") {
+        jdwp_options->server = false;
+      } else if (value == "y") {
+        jdwp_options->server = true;
+      } else {
+        error_stream << "JDWP option 'server' must be 'y' or 'n'";
+        return false;
+      }
+    } else if (name == "suspend") {
+      if (value == "n") {
+        jdwp_options->suspend = false;
+      } else if (value == "y") {
+        jdwp_options->suspend = true;
+      } else {
+        error_stream << "JDWP option 'suspend' must be 'y' or 'n'";
+        return false;
+      }
+    } else if (name == "address") {
+      /* this is either <port> or <host>:<port> */
+      std::string port_string;
+      jdwp_options->host.clear();
+      std::string::size_type colon = value.find(':');
+      if (colon != std::string::npos) {
+        jdwp_options->host = value.substr(0, colon);
+        port_string = value.substr(colon + 1);
+      } else {
+        port_string = value;
+      }
+      if (port_string.empty()) {
+        error_stream << "JDWP address missing port: " << value;
+        return false;
+      }
+      char* end;
+      uint64_t port = strtoul(port_string.c_str(), &end, 10);
+      if (*end != '\0' || port > 0xffff) {
+        error_stream << "JDWP address has junk in port field: " << value;
+        return false;
+      }
+      jdwp_options->port = port;
+    } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
+      /* valid but unsupported */
+      LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+    } else {
+      LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+    }
+
+    return true;
+  }
+
+  static const char* Name() { return "JdwpOptions"; }
+};
+
+template <size_t Divisor>
+struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
+  using typename CmdlineTypeParser<Memory<Divisor>>::Result;
+
+  Result Parse(const std::string arg) {
+    CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
+    size_t val = ParseMemoryOption(arg.c_str(), Divisor);
+    CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
+
+    if (val == 0) {
+      return Result::Failure(std::string("not a valid memory value, or not divisible by ")
+                             + std::to_string(Divisor));
+    }
+
+    return Result::Success(Memory<Divisor>(val));
+  }
+
+  // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+  // memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
+  // [gG] gigabytes.
+  //
+  // "s" should point just past the "-Xm?" part of the string.
+  // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+  // of 1024.
+  //
+  // The spec says the -Xmx and -Xms options must be multiples of 1024.  It
+  // doesn't say anything about -Xss.
+  //
+  // Returns 0 (a useless size) if "s" is malformed or specifies a low or
+  // non-evenly-divisible value.
+  //
+  static size_t ParseMemoryOption(const char* s, size_t div) {
+    // strtoul accepts a leading [+-], which we don't want,
+    // so make sure our string starts with a decimal digit.
+    if (isdigit(*s)) {
+      char* s2;
+      size_t val = strtoul(s, &s2, 10);
+      if (s2 != s) {
+        // s2 should be pointing just after the number.
+        // If this is the end of the string, the user
+        // has specified a number of bytes.  Otherwise,
+        // there should be exactly one more character
+        // that specifies a multiplier.
+        if (*s2 != '\0') {
+          // The remainder of the string is either a single multiplier
+          // character, or nothing to indicate that the value is in
+          // bytes.
+          char c = *s2++;
+          if (*s2 == '\0') {
+            size_t mul;
+            if (c == '\0') {
+              mul = 1;
+            } else if (c == 'k' || c == 'K') {
+              mul = KB;
+            } else if (c == 'm' || c == 'M') {
+              mul = MB;
+            } else if (c == 'g' || c == 'G') {
+              mul = GB;
+            } else {
+              // Unknown multiplier character.
+              return 0;
+            }
+
+            if (val <= std::numeric_limits<size_t>::max() / mul) {
+              val *= mul;
+            } else {
+              // Clamp to a multiple of 1024.
+              val = std::numeric_limits<size_t>::max() & ~(1024-1);
+            }
+          } else {
+            // There's more than one character after the numeric part.
+            return 0;
+          }
+        }
+        // The man page says that a -Xm value must be a multiple of 1024.
+        if (val % div == 0) {
+          return val;
+        }
+      }
+    }
+    return 0;
+  }
+
+  static const char* Name() { return Memory<Divisor>::Name(); }
+};
+
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+  Result Parse(const std::string& str) {
+    char* end = nullptr;
+    errno = 0;
+    double value = strtod(str.c_str(), &end);
+
+    if (*end != '\0') {
+      return Result::Failure("Failed to parse double from " + str);
+    }
+    if (errno == ERANGE) {
+      return Result::OutOfRange(
+          "Failed to parse double from " + str + "; overflow/underflow occurred");
+    }
+
+    return Result::Success(value);
+  }
+
+  static const char* Name() { return "double"; }
+};
+
+template <>
+struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
+  Result Parse(const std::string& str) {
+    const char* begin = str.c_str();
+    char* end;
+
+    // Parse into a larger type (long long) because we can't use strtoul
+    // since it silently converts negative values into unsigned long and doesn't set errno.
+    errno = 0;
+    long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
+    if (begin == end || *end != '\0' || errno == EINVAL) {
+      return Result::Failure("Failed to parse integer from " + str);
+    } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
+        result < std::numeric_limits<int>::min()
+        || result > std::numeric_limits<unsigned int>::max() || result < 0) {
+      return Result::OutOfRange(
+          "Failed to parse integer from " + str + "; out of unsigned int range");
+    }
+
+    return Result::Success(static_cast<unsigned int>(result));
+  }
+
+  static const char* Name() { return "unsigned integer"; }
+};
+
+// Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
+// to nanoseconds automatically after parsing.
+//
+// All implicit conversion from uint64_t uses nanoseconds.
+struct MillisecondsToNanoseconds {
+  // Create from nanoseconds.
+  MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) {  // NOLINT [runtime/explicit] [5]
+  }
+
+  // Create from milliseconds.
+  static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
+    return MillisecondsToNanoseconds(MsToNs(milliseconds));
+  }
+
+  // Get the underlying nanoseconds value.
+  uint64_t GetNanoseconds() const {
+    return nanoseconds_;
+  }
+
+  // Get the milliseconds value [via a conversion]. Loss of precision will occur.
+  uint64_t GetMilliseconds() const {
+    return NsToMs(nanoseconds_);
+  }
+
+  // Get the underlying nanoseconds value.
+  operator uint64_t() const {
+    return GetNanoseconds();
+  }
+
+  // Default constructors/copy-constructors.
+  MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
+  MillisecondsToNanoseconds(const MillisecondsToNanoseconds& rhs) = default;
+  MillisecondsToNanoseconds(MillisecondsToNanoseconds&& rhs) = default;
+
+ private:
+  uint64_t nanoseconds_;
+};
+
+template <>
+struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
+  Result Parse(const std::string& str) {
+    CmdlineType<unsigned int> uint_parser;
+    CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
+
+    if (res.IsSuccess()) {
+      return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
+    } else {
+      return Result::CastError(res);
+    }
+  }
+
+  static const char* Name() { return "MillisecondsToNanoseconds"; }
+};
+
+template <>
+struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
+  Result Parse(const std::string& args) {
+    return Result::Success(args);
+  }
+
+  Result ParseAndAppend(const std::string& args,
+                        std::string& existing_value) {
+    if (existing_value.empty()) {
+      existing_value = args;
+    } else {
+      existing_value += ' ';
+      existing_value += args;
+    }
+    return Result::SuccessNoValue();
+  }
+};
+
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+  Result Parse(const std::string& args) {
+    assert(false && "Use AppendValues() for a string vector type");
+    return Result::Failure("Unconditional failure: string vector must be appended: " + args);
+  }
+
+  Result ParseAndAppend(const std::string& args,
+                        std::vector<std::string>& existing_value) {
+    existing_value.push_back(args);
+    return Result::SuccessNoValue();
+  }
+
+  static const char* Name() { return "std::vector<std::string>"; }
+};
+
+template <char Separator>
+struct ParseStringList {
+  explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
+
+  operator std::vector<std::string>() const {
+    return list_;
+  }
+
+  operator std::vector<std::string>&&() && {
+    return std::move(list_);
+  }
+
+  size_t Size() const {
+    return list_.size();
+  }
+
+  std::string Join() const {
+    return art::Join(list_, Separator);
+  }
+
+  static ParseStringList<Separator> Split(const std::string& str) {
+    std::vector<std::string> list;
+    art::Split(str, Separator, &list);
+    return ParseStringList<Separator>(std::move(list));
+  }
+
+  ParseStringList() = default;
+  ParseStringList(const ParseStringList& rhs) = default;
+  ParseStringList(ParseStringList&& rhs) = default;
+
+ private:
+  std::vector<std::string> list_;
+};
+
+template <char Separator>
+struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
+  using Result = CmdlineParseResult<ParseStringList<Separator>>;
+
+  Result Parse(const std::string& args) {
+    return Result::Success(ParseStringList<Separator>::Split(args));
+  }
+
+  static const char* Name() { return "ParseStringList<Separator>"; }
+};
+
+static gc::CollectorType ParseCollectorType(const std::string& option) {
+  if (option == "MS" || option == "nonconcurrent") {
+    return gc::kCollectorTypeMS;
+  } else if (option == "CMS" || option == "concurrent") {
+    return gc::kCollectorTypeCMS;
+  } else if (option == "SS") {
+    return gc::kCollectorTypeSS;
+  } else if (option == "GSS") {
+    return gc::kCollectorTypeGSS;
+  } else if (option == "CC") {
+    return gc::kCollectorTypeCC;
+  } else if (option == "MC") {
+    return gc::kCollectorTypeMC;
+  } else {
+    return gc::kCollectorTypeNone;
+  }
+}
+
+struct XGcOption {
+  // These defaults are used when the command line arguments for -Xgc:
+  // are either omitted completely or partially.
+  gc::CollectorType collector_type_ =  kUseReadBarrier ?
+                                           // If RB is enabled (currently a build-time decision),
+                                           // use CC as the default GC.
+                                           gc::kCollectorTypeCC :
+                                           gc::kCollectorTypeDefault;
+  bool verify_pre_gc_heap_ = false;
+  bool verify_pre_sweeping_heap_ = kIsDebugBuild;
+  bool verify_post_gc_heap_ = false;
+  bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
+  bool verify_pre_sweeping_rosalloc_ = false;
+  bool verify_post_gc_rosalloc_ = false;
+};
+
+template <>
+struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
+  Result Parse(const std::string& option) {  // -Xgc: already stripped
+    XGcOption xgc{};  // NOLINT [readability/braces] [4]
+
+    std::vector<std::string> gc_options;
+    Split(option, ',', &gc_options);
+    for (const std::string& gc_option : gc_options) {
+      gc::CollectorType collector_type = ParseCollectorType(gc_option);
+      if (collector_type != gc::kCollectorTypeNone) {
+        xgc.collector_type_ = collector_type;
+      } else if (gc_option == "preverify") {
+        xgc.verify_pre_gc_heap_ = true;
+      } else if (gc_option == "nopreverify") {
+        xgc.verify_pre_gc_heap_ = false;
+      }  else if (gc_option == "presweepingverify") {
+        xgc.verify_pre_sweeping_heap_ = true;
+      } else if (gc_option == "nopresweepingverify") {
+        xgc.verify_pre_sweeping_heap_ = false;
+      } else if (gc_option == "postverify") {
+        xgc.verify_post_gc_heap_ = true;
+      } else if (gc_option == "nopostverify") {
+        xgc.verify_post_gc_heap_ = false;
+      } else if (gc_option == "preverify_rosalloc") {
+        xgc.verify_pre_gc_rosalloc_ = true;
+      } else if (gc_option == "nopreverify_rosalloc") {
+        xgc.verify_pre_gc_rosalloc_ = false;
+      } else if (gc_option == "presweepingverify_rosalloc") {
+        xgc.verify_pre_sweeping_rosalloc_ = true;
+      } else if (gc_option == "nopresweepingverify_rosalloc") {
+        xgc.verify_pre_sweeping_rosalloc_ = false;
+      } else if (gc_option == "postverify_rosalloc") {
+        xgc.verify_post_gc_rosalloc_ = true;
+      } else if (gc_option == "nopostverify_rosalloc") {
+        xgc.verify_post_gc_rosalloc_ = false;
+      } else if ((gc_option == "precise") ||
+                 (gc_option == "noprecise") ||
+                 (gc_option == "verifycardtable") ||
+                 (gc_option == "noverifycardtable")) {
+        // Ignored for backwards compatibility.
+      } else {
+        return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
+      }
+    }
+
+    return Result::Success(std::move(xgc));
+  }
+
+  static const char* Name() { return "XgcOption"; }
+};
+
+struct BackgroundGcOption {
+  // If background_collector_type_ is kCollectorTypeNone, it defaults to the
+  // XGcOption::collector_type_ after parsing options. If you set this to
+  // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
+  // we transition to background instead of a normal collector transition.
+  gc::CollectorType background_collector_type_;
+
+  BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
+    : background_collector_type_(background_collector_type) {}
+  BackgroundGcOption()
+    : background_collector_type_(gc::kCollectorTypeNone) {
+
+    if (kUseReadBarrier) {
+      background_collector_type_ = gc::kCollectorTypeCC;  // Disable background compaction for CC.
+    }
+  }
+
+  operator gc::CollectorType() const { return background_collector_type_; }
+};
+
+template<>
+struct CmdlineType<BackgroundGcOption>
+  : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
+  Result Parse(const std::string& substring) {
+    // Special handling for HSpaceCompact since this is only valid as a background GC type.
+    if (substring == "HSpaceCompact") {
+      background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
+    } else {
+      gc::CollectorType collector_type = ParseCollectorType(substring);
+      if (collector_type != gc::kCollectorTypeNone) {
+        background_collector_type_ = collector_type;
+      } else {
+        return Result::Failure();
+      }
+    }
+
+    BackgroundGcOption res = *this;
+    return Result::Success(res);
+  }
+
+  static const char* Name() { return "BackgroundGcOption"; }
+};
+
+template <>
+struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
+  Result Parse(const std::string& options) {
+    LogVerbosity log_verbosity = LogVerbosity();
+
+    std::vector<std::string> verbose_options;
+    Split(options, ',', &verbose_options);
+    for (size_t j = 0; j < verbose_options.size(); ++j) {
+      if (verbose_options[j] == "class") {
+        log_verbosity.class_linker = true;
+      } else if (verbose_options[j] == "compiler") {
+        log_verbosity.compiler = true;
+      } else if (verbose_options[j] == "gc") {
+        log_verbosity.gc = true;
+      } else if (verbose_options[j] == "heap") {
+        log_verbosity.heap = true;
+      } else if (verbose_options[j] == "jdwp") {
+        log_verbosity.jdwp = true;
+      } else if (verbose_options[j] == "jni") {
+        log_verbosity.jni = true;
+      } else if (verbose_options[j] == "monitor") {
+        log_verbosity.monitor = true;
+      } else if (verbose_options[j] == "profiler") {
+        log_verbosity.profiler = true;
+      } else if (verbose_options[j] == "signals") {
+        log_verbosity.signals = true;
+      } else if (verbose_options[j] == "startup") {
+        log_verbosity.startup = true;
+      } else if (verbose_options[j] == "third-party-jni") {
+        log_verbosity.third_party_jni = true;
+      } else if (verbose_options[j] == "threads") {
+        log_verbosity.threads = true;
+      } else if (verbose_options[j] == "verifier") {
+        log_verbosity.verifier = true;
+      } else {
+        return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
+      }
+    }
+
+    return Result::Success(log_verbosity);
+  }
+
+  static const char* Name() { return "LogVerbosity"; }
+};
+
+// TODO: Replace with art::ProfilerOptions for the real thing.
+struct TestProfilerOptions {
+  // Whether or not the applications should be profiled.
+  bool enabled_;
+  // Destination file name where the profiling data will be saved into.
+  std::string output_file_name_;
+  // Generate profile every n seconds.
+  uint32_t period_s_;
+  // Run profile for n seconds.
+  uint32_t duration_s_;
+  // Microseconds between samples.
+  uint32_t interval_us_;
+  // Coefficient to exponential backoff.
+  double backoff_coefficient_;
+  // Whether the profile should start upon app startup or be delayed by some random offset.
+  bool start_immediately_;
+  // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
+  double top_k_threshold_;
+  // How much the top K% samples needs to change in order for the app to be recompiled.
+  double top_k_change_threshold_;
+  // The type of profile data dumped to the disk.
+  ProfileDataType profile_type_;
+  // The max depth of the stack collected by the profiler
+  uint32_t max_stack_depth_;
+
+  TestProfilerOptions() :
+    enabled_(false),
+    output_file_name_(),
+    period_s_(0),
+    duration_s_(0),
+    interval_us_(0),
+    backoff_coefficient_(0),
+    start_immediately_(0),
+    top_k_threshold_(0),
+    top_k_change_threshold_(0),
+    profile_type_(ProfileDataType::kProfilerMethod),
+    max_stack_depth_(0) {
+  }
+
+  TestProfilerOptions(const TestProfilerOptions& other) = default;
+  TestProfilerOptions(TestProfilerOptions&& other) = default;
+};
+
+static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
+  stream << "TestProfilerOptions {" << std::endl;
+
+#define PRINT_TO_STREAM(field) \
+  stream << #field << ": '" << options.field << "'" << std::endl;
+
+  PRINT_TO_STREAM(enabled_);
+  PRINT_TO_STREAM(output_file_name_);
+  PRINT_TO_STREAM(period_s_);
+  PRINT_TO_STREAM(duration_s_);
+  PRINT_TO_STREAM(interval_us_);
+  PRINT_TO_STREAM(backoff_coefficient_);
+  PRINT_TO_STREAM(start_immediately_);
+  PRINT_TO_STREAM(top_k_threshold_);
+  PRINT_TO_STREAM(top_k_change_threshold_);
+  PRINT_TO_STREAM(profile_type_);
+  PRINT_TO_STREAM(max_stack_depth_);
+
+  stream << "}";
+
+  return stream;
+#undef PRINT_TO_STREAM
+}
+
+template <>
+struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
+  using Result = CmdlineParseResult<TestProfilerOptions>;
+
+ private:
+  using StringResult = CmdlineParseResult<std::string>;
+  using DoubleResult = CmdlineParseResult<double>;
+
+  template <typename T>
+  static Result ParseInto(TestProfilerOptions& options,
+                          T TestProfilerOptions::*pField,
+                          CmdlineParseResult<T>&& result) {
+    assert(pField != nullptr);
+
+    if (result.IsSuccess()) {
+      options.*pField = result.ReleaseValue();
+      return Result::SuccessNoValue();
+    }
+
+    return Result::CastError(result);
+  }
+
+  template <typename T>
+  static Result ParseIntoRangeCheck(TestProfilerOptions& options,
+                                    T TestProfilerOptions::*pField,
+                                    CmdlineParseResult<T>&& result,
+                                    T min,
+                                    T max) {
+    if (result.IsSuccess()) {
+      const T& value = result.GetValue();
+
+      if (value < min || value > max) {
+        CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
+        return Result::CastError(out_of_range);
+      }
+    }
+
+    return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
+  }
+
+  static StringResult ParseStringAfterChar(const std::string& s, char c) {
+    std::string parsed_value;
+
+    std::string::size_type colon = s.find(c);
+    if (colon == std::string::npos) {
+      return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
+    }
+    // Add one to remove the char we were trimming until.
+    parsed_value = s.substr(colon + 1);
+    return StringResult::Success(parsed_value);
+  }
+
+  static std::string RemovePrefix(const std::string& source) {
+    size_t prefix_idx = source.find(":");
+
+    if (prefix_idx == std::string::npos) {
+      return "";
+    }
+
+    return source.substr(prefix_idx + 1);
+  }
+
+ public:
+  Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
+    // Special case which doesn't include a wildcard argument definition.
+    // We pass-it through as-is.
+    if (option == "-Xenable-profiler") {
+      existing.enabled_ = true;
+      return Result::SuccessNoValue();
+    }
+
+    // The rest of these options are always the wildcard from '-Xprofile-*'
+    std::string suffix = RemovePrefix(option);
+
+    if (StartsWith(option, "filename:")) {
+      CmdlineType<std::string> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::output_file_name_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "period:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::period_s_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "duration:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::duration_s_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "interval:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::interval_us_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "backoff:")) {
+      CmdlineType<double> type_parser;
+
+      return ParseIntoRangeCheck(existing,
+                                 &TestProfilerOptions::backoff_coefficient_,
+                                 type_parser.Parse(suffix),
+                                 1.0,
+                                 10.0);
+
+    } else if (option == "start-immediately") {
+      existing.start_immediately_ = true;
+      return Result::SuccessNoValue();
+    } else if (StartsWith(option, "top-k-threshold:")) {
+      CmdlineType<double> type_parser;
+
+      return ParseIntoRangeCheck(existing,
+                                 &TestProfilerOptions::top_k_threshold_,
+                                 type_parser.Parse(suffix),
+                                 0.0,
+                                 100.0);
+    } else if (StartsWith(option, "top-k-change-threshold:")) {
+      CmdlineType<double> type_parser;
+
+      return ParseIntoRangeCheck(existing,
+                                 &TestProfilerOptions::top_k_change_threshold_,
+                                 type_parser.Parse(suffix),
+                                 0.0,
+                                 100.0);
+    } else if (option == "type:method") {
+      existing.profile_type_ = kProfilerMethod;
+      return Result::SuccessNoValue();
+    } else if (option == "type:stack") {
+      existing.profile_type_ = kProfilerBoundedStack;
+      return Result::SuccessNoValue();
+    } else if (StartsWith(option, "max-stack-depth:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::max_stack_depth_,
+                       type_parser.Parse(suffix));
+    } else {
+      return Result::Failure(std::string("Invalid suboption '") + option + "'");
+    }
+  }
+
+  static const char* Name() { return "TestProfilerOptions"; }
+  static constexpr bool kCanParseBlankless = true;
+};
+
+
+}  // namespace art
+#endif  // ART_CMDLINE_CMDLINE_TYPES_H_
diff --git a/cmdline/detail/cmdline_debug_detail.h b/cmdline/detail/cmdline_debug_detail.h
new file mode 100644
index 0000000..79028f4
--- /dev/null
+++ b/cmdline/detail/cmdline_debug_detail.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
+
+#include <iostream>
+#ifndef CMDLINE_NDEBUG
+#define CMDLINE_DEBUG_LOG std::cerr
+#else
+#define CMDLINE_DEBUG_LOG ::art::detail::debug_log_ignore()
+#endif
+
+namespace art {
+  // Implementation details for some template querying. Don't look inside if you hate templates.
+  namespace detail {
+    struct debug_log_ignore {
+      // Ignore most of the normal operator<< usage.
+      template <typename T>
+      debug_log_ignore& operator<<(const T&) { return *this; }
+      // Ignore std::endl and the like.
+      debug_log_ignore& operator<<(std::ostream& (*)(std::ostream&) ) { return *this; }
+    };
+  }  // namespace detail  // NOLINT [readability/namespace] [5]
+}  // namespace art
+
+#endif  // ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
new file mode 100644
index 0000000..81ef36b
--- /dev/null
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -0,0 +1,503 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
+
+#include <type_traits>
+#include <assert.h>
+#include <functional>
+#include <vector>
+#include <algorithm>
+#include <numeric>
+#include <memory>
+
+#include "cmdline/cmdline_parse_result.h"
+#include "cmdline/token_range.h"
+#include "cmdline/unit.h"
+#include "cmdline/cmdline_types.h"
+
+namespace art {
+  // Implementation details for the parser. Do not look inside if you hate templates.
+  namespace detail {
+    // A non-templated base class for argument parsers. Used by the general parser
+    // to parse arguments, without needing to know the argument type at compile time.
+    //
+    // This is an application of the type erasure idiom.
+    struct CmdlineParseArgumentAny {
+      virtual ~CmdlineParseArgumentAny() {}
+
+      // Attempt to parse this argument starting at arguments[position].
+      // If the parsing succeeds, the parsed value will be saved as a side-effect.
+      //
+      // In most situations, the parsing will not match by returning kUnknown. In this case,
+      // no tokens were consumed and the position variable will not be updated.
+      //
+      // At other times, parsing may fail due to validation but the initial token was still matched
+      // (for example an out of range value, or passing in a string where an int was expected).
+      // In this case the tokens are still consumed, and the position variable will get incremented
+      // by all the consumed tokens.
+      //
+      // The # of tokens consumed by the parse attempt will be set as an out-parameter into
+      // consumed_tokens. The parser should skip this many tokens before parsing the next
+      // argument.
+      virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
+      // How many tokens should be taken off argv for parsing this argument.
+      // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+      //
+      // A [min,max] range is returned to represent argument definitions with multiple
+      // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+      virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
+      // Get the run-time typename of the argument type.
+      virtual const char* GetTypeName() const = 0;
+      // Try to do a close match, returning how many tokens were matched against this argument
+      // definition. More tokens is better.
+      //
+      // Do a quick match token-by-token, and see if they match.
+      // Any tokens with a wildcard in them are only matched up until the wildcard.
+      // If this is true, then the wildcard matching later on can still fail, so this is not
+      // a guarantee that the argument is correct, it's more of a strong hint that the
+      // user-provided input *probably* was trying to match this argument.
+      //
+      // Returns how many tokens were either matched (or ignored because there was a
+      // wildcard present). 0 means no match. If the Size() tokens are returned.
+      virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
+    };
+
+    template <typename T>
+    using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
+
+    template <typename T>
+    using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
+
+    // Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
+    template <typename TArg>
+    struct CmdlineParserArgumentInfo {
+      // This version will only be used if TArg is arithmetic and thus has the <= operators.
+      template <typename T = TArg>  // Necessary to get SFINAE to kick in.
+      bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = 0) {
+        if (has_range_) {
+          return min_ <= value && value <= max_;
+        }
+        return true;
+      }
+
+      // This version will be used at other times when TArg is not arithmetic.
+      template <typename T = TArg>
+      bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = 0) {
+        assert(!has_range_);
+        return true;
+      }
+
+      // Do a quick match token-by-token, and see if they match.
+      // Any tokens with a wildcard in them only match the prefix up until the wildcard.
+      //
+      // If this is true, then the wildcard matching later on can still fail, so this is not
+      // a guarantee that the argument is correct, it's more of a strong hint that the
+      // user-provided input *probably* was trying to match this argument.
+      size_t MaybeMatches(TokenRange token_list) const {
+        auto best_match = FindClosestMatch(token_list);
+
+        return best_match.second;
+      }
+
+      // Attempt to find the closest match (see MaybeMatches).
+      //
+      // Returns the token range that was the closest match and the # of tokens that
+      // this range was matched up until.
+      std::pair<const TokenRange*, size_t> FindClosestMatch(TokenRange token_list) const {
+        const TokenRange* best_match_ptr = nullptr;
+
+        size_t best_match = 0;
+        for (auto&& token_range : tokenized_names_) {
+          size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
+
+          if (this_match > best_match) {
+            best_match_ptr = &token_range;
+            best_match = this_match;
+          }
+        }
+
+        return std::make_pair(best_match_ptr, best_match);
+      }
+
+      // Mark the argument definition as completed, do not mutate the object anymore after this
+      // call is done.
+      //
+      // Performs several sanity checks and token calculations.
+      void CompleteArgument() {
+        assert(names_.size() >= 1);
+        assert(!is_completed_);
+
+        is_completed_ = true;
+
+        size_t blank_count = 0;
+        size_t token_count = 0;
+
+        size_t global_blank_count = 0;
+        size_t global_token_count = 0;
+        for (auto&& name : names_) {
+          std::string s(name);
+
+          size_t local_blank_count = std::count(s.begin(), s.end(), '_');
+          size_t local_token_count = std::count(s.begin(), s.end(), ' ');
+
+          if (global_blank_count != 0) {
+            assert(local_blank_count == global_blank_count
+                   && "Every argument descriptor string must have same amount of blanks (_)");
+          }
+
+          if (local_blank_count != 0) {
+            global_blank_count = local_blank_count;
+            blank_count++;
+
+            assert(local_blank_count == 1 && "More than one blank is not supported");
+            assert(s.back() == '_' && "The blank character must only be at the end of the string");
+          }
+
+          if (global_token_count != 0) {
+            assert(local_token_count == global_token_count
+                   && "Every argument descriptor string must have same amount of tokens (spaces)");
+          }
+
+          if (local_token_count != 0) {
+            global_token_count = local_token_count;
+            token_count++;
+          }
+
+          // Tokenize every name, turning it from a string to a token list.
+          tokenized_names_.clear();
+          for (auto&& name1 : names_) {
+            // Split along ' ' only, removing any duplicated spaces.
+            tokenized_names_.push_back(
+                TokenRange::Split(name1, {' '}).RemoveToken(" "));
+          }
+
+          // remove the _ character from each of the token ranges
+          // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
+          // and this is OK because we still need an empty token to simplify
+          // range comparisons
+          simple_names_.clear();
+
+          for (auto&& tokenized_name : tokenized_names_) {
+            simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
+          }
+        }
+
+        if (token_count != 0) {
+          assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
+              token_count == names_.size()));
+        }
+
+        if (blank_count != 0) {
+          assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
+              blank_count == names_.size()));
+        }
+
+        using_blanks_ = blank_count > 0;
+        {
+          size_t smallest_name_token_range_size =
+              std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
+                              [](size_t min, const TokenRange& cur) {
+                                return std::min(min, cur.Size());
+                              });
+          size_t largest_name_token_range_size =
+              std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
+                              [](size_t max, const TokenRange& cur) {
+                                return std::max(max, cur.Size());
+                              });
+
+          token_range_size_ = std::make_pair(smallest_name_token_range_size,
+                                             largest_name_token_range_size);
+        }
+
+        if (has_value_list_) {
+          assert(names_.size() == value_list_.size()
+                 && "Number of arg descriptors must match number of values");
+          assert(!has_value_map_);
+        }
+        if (has_value_map_) {
+          if (!using_blanks_) {
+            assert(names_.size() == value_map_.size() &&
+                   "Since no blanks were specified, each arg is mapped directly into a mapped "
+                   "value without parsing; sizes must match");
+          }
+
+          assert(!has_value_list_);
+        }
+
+        if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
+          assert((has_value_map_ || has_value_list_) &&
+                 "Arguments without a blank (_) must provide either a value map or a value list");
+        }
+
+        TypedCheck();
+      }
+
+      // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
+      std::vector<const char*> names_;
+      // Is there at least 1 wildcard '_' in the argument definition?
+      bool using_blanks_ = false;
+      // [min, max] token counts in each arg def
+      std::pair<size_t, size_t> token_range_size_;
+
+      // contains all the names in a tokenized form, i.e. as a space-delimited list
+      std::vector<TokenRange> tokenized_names_;
+
+      // contains the tokenized names, but with the _ character stripped
+      std::vector<TokenRange> simple_names_;
+
+      // For argument definitions created with '.AppendValues()'
+      // Meaning that parsing should mutate the existing value in-place if possible.
+      bool appending_values_ = false;
+
+      // For argument definitions created with '.WithRange(min, max)'
+      bool has_range_ = false;
+      TArg min_;
+      TArg max_;
+
+      // For argument definitions created with '.WithValueMap'
+      bool has_value_map_ = false;
+      std::vector<std::pair<const char*, TArg>> value_map_;
+
+      // For argument definitions created with '.WithValues'
+      bool has_value_list_ = false;
+      std::vector<TArg> value_list_;
+
+      // Make sure there's a default constructor.
+      CmdlineParserArgumentInfo() = default;
+
+      // Ensure there's a default move constructor.
+      CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) = default;
+
+     private:
+      // Perform type-specific checks at runtime.
+      template <typename T = TArg>
+      void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
+        assert(!using_blanks_ &&
+               "Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
+      }
+
+      void TypedCheck() {}
+
+      bool is_completed_ = false;
+    };
+
+    // A virtual-implementation of the necessary argument information in order to
+    // be able to parse arguments.
+    template <typename TArg>
+    struct CmdlineParseArgument : CmdlineParseArgumentAny {
+      explicit CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
+                                    std::function<void(TArg&)>&& save_argument,
+                                    std::function<TArg&(void)>&& load_argument)
+          : argument_info_(std::forward<decltype(argument_info)>(argument_info)),
+            save_argument_(std::forward<decltype(save_argument)>(save_argument)),
+            load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
+      }
+
+      using UserTypeInfo = CmdlineType<TArg>;
+
+      virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
+        assert(arguments.Size() > 0);
+        assert(consumed_tokens != nullptr);
+
+        auto closest_match_res = argument_info_.FindClosestMatch(arguments);
+        size_t best_match_size = closest_match_res.second;
+        const TokenRange* best_match_arg_def = closest_match_res.first;
+
+        if (best_match_size > arguments.Size()) {
+          // The best match has more tokens than were provided.
+          // Shouldn't happen in practice since the outer parser does this check.
+          return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
+        }
+
+        assert(best_match_arg_def != nullptr);
+        *consumed_tokens = best_match_arg_def->Size();
+
+        if (!argument_info_.using_blanks_) {
+          return ParseArgumentSingle(arguments.Join(' '));
+        }
+
+        // Extract out the blank value from arguments
+        // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
+        std::string blank_value = "";
+        size_t idx = 0;
+        for (auto&& def_token : *best_match_arg_def) {
+          auto&& arg_token = arguments[idx];
+
+          // Does this definition-token have a wildcard in it?
+          if (def_token.find('_') == std::string::npos) {
+            // No, regular token. Match 1:1 against the argument token.
+            bool token_match = def_token == arg_token;
+
+            if (!token_match) {
+              return CmdlineResult(CmdlineResult::kFailure,
+                                   std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+                                   + " at token " + std::to_string(idx));
+            }
+          } else {
+            // This is a wild-carded token.
+            TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
+
+            // Extract the wildcard contents out of the user-provided arg_token.
+            std::unique_ptr<TokenRange> arg_matches =
+                def_split_wildcards.MatchSubstrings(arg_token, "_");
+            if (arg_matches == nullptr) {
+              return CmdlineResult(CmdlineResult::kFailure,
+                                   std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+                                   + ", with a wildcard pattern " + def_token
+                                   + " at token " + std::to_string(idx));
+            }
+
+            // Get the corresponding wildcard tokens from arg_matches,
+            // and concatenate it to blank_value.
+            for (size_t sub_idx = 0;
+                sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
+              if (def_split_wildcards[sub_idx] == "_") {
+                blank_value += arg_matches->GetToken(sub_idx);
+              }
+            }
+          }
+
+          ++idx;
+        }
+
+        return ParseArgumentSingle(blank_value);
+      }
+
+     private:
+      virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
+        // TODO: refactor to use LookupValue for the value lists/maps
+
+        // Handle the 'WithValueMap(...)' argument definition
+        if (argument_info_.has_value_map_) {
+          for (auto&& value_pair : argument_info_.value_map_) {
+            const char* name = value_pair.first;
+
+            if (argument == name) {
+              return SaveArgument(value_pair.second);
+            }
+          }
+
+          // Error case: Fail, telling the user what the allowed values were.
+          std::vector<std::string> allowed_values;
+          for (auto&& value_pair : argument_info_.value_map_) {
+            const char* name = value_pair.first;
+            allowed_values.push_back(name);
+          }
+
+          std::string allowed_values_flat = Join(allowed_values, ',');
+          return CmdlineResult(CmdlineResult::kFailure,
+                               "Argument value '" + argument + "' does not match any of known valid"
+                                "values: {" + allowed_values_flat + "}");
+        }
+
+        // Handle the 'WithValues(...)' argument definition
+        if (argument_info_.has_value_list_) {
+          size_t arg_def_idx = 0;
+          for (auto&& value : argument_info_.value_list_) {
+            auto&& arg_def_token = argument_info_.names_[arg_def_idx];
+
+            if (arg_def_token == argument) {
+              return SaveArgument(value);
+            }
+            ++arg_def_idx;
+          }
+
+          assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
+                 "Number of named argument definitions must match number of values defined");
+
+          // Error case: Fail, telling the user what the allowed values were.
+          std::vector<std::string> allowed_values;
+          for (auto&& arg_name : argument_info_.names_) {
+            allowed_values.push_back(arg_name);
+          }
+
+          std::string allowed_values_flat = Join(allowed_values, ',');
+          return CmdlineResult(CmdlineResult::kFailure,
+                               "Argument value '" + argument + "' does not match any of known valid"
+                                "values: {" + allowed_values_flat + "}");
+        }
+
+        // Handle the regular case where we parsed an unknown value from a blank.
+        UserTypeInfo type_parser;
+
+        if (argument_info_.appending_values_) {
+          TArg& existing = load_argument_();
+          CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
+
+          assert(!argument_info_.has_range_);
+
+          return result;
+        }
+
+        CmdlineParseResult<TArg> result = type_parser.Parse(argument);
+
+        if (result.IsSuccess()) {
+          TArg& value = result.GetValue();
+
+          // Do a range check for 'WithRange(min,max)' argument definition.
+          if (!argument_info_.CheckRange(value)) {
+            return CmdlineParseResult<TArg>::OutOfRange(
+                value, argument_info_.min_, argument_info_.max_);
+          }
+
+          return SaveArgument(value);
+        }
+
+        // Some kind of type-specific parse error. Pass the result as-is.
+        CmdlineResult raw_result = std::move(result);
+        return raw_result;
+      }
+
+     public:
+      virtual const char* GetTypeName() const {
+        // TODO: Obviate the need for each type specialization to hardcode the type name
+        return UserTypeInfo::Name();
+      }
+
+      // How many tokens should be taken off argv for parsing this argument.
+      // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+      //
+      // A [min,max] range is returned to represent argument definitions with multiple
+      // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+      virtual std::pair<size_t, size_t> GetNumTokens() const {
+        return argument_info_.token_range_size_;
+      }
+
+      // See if this token range might begin the same as the argument definition.
+      virtual size_t MaybeMatches(const TokenRange& tokens) {
+        return argument_info_.MaybeMatches(tokens);
+      }
+
+     private:
+      CmdlineResult SaveArgument(const TArg& value) {
+        assert(!argument_info_.appending_values_
+               && "If the values are being appended, then the updated parse value is "
+                   "updated by-ref as a side effect and shouldn't be stored directly");
+        TArg val = value;
+        save_argument_(val);
+        return CmdlineResult(CmdlineResult::kSuccess);
+      }
+
+      CmdlineParserArgumentInfo<TArg> argument_info_;
+      std::function<void(TArg&)> save_argument_;
+      std::function<TArg&(void)> load_argument_;
+    };
+  } // namespace detail // NOLINT [readability/namespace] [5] [whitespace/comments] [2]
+}  // namespace art
+
+#endif  // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
new file mode 100644
index 0000000..9b43bb0
--- /dev/null
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace art {
+  // Implementation details for some template querying. Don't look inside if you hate templates.
+  namespace detail {
+    template <typename T>
+    typename std::remove_reference<T>::type& FakeReference();
+
+    // SupportsInsertionOperator<T, TStream>::value will evaluate to a boolean,
+    // whose value is true if the TStream class supports the << operator against T,
+    // and false otherwise.
+    template <typename T2, typename TStream2 = std::ostream>
+    struct SupportsInsertionOperator {
+     private:
+      template <typename TStream, typename T>
+      static std::true_type InsertionOperatorTest(TStream& os, const T& value,
+                                                  std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
+
+      template <typename TStream, typename ... T>
+      static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
+
+     public:
+      static constexpr bool value =
+          decltype(InsertionOperatorTest(FakeReference<TStream2>(), std::declval<T2>()))::value;
+    };
+
+    template <typename TLeft, typename TRight = TLeft, bool IsFloatingPoint = false>
+    struct SupportsEqualityOperatorImpl;
+
+    template <typename TLeft, typename TRight>
+    struct SupportsEqualityOperatorImpl<TLeft, TRight, false> {
+     private:
+      template <typename TL, typename TR>
+      static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
+                                                 std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
+
+      template <typename TL, typename ... T>
+      static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
+
+     public:
+      static constexpr bool value =
+          decltype(EqualityOperatorTest(std::declval<TLeft>(), std::declval<TRight>()))::value;
+    };
+
+    // Partial specialization when TLeft/TRight are both floating points.
+    // This is a work-around because decltype(floatvar1 == floatvar2)
+    // will not compile with clang:
+    // error: comparing floating point with == or != is unsafe [-Werror,-Wfloat-equal]
+    template <typename TLeft, typename TRight>
+    struct SupportsEqualityOperatorImpl<TLeft, TRight, true> {
+      static constexpr bool value = true;
+    };
+
+    // SupportsEqualityOperatorImpl<T1, T2>::value will evaluate to a boolean,
+    // whose value is true if T1 can be compared against T2 with ==,
+    // and false otherwise.
+    template <typename TLeft, typename TRight = TLeft>
+    struct SupportsEqualityOperator :
+        SupportsEqualityOperatorImpl<TLeft, TRight,
+                                     std::is_floating_point<TLeft>::value
+                                     && std::is_floating_point<TRight>::value> {
+    };
+
+    // Convert any kind of type to an std::string, even if there's no
+    // serialization support for it. Unknown types get converted to an
+    // an arbitrary value.
+    //
+    // Meant for printing user-visible errors or unit test failures only.
+    template <typename T>
+    std::string ToStringAny(const T& value,
+                            typename std::enable_if<
+                                SupportsInsertionOperator<T>::value>::type* = 0) {
+      std::stringstream stream;
+      stream << value;
+      return stream.str();
+    }
+
+    template <typename T>
+    std::string ToStringAny(const std::vector<T> value,
+                            typename std::enable_if<
+                                SupportsInsertionOperator<T>::value>::type* = 0) {
+      std::stringstream stream;
+      stream << "vector{";
+
+      for (size_t i = 0; i < value.size(); ++i) {
+        stream << ToStringAny(value[i]);
+
+        if (i != value.size() - 1) {
+          stream << ',';
+        }
+      }
+
+      stream << "}";
+      return stream.str();
+    }
+
+    template <typename T>
+    std::string ToStringAny(const T&,
+                            typename std::enable_if<
+                                !SupportsInsertionOperator<T>::value>::type* = 0
+    ) {
+      return std::string("(unknown type [no operator<< implemented] for )");
+    }
+  }  // namespace detail  // NOLINT [readability/namespace] [5]
+}  // namespace art
+
+#endif  // ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
diff --git a/cmdline/memory_representation.h b/cmdline/memory_representation.h
new file mode 100644
index 0000000..93387de
--- /dev/null
+++ b/cmdline/memory_representation.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_MEMORY_REPRESENTATION_H_
+#define ART_CMDLINE_MEMORY_REPRESENTATION_H_
+
+#include <string>
+#include <assert.h>
+#include <ostream>
+#include "utils.h"
+
+namespace art {
+
+// An integral representation of bytes of memory.
+// The underlying runtime size_t value is guaranteed to be a multiple of Divisor.
+template <size_t Divisor = 1024>
+struct Memory {
+  static_assert(IsPowerOfTwo(Divisor), "Divisor must be a power of 2");
+
+  static Memory<Divisor> FromBytes(size_t bytes) {
+    assert(bytes % Divisor == 0);
+    return Memory<Divisor>(bytes);
+  }
+
+  Memory() : Value(0u) {}
+  Memory(size_t value) : Value(value) {  // NOLINT [runtime/explicit] [5]
+    assert(value % Divisor == 0);
+  }
+  operator size_t() const { return Value; }
+
+  size_t ToBytes() const {
+    return Value;
+  }
+
+  static constexpr size_t kDivisor = Divisor;
+
+  static const char* Name() {
+    static std::string str;
+    if (str.empty()) {
+      str = "Memory<" + std::to_string(Divisor) + '>';
+    }
+
+    return str.c_str();
+  }
+
+  size_t Value;
+};
+
+template <size_t Divisor>
+std::ostream& operator<<(std::ostream& stream, Memory<Divisor> memory) {
+  return stream << memory.Value << '*' << Divisor;
+}
+
+using MemoryKiB = Memory<1024>;
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_MEMORY_REPRESENTATION_H_
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
new file mode 100644
index 0000000..50c54fe
--- /dev/null
+++ b/cmdline/token_range.h
@@ -0,0 +1,425 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_CMDLINE_TOKEN_RANGE_H_
+#define ART_CMDLINE_TOKEN_RANGE_H_
+
+#include <assert.h>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <memory>
+
+namespace art {
+// A range of tokens to make token matching algorithms easier.
+//
+// We try really hard to avoid copying and store only a pointer and iterators to the
+// interiors of the vector, so a typical copy constructor never ends up doing a deep copy.
+// It is up to the user to play nice and not to mutate the strings in-place.
+//
+// Tokens are only copied if a mutating operation is performed (and even then only
+// if it *actually* mutates the token).
+struct TokenRange {
+  // Short-hand for a vector of strings. A single string and a token is synonymous.
+  using TokenList = std::vector<std::string>;
+
+  // Copying-from-vector constructor.
+  explicit TokenRange(const TokenList& token_list)
+    : token_list_(new TokenList(token_list)),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Copying-from-iterator constructor
+  template <typename ForwardIterator>
+  explicit TokenRange(ForwardIterator it_begin, ForwardIterator it_end)
+    : token_list_(new TokenList(it_begin, it_end)),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+#if 0
+  // Copying-from-vector constructor.
+  TokenRange(const TokenList& token_list ATTRIBUTE_UNUSED,
+             TokenList::const_iterator it_begin,
+             TokenList::const_iterator it_end)
+    : token_list_(new TokenList(it_begin, it_end)),
+      begin_(token_list_->begin()),
+      end_(token_list_->end()) {
+    assert(it_begin >= token_list.begin());
+    assert(it_end <= token_list.end());
+  }
+#endif
+
+  // Copying from char array constructor, convertings into tokens (strings) along the way.
+  TokenRange(const char* token_list[], size_t length)
+    : token_list_(new TokenList(&token_list[0], &token_list[length])),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Non-copying move-from-vector constructor. Takes over the token vector.
+  explicit TokenRange(TokenList&& token_list)
+    : token_list_(new TokenList(std::forward<TokenList>(token_list))),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Non-copying constructor. Retain reference to existing list of tokens.
+  TokenRange(std::shared_ptr<TokenList> token_list,
+             TokenList::const_iterator it_begin,
+             TokenList::const_iterator it_end)
+    : token_list_(token_list),
+      begin_(it_begin),
+      end_(it_end) {
+    assert(it_begin >= token_list->begin());
+    assert(it_end <= token_list->end());
+  }
+
+  // Non-copying copy constructor.
+  TokenRange(const TokenRange& other) = default;
+
+  // Non-copying move constructor.
+  TokenRange(TokenRange&& other) = default;
+
+  // Non-copying constructor. Retains reference to an existing list of tokens, with offset.
+  explicit TokenRange(std::shared_ptr<TokenList> token_list)
+    : token_list_(token_list),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Iterator type for begin() and end(). Guaranteed to be a RandomAccessIterator.
+  using iterator = TokenList::const_iterator;
+
+  // Iterator type for const begin() and const end(). Guaranteed to be a RandomAccessIterator.
+  using const_iterator = iterator;
+
+  // Create a token range by splitting a string. Each separator gets their own token.
+  // Since the separator are retained as tokens, it might be useful to call
+  // RemoveToken afterwards.
+  static TokenRange Split(const std::string& string, std::initializer_list<char> separators) {
+    TokenList new_token_list;
+
+    std::string tok;
+    for (auto&& c : string) {
+      for (char sep : separators) {
+        if (c == sep) {
+          // We spotted a separator character.
+          // Push back everything before the last separator as a new token.
+          // Push back the separator as a token.
+          if (!tok.empty()) {
+            new_token_list.push_back(tok);
+            tok = "";
+          }
+          new_token_list.push_back(std::string() + sep);
+        } else {
+          // Build up the token with another character.
+          tok += c;
+        }
+      }
+    }
+
+    if (!tok.empty()) {
+      new_token_list.push_back(tok);
+    }
+
+    return TokenRange(std::move(new_token_list));
+  }
+
+  // A RandomAccessIterator to the first element in this range.
+  iterator begin() const {
+    return begin_;
+  }
+
+  // A RandomAccessIterator to one past the last element in this range.
+  iterator end() const {
+    return end_;
+  }
+
+  // The size of the range, i.e. how many tokens are in it.
+  size_t Size() const {
+    return std::distance(begin_, end_);
+  }
+
+  // Are there 0 tokens in this range?
+  bool IsEmpty() const {
+    return Size() > 0;
+  }
+
+  // Look up a token by it's offset.
+  const std::string& GetToken(size_t offset) const {
+    assert(offset < Size());
+    return *(begin_ + offset);
+  }
+
+  // Does this token range equal the other range?
+  // Equality is defined as having both the same size, and
+  // each corresponding token being equal.
+  bool operator==(const TokenRange& other) const {
+    if (this == &other) {
+      return true;
+    }
+
+    if (Size() != other.Size()) {
+      return false;
+    }
+
+    return std::equal(begin(), end(), other.begin());
+  }
+
+  // Look up the token at the requested index.
+  const std::string& operator[](int index) const {
+    assert(index >= 0 && static_cast<size_t>(index) < Size());
+    return *(begin() + index);
+  }
+
+  // Does this current range start with the other range?
+  bool StartsWith(const TokenRange& other) const {
+    if (this == &other) {
+      return true;
+    }
+
+    if (Size() < other.Size()) {
+      return false;
+    }
+
+    auto& smaller = Size() < other.Size() ? *this : other;
+    auto& greater = Size() < other.Size() ? other : *this;
+
+    return std::equal(smaller.begin(), smaller.end(), greater.begin());
+  }
+
+  // Remove all characters 'c' from each token, potentially copying the underlying tokens.
+  TokenRange RemoveCharacter(char c) const {
+    TokenList new_token_list(begin(), end());
+
+    bool changed = false;
+    for (auto&& token : new_token_list) {
+      auto it = std::remove_if(token.begin(), token.end(), [&](char ch) {
+        if (ch == c) {
+          changed = true;
+          return true;
+        }
+        return false;
+      });
+      token.erase(it, token.end());
+    }
+
+    if (!changed) {
+      return *this;
+    }
+
+    return TokenRange(std::move(new_token_list));
+  }
+
+  // Remove all tokens matching this one, potentially copying the underlying tokens.
+  TokenRange RemoveToken(const std::string& token) {
+    return RemoveIf([&](const std::string& tok) { return tok == token; });
+  }
+
+  // Discard all empty tokens, potentially copying the underlying tokens.
+  TokenRange DiscardEmpty() const {
+    return RemoveIf([](const std::string& token) { return token.empty(); });
+  }
+
+  // Create a non-copying subset of this range.
+  // Length is trimmed so that the Slice does not go out of range.
+  TokenRange Slice(size_t offset, size_t length = std::string::npos) const {
+    assert(offset < Size());
+
+    if (length != std::string::npos && offset + length > Size()) {
+      length = Size() - offset;
+    }
+
+    iterator it_end;
+    if (length == std::string::npos) {
+      it_end = end();
+    } else {
+      it_end = begin() + offset + length;
+    }
+
+    return TokenRange(token_list_, begin() + offset, it_end);
+  }
+
+  // Try to match the string with tokens from this range.
+  // Each token is used to match exactly once (after which the next token is used, and so on).
+  // The matching happens from left-to-right in a non-greedy fashion.
+  // If the currently-matched token is the wildcard, then the new outputted token will
+  // contain as much as possible until the next token is matched.
+  //
+  // For example, if this == ["a:", "_", "b:] and "_" is the match string, then
+  // MatchSubstrings on "a:foob:" will yield: ["a:", "foo", "b:"]
+  //
+  // Since the string matching can fail (e.g. ["foo"] against "bar"), then this
+  // function can fail, in which cause it will return null.
+  std::unique_ptr<TokenRange> MatchSubstrings(const std::string& string,
+                                              const std::string& wildcard) const {
+    TokenList new_token_list;
+
+    size_t wildcard_idx = std::string::npos;
+    size_t string_idx = 0;
+
+    // Function to push all the characters matched as a wildcard so far
+    // as a brand new token. It resets the wildcard matching.
+    // Empty wildcards are possible and ok, but only if wildcard matching was on.
+    auto maybe_push_wildcard_token = [&]() {
+      if (wildcard_idx != std::string::npos) {
+        size_t wildcard_length = string_idx - wildcard_idx;
+        std::string wildcard_substr = string.substr(wildcard_idx, wildcard_length);
+        new_token_list.push_back(std::move(wildcard_substr));
+
+        wildcard_idx = std::string::npos;
+      }
+    };
+
+    for (iterator it = begin(); it != end(); ++it) {
+      const std::string& tok = *it;
+
+      if (tok == wildcard) {
+        maybe_push_wildcard_token();
+        wildcard_idx = string_idx;
+        continue;
+      }
+
+      size_t next_token_idx = string.find(tok);
+      if (next_token_idx == std::string::npos) {
+        // Could not find token at all
+        return nullptr;
+      } else if (next_token_idx != string_idx && wildcard_idx == std::string::npos) {
+        // Found the token at a non-starting location, and we weren't
+        // trying to parse the wildcard.
+        return nullptr;
+      }
+
+      new_token_list.push_back(string.substr(next_token_idx, tok.size()));
+      maybe_push_wildcard_token();
+      string_idx += tok.size();
+    }
+
+    size_t remaining = string.size() - string_idx;
+    if (remaining > 0) {
+      if (wildcard_idx == std::string::npos) {
+        // Some characters were still remaining in the string,
+        // but it wasn't trying to match a wildcard.
+        return nullptr;
+      }
+    }
+
+    // If some characters are remaining, the rest must be a wildcard.
+    string_idx += remaining;
+    maybe_push_wildcard_token();
+
+    return std::unique_ptr<TokenRange>(new TokenRange(std::move(new_token_list)));
+  }
+
+  // Do a quick match token-by-token, and see if they match.
+  // Any tokens with a wildcard in them are only matched up until the wildcard.
+  // If this is true, then the wildcard matching later on can still fail, so this is not
+  // a guarantee that the argument is correct, it's more of a strong hint that the
+  // user-provided input *probably* was trying to match this argument.
+  //
+  // Returns how many tokens were either matched (or ignored because there was a
+  // wildcard present). 0 means no match. If the size() tokens are returned.
+  size_t MaybeMatches(const TokenRange& token_list, const std::string& wildcard) const {
+    auto token_it = token_list.begin();
+    auto token_end = token_list.end();
+    auto name_it = begin();
+    auto name_end = end();
+
+    size_t matched_tokens = 0;
+
+    while (token_it != token_end && name_it != name_end) {
+      // Skip token matching when the corresponding name has a wildcard in it.
+      const std::string& name = *name_it;
+
+      size_t wildcard_idx = name.find(wildcard);
+      if (wildcard_idx == std::string::npos) {  // No wildcard present
+        // Did the definition token match the user token?
+        if (name != *token_it) {
+          return matched_tokens;
+        }
+      } else {
+        std::string name_prefix = name.substr(0, wildcard_idx);
+
+        // Did the user token start with the up-to-the-wildcard prefix?
+        if (!StartsWith(*token_it, name_prefix)) {
+          return matched_tokens;
+        }
+      }
+
+      ++token_it;
+      ++name_it;
+      ++matched_tokens;
+    }
+
+    // If we got this far, it's either a full match or the token list was too short.
+    return matched_tokens;
+  }
+
+  // Flatten the token range by joining every adjacent token with the separator character.
+  // e.g. ["hello", "world"].join('$') == "hello$world"
+  std::string Join(char separator) const {
+    TokenList tmp(begin(), end());
+    return art::Join(tmp, separator);
+    // TODO: Join should probably take an offset or iterators
+  }
+
+ private:
+  static bool StartsWith(const std::string& larger, const std::string& smaller) {
+    if (larger.size() >= smaller.size()) {
+      return std::equal(smaller.begin(), smaller.end(), larger.begin());
+    }
+
+    return false;
+  }
+
+  template <typename TPredicate>
+  TokenRange RemoveIf(const TPredicate& predicate) const {
+    // If any of the tokens in the token lists are empty, then
+    // we need to remove them and compress the token list into a smaller one.
+    bool remove = false;
+    for (auto it = begin_; it != end_; ++it) {
+      auto&& token = *it;
+
+      if (predicate(token)) {
+        remove = true;
+        break;
+      }
+    }
+
+    // Actually copy the token list and remove the tokens that don't match our predicate.
+    if (remove) {
+      auto token_list = std::make_shared<TokenList>(begin(), end());
+      TokenList::iterator new_end =
+          std::remove_if(token_list->begin(), token_list->end(), predicate);
+      token_list->erase(new_end, token_list->end());
+
+      assert(token_list_->size() > token_list->size() && "Nothing was actually removed!");
+
+      return TokenRange(token_list);
+    }
+
+    return *this;
+  }
+
+  const std::shared_ptr<std::vector<std::string>> token_list_;
+  const iterator begin_;
+  const iterator end_;
+};
+}  // namespace art
+
+#endif  // ART_CMDLINE_TOKEN_RANGE_H_
diff --git a/cmdline/unit.h b/cmdline/unit.h
new file mode 100644
index 0000000..6b53b18
--- /dev/null
+++ b/cmdline/unit.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_UNIT_H_
+#define ART_CMDLINE_UNIT_H_
+
+namespace art {
+
+// Used for arguments that simply indicate presence (e.g. "-help") without any values.
+struct Unit {
+  // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors
+  // when running valgrind by specifying a user-defined constructor.
+  Unit() {}
+  ~Unit() {}
+  bool operator==(Unit) const {
+    return true;
+  }
+};
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_UNIT_H_
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 70c7e52..61379fb 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -60,7 +60,6 @@
 	dex/dex_to_dex_compiler.cc \
 	dex/bb_optimizations.cc \
 	dex/compiler_ir.cc \
-	dex/frontend.cc \
 	dex/mir_analysis.cc \
 	dex/mir_dataflow.cc \
 	dex/mir_field_info.cc \
@@ -70,12 +69,14 @@
 	dex/post_opt_passes.cc \
 	dex/pass_driver_me_opts.cc \
 	dex/pass_driver_me_post_opt.cc \
+	dex/pass_manager.cc \
 	dex/ssa_transformation.cc \
 	dex/verified_method.cc \
 	dex/verification_results.cc \
 	dex/vreg_analysis.cc \
 	dex/quick_compiler_callbacks.cc \
 	driver/compiler_driver.cc \
+	driver/compiler_options.cc \
 	driver/dex_compilation_unit.cc \
 	jni/quick/arm/calling_convention_arm.cc \
 	jni/quick/arm64/calling_convention_arm64.cc \
@@ -84,8 +85,8 @@
 	jni/quick/x86_64/calling_convention_x86_64.cc \
 	jni/quick/calling_convention.cc \
 	jni/quick/jni_compiler.cc \
-	llvm/llvm_compiler.cc \
 	optimizing/builder.cc \
+	optimizing/bounds_check_elimination.cc \
 	optimizing/code_generator.cc \
 	optimizing/code_generator_arm.cc \
 	optimizing/code_generator_arm64.cc \
@@ -96,7 +97,13 @@
 	optimizing/graph_checker.cc \
 	optimizing/graph_visualizer.cc \
 	optimizing/gvn.cc \
+	optimizing/inliner.cc \
 	optimizing/instruction_simplifier.cc \
+	optimizing/intrinsics.cc \
+	optimizing/intrinsics_arm.cc \
+	optimizing/intrinsics_arm64.cc \
+	optimizing/intrinsics_x86_64.cc \
+	optimizing/licm.cc \
 	optimizing/locations.cc \
 	optimizing/nodes.cc \
 	optimizing/optimization.cc \
@@ -104,10 +111,12 @@
 	optimizing/parallel_move_resolver.cc \
 	optimizing/prepare_for_register_allocation.cc \
 	optimizing/register_allocator.cc \
+	optimizing/side_effects_analysis.cc \
 	optimizing/ssa_builder.cc \
 	optimizing/ssa_liveness_analysis.cc \
 	optimizing/ssa_phi_elimination.cc \
-	optimizing/ssa_type_propagation.cc \
+	optimizing/primitive_type_propagation.cc \
+	optimizing/reference_type_propagation.cc \
 	trampolines/trampoline_compiler.cc \
 	utils/arena_allocator.cc \
 	utils/arena_bit_vector.cc \
@@ -121,11 +130,14 @@
 	utils/dwarf_cfi.cc \
 	utils/mips/assembler_mips.cc \
 	utils/mips/managed_register_mips.cc \
+	utils/mips64/assembler_mips64.cc \
+	utils/mips64/managed_register_mips64.cc \
 	utils/x86/assembler_x86.cc \
 	utils/x86/managed_register_x86.cc \
 	utils/x86_64/assembler_x86_64.cc \
 	utils/x86_64/managed_register_x86_64.cc \
 	utils/scoped_arena_allocator.cc \
+	utils/swap_space.cc \
 	buffered_output_stream.cc \
 	compiler.cc \
 	elf_writer.cc \
@@ -136,38 +148,8 @@
 	output_stream.cc \
 	vector_output_stream.cc
 
-ifeq ($(ART_SEA_IR_MODE),true)
-LIBART_COMPILER_SRC_FILES += \
-	sea_ir/frontend.cc \
-	sea_ir/ir/instruction_tools.cc \
-	sea_ir/ir/sea.cc \
-	sea_ir/code_gen/code_gen.cc \
-	sea_ir/code_gen/code_gen_data.cc \
-	sea_ir/types/type_inference.cc \
-	sea_ir/types/type_inference_visitor.cc \
-	sea_ir/debug/dot_gen.cc
-endif
-
 LIBART_COMPILER_CFLAGS :=
 
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-LIBART_COMPILER_SRC_FILES += \
-	dex/portable/mir_to_gbc.cc \
-	elf_writer_mclinker.cc \
-	jni/portable/jni_compiler.cc \
-	llvm/compiler_llvm.cc \
-	llvm/gbc_expander.cc \
-	llvm/generated/art_module.cc \
-	llvm/intrinsic_helper.cc \
-	llvm/ir_builder.cc \
-	llvm/llvm_compilation_unit.cc \
-	llvm/md_builder.cc \
-	llvm/runtime_support_builder.cc \
-	llvm/runtime_support_builder_arm.cc \
-	llvm/runtime_support_builder_x86.cc
-LIBART_COMPILER_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
-
 LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \
   dex/quick/arm/arm_lir.h \
   dex/quick/arm64/arm64_lir.h \
@@ -233,7 +215,6 @@
   LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN)
 
   LOCAL_CFLAGS := $$(LIBART_COMPILER_CFLAGS)
-  include external/libcxx/libcxx.mk
   ifeq ($$(art_target_or_host),target)
     $(call set-target-local-clang-vars)
     $(call set-target-local-cflags-vars,$(2))
@@ -248,28 +229,6 @@
     endif
   endif
 
-  ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-    LOCAL_SHARED_LIBRARIES += libLLVM
-    LOCAL_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-    ifeq ($$(art_target_or_host),target)
-      LOCAL_STATIC_LIBRARIES_arm += libmcldARMInfo libmcldARMTarget
-      LOCAL_STATIC_LIBRARIES_x86 += libmcldX86Info libmcldX86Target
-      LOCAL_STATIC_LIBRARIES_x86_64 += libmcldX86Info libmcldX86Target
-      LOCAL_STATIC_LIBRARIES_mips += libmcldMipsInfo libmcldMipsTarget
-      ifeq ($(TARGET_ARCH),arm64)
-         $$(info TODOAArch64: $$(LOCAL_PATH)/Android.mk Add Arm64 specific MCLinker libraries)
-      endif # TARGET_ARCH != arm64
-      include $(LLVM_DEVICE_BUILD_MK)
-    else # host
-      LOCAL_STATIC_LIBRARIES += libmcldARMInfo libmcldARMTarget
-      LOCAL_STATIC_LIBRARIES += libmcldX86Info libmcldX86Target
-      LOCAL_STATIC_LIBRARIES += libmcldMipsInfo libmcldMipsTarget
-      include $(LLVM_HOST_BUILD_MK)
-    endif
-    LOCAL_STATIC_LIBRARIES += libmcldCore libmcldObject libmcldADT libmcldFragment libmcldTarget libmcldCodeGen libmcldLDVariant libmcldMC libmcldSupport libmcldLD libmcldScript
-    include $(LLVM_GEN_INTRINSICS_MK)
-  endif
-
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
 
   ifeq ($$(art_target_or_host),host)
@@ -284,6 +243,9 @@
   else
     LOCAL_SHARED_LIBRARIES += libvixl
   endif
+
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
   ifeq ($$(art_target_or_host),target)
     # For atrace.
     LOCAL_SHARED_LIBRARIES += libcutils
@@ -322,18 +284,3 @@
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
   $(eval $(call build-libart-compiler,target,debug))
 endif
-
-# Rule to build /system/lib/libcompiler_rt.a
-# Usually static libraries are not installed on the device.
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-ifeq ($(ART_BUILD_TARGET),true)
-# TODO: Move to external/compiler_rt
-$(eval $(call copy-one-file, $(call intermediates-dir-for,STATIC_LIBRARIES,libcompiler_rt,,)/libcompiler_rt.a, $(TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a))
-ifdef TARGET_2ND_ARCH
-$(eval $(call copy-one-file, $(call intermediates-dir-for,STATIC_LIBRARIES,libcompiler_rt,,,t)/libcompiler_rt.a, $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a))
-endif
-
-$(DEX2OAT): $(TARGET_OUT_SHARED_LIBRARIES)/libcompiler_rt.a
-
-endif
-endif
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index a3d9a0b..1cd78f8 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -19,9 +19,10 @@
 #include "arch/instruction_set_features.h"
 #include "class_linker.h"
 #include "compiled_method.h"
+#include "dex/pass_manager.h"
 #include "dex/quick_compiler_callbacks.h"
-#include "dex/verification_results.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/verification_results.h"
 #include "driver/compiler_driver.h"
 #include "interpreter/interpreter.h"
 #include "mirror/art_method.h"
@@ -48,51 +49,52 @@
                                                             method->GetDexMethodIndex()));
   }
   if (compiled_method != nullptr) {
-    const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
-    const void* code_ptr;
-    bool is_portable = (code == nullptr);
-    if (!is_portable) {
-      uint32_t code_size = code->size();
-      CHECK_NE(0u, code_size);
-      const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
-      uint32_t vmap_table_offset = vmap_table.empty() ? 0u
-          : sizeof(OatQuickMethodHeader) + vmap_table.size();
-      const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
-      uint32_t mapping_table_offset = mapping_table.empty() ? 0u
-          : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size();
-      const std::vector<uint8_t>& gc_map = *compiled_method->GetGcMap();
-      uint32_t gc_map_offset = gc_map.empty() ? 0u
-          : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size() + gc_map.size();
-      OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
-                                         compiled_method->GetFrameSizeInBytes(),
-                                         compiled_method->GetCoreSpillMask(),
-                                         compiled_method->GetFpSpillMask(), code_size);
+    const SwapVector<uint8_t>* code = compiled_method->GetQuickCode();
+    uint32_t code_size = code->size();
+    CHECK_NE(0u, code_size);
+    const SwapVector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+    uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+        : sizeof(OatQuickMethodHeader) + vmap_table.size();
+    const SwapVector<uint8_t>* mapping_table = compiled_method->GetMappingTable();
+    bool mapping_table_used = mapping_table != nullptr && !mapping_table->empty();
+    size_t mapping_table_size = mapping_table_used ? mapping_table->size() : 0U;
+    uint32_t mapping_table_offset = !mapping_table_used ? 0u
+        : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size;
+    const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap();
+    bool gc_map_used = gc_map != nullptr && !gc_map->empty();
+    size_t gc_map_size = gc_map_used ? gc_map->size() : 0U;
+    uint32_t gc_map_offset = !gc_map_used ? 0u
+        : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size + gc_map_size;
+    OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
+                                       compiled_method->GetFrameSizeInBytes(),
+                                       compiled_method->GetCoreSpillMask(),
+                                       compiled_method->GetFpSpillMask(), code_size);
 
-      header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
-      std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
-      size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size() +
-          gc_map.size();
-      size_t code_offset = compiled_method->AlignCode(size - code_size);
-      size_t padding = code_offset - (size - code_size);
-      chunk->reserve(padding + size);
-      chunk->resize(sizeof(method_header));
-      memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
-      chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
-      chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
-      chunk->insert(chunk->begin(), gc_map.begin(), gc_map.end());
-      chunk->insert(chunk->begin(), padding, 0);
-      chunk->insert(chunk->end(), code->begin(), code->end());
-      CHECK_EQ(padding + size, chunk->size());
-      code_ptr = &(*chunk)[code_offset];
-    } else {
-      code = compiled_method->GetPortableCode();
-      code_ptr = &(*code)[0];
+    header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
+    std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
+    size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table_size +
+        gc_map_size;
+    size_t code_offset = compiled_method->AlignCode(size - code_size);
+    size_t padding = code_offset - (size - code_size);
+    chunk->reserve(padding + size);
+    chunk->resize(sizeof(method_header));
+    memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
+    chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
+    if (mapping_table_used) {
+      chunk->insert(chunk->begin(), mapping_table->begin(), mapping_table->end());
     }
+    if (gc_map_used) {
+      chunk->insert(chunk->begin(), gc_map->begin(), gc_map->end());
+    }
+    chunk->insert(chunk->begin(), padding, 0);
+    chunk->insert(chunk->end(), code->begin(), code->end());
+    CHECK_EQ(padding + size, chunk->size());
+    const void* code_ptr = &(*chunk)[code_offset];
     MakeExecutable(code_ptr, code->size());
     const void* method_code = CompiledMethod::CodePointer(code_ptr,
                                                           compiled_method->GetInstructionSet());
     LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
-    class_linker_->SetEntryPointsToCompiledCode(method, method_code, is_portable);
+    class_linker_->SetEntryPointsToCompiledCode(method, method_code);
   } else {
     // No code? You must mean to go into the interpreter.
     // Or the generic JNI...
@@ -155,7 +157,7 @@
     }
 
     // TODO: make selectable
-    Compiler::Kind compiler_kind = kUsePortableCompiler ? Compiler::kPortable : Compiler::kQuick;
+    Compiler::Kind compiler_kind = Compiler::kQuick;
     timer_.reset(new CumulativeLogger("Compilation times"));
     compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
                                               verification_results_.get(),
@@ -163,7 +165,7 @@
                                               compiler_kind, instruction_set,
                                               instruction_set_features_.get(),
                                               true, new std::set<std::string>, nullptr,
-                                              2, true, true, timer_.get(), ""));
+                                              2, true, true, "", timer_.get(), -1, ""));
   }
   // We typically don't generate an image in unit tests, disable this optimization by default.
   compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index 698bf3b..22be28c 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -20,35 +20,13 @@
 namespace art {
 
 CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-                           const std::vector<uint8_t>& quick_code)
+                           const ArrayRef<const uint8_t>& quick_code)
     : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
-      portable_code_(nullptr), quick_code_(nullptr) {
-  SetCode(&quick_code, nullptr);
+      quick_code_(nullptr) {
+  SetCode(&quick_code);
 }
 
-CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-                           const std::string& elf_object, const std::string& symbol)
-    : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
-      portable_code_(nullptr), quick_code_(nullptr), symbol_(symbol) {
-  CHECK_NE(elf_object.size(), 0U);
-  CHECK_NE(symbol.size(), 0U);
-  std::vector<uint8_t> temp_code(elf_object.size());
-  for (size_t i = 0; i < elf_object.size(); ++i) {
-    temp_code[i] = elf_object[i];
-  }
-  // TODO: we shouldn't just shove ELF objects in as "code" but
-  // change to have different kinds of compiled methods.  This is
-  // being deferred until we work on hybrid execution or at least
-  // until we work on batch compilation.
-  SetCode(nullptr, &temp_code);
-}
-
-void CompiledCode::SetCode(const std::vector<uint8_t>* quick_code,
-                           const std::vector<uint8_t>* portable_code) {
-  if (portable_code != nullptr) {
-    CHECK(!portable_code->empty());
-    portable_code_ = compiler_driver_->DeduplicateCode(*portable_code);
-  }
+void CompiledCode::SetCode(const ArrayRef<const uint8_t>* quick_code) {
   if (quick_code != nullptr) {
     CHECK(!quick_code->empty());
     quick_code_ = compiler_driver_->DeduplicateCode(*quick_code);
@@ -64,17 +42,8 @@
     } else {
       return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
     }
-  } else if (portable_code_ != nullptr) {
-    if (rhs.portable_code_ == nullptr) {
-      return false;
-    } else if (portable_code_->size() != rhs.portable_code_->size()) {
-      return false;
-    } else {
-      return std::equal(portable_code_->begin(), portable_code_->end(),
-                        rhs.portable_code_->begin());
-    }
   }
-  return (rhs.quick_code_ == nullptr) && (rhs.portable_code_ == nullptr);
+  return (rhs.quick_code_ == nullptr);
 }
 
 uint32_t CompiledCode::AlignCode(uint32_t offset) const {
@@ -94,6 +63,7 @@
     case kArm:
     case kArm64:
     case kMips:
+    case kMips64:
     case kX86:
     case kX86_64:
       return 0;
@@ -113,6 +83,7 @@
     case kArm:
     case kArm64:
     case kMips:
+    case kMips64:
     case kX86:
     case kX86_64:
       return code_pointer;
@@ -128,13 +99,8 @@
   }
 }
 
-const std::string& CompiledCode::GetSymbol() const {
-  CHECK_NE(0U, symbol_.size());
-  return symbol_;
-}
-
 const std::vector<uint32_t>& CompiledCode::GetOatdataOffsetsToCompliledCodeOffset() const {
-  CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size()) << symbol_;
+  CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size());
   return oatdata_offsets_to_compiled_code_offset_;
 }
 
@@ -144,90 +110,88 @@
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
                                InstructionSet instruction_set,
-                               const std::vector<uint8_t>& quick_code,
+                               const ArrayRef<const uint8_t>& quick_code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
                                const uint32_t fp_spill_mask,
-                               SrcMap* src_mapping_table,
-                               const std::vector<uint8_t>& mapping_table,
-                               const std::vector<uint8_t>& vmap_table,
-                               const std::vector<uint8_t>& native_gc_map,
-                               const std::vector<uint8_t>* cfi_info,
+                               DefaultSrcMap* src_mapping_table,
+                               const ArrayRef<const uint8_t>& mapping_table,
+                               const ArrayRef<const uint8_t>& vmap_table,
+                               const ArrayRef<const uint8_t>& native_gc_map,
+                               const ArrayRef<const uint8_t>& cfi_info,
                                const ArrayRef<LinkerPatch>& patches)
     : CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-      src_mapping_table_(driver->DeduplicateSrcMappingTable(src_mapping_table->Arrange())),
-      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+      src_mapping_table_(src_mapping_table == nullptr ?
+          driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>()) :
+          driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>(src_mapping_table->Arrange()))),
+      mapping_table_(mapping_table.data() == nullptr ?
+          nullptr : driver->DeduplicateMappingTable(mapping_table)),
       vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
-      gc_map_(driver->DeduplicateGCMap(native_gc_map)),
-      cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
-      patches_(patches.begin(), patches.end()) {
+      gc_map_(native_gc_map.data() == nullptr ? nullptr : driver->DeduplicateGCMap(native_gc_map)),
+      cfi_info_(cfi_info.data() == nullptr ? nullptr : driver->DeduplicateCFIInfo(cfi_info)),
+      patches_(patches.begin(), patches.end(), driver->GetSwapSpaceAllocator()) {
 }
 
-CompiledMethod::CompiledMethod(CompilerDriver* driver,
-                               InstructionSet instruction_set,
-                               const std::vector<uint8_t>& quick_code,
-                               const size_t frame_size_in_bytes,
-                               const uint32_t core_spill_mask,
-                               const uint32_t fp_spill_mask,
-                               const std::vector<uint8_t>& mapping_table,
-                               const std::vector<uint8_t>& stack_map)
-    : CompiledCode(driver, instruction_set, quick_code),
-      frame_size_in_bytes_(frame_size_in_bytes),
-      core_spill_mask_(core_spill_mask),
-      fp_spill_mask_(fp_spill_mask),
-      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
-      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
-      vmap_table_(driver->DeduplicateVMapTable(stack_map)),
-      gc_map_(nullptr),
-      cfi_info_(nullptr),
-      patches_() {
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
+    CompilerDriver* driver,
+    InstructionSet instruction_set,
+    const ArrayRef<const uint8_t>& quick_code,
+    const size_t frame_size_in_bytes,
+    const uint32_t core_spill_mask,
+    const uint32_t fp_spill_mask,
+    DefaultSrcMap* src_mapping_table,
+    const ArrayRef<const uint8_t>& mapping_table,
+    const ArrayRef<const uint8_t>& vmap_table,
+    const ArrayRef<const uint8_t>& native_gc_map,
+    const ArrayRef<const uint8_t>& cfi_info,
+    const ArrayRef<LinkerPatch>& patches) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask, src_mapping_table, mapping_table, vmap_table, native_gc_map,
+                  cfi_info, patches);
+  return ret;
 }
 
-CompiledMethod::CompiledMethod(CompilerDriver* driver,
-                               InstructionSet instruction_set,
-                               const std::vector<uint8_t>& code,
-                               const size_t frame_size_in_bytes,
-                               const uint32_t core_spill_mask,
-                               const uint32_t fp_spill_mask,
-                               const std::vector<uint8_t>* cfi_info)
-    : CompiledCode(driver, instruction_set, code),
-      frame_size_in_bytes_(frame_size_in_bytes),
-      core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
-      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
-      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
-      gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
-      cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
-      patches_() {
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethodStackMap(
+    CompilerDriver* driver,
+    InstructionSet instruction_set,
+    const ArrayRef<const uint8_t>& quick_code,
+    const size_t frame_size_in_bytes,
+    const uint32_t core_spill_mask,
+    const uint32_t fp_spill_mask,
+    const ArrayRef<const uint8_t>& stack_map) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask, nullptr, ArrayRef<const uint8_t>(), stack_map,
+                  ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), ArrayRef<LinkerPatch>());
+  return ret;
 }
 
-// Constructs a CompiledMethod for the Portable compiler.
-CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
-                               const std::string& code, const std::vector<uint8_t>& gc_map,
-                               const std::string& symbol)
-    : CompiledCode(driver, instruction_set, code, symbol),
-      frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0),
-      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
-      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
-      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
-      gc_map_(driver->DeduplicateGCMap(gc_map)),
-      cfi_info_(nullptr),
-      patches_() {
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethodCFI(
+    CompilerDriver* driver,
+    InstructionSet instruction_set,
+    const ArrayRef<const uint8_t>& quick_code,
+    const size_t frame_size_in_bytes,
+    const uint32_t core_spill_mask,
+    const uint32_t fp_spill_mask,
+    const ArrayRef<const uint8_t>& cfi_info) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask, nullptr, ArrayRef<const uint8_t>(),
+                  ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
+                  cfi_info, ArrayRef<LinkerPatch>());
+  return ret;
 }
 
-CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
-                               const std::string& code, const std::string& symbol)
-    : CompiledCode(driver, instruction_set, code, symbol),
-      frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0),
-      src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
-      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
-      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
-      gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
-      cfi_info_(nullptr),
-      patches_() {
+
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  alloc.destroy(m);
+  alloc.deallocate(m, 1);
 }
 
 }  // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 7f76eef..6013507 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -25,6 +25,7 @@
 #include "method_reference.h"
 #include "utils.h"
 #include "utils/array_ref.h"
+#include "utils/swap_space.h"
 
 namespace llvm {
   class Function;
@@ -38,25 +39,17 @@
  public:
   // For Quick to supply an code blob
   CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-               const std::vector<uint8_t>& quick_code);
-
-  // For Portable to supply an ELF object
-  CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-               const std::string& elf_object, const std::string &symbol);
+               const ArrayRef<const uint8_t>& quick_code);
 
   InstructionSet GetInstructionSet() const {
     return instruction_set_;
   }
 
-  const std::vector<uint8_t>* GetPortableCode() const {
-    return portable_code_;
-  }
-
-  const std::vector<uint8_t>* GetQuickCode() const {
+  const SwapVector<uint8_t>* GetQuickCode() const {
     return quick_code_;
   }
 
-  void SetCode(const std::vector<uint8_t>* quick_code, const std::vector<uint8_t>* portable_code);
+  void SetCode(const ArrayRef<const uint8_t>* quick_code);
 
   bool operator==(const CompiledCode& rhs) const;
 
@@ -77,7 +70,6 @@
   static const void* CodePointer(const void* code_pointer,
                                  InstructionSet instruction_set);
 
-  const std::string& GetSymbol() const;
   const std::vector<uint32_t>& GetOatdataOffsetsToCompliledCodeOffset() const;
   void AddOatdataOffsetToCompliledCodeOffset(uint32_t offset);
 
@@ -86,14 +78,8 @@
 
   const InstructionSet instruction_set_;
 
-  // The ELF image for portable.
-  std::vector<uint8_t>* portable_code_;
-
   // Used to store the PIC code for Quick.
-  std::vector<uint8_t>* quick_code_;
-
-  // Used for the Portable ELF symbol name.
-  const std::string symbol_;
+  SwapVector<uint8_t>* quick_code_;
 
   // There are offsets from the oatdata symbol to where the offset to
   // the compiled method will be found. These are computed by the
@@ -124,8 +110,23 @@
   }
 };
 
-class SrcMap FINAL : public std::vector<SrcMapElem> {
+template <class Allocator>
+class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> {
  public:
+  using std::vector<SrcMapElem, Allocator>::begin;
+  using typename std::vector<SrcMapElem, Allocator>::const_iterator;
+  using std::vector<SrcMapElem, Allocator>::empty;
+  using std::vector<SrcMapElem, Allocator>::end;
+  using std::vector<SrcMapElem, Allocator>::resize;
+  using std::vector<SrcMapElem, Allocator>::shrink_to_fit;
+  using std::vector<SrcMapElem, Allocator>::size;
+
+  explicit SrcMap() {}
+
+  template <class InputIt>
+  SrcMap(InputIt first, InputIt last, const Allocator& alloc)
+      : std::vector<SrcMapElem, Allocator>(first, last, alloc) {}
+
   void SortByFrom() {
     std::sort(begin(), end(), [] (const SrcMapElem& lhs, const SrcMapElem& rhs) -> bool {
       return lhs.from_ < rhs.from_;
@@ -173,6 +174,10 @@
   }
 };
 
+using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>;
+using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
+
+
 enum LinkerPatchType {
   kLinkerPatchMethod,
   kLinkerPatchCall,
@@ -270,49 +275,57 @@
 
 class CompiledMethod FINAL : public CompiledCode {
  public:
-  // Constructs a CompiledMethod for Quick.
+  // Constructs a CompiledMethod.
+  // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
+  //       in the swap space.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
+                 const ArrayRef<const uint8_t>& quick_code,
                  const size_t frame_size_in_bytes,
                  const uint32_t core_spill_mask,
                  const uint32_t fp_spill_mask,
-                 SrcMap* src_mapping_table,
-                 const std::vector<uint8_t>& mapping_table,
-                 const std::vector<uint8_t>& vmap_table,
-                 const std::vector<uint8_t>& native_gc_map,
-                 const std::vector<uint8_t>* cfi_info,
+                 DefaultSrcMap* src_mapping_table,
+                 const ArrayRef<const uint8_t>& mapping_table,
+                 const ArrayRef<const uint8_t>& vmap_table,
+                 const ArrayRef<const uint8_t>& native_gc_map,
+                 const ArrayRef<const uint8_t>& cfi_info,
                  const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
 
-  // Constructs a CompiledMethod for Optimizing.
-  CompiledMethod(CompilerDriver* driver,
-                 InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
-                 const size_t frame_size_in_bytes,
-                 const uint32_t core_spill_mask,
-                 const uint32_t fp_spill_mask,
-                 const std::vector<uint8_t>& mapping_table,
-                 const std::vector<uint8_t>& vmap_table);
-
-  // Constructs a CompiledMethod for the QuickJniCompiler.
-  CompiledMethod(CompilerDriver* driver,
-                 InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
-                 const size_t frame_size_in_bytes,
-                 const uint32_t core_spill_mask,
-                 const uint32_t fp_spill_mask,
-                 const std::vector<uint8_t>* cfi_info);
-
-  // Constructs a CompiledMethod for the Portable compiler.
-  CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
-                 const std::vector<uint8_t>& gc_map, const std::string& symbol);
-
-  // Constructs a CompiledMethod for the Portable JniCompiler.
-  CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
-                 const std::string& symbol);
-
   ~CompiledMethod() {}
 
+  static CompiledMethod* SwapAllocCompiledMethod(
+      CompilerDriver* driver,
+      InstructionSet instruction_set,
+      const ArrayRef<const uint8_t>& quick_code,
+      const size_t frame_size_in_bytes,
+      const uint32_t core_spill_mask,
+      const uint32_t fp_spill_mask,
+      DefaultSrcMap* src_mapping_table,
+      const ArrayRef<const uint8_t>& mapping_table,
+      const ArrayRef<const uint8_t>& vmap_table,
+      const ArrayRef<const uint8_t>& native_gc_map,
+      const ArrayRef<const uint8_t>& cfi_info,
+      const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
+
+  static CompiledMethod* SwapAllocCompiledMethodStackMap(
+      CompilerDriver* driver,
+      InstructionSet instruction_set,
+      const ArrayRef<const uint8_t>& quick_code,
+      const size_t frame_size_in_bytes,
+      const uint32_t core_spill_mask,
+      const uint32_t fp_spill_mask,
+      const ArrayRef<const uint8_t>& stack_map);
+
+  static CompiledMethod* SwapAllocCompiledMethodCFI(CompilerDriver* driver,
+                                                    InstructionSet instruction_set,
+                                                    const ArrayRef<const uint8_t>& quick_code,
+                                                    const size_t frame_size_in_bytes,
+                                                    const uint32_t core_spill_mask,
+                                                    const uint32_t fp_spill_mask,
+                                                    const ArrayRef<const uint8_t>& cfi_info);
+
+  static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m);
+
   size_t GetFrameSizeInBytes() const {
     return frame_size_in_bytes_;
   }
@@ -325,30 +338,29 @@
     return fp_spill_mask_;
   }
 
-  const SrcMap& GetSrcMappingTable() const {
+  const SwapSrcMap& GetSrcMappingTable() const {
     DCHECK(src_mapping_table_ != nullptr);
     return *src_mapping_table_;
   }
 
-  const std::vector<uint8_t>& GetMappingTable() const {
-    DCHECK(mapping_table_ != nullptr);
-    return *mapping_table_;
+  SwapVector<uint8_t> const* GetMappingTable() const {
+    return mapping_table_;
   }
 
-  const std::vector<uint8_t>& GetVmapTable() const {
+  const SwapVector<uint8_t>& GetVmapTable() const {
     DCHECK(vmap_table_ != nullptr);
     return *vmap_table_;
   }
 
-  std::vector<uint8_t> const* GetGcMap() const {
+  SwapVector<uint8_t> const* GetGcMap() const {
     return gc_map_;
   }
 
-  const std::vector<uint8_t>* GetCFIInfo() const {
+  const SwapVector<uint8_t>* GetCFIInfo() const {
     return cfi_info_;
   }
 
-  const std::vector<LinkerPatch>& GetPatches() const {
+  const SwapVector<LinkerPatch>& GetPatches() const {
     return patches_;
   }
 
@@ -360,19 +372,19 @@
   // For quick code, a bit mask describing spilled FPR callee-save registers.
   const uint32_t fp_spill_mask_;
   // For quick code, a set of pairs (PC, Line) mapping from native PC offset to Java line
-  SrcMap* src_mapping_table_;
+  SwapSrcMap* src_mapping_table_;
   // For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
   // native PC offset. Size prefixed.
-  std::vector<uint8_t>* mapping_table_;
+  SwapVector<uint8_t>* mapping_table_;
   // For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed.
-  std::vector<uint8_t>* vmap_table_;
+  SwapVector<uint8_t>* vmap_table_;
   // For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers
-  // are live. For portable code, the key is a dalvik PC.
-  std::vector<uint8_t>* gc_map_;
+  // are live.
+  SwapVector<uint8_t>* gc_map_;
   // For quick code, a FDE entry for the debug_frame section.
-  std::vector<uint8_t>* cfi_info_;
+  SwapVector<uint8_t>* cfi_info_;
   // For quick code, linker patches needed by the method.
-  std::vector<LinkerPatch> patches_;
+  SwapVector<LinkerPatch> patches_;
 };
 
 }  // namespace art
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index b9fcf5b..5e8ec1e 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -17,57 +17,12 @@
 #include "compiler.h"
 
 #include "base/logging.h"
-#include "dex/quick/quick_compiler.h"
+#include "dex/quick/quick_compiler_factory.h"
 #include "driver/compiler_driver.h"
-#include "llvm/llvm_compiler.h"
 #include "optimizing/optimizing_compiler.h"
 
 namespace art {
 
-#ifdef ART_SEA_IR_MODE
-constexpr bool kCanUseSeaIR = true;
-#else
-constexpr bool kCanUseSeaIR = false;
-#endif
-
-extern "C" art::CompiledMethod* SeaIrCompileMethod(const art::DexFile::CodeItem* code_item ATTRIBUTE_UNUSED,
-                                                   uint32_t access_flags ATTRIBUTE_UNUSED,
-                                                   art::InvokeType invoke_type ATTRIBUTE_UNUSED,
-                                                   uint16_t class_def_idx ATTRIBUTE_UNUSED,
-                                                   uint32_t method_idx ATTRIBUTE_UNUSED,
-                                                   jobject class_loader ATTRIBUTE_UNUSED,
-                                                   const art::DexFile& dex_file ATTRIBUTE_UNUSED)
-#ifdef ART_SEA_IR_MODE
-;   // NOLINT(whitespace/semicolon)
-#else
-{
-  UNREACHABLE();
-}
-#endif
-
-
-CompiledMethod* Compiler::TryCompileWithSeaIR(const art::DexFile::CodeItem* code_item,
-                                              uint32_t access_flags,
-                                              art::InvokeType invoke_type,
-                                              uint16_t class_def_idx,
-                                              uint32_t method_idx,
-                                              jobject class_loader,
-                                              const art::DexFile& dex_file) {
-  bool use_sea = kCanUseSeaIR &&
-      (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci"));
-  if (use_sea) {
-    LOG(INFO) << "Using SEA IR to compile..." << std::endl;
-    return SeaIrCompileMethod(code_item,
-                              access_flags,
-                              invoke_type,
-                              class_def_idx,
-                              method_idx,
-                              class_loader,
-                              dex_file);
-  }
-  return nullptr;
-}
-
 Compiler* Compiler::Create(CompilerDriver* driver, Compiler::Kind kind) {
   switch (kind) {
     case kQuick:
@@ -76,13 +31,6 @@
     case kOptimizing:
       return CreateOptimizingCompiler(driver);
 
-    case kPortable:
-      {
-        Compiler* compiler = CreateLLVMCompiler(driver);
-        CHECK(compiler != nullptr) << "Portable compiler not compiled";
-        return compiler;
-      }
-
     default:
       LOG(FATAL) << "UNREACHABLE";
       UNREACHABLE();
diff --git a/compiler/compiler.h b/compiler/compiler.h
index c2c15ff..6ec39f9 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -32,24 +32,16 @@
   class ArtMethod;
 }
 
-// Base class for compiler-specific thread-local storage for compiler worker threads
-class CompilerTls {
-  public:
-    CompilerTls() {}
-    ~CompilerTls() {}
-};
-
 class Compiler {
  public:
   enum Kind {
     kQuick,
-    kOptimizing,
-    kPortable
+    kOptimizing
   };
 
   static Compiler* Create(CompilerDriver* driver, Kind kind);
 
-  virtual void Init() const = 0;
+  virtual void Init() = 0;
 
   virtual void UnInit() const = 0;
 
@@ -64,14 +56,6 @@
                                   jobject class_loader,
                                   const DexFile& dex_file) const = 0;
 
-  static CompiledMethod* TryCompileWithSeaIR(const art::DexFile::CodeItem* code_item,
-                                             uint32_t access_flags,
-                                             art::InvokeType invoke_type,
-                                             uint16_t class_def_idx,
-                                             uint32_t method_idx,
-                                             jobject class_loader,
-                                             const art::DexFile& dex_file);
-
   virtual CompiledMethod* JniCompile(uint32_t access_flags,
                                      uint32_t method_idx,
                                      const DexFile& dex_file) const = 0;
@@ -86,21 +70,10 @@
                         bool is_host) const
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
 
-  virtual Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const = 0;
-
   uint64_t GetMaximumCompilationTimeBeforeWarning() const {
     return maximum_compilation_time_before_warning_;
   }
 
-  virtual bool IsPortable() const {
-    return false;
-  }
-
-  void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
-    UNUSED(driver);
-    UNUSED(filename);
-  }
-
   virtual void InitCompilationUnit(CompilationUnit& cu) const = 0;
 
   virtual ~Compiler() {}
@@ -119,10 +92,6 @@
     return nullptr;
   }
 
-  virtual CompilerTls* CreateNewCompilerTls() {
-    return nullptr;
-  }
-
   // Returns whether the method to compile is such a pathological case that
   // it's not worth compiling.
   static bool IsPathologicalCase(const DexFile::CodeItem& code_item,
diff --git a/compiler/dex/backend.h b/compiler/dex/backend.h
deleted file mode 100644
index 9cad933..0000000
--- a/compiler/dex/backend.h
+++ /dev/null
@@ -1,61 +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.
- */
-
-#ifndef ART_COMPILER_DEX_BACKEND_H_
-#define ART_COMPILER_DEX_BACKEND_H_
-
-namespace art {
-
-class ArenaAllocator;
-class CompiledMethod;
-
-class Backend {
-  public:
-    virtual ~Backend() {}
-    virtual void Materialize() = 0;
-    virtual CompiledMethod* GetCompiledMethod() = 0;
-
-    // Queries for backend support for vectors
-    /*
-     * Return the number of bits in a vector register.
-     * @return 0 if vector registers are not supported, or the
-     * number of bits in the vector register if supported.
-     */
-    virtual int VectorRegisterSize() { return 0; }
-
-    /*
-     * Return the number of reservable vector registers supported
-     * @param long_or_fp, true if floating point computations will be
-     * executed or the operations will be long type while vector
-     * registers are reserved.
-     * @return the number of vector registers that are available
-     * @note The backend should ensure that sufficient vector registers
-     * are held back to generate scalar code without exhausting vector
-     * registers, if scalar code also uses the vector registers.
-     */
-    virtual int NumReservableVectorRegisters(bool long_or_fp) {
-      UNUSED(long_or_fp);
-      return 0;
-    }
-
-  protected:
-    explicit Backend(ArenaAllocator* arena) : arena_(arena) {}
-    ArenaAllocator* const arena_;
-};  // Class Backend
-
-}  // namespace art
-
-#endif  // ART_COMPILER_DEX_BACKEND_H_
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index 6a610ab..11a7e44 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -52,19 +52,31 @@
 }
 
 /*
- * BasicBlock Optimization pass implementation start.
+ * MethodUseCount pass implementation start.
  */
-void BBOptimizations::Start(PassDataHolder* data) const {
+bool MethodUseCount::Gate(const PassDataHolder* data) const {
   DCHECK(data != nullptr);
-  CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+  CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
   DCHECK(c_unit != nullptr);
-  /*
-   * This pass has a different ordering depEnding on the suppress exception,
-   * so do the pass here for now:
-   *   - Later, the Start should just change the ordering and we can move the extended
-   *     creation into the pass driver's main job with a new iterator
-   */
-  c_unit->mir_graph->BasicBlockOptimization();
+  // First initialize the data.
+  c_unit->mir_graph->InitializeMethodUses();
+
+  // Now check if the pass is to be ignored.
+  bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
+
+  return res;
+}
+
+bool MethodUseCount::Worker(PassDataHolder* data) const {
+  DCHECK(data != nullptr);
+  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+  CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+  DCHECK(c_unit != nullptr);
+  BasicBlock* bb = pass_me_data_holder->bb;
+  DCHECK(bb != nullptr);
+  c_unit->mir_graph->CountUses(bb);
+  // No need of repeating, so just return false.
+  return false;
 }
 
 }  // namespace art
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index 764a4cf..7685200 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -18,8 +18,10 @@
 #define ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_
 
 #include "base/casts.h"
-#include "compiler_internals.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
 #include "pass_me.h"
+#include "mir_graph.h"
 
 namespace art {
 
@@ -171,27 +173,6 @@
   }
 };
 
-/**
- * @class TypeInference
- * @brief Type inference pass.
- */
-class TypeInference : public PassME {
- public:
-  TypeInference()
-    : PassME("TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_type_cfg") {
-  }
-
-  bool Worker(PassDataHolder* data) const {
-    DCHECK(data != nullptr);
-    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
-    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
-    DCHECK(c_unit != nullptr);
-    BasicBlock* bb = pass_me_data_holder->bb;
-    DCHECK(bb != nullptr);
-    return c_unit->mir_graph->InferTypes(bb);
-  }
-};
-
 class ClassInitCheckElimination : public PassME {
  public:
   ClassInitCheckElimination()
@@ -279,12 +260,55 @@
 };
 
 /**
+ * @class ConstantPropagation
+ * @brief Perform a constant propagation pass.
+ */
+class ConstantPropagation : public PassME {
+ public:
+  ConstantPropagation() : PassME("ConstantPropagation") {
+  }
+
+  void Start(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->InitializeConstantPropagation();
+  }
+
+  bool Worker(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+    DCHECK(bb != nullptr);
+    c_unit->mir_graph->DoConstantPropagation(bb);
+    // No need of repeating, so just return false.
+    return false;
+  }
+};
+
+/**
+ * @class MethodUseCount
+ * @brief Count the register uses of the method
+ */
+class MethodUseCount : public PassME {
+ public:
+  MethodUseCount() : PassME("UseCount") {
+  }
+
+  bool Worker(PassDataHolder* data) const;
+
+  bool Gate(const PassDataHolder* data) const;
+};
+
+/**
  * @class BasicBlock Optimizations
  * @brief Any simple BasicBlock optimization can be put here.
  */
 class BBOptimizations : public PassME {
  public:
-  BBOptimizations() : PassME("BBOptimizations", kNoNodes, "5_post_bbo_cfg") {
+  BBOptimizations()
+      : PassME("BBOptimizations", kNoNodes, kOptimizationBasicBlockChange, "5_post_bbo_cfg") {
   }
 
   bool Gate(const PassDataHolder* data) const {
@@ -294,7 +318,63 @@
     return ((c_unit->disable_opt & (1 << kBBOpt)) == 0);
   }
 
-  void Start(PassDataHolder* data) const;
+  void Start(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->BasicBlockOptimizationStart();
+
+    /*
+     * This pass has a different ordering depending on the suppress exception,
+     * so do the pass here for now:
+     *   - Later, the Start should just change the ordering and we can move the extended
+     *     creation into the pass driver's main job with a new iterator
+     */
+    c_unit->mir_graph->BasicBlockOptimization();
+  }
+
+  void End(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->BasicBlockOptimizationEnd();
+    down_cast<PassMEDataHolder*>(data)->dirty = !c_unit->mir_graph->DfsOrdersUpToDate();
+  }
+};
+
+/**
+ * @class SuspendCheckElimination
+ * @brief Any simple BasicBlock optimization can be put here.
+ */
+class SuspendCheckElimination : public PassME {
+ public:
+  SuspendCheckElimination()
+    : PassME("SuspendCheckElimination", kTopologicalSortTraversal, "6_post_sce_cfg") {
+  }
+
+  bool Gate(const PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return c_unit->mir_graph->EliminateSuspendChecksGate();
+  }
+
+  bool Worker(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+    DCHECK(c_unit != nullptr);
+    BasicBlock* bb = pass_me_data_holder->bb;
+    DCHECK(bb != nullptr);
+    return c_unit->mir_graph->EliminateSuspendChecks(bb);
+  }
+
+  void End(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->EliminateSuspendChecksEnd();
+  }
 };
 
 }  // namespace art
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 3b3170e..7edb490 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -311,6 +311,34 @@
   // arg[0]: TypeSize (most other vector opcodes have this in vC)
   kMirOpPackedArrayPut,
 
+  // @brief Multiply-add integer.
+  // vA: destination
+  // vB: multiplicand
+  // vC: multiplier
+  // arg[0]: addend
+  kMirOpMaddInt,
+
+  // @brief Multiply-subtract integer.
+  // vA: destination
+  // vB: multiplicand
+  // vC: multiplier
+  // arg[0]: minuend
+  kMirOpMsubInt,
+
+  // @brief Multiply-add long.
+  // vA: destination
+  // vB: multiplicand
+  // vC: multiplier
+  // arg[0]: addend
+  kMirOpMaddLong,
+
+  // @brief Multiply-subtract long.
+  // vA: destination
+  // vB: multiplicand
+  // vC: multiplier
+  // arg[0]: minuend
+  kMirOpMsubLong,
+
   kMirOpLast,
 };
 
@@ -527,7 +555,7 @@
  * The current recipe is as follows:
  * -# Use AnyStore ~= (LoadStore | StoreStore) ~= release barrier before volatile store.
  * -# Use AnyAny barrier after volatile store.  (StoreLoad is as expensive.)
- * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrierafter each volatile load.
+ * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrier after each volatile load.
  * -# Use StoreStore barrier after all stores but before return from any constructor whose
  *    class has final fields.
  * -# Use NTStoreStore to order non-temporal stores with respect to all later
@@ -606,7 +634,7 @@
 };
 std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind);
 
-// LIR fixup kinds for Arm
+// LIR fixup kinds for Arm and X86.
 enum FixupKind {
   kFixupNone,
   kFixupLabel,             // For labels we just adjust the offset.
@@ -624,6 +652,7 @@
   kFixupMovImmHST,         // kThumb2MovImm16HST.
   kFixupAlign4,            // Align to 4-byte boundary.
   kFixupA53Erratum835769,  // Cortex A53 Erratum 835769.
+  kFixupSwitchTable,       // X86_64 packed switch table.
 };
 std::ostream& operator<<(std::ostream& os, const FixupKind& kind);
 
diff --git a/compiler/dex/compiler_internals.h b/compiler/dex/compiler_internals.h
deleted file mode 100644
index 2019f0b..0000000
--- a/compiler/dex/compiler_internals.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DEX_COMPILER_INTERNALS_H_
-#define ART_COMPILER_DEX_COMPILER_INTERNALS_H_
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include "base/logging.h"
-#include "mir_graph.h"
-#include "compiler_ir.h"
-#include "frontend.h"  // Debug flags.
-#include "utils.h"
-
-#endif  // ART_COMPILER_DEX_COMPILER_INTERNALS_H_
diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc
index a2b3fe4..7fc1b03 100644
--- a/compiler/dex/compiler_ir.cc
+++ b/compiler/dex/compiler_ir.cc
@@ -16,16 +16,19 @@
 
 #include "compiler_ir.h"
 
+#include "arch/instruction_set_features.h"
 #include "base/dumpable.h"
-#include "backend.h"
-#include "frontend.h"
+#include "dex_flags.h"
+#include "dex/quick/mir_to_lir.h"
+#include "driver/compiler_driver.h"
 #include "mir_graph.h"
 
 namespace art {
 
-CompilationUnit::CompilationUnit(ArenaPool* pool)
-  : compiler_driver(nullptr),
-    class_linker(nullptr),
+CompilationUnit::CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver,
+                                 ClassLinker* linker)
+  : compiler_driver(driver),
+    class_linker(linker),
     dex_file(nullptr),
     class_loader(nullptr),
     class_def_idx(0),
@@ -36,10 +39,8 @@
     disable_opt(0),
     enable_debug(0),
     verbose(false),
-    compiler(nullptr),
-    instruction_set(kNone),
-    target64(false),
-    compiler_flip_match(false),
+    instruction_set(isa),
+    target64(Is64BitInstructionSet(isa)),
     arena(pool),
     arena_stack(pool),
     mir_graph(nullptr),
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 34585c1..0c46d43 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -17,31 +17,148 @@
 #ifndef ART_COMPILER_DEX_COMPILER_IR_H_
 #define ART_COMPILER_DEX_COMPILER_IR_H_
 
+#include "jni.h"
 #include <string>
 #include <vector>
 
-#include "compiler_enums.h"
-#include "driver/compiler_driver.h"
-#include "utils/scoped_arena_allocator.h"
 #include "base/timing_logger.h"
+#include "invoke_type.h"
+#include "safe_map.h"
 #include "utils/arena_allocator.h"
+#include "utils/scoped_arena_allocator.h"
 
 namespace art {
 
-class Backend;
 class ClassLinker;
+class CompilerDriver;
+class Mir2Lir;
 class MIRGraph;
 
-/*
- * TODO: refactoring pass to move these (and other) typedefs towards usage style of runtime to
- * add type safety (see runtime/offsets.h).
+constexpr size_t kOptionStringMaxLength = 2048;
+
+/**
+ * Structure abstracting pass option values, which can be of type string or integer.
  */
-typedef uint32_t DexOffset;          // Dex offset in code units.
-typedef uint16_t NarrowDexOffset;    // For use in structs, Dex offsets range from 0 .. 0xffff.
-typedef uint32_t CodeOffset;         // Native code offset in bytes.
+struct OptionContent {
+  OptionContent(const OptionContent& option) :
+    type(option.type), container(option.container, option.type) {}
+
+  explicit OptionContent(const char* value) :
+    type(kString), container(value) {}
+
+  explicit OptionContent(int value) :
+    type(kInteger), container(value) {}
+
+  explicit OptionContent(int64_t value) :
+    type(kInteger), container(value) {}
+
+  ~OptionContent() {
+    if (type == kString) {
+      container.StringDelete();
+    }
+  }
+
+  /**
+   * Allows for a transparent display of the option content.
+   */
+  friend std::ostream& operator<<(std::ostream& out, const OptionContent& option) {
+    if (option.type == kString) {
+      out << option.container.s;
+    } else {
+      out << option.container.i;
+    }
+
+    return out;
+  }
+
+  inline const char* GetString() const {
+    return container.s;
+  }
+
+  inline int64_t GetInteger() const {
+    return container.i;
+  }
+
+  /**
+   * @brief Used to compare a string option value to a given @p value.
+   * @details Will return whether the internal string option is equal to
+   * the parameter @p value. It will return false if the type of the
+   * object is not a string.
+   * @param value The string to compare to.
+   * @return Returns whether the internal string option is equal to the
+   * parameter @p value.
+  */
+  inline bool Equals(const char* value) const {
+    DCHECK(value != nullptr);
+    if (type != kString) {
+      return false;
+    }
+    return !strncmp(container.s, value, kOptionStringMaxLength);
+  }
+
+  /**
+   * @brief Used to compare an integer option value to a given @p value.
+   * @details Will return whether the internal integer option is equal to
+   * the parameter @p value. It will return false if the type of the
+   * object is not an integer.
+   * @param value The integer to compare to.
+   * @return Returns whether the internal integer option is equal to the
+   * parameter @p value.
+  */
+  inline bool Equals(int64_t value) const {
+    if (type != kInteger) {
+      return false;
+    }
+    return container.i == value;
+  }
+
+  /**
+   * Describes the type of parameters allowed as option values.
+   */
+  enum OptionType {
+    kString = 0,
+    kInteger
+  };
+
+  OptionType type;
+
+ private:
+  /**
+   * Union containing the option value of either type.
+   */
+  union OptionContainer {
+    explicit OptionContainer(const OptionContainer& c, OptionType t) {
+      if (t == kString) {
+        DCHECK(c.s != nullptr);
+        s = strndup(c.s, kOptionStringMaxLength);
+      } else {
+        i = c.i;
+      }
+    }
+
+    explicit OptionContainer(const char* value) {
+      DCHECK(value != nullptr);
+      s = strndup(value, kOptionStringMaxLength);
+    }
+
+    explicit OptionContainer(int64_t value) : i(value) {}
+    ~OptionContainer() {}
+
+    void StringDelete() {
+      if (s != nullptr) {
+        free(s);
+      }
+    }
+
+    char* s;
+    int64_t i;
+  };
+
+  OptionContainer container;
+};
 
 struct CompilationUnit {
-  explicit CompilationUnit(ArenaPool* pool);
+  CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, ClassLinker* linker);
   ~CompilationUnit();
 
   void StartTimingSplit(const char* label);
@@ -52,37 +169,27 @@
    * Fields needed/generated by common frontend and generally used throughout
    * the compiler.
   */
-  CompilerDriver* compiler_driver;
-  ClassLinker* class_linker;           // Linker to resolve fields and methods.
-  const DexFile* dex_file;             // DexFile containing the method being compiled.
-  jobject class_loader;                // compiling method's class loader.
-  uint16_t class_def_idx;              // compiling method's defining class definition index.
-  uint32_t method_idx;                 // compiling method's index into method_ids of DexFile.
-  uint32_t access_flags;               // compiling method's access flags.
-  InvokeType invoke_type;              // compiling method's invocation type.
-  const char* shorty;                  // compiling method's shorty.
-  uint32_t disable_opt;                // opt_control_vector flags.
-  uint32_t enable_debug;               // debugControlVector flags.
+  CompilerDriver* const compiler_driver;
+  ClassLinker* const class_linker;        // Linker to resolve fields and methods.
+  const DexFile* dex_file;                // DexFile containing the method being compiled.
+  jobject class_loader;                   // compiling method's class loader.
+  uint16_t class_def_idx;                 // compiling method's defining class definition index.
+  uint32_t method_idx;                    // compiling method's index into method_ids of DexFile.
+  uint32_t access_flags;                  // compiling method's access flags.
+  InvokeType invoke_type;                 // compiling method's invocation type.
+  const char* shorty;                     // compiling method's shorty.
+  uint32_t disable_opt;                   // opt_control_vector flags.
+  uint32_t enable_debug;                  // debugControlVector flags.
   bool verbose;
-  const Compiler* compiler;
-  InstructionSet instruction_set;
-  bool target64;
-
-  const InstructionSetFeatures* GetInstructionSetFeatures() {
-    return compiler_driver->GetInstructionSetFeatures();
-  }
-
-  // If non-empty, apply optimizer/debug flags only to matching methods.
-  std::string compiler_method_match;
-  // Flips sense of compiler_method_match - apply flags if doesn't match.
-  bool compiler_flip_match;
+  const InstructionSet instruction_set;
+  const bool target64;
 
   // TODO: move memory management to mir_graph, or just switch to using standard containers.
   ArenaAllocator arena;
   ArenaStack arena_stack;  // Arenas for ScopedArenaAllocator.
 
   std::unique_ptr<MIRGraph> mir_graph;   // MIR container.
-  std::unique_ptr<Backend> cg;           // Target-specific codegen.
+  std::unique_ptr<Mir2Lir> cg;           // Target-specific codegen.
   TimingLogger timings;
   bool print_pass;                 // Do we want to print a pass or not?
 
@@ -93,7 +200,7 @@
    * default settings have been changed. The key is simply the option string without
    * the pass name.
    */
-  SafeMap<const std::string, int> overridden_pass_options;
+  SafeMap<const std::string, const OptionContent> overridden_pass_options;
 };
 
 }  // namespace art
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index 9f17a3e..2a06cec 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -17,7 +17,7 @@
 #ifndef ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
 #define ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_
 
-#include "compiler_ir.h"
+#include "base/logging.h"
 #include "mir_graph.h"
 
 namespace art {
diff --git a/compiler/dex/dex_flags.h b/compiler/dex/dex_flags.h
new file mode 100644
index 0000000..eaf272b
--- /dev/null
+++ b/compiler/dex/dex_flags.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DEX_DEX_FLAGS_H_
+#define ART_COMPILER_DEX_DEX_FLAGS_H_
+
+namespace art {
+
+// Suppress optimization if corresponding bit set.
+enum OptControlVector {
+  kLoadStoreElimination = 0,
+  kLoadHoisting,
+  kSuppressLoads,
+  kNullCheckElimination,
+  kClassInitCheckElimination,
+  kGlobalValueNumbering,
+  kLocalValueNumbering,
+  kPromoteRegs,
+  kTrackLiveTemps,
+  kSafeOptimizations,
+  kBBOpt,
+  kSuspendCheckElimination,
+  kMatch,
+  kPromoteCompilerTemps,
+  kBranchFusing,
+  kSuppressExceptionEdges,
+  kSuppressMethodInlining,
+};
+
+// Force code generation paths for testing.
+enum DebugControlVector {
+  kDebugVerbose,
+  kDebugDumpCFG,
+  kDebugSlowFieldPath,
+  kDebugSlowInvokePath,
+  kDebugSlowStringPath,
+  kDebugSlowTypePath,
+  kDebugSlowestFieldPath,
+  kDebugSlowestStringPath,
+  kDebugExerciseResolveMethod,
+  kDebugVerifyDataflow,
+  kDebugShowMemoryUsage,
+  kDebugShowNops,
+  kDebugCountOpcodes,
+  kDebugDumpCheckStats,
+  kDebugShowSummaryMemoryUsage,
+  kDebugShowFilterStats,
+  kDebugTimings,
+  kDebugCodegenDump
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_DEX_FLAGS_H_
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 205a521..f7968c2 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -120,6 +120,22 @@
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
         break;
 
+      case Instruction::IGET_BOOLEAN:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN_QUICK, false);
+        break;
+
+      case Instruction::IGET_BYTE:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE_QUICK, false);
+        break;
+
+      case Instruction::IGET_CHAR:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR_QUICK, false);
+        break;
+
+      case Instruction::IGET_SHORT:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT_QUICK, false);
+        break;
+
       case Instruction::IPUT:
         CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
         break;
diff --git a/compiler/llvm/utils_llvm.h b/compiler/dex/dex_types.h
similarity index 67%
rename from compiler/llvm/utils_llvm.h
rename to compiler/dex/dex_types.h
index a606b91..f485c1c 100644
--- a/compiler/llvm/utils_llvm.h
+++ b/compiler/dex/dex_types.h
@@ -14,19 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_LLVM_UTILS_LLVM_H_
-#define ART_COMPILER_LLVM_UTILS_LLVM_H_
-
-#include <llvm/Analysis/Verifier.h>
+#ifndef ART_COMPILER_DEX_DEX_TYPES_H_
+#define ART_COMPILER_DEX_DEX_TYPES_H_
 
 namespace art {
 
-#ifndef NDEBUG
-#define VERIFY_LLVM_FUNCTION(func) ::llvm::verifyFunction(func, ::llvm::AbortProcessAction)
-#else
-#define VERIFY_LLVM_FUNCTION(func)
-#endif
+typedef uint32_t DexOffset;          // Dex offset in code units.
+typedef uint16_t NarrowDexOffset;    // For use in structs, Dex offsets range from 0 .. 0xffff.
 
 }  // namespace art
 
-#endif  // ART_COMPILER_LLVM_UTILS_LLVM_H_
+#endif  // ART_COMPILER_DEX_DEX_TYPES_H_
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
deleted file mode 100644
index 3f6231c..0000000
--- a/compiler/dex/frontend.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * 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.
- */
-
-#include "frontend.h"
-
-#include <cstdint>
-
-#include "backend.h"
-#include "base/dumpable.h"
-#include "compiler.h"
-#include "compiler_internals.h"
-#include "driver/compiler_driver.h"
-#include "driver/compiler_options.h"
-#include "mirror/object.h"
-#include "pass_driver_me_opts.h"
-#include "runtime.h"
-#include "base/logging.h"
-#include "base/timing_logger.h"
-#include "driver/compiler_options.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-
-namespace art {
-
-/* Default optimizer/debug setting for the compiler. */
-static uint32_t kCompilerOptimizerDisableFlags = 0 |  // Disable specific optimizations
-  // (1 << kLoadStoreElimination) |
-  // (1 << kLoadHoisting) |
-  // (1 << kSuppressLoads) |
-  // (1 << kNullCheckElimination) |
-  // (1 << kClassInitCheckElimination) |
-  // (1 << kGlobalValueNumbering) |
-  // (1 << kLocalValueNumbering) |
-  // (1 << kPromoteRegs) |
-  // (1 << kTrackLiveTemps) |
-  // (1 << kSafeOptimizations) |
-  // (1 << kBBOpt) |
-  // (1 << kMatch) |
-  // (1 << kPromoteCompilerTemps) |
-  // (1 << kSuppressExceptionEdges) |
-  // (1 << kSuppressMethodInlining) |
-  0;
-
-static uint32_t kCompilerDebugFlags = 0 |     // Enable debug/testing modes
-  // (1 << kDebugDisplayMissingTargets) |
-  // (1 << kDebugVerbose) |
-  // (1 << kDebugDumpCFG) |
-  // (1 << kDebugSlowFieldPath) |
-  // (1 << kDebugSlowInvokePath) |
-  // (1 << kDebugSlowStringPath) |
-  // (1 << kDebugSlowestFieldPath) |
-  // (1 << kDebugSlowestStringPath) |
-  // (1 << kDebugExerciseResolveMethod) |
-  // (1 << kDebugVerifyDataflow) |
-  // (1 << kDebugShowMemoryUsage) |
-  // (1 << kDebugShowNops) |
-  // (1 << kDebugCountOpcodes) |
-  // (1 << kDebugDumpCheckStats) |
-  // (1 << kDebugDumpBitcodeFile) |
-  // (1 << kDebugVerifyBitcode) |
-  // (1 << kDebugShowSummaryMemoryUsage) |
-  // (1 << kDebugShowFilterStats) |
-  // (1 << kDebugTimings) |
-  // (1 << kDebugCodegenDump) |
-  0;
-
-static CompiledMethod* CompileMethod(CompilerDriver& driver,
-                                     const Compiler* compiler,
-                                     const DexFile::CodeItem* code_item,
-                                     uint32_t access_flags, InvokeType invoke_type,
-                                     uint16_t class_def_idx, uint32_t method_idx,
-                                     jobject class_loader, const DexFile& dex_file,
-                                     void* llvm_compilation_unit) {
-  VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
-  if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
-    return nullptr;
-  }
-
-  DCHECK(driver.GetCompilerOptions().IsCompilationEnabled());
-
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  CompilationUnit cu(driver.GetArenaPool());
-
-  cu.compiler_driver = &driver;
-  cu.class_linker = class_linker;
-  cu.instruction_set = driver.GetInstructionSet();
-  if (cu.instruction_set == kArm) {
-    cu.instruction_set = kThumb2;
-  }
-  cu.target64 = Is64BitInstructionSet(cu.instruction_set);
-  cu.compiler = compiler;
-  // TODO: Mips64 is not yet implemented.
-  CHECK((cu.instruction_set == kThumb2) ||
-        (cu.instruction_set == kArm64) ||
-        (cu.instruction_set == kX86) ||
-        (cu.instruction_set == kX86_64) ||
-        (cu.instruction_set == kMips));
-
-  // TODO: set this from command line
-  cu.compiler_flip_match = false;
-  bool use_match = !cu.compiler_method_match.empty();
-  bool match = use_match && (cu.compiler_flip_match ^
-      (PrettyMethod(method_idx, dex_file).find(cu.compiler_method_match) != std::string::npos));
-  if (!use_match || match) {
-    cu.disable_opt = kCompilerOptimizerDisableFlags;
-    cu.enable_debug = kCompilerDebugFlags;
-    cu.verbose = VLOG_IS_ON(compiler) ||
-        (cu.enable_debug & (1 << kDebugVerbose));
-  }
-
-  if (driver.GetCompilerOptions().HasVerboseMethods()) {
-    cu.verbose = driver.GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file));
-  }
-
-  if (cu.verbose) {
-    cu.enable_debug |= (1 << kDebugCodegenDump);
-  }
-
-  /*
-   * TODO: rework handling of optimization and debug flags.  Should we split out
-   * MIR and backend flags?  Need command-line setting as well.
-   */
-
-  compiler->InitCompilationUnit(cu);
-
-  cu.StartTimingSplit("BuildMIRGraph");
-  cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
-
-  /*
-   * After creation of the MIR graph, also create the code generator.
-   * The reason we do this is that optimizations on the MIR graph may need to get information
-   * that is only available if a CG exists.
-   */
-  cu.cg.reset(compiler->GetCodeGenerator(&cu, llvm_compilation_unit));
-
-  /* Gathering opcode stats? */
-  if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
-    cu.mir_graph->EnableOpcodeCounting();
-  }
-
-  /* Build the raw MIR graph */
-  cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
-                              class_loader, dex_file);
-
-  if (!compiler->CanCompileMethod(method_idx, dex_file, &cu)) {
-    VLOG(compiler)  << cu.instruction_set << ": Cannot compile method : "
-        << PrettyMethod(method_idx, dex_file);
-    return nullptr;
-  }
-
-  cu.NewTimingSplit("MIROpt:CheckFilters");
-  std::string skip_message;
-  if (cu.mir_graph->SkipCompilation(&skip_message)) {
-    VLOG(compiler) << cu.instruction_set << ": Skipping method : "
-                   << PrettyMethod(method_idx, dex_file) << "  Reason = " << skip_message;
-    return nullptr;
-  }
-
-  /* Create the pass driver and launch it */
-  PassDriverMEOpts pass_driver(&cu);
-  pass_driver.Launch();
-
-  /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */
-  if (cu.compiler_driver->ProfilePresent()
-      && !cu.mir_graph->MethodIsLeaf()
-      && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) {
-    return nullptr;
-  }
-
-  if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
-    cu.mir_graph->DumpCheckStats();
-  }
-
-  if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
-    cu.mir_graph->ShowOpcodeStats();
-  }
-
-  /* Reassociate sreg names with original Dalvik vreg names. */
-  cu.mir_graph->RemapRegLocations();
-
-  /* Free Arenas from the cu.arena_stack for reuse by the cu.arena in the codegen. */
-  if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
-    if (cu.arena_stack.PeakBytesAllocated() > 1 * 1024 * 1024) {
-      MemStats stack_stats(cu.arena_stack.GetPeakStats());
-      LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(stack_stats);
-    }
-  }
-  cu.arena_stack.Reset();
-
-  CompiledMethod* result = NULL;
-
-  if (cu.mir_graph->PuntToInterpreter()) {
-    VLOG(compiler) << cu.instruction_set << ": Punted method to interpreter: "
-        << PrettyMethod(method_idx, dex_file);
-    return nullptr;
-  }
-
-  cu.cg->Materialize();
-
-  cu.NewTimingSplit("Dedupe");  /* deduping takes up the vast majority of time in GetCompiledMethod(). */
-  result = cu.cg->GetCompiledMethod();
-  cu.NewTimingSplit("Cleanup");
-
-  if (result) {
-    VLOG(compiler) << cu.instruction_set << ": Compiled " << PrettyMethod(method_idx, dex_file);
-  } else {
-    VLOG(compiler) << cu.instruction_set << ": Deferred " << PrettyMethod(method_idx, dex_file);
-  }
-
-  if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
-    if (cu.arena.BytesAllocated() > (1 * 1024 *1024)) {
-      MemStats mem_stats(cu.arena.GetMemStats());
-      LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
-    }
-  }
-
-  if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
-    LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks()
-              << " " << PrettyMethod(method_idx, dex_file);
-  }
-
-  cu.EndTiming();
-  driver.GetTimingsLogger()->AddLogger(cu.timings);
-  return result;
-}
-
-CompiledMethod* CompileOneMethod(CompilerDriver* driver,
-                                 const Compiler* compiler,
-                                 const DexFile::CodeItem* code_item,
-                                 uint32_t access_flags,
-                                 InvokeType invoke_type,
-                                 uint16_t class_def_idx,
-                                 uint32_t method_idx,
-                                 jobject class_loader,
-                                 const DexFile& dex_file,
-                                 void* compilation_unit) {
-  return CompileMethod(*driver, compiler, code_item, access_flags, invoke_type, class_def_idx,
-                       method_idx, class_loader, dex_file, compilation_unit);
-}
-
-}  // namespace art
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
deleted file mode 100644
index bed3b97..0000000
--- a/compiler/dex/frontend.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DEX_FRONTEND_H_
-#define ART_COMPILER_DEX_FRONTEND_H_
-
-#include "dex_file.h"
-#include "invoke_type.h"
-
-namespace art {
-
-class CompiledMethod;
-class Compiler;
-class CompilerDriver;
-
-/*
- * Assembly is an iterative process, and usually terminates within
- * two or three passes.  This should be high enough to handle bizarre
- * cases, but detect an infinite loop bug.
- */
-#define MAX_ASSEMBLER_RETRIES 50
-
-// Suppress optimization if corresponding bit set.
-enum opt_control_vector {
-  kLoadStoreElimination = 0,
-  kLoadHoisting,
-  kSuppressLoads,
-  kNullCheckElimination,
-  kClassInitCheckElimination,
-  kGlobalValueNumbering,
-  kLocalValueNumbering,
-  kPromoteRegs,
-  kTrackLiveTemps,
-  kSafeOptimizations,
-  kBBOpt,
-  kMatch,
-  kPromoteCompilerTemps,
-  kBranchFusing,
-  kSuppressExceptionEdges,
-  kSuppressMethodInlining,
-};
-
-// Force code generation paths for testing.
-enum debugControlVector {
-  kDebugVerbose,
-  kDebugDumpCFG,
-  kDebugSlowFieldPath,
-  kDebugSlowInvokePath,
-  kDebugSlowStringPath,
-  kDebugSlowTypePath,
-  kDebugSlowestFieldPath,
-  kDebugSlowestStringPath,
-  kDebugExerciseResolveMethod,
-  kDebugVerifyDataflow,
-  kDebugShowMemoryUsage,
-  kDebugShowNops,
-  kDebugCountOpcodes,
-  kDebugDumpCheckStats,
-  kDebugDumpBitcodeFile,
-  kDebugVerifyBitcode,
-  kDebugShowSummaryMemoryUsage,
-  kDebugShowFilterStats,
-  kDebugTimings,
-  kDebugCodegenDump
-};
-
-CompiledMethod* CompileOneMethod(CompilerDriver* driver,
-                                 const Compiler* compiler,
-                                 const DexFile::CodeItem* code_item,
-                                 uint32_t access_flags,
-                                 InvokeType invoke_type,
-                                 uint16_t class_def_idx,
-                                 uint32_t method_idx,
-                                 jobject class_loader,
-                                 const DexFile& dex_file,
-                                 void* compilation_unit);
-
-}  // namespace art
-
-#endif  // ART_COMPILER_DEX_FRONTEND_H_
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc
index 578952b..a8fd812 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -15,6 +15,8 @@
  */
 
 #include "global_value_numbering.h"
+
+#include "base/stl_util.h"
 #include "local_value_numbering.h"
 
 namespace art {
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index d72144a..cdafc68 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -17,8 +17,11 @@
 #ifndef ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_
 #define ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_
 
+#include "base/logging.h"
 #include "base/macros.h"
-#include "compiler_internals.h"
+#include "mir_graph.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
 #include "utils/arena_object.h"
 
 namespace art {
@@ -252,7 +255,7 @@
 };
 std::ostream& operator<<(std::ostream& os, const GlobalValueNumbering::Mode& rhs);
 
-inline  void GlobalValueNumbering::StartPostProcessing() {
+inline void GlobalValueNumbering::StartPostProcessing() {
   DCHECK(Good());
   DCHECK_EQ(mode_, kModeGvn);
   mode_ = kModeGvnPostProcessing;
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index 7e3b4d8..f71b7ae 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "compiler_internals.h"
+#include "base/logging.h"
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
 #include "dex/mir_field_info.h"
@@ -215,7 +215,6 @@
         bb->data_flow_info->live_in_v = live_in_v_;
       }
     }
-    cu_.mir_graph->num_blocks_ = count;
     ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
     cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
@@ -338,7 +337,7 @@
 
   GlobalValueNumberingTest()
       : pool_(),
-        cu_(&pool_),
+        cu_(&pool_, kRuntimeISA, nullptr, nullptr),
         mir_count_(0u),
         mirs_(nullptr),
         ssa_reps_(),
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 9b89c95..aef8c6d 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -19,7 +19,7 @@
 
 #include <memory>
 
-#include "compiler_internals.h"
+#include "base/logging.h"
 #include "global_value_numbering.h"
 #include "utils/arena_object.h"
 #include "utils/dex_instruction_utils.h"
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
index 0fcb584..c894892 100644
--- a/compiler/dex/local_value_numbering_test.cc
+++ b/compiler/dex/local_value_numbering_test.cc
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include "compiler_internals.h"
 #include "dex/mir_field_info.h"
 #include "global_value_numbering.h"
 #include "local_value_numbering.h"
@@ -202,7 +201,7 @@
 
   LocalValueNumberingTest()
       : pool_(),
-        cu_(&pool_),
+        cu_(&pool_, kRuntimeISA, nullptr, nullptr),
         mir_count_(0u),
         mirs_(nullptr),
         ssa_reps_(),
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 7b53b14..473196b 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -17,15 +17,18 @@
 #include <algorithm>
 #include <memory>
 
-#include "compiler_internals.h"
+#include "base/logging.h"
 #include "dataflow_iterator-inl.h"
-#include "dex_instruction.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
 #include "dex_instruction-inl.h"
 #include "dex/mir_field_info.h"
 #include "dex/verified_method.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
+#include "driver/dex_compilation_unit.h"
 #include "utils/scoped_arena_containers.h"
 
 namespace art {
@@ -1151,7 +1154,7 @@
     skip_compilation = true;
     *skip_message = "Huge method: " + std::to_string(GetNumDalvikInsns());
     // If we're got a huge number of basic blocks, don't bother with further analysis.
-    if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) {
+    if (static_cast<size_t>(GetNumBlocks()) > (compiler_options.GetHugeMethodThreshold() / 2)) {
       return true;
     }
   } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) &&
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index 5b7ac3c..a1f4294 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include "compiler_internals.h"
 #include "local_value_numbering.h"
 #include "dataflow_iterator-inl.h"
 
@@ -897,6 +896,18 @@
 
   // 120 MirOpPackedArrayPut
   DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
+
+  // 121 MirOpMaddInt
+  DF_FORMAT_EXTENDED,
+
+  // 122 MirOpMsubInt
+  DF_FORMAT_EXTENDED,
+
+  // 123 MirOpMaddLong
+  DF_FORMAT_EXTENDED,
+
+  // 124 MirOpMsubLong
+  DF_FORMAT_EXTENDED,
 };
 
 /* Return the base virtual register for a SSA name */
@@ -906,7 +917,7 @@
 
 /* Any register that is used before being defined is considered live-in */
 void MIRGraph::HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v,
-                            ArenaBitVector* live_in_v, int dalvik_reg_id) {
+                               ArenaBitVector* live_in_v, int dalvik_reg_id) {
   use_v->SetBit(dalvik_reg_id);
   if (!def_v->IsBitSet(dalvik_reg_id)) {
     live_in_v->SetBit(dalvik_reg_id);
@@ -919,8 +930,8 @@
 }
 
 void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v,
-                            ArenaBitVector* live_in_v,
-                            const MIR::DecodedInstruction& d_insn) {
+                              ArenaBitVector* live_in_v,
+                              const MIR::DecodedInstruction& d_insn) {
   // For vector MIRs, vC contains type information
   bool is_vector_type_wide = false;
   int type_size = d_insn.vC >> 16;
@@ -951,6 +962,24 @@
         HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1);
       }
       break;
+    case kMirOpMaddInt:
+    case kMirOpMsubInt:
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]);
+      HandleDef(def_v, d_insn.vA);
+      break;
+    case kMirOpMaddLong:
+    case kMirOpMsubLong:
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC + 1);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]);
+      HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0] + 1);
+      HandleDef(def_v, d_insn.vA);
+      HandleDef(def_v, d_insn.vA + 1);
+      break;
     default:
       LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode;
       break;
@@ -1139,6 +1168,28 @@
         HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1);
       }
       break;
+    case kMirOpMaddInt:
+    case kMirOpMsubInt:
+      AllocateSSAUseData(mir, 3);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 1);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 2);
+      AllocateSSADefData(mir, 1);
+      HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+      break;
+    case kMirOpMaddLong:
+    case kMirOpMsubLong:
+      AllocateSSAUseData(mir, 6);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 2);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.vC + 1, 3);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 4);
+      HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0] + 1, 5);
+      AllocateSSADefData(mir, 2);
+      HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+      HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1);
+      break;
     default:
       LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode;
       break;
@@ -1147,11 +1198,30 @@
 
 /* Entry function to convert a block into SSA representation */
 bool MIRGraph::DoSSAConversion(BasicBlock* bb) {
-  MIR* mir;
-
   if (bb->data_flow_info == NULL) return false;
 
-  for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+  /*
+   * Pruned SSA form: Insert phi nodes for each dalvik register marked in phi_node_blocks
+   * only if the dalvik register is in the live-in set.
+   */
+  BasicBlockId bb_id = bb->id;
+  for (int dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
+    if (temp_.ssa.phi_node_blocks[dalvik_reg]->IsBitSet(bb_id)) {
+      if (!bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) {
+        /* Variable will be clobbered before being used - no need for phi */
+        vreg_to_ssa_map_[dalvik_reg] = INVALID_SREG;
+        continue;
+      }
+      MIR *phi = NewMIR();
+      phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi);
+      phi->dalvikInsn.vA = dalvik_reg;
+      phi->offset = bb->start_offset;
+      phi->m_unit_index = 0;  // Arbitrarily assign all Phi nodes to outermost method.
+      bb->PrependMIR(phi);
+    }
+  }
+
+  for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
     mir->ssa_rep =
         static_cast<struct SSARepresentation *>(arena_->Alloc(sizeof(SSARepresentation),
                                                               kArenaAllocDFInfo));
@@ -1343,7 +1413,7 @@
  * counts explicitly used s_regs.  A later phase will add implicit
  * counts for things such as Method*, null-checked references, etc.
  */
-void MIRGraph::CountUses(class BasicBlock* bb) {
+void MIRGraph::CountUses(BasicBlock* bb) {
   if (bb->block_type != kDalvikByteCode) {
     return;
   }
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 023abca..92f960e 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -18,15 +18,19 @@
 
 #include <inttypes.h>
 #include <queue>
+#include <unistd.h>
 
 #include "base/bit_vector-inl.h"
+#include "base/logging.h"
 #include "base/stl_util.h"
-#include "compiler_internals.h"
+#include "base/stringprintf.h"
+#include "compiler_ir.h"
 #include "dex_file-inl.h"
+#include "dex_flags.h"
 #include "dex_instruction-inl.h"
-#include "dex/global_value_numbering.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-#include "dex/quick/dex_file_method_inliner.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
+#include "dex/quick/quick_compiler.h"
 #include "leb128.h"
 #include "pass_driver_me_post_opt.h"
 #include "stack.h"
@@ -70,6 +74,10 @@
   "MemBarrier",
   "PackedArrayGet",
   "PackedArrayPut",
+  "MaddInt",
+  "MsubInt",
+  "MaddLong",
+  "MsubLong",
 };
 
 MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
@@ -87,6 +95,9 @@
       num_reachable_blocks_(0),
       max_num_reachable_blocks_(0),
       dfs_orders_up_to_date_(false),
+      domination_up_to_date_(false),
+      mir_ssa_rep_up_to_date_(false),
+      topological_order_up_to_date_(false),
       dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)),
       dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)),
       dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)),
@@ -101,9 +112,7 @@
       try_block_addr_(NULL),
       entry_block_(NULL),
       exit_block_(NULL),
-      num_blocks_(0),
       current_code_item_(NULL),
-      dex_pc_to_block_map_(arena->Adapter()),
       m_units_(arena->Adapter()),
       method_stack_(arena->Adapter()),
       current_method_(kInvalidEntry),
@@ -127,7 +136,7 @@
       ifield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
       sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
       method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
-      gen_suspend_test_list_(arena->Adapter()) {
+      suspend_checks_in_loops_(nullptr) {
   memset(&temp_, 0, sizeof(temp_));
   use_counts_.reserve(256);
   raw_use_counts_.reserve(256);
@@ -258,31 +267,14 @@
   DCHECK(insn != orig_block->first_mir_insn);
   DCHECK(insn == bottom_block->first_mir_insn);
   DCHECK_EQ(insn->offset, bottom_block->start_offset);
-  DCHECK_EQ(dex_pc_to_block_map_[insn->offset], orig_block->id);
   // Scan the "bottom" instructions, remapping them to the
   // newly created "bottom" block.
   MIR* p = insn;
   p->bb = bottom_block->id;
-  dex_pc_to_block_map_[p->offset] = bottom_block->id;
   while (p != bottom_block->last_mir_insn) {
     p = p->next;
     DCHECK(p != nullptr);
     p->bb = bottom_block->id;
-    int opcode = p->dalvikInsn.opcode;
-    /*
-     * Some messiness here to ensure that we only enter real opcodes and only the
-     * first half of a potentially throwing instruction that has been split into
-     * CHECK and work portions. Since the 2nd half of a split operation is always
-     * the first in a BasicBlock, we can't hit it here.
-     */
-    if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
-      BasicBlockId mapped_id = dex_pc_to_block_map_[p->offset];
-      // At first glance the instructions should all be mapped to orig_block.
-      // However, multiple instructions may correspond to the same dex, hence an earlier
-      // instruction may have already moved the mapping for dex to bottom_block.
-      DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id));
-      dex_pc_to_block_map_[p->offset] = bottom_block->id;
-    }
   }
 
   return bottom_block;
@@ -297,12 +289,13 @@
  * Utilizes a map for fast lookup of the typical cases.
  */
 BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool create,
-                                BasicBlock** immed_pred_block_p) {
+                                BasicBlock** immed_pred_block_p,
+                                ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
   if (code_offset >= current_code_item_->insns_size_in_code_units_) {
     return nullptr;
   }
 
-  int block_id = dex_pc_to_block_map_[code_offset];
+  int block_id = (*dex_pc_to_block_map)[code_offset];
   BasicBlock* bb = GetBasicBlock(block_id);
 
   if ((bb != nullptr) && (bb->start_offset == code_offset)) {
@@ -317,19 +310,46 @@
 
   if (bb != nullptr) {
     // The target exists somewhere in an existing block.
-    return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?  immed_pred_block_p : nullptr);
+    BasicBlock* bottom_block = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?  immed_pred_block_p : nullptr);
+    DCHECK(bottom_block != nullptr);
+    MIR* p = bottom_block->first_mir_insn;
+    BasicBlock* orig_block = bb;
+    DCHECK_EQ((*dex_pc_to_block_map)[p->offset], orig_block->id);
+    // Scan the "bottom" instructions, remapping them to the
+    // newly created "bottom" block.
+    (*dex_pc_to_block_map)[p->offset] = bottom_block->id;
+    while (p != bottom_block->last_mir_insn) {
+      p = p->next;
+      DCHECK(p != nullptr);
+      int opcode = p->dalvikInsn.opcode;
+      /*
+       * Some messiness here to ensure that we only enter real opcodes and only the
+       * first half of a potentially throwing instruction that has been split into
+       * CHECK and work portions. Since the 2nd half of a split operation is always
+       * the first in a BasicBlock, we can't hit it here.
+       */
+      if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
+        BasicBlockId mapped_id = (*dex_pc_to_block_map)[p->offset];
+        // At first glance the instructions should all be mapped to orig_block.
+        // However, multiple instructions may correspond to the same dex, hence an earlier
+        // instruction may have already moved the mapping for dex to bottom_block.
+        DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id));
+        (*dex_pc_to_block_map)[p->offset] = bottom_block->id;
+      }
+    }
+    return bottom_block;
   }
 
   // Create a new block.
   bb = CreateNewBB(kDalvikByteCode);
   bb->start_offset = code_offset;
-  dex_pc_to_block_map_[bb->start_offset] = bb->id;
+  (*dex_pc_to_block_map)[bb->start_offset] = bb->id;
   return bb;
 }
 
 
 /* Identify code range in try blocks and set up the empty catch blocks */
-void MIRGraph::ProcessTryCatchBlocks() {
+void MIRGraph::ProcessTryCatchBlocks(ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
   int tries_size = current_code_item_->tries_size_;
   DexOffset offset;
 
@@ -354,7 +374,7 @@
     CatchHandlerIterator iterator(handlers_ptr);
     for (; iterator.HasNext(); iterator.Next()) {
       uint32_t address = iterator.GetHandlerAddress();
-      FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr);
+      FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map);
     }
     handlers_ptr = iterator.EndDataPointer();
   }
@@ -429,7 +449,8 @@
 /* Process instructions with the kBranch flag */
 BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
                                        int width, int flags, const uint16_t* code_ptr,
-                                       const uint16_t* code_end) {
+                                       const uint16_t* code_end,
+                                       ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
   DexOffset target = cur_offset;
   switch (insn->dalvikInsn.opcode) {
     case Instruction::GOTO:
@@ -460,7 +481,8 @@
   }
   CountBranch(target);
   BasicBlock* taken_block = FindBlock(target, /* create */ true,
-                                      /* immed_pred_block_p */ &cur_block);
+                                      /* immed_pred_block_p */ &cur_block,
+                                      dex_pc_to_block_map);
   cur_block->taken = taken_block->id;
   taken_block->predecessors.push_back(cur_block->id);
 
@@ -470,18 +492,20 @@
                                              /* create */
                                              true,
                                              /* immed_pred_block_p */
-                                             &cur_block);
+                                             &cur_block,
+                                             dex_pc_to_block_map);
     cur_block->fall_through = fallthrough_block->id;
     fallthrough_block->predecessors.push_back(cur_block->id);
   } else if (code_ptr < code_end) {
-    FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+    FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map);
   }
   return cur_block;
 }
 
 /* Process instructions with the kSwitch flag */
 BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
-                                       int width, int flags) {
+                                       int width, int flags,
+                                       ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
   UNUSED(flags);
   const uint16_t* switch_data =
       reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB);
@@ -535,7 +559,8 @@
 
   for (i = 0; i < size; i++) {
     BasicBlock* case_block = FindBlock(cur_offset + target_table[i],  /* create */ true,
-                                       /* immed_pred_block_p */ &cur_block);
+                                       /* immed_pred_block_p */ &cur_block,
+                                       dex_pc_to_block_map);
     SuccessorBlockInfo* successor_block_info =
         static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
                                                        kArenaAllocSuccessor));
@@ -549,7 +574,8 @@
 
   /* Fall-through case */
   BasicBlock* fallthrough_block = FindBlock(cur_offset +  width, /* create */ true,
-                                            /* immed_pred_block_p */ nullptr);
+                                            /* immed_pred_block_p */ nullptr,
+                                            dex_pc_to_block_map);
   cur_block->fall_through = fallthrough_block->id;
   fallthrough_block->predecessors.push_back(cur_block->id);
   return cur_block;
@@ -558,7 +584,8 @@
 /* Process instructions with the kThrow flag */
 BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
                                       int width, int flags, ArenaBitVector* try_block_addr,
-                                      const uint16_t* code_ptr, const uint16_t* code_end) {
+                                      const uint16_t* code_ptr, const uint16_t* code_end,
+                                      ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
   UNUSED(flags);
   bool in_try_block = try_block_addr->IsBitSet(cur_offset);
   bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW);
@@ -575,7 +602,8 @@
 
     for (; iterator.HasNext(); iterator.Next()) {
       BasicBlock* catch_block = FindBlock(iterator.GetHandlerAddress(), false /* create */,
-                                          nullptr /* immed_pred_block_p */);
+                                          nullptr /* immed_pred_block_p */,
+                                          dex_pc_to_block_map);
       if (insn->dalvikInsn.opcode == Instruction::MONITOR_EXIT &&
           IsBadMonitorExitCatch(insn->offset, catch_block->start_offset)) {
         // Don't allow monitor-exit to catch its own exception, http://b/15745363 .
@@ -610,7 +638,7 @@
     cur_block->explicit_throw = true;
     if (code_ptr < code_end) {
       // Force creation of new block following THROW via side-effect.
-      FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+      FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map);
     }
     if (!in_try_block) {
        // Don't split a THROW that can't rethrow - we're done.
@@ -642,7 +670,7 @@
    * not automatically terminated after the work portion, and may
    * contain following instructions.
    *
-   * Note also that the dex_pc_to_block_map_ entry for the potentially
+   * Note also that the dex_pc_to_block_map entry for the potentially
    * throwing instruction will refer to the original basic block.
    */
   BasicBlock* new_block = CreateNewBB(kDalvikByteCode);
@@ -677,7 +705,11 @@
   // TODO: need to rework expansion of block list & try_block_addr when inlining activated.
   // TUNING: use better estimate of basic blocks for following resize.
   block_list_.reserve(block_list_.size() + current_code_item_->insns_size_in_code_units_);
-  dex_pc_to_block_map_.resize(dex_pc_to_block_map_.size() + current_code_item_->insns_size_in_code_units_);
+  // FindBlock lookup cache.
+  ScopedArenaAllocator allocator(&cu_->arena_stack);
+  ScopedArenaVector<uint16_t> dex_pc_to_block_map(allocator.Adapter());
+  dex_pc_to_block_map.resize(dex_pc_to_block_map.size() +
+                             current_code_item_->insns_size_in_code_units_);
 
   // TODO: replace with explicit resize routine.  Using automatic extension side effect for now.
   try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_);
@@ -687,7 +719,7 @@
   if (current_method_ == 0) {
     DCHECK(entry_block_ == NULL);
     DCHECK(exit_block_ == NULL);
-    DCHECK_EQ(num_blocks_, 0U);
+    DCHECK_EQ(GetNumBlocks(), 0U);
     // Use id 0 to represent a null block.
     BasicBlock* null_block = CreateNewBB(kNullBlock);
     DCHECK_EQ(null_block->id, NullBasicBlockId);
@@ -718,7 +750,7 @@
   cur_block->predecessors.push_back(entry_block_->id);
 
   /* Identify code range in try blocks and set up the empty catch blocks */
-  ProcessTryCatchBlocks();
+  ProcessTryCatchBlocks(&dex_pc_to_block_map);
 
   uint64_t merged_df_flags = 0u;
 
@@ -767,20 +799,21 @@
         DCHECK(cur_block->taken == NullBasicBlockId);
         // Unreachable instruction, mark for no continuation and end basic block.
         flags &= ~Instruction::kContinue;
-        FindBlock(current_offset_ + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+        FindBlock(current_offset_ + width, /* create */ true,
+                  /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map);
       }
     } else {
       cur_block->AppendMIR(insn);
     }
 
     // Associate the starting dex_pc for this opcode with its containing basic block.
-    dex_pc_to_block_map_[insn->offset] = cur_block->id;
+    dex_pc_to_block_map[insn->offset] = cur_block->id;
 
     code_ptr += width;
 
     if (flags & Instruction::kBranch) {
       cur_block = ProcessCanBranch(cur_block, insn, current_offset_,
-                                   width, flags, code_ptr, code_end);
+                                   width, flags, code_ptr, code_end, &dex_pc_to_block_map);
     } else if (flags & Instruction::kReturn) {
       cur_block->terminated_by_return = true;
       cur_block->fall_through = exit_block_->id;
@@ -794,13 +827,15 @@
          * Create a fallthrough block for real instructions
          * (incl. NOP).
          */
-         FindBlock(current_offset_ + width, /* create */ true, /* immed_pred_block_p */ nullptr);
+         FindBlock(current_offset_ + width, /* create */ true,
+                   /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map);
       }
     } else if (flags & Instruction::kThrow) {
       cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_,
-                                  code_ptr, code_end);
+                                  code_ptr, code_end, &dex_pc_to_block_map);
     } else if (flags & Instruction::kSwitch) {
-      cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width, flags);
+      cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width,
+                                   flags, &dex_pc_to_block_map);
     }
     if (verify_flags & Instruction::kVerifyVarArgRange ||
         verify_flags & Instruction::kVerifyVarArgRangeNonZero) {
@@ -818,7 +853,8 @@
     }
     current_offset_ += width;
     BasicBlock* next_block = FindBlock(current_offset_, /* create */ false,
-                                       /* immed_pred_block_p */ nullptr);
+                                       /* immed_pred_block_p */ nullptr,
+                                       &dex_pc_to_block_map);
     if (next_block) {
       /*
        * The next instruction could be the target of a previously parsed
@@ -870,6 +906,34 @@
   return GetDataFlowAttributes(opcode);
 }
 
+// The path can easily surpass FS limits because of parameters etc. Use pathconf to get FS
+// restrictions here. Note that a successful invocation will return an actual value. If the path
+// is too long for some reason, the return will be ENAMETOOLONG. Then cut off part of the name.
+//
+// It's possible the path is not valid, or some other errors appear. In that case return false.
+static bool CreateDumpFile(std::string& fname, const char* dir_prefix, NarrowDexOffset start_offset,
+                           const char *suffix, int nr, std::string* output) {
+  std::string dir = StringPrintf("./%s", dir_prefix);
+  int64_t max_name_length = pathconf(dir.c_str(), _PC_NAME_MAX);
+  if (max_name_length <= 0) {
+    PLOG(ERROR) << "Could not get file name restrictions for " << dir;
+    return false;
+  }
+
+  std::string name = StringPrintf("%s%x%s_%d.dot", fname.c_str(), start_offset,
+                                  suffix == nullptr ? "" : suffix, nr);
+  std::string fpath;
+  if (static_cast<int64_t>(name.size()) > max_name_length) {
+    std::string suffix_str = StringPrintf("_%d.dot", nr);
+    name = name.substr(0, static_cast<size_t>(max_name_length) - suffix_str.size()) + suffix_str;
+  }
+  // Sanity check.
+  DCHECK_LE(name.size(), static_cast<size_t>(max_name_length));
+
+  *output = StringPrintf("%s%s", dir_prefix, name.c_str());
+  return true;
+}
+
 // TODO: use a configurable base prefix, and adjust callers to supply pass name.
 /* Dump the CFG into a DOT graph */
 void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suffix) {
@@ -878,15 +942,19 @@
 
   // Increment counter to get a unique file number.
   cnt++;
+  int nr = cnt.LoadRelaxed();
 
   std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
   ReplaceSpecialChars(fname);
-  fname = StringPrintf("%s%s%x%s_%d.dot", dir_prefix, fname.c_str(),
-                      GetBasicBlock(GetEntryBlock()->fall_through)->start_offset,
-                      suffix == nullptr ? "" : suffix,
-                      cnt.LoadRelaxed());
-  file = fopen(fname.c_str(), "w");
+  std::string fpath;
+  if (!CreateDumpFile(fname, dir_prefix, GetBasicBlock(GetEntryBlock()->fall_through)->start_offset,
+                      suffix, nr, &fpath)) {
+    LOG(ERROR) << "Could not create dump file name for " << fname;
+    return;
+  }
+  file = fopen(fpath.c_str(), "w");
   if (file == NULL) {
+    PLOG(ERROR) << "Could not open " << fpath << " for DumpCFG.";
     return;
   }
   fprintf(file, "digraph G {\n");
@@ -1386,6 +1454,27 @@
       }
       FillTypeSizeString(mir->dalvikInsn.arg[0], decoded_mir);
       break;
+    case kMirOpMaddInt:
+    case kMirOpMsubInt:
+    case kMirOpMaddLong:
+    case kMirOpMsubLong:
+      if (ssa_rep != nullptr) {
+        decoded_mir->append(" ");
+        decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+        if (defs > 1) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+        }
+        for (int i = 0; i < uses; i++) {
+          decoded_mir->append(", ");
+          decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false));
+        }
+      } else {
+        decoded_mir->append(StringPrintf(" v%d, v%d, v%d, v%d",
+                                         mir->dalvikInsn.vA, mir->dalvikInsn.vB,
+                                         mir->dalvikInsn.vC, mir->dalvikInsn.arg[0]));
+      }
+      break;
     default:
       break;
   }
@@ -1590,6 +1679,12 @@
   return cu_->dex_file->GetShorty(method_id.proto_idx_);
 }
 
+const char* MIRGraph::GetShortyFromMethodReference(const MethodReference& target_method) {
+  const DexFile::MethodId& method_id =
+      target_method.dex_file->GetMethodId(target_method.dex_method_index);
+  return target_method.dex_file->GetShorty(method_id.proto_idx_);
+}
+
 /* Debug Utility - dump a compilation unit */
 void MIRGraph::DumpMIRGraph() {
   const char* block_type_names[] = {
@@ -1703,12 +1798,16 @@
 
   temp_.ssa.num_vregs = 0u;
   temp_.ssa.work_live_vregs = nullptr;
-  temp_.ssa.def_block_matrix = nullptr;
+  DCHECK(temp_.ssa.def_block_matrix == nullptr);
+  temp_.ssa.phi_node_blocks = nullptr;
   DCHECK(temp_scoped_alloc_.get() != nullptr);
   temp_scoped_alloc_.reset();
 
   // Update the maximum number of reachable blocks.
   max_num_reachable_blocks_ = num_reachable_blocks_;
+
+  // Mark MIR SSA representations as up to date.
+  mir_ssa_rep_up_to_date_ = true;
 }
 
 size_t MIRGraph::GetNumDalvikInsns() const {
@@ -1941,6 +2040,7 @@
     DCHECK_EQ(bb->hidden, false);
     DCHECK_EQ(bb->visited, false);
     bb->visited = true;
+    bb->nesting_depth = loop_head_stack.size();
 
     // Now add the basic block.
     uint16_t idx = static_cast<uint16_t>(topological_order_.size());
@@ -1973,6 +2073,7 @@
   topological_order_loop_head_stack_.clear();
   topological_order_loop_head_stack_.reserve(max_nested_loops);
   max_nested_loops_ = max_nested_loops;
+  topological_order_up_to_date_ = true;
 }
 
 bool BasicBlock::IsExceptionBlock() const {
@@ -1982,24 +2083,6 @@
   return false;
 }
 
-bool MIRGraph::HasSuspendTestBetween(BasicBlock* source, BasicBlockId target_id) {
-  BasicBlock* target = GetBasicBlock(target_id);
-
-  if (source == nullptr || target == nullptr)
-    return false;
-
-  int idx;
-  for (idx = gen_suspend_test_list_.size() - 1; idx >= 0; idx--) {
-    BasicBlock* bb = gen_suspend_test_list_[idx];
-    if (bb == source)
-      return true;  // The block has been inserted by a suspend check before.
-    if (source->dominators->IsBitSet(bb->id) && bb->dominators->IsBitSet(target_id))
-      return true;
-  }
-
-  return false;
-}
-
 ChildBlockIterator::ChildBlockIterator(BasicBlock* bb, MIRGraph* mir_graph)
     : basic_block_(bb), mir_graph_(mir_graph), visited_fallthrough_(false),
       visited_taken_(false), have_successors_(false) {
@@ -2222,21 +2305,7 @@
   }
 }
 
-void BasicBlock::Hide(MIRGraph* mir_graph) {
-  // First lets make it a dalvik bytecode block so it doesn't have any special meaning.
-  block_type = kDalvikByteCode;
-
-  // Mark it as hidden.
-  hidden = true;
-
-  // Detach it from its MIRs so we don't generate code for them. Also detached MIRs
-  // are updated to know that they no longer have a parent.
-  for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) {
-    mir->bb = NullBasicBlockId;
-  }
-  first_mir_insn = nullptr;
-  last_mir_insn = nullptr;
-
+void BasicBlock::Kill(MIRGraph* mir_graph) {
   for (BasicBlockId pred_id : predecessors) {
     BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
     DCHECK(pred_bb != nullptr);
@@ -2244,26 +2313,7 @@
     // Sadly we have to go through the children by hand here.
     pred_bb->ReplaceChild(id, NullBasicBlockId);
   }
-
-  // Iterate through children of bb we are hiding.
-  ChildBlockIterator successorChildIter(this, mir_graph);
-
-  for (BasicBlock* childPtr = successorChildIter.Next(); childPtr != 0; childPtr = successorChildIter.Next()) {
-    // Erase this predecessor from child.
-    childPtr->ErasePredecessor(id);
-  }
-
-  // Remove link to children.
-  taken = NullBasicBlockId;
-  fall_through = NullBasicBlockId;
-  successor_block_list_type = kNotUsed;
-}
-
-/*
- * Kill an unreachable block and all blocks that become unreachable by killing this one.
- */
-void BasicBlock::KillUnreachable(MIRGraph* mir_graph) {
-  DCHECK(predecessors.empty());  // Unreachable.
+  predecessors.clear();
 
   // Mark as dead and hidden.
   block_type = kDead;
@@ -2283,9 +2333,6 @@
   ChildBlockIterator iter(this, mir_graph);
   for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) {
     succ_bb->ErasePredecessor(id);
-    if (succ_bb->predecessors.empty()) {
-      succ_bb->KillUnreachable(mir_graph);
-    }
   }
 
   // Remove links to children.
@@ -2406,20 +2453,20 @@
 // Create a new basic block with block_id as num_blocks_ that is
 // post-incremented.
 BasicBlock* MIRGraph::CreateNewBB(BBType block_type) {
-  BasicBlock* res = NewMemBB(block_type, num_blocks_++);
+  BasicBlockId id = static_cast<BasicBlockId>(block_list_.size());
+  BasicBlock* res = NewMemBB(block_type, id);
   block_list_.push_back(res);
   return res;
 }
 
 void MIRGraph::CalculateBasicBlockInformation() {
-  PassDriverMEPostOpt driver(cu_);
+  auto* quick_compiler = down_cast<QuickCompiler*>(cu_->compiler_driver->GetCompiler());
+  DCHECK(quick_compiler != nullptr);
+  /* Create the pass driver and launch it */
+  PassDriverMEPostOpt driver(quick_compiler->GetPostOptPassManager(), cu_);
   driver.Launch();
 }
 
-void MIRGraph::InitializeBasicBlockData() {
-  num_blocks_ = block_list_.size();
-}
-
 int MIR::DecodedInstruction::FlagsOf() const {
   // Calculate new index.
   int idx = static_cast<int>(opcode) - kNumPackedOpcodes;
@@ -2497,9 +2544,19 @@
       return Instruction::kContinue | Instruction::kThrow;
     case kMirOpPackedArrayPut:
       return Instruction::kContinue | Instruction::kThrow;
+    case kMirOpMaddInt:
+    case kMirOpMsubInt:
+    case kMirOpMaddLong:
+    case kMirOpMsubLong:
+      return Instruction::kContinue;
     default:
       LOG(WARNING) << "ExtendedFlagsOf: Unhandled case: " << static_cast<int> (opcode);
       return 0;
   }
 }
+
+const uint16_t* MIRGraph::GetInsns(int m_unit_index) const {
+  return m_units_[m_unit_index]->GetCodeItem()->insns_;
+}
+
 }  // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 1a18841..27dca65 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -19,10 +19,9 @@
 
 #include <stdint.h>
 
-#include "compiler_ir.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
-#include "driver/dex_compilation_unit.h"
+#include "dex_types.h"
 #include "invoke_type.h"
 #include "mir_field_info.h"
 #include "mir_method_info.h"
@@ -34,8 +33,14 @@
 
 namespace art {
 
+struct CompilationUnit;
+class DexCompilationUnit;
+class DexFileMethodInliner;
 class GlobalValueNumbering;
 
+// Forward declaration.
+class MIRGraph;
+
 enum DataFlowAttributePos {
   kUA = 0,
   kUB,
@@ -131,16 +136,13 @@
 
 enum OatMethodAttributes {
   kIsLeaf,            // Method is leaf.
-  kHasLoop,           // Method contains simple loop.
 };
 
 #define METHOD_IS_LEAF          (1 << kIsLeaf)
-#define METHOD_HAS_LOOP         (1 << kHasLoop)
 
 // Minimum field size to contain Dalvik v_reg number.
 #define VREG_NUM_WIDTH 16
 
-#define INVALID_SREG (-1)
 #define INVALID_VREG (0xFFFFU)
 #define INVALID_OFFSET (0xDEADF00FU)
 
@@ -410,17 +412,11 @@
   void ResetOptimizationFlags(uint16_t reset_flags);
 
   /**
-   * @brief Hide the BasicBlock.
-   * @details Set it to kDalvikByteCode, set hidden to true, remove all MIRs,
-   *          remove itself from any predecessor edges, remove itself from any
-   *          child's predecessor array.
+   * @brief Kill the BasicBlock.
+   * @details Unlink predecessors and successors, remove all MIRs, set the block type to kDead
+   *          and set hidden to true.
    */
-  void Hide(MIRGraph* mir_graph);
-
-  /**
-   *  @brief Kill the unreachable block and all blocks that become unreachable by killing this one.
-   */
-  void KillUnreachable(MIRGraph* mir_graph);
+  void Kill(MIRGraph* mir_graph);
 
   /**
    * @brief Is ssa_reg the last SSA definition of that VR in the block?
@@ -546,8 +542,9 @@
                     uint32_t method_idx, jobject class_loader, const DexFile& dex_file);
 
   /* Find existing block */
-  BasicBlock* FindBlock(DexOffset code_offset) {
-    return FindBlock(code_offset, false, NULL);
+  BasicBlock* FindBlock(DexOffset code_offset,
+                        ScopedArenaVector<uint16_t>* dex_pc_to_block_map) {
+    return FindBlock(code_offset, false, nullptr, dex_pc_to_block_map);
   }
 
   const uint16_t* GetCurrentInsns() const {
@@ -560,9 +557,7 @@
    * This is guaranteed to contain index 0 which is the base method being compiled.
    * @return Returns the raw instruction pointer.
    */
-  const uint16_t* GetInsns(int m_unit_index) const {
-    return m_units_[m_unit_index]->GetCodeItem()->insns_;
-  }
+  const uint16_t* GetInsns(int m_unit_index) const;
 
   /**
    * @brief Used to obtain the raw data table.
@@ -575,7 +570,7 @@
   }
 
   unsigned int GetNumBlocks() const {
-    return num_blocks_;
+    return block_list_.size();
   }
 
   /**
@@ -705,7 +700,9 @@
 
   void DumpRegLocTable(RegLocation* table, int count);
 
+  void BasicBlockOptimizationStart();
   void BasicBlockOptimization();
+  void BasicBlockOptimizationEnd();
 
   const ArenaVector<BasicBlockId>& GetTopologicalSortOrder() {
     DCHECK(!topological_order_.empty());
@@ -731,6 +728,10 @@
     return max_nested_loops_;
   }
 
+  bool IsLoopHead(BasicBlockId bb_id) {
+    return topological_order_loop_ends_[topological_order_indexes_[bb_id]] != 0u;
+  }
+
   bool IsConst(int32_t s_reg) const {
     return is_constant_v_->IsBitSet(s_reg);
   }
@@ -969,13 +970,23 @@
     return reg_location_[method_sreg_];
   }
 
-  bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
-    return ((target_bb_id != NullBasicBlockId) &&
-            (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset));
+  bool IsBackEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
+    DCHECK_NE(target_bb_id, NullBasicBlockId);
+    DCHECK_LT(target_bb_id, topological_order_indexes_.size());
+    DCHECK_LT(branch_bb->id, topological_order_indexes_.size());
+    return topological_order_indexes_[target_bb_id] <= topological_order_indexes_[branch_bb->id];
   }
 
-  bool IsBackwardsBranch(BasicBlock* branch_bb) {
-    return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through);
+  bool IsSuspendCheckEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
+    if (!IsBackEdge(branch_bb, target_bb_id)) {
+      return false;
+    }
+    if (suspend_checks_in_loops_ == nullptr) {
+      // We didn't run suspend check elimination.
+      return true;
+    }
+    uint16_t target_depth = GetBasicBlock(target_bb_id)->nesting_depth;
+    return (suspend_checks_in_loops_[branch_bb->id] & (1u << (target_depth - 1u))) == 0;
   }
 
   void CountBranch(DexOffset target_offset) {
@@ -1055,6 +1066,9 @@
   bool ApplyGlobalValueNumberingGate();
   bool ApplyGlobalValueNumbering(BasicBlock* bb);
   void ApplyGlobalValueNumberingEnd();
+  bool EliminateSuspendChecksGate();
+  bool EliminateSuspendChecks(BasicBlock* bb);
+  void EliminateSuspendChecksEnd();
 
   uint16_t GetGvnIFieldId(MIR* mir) const {
     DCHECK(IsInstructionIGetOrIPut(mir->dalvikInsn.opcode));
@@ -1113,6 +1127,7 @@
   std::string GetSSANameWithConst(int ssa_reg, bool singles_only);
   void GetBlockName(BasicBlock* bb, char* name);
   const char* GetShortyFromTargetIdx(int);
+  const char* GetShortyFromMethodReference(const MethodReference& target_method);
   void DumpMIRGraph();
   CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range);
   BasicBlock* NewMemBB(BBType block_type, int block_id);
@@ -1165,7 +1180,7 @@
    * @brief Count the uses in the BasicBlock
    * @param bb the BasicBlock
    */
-  void CountUses(class BasicBlock* bb);
+  void CountUses(BasicBlock* bb);
 
   static uint64_t GetDataFlowAttributes(Instruction::Code opcode);
   static uint64_t GetDataFlowAttributes(MIR* mir);
@@ -1181,19 +1196,30 @@
   void AllocateSSAUseData(MIR *mir, int num_uses);
   void AllocateSSADefData(MIR *mir, int num_defs);
   void CalculateBasicBlockInformation();
-  void InitializeBasicBlockData();
   void ComputeDFSOrders();
   void ComputeDefBlockMatrix();
   void ComputeDominators();
   void CompilerInitializeSSAConversion();
   virtual void InitializeBasicBlockDataFlow();
-  void InsertPhiNodes();
+  void FindPhiNodeBlocks();
   void DoDFSPreOrderSSARename(BasicBlock* block);
 
   bool DfsOrdersUpToDate() const {
     return dfs_orders_up_to_date_;
   }
 
+  bool DominationUpToDate() const {
+    return domination_up_to_date_;
+  }
+
+  bool MirSsaRepUpToDate() const {
+    return mir_ssa_rep_up_to_date_;
+  }
+
+  bool TopologicalOrderUpToDate() const {
+    return topological_order_up_to_date_;
+  }
+
   /*
    * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
    * we can verify that all catch entries have native PC entries.
@@ -1209,20 +1235,6 @@
   void HandleSSADef(int* defs, int dalvik_reg, int reg_index);
   bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed);
 
-  // Used for removing redudant suspend tests
-  void AppendGenSuspendTestList(BasicBlock* bb) {
-    if (gen_suspend_test_list_.size() == 0 ||
-        gen_suspend_test_list_.back() != bb) {
-      gen_suspend_test_list_.push_back(bb);
-    }
-  }
-
-  /* This is used to check if there is already a method call dominating the
-   * source basic block of a backedge and being dominated by the target basic
-   * block of the backedge.
-   */
-  bool HasSuspendTestBetween(BasicBlock* source, BasicBlockId target_id);
-
  protected:
   int FindCommonParent(int block1, int block2);
   void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1,
@@ -1238,16 +1250,20 @@
   bool ContentIsInsn(const uint16_t* code_ptr);
   BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block,
                          BasicBlock** immed_pred_block_p);
-  BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p);
-  void ProcessTryCatchBlocks();
+  BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p,
+                        ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
+  void ProcessTryCatchBlocks(ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
   bool IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, NarrowDexOffset catch_offset);
   BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
-                               int flags, const uint16_t* code_ptr, const uint16_t* code_end);
+                               int flags, const uint16_t* code_ptr, const uint16_t* code_end,
+                               ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
   BasicBlock* ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
-                               int flags);
+                               int flags,
+                               ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
   BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
                               int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr,
-                              const uint16_t* code_end);
+                              const uint16_t* code_end,
+                              ScopedArenaVector<uint16_t>* dex_pc_to_block_map);
   int AddNewSReg(int v_reg);
   void HandleSSAUse(int* uses, int dalvik_reg, int reg_index);
   void DataFlowSSAFormat35C(MIR* mir);
@@ -1262,6 +1278,34 @@
   void ComputeDomPostOrderTraversal(BasicBlock* bb);
   int GetSSAUseCount(int s_reg);
   bool BasicBlockOpt(BasicBlock* bb);
+  void MultiplyAddOpt(BasicBlock* bb);
+
+  /**
+   * @brief Check whether the given MIR is possible to throw an exception.
+   * @param mir The mir to check.
+   * @return Returns 'true' if the given MIR might throw an exception.
+   */
+  bool CanThrow(MIR* mir);
+  /**
+   * @brief Combine multiply and add/sub MIRs into corresponding extended MAC MIR.
+   * @param mul_mir The multiply MIR to be combined.
+   * @param add_mir The add/sub MIR to be combined.
+   * @param mul_is_first_addend 'true' if multiply product is the first addend of add operation.
+   * @param is_wide 'true' if the operations are long type.
+   * @param is_sub 'true' if it is a multiply-subtract operation.
+   */
+  void CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend,
+                          bool is_wide, bool is_sub);
+  /*
+   * @brief Check whether the first MIR anti-depends on the second MIR.
+   * @details To check whether one of first MIR's uses of vregs is redefined by the second MIR,
+   * i.e. there is a write-after-read dependency.
+   * @param first The first MIR.
+   * @param second The second MIR.
+   * @param Returns true if there is a write-after-read dependency.
+   */
+  bool HasAntiDependency(MIR* first, MIR* second);
+
   bool BuildExtendedBBList(class BasicBlock* bb);
   bool FillDefBlockMatrix(BasicBlock* bb);
   void InitializeDominationInfo(BasicBlock* bb);
@@ -1290,6 +1334,9 @@
   unsigned int num_reachable_blocks_;
   unsigned int max_num_reachable_blocks_;
   bool dfs_orders_up_to_date_;
+  bool domination_up_to_date_;
+  bool mir_ssa_rep_up_to_date_;
+  bool topological_order_up_to_date_;
   ArenaVector<BasicBlockId> dfs_order_;
   ArenaVector<BasicBlockId> dfs_post_order_;
   ArenaVector<BasicBlockId> dom_post_order_traversal_;
@@ -1331,6 +1378,7 @@
       size_t num_vregs;
       ArenaBitVector* work_live_vregs;
       ArenaBitVector** def_block_matrix;  // num_vregs x num_blocks_.
+      ArenaBitVector** phi_node_blocks;  // num_vregs x num_blocks_.
     } ssa;
     // Global value numbering.
     struct {
@@ -1338,15 +1386,17 @@
       uint16_t* ifield_ids_;  // Part of GVN/LVN but cached here for LVN to avoid recalculation.
       uint16_t* sfield_ids_;  // Ditto.
     } gvn;
+    // Suspend check elimination.
+    struct {
+      DexFileMethodInliner* inliner;
+    } sce;
   } temp_;
   static const int kInvalidEntry = -1;
   ArenaVector<BasicBlock*> block_list_;
   ArenaBitVector* try_block_addr_;
   BasicBlock* entry_block_;
   BasicBlock* exit_block_;
-  unsigned int num_blocks_;
   const DexFile::CodeItem* current_code_item_;
-  ArenaVector<uint16_t> dex_pc_to_block_map_;    // FindBlock lookup cache.
   ArenaVector<DexCompilationUnit*> m_units_;     // List of methods included in this graph
   typedef std::pair<int, int> MIRLocation;       // Insert point, (m_unit_ index, offset)
   ArenaVector<MIRLocation> method_stack_;        // Include stack
@@ -1373,11 +1423,19 @@
   ArenaVector<MirIFieldLoweringInfo> ifield_lowering_infos_;
   ArenaVector<MirSFieldLoweringInfo> sfield_lowering_infos_;
   ArenaVector<MirMethodLoweringInfo> method_lowering_infos_;
+
+  // In the suspend check elimination pass we determine for each basic block and enclosing
+  // loop whether there's guaranteed to be a suspend check on the path from the loop head
+  // to this block. If so, we can eliminate the back-edge suspend check.
+  // The bb->id is index into suspend_checks_in_loops_ and the loop head's depth is bit index
+  // in a suspend_checks_in_loops_[bb->id].
+  uint32_t* suspend_checks_in_loops_;
+
   static const uint64_t oat_data_flow_attributes_[kMirOpLast];
-  ArenaVector<BasicBlock*> gen_suspend_test_list_;  // List of blocks containing suspend tests
 
   friend class MirOptimizationTest;
   friend class ClassInitCheckEliminationTest;
+  friend class SuspendCheckEliminationTest;
   friend class NullCheckEliminationTest;
   friend class GlobalValueNumberingTest;
   friend class LocalValueNumberingTest;
diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc
index a96cd84..b3ad040 100644
--- a/compiler/dex/mir_graph_test.cc
+++ b/compiler/dex/mir_graph_test.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "compiler_ir.h"
 #include "mir_graph.h"
 #include "gtest/gtest.h"
 
@@ -89,7 +90,6 @@
             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
       }
     }
-    cu_.mir_graph->num_blocks_ = count;
     ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
     cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
@@ -149,7 +149,7 @@
 
   TopologicalSortOrderTest()
       : pool_(),
-        cu_(&pool_) {
+        cu_(&pool_, kRuntimeISA, nullptr, nullptr) {
     cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
   }
 
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 55f2abc..8718191 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -15,8 +15,11 @@
  */
 
 #include "base/bit_vector-inl.h"
-#include "compiler_internals.h"
+#include "base/logging.h"
 #include "dataflow_iterator-inl.h"
+#include "dex_flags.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
 #include "global_value_numbering.h"
 #include "local_value_numbering.h"
 #include "mir_field_info.h"
@@ -35,6 +38,7 @@
 void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) {
   is_constant_v_->SetBit(ssa_reg);
   constant_values_[ssa_reg] = value;
+  reg_location_[ssa_reg].is_const = true;
 }
 
 void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
@@ -42,6 +46,8 @@
   is_constant_v_->SetBit(ssa_reg + 1);
   constant_values_[ssa_reg] = Low32Bits(value);
   constant_values_[ssa_reg + 1] = High32Bits(value);
+  reg_location_[ssa_reg].is_const = true;
+  reg_location_[ssa_reg + 1].is_const = true;
 }
 
 void MIRGraph::DoConstantPropagation(BasicBlock* bb) {
@@ -110,13 +116,14 @@
   BasicBlock* bb = *p_bb;
   if (mir != NULL) {
     mir = mir->next;
-    if (mir == NULL) {
+    while (mir == NULL) {
       bb = GetBasicBlock(bb->fall_through);
       if ((bb == NULL) || Predecessors(bb) != 1) {
-        mir = NULL;
+        // mir is null and we cannot proceed further.
+        break;
       } else {
-      *p_bb = bb;
-      mir = bb->first_mir_insn;
+        *p_bb = bb;
+        mir = bb->first_mir_insn;
       }
     }
   }
@@ -426,6 +433,10 @@
   if (bb->block_type == kDead) {
     return true;
   }
+  // Currently multiply-accumulate backend supports are only available on arm32 and arm64.
+  if (cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2) {
+    MultiplyAddOpt(bb);
+  }
   bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kLocalValueNumbering)) == 0u;
   std::unique_ptr<ScopedArenaAllocator> allocator;
   std::unique_ptr<GlobalValueNumbering> global_valnum;
@@ -481,9 +492,11 @@
             mir->ssa_rep->num_uses = 0;
             BasicBlock* successor_to_unlink = GetBasicBlock(edge_to_kill);
             successor_to_unlink->ErasePredecessor(bb->id);
-            if (successor_to_unlink->predecessors.empty()) {
-              successor_to_unlink->KillUnreachable(this);
-            }
+            // We have changed the graph structure.
+            dfs_orders_up_to_date_ = false;
+            domination_up_to_date_ = false;
+            topological_order_up_to_date_ = false;
+            // Keep MIR SSA rep, the worst that can happen is a Phi with just 1 input.
           }
           break;
         case Instruction::CMPL_FLOAT:
@@ -526,6 +539,9 @@
                 default: LOG(ERROR) << "Unexpected opcode: " << opcode;
               }
               mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+              // Clear use count of temp VR.
+              use_counts_[mir->ssa_rep->defs[0]] = 0;
+              raw_use_counts_[mir->ssa_rep->defs[0]] = 0;
               // Copy the SSA information that is relevant.
               mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses;
               mir_next->ssa_rep->uses = mir->ssa_rep->uses;
@@ -539,36 +555,13 @@
             }
           }
           break;
-        case Instruction::RETURN_VOID:
-        case Instruction::RETURN:
-        case Instruction::RETURN_WIDE:
-        case Instruction::RETURN_OBJECT:
-          if (bb->GetFirstNonPhiInsn() == mir) {
-            // This is a simple return BB. Eliminate suspend checks on predecessor back-edges.
-            for (BasicBlockId pred_id : bb->predecessors) {
-              BasicBlock* pred_bb = GetBasicBlock(pred_id);
-              DCHECK(pred_bb != nullptr);
-              if (IsBackedge(pred_bb, bb->id) && pred_bb->last_mir_insn != nullptr &&
-                  (IsInstructionIfCc(pred_bb->last_mir_insn->dalvikInsn.opcode) ||
-                   IsInstructionIfCcZ(pred_bb->last_mir_insn->dalvikInsn.opcode) ||
-                   IsInstructionGoto(pred_bb->last_mir_insn->dalvikInsn.opcode))) {
-                pred_bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
-                if (cu_->verbose) {
-                  LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex
-                            << pred_bb->last_mir_insn->offset;
-                }
-              }
-            }
-          }
-          break;
         default:
           break;
       }
       // Is this the select pattern?
       // TODO: flesh out support for Mips.  NOTE: llvm's select op doesn't quite work here.
       // TUNING: expand to support IF_xx compare & branches
-      if (!cu_->compiler->IsPortable() &&
-          (cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2 ||
+      if ((cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2 ||
            cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) &&
           IsInstructionIfCcZ(mir->dalvikInsn.opcode)) {
         BasicBlock* ft = GetBasicBlock(bb->fall_through);
@@ -589,12 +582,8 @@
         if ((tk_ft == NULL) && (ft_tk == NULL) && (tk_tk == ft_ft) &&
             (Predecessors(tk) == 1) && (Predecessors(ft) == 1)) {
           /*
-           * Okay - we have the basic diamond shape.  At the very least, we can eliminate the
-           * suspend check on the taken-taken branch back to the join point.
+           * Okay - we have the basic diamond shape.
            */
-          if (SelectKind(tk->last_mir_insn) == kSelectGoto) {
-              tk->last_mir_insn->optimization_flags |= (MIR_IGNORE_SUSPEND_CHECK);
-          }
 
           // TODO: Add logic for LONG.
           // Are the block bodies something we can handle?
@@ -669,36 +658,36 @@
                * Phi node only contains our two cases as input, we will use the result
                * SSA name of the Phi node as our select result and delete the Phi.  If
                * the Phi node has more than two operands, we will arbitrarily use the SSA
-               * name of the "true" path, delete the SSA name of the "false" path from the
+               * name of the "false" path, delete the SSA name of the "true" path from the
                * Phi node (and fix up the incoming arc list).
                */
               if (phi->ssa_rep->num_uses == 2) {
                 mir->ssa_rep->defs[0] = phi->ssa_rep->defs[0];
-                phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+                // Rather than changing the Phi to kMirOpNop, remove it completely.
+                // This avoids leaving other Phis after kMirOpNop (i.e. a non-Phi) insn.
+                tk_tk->RemoveMIR(phi);
+                int dead_false_def = if_false->ssa_rep->defs[0];
+                raw_use_counts_[dead_false_def] = use_counts_[dead_false_def] = 0;
               } else {
-                int dead_def = if_false->ssa_rep->defs[0];
-                int live_def = if_true->ssa_rep->defs[0];
+                int live_def = if_false->ssa_rep->defs[0];
                 mir->ssa_rep->defs[0] = live_def;
-                BasicBlockId* incoming = phi->meta.phi_incoming;
-                for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
-                  if (phi->ssa_rep->uses[i] == live_def) {
-                    incoming[i] = bb->id;
-                  }
-                }
-                for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
-                  if (phi->ssa_rep->uses[i] == dead_def) {
-                    int last_slot = phi->ssa_rep->num_uses - 1;
-                    phi->ssa_rep->uses[i] = phi->ssa_rep->uses[last_slot];
-                    incoming[i] = incoming[last_slot];
-                  }
-                }
               }
-              phi->ssa_rep->num_uses--;
-              bb->taken = NullBasicBlockId;
-              tk->block_type = kDead;
-              for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) {
-                tmir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
-              }
+              int dead_true_def = if_true->ssa_rep->defs[0];
+              raw_use_counts_[dead_true_def] = use_counts_[dead_true_def] = 0;
+              // We want to remove ft and tk and link bb directly to ft_ft. First, we need
+              // to update all Phi inputs correctly with UpdatePredecessor(ft->id, bb->id)
+              // since the live_def above comes from ft->first_mir_insn (if_false).
+              DCHECK(if_false == ft->first_mir_insn);
+              ft_ft->UpdatePredecessor(ft->id, bb->id);
+              // Correct the rest of the links between bb, ft and ft_ft.
+              ft->ErasePredecessor(bb->id);
+              ft->fall_through = NullBasicBlockId;
+              bb->fall_through = ft_ft->id;
+              // Now we can kill tk and ft.
+              tk->Kill(this);
+              ft->Kill(this);
+              // NOTE: DFS order, domination info and topological order are still usable
+              // despite the newly dead blocks.
             }
           }
         }
@@ -795,10 +784,6 @@
       break;
     }
     walker = prev;
-
-    if (walker->visited) {
-      break;
-    }
   }
   return false;
 }
@@ -812,43 +797,9 @@
     MIR* mir = bb->last_mir_insn;
     DCHECK(bb->first_mir_insn !=  nullptr);
 
-    // Grab the attributes from the paired opcode.
+    // Get the paired insn and check if it can still throw.
     MIR* throw_insn = mir->meta.throw_insn;
-    uint64_t df_attributes = GetDataFlowAttributes(throw_insn);
-
-    // Don't combine if the throw_insn can still throw NPE.
-    if ((df_attributes & DF_HAS_NULL_CHKS) != 0 &&
-        (throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
-      break;
-    }
-    // Now whitelist specific instructions.
-    bool ok = false;
-    if ((df_attributes & DF_IFIELD) != 0) {
-      // Combine only if fast, otherwise weird things can happen.
-      const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(throw_insn);
-      ok = (df_attributes & DF_DA)  ? field_info.FastGet() : field_info.FastPut();
-    } else if ((df_attributes & DF_SFIELD) != 0) {
-      // Combine only if fast, otherwise weird things can happen.
-      const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(throw_insn);
-      bool fast = ((df_attributes & DF_DA)  ? field_info.FastGet() : field_info.FastPut());
-      // Don't combine if the SGET/SPUT can call <clinit>().
-      bool clinit = !field_info.IsClassInitialized() &&
-          (throw_insn->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0;
-      ok = fast && !clinit;
-    } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) {
-      // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above.
-      DCHECK_NE(throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK, 0);
-      ok = ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
-    } else if ((throw_insn->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
-      // We can encounter a non-throwing insn here thanks to inlining or other optimizations.
-      ok = true;
-    } else if (throw_insn->dalvikInsn.opcode == Instruction::ARRAY_LENGTH ||
-        throw_insn->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA ||
-        static_cast<int>(throw_insn->dalvikInsn.opcode) == kMirOpNullCheck) {
-      // No more checks for these (null check was processed above).
-      ok = true;
-    }
-    if (!ok) {
+    if (CanThrow(throw_insn)) {
       break;
     }
 
@@ -887,9 +838,6 @@
           BasicBlock* succ_bb = GetBasicBlock(succ_info->block);
           DCHECK(succ_bb->catch_entry);
           succ_bb->ErasePredecessor(bb->id);
-          if (succ_bb->predecessors.empty()) {
-            succ_bb->KillUnreachable(this);
-          }
         }
       }
     }
@@ -932,8 +880,10 @@
       child->UpdatePredecessor(bb_next->id, bb->id);
     }
 
-    // DFS orders are not up to date anymore.
+    // DFS orders, domination and topological order are not up to date anymore.
     dfs_orders_up_to_date_ = false;
+    domination_up_to_date_ = false;
+    topological_order_up_to_date_ = false;
 
     // Now, loop back and see if we can keep going
   }
@@ -1605,7 +1555,7 @@
   return false;  // Not iterative - return value will be ignored
 }
 
-void MIRGraph::BasicBlockOptimization() {
+void MIRGraph::BasicBlockOptimizationStart() {
   if ((cu_->disable_opt & (1 << kLocalValueNumbering)) == 0) {
     temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
     temp_.gvn.ifield_ids_ =
@@ -1613,7 +1563,9 @@
     temp_.gvn.sfield_ids_ =
         GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_);
   }
+}
 
+void MIRGraph::BasicBlockOptimization() {
   if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) {
     ClearAllVisitedFlags();
     PreOrderDfsIterator iter2(this);
@@ -1630,11 +1582,318 @@
       BasicBlockOpt(bb);
     }
   }
+}
 
+void MIRGraph::BasicBlockOptimizationEnd() {
   // Clean up after LVN.
   temp_.gvn.ifield_ids_ = nullptr;
   temp_.gvn.sfield_ids_ = nullptr;
   temp_scoped_alloc_.reset();
 }
 
+bool MIRGraph::EliminateSuspendChecksGate() {
+  if ((cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 ||  // Disabled.
+      GetMaxNestedLoops() == 0u ||   // Nothing to do.
+      GetMaxNestedLoops() >= 32u ||  // Only 32 bits in suspend_checks_in_loops_[.].
+                                     // Exclude 32 as well to keep bit shifts well-defined.
+      !HasInvokes()) {               // No invokes to actually eliminate any suspend checks.
+    return false;
+  }
+  if (cu_->compiler_driver != nullptr && cu_->compiler_driver->GetMethodInlinerMap() != nullptr) {
+    temp_.sce.inliner =
+        cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
+  }
+  suspend_checks_in_loops_ = static_cast<uint32_t*>(
+      arena_->Alloc(GetNumBlocks() * sizeof(*suspend_checks_in_loops_), kArenaAllocMisc));
+  return true;
+}
+
+bool MIRGraph::EliminateSuspendChecks(BasicBlock* bb) {
+  if (bb->block_type != kDalvikByteCode) {
+    return false;
+  }
+  DCHECK_EQ(GetTopologicalSortOrderLoopHeadStack()->size(), bb->nesting_depth);
+  if (bb->nesting_depth == 0u) {
+    // Out of loops.
+    DCHECK_EQ(suspend_checks_in_loops_[bb->id], 0u);  // The array was zero-initialized.
+    return false;
+  }
+  uint32_t suspend_checks_in_loops = (1u << bb->nesting_depth) - 1u;  // Start with all loop heads.
+  bool found_invoke = false;
+  for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+    if (IsInstructionInvoke(mir->dalvikInsn.opcode) &&
+        (temp_.sce.inliner == nullptr ||
+         !temp_.sce.inliner->IsIntrinsic(mir->dalvikInsn.vB, nullptr))) {
+      // Non-intrinsic invoke, rely on a suspend point in the invoked method.
+      found_invoke = true;
+      break;
+    }
+  }
+  if (!found_invoke) {
+    // Intersect suspend checks from predecessors.
+    uint16_t bb_topo_idx = topological_order_indexes_[bb->id];
+    uint32_t pred_mask_union = 0u;
+    for (BasicBlockId pred_id : bb->predecessors) {
+      uint16_t pred_topo_idx = topological_order_indexes_[pred_id];
+      if (pred_topo_idx < bb_topo_idx) {
+        // Determine the loop depth of the predecessors relative to this block.
+        size_t pred_loop_depth = topological_order_loop_head_stack_.size();
+        while (pred_loop_depth != 0u &&
+            pred_topo_idx < topological_order_loop_head_stack_[pred_loop_depth - 1].first) {
+          --pred_loop_depth;
+        }
+        DCHECK_LE(pred_loop_depth, GetBasicBlock(pred_id)->nesting_depth);
+        uint32_t pred_mask = (1u << pred_loop_depth) - 1u;
+        // Intersect pred_mask bits in suspend_checks_in_loops with
+        // suspend_checks_in_loops_[pred_id].
+        uint32_t pred_loops_without_checks = pred_mask & ~suspend_checks_in_loops_[pred_id];
+        suspend_checks_in_loops = suspend_checks_in_loops & ~pred_loops_without_checks;
+        pred_mask_union |= pred_mask;
+      }
+    }
+    DCHECK_EQ(((1u << (IsLoopHead(bb->id) ? bb->nesting_depth - 1u: bb->nesting_depth)) - 1u),
+              pred_mask_union);
+    suspend_checks_in_loops &= pred_mask_union;
+  }
+  suspend_checks_in_loops_[bb->id] = suspend_checks_in_loops;
+  if (suspend_checks_in_loops == 0u) {
+    return false;
+  }
+  // Apply MIR_IGNORE_SUSPEND_CHECK if appropriate.
+  if (bb->taken != NullBasicBlockId) {
+    DCHECK(bb->last_mir_insn != nullptr);
+    DCHECK(IsInstructionIfCc(bb->last_mir_insn->dalvikInsn.opcode) ||
+           IsInstructionIfCcZ(bb->last_mir_insn->dalvikInsn.opcode) ||
+           IsInstructionGoto(bb->last_mir_insn->dalvikInsn.opcode) ||
+           (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) >= kMirOpFusedCmplFloat &&
+            static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) <= kMirOpFusedCmpLong));
+    if (!IsSuspendCheckEdge(bb, bb->taken) &&
+        (bb->fall_through == NullBasicBlockId || !IsSuspendCheckEdge(bb, bb->fall_through))) {
+      bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
+    }
+  } else if (bb->fall_through != NullBasicBlockId && IsSuspendCheckEdge(bb, bb->fall_through)) {
+    // We've got a fall-through suspend edge. Add an artificial GOTO to force suspend check.
+    MIR* mir = NewMIR();
+    mir->dalvikInsn.opcode = Instruction::GOTO;
+    mir->dalvikInsn.vA = 0;  // Branch offset.
+    mir->offset = GetBasicBlock(bb->fall_through)->start_offset;
+    mir->m_unit_index = current_method_;
+    mir->ssa_rep = reinterpret_cast<SSARepresentation*>(
+        arena_->Alloc(sizeof(SSARepresentation), kArenaAllocDFInfo));  // Zero-initialized.
+    bb->AppendMIR(mir);
+    std::swap(bb->fall_through, bb->taken);  // The fall-through has become taken.
+  }
+  return true;
+}
+
+void MIRGraph::EliminateSuspendChecksEnd() {
+  temp_.sce.inliner = nullptr;
+}
+
+bool MIRGraph::CanThrow(MIR* mir) {
+  if ((mir->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
+    return false;
+  }
+  const int opt_flags = mir->optimization_flags;
+  uint64_t df_attributes = GetDataFlowAttributes(mir);
+
+  // First, check if the insn can still throw NPE.
+  if (((df_attributes & DF_HAS_NULL_CHKS) != 0) && ((opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) {
+    return true;
+  }
+
+  // Now process specific instructions.
+  if ((df_attributes & DF_IFIELD) != 0) {
+    // The IGET/IPUT family. We have processed the IGET/IPUT null check above.
+    DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0);
+    // If not fast, weird things can happen and the insn can throw.
+    const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(mir);
+    bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut();
+    return !fast;
+  } else if ((df_attributes & DF_SFIELD) != 0) {
+    // The SGET/SPUT family. Check for potentially throwing class initialization.
+    // Also, if not fast, weird things can happen and the insn can throw.
+    const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
+    bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut();
+    bool is_class_initialized = field_info.IsClassInitialized() ||
+        ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0);
+    return !(fast && is_class_initialized);
+  } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) {
+    // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above.
+    DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0);
+    // Non-throwing only if range check has been eliminated.
+    return ((opt_flags & MIR_IGNORE_RANGE_CHECK) == 0);
+  } else if (mir->dalvikInsn.opcode == Instruction::ARRAY_LENGTH ||
+      mir->dalvikInsn.opcode == Instruction::FILL_ARRAY_DATA ||
+      static_cast<int>(mir->dalvikInsn.opcode) == kMirOpNullCheck) {
+    // No more checks for these (null check was processed above).
+    return false;
+  }
+  return true;
+}
+
+bool MIRGraph::HasAntiDependency(MIR* first, MIR* second) {
+  DCHECK(first->ssa_rep != nullptr);
+  DCHECK(second->ssa_rep != nullptr);
+  if ((second->ssa_rep->num_defs > 0) && (first->ssa_rep->num_uses > 0)) {
+    int vreg0 = SRegToVReg(second->ssa_rep->defs[0]);
+    int vreg1 = (second->ssa_rep->num_defs == 2) ?
+        SRegToVReg(second->ssa_rep->defs[1]) : INVALID_VREG;
+    for (int i = 0; i < first->ssa_rep->num_uses; i++) {
+      int32_t use = SRegToVReg(first->ssa_rep->uses[i]);
+      if (use == vreg0 || use == vreg1) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+void MIRGraph::CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend,
+                                  bool is_wide, bool is_sub) {
+  if (is_wide) {
+    if (is_sub) {
+      add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMsubLong);
+    } else {
+      add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMaddLong);
+    }
+  } else {
+    if (is_sub) {
+      add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMsubInt);
+    } else {
+      add_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpMaddInt);
+    }
+  }
+  add_mir->ssa_rep->num_uses = is_wide ? 6 : 3;
+  int32_t addend0 = INVALID_SREG;
+  int32_t addend1 = INVALID_SREG;
+  if (is_wide) {
+    addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[2] : add_mir->ssa_rep->uses[0];
+    addend1 = mul_is_first_addend ? add_mir->ssa_rep->uses[3] : add_mir->ssa_rep->uses[1];
+  } else {
+    addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[1] : add_mir->ssa_rep->uses[0];
+  }
+
+  AllocateSSAUseData(add_mir, add_mir->ssa_rep->num_uses);
+  add_mir->ssa_rep->uses[0] = mul_mir->ssa_rep->uses[0];
+  add_mir->ssa_rep->uses[1] = mul_mir->ssa_rep->uses[1];
+  // Clear the original multiply product ssa use count, as it is not used anymore.
+  raw_use_counts_[mul_mir->ssa_rep->defs[0]] = 0;
+  use_counts_[mul_mir->ssa_rep->defs[0]] = 0;
+  if (is_wide) {
+    DCHECK_EQ(add_mir->ssa_rep->num_uses, 6);
+    add_mir->ssa_rep->uses[2] = mul_mir->ssa_rep->uses[2];
+    add_mir->ssa_rep->uses[3] = mul_mir->ssa_rep->uses[3];
+    add_mir->ssa_rep->uses[4] = addend0;
+    add_mir->ssa_rep->uses[5] = addend1;
+    raw_use_counts_[mul_mir->ssa_rep->defs[1]] = 0;
+    use_counts_[mul_mir->ssa_rep->defs[1]] = 0;
+  } else {
+    DCHECK_EQ(add_mir->ssa_rep->num_uses, 3);
+    add_mir->ssa_rep->uses[2] = addend0;
+  }
+  // Copy in the decoded instruction information.
+  add_mir->dalvikInsn.vB = SRegToVReg(add_mir->ssa_rep->uses[0]);
+  if (is_wide) {
+    add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[2]);
+    add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[4]);
+  } else {
+    add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[1]);
+    add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[2]);
+  }
+  // Original multiply MIR is set to Nop.
+  mul_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+}
+
+void MIRGraph::MultiplyAddOpt(BasicBlock* bb) {
+  if (bb->block_type == kDead) {
+    return;
+  }
+  ScopedArenaAllocator allocator(&cu_->arena_stack);
+  ScopedArenaSafeMap<uint32_t, MIR*> ssa_mul_map(std::less<uint32_t>(), allocator.Adapter());
+  ScopedArenaSafeMap<uint32_t, MIR*>::iterator map_it;
+  for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+    Instruction::Code opcode = mir->dalvikInsn.opcode;
+    bool is_sub = true;
+    bool is_candidate_multiply = false;
+    switch (opcode) {
+      case Instruction::MUL_INT:
+      case Instruction::MUL_INT_2ADDR:
+        is_candidate_multiply = true;
+        break;
+      case Instruction::MUL_LONG:
+      case Instruction::MUL_LONG_2ADDR:
+        if (cu_->target64) {
+          is_candidate_multiply = true;
+        }
+        break;
+      case Instruction::ADD_INT:
+      case Instruction::ADD_INT_2ADDR:
+        is_sub = false;
+        FALLTHROUGH_INTENDED;
+      case Instruction::SUB_INT:
+      case Instruction::SUB_INT_2ADDR:
+        if (((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end()) && !is_sub) {
+          // a*b+c
+          CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */,
+                             false /* is_wide */, false /* is_sub */);
+          ssa_mul_map.erase(mir->ssa_rep->uses[0]);
+        } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[1])) != ssa_mul_map.end()) {
+          // c+a*b or c-a*b
+          CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */,
+                             false /* is_wide */, is_sub);
+          ssa_mul_map.erase(map_it);
+        }
+        break;
+      case Instruction::ADD_LONG:
+      case Instruction::ADD_LONG_2ADDR:
+        is_sub = false;
+        FALLTHROUGH_INTENDED;
+      case Instruction::SUB_LONG:
+      case Instruction::SUB_LONG_2ADDR:
+        if (!cu_->target64) {
+          break;
+        }
+        if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end() && !is_sub) {
+          // a*b+c
+          CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */,
+                             true /* is_wide */, false /* is_sub */);
+          ssa_mul_map.erase(map_it);
+        } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[2])) != ssa_mul_map.end()) {
+          // c+a*b or c-a*b
+          CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */,
+                             true /* is_wide */, is_sub);
+          ssa_mul_map.erase(map_it);
+        }
+        break;
+      default:
+        if (!ssa_mul_map.empty() && CanThrow(mir)) {
+          // Should not combine multiply and add MIRs across potential exception.
+          ssa_mul_map.clear();
+        }
+        break;
+    }
+
+    // Exclude the case when an MIR writes a vreg which is previous candidate multiply MIR's uses.
+    // It is because that current RA may allocate the same physical register to them. For this
+    // kind of cases, the multiplier has been updated, we should not use updated value to the
+    // multiply-add insn.
+    if (ssa_mul_map.size() > 0) {
+      for (auto it = ssa_mul_map.begin(); it != ssa_mul_map.end();) {
+        MIR* mul = it->second;
+        if (HasAntiDependency(mul, mir)) {
+          it = ssa_mul_map.erase(it);
+        } else {
+          ++it;
+        }
+      }
+    }
+
+    if (is_candidate_multiply &&
+        (GetRawUseCount(mir->ssa_rep->defs[0]) == 1) && (mir->next != nullptr)) {
+      ssa_mul_map.Put(mir->ssa_rep->defs[0], mir);
+    }
+  }
+}
+
 }  // namespace art
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index c794cc6..199bc27 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -16,9 +16,10 @@
 
 #include <vector>
 
-#include "compiler_internals.h"
+#include "base/logging.h"
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
+#include "dex/compiler_ir.h"
 #include "dex/mir_field_info.h"
 #include "gtest/gtest.h"
 
@@ -88,6 +89,8 @@
     { bb, opcode, 0u, vA, vB, vC }
 #define DEF_INVOKE(bb, opcode, vC, method_info) \
     { bb, opcode, method_info, 0u, 0u, vC }
+#define DEF_OTHER0(bb, opcode) \
+    { bb, opcode, 0u, 0u, 0u, 0u }
 #define DEF_OTHER1(bb, opcode, vA) \
     { bb, opcode, 0u, vA, 0u, 0u }
 #define DEF_OTHER2(bb, opcode, vA, vB) \
@@ -127,7 +130,6 @@
             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
       }
     }
-    cu_.mir_graph->num_blocks_ = count;
     ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
     cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
@@ -175,6 +177,56 @@
     PrepareBasicBlocks(bbs);
   }
 
+  void PrepareNestedLoopsWhile_While() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)),  // Outer while loop head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)),  // Inner while loop head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)),        // "taken" loops to inner head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)),        // "taken" loops to outer head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareNestedLoopsWhile_WhileWhile() {
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)),   // Outer while loop head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)),    // Inner while loop head 1.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)),          // Loops to inner head 1.
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)),    // Inner while loop head 2.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)),          // loops to inner head 2.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)),          // loops to outer head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
+  void PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge() {
+    // Extra edge from the first inner loop body to second inner loop body (6u->8u).
+    static const BBDef bbs[] = {
+        DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+        DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+        DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)),   // Outer while loop head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)),    // Inner while loop head 1.
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED1(5)),       // Loops to inner head 1.
+        DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)),    // Inner while loop head 2.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED2(7, 6)),       // loops to inner head 2.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)),          // loops to outer head.
+        DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+    };
+    PrepareBasicBlocks(bbs);
+  }
+
   void PrepareCatch() {
     static const BBDef bbs[] = {
         DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
@@ -275,7 +327,7 @@
 
   MirOptimizationTest()
       : pool_(),
-        cu_(&pool_),
+        cu_(&pool_, kRuntimeISA, nullptr, nullptr),
         mir_count_(0u),
         mirs_(nullptr),
         code_item_(nullptr) {
@@ -397,6 +449,43 @@
   }
 };
 
+class SuspendCheckEliminationTest : public MirOptimizationTest {
+ protected:
+  bool IsBackEdge(BasicBlockId branch_bb, BasicBlockId target_bb) {
+    BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb);
+    return target_bb != NullBasicBlockId && cu_.mir_graph->IsBackEdge(branch, target_bb);
+  }
+
+  bool IsSuspendCheckEdge(BasicBlockId branch_bb, BasicBlockId target_bb) {
+    BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb);
+    return cu_.mir_graph->IsSuspendCheckEdge(branch, target_bb);
+  }
+
+  void PerformSuspendCheckElimination() {
+    cu_.mir_graph->SSATransformationStart();
+    cu_.mir_graph->ComputeDFSOrders();
+    cu_.mir_graph->ComputeDominators();
+    cu_.mir_graph->ComputeTopologicalSortOrder();
+    cu_.mir_graph->SSATransformationEnd();
+    bool gate_result = cu_.mir_graph->EliminateSuspendChecksGate();
+    ASSERT_TRUE(gate_result);
+    TopologicalSortIterator iterator(cu_.mir_graph.get());
+    bool change = false;
+    for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+      change = cu_.mir_graph->EliminateSuspendChecks(bb);
+    }
+    cu_.mir_graph->EliminateSuspendChecksEnd();
+  }
+
+  SuspendCheckEliminationTest()
+      : MirOptimizationTest() {
+    static const MethodDef methods[] = {
+        { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false },  // Dummy.
+    };
+    PrepareMethods(methods);
+  }
+};
+
 TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
   static const SFieldDef sfields[] = {
       { 0u, 1u, 0u, 0u, kDexMemAccessWord },
@@ -882,7 +971,208 @@
   }
 }
 
-// Undefine MIR_DEF for null check elimination.
-#undef MIR_DEF
+TEST_F(SuspendCheckEliminationTest, LoopNoElimination) {
+  static const MIRDef mirs[] = {
+    DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u),  // Force the pass to run.
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge back.
+  };
+
+  PrepareLoop();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(4u, 4u));
+  EXPECT_TRUE(IsSuspendCheckEdge(4u, 4u));  // Suspend point on loop to self.
+}
+
+TEST_F(SuspendCheckEliminationTest, LoopElimination) {
+  static const MIRDef mirs[] = {
+    DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in the loop.
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge back.
+  };
+
+  PrepareLoop();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(4u, 4u));
+  EXPECT_FALSE(IsSuspendCheckEdge(4u, 4u));  // No suspend point on loop to self.
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_NoElimination) {
+  static const MIRDef mirs[] = {
+    DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u),  // Force the pass to run.
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_While();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(7u, 4u));
+  EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopHead) {
+  static const MIRDef mirs[] = {
+    DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in outer loop head.
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_While();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(7u, 4u));
+  EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopBody) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in outer loop body.
+    DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_While();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(7u, 4u));
+  EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopHead) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in inner loop head.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_While();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(7u, 4u));
+  EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopBody) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop.
+    DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in inner loop body.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER0(7u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_While();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(7u, 4u));
+  EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopHead) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in first inner loop head.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
+    DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
+    DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_WhileWhile();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(8u, 7u));
+  EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));
+  ASSERT_TRUE(IsBackEdge(9u, 4u));
+  EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopBody) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
+    DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in first inner loop body.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
+    DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
+    DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_WhileWhile();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(8u, 7u));
+  EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));
+  ASSERT_TRUE(IsBackEdge(9u, 4u));
+  EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInFirstInnerLoopBody) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
+    DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in first inner loop body.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
+    DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
+    DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(8u, 7u));
+  EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u));  // Unaffected by the extra edge.
+  ASSERT_TRUE(IsBackEdge(9u, 4u));
+  EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u));
+}
+
+TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInSecondInnerLoopHead) {
+  static const MIRDef mirs[] = {
+    DEF_OTHER1(4u, Instruction::IF_NEZ, 1u),             // Edge out of outer loop.
+    DEF_OTHER1(5u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 1.
+    DEF_OTHER0(6u, Instruction::GOTO),                   // Edge back to inner loop head.
+    DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u),  // Invoke in second inner loop head.
+    DEF_OTHER1(7u, Instruction::IF_NEZ, 2u),             // Edge out of inner loop 2.
+    DEF_OTHER0(8u, Instruction::GOTO),                   // Edge back to inner loop 2 head.
+    DEF_OTHER0(9u, Instruction::GOTO),                   // Edge back to outer loop head.
+  };
+
+  PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge();
+  PrepareMIRs(mirs);
+  PerformSuspendCheckElimination();
+  ASSERT_TRUE(IsBackEdge(6u, 5u));
+  EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u));
+  ASSERT_TRUE(IsBackEdge(8u, 7u));
+  EXPECT_FALSE(IsSuspendCheckEdge(8u, 7u));  // Unaffected by the extra edge.
+  ASSERT_TRUE(IsBackEdge(9u, 4u));
+  EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u));
+}
 
 }  // namespace art
diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h
index d3e54a0..0def056 100644
--- a/compiler/dex/pass.h
+++ b/compiler/dex/pass.h
@@ -19,14 +19,12 @@
 
 #include <string>
 
-#include "compiler_ir.h"
 #include "base/logging.h"
 
 namespace art {
 
 // Forward declarations.
 class BasicBlock;
-struct CompilationUnit;
 class Pass;
 
 // Empty Pass Data Class, can be extended by any pass extending the base Pass class.
@@ -89,21 +87,6 @@
     UNREACHABLE();
   }
 
-  static void BasePrintMessage(CompilationUnit* c_unit, const char* pass_name, const char* message, ...) {
-    // Check if we want to log something or not.
-    if (c_unit->print_pass) {
-      // Stringify the message.
-      va_list args;
-      va_start(args, message);
-      std::string stringified_message;
-      StringAppendV(&stringified_message, message, args);
-      va_end(args);
-
-      // Log the message and ensure to include pass name.
-      LOG(INFO) << pass_name << ": " << stringified_message;
-    }
-  }
-
  protected:
   /** @brief The pass name: used for searching for a pass when running a particular pass or debugging. */
   const char* const pass_name_;
diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h
index 8a3eae1..671bcec 100644
--- a/compiler/dex/pass_driver.h
+++ b/compiler/dex/pass_driver.h
@@ -18,22 +18,17 @@
 #define ART_COMPILER_DEX_PASS_DRIVER_H_
 
 #include <vector>
-#include "pass.h"
-#include "safe_map.h"
 
-// Forward Declarations.
-class Pass;
-class PassDriver;
+#include "base/logging.h"
+#include "pass.h"
+#include "pass_manager.h"
+
 namespace art {
-/**
- * @brief Helper function to create a single instance of a given Pass and can be shared across
- * the threads.
- */
-template <typename PassType>
-const Pass* GetPassInstance() {
-  static const PassType pass;
-  return &pass;
-}
+
+class Pass;
+class PassDataHolder;
+class PassDriver;
+class PassManager;
 
 // Empty holder for the constructor.
 class PassDriverDataHolder {
@@ -43,11 +38,11 @@
  * @class PassDriver
  * @brief PassDriver is the wrapper around all Pass instances in order to execute them
  */
-template <typename PassDriverType>
 class PassDriver {
  public:
-  explicit PassDriver() {
-    InitializePasses();
+  explicit PassDriver(const PassManager* const pass_manager) : pass_manager_(pass_manager) {
+    pass_list_ = *pass_manager_->GetDefaultPassList();
+    DCHECK(!pass_list_.empty());
   }
 
   virtual ~PassDriver() {
@@ -58,12 +53,12 @@
    */
   void InsertPass(const Pass* new_pass) {
     DCHECK(new_pass != nullptr);
-    DCHECK(new_pass->GetName() != nullptr && new_pass->GetName()[0] != 0);
+    DCHECK(new_pass->GetName() != nullptr);
+    DCHECK_NE(new_pass->GetName()[0], 0);
 
     // It is an error to override an existing pass.
     DCHECK(GetPass(new_pass->GetName()) == nullptr)
         << "Pass name " << new_pass->GetName() << " already used.";
-
     // Now add to the list.
     pass_list_.push_back(new_pass);
   }
@@ -74,7 +69,8 @@
    */
   virtual bool RunPass(const char* pass_name) {
     // Paranoid: c_unit cannot be nullptr and we need a pass name.
-    DCHECK(pass_name != nullptr && pass_name[0] != 0);
+    DCHECK(pass_name != nullptr);
+    DCHECK_NE(pass_name[0], 0);
 
     const Pass* cur_pass = GetPass(pass_name);
 
@@ -108,21 +104,6 @@
     return nullptr;
   }
 
-  static void CreateDefaultPassList(const std::string& disable_passes) {
-    // Insert each pass from g_passes into g_default_pass_list.
-    PassDriverType::g_default_pass_list.clear();
-    PassDriverType::g_default_pass_list.reserve(PassDriver<PassDriverType>::g_passes_size);
-    for (uint16_t i = 0; i < PassDriver<PassDriverType>::g_passes_size; ++i) {
-      const Pass* pass = PassDriver<PassDriverType>::g_passes[i];
-      // Check if we should disable this pass.
-      if (disable_passes.find(pass->GetName()) != std::string::npos) {
-        LOG(INFO) << "Skipping " << pass->GetName();
-      } else {
-        PassDriver<PassDriverType>::g_default_pass_list.push_back(pass);
-      }
-    }
-  }
-
   /**
    * @brief Run a pass using the Pass itself.
    * @param time_split do we want a time split request(default: false)?
@@ -130,57 +111,7 @@
    */
   virtual bool RunPass(const Pass* pass, bool time_split = false) = 0;
 
-  /**
-   * @brief Print the pass names of all the passes available.
-   */
-  static void PrintPassNames() {
-    LOG(INFO) << "Loop Passes are:";
-
-    for (const Pass* cur_pass : PassDriver<PassDriverType>::g_default_pass_list) {
-      LOG(INFO) << "\t-" << cur_pass->GetName();
-    }
-  }
-
-  /**
-   * @brief Gets the list of passes currently schedule to execute.
-   * @return pass_list_
-   */
-  std::vector<const Pass*>& GetPasses() {
-    return pass_list_;
-  }
-
-  static void SetPrintAllPasses() {
-    default_print_passes_ = true;
-  }
-
-  static void SetDumpPassList(const std::string& list) {
-    dump_pass_list_ = list;
-  }
-
-  static void SetPrintPassList(const std::string& list) {
-    print_pass_list_ = list;
-  }
-
-  /**
-   * @brief Used to set a string that contains the overridden pass options.
-   * @details An overridden pass option means that the pass uses this option
-   * instead of using its default option.
-   * @param s The string passed by user with overridden options. The string is in format
-   * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
-   */
-  static void SetOverriddenPassOptions(const std::string& s) {
-    overridden_pass_options_list_ = s;
-  }
-
-  void SetDefaultPasses() {
-    pass_list_ = PassDriver<PassDriverType>::g_default_pass_list;
-  }
-
  protected:
-  virtual void InitializePasses() {
-    SetDefaultPasses();
-  }
-
   /**
    * @brief Apply a patch: perform start/work/end functions.
    */
@@ -189,6 +120,7 @@
     DispatchPass(pass);
     pass->End(data);
   }
+
   /**
    * @brief Dispatch a patch.
    * Gives the ability to add logic when running the patch.
@@ -197,29 +129,11 @@
     UNUSED(pass);
   }
 
-  /** @brief List of passes: provides the order to execute the passes. */
+  /** @brief List of passes: provides the order to execute the passes.
+   *  Passes are owned by pass_manager_. */
   std::vector<const Pass*> pass_list_;
 
-  /** @brief The number of passes within g_passes.  */
-  static const uint16_t g_passes_size;
-
-  /** @brief The number of passes within g_passes.  */
-  static const Pass* const g_passes[];
-
-  /** @brief The default pass list is used to initialize pass_list_. */
-  static std::vector<const Pass*> g_default_pass_list;
-
-  /** @brief Do we, by default, want to be printing the log messages? */
-  static bool default_print_passes_;
-
-  /** @brief What are the passes we want to be printing the log messages? */
-  static std::string print_pass_list_;
-
-  /** @brief What are the passes we want to be dumping the CFG? */
-  static std::string dump_pass_list_;
-
-  /** @brief String of all options that should be overridden for selected passes */
-  static std::string overridden_pass_options_list_;
+  const PassManager* const pass_manager_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h
index 7bfaf82..94eef22 100644
--- a/compiler/dex/pass_driver_me.h
+++ b/compiler/dex/pass_driver_me.h
@@ -19,19 +19,25 @@
 
 #include <cstdlib>
 #include <cstring>
+
 #include "bb_optimizations.h"
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
+#include "dex_flags.h"
 #include "pass_driver.h"
+#include "pass_manager.h"
 #include "pass_me.h"
+#include "safe_map.h"
 
 namespace art {
 
-template <typename PassDriverType>
-class PassDriverME: public PassDriver<PassDriverType> {
+class PassManager;
+class PassManagerOptions;
+
+class PassDriverME: public PassDriver {
  public:
-  explicit PassDriverME(CompilationUnit* cu)
-      : pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") {
+  explicit PassDriverME(const PassManager* const pass_manager, CompilationUnit* cu)
+      : PassDriver(pass_manager), pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") {
         pass_me_data_holder_.bb = nullptr;
         pass_me_data_holder_.c_unit = cu;
   }
@@ -81,7 +87,7 @@
     }
   }
 
-  bool RunPass(const Pass* pass, bool time_split) {
+  bool RunPass(const Pass* pass, bool time_split) OVERRIDE {
     // Paranoid: c_unit and pass cannot be nullptr, and the pass should have a name
     DCHECK(pass != nullptr);
     DCHECK(pass->GetName() != nullptr && pass->GetName()[0] != 0);
@@ -95,15 +101,17 @@
 
     // First, work on determining pass verbosity.
     bool old_print_pass = c_unit->print_pass;
-    c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
-    const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
-    if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
+    c_unit->print_pass = pass_manager_->GetOptions().GetPrintAllPasses();
+    auto* const options = &pass_manager_->GetOptions();
+    const std::string& print_pass_list = options->GetPrintPassList();
+    if (!print_pass_list.empty() && strstr(print_pass_list.c_str(), pass->GetName()) != nullptr) {
       c_unit->print_pass = true;
     }
 
-    // Next, check if there are any overridden settings for the pass that change default configuration.
+    // Next, check if there are any overridden settings for the pass that change default
+    // configuration.
     c_unit->overridden_pass_options.clear();
-    FillOverriddenPassSettings(pass->GetName(), c_unit->overridden_pass_options);
+    FillOverriddenPassSettings(options, pass->GetName(), c_unit->overridden_pass_options);
     if (c_unit->print_pass) {
       for (auto setting_it : c_unit->overridden_pass_options) {
         LOG(INFO) << "Overridden option \"" << setting_it.first << ":"
@@ -117,13 +125,12 @@
       // Applying the pass: first start, doWork, and end calls.
       this->ApplyPass(&pass_me_data_holder_, pass);
 
-      bool should_dump = ((c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0);
+      bool should_dump = (c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0;
 
-      const char* dump_pass_list = PassDriver<PassDriverType>::dump_pass_list_.c_str();
-
-      if (dump_pass_list != nullptr) {
-        bool found = strstr(dump_pass_list, pass->GetName());
-        should_dump = (should_dump || found);
+      const std::string& dump_pass_list = pass_manager_->GetOptions().GetDumpPassList();
+      if (!dump_pass_list.empty()) {
+        const bool found = strstr(dump_pass_list.c_str(), pass->GetName());
+        should_dump = should_dump || found;
       }
 
       if (should_dump) {
@@ -153,22 +160,23 @@
     return should_apply_pass;
   }
 
-  const char* GetDumpCFGFolder() const {
-    return dump_cfg_folder_;
-  }
-
-  static void PrintPassOptions() {
-    for (auto pass : PassDriver<PassDriverType>::g_default_pass_list) {
+  static void PrintPassOptions(PassManager* manager) {
+    for (const auto* pass : *manager->GetDefaultPassList()) {
       const PassME* me_pass = down_cast<const PassME*>(pass);
       if (me_pass->HasOptions()) {
         LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:";
-        SafeMap<const std::string, int> overridden_settings;
-        FillOverriddenPassSettings(me_pass->GetName(), overridden_settings);
+        SafeMap<const std::string, const OptionContent> overridden_settings;
+        FillOverriddenPassSettings(&manager->GetOptions(), me_pass->GetName(),
+                                   overridden_settings);
         me_pass->PrintPassOptions(overridden_settings);
       }
     }
   }
 
+  const char* GetDumpCFGFolder() const {
+    return dump_cfg_folder_;
+  }
+
  protected:
   /** @brief The data holder that contains data needed for the PassDriverME. */
   PassMEDataHolder pass_me_data_holder_;
@@ -197,12 +205,15 @@
     }
 
   /**
-   * @brief Fills the settings_to_fill by finding all of the applicable options in the overridden_pass_options_list_.
+   * @brief Fills the settings_to_fill by finding all of the applicable options in the
+   * overridden_pass_options_list_.
    * @param pass_name The pass name for which to fill settings.
-   * @param settings_to_fill Fills the options to contain the mapping of name of option to the new configuration.
+   * @param settings_to_fill Fills the options to contain the mapping of name of option to the new
+   * configuration.
    */
-  static void FillOverriddenPassSettings(const char* pass_name, SafeMap<const std::string, int>& settings_to_fill) {
-    const std::string& settings = PassDriver<PassDriverType>::overridden_pass_options_list_;
+  static void FillOverriddenPassSettings(const PassManagerOptions* options, const char* pass_name,
+                                         SafeMap<const std::string, const OptionContent>& settings_to_fill) {
+    const std::string& settings = options->GetOverriddenPassOptions();
     const size_t settings_len = settings.size();
 
     // Before anything, check if we care about anything right now.
@@ -274,15 +285,28 @@
           continue;
       }
 
-      // Get the actual setting itself. Strtol is being used to convert because it is
-      // exception safe. If the input is not sane, it will set a setting of 0.
-      std::string setting_string = settings.substr(setting_pos, next_configuration_separator - setting_pos);
-      int setting = std::strtol(setting_string.c_str(), 0, 0);
+      // Get the actual setting itself.
+      std::string setting_string =
+          settings.substr(setting_pos, next_configuration_separator - setting_pos);
 
-      std::string setting_name = settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
+      std::string setting_name =
+          settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
 
-      settings_to_fill.Put(setting_name, setting);
+      // We attempt to convert the option value to integer. Strtoll is being used to
+      // convert because it is exception safe.
+      char* end_ptr = nullptr;
+      const char* setting_ptr = setting_string.c_str();
+      DCHECK(setting_ptr != nullptr);  // Paranoid: setting_ptr must be a valid pointer.
+      int64_t int_value = strtoll(setting_ptr, &end_ptr, 0);
+      DCHECK(end_ptr != nullptr);  // Paranoid: end_ptr must be set by the strtoll call.
 
+      // If strtoll call succeeded, the option is now considered as integer.
+      if (*setting_ptr != '\0' && end_ptr != setting_ptr && *end_ptr == '\0') {
+        settings_to_fill.Put(setting_name, OptionContent(int_value));
+      } else {
+        // Otherwise, it is considered as a string.
+        settings_to_fill.Put(setting_name, OptionContent(setting_string.c_str()));
+      }
       search_pos = next_configuration_separator;
     } while (true);
   }
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index a2bf8b4..8c8bde6 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -14,81 +14,51 @@
  * limitations under the License.
  */
 
+#include "pass_driver_me_opts.h"
+
+#include "base/logging.h"
 #include "base/macros.h"
 #include "bb_optimizations.h"
-#include "compiler_internals.h"
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
 #include "pass_driver_me_opts.h"
+#include "pass_manager.h"
 #include "post_opt_passes.h"
 
 namespace art {
 
-/*
- * Create the pass list. These passes are immutable and are shared across the threads.
- *
- * Advantage is that there will be no race conditions here.
- * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
- *   - This is not yet an issue: no current pass would require it.
- */
-// The initial list of passes to be used by the PassDriveMEOpts.
-template<>
-const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = {
-  GetPassInstance<CacheFieldLoweringInfo>(),
-  GetPassInstance<CacheMethodLoweringInfo>(),
-  GetPassInstance<CalculatePredecessors>(),
-  GetPassInstance<DFSOrders>(),
-  GetPassInstance<ClassInitCheckElimination>(),
-  GetPassInstance<SpecialMethodInliner>(),
-  GetPassInstance<NullCheckElimination>(),
-  GetPassInstance<BBCombine>(),
-  GetPassInstance<CodeLayout>(),
-  GetPassInstance<TypeInference>(),
-  GetPassInstance<GlobalValueNumberingPass>(),
-  GetPassInstance<BBOptimizations>(),
-};
-
-// The number of the passes in the initial list of Passes (g_passes).
-template<>
-uint16_t const PassDriver<PassDriverMEOpts>::g_passes_size =
-    arraysize(PassDriver<PassDriverMEOpts>::g_passes);
-
-// The default pass list is used by the PassDriverME instance of PassDriver
-// to initialize pass_list_.
-template<>
-std::vector<const Pass*> PassDriver<PassDriverMEOpts>::g_default_pass_list(
-    PassDriver<PassDriverMEOpts>::g_passes,
-    PassDriver<PassDriverMEOpts>::g_passes +
-    PassDriver<PassDriverMEOpts>::g_passes_size);
-
-// By default, do not have a dump pass list.
-template<>
-std::string PassDriver<PassDriverMEOpts>::dump_pass_list_ = std::string();
-
-// By default, do not have a print pass list.
-template<>
-std::string PassDriver<PassDriverMEOpts>::print_pass_list_ = std::string();
-
-// By default, we do not print the pass' information.
-template<>
-bool PassDriver<PassDriverMEOpts>::default_print_passes_ = false;
-
-// By default, there are no overridden pass settings.
-template<>
-std::string PassDriver<PassDriverMEOpts>::overridden_pass_options_list_ = std::string();
+void PassDriverMEOpts::SetupPasses(PassManager* pass_manager) {
+  /*
+   * Create the pass list. These passes are immutable and are shared across the threads.
+   *
+   * Advantage is that there will be no race conditions here.
+   * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
+   *   - This is not yet an issue: no current pass would require it.
+   */
+  pass_manager->AddPass(new CacheFieldLoweringInfo);
+  pass_manager->AddPass(new CacheMethodLoweringInfo);
+  pass_manager->AddPass(new CalculatePredecessors);
+  pass_manager->AddPass(new DFSOrders);
+  pass_manager->AddPass(new ClassInitCheckElimination);
+  pass_manager->AddPass(new SpecialMethodInliner);
+  pass_manager->AddPass(new NullCheckElimination);
+  pass_manager->AddPass(new BBCombine);
+  pass_manager->AddPass(new CodeLayout);
+  pass_manager->AddPass(new GlobalValueNumberingPass);
+  pass_manager->AddPass(new ConstantPropagation);
+  pass_manager->AddPass(new MethodUseCount);
+  pass_manager->AddPass(new BBOptimizations);
+  pass_manager->AddPass(new SuspendCheckElimination);
+}
 
 void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
-  const PassME* pass_me = down_cast<const PassME*> (pass);
+  const PassME* const pass_me = down_cast<const PassME*>(pass);
   DCHECK(pass_me != nullptr);
-
-  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
-
+  PassMEDataHolder* const pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
   // Set to dirty.
   pass_me_data_holder->dirty = true;
-
   // First call the base class' version.
   PassDriver::ApplyPass(data, pass);
-
   // Now we care about flags.
   if ((pass_me->GetFlag(kOptimizationBasicBlockChange) == true) ||
       (pass_me->GetFlag(kOptimizationDefUsesChange) == true)) {
diff --git a/compiler/dex/pass_driver_me_opts.h b/compiler/dex/pass_driver_me_opts.h
index 0a5b5ae..b930d02 100644
--- a/compiler/dex/pass_driver_me_opts.h
+++ b/compiler/dex/pass_driver_me_opts.h
@@ -25,19 +25,26 @@
 struct CompilationUnit;
 class Pass;
 class PassDataHolder;
+class PassManager;
 
-class PassDriverMEOpts : public PassDriverME<PassDriverMEOpts> {
+class PassDriverMEOpts : public PassDriverME {
  public:
-  explicit PassDriverMEOpts(CompilationUnit* cu):PassDriverME<PassDriverMEOpts>(cu) {
+  explicit PassDriverMEOpts(const PassManager* const manager, CompilationUnit* cu)
+      : PassDriverME(manager, cu) {
   }
 
   ~PassDriverMEOpts() {
   }
 
   /**
+   * @brief Write and allocate corresponding passes into the pass manager.
+   */
+  static void SetupPasses(PassManager* pass_manasger);
+
+  /**
    * @brief Apply a patch: perform start/work/end functions.
    */
-  virtual void ApplyPass(PassDataHolder* data, const Pass* pass);
+  virtual void ApplyPass(PassDataHolder* data, const Pass* pass) OVERRIDE;
 };
 
 }  // namespace art
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index e6238e9..a8b8a54 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -14,66 +14,35 @@
  * limitations under the License.
  */
 
+#include "pass_driver_me_post_opt.h"
+
 #include "base/macros.h"
 #include "post_opt_passes.h"
-#include "compiler_internals.h"
-#include "pass_driver_me_post_opt.h"
+#include "pass_manager.h"
 
 namespace art {
 
-/*
- * Create the pass list. These passes are immutable and are shared across the threads.
- *
- * Advantage is that there will be no race conditions here.
- * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
- *   - This is not yet an issue: no current pass would require it.
- */
-// The initial list of passes to be used by the PassDriveMEPostOpt.
-template<>
-const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = {
-  GetPassInstance<InitializeData>(),
-  GetPassInstance<ClearPhiInstructions>(),
-  GetPassInstance<DFSOrders>(),
-  GetPassInstance<BuildDomination>(),
-  GetPassInstance<TopologicalSortOrders>(),
-  GetPassInstance<DefBlockMatrix>(),
-  GetPassInstance<CreatePhiNodes>(),
-  GetPassInstance<ClearVisitedFlag>(),
-  GetPassInstance<SSAConversion>(),
-  GetPassInstance<PhiNodeOperands>(),
-  GetPassInstance<ConstantPropagation>(),
-  GetPassInstance<PerformInitRegLocations>(),
-  GetPassInstance<MethodUseCount>(),
-  GetPassInstance<FreeData>(),
-};
-
-// The number of the passes in the initial list of Passes (g_passes).
-template<>
-uint16_t const PassDriver<PassDriverMEPostOpt>::g_passes_size =
-    arraysize(PassDriver<PassDriverMEPostOpt>::g_passes);
-
-// The default pass list is used by the PassDriverME instance of PassDriver
-// to initialize pass_list_.
-template<>
-std::vector<const Pass*> PassDriver<PassDriverMEPostOpt>::g_default_pass_list(
-    PassDriver<PassDriverMEPostOpt>::g_passes,
-    PassDriver<PassDriverMEPostOpt>::g_passes +
-    PassDriver<PassDriverMEPostOpt>::g_passes_size);
-
-// By default, do not have a dump pass list.
-template<>
-std::string PassDriver<PassDriverMEPostOpt>::dump_pass_list_ = std::string();
-
-// By default, do not have a print pass list.
-template<>
-std::string PassDriver<PassDriverMEPostOpt>::print_pass_list_ = std::string();
-
-// By default, we do not print the pass' information.
-template<>
-bool PassDriver<PassDriverMEPostOpt>::default_print_passes_ = false;
-
-// By default, there are no overridden pass settings.
-template<>
-std::string PassDriver<PassDriverMEPostOpt>::overridden_pass_options_list_ = std::string();
+void PassDriverMEPostOpt::SetupPasses(PassManager* pass_manager) {
+  /*
+   * Create the pass list. These passes are immutable and are shared across the threads.
+   *
+   * Advantage is that there will be no race conditions here.
+   * Disadvantage is the passes can't change their internal states depending on CompilationUnit:
+   *   - This is not yet an issue: no current pass would require it.
+   */
+  // The initial list of passes to be used by the PassDriveMEPostOpt.
+  pass_manager->AddPass(new DFSOrders);
+  pass_manager->AddPass(new BuildDomination);
+  pass_manager->AddPass(new TopologicalSortOrders);
+  pass_manager->AddPass(new InitializeSSATransformation);
+  pass_manager->AddPass(new ClearPhiInstructions);
+  pass_manager->AddPass(new DefBlockMatrix);
+  pass_manager->AddPass(new FindPhiNodeBlocksPass);
+  pass_manager->AddPass(new SSAConversion);
+  pass_manager->AddPass(new PhiNodeOperands);
+  pass_manager->AddPass(new PerformInitRegLocations);
+  pass_manager->AddPass(new TypeInference);
+  pass_manager->AddPass(new FinishSSATransformation);
+}
 
 }  // namespace art
diff --git a/compiler/dex/pass_driver_me_post_opt.h b/compiler/dex/pass_driver_me_post_opt.h
index 574a6ba..9e03c4e 100644
--- a/compiler/dex/pass_driver_me_post_opt.h
+++ b/compiler/dex/pass_driver_me_post_opt.h
@@ -26,13 +26,19 @@
 class Pass;
 class PassDataHolder;
 
-class PassDriverMEPostOpt : public PassDriverME<PassDriverMEPostOpt> {
+class PassDriverMEPostOpt : public PassDriverME {
  public:
-  explicit PassDriverMEPostOpt(CompilationUnit* cu) : PassDriverME<PassDriverMEPostOpt>(cu) {
+  explicit PassDriverMEPostOpt(const PassManager* const manager, CompilationUnit* cu)
+      : PassDriverME(manager, cu) {
   }
 
   ~PassDriverMEPostOpt() {
   }
+
+  /**
+   * @brief Write and allocate corresponding passes into the pass manager.
+   */
+  static void SetupPasses(PassManager* pass_manager);
 };
 
 }  // namespace art
diff --git a/compiler/dex/pass_manager.cc b/compiler/dex/pass_manager.cc
new file mode 100644
index 0000000..6d58f65
--- /dev/null
+++ b/compiler/dex/pass_manager.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pass_manager.h"
+
+#include "base/stl_util.h"
+#include "pass_me.h"
+
+namespace art {
+
+PassManager::PassManager(const PassManagerOptions& options) : options_(options) {
+}
+
+PassManager::~PassManager() {
+  STLDeleteElements(&passes_);
+}
+
+void PassManager::CreateDefaultPassList() {
+  default_pass_list_.clear();
+  // Add each pass which isn't disabled into default_pass_list_.
+  for (const auto* pass : passes_) {
+    if (options_.GetDisablePassList().find(pass->GetName()) != std::string::npos) {
+      LOG(INFO) << "Skipping disabled pass " << pass->GetName();
+    } else {
+      default_pass_list_.push_back(pass);
+    }
+  }
+}
+
+void PassManager::PrintPassNames() const {
+  LOG(INFO) << "Loop Passes are:";
+  for (const Pass* cur_pass : default_pass_list_) {
+    LOG(INFO) << "\t-" << cur_pass->GetName();
+  }
+}
+
+}  // namespace art
diff --git a/compiler/dex/pass_manager.h b/compiler/dex/pass_manager.h
new file mode 100644
index 0000000..68e488d
--- /dev/null
+++ b/compiler/dex/pass_manager.h
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DEX_PASS_MANAGER_H_
+#define ART_COMPILER_DEX_PASS_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace art {
+
+class Pass;
+
+class PassManagerOptions {
+ public:
+  PassManagerOptions()
+     : default_print_passes_(false),
+       print_pass_names_(false),
+       print_pass_options_(false) {
+  }
+  explicit PassManagerOptions(const PassManagerOptions&) = default;
+
+  void SetPrintPassNames(bool b) {
+    print_pass_names_ = b;
+  }
+
+  void SetPrintAllPasses() {
+    default_print_passes_ = true;
+  }
+  bool GetPrintAllPasses() const {
+    return default_print_passes_;
+  }
+
+  void SetDisablePassList(const std::string& list) {
+    disable_pass_list_ = list;
+  }
+  const std::string& GetDisablePassList() const {
+    return disable_pass_list_;
+  }
+
+  void SetPrintPassList(const std::string& list) {
+    print_pass_list_ = list;
+  }
+  const std::string& GetPrintPassList() const {
+    return print_pass_list_;
+  }
+
+  void SetDumpPassList(const std::string& list) {
+    dump_pass_list_ = list;
+  }
+  const std::string& GetDumpPassList() const {
+    return dump_pass_list_;
+  }
+
+  /**
+   * @brief Used to set a string that contains the overridden pass options.
+   * @details An overridden pass option means that the pass uses this option
+   * instead of using its default option.
+   * @param s The string passed by user with overridden options. The string is in format
+   * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
+   */
+  void SetOverriddenPassOptions(const std::string& list) {
+    overridden_pass_options_list_ = list;
+  }
+  const std::string& GetOverriddenPassOptions() const {
+    return overridden_pass_options_list_;
+  }
+
+  void SetPrintPassOptions(bool b) {
+    print_pass_options_ = b;
+  }
+  bool GetPrintPassOptions() const {
+    return print_pass_options_;
+  }
+
+ private:
+  /** @brief Do we, by default, want to be printing the log messages? */
+  bool default_print_passes_;
+
+  /** @brief What are the passes we want to be printing the log messages? */
+  std::string print_pass_list_;
+
+  /** @brief What are the passes we want to be dumping the CFG? */
+  std::string dump_pass_list_;
+
+  /** @brief String of all options that should be overridden for selected passes */
+  std::string overridden_pass_options_list_;
+
+  /** @brief String of all options that should be overridden for selected passes */
+  std::string disable_pass_list_;
+
+  /** @brief Whether or not we print all the passes when we create the pass manager */
+  bool print_pass_names_;
+
+  /** @brief Whether or not we print all the pass options when we create the pass manager */
+  bool print_pass_options_;
+};
+
+/**
+ * @class PassManager
+ * @brief Owns passes
+ */
+class PassManager {
+ public:
+  explicit PassManager(const PassManagerOptions& options);
+  virtual ~PassManager();
+  void CreateDefaultPassList();
+  void AddPass(const Pass* pass) {
+    passes_.push_back(pass);
+  }
+  /**
+   * @brief Print the pass names of all the passes available.
+   */
+  void PrintPassNames() const;
+  const std::vector<const Pass*>* GetDefaultPassList() const {
+    return &default_pass_list_;
+  }
+  const PassManagerOptions& GetOptions() const {
+    return options_;
+  }
+
+ private:
+  /** @brief The set of possible passes.  */
+  std::vector<const Pass*> passes_;
+
+  /** @brief The default pass list is used to initialize pass_list_. */
+  std::vector<const Pass*> default_pass_list_;
+
+  /** @brief Pass manager options. */
+  PassManagerOptions options_;
+
+  DISALLOW_COPY_AND_ASSIGN(PassManager);
+};
+}  // namespace art
+#endif  // ART_COMPILER_DEX_PASS_MANAGER_H_
diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h
index d0b450a..d3cf393 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -18,14 +18,17 @@
 #define ART_COMPILER_DEX_PASS_ME_H_
 
 #include <string>
+
+#include "base/logging.h"
 #include "pass.h"
+#include "compiler_ir.h"
+#include "safe_map.h"
 
 namespace art {
 
 // Forward declarations.
 class BasicBlock;
 struct CompilationUnit;
-class Pass;
 
 /**
  * @brief OptimizationFlag is an enumeration to perform certain tasks for a given pass.
@@ -40,11 +43,11 @@
 
 // Data holder class.
 class PassMEDataHolder: public PassDataHolder {
-  public:
-    CompilationUnit* c_unit;
-    BasicBlock* bb;
-    void* data;               /**< @brief Any data the pass wants to use */
-    bool dirty;               /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */
+ public:
+  CompilationUnit* c_unit;
+  BasicBlock* bb;
+  void* data;               /**< @brief Any data the pass wants to use */
+  bool dirty;               /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */
 };
 
 enum DataFlowAnalysisMode {
@@ -101,8 +104,8 @@
    * @details The printing is done using LOG(INFO).
    */
   void PrintPassDefaultOptions() const {
-    for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
-      LOG(INFO) << "\t" << option_it->first << ":" << std::dec << option_it->second;
+    for (const auto& option : default_options_) {
+      LOG(INFO) << "\t" << option.first << ":" << option.second;
     }
   }
 
@@ -110,25 +113,49 @@
    * @brief Prints the pass options along with either default or overridden setting.
    * @param overridden_options The overridden settings for this pass.
    */
-  void PrintPassOptions(SafeMap<const std::string, int>& overridden_options) const {
+  void PrintPassOptions(SafeMap<const std::string, const OptionContent>& overridden_options) const {
     // We walk through the default options only to get the pass names. We use GetPassOption to
     // also consider the overridden ones.
-    for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
-      LOG(INFO) << "\t" << option_it->first << ":" << std::dec << GetPassOption(option_it->first, overridden_options);
+    for (const auto& option : default_options_) {
+      LOG(INFO) << "\t" << option.first << ":"
+                << GetPassOption(option.first, overridden_options);
     }
   }
 
   /**
-   * @brief Used to obtain the option for a pass.
-   * @details Will return the overridden option if it exists or default one.
+   * @brief Used to obtain the option structure for a pass.
+   * @details Will return the overridden option if it exists or default one otherwise.
    * @param option_name The name of option whose setting to look for.
    * @param c_unit The compilation unit currently being handled.
-   * @return Returns the setting for the pass option.
-   */
-  int GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
+   * @return Returns the option structure containing the option value.
+  */
+  const OptionContent& GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
     return GetPassOption(option_name, c_unit->overridden_pass_options);
   }
 
+  /**
+   * @brief Used to obtain the option for a pass as a string.
+   * @details Will return the overridden option if it exists or default one otherwise.
+   * It will return nullptr if the required option value is not a string.
+   * @param option_name The name of option whose setting to look for.
+   * @param c_unit The compilation unit currently being handled.
+   * @return Returns the overridden option if it exists or the default one otherwise.
+  */
+  const char* GetStringPassOption(const char* option_name, CompilationUnit* c_unit) const {
+    return GetStringPassOption(option_name, c_unit->overridden_pass_options);
+  }
+
+  /**
+    * @brief Used to obtain the pass option value as an integer.
+    * @details Will return the overridden option if it exists or default one otherwise.
+    * It will return 0 if the required option value is not an integer.
+    * @param c_unit The compilation unit currently being handled.
+    * @return Returns the overriden option if it exists or the default one otherwise.
+   */
+  int64_t GetIntegerPassOption(const char* option_name, CompilationUnit* c_unit) const {
+    return GetIntegerPassOption(option_name, c_unit->overridden_pass_options);
+  }
+
   const char* GetDumpCFGFolder() const {
     return dump_cfg_folder_;
   }
@@ -138,29 +165,51 @@
   }
 
  protected:
-  int GetPassOption(const char* option_name, const SafeMap<const std::string, int>& overridden_options) const {
+  const OptionContent& GetPassOption(const char* option_name,
+        const SafeMap<const std::string, const OptionContent>& overridden_options) const {
+    DCHECK(option_name != nullptr);
+
     // First check if there are any overridden settings.
     auto overridden_it = overridden_options.find(std::string(option_name));
     if (overridden_it != overridden_options.end()) {
       return overridden_it->second;
+    } else {
+      // Otherwise, there must be a default value for this option name.
+      auto default_it = default_options_.find(option_name);
+      // An invalid option is being requested.
+      if (default_it == default_options_.end()) {
+        LOG(FATAL) << "Fatal: Cannot find an option named \"" << option_name << "\"";
+      }
+
+      return default_it->second;
+    }
+  }
+
+  const char* GetStringPassOption(const char* option_name,
+        const SafeMap<const std::string, const OptionContent>& overridden_options) const {
+    const OptionContent& option_content = GetPassOption(option_name, overridden_options);
+    if (option_content.type != OptionContent::kString) {
+      return nullptr;
     }
 
-    // Next check the default options.
-    auto default_it = default_options_.find(option_name);
+    return option_content.GetString();
+  }
 
-    if (default_it == default_options_.end()) {
-      // An invalid option is being requested.
-      DCHECK(false);
+  int64_t GetIntegerPassOption(const char* option_name,
+          const SafeMap<const std::string, const OptionContent>& overridden_options) const {
+    const OptionContent& option_content = GetPassOption(option_name, overridden_options);
+    if (option_content.type != OptionContent::kInteger) {
       return 0;
     }
 
-    return default_it->second;
+    return option_content.GetInteger();
   }
 
   /** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */
   const DataFlowAnalysisMode traversal_type_;
 
-  /** @brief Flags for additional directives: used to determine if a particular post-optimization pass is necessary. */
+  /** @brief Flags for additional directives: used to determine if a particular
+    * post-optimization pass is necessary. */
   const unsigned int flags_;
 
   /** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */
@@ -171,7 +220,7 @@
    * @details The constructor of the specific pass instance should fill this
    * with default options.
    * */
-  SafeMap<const char*, int> default_options_;
+  SafeMap<const char*, const OptionContent> default_options_;
 };
 }  // namespace art
 #endif  // ART_COMPILER_DEX_PASS_ME_H_
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
deleted file mode 100644
index ba255e0..0000000
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ /dev/null
@@ -1,2003 +0,0 @@
-/*
- * 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.
- */
-
-#include "object_utils.h"
-
-#include <llvm/ADT/DepthFirstIterator.h>
-#include <llvm/Analysis/Verifier.h>
-#include <llvm/Bitcode/ReaderWriter.h>
-#include <llvm/IR/Instruction.h>
-#include <llvm/IR/Instructions.h>
-#include <llvm/IR/Metadata.h>
-#include <llvm/IR/Type.h>
-#include <llvm/Support/Casting.h>
-#include <llvm/Support/InstIterator.h>
-#include <llvm/Support/ToolOutputFile.h>
-
-#include "dex/compiler_internals.h"
-#include "dex/dataflow_iterator-inl.h"
-#include "dex/frontend.h"
-#include "llvm/ir_builder.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "llvm/utils_llvm.h"
-#include "mir_to_gbc.h"
-#include "thread-inl.h"
-
-const char* kLabelFormat = "%c0x%x_%d";
-const char kInvalidBlock = 0xff;
-const char kNormalBlock = 'L';
-const char kCatchBlock = 'C';
-
-namespace art {
-namespace llvm {
-::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
-}
-
-LLVMInfo::LLVMInfo() {
-  // Create context, module, intrinsic helper & ir builder
-  llvm_context_.reset(new ::llvm::LLVMContext());
-  llvm_module_ = new ::llvm::Module("art", *llvm_context_);
-  ::llvm::StructType::create(*llvm_context_, "JavaObject");
-  art::llvm::makeLLVMModuleContents(llvm_module_);
-  intrinsic_helper_.reset(new art::llvm::IntrinsicHelper(*llvm_context_, *llvm_module_));
-  ir_builder_.reset(new art::llvm::IRBuilder(*llvm_context_, *llvm_module_, *intrinsic_helper_));
-}
-
-LLVMInfo::~LLVMInfo() {
-}
-
-::llvm::BasicBlock* MirConverter::GetLLVMBlock(int id) {
-  return id_to_block_map_.Get(id);
-}
-
-::llvm::Value* MirConverter::GetLLVMValue(int s_reg) {
-  return llvm_values_[s_reg];
-}
-
-void MirConverter::SetVregOnValue(::llvm::Value* val, int s_reg) {
-  // Set vreg for debugging
-  art::llvm::IntrinsicHelper::IntrinsicId id = art::llvm::IntrinsicHelper::SetVReg;
-  ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
-  int v_reg = mir_graph_->SRegToVReg(s_reg);
-  ::llvm::Value* table_slot = irb_->getInt32(v_reg);
-  ::llvm::Value* args[] = { table_slot, val };
-  irb_->CreateCall(func, args);
-}
-
-// Replace the placeholder value with the real definition
-void MirConverter::DefineValueOnly(::llvm::Value* val, int s_reg) {
-  ::llvm::Value* placeholder = GetLLVMValue(s_reg);
-  if (placeholder == NULL) {
-    // This can happen on instruction rewrite on verification failure
-    LOG(WARNING) << "Null placeholder";
-    return;
-  }
-  placeholder->replaceAllUsesWith(val);
-  val->takeName(placeholder);
-  llvm_values_[s_reg] = val;
-  ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(placeholder);
-  DCHECK(inst != NULL);
-  inst->eraseFromParent();
-}
-
-void MirConverter::DefineValue(::llvm::Value* val, int s_reg) {
-  DefineValueOnly(val, s_reg);
-  SetVregOnValue(val, s_reg);
-}
-
-::llvm::Type* MirConverter::LlvmTypeFromLocRec(RegLocation loc) {
-  ::llvm::Type* res = NULL;
-  if (loc.wide) {
-    if (loc.fp)
-        res = irb_->getDoubleTy();
-    else
-        res = irb_->getInt64Ty();
-  } else {
-    if (loc.fp) {
-      res = irb_->getFloatTy();
-    } else {
-      if (loc.ref)
-        res = irb_->getJObjectTy();
-      else
-        res = irb_->getInt32Ty();
-    }
-  }
-  return res;
-}
-
-void MirConverter::InitIR() {
-  if (llvm_info_ == NULL) {
-    CompilerTls* tls = cu_->compiler_driver->GetTls();
-    CHECK(tls != NULL);
-    llvm_info_ = static_cast<LLVMInfo*>(tls->GetLLVMInfo());
-    if (llvm_info_ == NULL) {
-      llvm_info_ = new LLVMInfo();
-      tls->SetLLVMInfo(llvm_info_);
-    }
-  }
-  context_ = llvm_info_->GetLLVMContext();
-  module_ = llvm_info_->GetLLVMModule();
-  intrinsic_helper_ = llvm_info_->GetIntrinsicHelper();
-  irb_ = llvm_info_->GetIRBuilder();
-}
-
-::llvm::BasicBlock* MirConverter::FindCaseTarget(uint32_t vaddr) {
-  BasicBlock* bb = mir_graph_->FindBlock(vaddr);
-  DCHECK(bb != NULL);
-  return GetLLVMBlock(bb->id);
-}
-
-void MirConverter::ConvertPackedSwitch(BasicBlock* bb, MIR* mir,
-                                int32_t table_offset, RegLocation rl_src) {
-  const Instruction::PackedSwitchPayload* payload =
-      reinterpret_cast<const Instruction::PackedSwitchPayload*>(
-      mir_graph_->GetTable(mir, table_offset));
-
-  ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
-
-  ::llvm::SwitchInst* sw =
-    irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
-                             payload->case_count);
-
-  for (uint16_t i = 0; i < payload->case_count; ++i) {
-    ::llvm::BasicBlock* llvm_bb =
-        FindCaseTarget(current_dalvik_offset_ + payload->targets[i]);
-    sw->addCase(irb_->getInt32(payload->first_key + i), llvm_bb);
-  }
-  ::llvm::MDNode* switch_node =
-      ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
-  sw->setMetadata("SwitchTable", switch_node);
-  bb->taken = NullBasicBlockId;
-  bb->fall_through = NullBasicBlockId;
-}
-
-void MirConverter::ConvertSparseSwitch(BasicBlock* bb, MIR* mir,
-                                int32_t table_offset, RegLocation rl_src) {
-  const Instruction::SparseSwitchPayload* payload =
-      reinterpret_cast<const Instruction::SparseSwitchPayload*>(
-      mir_graph_->GetTable(mir, table_offset));
-
-  const int32_t* keys = payload->GetKeys();
-  const int32_t* targets = payload->GetTargets();
-
-  ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
-
-  ::llvm::SwitchInst* sw =
-    irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
-                             payload->case_count);
-
-  for (size_t i = 0; i < payload->case_count; ++i) {
-    ::llvm::BasicBlock* llvm_bb =
-        FindCaseTarget(current_dalvik_offset_ + targets[i]);
-    sw->addCase(irb_->getInt32(keys[i]), llvm_bb);
-  }
-  ::llvm::MDNode* switch_node =
-      ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
-  sw->setMetadata("SwitchTable", switch_node);
-  bb->taken = NullBasicBlockId;
-  bb->fall_through = NullBasicBlockId;
-}
-
-void MirConverter::ConvertSget(int32_t field_index,
-                        art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest) {
-  ::llvm::Constant* field_idx = irb_->getInt32(field_index);
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* res = irb_->CreateCall(intr, field_idx);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertSput(int32_t field_index,
-                        art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src) {
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(field_index));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertFillArrayData(int32_t offset, RegLocation rl_array) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  id = art::llvm::IntrinsicHelper::HLFillArrayData;
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(offset));
-  args.push_back(GetLLVMValue(rl_array.orig_sreg));
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  irb_->CreateCall(intr, args);
-}
-
-::llvm::Value* MirConverter::EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
-                              RegLocation loc) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  if (loc.wide) {
-    if (loc.fp) {
-      id = art::llvm::IntrinsicHelper::ConstDouble;
-    } else {
-      id = art::llvm::IntrinsicHelper::ConstLong;
-    }
-  } else {
-    if (loc.fp) {
-      id = art::llvm::IntrinsicHelper::ConstFloat;
-    } else if (loc.ref) {
-      id = art::llvm::IntrinsicHelper::ConstObj;
-    } else {
-      id = art::llvm::IntrinsicHelper::ConstInt;
-    }
-  }
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  return irb_->CreateCall(intr, src);
-}
-
-void MirConverter::EmitPopShadowFrame() {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
-      art::llvm::IntrinsicHelper::PopShadowFrame);
-  irb_->CreateCall(intr);
-}
-
-::llvm::Value* MirConverter::EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
-                             RegLocation loc) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  if (loc.wide) {
-    if (loc.fp) {
-      id = art::llvm::IntrinsicHelper::CopyDouble;
-    } else {
-      id = art::llvm::IntrinsicHelper::CopyLong;
-    }
-  } else {
-    if (loc.fp) {
-      id = art::llvm::IntrinsicHelper::CopyFloat;
-    } else if (loc.ref) {
-      id = art::llvm::IntrinsicHelper::CopyObj;
-    } else {
-      id = art::llvm::IntrinsicHelper::CopyInt;
-    }
-  }
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  return irb_->CreateCall(intr, src);
-}
-
-void MirConverter::ConvertMoveException(RegLocation rl_dest) {
-  ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
-      art::llvm::IntrinsicHelper::GetException);
-  ::llvm::Value* res = irb_->CreateCall(func);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertThrow(RegLocation rl_src) {
-  ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
-  ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
-      art::llvm::IntrinsicHelper::HLThrowException);
-  irb_->CreateCall(func, src);
-}
-
-void MirConverter::ConvertMonitorEnterExit(int opt_flags,
-                                    art::llvm::IntrinsicHelper::IntrinsicId id,
-                                    RegLocation rl_src) {
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(opt_flags));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
-  irb_->CreateCall(func, args);
-}
-
-void MirConverter::ConvertArrayLength(int opt_flags,
-                               RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(opt_flags));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
-      art::llvm::IntrinsicHelper::OptArrayLength);
-  ::llvm::Value* res = irb_->CreateCall(func, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::EmitSuspendCheck() {
-  art::llvm::IntrinsicHelper::IntrinsicId id =
-      art::llvm::IntrinsicHelper::CheckSuspend;
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  irb_->CreateCall(intr);
-}
-
-::llvm::Value* MirConverter::ConvertCompare(ConditionCode cc,
-                                   ::llvm::Value* src1, ::llvm::Value* src2) {
-  ::llvm::Value* res = NULL;
-  DCHECK_EQ(src1->getType(), src2->getType());
-  switch (cc) {
-    case kCondEq: res = irb_->CreateICmpEQ(src1, src2); break;
-    case kCondNe: res = irb_->CreateICmpNE(src1, src2); break;
-    case kCondLt: res = irb_->CreateICmpSLT(src1, src2); break;
-    case kCondGe: res = irb_->CreateICmpSGE(src1, src2); break;
-    case kCondGt: res = irb_->CreateICmpSGT(src1, src2); break;
-    case kCondLe: res = irb_->CreateICmpSLE(src1, src2); break;
-    default: LOG(FATAL) << "Unexpected cc value " << cc;
-  }
-  return res;
-}
-
-void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir,
-                                    ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2) {
-  if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
-    EmitSuspendCheck();
-  }
-  ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
-  ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
-  ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
-  cond_value->setName(StringPrintf("t%d", temp_name_++));
-  irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
-                           GetLLVMBlock(bb->fall_through));
-  // Don't redo the fallthrough branch in the BB driver
-  bb->fall_through = NullBasicBlockId;
-}
-
-void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb,
-                                        MIR* mir, ConditionCode cc, RegLocation rl_src1) {
-  if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
-    EmitSuspendCheck();
-  }
-  ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
-  ::llvm::Value* src2;
-  if (rl_src1.ref) {
-    src2 = irb_->getJNull();
-  } else {
-    src2 = irb_->getInt32(0);
-  }
-  ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
-  irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
-                           GetLLVMBlock(bb->fall_through));
-  // Don't redo the fallthrough branch in the BB driver
-  bb->fall_through = NullBasicBlockId;
-}
-
-::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long,
-                                ::llvm::Value* src1, ::llvm::Value* src2) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  if (is_long) {
-    if (is_div) {
-      id = art::llvm::IntrinsicHelper::DivLong;
-    } else {
-      id = art::llvm::IntrinsicHelper::RemLong;
-    }
-  } else {
-    if (is_div) {
-      id = art::llvm::IntrinsicHelper::DivInt;
-    } else {
-      id = art::llvm::IntrinsicHelper::RemInt;
-    }
-  }
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2>args;
-  args.push_back(src1);
-  args.push_back(src2);
-  return irb_->CreateCall(intr, args);
-}
-
-::llvm::Value* MirConverter::GenArithOp(OpKind op, bool is_long,
-                               ::llvm::Value* src1, ::llvm::Value* src2) {
-  ::llvm::Value* res = NULL;
-  switch (op) {
-    case kOpAdd: res = irb_->CreateAdd(src1, src2); break;
-    case kOpSub: res = irb_->CreateSub(src1, src2); break;
-    case kOpRsub: res = irb_->CreateSub(src2, src1); break;
-    case kOpMul: res = irb_->CreateMul(src1, src2); break;
-    case kOpOr: res = irb_->CreateOr(src1, src2); break;
-    case kOpAnd: res = irb_->CreateAnd(src1, src2); break;
-    case kOpXor: res = irb_->CreateXor(src1, src2); break;
-    case kOpDiv: res = GenDivModOp(true, is_long, src1, src2); break;
-    case kOpRem: res = GenDivModOp(false, is_long, src1, src2); break;
-    case kOpLsl: res = irb_->CreateShl(src1, src2); break;
-    case kOpLsr: res = irb_->CreateLShr(src1, src2); break;
-    case kOpAsr: res = irb_->CreateAShr(src1, src2); break;
-    default:
-      LOG(FATAL) << "Invalid op " << op;
-  }
-  return res;
-}
-
-void MirConverter::ConvertFPArithOp(OpKind op, RegLocation rl_dest,
-                             RegLocation rl_src1, RegLocation rl_src2) {
-  ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
-  ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
-  ::llvm::Value* res = NULL;
-  switch (op) {
-    case kOpAdd: res = irb_->CreateFAdd(src1, src2); break;
-    case kOpSub: res = irb_->CreateFSub(src1, src2); break;
-    case kOpMul: res = irb_->CreateFMul(src1, src2); break;
-    case kOpDiv: res = irb_->CreateFDiv(src1, src2); break;
-    case kOpRem: res = irb_->CreateFRem(src1, src2); break;
-    default:
-      LOG(FATAL) << "Invalid op " << op;
-  }
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
-                         RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2>args;
-  args.push_back(GetLLVMValue(rl_src1.orig_sreg));
-  args.push_back(GetLLVMValue(rl_src2.orig_sreg));
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
-                            RegLocation rl_dest, RegLocation rl_src, int shift_amount) {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2>args;
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  args.push_back(irb_->getInt32(shift_amount));
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertArithOp(OpKind op, RegLocation rl_dest,
-                           RegLocation rl_src1, RegLocation rl_src2) {
-  ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
-  ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
-  DCHECK_EQ(src1->getType(), src2->getType());
-  ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertArithOpLit(OpKind op, RegLocation rl_dest,
-                              RegLocation rl_src1, int32_t imm) {
-  ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
-  ::llvm::Value* src2 = irb_->getInt32(imm);
-  ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-/*
- * Process arguments for invoke.  Note: this code is also used to
- * collect and process arguments for NEW_FILLED_ARRAY and NEW_FILLED_ARRAY_RANGE.
- * The requirements are similar.
- */
-void MirConverter::ConvertInvoke(BasicBlock* bb, MIR* mir,
-                          InvokeType invoke_type, bool is_range, bool is_filled_new_array) {
-  CallInfo* info = mir_graph_->NewMemCallInfo(bb, mir, invoke_type, is_range);
-  ::llvm::SmallVector< ::llvm::Value*, 10> args;
-  // Insert the invoke_type
-  args.push_back(irb_->getInt32(static_cast<int>(invoke_type)));
-  // Insert the method_idx
-  args.push_back(irb_->getInt32(info->index));
-  // Insert the optimization flags
-  args.push_back(irb_->getInt32(info->opt_flags));
-  // Now, insert the actual arguments
-  for (int i = 0; i < info->num_arg_words;) {
-    ::llvm::Value* val = GetLLVMValue(info->args[i].orig_sreg);
-    args.push_back(val);
-    i += info->args[i].wide ? 2 : 1;
-  }
-  /*
-   * Choose the invoke return type based on actual usage.  Note: may
-   * be different than shorty.  For example, if a function return value
-   * is not used, we'll treat this as a void invoke.
-   */
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  if (is_filled_new_array) {
-    id = art::llvm::IntrinsicHelper::HLFilledNewArray;
-  } else if (info->result.location == kLocInvalid) {
-    id = art::llvm::IntrinsicHelper::HLInvokeVoid;
-  } else {
-    if (info->result.wide) {
-      if (info->result.fp) {
-        id = art::llvm::IntrinsicHelper::HLInvokeDouble;
-      } else {
-        id = art::llvm::IntrinsicHelper::HLInvokeLong;
-      }
-    } else if (info->result.ref) {
-        id = art::llvm::IntrinsicHelper::HLInvokeObj;
-    } else if (info->result.fp) {
-        id = art::llvm::IntrinsicHelper::HLInvokeFloat;
-    } else {
-        id = art::llvm::IntrinsicHelper::HLInvokeInt;
-    }
-  }
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  if (info->result.location != kLocInvalid) {
-    DefineValue(res, info->result.orig_sreg);
-  }
-}
-
-void MirConverter::ConvertConstObject(uint32_t idx,
-                               art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest) {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* index = irb_->getInt32(idx);
-  ::llvm::Value* res = irb_->CreateCall(intr, index);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertCheckCast(uint32_t type_idx, RegLocation rl_src) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  id = art::llvm::IntrinsicHelper::HLCheckCast;
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(type_idx));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  id = art::llvm::IntrinsicHelper::NewInstance;
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* index = irb_->getInt32(type_idx);
-  ::llvm::Value* res = irb_->CreateCall(intr, index);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertNewArray(uint32_t type_idx,
-                            RegLocation rl_dest, RegLocation rl_src) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  id = art::llvm::IntrinsicHelper::NewArray;
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(type_idx));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertAget(int opt_flags,
-                        art::llvm::IntrinsicHelper::IntrinsicId id,
-                        RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index) {
-  ::llvm::SmallVector< ::llvm::Value*, 3> args;
-  args.push_back(irb_->getInt32(opt_flags));
-  args.push_back(GetLLVMValue(rl_array.orig_sreg));
-  args.push_back(GetLLVMValue(rl_index.orig_sreg));
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertAput(int opt_flags,
-                        art::llvm::IntrinsicHelper::IntrinsicId id,
-                        RegLocation rl_src, RegLocation rl_array, RegLocation rl_index) {
-  ::llvm::SmallVector< ::llvm::Value*, 4> args;
-  args.push_back(irb_->getInt32(opt_flags));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  args.push_back(GetLLVMValue(rl_array.orig_sreg));
-  args.push_back(GetLLVMValue(rl_index.orig_sreg));
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertIget(int opt_flags,
-                        art::llvm::IntrinsicHelper::IntrinsicId id,
-                        RegLocation rl_dest, RegLocation rl_obj, int field_index) {
-  ::llvm::SmallVector< ::llvm::Value*, 3> args;
-  args.push_back(irb_->getInt32(opt_flags));
-  args.push_back(GetLLVMValue(rl_obj.orig_sreg));
-  args.push_back(irb_->getInt32(field_index));
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIput(int opt_flags,
-                        art::llvm::IntrinsicHelper::IntrinsicId id,
-                        RegLocation rl_src, RegLocation rl_obj, int field_index) {
-  ::llvm::SmallVector< ::llvm::Value*, 4> args;
-  args.push_back(irb_->getInt32(opt_flags));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  args.push_back(GetLLVMValue(rl_obj.orig_sreg));
-  args.push_back(irb_->getInt32(field_index));
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  irb_->CreateCall(intr, args);
-}
-
-void MirConverter::ConvertInstanceOf(uint32_t type_idx,
-                              RegLocation rl_dest, RegLocation rl_src) {
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  id = art::llvm::IntrinsicHelper::InstanceOf;
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(irb_->getInt32(type_idx));
-  args.push_back(GetLLVMValue(rl_src.orig_sreg));
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* res = irb_->CreateSExt(GetLLVMValue(rl_src.orig_sreg),
-                                            irb_->getInt64Ty());
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
-  ::llvm::Value* res = irb_->CreateTrunc(src, irb_->getInt32Ty());
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
-  ::llvm::Value* res = irb_->CreateFPExt(src, irb_->getDoubleTy());
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
-  ::llvm::Value* res = irb_->CreateFPTrunc(src, irb_->getFloatTy());
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
-                                         RegLocation rl_dest, RegLocation rl_src1,
-                                         RegLocation rl_src2) {
-  DCHECK_EQ(rl_src1.fp, rl_src2.fp);
-  DCHECK_EQ(rl_src1.wide, rl_src2.wide);
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::SmallVector< ::llvm::Value*, 2> args;
-  args.push_back(GetLLVMValue(rl_src1.orig_sreg));
-  args.push_back(GetLLVMValue(rl_src2.orig_sreg));
-  ::llvm::Value* res = irb_->CreateCall(intr, args);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
-                                art::llvm::IntrinsicHelper::IntrinsicId id) {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* res =
-      irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg));
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertNeg(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* res = irb_->CreateNeg(GetLLVMValue(rl_src.orig_sreg));
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest,
-                           RegLocation rl_src) {
-  ::llvm::Value* res =
-      irb_->CreateSIToFP(GetLLVMValue(rl_src.orig_sreg), ty);
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
-                           RegLocation rl_dest,
-                    RegLocation rl_src) {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Value* res = irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg));
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-
-void MirConverter::ConvertNegFP(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* res =
-      irb_->CreateFNeg(GetLLVMValue(rl_src.orig_sreg));
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::ConvertNot(RegLocation rl_dest, RegLocation rl_src) {
-  ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
-  ::llvm::Value* res = irb_->CreateXor(src, static_cast<uint64_t>(-1));
-  DefineValue(res, rl_dest.orig_sreg);
-}
-
-void MirConverter::EmitConstructorBarrier() {
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
-      art::llvm::IntrinsicHelper::ConstructorBarrier);
-  irb_->CreateCall(intr);
-}
-
-/*
- * Target-independent code generation.  Use only high-level
- * load/store utilities here, or target-dependent genXX() handlers
- * when necessary.
- */
-bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb,
-                           ::llvm::BasicBlock* llvm_bb) {
-  bool res = false;   // Assume success
-  RegLocation rl_src[3];
-  RegLocation rl_dest = mir_graph_->GetBadLoc();
-  Instruction::Code opcode = mir->dalvikInsn.opcode;
-  int op_val = opcode;
-  uint32_t vB = mir->dalvikInsn.vB;
-  uint32_t vC = mir->dalvikInsn.vC;
-  int opt_flags = mir->optimization_flags;
-
-  if (cu_->verbose) {
-    if (!IsPseudoMirOp(op_val)) {
-      LOG(INFO) << ".. " << Instruction::Name(opcode) << " 0x" << std::hex << op_val;
-    } else {
-      LOG(INFO) << mir_graph_->extended_mir_op_names_[op_val - kMirOpFirst] << " 0x" << std::hex << op_val;
-    }
-  }
-
-  /* Prep Src and Dest locations */
-  int next_sreg = 0;
-  int next_loc = 0;
-  uint64_t attrs = MirGraph::GetDataFlowAttributes(opcode);
-  rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
-  if (attrs & DF_UA) {
-    if (attrs & DF_A_WIDE) {
-      rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
-      next_sreg+= 2;
-    } else {
-      rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
-      next_sreg++;
-    }
-  }
-  if (attrs & DF_UB) {
-    if (attrs & DF_B_WIDE) {
-      rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
-      next_sreg+= 2;
-    } else {
-      rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
-      next_sreg++;
-    }
-  }
-  if (attrs & DF_UC) {
-    if (attrs & DF_C_WIDE) {
-      rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
-    } else {
-      rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
-    }
-  }
-  if (attrs & DF_DA) {
-    if (attrs & DF_A_WIDE) {
-      rl_dest = mir_graph_->GetDestWide(mir);
-    } else {
-      rl_dest = mir_graph_->GetDest(mir);
-    }
-  }
-
-  switch (opcode) {
-    case Instruction::NOP:
-      break;
-
-    case Instruction::MOVE:
-    case Instruction::MOVE_OBJECT:
-    case Instruction::MOVE_16:
-    case Instruction::MOVE_OBJECT_16:
-    case Instruction::MOVE_OBJECT_FROM16:
-    case Instruction::MOVE_FROM16:
-    case Instruction::MOVE_WIDE:
-    case Instruction::MOVE_WIDE_16:
-    case Instruction::MOVE_WIDE_FROM16: {
-        /*
-         * Moves/copies are meaningless in pure SSA register form,
-         * but we need to preserve them for the conversion back into
-         * MIR (at least until we stop using the Dalvik register maps).
-         * Insert a dummy intrinsic copy call, which will be recognized
-         * by the quick path and removed by the portable path.
-         */
-        ::llvm::Value* src = GetLLVMValue(rl_src[0].orig_sreg);
-        ::llvm::Value* res = EmitCopy(src, rl_dest);
-        DefineValue(res, rl_dest.orig_sreg);
-      }
-      break;
-
-    case Instruction::CONST:
-    case Instruction::CONST_4:
-    case Instruction::CONST_16: {
-        ::llvm::Constant* imm_value = irb_->getJInt(vB);
-        ::llvm::Value* res = EmitConst(imm_value, rl_dest);
-        DefineValue(res, rl_dest.orig_sreg);
-      }
-      break;
-
-    case Instruction::CONST_WIDE_16:
-    case Instruction::CONST_WIDE_32: {
-        // Sign extend to 64 bits
-        int64_t imm = static_cast<int32_t>(vB);
-        ::llvm::Constant* imm_value = irb_->getJLong(imm);
-        ::llvm::Value* res = EmitConst(imm_value, rl_dest);
-        DefineValue(res, rl_dest.orig_sreg);
-      }
-      break;
-
-    case Instruction::CONST_HIGH16: {
-        ::llvm::Constant* imm_value = irb_->getJInt(vB << 16);
-        ::llvm::Value* res = EmitConst(imm_value, rl_dest);
-        DefineValue(res, rl_dest.orig_sreg);
-      }
-      break;
-
-    case Instruction::CONST_WIDE: {
-        ::llvm::Constant* imm_value =
-            irb_->getJLong(mir->dalvikInsn.vB_wide);
-        ::llvm::Value* res = EmitConst(imm_value, rl_dest);
-        DefineValue(res, rl_dest.orig_sreg);
-      }
-      break;
-    case Instruction::CONST_WIDE_HIGH16: {
-        int64_t imm = static_cast<int64_t>(vB) << 48;
-        ::llvm::Constant* imm_value = irb_->getJLong(imm);
-        ::llvm::Value* res = EmitConst(imm_value, rl_dest);
-        DefineValue(res, rl_dest.orig_sreg);
-      }
-      break;
-
-    case Instruction::SPUT_OBJECT:
-      ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputObject,
-                  rl_src[0]);
-      break;
-    case Instruction::SPUT:
-      if (rl_src[0].fp) {
-        ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputFloat,
-                    rl_src[0]);
-      } else {
-        ConvertSput(vB, art::llvm::IntrinsicHelper::HLSput, rl_src[0]);
-      }
-      break;
-    case Instruction::SPUT_BOOLEAN:
-      ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputBoolean,
-                  rl_src[0]);
-      break;
-    case Instruction::SPUT_BYTE:
-      ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputByte, rl_src[0]);
-      break;
-    case Instruction::SPUT_CHAR:
-      ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputChar, rl_src[0]);
-      break;
-    case Instruction::SPUT_SHORT:
-      ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputShort, rl_src[0]);
-      break;
-    case Instruction::SPUT_WIDE:
-      if (rl_src[0].fp) {
-        ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputDouble,
-                    rl_src[0]);
-      } else {
-        ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputWide,
-                    rl_src[0]);
-      }
-      break;
-
-    case Instruction::SGET_OBJECT:
-      ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetObject, rl_dest);
-      break;
-    case Instruction::SGET:
-      if (rl_dest.fp) {
-        ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetFloat, rl_dest);
-      } else {
-        ConvertSget(vB, art::llvm::IntrinsicHelper::HLSget, rl_dest);
-      }
-      break;
-    case Instruction::SGET_BOOLEAN:
-      ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetBoolean, rl_dest);
-      break;
-    case Instruction::SGET_BYTE:
-      ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetByte, rl_dest);
-      break;
-    case Instruction::SGET_CHAR:
-      ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetChar, rl_dest);
-      break;
-    case Instruction::SGET_SHORT:
-      ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetShort, rl_dest);
-      break;
-    case Instruction::SGET_WIDE:
-      if (rl_dest.fp) {
-        ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetDouble,
-                    rl_dest);
-      } else {
-        ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetWide, rl_dest);
-      }
-      break;
-
-    case Instruction::RETURN_WIDE:
-    case Instruction::RETURN:
-    case Instruction::RETURN_OBJECT: {
-        if (!mir_graph_->MethodIsLeaf()) {
-          EmitSuspendCheck();
-        }
-        EmitPopShadowFrame();
-        irb_->CreateRet(GetLLVMValue(rl_src[0].orig_sreg));
-        DCHECK(bb->terminated_by_return);
-      }
-      break;
-
-    case Instruction::RETURN_VOID: {
-        if (((cu_->access_flags & kAccConstructor) != 0) &&
-            cu_->compiler_driver->RequiresConstructorBarrier(Thread::Current(),
-                                                            cu_->dex_file,
-                                                            cu_->class_def_idx)) {
-          EmitConstructorBarrier();
-        }
-        if (!mir_graph_->MethodIsLeaf()) {
-          EmitSuspendCheck();
-        }
-        EmitPopShadowFrame();
-        irb_->CreateRetVoid();
-        DCHECK(bb->terminated_by_return);
-      }
-      break;
-
-    case Instruction::IF_EQ:
-      ConvertCompareAndBranch(bb, mir, kCondEq, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::IF_NE:
-      ConvertCompareAndBranch(bb, mir, kCondNe, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::IF_LT:
-      ConvertCompareAndBranch(bb, mir, kCondLt, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::IF_GE:
-      ConvertCompareAndBranch(bb, mir, kCondGe, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::IF_GT:
-      ConvertCompareAndBranch(bb, mir, kCondGt, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::IF_LE:
-      ConvertCompareAndBranch(bb, mir, kCondLe, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::IF_EQZ:
-      ConvertCompareZeroAndBranch(bb, mir, kCondEq, rl_src[0]);
-      break;
-    case Instruction::IF_NEZ:
-      ConvertCompareZeroAndBranch(bb, mir, kCondNe, rl_src[0]);
-      break;
-    case Instruction::IF_LTZ:
-      ConvertCompareZeroAndBranch(bb, mir, kCondLt, rl_src[0]);
-      break;
-    case Instruction::IF_GEZ:
-      ConvertCompareZeroAndBranch(bb, mir, kCondGe, rl_src[0]);
-      break;
-    case Instruction::IF_GTZ:
-      ConvertCompareZeroAndBranch(bb, mir, kCondGt, rl_src[0]);
-      break;
-    case Instruction::IF_LEZ:
-      ConvertCompareZeroAndBranch(bb, mir, kCondLe, rl_src[0]);
-      break;
-
-    case Instruction::GOTO:
-    case Instruction::GOTO_16:
-    case Instruction::GOTO_32: {
-        if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= bb->start_offset) {
-          EmitSuspendCheck();
-        }
-        irb_->CreateBr(GetLLVMBlock(bb->taken));
-      }
-      break;
-
-    case Instruction::ADD_LONG:
-    case Instruction::ADD_LONG_2ADDR:
-    case Instruction::ADD_INT:
-    case Instruction::ADD_INT_2ADDR:
-      ConvertArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::SUB_LONG:
-    case Instruction::SUB_LONG_2ADDR:
-    case Instruction::SUB_INT:
-    case Instruction::SUB_INT_2ADDR:
-      ConvertArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::MUL_LONG:
-    case Instruction::MUL_LONG_2ADDR:
-    case Instruction::MUL_INT:
-    case Instruction::MUL_INT_2ADDR:
-      ConvertArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::DIV_LONG:
-    case Instruction::DIV_LONG_2ADDR:
-    case Instruction::DIV_INT:
-    case Instruction::DIV_INT_2ADDR:
-      ConvertArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::REM_LONG:
-    case Instruction::REM_LONG_2ADDR:
-    case Instruction::REM_INT:
-    case Instruction::REM_INT_2ADDR:
-      ConvertArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::AND_LONG:
-    case Instruction::AND_LONG_2ADDR:
-    case Instruction::AND_INT:
-    case Instruction::AND_INT_2ADDR:
-      ConvertArithOp(kOpAnd, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::OR_LONG:
-    case Instruction::OR_LONG_2ADDR:
-    case Instruction::OR_INT:
-    case Instruction::OR_INT_2ADDR:
-      ConvertArithOp(kOpOr, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::XOR_LONG:
-    case Instruction::XOR_LONG_2ADDR:
-    case Instruction::XOR_INT:
-    case Instruction::XOR_INT_2ADDR:
-      ConvertArithOp(kOpXor, rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::SHL_LONG:
-    case Instruction::SHL_LONG_2ADDR:
-      ConvertShift(art::llvm::IntrinsicHelper::SHLLong,
-                    rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::SHL_INT:
-    case Instruction::SHL_INT_2ADDR:
-      ConvertShift(art::llvm::IntrinsicHelper::SHLInt,
-                   rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::SHR_LONG:
-    case Instruction::SHR_LONG_2ADDR:
-      ConvertShift(art::llvm::IntrinsicHelper::SHRLong,
-                   rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::SHR_INT:
-    case Instruction::SHR_INT_2ADDR:
-      ConvertShift(art::llvm::IntrinsicHelper::SHRInt,
-                   rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::USHR_LONG:
-    case Instruction::USHR_LONG_2ADDR:
-      ConvertShift(art::llvm::IntrinsicHelper::USHRLong,
-                   rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::USHR_INT:
-    case Instruction::USHR_INT_2ADDR:
-      ConvertShift(art::llvm::IntrinsicHelper::USHRInt,
-                   rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::ADD_INT_LIT16:
-    case Instruction::ADD_INT_LIT8:
-      ConvertArithOpLit(kOpAdd, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::RSUB_INT:
-    case Instruction::RSUB_INT_LIT8:
-      ConvertArithOpLit(kOpRsub, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::MUL_INT_LIT16:
-    case Instruction::MUL_INT_LIT8:
-      ConvertArithOpLit(kOpMul, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::DIV_INT_LIT16:
-    case Instruction::DIV_INT_LIT8:
-      ConvertArithOpLit(kOpDiv, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::REM_INT_LIT16:
-    case Instruction::REM_INT_LIT8:
-      ConvertArithOpLit(kOpRem, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::AND_INT_LIT16:
-    case Instruction::AND_INT_LIT8:
-      ConvertArithOpLit(kOpAnd, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::OR_INT_LIT16:
-    case Instruction::OR_INT_LIT8:
-      ConvertArithOpLit(kOpOr, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::XOR_INT_LIT16:
-    case Instruction::XOR_INT_LIT8:
-      ConvertArithOpLit(kOpXor, rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::SHL_INT_LIT8:
-      ConvertShiftLit(art::llvm::IntrinsicHelper::SHLInt,
-                      rl_dest, rl_src[0], vC & 0x1f);
-      break;
-    case Instruction::SHR_INT_LIT8:
-      ConvertShiftLit(art::llvm::IntrinsicHelper::SHRInt,
-                      rl_dest, rl_src[0], vC & 0x1f);
-      break;
-    case Instruction::USHR_INT_LIT8:
-      ConvertShiftLit(art::llvm::IntrinsicHelper::USHRInt,
-                      rl_dest, rl_src[0], vC & 0x1f);
-      break;
-
-    case Instruction::ADD_FLOAT:
-    case Instruction::ADD_FLOAT_2ADDR:
-    case Instruction::ADD_DOUBLE:
-    case Instruction::ADD_DOUBLE_2ADDR:
-      ConvertFPArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::SUB_FLOAT:
-    case Instruction::SUB_FLOAT_2ADDR:
-    case Instruction::SUB_DOUBLE:
-    case Instruction::SUB_DOUBLE_2ADDR:
-      ConvertFPArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::MUL_FLOAT:
-    case Instruction::MUL_FLOAT_2ADDR:
-    case Instruction::MUL_DOUBLE:
-    case Instruction::MUL_DOUBLE_2ADDR:
-      ConvertFPArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::DIV_FLOAT:
-    case Instruction::DIV_FLOAT_2ADDR:
-    case Instruction::DIV_DOUBLE:
-    case Instruction::DIV_DOUBLE_2ADDR:
-      ConvertFPArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::REM_FLOAT:
-    case Instruction::REM_FLOAT_2ADDR:
-    case Instruction::REM_DOUBLE:
-    case Instruction::REM_DOUBLE_2ADDR:
-      ConvertFPArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::INVOKE_STATIC:
-      ConvertInvoke(bb, mir, kStatic, false /*range*/,
-                    false /* NewFilledArray */);
-      break;
-    case Instruction::INVOKE_STATIC_RANGE:
-      ConvertInvoke(bb, mir, kStatic, true /*range*/,
-                    false /* NewFilledArray */);
-      break;
-
-    case Instruction::INVOKE_DIRECT:
-      ConvertInvoke(bb,  mir, kDirect, false /*range*/,
-                    false /* NewFilledArray */);
-      break;
-    case Instruction::INVOKE_DIRECT_RANGE:
-      ConvertInvoke(bb, mir, kDirect, true /*range*/,
-                    false /* NewFilledArray */);
-      break;
-
-    case Instruction::INVOKE_VIRTUAL:
-      ConvertInvoke(bb, mir, kVirtual, false /*range*/,
-                    false /* NewFilledArray */);
-      break;
-    case Instruction::INVOKE_VIRTUAL_RANGE:
-      ConvertInvoke(bb, mir, kVirtual, true /*range*/,
-                    false /* NewFilledArray */);
-      break;
-
-    case Instruction::INVOKE_SUPER:
-      ConvertInvoke(bb, mir, kSuper, false /*range*/,
-                    false /* NewFilledArray */);
-      break;
-    case Instruction::INVOKE_SUPER_RANGE:
-      ConvertInvoke(bb, mir, kSuper, true /*range*/,
-                    false /* NewFilledArray */);
-      break;
-
-    case Instruction::INVOKE_INTERFACE:
-      ConvertInvoke(bb, mir, kInterface, false /*range*/,
-                    false /* NewFilledArray */);
-      break;
-    case Instruction::INVOKE_INTERFACE_RANGE:
-      ConvertInvoke(bb, mir, kInterface, true /*range*/,
-                    false /* NewFilledArray */);
-      break;
-    case Instruction::FILLED_NEW_ARRAY:
-      ConvertInvoke(bb, mir, kInterface, false /*range*/,
-                    true /* NewFilledArray */);
-      break;
-    case Instruction::FILLED_NEW_ARRAY_RANGE:
-      ConvertInvoke(bb, mir, kInterface, true /*range*/,
-                    true /* NewFilledArray */);
-      break;
-
-    case Instruction::CONST_STRING:
-    case Instruction::CONST_STRING_JUMBO:
-      ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstString,
-                         rl_dest);
-      break;
-
-    case Instruction::CONST_CLASS:
-      ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstClass,
-                         rl_dest);
-      break;
-
-    case Instruction::CHECK_CAST:
-      ConvertCheckCast(vB, rl_src[0]);
-      break;
-
-    case Instruction::NEW_INSTANCE:
-      ConvertNewInstance(vB, rl_dest);
-      break;
-
-    case Instruction::MOVE_EXCEPTION:
-      ConvertMoveException(rl_dest);
-      break;
-
-    case Instruction::THROW:
-      ConvertThrow(rl_src[0]);
-      /*
-       * If this throw is standalone, terminate.
-       * If it might rethrow, force termination
-       * of the following block.
-       */
-      if (bb->fall_through == NullBasicBlockId) {
-        irb_->CreateUnreachable();
-      } else {
-        mir_graph_->GetBasicBlock(bb->fall_through)->fall_through = NullBasicBlockId;
-        mir_graph_->GetBasicBlock(bb->fall_through)->taken = NullBasicBlockId;
-      }
-      break;
-
-    case Instruction::MOVE_RESULT_WIDE:
-    case Instruction::MOVE_RESULT:
-    case Instruction::MOVE_RESULT_OBJECT:
-      /*
-       * All move_results should have been folded into the preceeding invoke.
-       */
-      LOG(FATAL) << "Unexpected move_result";
-      break;
-
-    case Instruction::MONITOR_ENTER:
-      ConvertMonitorEnterExit(opt_flags,
-                              art::llvm::IntrinsicHelper::MonitorEnter,
-                              rl_src[0]);
-      break;
-
-    case Instruction::MONITOR_EXIT:
-      ConvertMonitorEnterExit(opt_flags,
-                              art::llvm::IntrinsicHelper::MonitorExit,
-                              rl_src[0]);
-      break;
-
-    case Instruction::ARRAY_LENGTH:
-      ConvertArrayLength(opt_flags, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::NEW_ARRAY:
-      ConvertNewArray(vC, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::INSTANCE_OF:
-      ConvertInstanceOf(vC, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::AGET:
-      if (rl_dest.fp) {
-        ConvertAget(opt_flags,
-                    art::llvm::IntrinsicHelper::HLArrayGetFloat,
-                    rl_dest, rl_src[0], rl_src[1]);
-      } else {
-        ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGet,
-                    rl_dest, rl_src[0], rl_src[1]);
-      }
-      break;
-    case Instruction::AGET_OBJECT:
-      ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetObject,
-                  rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::AGET_BOOLEAN:
-      ConvertAget(opt_flags,
-                  art::llvm::IntrinsicHelper::HLArrayGetBoolean,
-                  rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::AGET_BYTE:
-      ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetByte,
-                  rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::AGET_CHAR:
-      ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetChar,
-                  rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::AGET_SHORT:
-      ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetShort,
-                  rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::AGET_WIDE:
-      if (rl_dest.fp) {
-        ConvertAget(opt_flags,
-                    art::llvm::IntrinsicHelper::HLArrayGetDouble,
-                    rl_dest, rl_src[0], rl_src[1]);
-      } else {
-        ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetWide,
-                    rl_dest, rl_src[0], rl_src[1]);
-      }
-      break;
-
-    case Instruction::APUT:
-      if (rl_src[0].fp) {
-        ConvertAput(opt_flags,
-                    art::llvm::IntrinsicHelper::HLArrayPutFloat,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      } else {
-        ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPut,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      }
-      break;
-    case Instruction::APUT_OBJECT:
-      ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutObject,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      break;
-    case Instruction::APUT_BOOLEAN:
-      ConvertAput(opt_flags,
-                  art::llvm::IntrinsicHelper::HLArrayPutBoolean,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      break;
-    case Instruction::APUT_BYTE:
-      ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutByte,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      break;
-    case Instruction::APUT_CHAR:
-      ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutChar,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      break;
-    case Instruction::APUT_SHORT:
-      ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutShort,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      break;
-    case Instruction::APUT_WIDE:
-      if (rl_src[0].fp) {
-        ConvertAput(opt_flags,
-                    art::llvm::IntrinsicHelper::HLArrayPutDouble,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      } else {
-        ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutWide,
-                    rl_src[0], rl_src[1], rl_src[2]);
-      }
-      break;
-
-    case Instruction::IGET:
-      if (rl_dest.fp) {
-        ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetFloat,
-                    rl_dest, rl_src[0], vC);
-      } else {
-        ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGet,
-                    rl_dest, rl_src[0], vC);
-      }
-      break;
-    case Instruction::IGET_OBJECT:
-      ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetObject,
-                  rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::IGET_BOOLEAN:
-      ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetBoolean,
-                  rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::IGET_BYTE:
-      ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetByte,
-                  rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::IGET_CHAR:
-      ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetChar,
-                  rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::IGET_SHORT:
-      ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetShort,
-                  rl_dest, rl_src[0], vC);
-      break;
-    case Instruction::IGET_WIDE:
-      if (rl_dest.fp) {
-        ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetDouble,
-                    rl_dest, rl_src[0], vC);
-      } else {
-        ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetWide,
-                    rl_dest, rl_src[0], vC);
-      }
-      break;
-    case Instruction::IPUT:
-      if (rl_src[0].fp) {
-        ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutFloat,
-                    rl_src[0], rl_src[1], vC);
-      } else {
-        ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPut,
-                    rl_src[0], rl_src[1], vC);
-      }
-      break;
-    case Instruction::IPUT_OBJECT:
-      ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutObject,
-                  rl_src[0], rl_src[1], vC);
-      break;
-    case Instruction::IPUT_BOOLEAN:
-      ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutBoolean,
-                  rl_src[0], rl_src[1], vC);
-      break;
-    case Instruction::IPUT_BYTE:
-      ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutByte,
-                  rl_src[0], rl_src[1], vC);
-      break;
-    case Instruction::IPUT_CHAR:
-      ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutChar,
-                  rl_src[0], rl_src[1], vC);
-      break;
-    case Instruction::IPUT_SHORT:
-      ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutShort,
-                  rl_src[0], rl_src[1], vC);
-      break;
-    case Instruction::IPUT_WIDE:
-      if (rl_src[0].fp) {
-        ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutDouble,
-                    rl_src[0], rl_src[1], vC);
-      } else {
-        ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutWide,
-                    rl_src[0], rl_src[1], vC);
-      }
-      break;
-
-    case Instruction::FILL_ARRAY_DATA:
-      ConvertFillArrayData(vB, rl_src[0]);
-      break;
-
-    case Instruction::LONG_TO_INT:
-      ConvertLongToInt(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::INT_TO_LONG:
-      ConvertIntToLong(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::INT_TO_CHAR:
-      ConvertIntNarrowing(rl_dest, rl_src[0],
-                          art::llvm::IntrinsicHelper::IntToChar);
-      break;
-    case Instruction::INT_TO_BYTE:
-      ConvertIntNarrowing(rl_dest, rl_src[0],
-                          art::llvm::IntrinsicHelper::IntToByte);
-      break;
-    case Instruction::INT_TO_SHORT:
-      ConvertIntNarrowing(rl_dest, rl_src[0],
-                          art::llvm::IntrinsicHelper::IntToShort);
-      break;
-
-    case Instruction::INT_TO_FLOAT:
-    case Instruction::LONG_TO_FLOAT:
-      ConvertIntToFP(irb_->getFloatTy(), rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::INT_TO_DOUBLE:
-    case Instruction::LONG_TO_DOUBLE:
-      ConvertIntToFP(irb_->getDoubleTy(), rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::FLOAT_TO_DOUBLE:
-      ConvertFloatToDouble(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::DOUBLE_TO_FLOAT:
-      ConvertDoubleToFloat(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::NEG_LONG:
-    case Instruction::NEG_INT:
-      ConvertNeg(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::NEG_FLOAT:
-    case Instruction::NEG_DOUBLE:
-      ConvertNegFP(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::NOT_LONG:
-    case Instruction::NOT_INT:
-      ConvertNot(rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::FLOAT_TO_INT:
-      ConvertFPToInt(art::llvm::IntrinsicHelper::F2I, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::DOUBLE_TO_INT:
-      ConvertFPToInt(art::llvm::IntrinsicHelper::D2I, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::FLOAT_TO_LONG:
-      ConvertFPToInt(art::llvm::IntrinsicHelper::F2L, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::DOUBLE_TO_LONG:
-      ConvertFPToInt(art::llvm::IntrinsicHelper::D2L, rl_dest, rl_src[0]);
-      break;
-
-    case Instruction::CMPL_FLOAT:
-      ConvertWideComparison(art::llvm::IntrinsicHelper::CmplFloat,
-                            rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::CMPG_FLOAT:
-      ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgFloat,
-                            rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::CMPL_DOUBLE:
-      ConvertWideComparison(art::llvm::IntrinsicHelper::CmplDouble,
-                            rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::CMPG_DOUBLE:
-      ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgDouble,
-                            rl_dest, rl_src[0], rl_src[1]);
-      break;
-    case Instruction::CMP_LONG:
-      ConvertWideComparison(art::llvm::IntrinsicHelper::CmpLong,
-                            rl_dest, rl_src[0], rl_src[1]);
-      break;
-
-    case Instruction::PACKED_SWITCH:
-      ConvertPackedSwitch(bb, vB, rl_src[0]);
-      break;
-
-    case Instruction::SPARSE_SWITCH:
-      ConvertSparseSwitch(bb, vB, rl_src[0]);
-      break;
-
-    default:
-      UNIMPLEMENTED(FATAL) << "Unsupported Dex opcode 0x" << std::hex << opcode;
-      res = true;
-  }
-  return res;
-}  // NOLINT(readability/fn_size)
-
-void MirConverter::SetDexOffset(int32_t offset) {
-  current_dalvik_offset_ = offset;
-  ::llvm::SmallVector< ::llvm::Value*, 1> array_ref;
-  array_ref.push_back(irb_->getInt32(offset));
-  ::llvm::MDNode* node = ::llvm::MDNode::get(*context_, array_ref);
-  irb_->SetDexOffset(node);
-}
-
-// Attach method info as metadata to special intrinsic
-void MirConverter::SetMethodInfo() {
-  // We don't want dex offset on this
-  irb_->SetDexOffset(NULL);
-  art::llvm::IntrinsicHelper::IntrinsicId id;
-  id = art::llvm::IntrinsicHelper::MethodInfo;
-  ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
-  ::llvm::Instruction* inst = irb_->CreateCall(intr);
-  ::llvm::SmallVector< ::llvm::Value*, 2> reg_info;
-  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfInVRs()));
-  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfLocalCodeVRs()));
-  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfOutVRs()));
-  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumUsedCompilerTemps()));
-  reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs()));
-  ::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info);
-  inst->setMetadata("RegInfo", reg_info_node);
-  SetDexOffset(current_dalvik_offset_);
-}
-
-void MirConverter::HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb) {
-  SetDexOffset(bb->start_offset);
-  for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
-    int opcode = mir->dalvikInsn.opcode;
-    if (!IsPseudoMirOp(opcode)) {
-      // Stop after first non-pseudo MIR op.
-      continue;
-    }
-    if (opcode != kMirOpPhi) {
-      // Skip other mir Pseudos.
-      continue;
-    }
-    RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]];
-    /*
-     * The Art compiler's Phi nodes only handle 32-bit operands,
-     * representing wide values using a matched set of Phi nodes
-     * for the lower and upper halves.  In the llvm world, we only
-     * want a single Phi for wides.  Here we will simply discard
-     * the Phi node representing the high word.
-     */
-    if (rl_dest.high_word) {
-      continue;  // No Phi node - handled via low word
-    }
-    BasicBlockId* incoming = mir->meta.phi_incoming;
-    ::llvm::Type* phi_type =
-        LlvmTypeFromLocRec(rl_dest);
-    ::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses);
-    for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
-      RegLocation loc;
-      // Don't check width here.
-      loc = mir_graph_->GetRawSrc(mir, i);
-      DCHECK_EQ(rl_dest.wide, loc.wide);
-      DCHECK_EQ(rl_dest.wide & rl_dest.high_word, loc.wide & loc.high_word);
-      DCHECK_EQ(rl_dest.fp, loc.fp);
-      DCHECK_EQ(rl_dest.core, loc.core);
-      DCHECK_EQ(rl_dest.ref, loc.ref);
-      SafeMap<unsigned int, unsigned int>::iterator it;
-      it = mir_graph_->block_id_map_.find(incoming[i]);
-      DCHECK(it != mir_graph_->block_id_map_.end());
-      DCHECK(GetLLVMValue(loc.orig_sreg) != NULL);
-      DCHECK(GetLLVMBlock(it->second) != NULL);
-      phi->addIncoming(GetLLVMValue(loc.orig_sreg),
-                       GetLLVMBlock(it->second));
-    }
-    DefineValueOnly(phi, rl_dest.orig_sreg);
-  }
-}
-
-/* Extended MIR instructions like PHI */
-void MirConverter::ConvertExtendedMIR(BasicBlock* bb, MIR* mir,
-                                      ::llvm::BasicBlock* llvm_bb) {
-  switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
-    case kMirOpPhi: {
-      // The llvm Phi node already emitted - just DefineValue() here.
-      RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]];
-      if (!rl_dest.high_word) {
-        // Only consider low word of pairs.
-        DCHECK(GetLLVMValue(rl_dest.orig_sreg) != NULL);
-        ::llvm::Value* phi = GetLLVMValue(rl_dest.orig_sreg);
-        if (1) SetVregOnValue(phi, rl_dest.orig_sreg);
-      }
-      break;
-    }
-    case kMirOpCopy: {
-      UNIMPLEMENTED(WARNING) << "unimp kMirOpPhi";
-      break;
-    }
-    case kMirOpNop:
-      if ((mir == bb->last_mir_insn) && (bb->taken == NullBasicBlockId) &&
-          (bb->fall_through == NullBasicBlockId)) {
-        irb_->CreateUnreachable();
-      }
-      break;
-
-    // TODO: need GBC intrinsic to take advantage of fused operations
-    case kMirOpFusedCmplFloat:
-      UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpFloat unsupported";
-      break;
-    case kMirOpFusedCmpgFloat:
-      UNIMPLEMENTED(FATAL) << "kMirOpFusedCmgFloat unsupported";
-      break;
-    case kMirOpFusedCmplDouble:
-      UNIMPLEMENTED(FATAL) << "kMirOpFusedCmplDouble unsupported";
-      break;
-    case kMirOpFusedCmpgDouble:
-      UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpgDouble unsupported";
-      break;
-    case kMirOpFusedCmpLong:
-      UNIMPLEMENTED(FATAL) << "kMirOpLongCmpBranch unsupported";
-      break;
-    default:
-      break;
-  }
-}
-
-/* Handle the content in each basic block */
-bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) {
-  if (bb->block_type == kDead) return false;
-  ::llvm::BasicBlock* llvm_bb = GetLLVMBlock(bb->id);
-  if (llvm_bb == NULL) {
-    CHECK(bb->block_type == kExitBlock);
-  } else {
-    irb_->SetInsertPoint(llvm_bb);
-    SetDexOffset(bb->start_offset);
-  }
-
-  if (cu_->verbose) {
-    LOG(INFO) << "................................";
-    LOG(INFO) << "Block id " << bb->id;
-    if (llvm_bb != NULL) {
-      LOG(INFO) << "label " << llvm_bb->getName().str().c_str();
-    } else {
-      LOG(INFO) << "llvm_bb is NULL";
-    }
-  }
-
-  if (bb->block_type == kEntryBlock) {
-    SetMethodInfo();
-
-    {  // Allocate shadowframe.
-      art::llvm::IntrinsicHelper::IntrinsicId id =
-              art::llvm::IntrinsicHelper::AllocaShadowFrame;
-      ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
-      ::llvm::Value* entries = irb_->getInt32(mir_graph_->GetNumOfCodeVRs());
-      irb_->CreateCall(func, entries);
-    }
-
-    {  // Store arguments to vregs.
-      uint16_t arg_reg = mir_graph_->GetFirstInVR();
-
-      ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
-
-      const char* shorty = cu_->shorty;
-      uint32_t shorty_size = strlen(shorty);
-      CHECK_GE(shorty_size, 1u);
-
-      ++arg_iter;  // skip method object
-
-      if ((cu_->access_flags & kAccStatic) == 0) {
-        SetVregOnValue(arg_iter, arg_reg);
-        ++arg_iter;
-        ++arg_reg;
-      }
-
-      for (uint32_t i = 1; i < shorty_size; ++i, ++arg_iter) {
-        SetVregOnValue(arg_iter, arg_reg);
-
-        ++arg_reg;
-        if (shorty[i] == 'J' || shorty[i] == 'D') {
-          // Wide types, such as long and double, are using a pair of registers
-          // to store the value, so we have to increase arg_reg again.
-          ++arg_reg;
-        }
-      }
-    }
-  } else if (bb->block_type == kExitBlock) {
-    /*
-     * Because of the differences between how MIR/LIR and llvm handle exit
-     * blocks, we won't explicitly covert them.  On the llvm-to-lir
-     * path, it will need to be regenereated.
-     */
-    return false;
-  } else if (bb->block_type == kExceptionHandling) {
-    /*
-     * Because we're deferring null checking, delete the associated empty
-     * exception block.
-     */
-    llvm_bb->eraseFromParent();
-    return false;
-  }
-
-  HandlePhiNodes(bb, llvm_bb);
-
-  for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
-    SetDexOffset(mir->offset);
-
-    int opcode = mir->dalvikInsn.opcode;
-    Instruction::Format dalvik_format =
-        Instruction::FormatOf(mir->dalvikInsn.opcode);
-
-    if (opcode == kMirOpCheck) {
-      // Combine check and work halves of throwing instruction.
-      MIR* work_half = mir->meta.throw_insn;
-      mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode;
-      opcode = mir->dalvikInsn.opcode;
-      SSARepresentation* ssa_rep = work_half->ssa_rep;
-      work_half->ssa_rep = mir->ssa_rep;
-      mir->ssa_rep = ssa_rep;
-      work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
-      if (bb->successor_block_list_type == kCatch) {
-        ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
-            art::llvm::IntrinsicHelper::CatchTargets);
-        ::llvm::Value* switch_key =
-            irb_->CreateCall(intr, irb_->getInt32(mir->offset));
-        // New basic block to use for work half
-        ::llvm::BasicBlock* work_bb =
-            ::llvm::BasicBlock::Create(*context_, "", func_);
-        ::llvm::SwitchInst* sw =
-            irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks.size());
-        for (SuccessorBlockInfo *successor_block_info : bb->successor_blocks) {
-          ::llvm::BasicBlock *target =
-              GetLLVMBlock(successor_block_info->block);
-          int type_index = successor_block_info->key;
-          sw->addCase(irb_->getInt32(type_index), target);
-        }
-        llvm_bb = work_bb;
-        irb_->SetInsertPoint(llvm_bb);
-      }
-    }
-
-    if (IsPseudoMirOp(opcode)) {
-      ConvertExtendedMIR(bb, mir, llvm_bb);
-      continue;
-    }
-
-    bool not_handled = ConvertMIRNode(mir, bb, llvm_bb);
-    if (not_handled) {
-      Instruction::Code dalvik_opcode = static_cast<Instruction::Code>(opcode);
-      LOG(WARNING) << StringPrintf("%#06x: Op %#x (%s) / Fmt %d not handled",
-                                   mir->offset, opcode,
-                                   Instruction::Name(dalvik_opcode),
-                                   dalvik_format);
-    }
-  }
-
-  if (bb->block_type == kEntryBlock) {
-    entry_target_bb_ = GetLLVMBlock(bb->fall_through);
-  } else if ((bb->fall_through != NullBasicBlockId) && !bb->terminated_by_return) {
-    irb_->CreateBr(GetLLVMBlock(bb->fall_through));
-  }
-
-  return false;
-}
-
-char RemapShorty(char shorty_type) {
-  /*
-   * TODO: might want to revisit this.  Dalvik registers are 32-bits wide,
-   * and longs/doubles are represented as a pair of registers.  When sub-word
-   * arguments (and method results) are passed, they are extended to Dalvik
-   * virtual register containers.  Because llvm is picky about type consistency,
-   * we must either cast the "real" type to 32-bit container multiple Dalvik
-   * register types, or always use the expanded values.
-   * Here, we're doing the latter.  We map the shorty signature to container
-   * types (which is valid so long as we always do a real expansion of passed
-   * arguments and field loads).
-   */
-  switch (shorty_type) {
-    case 'Z' : shorty_type = 'I'; break;
-    case 'B' : shorty_type = 'I'; break;
-    case 'S' : shorty_type = 'I'; break;
-    case 'C' : shorty_type = 'I'; break;
-    default: break;
-  }
-  return shorty_type;
-}
-
-::llvm::FunctionType* MirConverter::GetFunctionType() {
-  // Get return type
-  ::llvm::Type* ret_type = irb_->getJType(RemapShorty(cu_->shorty[0]));
-
-  // Get argument type
-  std::vector< ::llvm::Type*> args_type;
-
-  // method object
-  args_type.push_back(irb_->getJMethodTy());
-
-  // Do we have  a "this"?
-  if ((cu_->access_flags & kAccStatic) == 0) {
-    args_type.push_back(irb_->getJObjectTy());
-  }
-
-  for (uint32_t i = 1; i < strlen(cu_->shorty); ++i) {
-    args_type.push_back(irb_->getJType(RemapShorty(cu_->shorty[i])));
-  }
-
-  return ::llvm::FunctionType::get(ret_type, args_type, false);
-}
-
-bool MirConverter::CreateFunction() {
-  ::llvm::FunctionType* func_type = GetFunctionType();
-  if (func_type == NULL) {
-    return false;
-  }
-
-  func_ = ::llvm::Function::Create(func_type,
-                                      ::llvm::Function::InternalLinkage,
-                                      symbol_, module_);
-
-  ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
-  ::llvm::Function::arg_iterator arg_end(func_->arg_end());
-
-  arg_iter->setName("method");
-  ++arg_iter;
-
-  int start_sreg = mir_graph_->GetFirstInVR();
-
-  for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
-    arg_iter->setName(StringPrintf("v%i_0", start_sreg));
-    start_sreg += mir_graph_->reg_location_[start_sreg].wide ? 2 : 1;
-  }
-
-  return true;
-}
-
-bool MirConverter::CreateLLVMBasicBlock(BasicBlock* bb) {
-  // Skip the exit block
-  if ((bb->block_type == kDead) ||(bb->block_type == kExitBlock)) {
-    id_to_block_map_.Put(bb->id, NULL);
-  } else {
-    int offset = bb->start_offset;
-    bool entry_block = (bb->block_type == kEntryBlock);
-    ::llvm::BasicBlock* llvm_bb =
-        ::llvm::BasicBlock::Create(*context_, entry_block ? "entry" :
-                                 StringPrintf(kLabelFormat, bb->catch_entry ? kCatchBlock :
-                                              kNormalBlock, offset, bb->id), func_);
-    if (entry_block) {
-        entry_bb_ = llvm_bb;
-        placeholder_bb_ =
-            ::llvm::BasicBlock::Create(*context_, "placeholder",
-                                     func_);
-    }
-    id_to_block_map_.Put(bb->id, llvm_bb);
-  }
-  return false;
-}
-
-
-/*
- * Convert MIR to LLVM_IR
- *  o For each ssa name, create LLVM named value.  Type these
- *    appropriately, and ignore high half of wide and double operands.
- *  o For each MIR basic block, create an LLVM basic block.
- *  o Iterate through the MIR a basic block at a time, setting arguments
- *    to recovered ssa name.
- */
-void MirConverter::MethodMIR2Bitcode() {
-  InitIR();
-
-  // Create the function
-  CreateFunction();
-
-  // Create an LLVM basic block for each MIR block in dfs preorder
-  PreOrderDfsIterator iter(mir_graph_);
-  for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
-    CreateLLVMBasicBlock(bb);
-  }
-
-  /*
-   * Create an llvm named value for each MIR SSA name.  Note: we'll use
-   * placeholders for all non-argument values (because we haven't seen
-   * the definition yet).
-   */
-  irb_->SetInsertPoint(placeholder_bb_);
-  ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
-  arg_iter++;  /* Skip path method */
-  for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
-    ::llvm::Value* val;
-    RegLocation rl_temp = mir_graph_->reg_location_[i];
-    if ((mir_graph_->SRegToVReg(i) < 0) || rl_temp.high_word) {
-      llvm_values_.push_back(0);
-    } else if ((i < mir_graph_->GetFirstInVR()) ||
-               (i >= (mir_graph_->GetFirstTempVR()))) {
-      ::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ?
-         irb_->getJLong(0) : irb_->getJInt(0);
-      val = EmitConst(imm_value, mir_graph_->reg_location_[i]);
-      val->setName(mir_graph_->GetSSAName(i));
-      llvm_values_.push_back(val);
-    } else {
-      // Recover previously-created argument values
-      ::llvm::Value* arg_val = arg_iter++;
-      llvm_values_.push_back(arg_val);
-    }
-  }
-
-  PreOrderDfsIterator iter2(mir_graph_);
-  for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
-    BlockBitcodeConversion(bb);
-  }
-
-  /*
-   * In a few rare cases of verification failure, the verifier will
-   * replace one or more Dalvik opcodes with the special
-   * throw-verification-failure opcode.  This can leave the SSA graph
-   * in an invalid state, as definitions may be lost, while uses retained.
-   * To work around this problem, we insert placeholder definitions for
-   * all Dalvik SSA regs in the "placeholder" block.  Here, after
-   * bitcode conversion is complete, we examine those placeholder definitions
-   * and delete any with no references (which normally is all of them).
-   *
-   * If any definitions remain, we link the placeholder block into the
-   * CFG.  Otherwise, it is deleted.
-   */
-  for (::llvm::BasicBlock::iterator it = placeholder_bb_->begin(),
-       it_end = placeholder_bb_->end(); it != it_end;) {
-    ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(it++);
-    DCHECK(inst != NULL);
-    ::llvm::Value* val = ::llvm::dyn_cast< ::llvm::Value>(inst);
-    DCHECK(val != NULL);
-    if (val->getNumUses() == 0) {
-      inst->eraseFromParent();
-    }
-  }
-  SetDexOffset(0);
-  if (placeholder_bb_->empty()) {
-    placeholder_bb_->eraseFromParent();
-  } else {
-    irb_->SetInsertPoint(placeholder_bb_);
-    irb_->CreateBr(entry_target_bb_);
-    entry_target_bb_ = placeholder_bb_;
-  }
-  irb_->SetInsertPoint(entry_bb_);
-  irb_->CreateBr(entry_target_bb_);
-
-  if (cu_->enable_debug & (1 << kDebugVerifyBitcode)) {
-     if (::llvm::verifyFunction(*func_, ::llvm::PrintMessageAction)) {
-       LOG(INFO) << "Bitcode verification FAILED for "
-                 << PrettyMethod(cu_->method_idx, *cu_->dex_file)
-                 << " of size " << mir_graph_->GetNumDalvikInsns();
-       cu_->enable_debug |= (1 << kDebugDumpBitcodeFile);
-     }
-  }
-
-  if (cu_->enable_debug & (1 << kDebugDumpBitcodeFile)) {
-    // Write bitcode to file
-    std::string errmsg;
-    std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
-    mir_graph_->ReplaceSpecialChars(fname);
-    // TODO: make configurable change naming mechanism to avoid fname length issues.
-    fname = StringPrintf("/sdcard/Bitcode/%s.bc", fname.c_str());
-
-    if (fname.size() > 240) {
-      LOG(INFO) << "Warning: bitcode filename too long. Truncated.";
-      fname.resize(240);
-    }
-
-    ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
-        new ::llvm::tool_output_file(fname.c_str(), errmsg,
-                                   ::llvm::sys::fs::F_Binary));
-
-    if (!errmsg.empty()) {
-      LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
-    }
-
-    ::llvm::WriteBitcodeToFile(module_, out_file->os());
-    out_file->keep();
-  }
-}
-
-Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
-                               ArenaAllocator* const arena,
-                               llvm::LlvmCompilationUnit* const llvm_compilation_unit) {
-  return new MirConverter(cu, mir_graph, arena, llvm_compilation_unit);
-}
-
-}  // namespace art
diff --git a/compiler/dex/portable/mir_to_gbc.h b/compiler/dex/portable/mir_to_gbc.h
deleted file mode 100644
index bc4f5c4..0000000
--- a/compiler/dex/portable/mir_to_gbc.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
-#define ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
-
-#include <llvm/ADT/ArrayRef.h>
-#include <llvm/IR/BasicBlock.h>
-#include <llvm/IR/IRBuilder.h>
-#include <llvm/IR/LLVMContext.h>
-#include <llvm/IR/Module.h>
-
-#include "invoke_type.h"
-#include "compiled_method.h"
-#include "dex/compiler_enums.h"
-#include "dex/compiler_ir.h"
-#include "dex/backend.h"
-#include "llvm/intrinsic_helper.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "safe_map.h"
-#include "utils/arena_containers.h"
-
-namespace llvm {
-  class Module;
-  class LLVMContext;
-}
-
-namespace art {
-
-namespace llvm {
-  class IntrinsicHelper;
-  class IRBuilder;
-}
-
-class LLVMInfo {
-  public:
-    LLVMInfo();
-    ~LLVMInfo();
-
-    ::llvm::LLVMContext* GetLLVMContext() {
-      return llvm_context_.get();
-    }
-
-    ::llvm::Module* GetLLVMModule() {
-      return llvm_module_;
-    }
-
-    art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
-      return intrinsic_helper_.get();
-    }
-
-    art::llvm::IRBuilder* GetIRBuilder() {
-      return ir_builder_.get();
-    }
-
-  private:
-    std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
-    ::llvm::Module* llvm_module_;  // Managed by context_.
-    std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
-    std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
-};
-
-class BasicBlock;
-struct CallInfo;
-struct CompilationUnit;
-struct MIR;
-struct RegLocation;
-struct RegisterInfo;
-class MIRGraph;
-
-// Target-specific initialization.
-Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
-                               ArenaAllocator* const arena,
-                               llvm::LlvmCompilationUnit* const llvm_compilation_unit);
-
-class MirConverter : public Backend {
-  public:
-    // TODO: flesh out and integrate into new world order.
-    MirConverter(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena,
-                 llvm::LlvmCompilationUnit* llvm_compilation_unit)
-      : Backend(arena),
-        cu_(cu),
-        mir_graph_(mir_graph),
-        llvm_compilation_unit_(llvm_compilation_unit),
-        llvm_info_(llvm_compilation_unit->GetQuickContext()),
-        symbol_(llvm_compilation_unit->GetDexCompilationUnit()->GetSymbol()),
-        context_(NULL),
-        module_(NULL),
-        func_(NULL),
-        intrinsic_helper_(NULL),
-        irb_(NULL),
-        placeholder_bb_(NULL),
-        entry_bb_(NULL),
-        entry_target_bb_(NULL),
-        llvm_values_(arena->Adapter()),
-        temp_name_(0),
-        current_dalvik_offset_(0) {
-      llvm_values_.reserve(mir_graph->GetNumSSARegs());
-      if (kIsDebugBuild) {
-        cu->enable_debug |= (1 << kDebugVerifyBitcode);
-      }
-    }
-
-    void Materialize() {
-      MethodMIR2Bitcode();
-    }
-
-    CompiledMethod* GetCompiledMethod() {
-      return NULL;
-    }
-
-  private:
-    ::llvm::BasicBlock* GetLLVMBlock(int id);
-    ::llvm::Value* GetLLVMValue(int s_reg);
-    void SetVregOnValue(::llvm::Value* val, int s_reg);
-    void DefineValueOnly(::llvm::Value* val, int s_reg);
-    void DefineValue(::llvm::Value* val, int s_reg);
-    ::llvm::Type* LlvmTypeFromLocRec(RegLocation loc);
-    void InitIR();
-    ::llvm::BasicBlock* FindCaseTarget(uint32_t vaddr);
-    void ConvertPackedSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
-                             RegLocation rl_src);
-    void ConvertSparseSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
-                             RegLocation rl_src);
-    void ConvertSget(int32_t field_index,
-                     art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
-    void ConvertSput(int32_t field_index,
-                     art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
-    void ConvertFillArrayData(int32_t offset, RegLocation rl_array);
-    ::llvm::Value* EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
-                             RegLocation loc);
-    void EmitPopShadowFrame();
-    ::llvm::Value* EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
-                            RegLocation loc);
-    void ConvertMoveException(RegLocation rl_dest);
-    void ConvertThrow(RegLocation rl_src);
-    void ConvertMonitorEnterExit(int opt_flags,
-                                 art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
-    void ConvertArrayLength(int opt_flags, RegLocation rl_dest,
-                            RegLocation rl_src);
-    void EmitSuspendCheck();
-    ::llvm::Value* ConvertCompare(ConditionCode cc,
-                                  ::llvm::Value* src1, ::llvm::Value* src2);
-    void ConvertCompareAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
-                                 RegLocation rl_src1, RegLocation rl_src2);
-    void ConvertCompareZeroAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
-                                     RegLocation rl_src1);
-    ::llvm::Value* GenDivModOp(bool is_div, bool is_long, ::llvm::Value* src1,
-                               ::llvm::Value* src2);
-    ::llvm::Value* GenArithOp(OpKind op, bool is_long, ::llvm::Value* src1,
-                              ::llvm::Value* src2);
-    void ConvertFPArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
-                          RegLocation rl_src2);
-    void ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
-                      RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
-    void ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
-                         RegLocation rl_dest, RegLocation rl_src, int shift_amount);
-    void ConvertArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
-                        RegLocation rl_src2);
-    void ConvertArithOpLit(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
-                           int32_t imm);
-    void ConvertInvoke(BasicBlock* bb, MIR* mir, InvokeType invoke_type,
-                       bool is_range, bool is_filled_new_array);
-    void ConvertConstObject(uint32_t idx,
-                            art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
-    void ConvertCheckCast(uint32_t type_idx, RegLocation rl_src);
-    void ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest);
-    void ConvertNewArray(uint32_t type_idx, RegLocation rl_dest,
-                         RegLocation rl_src);
-    void ConvertAget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
-                     RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index);
-    void ConvertAput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
-                     RegLocation rl_src, RegLocation rl_array, RegLocation rl_index);
-    void ConvertIget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
-                     RegLocation rl_dest, RegLocation rl_obj, int field_index);
-    void ConvertIput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
-                     RegLocation rl_src, RegLocation rl_obj, int field_index);
-    void ConvertInstanceOf(uint32_t type_idx, RegLocation rl_dest,
-                           RegLocation rl_src);
-    void ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src);
-    void ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src);
-    void ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src);
-    void ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src);
-    void ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
-                               RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
-    void ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
-                             art::llvm::IntrinsicHelper::IntrinsicId id);
-    void ConvertNeg(RegLocation rl_dest, RegLocation rl_src);
-    void ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest, RegLocation rl_src);
-    void ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
-                        RegLocation rl_dest, RegLocation rl_src);
-    void ConvertNegFP(RegLocation rl_dest, RegLocation rl_src);
-    void ConvertNot(RegLocation rl_dest, RegLocation rl_src);
-    void EmitConstructorBarrier();
-    bool ConvertMIRNode(MIR* mir, BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
-    void SetDexOffset(int32_t offset);
-    void SetMethodInfo();
-    void HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
-    void ConvertExtendedMIR(BasicBlock* bb, MIR* mir, ::llvm::BasicBlock* llvm_bb);
-    bool BlockBitcodeConversion(BasicBlock* bb);
-    ::llvm::FunctionType* GetFunctionType();
-    bool CreateFunction();
-    bool CreateLLVMBasicBlock(BasicBlock* bb);
-    void MethodMIR2Bitcode();
-
-    CompilationUnit* cu_;
-    MIRGraph* mir_graph_;
-    llvm::LlvmCompilationUnit* const llvm_compilation_unit_;
-    LLVMInfo* llvm_info_;
-    std::string symbol_;
-    ::llvm::LLVMContext* context_;
-    ::llvm::Module* module_;
-    ::llvm::Function* func_;
-    art::llvm::IntrinsicHelper* intrinsic_helper_;
-    art::llvm::IRBuilder* irb_;
-    ::llvm::BasicBlock* placeholder_bb_;
-    ::llvm::BasicBlock* entry_bb_;
-    ::llvm::BasicBlock* entry_target_bb_;
-    std::string bitcode_filename_;
-    ArenaVector< ::llvm::Value*> llvm_values_;
-    int32_t temp_name_;
-    SafeMap<int32_t, ::llvm::BasicBlock*> id_to_block_map_;  // block id -> llvm bb.
-    int current_dalvik_offset_;
-};  // Class MirConverter
-
-}  // namespace art
-
-#endif  // ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc
index 675dbcf..9262440 100644
--- a/compiler/dex/post_opt_passes.cc
+++ b/compiler/dex/post_opt_passes.cc
@@ -15,40 +15,11 @@
  */
 
 #include "post_opt_passes.h"
-#include "dataflow_iterator.h"
+
 #include "dataflow_iterator-inl.h"
 
 namespace art {
 
-/*
- * MethodUseCount pass implementation start.
- */
-bool MethodUseCount::Gate(const PassDataHolder* data) const {
-  DCHECK(data != nullptr);
-  CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-  DCHECK(c_unit != nullptr);
-  // First initialize the data.
-  c_unit->mir_graph->InitializeMethodUses();
-
-  // Now check if the pass is to be ignored.
-  bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
-
-  return res;
-}
-
-bool MethodUseCount::Worker(PassDataHolder* data) const {
-  DCHECK(data != nullptr);
-  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
-  CompilationUnit* c_unit = pass_me_data_holder->c_unit;
-  DCHECK(c_unit != nullptr);
-  BasicBlock* bb = pass_me_data_holder->bb;
-  DCHECK(bb != nullptr);
-  c_unit->mir_graph->CountUses(bb);
-  // No need of repeating, so just return false.
-  return false;
-}
-
-
 bool ClearPhiInstructions::Worker(PassDataHolder* data) const {
   DCHECK(data != nullptr);
   PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index 7b84ba8..1ab8625 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -17,20 +17,41 @@
 #ifndef ART_COMPILER_DEX_POST_OPT_PASSES_H_
 #define ART_COMPILER_DEX_POST_OPT_PASSES_H_
 
-#include "dex/quick/mir_to_lir.h"
-#include "compiler_internals.h"
+#include "base/casts.h"
+#include "base/logging.h"
+#include "compiler_ir.h"
+#include "dex_flags.h"
+#include "mir_graph.h"
 #include "pass_me.h"
 
 namespace art {
 
 /**
- * @class InitializeData
+ * @class PassMEMirSsaRep
+ * @brief Convenience class for passes that check MIRGraph::MirSsaRepUpToDate().
+ */
+class PassMEMirSsaRep : public PassME {
+ public:
+  PassMEMirSsaRep(const char* name, DataFlowAnalysisMode type = kAllNodes)
+      : PassME(name, type) {
+  }
+
+  bool Gate(const PassDataHolder* data) const OVERRIDE {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return !c_unit->mir_graph->MirSsaRepUpToDate();
+  }
+};
+
+/**
+ * @class InitializeSSATransformation
  * @brief There is some data that needs to be initialized before performing
  * the post optimization passes.
  */
-class InitializeData : public PassME {
+class InitializeSSATransformation : public PassMEMirSsaRep {
  public:
-  InitializeData() : PassME("InitializeData", kNoNodes) {
+  InitializeSSATransformation() : PassMEMirSsaRep("InitializeSSATransformation", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -39,32 +60,18 @@
     DCHECK(data != nullptr);
     CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
-    c_unit->mir_graph.get()->InitializeBasicBlockData();
-    c_unit->mir_graph.get()->SSATransformationStart();
+    c_unit->mir_graph->SSATransformationStart();
+    c_unit->mir_graph->CompilerInitializeSSAConversion();
   }
 };
 
 /**
- * @class MethodUseCount
- * @brief Count the register uses of the method
- */
-class MethodUseCount : public PassME {
- public:
-  MethodUseCount() : PassME("UseCount") {
-  }
-
-  bool Worker(PassDataHolder* data) const;
-
-  bool Gate(const PassDataHolder* data) const;
-};
-
-/**
  * @class ClearPhiInformation
  * @brief Clear the PHI nodes from the CFG.
  */
-class ClearPhiInstructions : public PassME {
+class ClearPhiInstructions : public PassMEMirSsaRep {
  public:
-  ClearPhiInstructions() : PassME("ClearPhiInstructions") {
+  ClearPhiInstructions() : PassMEMirSsaRep("ClearPhiInstructions") {
   }
 
   bool Worker(PassDataHolder* data) const;
@@ -115,12 +122,18 @@
   BuildDomination() : PassME("BuildDomination", kNoNodes) {
   }
 
+  bool Gate(const PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return !c_unit->mir_graph->DominationUpToDate();
+  }
+
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
     CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
-    c_unit->mir_graph.get()->ComputeDominators();
-    c_unit->mir_graph.get()->CompilerInitializeSSAConversion();
+    c_unit->mir_graph->ComputeDominators();
   }
 
   void End(PassDataHolder* data) const {
@@ -143,6 +156,13 @@
   TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) {
   }
 
+  bool Gate(const PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    return !c_unit->mir_graph->TopologicalOrderUpToDate();
+  }
+
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
     CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
@@ -155,9 +175,9 @@
  * @class DefBlockMatrix
  * @brief Calculate the matrix of definition per basic block
  */
-class DefBlockMatrix : public PassME {
+class DefBlockMatrix : public PassMEMirSsaRep {
  public:
-  DefBlockMatrix() : PassME("DefBlockMatrix", kNoNodes) {
+  DefBlockMatrix() : PassMEMirSsaRep("DefBlockMatrix", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -169,37 +189,19 @@
 };
 
 /**
- * @class CreatePhiNodes
- * @brief Pass to create the phi nodes after SSA calculation
+ * @class FindPhiNodeBlocksPass
+ * @brief Pass to find out where we need to insert the phi nodes for the SSA conversion.
  */
-class CreatePhiNodes : public PassME {
+class FindPhiNodeBlocksPass : public PassMEMirSsaRep {
  public:
-  CreatePhiNodes() : PassME("CreatePhiNodes", kNoNodes) {
+  FindPhiNodeBlocksPass() : PassMEMirSsaRep("FindPhiNodeBlocks", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
     DCHECK(data != nullptr);
     CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
-    c_unit->mir_graph.get()->InsertPhiNodes();
-  }
-};
-
-/**
- * @class ClearVisitedFlag
- * @brief Pass to clear the visited flag for all basic blocks.
- */
-
-class ClearVisitedFlag : public PassME {
- public:
-  ClearVisitedFlag() : PassME("ClearVisitedFlag", kNoNodes) {
-  }
-
-  void Start(PassDataHolder* data) const {
-    DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(c_unit != nullptr);
-    c_unit->mir_graph.get()->ClearAllVisitedFlags();
+    c_unit->mir_graph.get()->FindPhiNodeBlocks();
   }
 };
 
@@ -207,9 +209,9 @@
  * @class SSAConversion
  * @brief Pass for SSA conversion of MIRs
  */
-class SSAConversion : public PassME {
+class SSAConversion : public PassMEMirSsaRep {
  public:
-  SSAConversion() : PassME("SSAConversion", kNoNodes) {
+  SSAConversion() : PassMEMirSsaRep("SSAConversion", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -217,6 +219,7 @@
     CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
     DCHECK(c_unit != nullptr);
     MIRGraph *mir_graph = c_unit->mir_graph.get();
+    mir_graph->ClearAllVisitedFlags();
     mir_graph->DoDFSPreOrderSSARename(mir_graph->GetEntryBlock());
   }
 };
@@ -225,9 +228,9 @@
  * @class PhiNodeOperands
  * @brief Pass to insert the Phi node operands to basic blocks
  */
-class PhiNodeOperands : public PassME {
+class PhiNodeOperands : public PassMEMirSsaRep {
  public:
-  PhiNodeOperands() : PassME("PhiNodeOperands", kPreOrderDFSTraversal) {
+  PhiNodeOperands() : PassMEMirSsaRep("PhiNodeOperands", kPreOrderDFSTraversal) {
   }
 
   bool Worker(PassDataHolder* data) const {
@@ -246,9 +249,9 @@
  * @class InitRegLocations
  * @brief Initialize Register Locations.
  */
-class PerformInitRegLocations : public PassME {
+class PerformInitRegLocations : public PassMEMirSsaRep {
  public:
-  PerformInitRegLocations() : PassME("PerformInitRegLocation", kNoNodes) {
+  PerformInitRegLocations() : PassMEMirSsaRep("PerformInitRegLocation", kNoNodes) {
   }
 
   void Start(PassDataHolder* data) const {
@@ -260,40 +263,32 @@
 };
 
 /**
- * @class ConstantPropagation
- * @brief Perform a constant propagation pass.
+ * @class TypeInference
+ * @brief Type inference pass.
  */
-class ConstantPropagation : public PassME {
+class TypeInference : public PassMEMirSsaRep {
  public:
-  ConstantPropagation() : PassME("ConstantPropagation") {
+  TypeInference() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) {
   }
 
   bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
     DCHECK(c_unit != nullptr);
-    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+    BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    c_unit->mir_graph->DoConstantPropagation(bb);
-    // No need of repeating, so just return false.
-    return false;
-  }
-
-  void Start(PassDataHolder* data) const {
-    DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(c_unit != nullptr);
-    c_unit->mir_graph->InitializeConstantPropagation();
+    return c_unit->mir_graph->InferTypes(bb);
   }
 };
 
 /**
- * @class FreeData
+ * @class FinishSSATransformation
  * @brief There is some data that needs to be freed after performing the post optimization passes.
  */
-class FreeData : public PassME {
+class FinishSSATransformation : public PassMEMirSsaRep {
  public:
-  FreeData() : PassME("FreeData", kNoNodes) {
+  FinishSSATransformation() : PassMEMirSsaRep("FinishSSATransformation", kNoNodes) {
   }
 
   void End(PassDataHolder* data) const {
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index b9d9a11..9717459 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -17,7 +17,9 @@
 #ifndef ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_
 #define ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_
 
-#include "dex/compiler_internals.h"
+#include "dex/compiler_enums.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
 
 namespace art {
 
@@ -481,10 +483,10 @@
   kThumb2LsrRRR,     // lsr [111110100010] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
   kThumb2AsrRRR,     // asr [111110100100] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
   kThumb2RorRRR,     // ror [111110100110] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
-  kThumb2LslRRI5,    // lsl [11101010010011110] imm[14.12] rd[11..8] [00] rm[3..0].
-  kThumb2LsrRRI5,    // lsr [11101010010011110] imm[14.12] rd[11..8] [01] rm[3..0].
-  kThumb2AsrRRI5,    // asr [11101010010011110] imm[14.12] rd[11..8] [10] rm[3..0].
-  kThumb2RorRRI5,    // ror [11101010010011110] imm[14.12] rd[11..8] [11] rm[3..0].
+  kThumb2LslRRI5,    // lsl [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [00] rm[3..0].
+  kThumb2LsrRRI5,    // lsr [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [01] rm[3..0].
+  kThumb2AsrRRI5,    // asr [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [10] rm[3..0].
+  kThumb2RorRRI5,    // ror [11101010010011110] imm3[14..12] rd[11..8] imm2[7..6] [11] rm[3..0].
   kThumb2BicRRI8M,   // bic rd, rn, #<const> [11110] i [000010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
   kThumb2AndRRI8M,   // and rd, rn, #<const> [11110] i [000000] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
   kThumb2OrrRRI8M,   // orr rd, rn, #<const> [11110] i [000100] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
@@ -512,7 +514,8 @@
   kThumb2Vnegs,      // vneg.f32 [111011101] D [110000] rd[15-12] [1010110] M [0] vm[3-0].
   kThumb2Vmovs_IMM8,  // vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12] [10100000] imm4l[3-0].
   kThumb2Vmovd_IMM8,  // vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12] [10110000] imm4l[3-0].
-  kThumb2Mla,        // mla [111110110000] rn[19-16] ra[15-12] rd[7-4] [0000] rm[3-0].
+  kThumb2Mla,        // mla [111110110000] rn[19-16] ra[15-12] rd[11-8] [0000] rm[3-0].
+  kThumb2Mls,        // mls [111110110000] rn[19-16] ra[15-12] rd[11-8] [0001] rm[3-0].
   kThumb2Umull,      // umull [111110111010] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0].
   kThumb2Ldrex,      // ldrex [111010000101] rn[19-16] rt[15-12] [1111] imm8[7-0].
   kThumb2Ldrexd,     // ldrexd [111010001101] rn[19-16] rt[15-12] rt2[11-8] [11111111].
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index de93e26..3d64833 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-#include "arm_lir.h"
 #include "codegen_arm.h"
+
+#include "arm_lir.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
 #include "dex/quick/mir_to_lir-inl.h"
 
 namespace art {
@@ -896,6 +899,10 @@
                  kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
                  kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE123,
                  "mla", "!0C, !1C, !2C, !3C", 4, kFixupNone),
+    ENCODING_MAP(kThumb2Mls,  0xfb000010,
+                 kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+                 kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0_USE123,
+                 "mls", "!0C, !1C, !2C, !3C", 4, kFixupNone),
     ENCODING_MAP(kThumb2Umull,  0xfba00000,
                  kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
                  kFmtBitBlt, 3, 0,
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 99b2166..1b5dde2 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -16,13 +16,18 @@
 
 /* This file contains codegen for the Thumb2 ISA. */
 
-#include "arm_lir.h"
 #include "codegen_arm.h"
+
+#include "arm_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 #include "gc/accounting/card_table.h"
 #include "mirror/art_method.h"
 #include "mirror/object_array-inl.h"
 #include "entrypoints/quick/quick_entrypoints.h"
+#include "utils.h"
 
 namespace art {
 
@@ -47,16 +52,13 @@
  */
 void ArmMir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
-  if (cu_->verbose) {
-    DumpSparseSwitchTable(table);
-  }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint32_t size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // Get the switch value
@@ -95,17 +97,13 @@
 
 void ArmMir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
-  if (cu_->verbose) {
-    DumpPackedSwitchTable(table);
-  }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable),  kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint32_t size = table[1];
-  tab_rec->targets =
-      static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // Get the switch value
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 0bc4c3b..025e69f 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -18,24 +18,16 @@
 #define ART_COMPILER_DEX_QUICK_ARM_CODEGEN_ARM_H_
 
 #include "arm_lir.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir.h"
 #include "utils/arena_containers.h"
 
 namespace art {
 
+struct CompilationUnit;
+
 class ArmMir2Lir FINAL : public Mir2Lir {
  protected:
-  // TODO: Consolidate hard float target support.
-  // InToRegStorageMapper and InToRegStorageMapping can be shared with all backends.
-  // Base class used to get RegStorage for next argument.
-  class InToRegStorageMapper {
-   public:
-    virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide) = 0;
-    virtual ~InToRegStorageMapper() {
-    }
-  };
-
   // Inherited class for ARM backend.
   class InToRegStorageArmMapper FINAL : public InToRegStorageMapper {
    public:
@@ -43,45 +35,25 @@
         : cur_core_reg_(0), cur_fp_reg_(0), cur_fp_double_reg_(0) {
     }
 
-    virtual ~InToRegStorageArmMapper() {
-    }
+    RegStorage GetNextReg(ShortyArg arg) OVERRIDE;
 
-    RegStorage GetNextReg(bool is_double_or_float, bool is_wide) OVERRIDE;
+    virtual void Reset() OVERRIDE {
+      cur_core_reg_ = 0;
+      cur_fp_reg_ = 0;
+      cur_fp_double_reg_ = 0;
+    }
 
    private:
-    uint32_t cur_core_reg_;
-    uint32_t cur_fp_reg_;
-    uint32_t cur_fp_double_reg_;
+    size_t cur_core_reg_;
+    size_t cur_fp_reg_;
+    size_t cur_fp_double_reg_;
   };
 
-  // Class to map argument to RegStorage. The mapping object is initialized by a mapper.
-  class InToRegStorageMapping FINAL {
-   public:
-    InToRegStorageMapping()
-        : max_mapped_in_(0), is_there_stack_mapped_(false), initialized_(false) {
-    }
-
-    int GetMaxMappedIn() const {
-      return max_mapped_in_;
-    }
-
-    bool IsThereStackMapped() const {
-      return is_there_stack_mapped_;
-    }
-
-    bool IsInitialized() const {
-      return initialized_;
-    }
-
-    void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
-    RegStorage Get(int in_position) const;
-
-   private:
-    std::map<int, RegStorage> mapping_;
-    int max_mapped_in_;
-    bool is_there_stack_mapped_;
-    bool initialized_;
-  };
+  InToRegStorageArmMapper in_to_reg_storage_arm_mapper_;
+  InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+    in_to_reg_storage_arm_mapper_.Reset();
+    return &in_to_reg_storage_arm_mapper_;
+  }
 
   public:
     ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -127,7 +99,6 @@
       }
     }
 
-    RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
     RegLocation GetReturnAlt() OVERRIDE;
     RegLocation GetReturnWideAlt() OVERRIDE;
     RegLocation LocCReturn() OVERRIDE;
@@ -213,6 +184,8 @@
     void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
     void GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
     void GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+    void GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                        RegLocation rl_src3, bool is_sub);
 
     // Required for target - single operation generators.
     LIR* OpUnconditionalBranch(LIR* target);
@@ -290,18 +263,10 @@
     LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
     size_t GetInstructionOffset(LIR* lir);
 
-    int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                             NextCallInsn next_call_insn,
-                             const MethodReference& target_method,
-                             uint32_t vtable_idx,
-                             uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                             bool skip_this) OVERRIDE;
-    int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                           NextCallInsn next_call_insn,
-                           const MethodReference& target_method,
-                           uint32_t vtable_idx,
-                           uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                           bool skip_this) OVERRIDE;
+    void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE;
+
+    bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
+                          RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE;
 
   private:
     void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
@@ -361,7 +326,7 @@
                                      RegStorage::FloatSolo32(reg_num * 2 + 1));
     }
 
-    InToRegStorageMapping in_to_reg_storage_mapping_;
+    int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 2b2592d..eb1383f 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-#include "arm_lir.h"
 #include "codegen_arm.h"
+
+#include "arm_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 
 namespace art {
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 1a7b439..3159886 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -16,13 +16,19 @@
 
 /* This file contains codegen for the Thumb2 ISA. */
 
+#include "codegen_arm.h"
+
 #include "arch/instruction_set_features.h"
 #include "arm_lir.h"
-#include "codegen_arm.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/array-inl.h"
+#include "utils.h"
 
 namespace art {
 
@@ -567,21 +573,30 @@
 
 // Try to convert *lit to 1 RegRegRegShift/RegRegShift form.
 bool ArmMir2Lir::GetEasyMultiplyOp(int lit, ArmMir2Lir::EasyMultiplyOp* op) {
+  if (lit == 0) {
+    // Special case for *divide-by-zero*. The ops won't actually be used to generate code, as
+    // GenArithOpIntLit will directly generate exception-throwing code, and multiply-by-zero will
+    // have been optimized away earlier.
+    op->op = kOpInvalid;
+    op->shift = 0;
+    return true;
+  }
+
   if (IsPowerOfTwo(lit)) {
     op->op = kOpLsl;
-    op->shift = LowestSetBit(lit);
+    op->shift = CTZ(lit);
     return true;
   }
 
   if (IsPowerOfTwo(lit - 1)) {
     op->op = kOpAdd;
-    op->shift = LowestSetBit(lit - 1);
+    op->shift = CTZ(lit - 1);
     return true;
   }
 
   if (IsPowerOfTwo(lit + 1)) {
     op->op = kOpRsub;
-    op->shift = LowestSetBit(lit + 1);
+    op->shift = CTZ(lit + 1);
     return true;
   }
 
@@ -599,7 +614,7 @@
   }
 
   int lit1 = lit;
-  uint32_t shift = LowestSetBit(lit1);
+  uint32_t shift = CTZ(lit1);
   if (GetEasyMultiplyOp(lit1 >> shift, &ops[0])) {
     ops[1].op = kOpLsl;
     ops[1].shift = shift;
@@ -607,7 +622,7 @@
   }
 
   lit1 = lit - 1;
-  shift = LowestSetBit(lit1);
+  shift = CTZ(lit1);
   if (GetEasyMultiplyOp(lit1 >> shift, &ops[0])) {
     ops[1].op = kOpAdd;
     ops[1].shift = shift;
@@ -615,7 +630,7 @@
   }
 
   lit1 = lit + 1;
-  shift = LowestSetBit(lit1);
+  shift = CTZ(lit1);
   if (GetEasyMultiplyOp(lit1 >> shift, &ops[0])) {
     ops[1].op = kOpRsub;
     ops[1].shift = shift;
@@ -1075,6 +1090,17 @@
   return NewLIR3(kThumb2Vstms, r_base.GetReg(), rs_fr0.GetReg(), count);
 }
 
+void ArmMir2Lir::GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                RegLocation rl_src3, bool is_sub) {
+  rl_src1 = LoadValue(rl_src1, kCoreReg);
+  rl_src2 = LoadValue(rl_src2, kCoreReg);
+  rl_src3 = LoadValue(rl_src3, kCoreReg);
+  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+  NewLIR4(is_sub ? kThumb2Mls : kThumb2Mla, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
+          rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
+  StoreValue(rl_dest, rl_result);
+}
+
 void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
                                                RegLocation rl_result, int lit,
                                                int first_bit, int second_bit) {
@@ -1119,7 +1145,7 @@
 }
 
 bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
-  if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+  if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
     return false;
   }
   // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
@@ -1635,4 +1661,19 @@
   StoreValueWide(rl_dest, rl_result);
 }
 
+bool ArmMir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
+                                  RegLocation rl_src, RegLocation rl_dest, int lit) {
+  if (lit < 2) {
+    return false;
+  }
+
+  // ARM does either not support a division instruction, or it is potentially expensive. Look for
+  // more special cases.
+  if (!IsPowerOfTwo(lit)) {
+    return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
+  }
+
+  return Mir2Lir::HandleEasyDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 0e8f645..13f9072 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -19,9 +19,11 @@
 #include <inttypes.h>
 
 #include <string>
+#include <sstream>
 
 #include "backend_arm.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 
 namespace art {
@@ -489,6 +491,24 @@
        buf += *fmt++;
     }
   }
+  // Dump thread offset.
+  std::string fmt_str = GetTargetInstFmt(lir->opcode);
+  if (std::string::npos != fmt_str.find(", [!1C, #!2") && rARM_SELF == lir->operands[1] &&
+      std::string::npos != buf.find(", [")) {
+    int offset = lir->operands[2];
+    if (std::string::npos != fmt_str.find("#!2d")) {
+    } else if (std::string::npos != fmt_str.find("#!2E")) {
+      offset *= 4;
+    } else if (std::string::npos != fmt_str.find("#!2F")) {
+      offset *= 2;
+    } else {
+      LOG(FATAL) << "Should not reach here";
+    }
+    std::ostringstream tmp_stream;
+    Thread::DumpThreadOffset<4>(tmp_stream, offset);
+    buf += "  ; ";
+    buf += tmp_stream.str();
+  }
   return buf;
 }
 
@@ -749,6 +769,7 @@
   FreeTemp(rs_r1);
   FreeTemp(rs_r2);
   FreeTemp(rs_r3);
+  FreeTemp(TargetReg(kHiddenArg));
   if (!kArm32QuickCodeUseSoftFloat) {
     FreeTemp(rs_fr0);
     FreeTemp(rs_fr1);
@@ -896,7 +917,7 @@
   Mir2Lir::InstallLiteralPools();
 }
 
-RegStorage ArmMir2Lir::InToRegStorageArmMapper::GetNextReg(bool is_double_or_float, bool is_wide) {
+RegStorage ArmMir2Lir::InToRegStorageArmMapper::GetNextReg(ShortyArg arg) {
   const RegStorage coreArgMappingToPhysicalReg[] =
       {rs_r1, rs_r2, rs_r3};
   const int coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
@@ -906,28 +927,18 @@
   constexpr uint32_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
   static_assert(fpArgMappingToPhysicalRegSize % 2 == 0, "Number of FP Arg regs is not even");
 
-  if (kArm32QuickCodeUseSoftFloat) {
-    is_double_or_float = false;  // Regard double as long, float as int.
-    is_wide = false;  // Map long separately.
-  }
-
   RegStorage result = RegStorage::InvalidReg();
-  if (is_double_or_float) {
-    // TODO: Remove "cur_fp_double_reg_ % 2 != 0" when we return double as double.
-    if (is_wide || cur_fp_double_reg_ % 2 != 0) {
+  // Regard double as long, float as int for kArm32QuickCodeUseSoftFloat.
+  if (arg.IsFP() && !kArm32QuickCodeUseSoftFloat) {
+    if (arg.IsWide()) {
       cur_fp_double_reg_ = std::max(cur_fp_double_reg_, RoundUp(cur_fp_reg_, 2));
       if (cur_fp_double_reg_ < fpArgMappingToPhysicalRegSize) {
-        // TODO: Replace by following code in the branch when FlushIns() support 64-bit registers.
-        // result = RegStorage::MakeRegPair(fpArgMappingToPhysicalReg[cur_fp_double_reg_],
-        //                                  fpArgMappingToPhysicalReg[cur_fp_double_reg_ + 1]);
-        // result = As64BitFloatReg(result);
-        // cur_fp_double_reg_ += 2;
-        result = fpArgMappingToPhysicalReg[cur_fp_double_reg_];
-        cur_fp_double_reg_++;
+        result = RegStorage::MakeRegPair(fpArgMappingToPhysicalReg[cur_fp_double_reg_],
+                                         fpArgMappingToPhysicalReg[cur_fp_double_reg_ + 1]);
+        result = As64BitFloatReg(result);
+        cur_fp_double_reg_ += 2;
       }
     } else {
-      // TODO: Remove the check when we return double as double.
-      DCHECK_EQ(cur_fp_double_reg_ % 2, 0U);
       if (cur_fp_reg_ % 2 == 0) {
         cur_fp_reg_ = std::max(cur_fp_double_reg_, cur_fp_reg_);
       }
@@ -938,271 +949,54 @@
     }
   } else {
     if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+      if (!kArm32QuickCodeUseSoftFloat && arg.IsWide() && cur_core_reg_ == 0) {
+        // Skip r1, and use r2-r3 for the register pair.
+        cur_core_reg_++;
+      }
       result = coreArgMappingToPhysicalReg[cur_core_reg_++];
-      // TODO: Enable following code when FlushIns() support 64-bit registers.
-      // if (is_wide && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
-      //   result = RegStorage::MakeRegPair(result, coreArgMappingToPhysicalReg[cur_core_reg_++]);
-      // }
+      if (arg.IsWide() && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+        result = RegStorage::MakeRegPair(result, coreArgMappingToPhysicalReg[cur_core_reg_++]);
+      }
     }
   }
   return result;
 }
 
-RegStorage ArmMir2Lir::InToRegStorageMapping::Get(int in_position) const {
-  DCHECK(IsInitialized());
-  auto res = mapping_.find(in_position);
-  return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
-}
-
-void ArmMir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
-                                                   InToRegStorageMapper* mapper) {
-  DCHECK(mapper != nullptr);
-  max_mapped_in_ = -1;
-  is_there_stack_mapped_ = false;
-  for (int in_position = 0; in_position < count; in_position++) {
-     RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
-                                         arg_locs[in_position].wide);
-     if (reg.Valid()) {
-       mapping_[in_position] = reg;
-       // TODO: Enable the following code when FlushIns() support 64-bit argument registers.
-       // if (arg_locs[in_position].wide) {
-       //  if (reg.Is32Bit()) {
-       //    // As it is a split long, the hi-part is on stack.
-       //    is_there_stack_mapped_ = true;
-       //  }
-       //  // We covered 2 v-registers, so skip the next one
-       //  in_position++;
-       // }
-       max_mapped_in_ = std::max(max_mapped_in_, in_position);
-     } else {
-       is_there_stack_mapped_ = true;
-     }
-  }
-  initialized_ = true;
-}
-
-// TODO: Should be able to return long, double registers.
-// Need check some common code as it will break some assumption.
-RegStorage ArmMir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
-  if (!in_to_reg_storage_mapping_.IsInitialized()) {
-    int start_vreg = mir_graph_->GetFirstInVR();
-    RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
-
-    InToRegStorageArmMapper mapper;
-    in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
-  }
-  return in_to_reg_storage_mapping_.Get(arg_num);
-}
-
-int ArmMir2Lir::GenDalvikArgsNoRange(CallInfo* info,
-                                     int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
-                                     const MethodReference& target_method,
-                                     uint32_t vtable_idx, uintptr_t direct_code,
-                                     uintptr_t direct_method, InvokeType type, bool skip_this) {
+int ArmMir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) {
   if (kArm32QuickCodeUseSoftFloat) {
-    return Mir2Lir::GenDalvikArgsNoRange(info, call_state, pcrLabel, next_call_insn, target_method,
-                                         vtable_idx, direct_code, direct_method, type, skip_this);
-  } else {
-    return GenDalvikArgsRange(info, call_state, pcrLabel, next_call_insn, target_method, vtable_idx,
-                              direct_code, direct_method, type, skip_this);
+    return Mir2Lir::GenDalvikArgsBulkCopy(info, first, count);
   }
+  /*
+   * TODO: Improve by adding block copy for large number of arguments.  For now, just
+   * copy a Dalvik vreg at a time.
+   */
+  return count;
 }
 
-int ArmMir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
-                                   LIR** pcrLabel, NextCallInsn next_call_insn,
-                                   const MethodReference& target_method,
-                                   uint32_t vtable_idx, uintptr_t direct_code,
-                                   uintptr_t direct_method, InvokeType type, bool skip_this) {
-  if (kArm32QuickCodeUseSoftFloat) {
-    return Mir2Lir::GenDalvikArgsRange(info, call_state, pcrLabel, next_call_insn, target_method,
-                                       vtable_idx, direct_code, direct_method, type, skip_this);
+void ArmMir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb);
+  DCHECK(MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode));
+  RegLocation rl_src[3];
+  RegLocation rl_dest = mir_graph_->GetBadLoc();
+  rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
+  switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
+    case kMirOpMaddInt:
+      rl_dest = mir_graph_->GetDest(mir);
+      rl_src[0] = mir_graph_->GetSrc(mir, 0);
+      rl_src[1] = mir_graph_->GetSrc(mir, 1);
+      rl_src[2]= mir_graph_->GetSrc(mir, 2);
+      GenMaddMsubInt(rl_dest, rl_src[0], rl_src[1], rl_src[2], false);
+      break;
+    case kMirOpMsubInt:
+      rl_dest = mir_graph_->GetDest(mir);
+      rl_src[0] = mir_graph_->GetSrc(mir, 0);
+      rl_src[1] = mir_graph_->GetSrc(mir, 1);
+      rl_src[2]= mir_graph_->GetSrc(mir, 2);
+      GenMaddMsubInt(rl_dest, rl_src[0], rl_src[1], rl_src[2], true);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected opcode: " << mir->dalvikInsn.opcode;
   }
-
-  // TODO: Rework the implementation when argument register can be long or double.
-
-  /* If no arguments, just return */
-  if (info->num_arg_words == 0) {
-    return call_state;
-  }
-
-  const int start_index = skip_this ? 1 : 0;
-
-  InToRegStorageArmMapper mapper;
-  InToRegStorageMapping in_to_reg_storage_mapping;
-  in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
-  const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
-  int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + 1);
-
-  // First of all, check whether it makes sense to use bulk copying.
-  // Bulk copying is done only for the range case.
-  // TODO: make a constant instead of 2
-  if (info->is_range && regs_left_to_pass_via_stack >= 2) {
-    // Scan the rest of the args - if in phys_reg flush to memory
-    for (int next_arg = last_mapped_in + 1; next_arg < info->num_arg_words;) {
-      RegLocation loc = info->args[next_arg];
-      if (loc.wide) {
-        // TODO: Only flush hi-part.
-        if (loc.high_word) {
-          loc = info->args[--next_arg];
-        }
-        loc = UpdateLocWide(loc);
-        if (loc.location == kLocPhysReg) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
-        }
-        next_arg += 2;
-      } else {
-        loc = UpdateLoc(loc);
-        if (loc.location == kLocPhysReg) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          if (loc.ref) {
-            StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
-          } else {
-            StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
-                          kNotVolatile);
-          }
-        }
-        next_arg++;
-      }
-    }
-
-    // The rest can be copied together
-    int start_offset = SRegOffset(info->args[last_mapped_in + 1].s_reg_low);
-    int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + 1,
-                                                   cu_->instruction_set);
-
-    int current_src_offset = start_offset;
-    int current_dest_offset = outs_offset;
-
-    // Only davik regs are accessed in this loop; no next_call_insn() calls.
-    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    while (regs_left_to_pass_via_stack > 0) {
-      /*
-       * TODO: Improve by adding block copy for large number of arguments.  This
-       * should be done, if possible, as a target-depending helper.  For now, just
-       * copy a Dalvik vreg at a time.
-       */
-      // Moving 32-bits via general purpose register.
-      size_t bytes_to_move = sizeof(uint32_t);
-
-      // Instead of allocating a new temp, simply reuse one of the registers being used
-      // for argument passing.
-      RegStorage temp = TargetReg(kArg3, kNotWide);
-
-      // Now load the argument VR and store to the outs.
-      Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
-      Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
-
-      current_src_offset += bytes_to_move;
-      current_dest_offset += bytes_to_move;
-      regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
-    }
-    DCHECK_EQ(regs_left_to_pass_via_stack, 0);
-  }
-
-  // Now handle rest not registers if they are
-  if (in_to_reg_storage_mapping.IsThereStackMapped()) {
-    RegStorage regWide = TargetReg(kArg2, kWide);
-    for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) {
-      RegLocation rl_arg = info->args[i];
-      rl_arg = UpdateRawLoc(rl_arg);
-      RegStorage reg = in_to_reg_storage_mapping.Get(i);
-      // TODO: Only pass split wide hi-part via stack.
-      if (!reg.Valid() || rl_arg.wide) {
-        int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
-
-        {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          if (rl_arg.wide) {
-            if (rl_arg.location == kLocPhysReg) {
-              StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
-            } else {
-              LoadValueDirectWideFixed(rl_arg, regWide);
-              StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
-            }
-          } else {
-            if (rl_arg.location == kLocPhysReg) {
-              if (rl_arg.ref) {
-                StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
-              } else {
-                StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
-              }
-            } else {
-              if (rl_arg.ref) {
-                RegStorage regSingle = TargetReg(kArg2, kRef);
-                LoadValueDirectFixed(rl_arg, regSingle);
-                StoreRefDisp(TargetPtrReg(kSp), out_offset, regSingle, kNotVolatile);
-              } else {
-                RegStorage regSingle = TargetReg(kArg2, kNotWide);
-                LoadValueDirectFixed(rl_arg, regSingle);
-                StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
-              }
-            }
-          }
-        }
-
-        call_state = next_call_insn(cu_, info, call_state, target_method,
-                                    vtable_idx, direct_code, direct_method, type);
-      }
-      if (rl_arg.wide) {
-        i++;
-      }
-    }
-  }
-
-  // Finish with mapped registers
-  for (int i = start_index; i <= last_mapped_in; i++) {
-    RegLocation rl_arg = info->args[i];
-    rl_arg = UpdateRawLoc(rl_arg);
-    RegStorage reg = in_to_reg_storage_mapping.Get(i);
-    if (reg.Valid()) {
-      if (reg.Is64Bit()) {
-        LoadValueDirectWideFixed(rl_arg, reg);
-      } else {
-        // TODO: Only split long should be the case we need to care about.
-        if (rl_arg.wide) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          int high_word = rl_arg.high_word ? 1 : 0;
-          rl_arg = high_word ? info->args[i - 1] : rl_arg;
-          if (rl_arg.location == kLocPhysReg) {
-            RegStorage rs_arg = rl_arg.reg;
-            if (rs_arg.IsDouble() && rs_arg.Is64BitSolo()) {
-              rs_arg = As64BitFloatRegPair(rs_arg);
-            }
-            RegStorage rs_arg_low = rs_arg.GetLow();
-            RegStorage rs_arg_high = rs_arg.GetHigh();
-            OpRegCopy(reg, high_word ? rs_arg_high : rs_arg_low);
-          } else {
-            Load32Disp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low + high_word), reg);
-          }
-        } else {
-          LoadValueDirectFixed(rl_arg, reg);
-        }
-      }
-      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                                  direct_code, direct_method, type);
-    }
-    if (reg.Is64Bit()) {
-      i++;
-    }
-  }
-
-  call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                           direct_code, direct_method, type);
-  if (pcrLabel) {
-    if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
-      *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
-    } else {
-      *pcrLabel = nullptr;
-      // In lieu of generating a check for kArg1 being null, we need to
-      // perform a load when doing implicit checks.
-      RegStorage tmp = AllocTemp();
-      Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
-      MarkPossibleNullPointerException(info->opt_flags);
-      FreeTemp(tmp);
-    }
-  }
-  return call_state;
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index 117d8f0..e4bd2a3 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -18,8 +18,10 @@
 
 #include "arch/arm/instruction_set_features_arm.h"
 #include "arm_lir.h"
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
 
 namespace art {
 
@@ -156,6 +158,13 @@
     case Instruction::USHR_INT:
     case Instruction::USHR_INT_2ADDR:
       return true;
+    case Instruction::CONST:
+    case Instruction::CONST_4:
+    case Instruction::CONST_16:
+      if ((value >> 16) == 0) {
+        return true;  // movw, 16-bit unsigned.
+      }
+      FALLTHROUGH_INTENDED;
     case Instruction::AND_INT:
     case Instruction::AND_INT_2ADDR:
     case Instruction::AND_INT_LIT16:
@@ -899,12 +908,12 @@
  */
 LIR* ArmMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest,
                                   OpSize size) {
-  LIR* load = NULL;
-  ArmOpcode opcode = kThumbBkpt;
+  LIR* load = nullptr;
+  ArmOpcode opcode16 = kThumbBkpt;  // 16-bit Thumb opcode.
+  ArmOpcode opcode32 = kThumbBkpt;  // 32-bit Thumb2 opcode.
   bool short_form = false;
-  bool thumb2Form = (displacement < 4092 && displacement >= 0);
   bool all_low = r_dest.Is32Bit() && r_base.Low8() && r_dest.Low8();
-  int encoded_disp = displacement;
+  int scale = 0;  // Used for opcode16 and some indexed loads.
   bool already_generated = false;
   switch (size) {
     case kDouble:
@@ -932,57 +941,45 @@
         already_generated = true;
         break;
       }
+      DCHECK_EQ((displacement & 0x3), 0);
+      scale = 2;
       if (r_dest.Low8() && (r_base == rs_rARM_PC) && (displacement <= 1020) &&
           (displacement >= 0)) {
         short_form = true;
-        encoded_disp >>= 2;
-        opcode = kThumbLdrPcRel;
+        opcode16 = kThumbLdrPcRel;
       } else if (r_dest.Low8() && (r_base == rs_rARM_SP) && (displacement <= 1020) &&
                  (displacement >= 0)) {
         short_form = true;
-        encoded_disp >>= 2;
-        opcode = kThumbLdrSpRel;
-      } else if (all_low && displacement < 128 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x3), 0);
-        short_form = true;
-        encoded_disp >>= 2;
-        opcode = kThumbLdrRRI5;
-      } else if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2LdrRRI12;
+        opcode16 = kThumbLdrSpRel;
+      } else {
+        short_form = all_low && (displacement >> (5 + scale)) == 0;
+        opcode16 = kThumbLdrRRI5;
+        opcode32 = kThumb2LdrRRI12;
       }
       break;
     case kUnsignedHalf:
-      if (all_low && displacement < 64 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x1), 0);
-        short_form = true;
-        encoded_disp >>= 1;
-        opcode = kThumbLdrhRRI5;
-      } else if (displacement < 4092 && displacement >= 0) {
-        short_form = true;
-        opcode = kThumb2LdrhRRI12;
-      }
+      DCHECK_EQ((displacement & 0x1), 0);
+      scale = 1;
+      short_form = all_low && (displacement >> (5 + scale)) == 0;
+      opcode16 = kThumbLdrhRRI5;
+      opcode32 = kThumb2LdrhRRI12;
       break;
     case kSignedHalf:
-      if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2LdrshRRI12;
-      }
+      DCHECK_EQ((displacement & 0x1), 0);
+      scale = 1;
+      DCHECK_EQ(opcode16, kThumbBkpt);  // Not available.
+      opcode32 = kThumb2LdrshRRI12;
       break;
     case kUnsignedByte:
-      if (all_low && displacement < 32 && displacement >= 0) {
-        short_form = true;
-        opcode = kThumbLdrbRRI5;
-      } else if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2LdrbRRI12;
-      }
+      DCHECK_EQ(scale, 0);  // Keep scale = 0.
+      short_form = all_low && (displacement >> (5 + scale)) == 0;
+      opcode16 = kThumbLdrbRRI5;
+      opcode32 = kThumb2LdrbRRI12;
       break;
     case kSignedByte:
-      if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2LdrsbRRI12;
-      }
+      DCHECK_EQ(scale, 0);  // Keep scale = 0.
+      DCHECK_EQ(opcode16, kThumbBkpt);  // Not available.
+      opcode32 = kThumb2LdrsbRRI12;
       break;
     default:
       LOG(FATAL) << "Bad size: " << size;
@@ -990,12 +987,33 @@
 
   if (!already_generated) {
     if (short_form) {
-      load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), encoded_disp);
+      load = NewLIR3(opcode16, r_dest.GetReg(), r_base.GetReg(), displacement >> scale);
+    } else if ((displacement >> 12) == 0) {  // Thumb2 form.
+      load = NewLIR3(opcode32, r_dest.GetReg(), r_base.GetReg(), displacement);
+    } else if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) &&
+        InexpensiveConstantInt(displacement & ~0x00000fff, Instruction::ADD_INT)) {
+      // In this case, using LoadIndexed would emit 3 insns (movw+movt+ldr) but we can
+      // actually do it in two because we know that the kOpAdd is a single insn. On the
+      // other hand, we introduce an extra dependency, so this is not necessarily faster.
+      if (opcode16 != kThumbBkpt && r_dest.Low8() &&
+          InexpensiveConstantInt(displacement & ~(0x1f << scale), Instruction::ADD_INT)) {
+        // We can use the 16-bit Thumb opcode for the load.
+        OpRegRegImm(kOpAdd, r_dest, r_base, displacement & ~(0x1f << scale));
+        load = NewLIR3(opcode16, r_dest.GetReg(), r_dest.GetReg(), (displacement >> scale) & 0x1f);
+      } else {
+        DCHECK_NE(opcode32, kThumbBkpt);
+        OpRegRegImm(kOpAdd, r_dest, r_base, displacement & ~0x00000fff);
+        load = NewLIR3(opcode32, r_dest.GetReg(), r_dest.GetReg(), displacement & 0x00000fff);
+      }
     } else {
+      if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) ||
+          (scale != 0 && InexpensiveConstantInt(displacement, Instruction::CONST))) {
+        scale = 0;  // Prefer unscaled indexing if the same number of insns.
+      }
       RegStorage reg_offset = AllocTemp();
-      LoadConstant(reg_offset, encoded_disp);
+      LoadConstant(reg_offset, displacement >> scale);
       DCHECK(!r_dest.IsFloat());
-      load = LoadBaseIndexed(r_base, reg_offset, r_dest, 0, size);
+      load = LoadBaseIndexed(r_base, reg_offset, r_dest, scale, size);
       FreeTemp(reg_offset);
     }
   }
@@ -1024,9 +1042,8 @@
     // Use LDREXD for the atomic load. (Expect displacement > 0, don't optimize for == 0.)
     RegStorage r_ptr = AllocTemp();
     OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
-    LIR* lir = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
+    load = NewLIR3(kThumb2Ldrexd, r_dest.GetLowReg(), r_dest.GetHighReg(), r_ptr.GetReg());
     FreeTemp(r_ptr);
-    return lir;
   } else {
     load = LoadBaseDispBody(r_base, displacement, r_dest, size);
   }
@@ -1041,12 +1058,12 @@
 
 LIR* ArmMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
                                    OpSize size) {
-  LIR* store = NULL;
-  ArmOpcode opcode = kThumbBkpt;
+  LIR* store = nullptr;
+  ArmOpcode opcode16 = kThumbBkpt;  // 16-bit Thumb opcode.
+  ArmOpcode opcode32 = kThumbBkpt;  // 32-bit Thumb2 opcode.
   bool short_form = false;
-  bool thumb2Form = (displacement < 4092 && displacement >= 0);
   bool all_low = r_src.Is32Bit() && r_base.Low8() && r_src.Low8();
-  int encoded_disp = displacement;
+  int scale = 0;  // Used for opcode16 and some indexed loads.
   bool already_generated = false;
   switch (size) {
     case kDouble:
@@ -1078,53 +1095,67 @@
         already_generated = true;
         break;
       }
+      DCHECK_EQ((displacement & 0x3), 0);
+      scale = 2;
       if (r_src.Low8() && (r_base == rs_r13sp) && (displacement <= 1020) && (displacement >= 0)) {
         short_form = true;
-        encoded_disp >>= 2;
-        opcode = kThumbStrSpRel;
-      } else if (all_low && displacement < 128 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x3), 0);
-        short_form = true;
-        encoded_disp >>= 2;
-        opcode = kThumbStrRRI5;
-      } else if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2StrRRI12;
+        opcode16 = kThumbStrSpRel;
+      } else {
+        short_form = all_low && (displacement >> (5 + scale)) == 0;
+        opcode16 = kThumbStrRRI5;
+        opcode32 = kThumb2StrRRI12;
       }
       break;
     case kUnsignedHalf:
     case kSignedHalf:
-      if (all_low && displacement < 64 && displacement >= 0) {
-        DCHECK_EQ((displacement & 0x1), 0);
-        short_form = true;
-        encoded_disp >>= 1;
-        opcode = kThumbStrhRRI5;
-      } else if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2StrhRRI12;
-      }
+      DCHECK_EQ((displacement & 0x1), 0);
+      scale = 1;
+      short_form = all_low && (displacement >> (5 + scale)) == 0;
+      opcode16 = kThumbStrhRRI5;
+      opcode32 = kThumb2StrhRRI12;
       break;
     case kUnsignedByte:
     case kSignedByte:
-      if (all_low && displacement < 32 && displacement >= 0) {
-        short_form = true;
-        opcode = kThumbStrbRRI5;
-      } else if (thumb2Form) {
-        short_form = true;
-        opcode = kThumb2StrbRRI12;
-      }
+      DCHECK_EQ(scale, 0);  // Keep scale = 0.
+      short_form = all_low && (displacement >> (5 + scale)) == 0;
+      opcode16 = kThumbStrbRRI5;
+      opcode32 = kThumb2StrbRRI12;
       break;
     default:
       LOG(FATAL) << "Bad size: " << size;
   }
   if (!already_generated) {
     if (short_form) {
-      store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), encoded_disp);
-    } else {
+      store = NewLIR3(opcode16, r_src.GetReg(), r_base.GetReg(), displacement >> scale);
+    } else if ((displacement >> 12) == 0) {
+      store = NewLIR3(opcode32, r_src.GetReg(), r_base.GetReg(), displacement);
+    } else if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) &&
+        InexpensiveConstantInt(displacement & ~0x00000fff, Instruction::ADD_INT)) {
+      // In this case, using StoreIndexed would emit 3 insns (movw+movt+str) but we can
+      // actually do it in two because we know that the kOpAdd is a single insn. On the
+      // other hand, we introduce an extra dependency, so this is not necessarily faster.
       RegStorage r_scratch = AllocTemp();
-      LoadConstant(r_scratch, encoded_disp);
+      if (opcode16 != kThumbBkpt && r_src.Low8() && r_scratch.Low8() &&
+          InexpensiveConstantInt(displacement & ~(0x1f << scale), Instruction::ADD_INT)) {
+        // We can use the 16-bit Thumb opcode for the load.
+        OpRegRegImm(kOpAdd, r_scratch, r_base, displacement & ~(0x1f << scale));
+        store = NewLIR3(opcode16, r_src.GetReg(), r_scratch.GetReg(),
+                        (displacement >> scale) & 0x1f);
+      } else {
+        DCHECK_NE(opcode32, kThumbBkpt);
+        OpRegRegImm(kOpAdd, r_scratch, r_base, displacement & ~0x00000fff);
+        store = NewLIR3(opcode32, r_src.GetReg(), r_scratch.GetReg(), displacement & 0x00000fff);
+      }
+      FreeTemp(r_scratch);
+    } else {
+      if (!InexpensiveConstantInt(displacement >> scale, Instruction::CONST) ||
+          (scale != 0 && InexpensiveConstantInt(displacement, Instruction::CONST))) {
+        scale = 0;  // Prefer unscaled indexing if the same number of insns.
+      }
+      RegStorage r_scratch = AllocTemp();
+      LoadConstant(r_scratch, displacement >> scale);
       DCHECK(!r_src.IsFloat());
-      store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
+      store = StoreBaseIndexed(r_base, r_scratch, r_src, scale, size);
       FreeTemp(r_scratch);
     }
   }
@@ -1144,7 +1175,7 @@
     GenMemBarrier(kAnyStore);
   }
 
-  LIR* store;
+  LIR* null_ck_insn;
   if (is_volatile == kVolatile && (size == k64 || size == kDouble) &&
       !cu_->compiler_driver->GetInstructionSetFeatures()->
           AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd()) {
@@ -1161,17 +1192,16 @@
     RegStorage r_temp = AllocTemp();
     RegStorage r_temp_high = AllocTemp(false);  // We may not have another temp.
     if (r_temp_high.Valid()) {
-      NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
+      null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
       FreeTemp(r_temp_high);
       FreeTemp(r_temp);
     } else {
       // If we don't have another temp, clobber r_ptr in LDREXD and reload it.
-      NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
+      null_ck_insn = NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_ptr.GetReg(), r_ptr.GetReg());
       FreeTemp(r_temp);  // May need the temp for kOpAdd.
       OpRegRegImm(kOpAdd, r_ptr, r_base, displacement);
     }
-    store = NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(),
-                    r_ptr.GetReg());
+    NewLIR4(kThumb2Strexd, r_temp.GetReg(), r_src.GetLowReg(), r_src.GetHighReg(), r_ptr.GetReg());
     OpCmpImmBranch(kCondNe, r_temp, 0, fail_target);
     FreeTemp(r_ptr);
   } else {
@@ -1180,7 +1210,7 @@
       size = k32;
     }
 
-    store = StoreBaseDispBody(r_base, displacement, r_src, size);
+    null_ck_insn = StoreBaseDispBody(r_base, displacement, r_src, size);
   }
 
   if (UNLIKELY(is_volatile == kVolatile)) {
@@ -1189,7 +1219,7 @@
     GenMemBarrier(kAnyAny);
   }
 
-  return store;
+  return null_ck_insn;
 }
 
 LIR* ArmMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index f8a7310..d15412a 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -17,7 +17,9 @@
 #ifndef ART_COMPILER_DEX_QUICK_ARM64_ARM64_LIR_H_
 #define ART_COMPILER_DEX_QUICK_ARM64_ARM64_LIR_H_
 
-#include "dex/compiler_internals.h"
+#include "dex/compiler_enums.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
 
 namespace art {
 
@@ -312,6 +314,7 @@
   kA64Lsl3rrr,       // lsl [s0011010110] rm[20-16] [001000] rn[9-5] rd[4-0].
   kA64Lsr3rrd,       // lsr alias of "ubfm arg0, arg1, arg2, #{31/63}".
   kA64Lsr3rrr,       // lsr [s0011010110] rm[20-16] [001001] rn[9-5] rd[4-0].
+  kA64Madd4rrrr,     // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0].
   kA64Movk3rdM,      // mov [010100101] hw[22-21] imm_16[20-5] rd[4-0].
   kA64Movn3rdM,      // mov [000100101] hw[22-21] imm_16[20-5] rd[4-0].
   kA64Movz3rdM,      // mov [011100101] hw[22-21] imm_16[20-5] rd[4-0].
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index cab11cc..806617b 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -18,7 +18,10 @@
 
 #include "arch/arm64/instruction_set_features_arm64.h"
 #include "arm64_lir.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 
 namespace art {
 
@@ -445,6 +448,10 @@
                  kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
                  "lsr", "!0r, !1r, !2r", kFixupNone),
+    ENCODING_MAP(WIDE(kA64Madd4rrrr), SF_VARIANTS(0x1b000000),
+                 kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
+                 kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP,
+                 "madd", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769),
     ENCODING_MAP(WIDE(kA64Movk3rdM), SF_VARIANTS(0x72800000),
                  kFmtRegR, 4, 0, kFmtBitBlt, 20, 5, kFmtBitBlt, 22, 21,
                  kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE0,
@@ -840,6 +847,20 @@
 // are better set directly from the code (they will require no more than 2 instructions).
 #define ALIGNED_DATA_OFFSET(offset) (((offset) + 0x7) & ~0x7)
 
+/*
+ * Get the LIR which emits the instruction preceding the given LIR.
+ * Returns nullptr, if no previous emitting insn found.
+ */
+static LIR* GetPrevEmittingLIR(LIR* lir) {
+  DCHECK(lir != nullptr);
+  LIR* prev_lir = lir->prev;
+  while ((prev_lir != nullptr) &&
+         (prev_lir->flags.is_nop || Mir2Lir::IsPseudoLirOp(prev_lir->opcode))) {
+    prev_lir = prev_lir->prev;
+  }
+  return prev_lir;
+}
+
 // Assemble the LIR into binary instruction format.
 void Arm64Mir2Lir::AssembleLIR() {
   LIR* lir;
@@ -998,11 +1019,15 @@
           // Avoid emitting code that could trigger Cortex A53's erratum 835769.
           // This fixup should be carried out for all multiply-accumulate instructions: madd, msub,
           // smaddl, smsubl, umaddl and umsubl.
-          if (cu_->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()
+          if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()
               ->NeedFixCortexA53_835769()) {
             // Check that this is a 64-bit multiply-accumulate.
             if (IS_WIDE(lir->opcode)) {
-              uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags;
+              LIR* prev_insn = GetPrevEmittingLIR(lir);
+              if (prev_insn == nullptr) {
+                break;
+              }
+              uint64_t prev_insn_flags = EncodingMap[UNWIDE(prev_insn->opcode)].flags;
               // Check that the instruction preceding the multiply-accumulate is a load or store.
               if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) {
                 // insert a NOP between the load/store and the multiply-accumulate.
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 089e4b6..d1e4b7e 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -16,9 +16,13 @@
 
 /* This file contains codegen for the Thumb2 ISA. */
 
-#include "arm64_lir.h"
 #include "codegen_arm64.h"
+
+#include "arm64_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 #include "gc/accounting/card_table.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/art_method.h"
@@ -47,16 +51,13 @@
  */
 void Arm64Mir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
-  if (cu_->verbose) {
-    DumpSparseSwitchTable(table);
-  }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint32_t size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // Get the switch value
@@ -99,17 +100,13 @@
 
 void Arm64Mir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
-  if (cu_->verbose) {
-    DumpPackedSwitchTable(table);
-  }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable),  kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint32_t size = table[1];
-  tab_rec->targets =
-      static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // Get the switch value
@@ -396,9 +393,8 @@
 }
 
 static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
-  UNUSED(cu, target_method);
-  // Always emit relative calls.
-  return true;
+  // Emit relative calls anywhere in the image or within a dex file otherwise.
+  return cu->compiler_driver->IsImage() || cu->dex_file == target_method.dex_file;
 }
 
 /*
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 5e10f80..49ca625 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -18,7 +18,7 @@
 #define ART_COMPILER_DEX_QUICK_ARM64_CODEGEN_ARM64_H_
 
 #include "arm64_lir.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir.h"
 
 #include <map>
@@ -27,38 +27,25 @@
 
 class Arm64Mir2Lir FINAL : public Mir2Lir {
  protected:
-  // TODO: consolidate 64-bit target support.
-  class InToRegStorageMapper {
-   public:
-    virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref) = 0;
-    virtual ~InToRegStorageMapper() {}
-  };
-
   class InToRegStorageArm64Mapper : public InToRegStorageMapper {
    public:
     InToRegStorageArm64Mapper() : cur_core_reg_(0), cur_fp_reg_(0) {}
     virtual ~InToRegStorageArm64Mapper() {}
-    virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref);
+    virtual RegStorage GetNextReg(ShortyArg arg);
+    virtual void Reset() OVERRIDE {
+      cur_core_reg_ = 0;
+      cur_fp_reg_ = 0;
+    }
    private:
-    int cur_core_reg_;
-    int cur_fp_reg_;
+    size_t cur_core_reg_;
+    size_t cur_fp_reg_;
   };
 
-  class InToRegStorageMapping {
-   public:
-    InToRegStorageMapping() : max_mapped_in_(0), is_there_stack_mapped_(false),
-    initialized_(false) {}
-    void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
-    int GetMaxMappedIn() { return max_mapped_in_; }
-    bool IsThereStackMapped() { return is_there_stack_mapped_; }
-    RegStorage Get(int in_position);
-    bool IsInitialized() { return initialized_; }
-   private:
-    std::map<int, RegStorage> mapping_;
-    int max_mapped_in_;
-    bool is_there_stack_mapped_;
-    bool initialized_;
-  };
+  InToRegStorageArm64Mapper in_to_reg_storage_arm64_mapper_;
+  InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+    in_to_reg_storage_arm64_mapper_.Reset();
+    return &in_to_reg_storage_arm64_mapper_;
+  }
 
  public:
   Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -79,21 +66,14 @@
   RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE;
   LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest,
                     OpSize size, VolatileKind is_volatile) OVERRIDE;
-  LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
-                   VolatileKind is_volatile) OVERRIDE;
   LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale,
                        OpSize size) OVERRIDE;
-  LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale)
-      OVERRIDE;
   LIR* LoadConstantNoClobber(RegStorage r_dest, int value) OVERRIDE;
   LIR* LoadConstantWide(RegStorage r_dest, int64_t value) OVERRIDE;
   LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size,
                      VolatileKind is_volatile) OVERRIDE;
-  LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src, VolatileKind is_volatile)
-      OVERRIDE;
   LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale,
                         OpSize size) OVERRIDE;
-  LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale) OVERRIDE;
 
   /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
   void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
@@ -113,7 +93,6 @@
   RegStorage TargetPtrReg(SpecialTargetRegister symbolic_reg) OVERRIDE {
     return As64BitReg(TargetReg(symbolic_reg));
   }
-  RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
   RegLocation GetReturnAlt() OVERRIDE;
   RegLocation GetReturnWideAlt() OVERRIDE;
   RegLocation LocCReturn() OVERRIDE;
@@ -207,6 +186,10 @@
   void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
   void GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
   void GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
+  void GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                      RegLocation rl_src3, bool is_sub);
+  void GenMaddMsubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                       RegLocation rl_src3, bool is_sub);
 
   // Required for target - single operation generators.
   LIR* OpUnconditionalBranch(LIR* target) OVERRIDE;
@@ -240,21 +223,7 @@
   bool InexpensiveConstantLong(int64_t value) OVERRIDE;
   bool InexpensiveConstantDouble(int64_t value) OVERRIDE;
 
-  void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
-
-  int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                           NextCallInsn next_call_insn,
-                           const MethodReference& target_method,
-                           uint32_t vtable_idx,
-                           uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                           bool skip_this) OVERRIDE;
-
-  int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                         NextCallInsn next_call_insn,
-                         const MethodReference& target_method,
-                         uint32_t vtable_idx,
-                         uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                         bool skip_this) OVERRIDE;
+  void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE;
 
   bool WideGPRsAreAliases() const OVERRIDE {
     return true;  // 64b architecture.
@@ -422,10 +391,11 @@
   void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
                      RegLocation rl_src2, bool is_div, int flags);
 
-  InToRegStorageMapping in_to_reg_storage_mapping_;
   static const A64EncodingMap EncodingMap[kA64Last];
 
   ArenaVector<LIR*> call_method_insns_;
+
+  int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index ff692b7..a8ec6c0 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-#include "arm64_lir.h"
 #include "codegen_arm64.h"
+
+#include "arm64_lir.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "utils.h"
 
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 57e67d5..92675f3 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -16,11 +16,16 @@
 
 /* This file contains codegen for the Thumb2 ISA. */
 
+#include "codegen_arm64.h"
+
 #include "arch/instruction_set_features.h"
 #include "arm64_lir.h"
-#include "codegen_arm64.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/array-inl.h"
 #include "utils.h"
@@ -543,7 +548,7 @@
       return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, static_cast<int32_t>(lit));
     }
   }
-  int k = LowestSetBit(lit);
+  int k = CTZ(lit);
   if (k >= nbits - 2) {
     // Avoid special cases.
     return false;
@@ -949,10 +954,33 @@
   UNREACHABLE();
 }
 
+void Arm64Mir2Lir::GenMaddMsubInt(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                  RegLocation rl_src3, bool is_sub) {
+  rl_src1 = LoadValue(rl_src1, kCoreReg);
+  rl_src2 = LoadValue(rl_src2, kCoreReg);
+  rl_src3 = LoadValue(rl_src3, kCoreReg);
+  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+  NewLIR4(is_sub ? kA64Msub4rrrr : kA64Madd4rrrr, rl_result.reg.GetReg(), rl_src1.reg.GetReg(),
+          rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
+  StoreValue(rl_dest, rl_result);
+}
+
+void Arm64Mir2Lir::GenMaddMsubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2,
+                                   RegLocation rl_src3, bool is_sub) {
+  rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+  rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+  rl_src3 = LoadValueWide(rl_src3, kCoreReg);
+  RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+  NewLIR4(is_sub ? WIDE(kA64Msub4rrrr) : WIDE(kA64Madd4rrrr), rl_result.reg.GetReg(),
+          rl_src1.reg.GetReg(), rl_src2.reg.GetReg(), rl_src3.reg.GetReg());
+  StoreValueWide(rl_dest, rl_result);
+}
+
 void Arm64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
                                                  RegLocation rl_result, int lit ATTRIBUTE_UNUSED,
                                                  int first_bit, int second_bit) {
-  OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg, EncodeShift(kA64Lsl, second_bit - first_bit));
+  OpRegRegRegShift(kOpAdd, rl_result.reg, rl_src.reg, rl_src.reg,
+                   EncodeShift(kA64Lsl, second_bit - first_bit));
   if (first_bit != 0) {
     OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit);
   }
@@ -980,7 +1008,7 @@
 }
 
 bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
-  if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+  if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
     return false;
   }
   // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
@@ -1686,7 +1714,8 @@
   RegLocation rl_src_i = info->args[0];
   RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-  RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
+  RegLocation rl_i = IsWide(size) ?
+      LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
   NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
   IsWide(size) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
   return true;
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 094ff51..136be94 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -19,9 +19,11 @@
 #include <inttypes.h>
 
 #include <string>
+#include <sstream>
 
 #include "backend_arm64.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 
@@ -521,6 +523,24 @@
        buf += *fmt++;
     }
   }
+  // Dump thread offset.
+  std::string fmt_str = GetTargetInstFmt(lir->opcode);
+  if (std::string::npos != fmt_str.find(", [!1X, #!2") && rxSELF == lir->operands[1] &&
+      std::string::npos != buf.find(", [")) {
+    int offset = lir->operands[2];
+    if (std::string::npos != fmt_str.find("#!2d")) {
+    } else if (std::string::npos != fmt_str.find("#!2D")) {
+      offset *= (IS_WIDE(lir->opcode)) ? 8 : 4;
+    } else if (std::string::npos != fmt_str.find("#!2F")) {
+      offset *= 2;
+    } else {
+      LOG(FATAL) << "Should not reach here";
+    }
+    std::ostringstream tmp_stream;
+    Thread::DumpThreadOffset<8>(tmp_stream, offset);
+    buf += "  ; ";
+    buf += tmp_stream.str();
+  }
   return buf;
 }
 
@@ -759,6 +779,7 @@
   FreeTemp(rs_f5);
   FreeTemp(rs_f6);
   FreeTemp(rs_f7);
+  FreeTemp(TargetReg(kHiddenArg));
 }
 
 RegStorage Arm64Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
@@ -790,27 +811,23 @@
   return Arm64Mir2Lir::EncodingMap[UNWIDE(opcode)].fmt;
 }
 
-RegStorage Arm64Mir2Lir::InToRegStorageArm64Mapper::GetNextReg(bool is_double_or_float,
-                                                               bool is_wide,
-                                                               bool is_ref) {
+RegStorage Arm64Mir2Lir::InToRegStorageArm64Mapper::GetNextReg(ShortyArg arg) {
   const RegStorage coreArgMappingToPhysicalReg[] =
       {rs_x1, rs_x2, rs_x3, rs_x4, rs_x5, rs_x6, rs_x7};
-  const int coreArgMappingToPhysicalRegSize =
-      sizeof(coreArgMappingToPhysicalReg) / sizeof(RegStorage);
+  const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
   const RegStorage fpArgMappingToPhysicalReg[] =
       {rs_f0, rs_f1, rs_f2, rs_f3, rs_f4, rs_f5, rs_f6, rs_f7};
-  const int fpArgMappingToPhysicalRegSize =
-      sizeof(fpArgMappingToPhysicalReg) / sizeof(RegStorage);
+  const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
 
   RegStorage result = RegStorage::InvalidReg();
-  if (is_double_or_float) {
+  if (arg.IsFP()) {
     if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
-      DCHECK(!is_ref);
+      DCHECK(!arg.IsRef());
       result = fpArgMappingToPhysicalReg[cur_fp_reg_++];
       if (result.Valid()) {
         // TODO: switching between widths remains a bit ugly.  Better way?
         int res_reg = result.GetReg();
-        result = is_wide ? RegStorage::FloatSolo64(res_reg) : RegStorage::FloatSolo32(res_reg);
+        result = arg.IsWide() ? RegStorage::FloatSolo64(res_reg) : RegStorage::FloatSolo32(res_reg);
       }
     }
   } else {
@@ -819,388 +836,15 @@
       if (result.Valid()) {
         // TODO: switching between widths remains a bit ugly.  Better way?
         int res_reg = result.GetReg();
-        DCHECK(!(is_wide && is_ref));
-        result = (is_wide || is_ref) ? RegStorage::Solo64(res_reg) : RegStorage::Solo32(res_reg);
+        DCHECK(!(arg.IsWide() && arg.IsRef()));
+        result = (arg.IsWide() || arg.IsRef()) ?
+                 RegStorage::Solo64(res_reg) : RegStorage::Solo32(res_reg);
       }
     }
   }
   return result;
 }
 
-RegStorage Arm64Mir2Lir::InToRegStorageMapping::Get(int in_position) {
-  DCHECK(IsInitialized());
-  auto res = mapping_.find(in_position);
-  return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
-}
-
-void Arm64Mir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
-                                                     InToRegStorageMapper* mapper) {
-  DCHECK(mapper != nullptr);
-  max_mapped_in_ = -1;
-  is_there_stack_mapped_ = false;
-  for (int in_position = 0; in_position < count; in_position++) {
-     RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
-                                         arg_locs[in_position].wide,
-                                         arg_locs[in_position].ref);
-     if (reg.Valid()) {
-       mapping_[in_position] = reg;
-       if (arg_locs[in_position].wide) {
-         // We covered 2 args, so skip the next one
-         in_position++;
-       }
-       max_mapped_in_ = std::max(max_mapped_in_, in_position);
-     } else {
-       is_there_stack_mapped_ = true;
-     }
-  }
-  initialized_ = true;
-}
-
-
-// Deprecate.  Use the new mechanism.
-// TODO(Arm64): reuse info in QuickArgumentVisitor?
-static RegStorage GetArgPhysicalReg(RegLocation* loc, int* num_gpr_used, int* num_fpr_used,
-                                    OpSize* op_size) {
-  if (loc->fp) {
-    int n = *num_fpr_used;
-    if (n < 8) {
-      *num_fpr_used = n + 1;
-      RegStorage::RegStorageKind reg_kind;
-      if (loc->wide) {
-        *op_size = kDouble;
-        reg_kind = RegStorage::k64BitSolo;
-      } else {
-        *op_size = kSingle;
-        reg_kind = RegStorage::k32BitSolo;
-      }
-      return RegStorage(RegStorage::kValid | reg_kind | RegStorage::kFloatingPoint | n);
-    }
-  } else {
-    int n = *num_gpr_used;
-    if (n < 8) {
-      *num_gpr_used = n + 1;
-      if (loc->wide || loc->ref) {
-        *op_size = k64;
-        return RegStorage::Solo64(n);
-      } else {
-        *op_size = k32;
-        return RegStorage::Solo32(n);
-      }
-    }
-  }
-  *op_size = kWord;
-  return RegStorage::InvalidReg();
-}
-
-RegStorage Arm64Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
-  if (!in_to_reg_storage_mapping_.IsInitialized()) {
-    int start_vreg = mir_graph_->GetFirstInVR();
-    RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
-
-    InToRegStorageArm64Mapper mapper;
-    in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
-  }
-  return in_to_reg_storage_mapping_.Get(arg_num);
-}
-
-
-/*
- * If there are any ins passed in registers that have not been promoted
- * to a callee-save register, flush them to the frame.  Perform initial
- * assignment of promoted arguments.
- *
- * ArgLocs is an array of location records describing the incoming arguments
- * with one location record per word of argument.
- */
-void Arm64Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
-  int num_gpr_used = 1;
-  int num_fpr_used = 0;
-
-  /*
-   * Dummy up a RegLocation for the incoming StackReference<mirror::ArtMethod>
-   * It will attempt to keep kArg0 live (or copy it to home location
-   * if promoted).
-   */
-  RegLocation rl_src = rl_method;
-  rl_src.location = kLocPhysReg;
-  rl_src.reg = TargetReg(kArg0, kRef);
-  rl_src.home = false;
-  MarkLive(rl_src);
-  StoreValue(rl_method, rl_src);
-  // If Method* has been promoted, explicitly flush
-  if (rl_method.location == kLocPhysReg) {
-    StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile);
-  }
-
-  if (mir_graph_->GetNumOfInVRs() == 0) {
-    return;
-  }
-
-  // Handle dalvik registers.
-  ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-  int start_vreg = mir_graph_->GetFirstInVR();
-  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
-    RegLocation* t_loc = &ArgLocs[i];
-    OpSize op_size;
-    RegStorage reg = GetArgPhysicalReg(t_loc, &num_gpr_used, &num_fpr_used, &op_size);
-
-    if (reg.Valid()) {
-      // If arriving in register.
-
-      // We have already updated the arg location with promoted info
-      // so we can be based on it.
-      if (t_loc->location == kLocPhysReg) {
-        // Just copy it.
-        OpRegCopy(t_loc->reg, reg);
-      } else {
-        // Needs flush.
-        if (t_loc->ref) {
-          StoreRefDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg, kNotVolatile);
-        } else {
-          StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32,
-              kNotVolatile);
-        }
-      }
-    } else {
-      // If arriving in frame & promoted.
-      if (t_loc->location == kLocPhysReg) {
-        if (t_loc->ref) {
-          LoadRefDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile);
-        } else {
-          LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), t_loc->reg,
-                       t_loc->wide ? k64 : k32, kNotVolatile);
-        }
-      }
-    }
-    if (t_loc->wide) {
-      // Increment i to skip the next one.
-      i++;
-    }
-    //      if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) {
-    //        OpRegCopy(RegStorage::Solo32(v_map->core_reg), reg);
-    //      } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) {
-    //        OpRegCopy(RegStorage::Solo32(v_map->fp_reg), reg);
-    //      } else {
-    //        StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i), reg, op_size, kNotVolatile);
-    //        if (reg.Is64Bit()) {
-    //          if (SRegOffset(start_vreg + i) + 4 != SRegOffset(start_vreg + i + 1)) {
-    //            LOG(FATAL) << "64 bit value stored in non-consecutive 4 bytes slots";
-    //          }
-    //          i += 1;
-    //        }
-    //      }
-    //    } else {
-    //      // If arriving in frame & promoted
-    //      if (v_map->core_location == kLocPhysReg) {
-    //        LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i),
-    //                     RegStorage::Solo32(v_map->core_reg));
-    //      }
-    //      if (v_map->fp_location == kLocPhysReg) {
-    //        LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i), RegStorage::Solo32(v_map->fp_reg));
-    //      }
-  }
-}
-
-/*
- * Load up to 5 arguments, the first three of which will be in
- * kArg1 .. kArg3.  On entry kArg0 contains the current method pointer,
- * and as part of the load sequence, it must be replaced with
- * the target method pointer.
- */
-int Arm64Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
-                                       int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
-                                       const MethodReference& target_method,
-                                       uint32_t vtable_idx, uintptr_t direct_code,
-                                       uintptr_t direct_method, InvokeType type, bool skip_this) {
-  return GenDalvikArgsRange(info,
-                       call_state, pcrLabel, next_call_insn,
-                       target_method,
-                       vtable_idx, direct_code,
-                       direct_method, type, skip_this);
-}
-
-/*
- * May have 0+ arguments (also used for jumbo).  Note that
- * source virtual registers may be in physical registers, so may
- * need to be flushed to home location before copying.  This
- * applies to arg3 and above (see below).
- *
- * FIXME: update comments.
- *
- * Two general strategies:
- *    If < 20 arguments
- *       Pass args 3-18 using vldm/vstm block copy
- *       Pass arg0, arg1 & arg2 in kArg1-kArg3
- *    If 20+ arguments
- *       Pass args arg19+ using memcpy block copy
- *       Pass arg0, arg1 & arg2 in kArg1-kArg3
- *
- */
-int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
-                                     LIR** pcrLabel, NextCallInsn next_call_insn,
-                                     const MethodReference& target_method,
-                                     uint32_t vtable_idx, uintptr_t direct_code,
-                                     uintptr_t direct_method, InvokeType type, bool skip_this) {
-  /* If no arguments, just return */
-  if (info->num_arg_words == 0)
-    return call_state;
-
-  const int start_index = skip_this ? 1 : 0;
-
-  InToRegStorageArm64Mapper mapper;
-  InToRegStorageMapping in_to_reg_storage_mapping;
-  in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
-  const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
-  int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + 1);
-
-  // First of all, check whether it makes sense to use bulk copying.
-  // Bulk copying is done only for the range case.
-  // TODO: make a constant instead of 2
-  if (info->is_range && regs_left_to_pass_via_stack >= 2) {
-    // Scan the rest of the args - if in phys_reg flush to memory
-    for (int next_arg = last_mapped_in + 1; next_arg < info->num_arg_words;) {
-      RegLocation loc = info->args[next_arg];
-      if (loc.wide) {
-        loc = UpdateLocWide(loc);
-        if (loc.location == kLocPhysReg) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
-        }
-        next_arg += 2;
-      } else {
-        loc = UpdateLoc(loc);
-        if (loc.location == kLocPhysReg) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          if (loc.ref) {
-            StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
-          } else {
-            StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
-                          kNotVolatile);
-          }
-        }
-        next_arg++;
-      }
-    }
-
-    // The rest can be copied together
-    int start_offset = SRegOffset(info->args[last_mapped_in + 1].s_reg_low);
-    int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + 1,
-                                                   cu_->instruction_set);
-
-    int current_src_offset = start_offset;
-    int current_dest_offset = outs_offset;
-
-    // Only davik regs are accessed in this loop; no next_call_insn() calls.
-    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    while (regs_left_to_pass_via_stack > 0) {
-      /*
-       * TODO: Improve by adding block copy for large number of arguments.  This
-       * should be done, if possible, as a target-depending helper.  For now, just
-       * copy a Dalvik vreg at a time.
-       */
-      // Moving 32-bits via general purpose register.
-      size_t bytes_to_move = sizeof(uint32_t);
-
-      // Instead of allocating a new temp, simply reuse one of the registers being used
-      // for argument passing.
-      RegStorage temp = TargetReg(kArg3, kNotWide);
-
-      // Now load the argument VR and store to the outs.
-      Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
-      Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
-
-      current_src_offset += bytes_to_move;
-      current_dest_offset += bytes_to_move;
-      regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
-    }
-    DCHECK_EQ(regs_left_to_pass_via_stack, 0);
-  }
-
-  // Now handle rest not registers if they are
-  if (in_to_reg_storage_mapping.IsThereStackMapped()) {
-    RegStorage regWide = TargetReg(kArg3, kWide);
-    for (int i = start_index; i <= last_mapped_in + regs_left_to_pass_via_stack; i++) {
-      RegLocation rl_arg = info->args[i];
-      rl_arg = UpdateRawLoc(rl_arg);
-      RegStorage reg = in_to_reg_storage_mapping.Get(i);
-      if (!reg.Valid()) {
-        int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
-
-        {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          if (rl_arg.wide) {
-            if (rl_arg.location == kLocPhysReg) {
-              StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
-            } else {
-              LoadValueDirectWideFixed(rl_arg, regWide);
-              StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
-            }
-          } else {
-            if (rl_arg.location == kLocPhysReg) {
-              if (rl_arg.ref) {
-                StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
-              } else {
-                StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
-              }
-            } else {
-              if (rl_arg.ref) {
-                RegStorage regSingle = TargetReg(kArg2, kRef);
-                LoadValueDirectFixed(rl_arg, regSingle);
-                StoreRefDisp(TargetPtrReg(kSp), out_offset, regSingle, kNotVolatile);
-              } else {
-                RegStorage regSingle = TargetReg(kArg2, kNotWide);
-                LoadValueDirectFixed(rl_arg, regSingle);
-                StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
-              }
-            }
-          }
-        }
-        call_state = next_call_insn(cu_, info, call_state, target_method,
-                                    vtable_idx, direct_code, direct_method, type);
-      }
-      if (rl_arg.wide) {
-        i++;
-      }
-    }
-  }
-
-  // Finish with mapped registers
-  for (int i = start_index; i <= last_mapped_in; i++) {
-    RegLocation rl_arg = info->args[i];
-    rl_arg = UpdateRawLoc(rl_arg);
-    RegStorage reg = in_to_reg_storage_mapping.Get(i);
-    if (reg.Valid()) {
-      if (rl_arg.wide) {
-        LoadValueDirectWideFixed(rl_arg, reg);
-      } else {
-        LoadValueDirectFixed(rl_arg, reg);
-      }
-      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                                  direct_code, direct_method, type);
-    }
-    if (rl_arg.wide) {
-      i++;
-    }
-  }
-
-  call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                           direct_code, direct_method, type);
-  if (pcrLabel) {
-    if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
-      *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
-    } else {
-      *pcrLabel = nullptr;
-      // In lieu of generating a check for kArg1 being null, we need to
-      // perform a load when doing implicit checks.
-      RegStorage tmp = AllocTemp();
-      Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
-      MarkPossibleNullPointerException(info->opt_flags);
-      FreeTemp(tmp);
-    }
-  }
-  return call_state;
-}
-
 void Arm64Mir2Lir::InstallLiteralPools() {
   // PC-relative calls to methods.
   patches_.reserve(call_method_insns_.size());
@@ -1218,4 +862,43 @@
   Mir2Lir::InstallLiteralPools();
 }
 
+int Arm64Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* /*info*/, int /*first*/, int count) {
+  /*
+   * TODO: Improve by adding block copy for large number of arguments.  For now, just
+   * copy a Dalvik vreg at a time.
+   */
+  return count;
+}
+
+void Arm64Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) {
+  UNUSED(bb);
+  DCHECK(MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode));
+  RegLocation rl_src[3];
+  RegLocation rl_dest = mir_graph_->GetBadLoc();
+  rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
+  ExtendedMIROpcode opcode = static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode);
+  switch (opcode) {
+    case kMirOpMaddInt:
+    case kMirOpMsubInt:
+      rl_dest = mir_graph_->GetDest(mir);
+      rl_src[0] = mir_graph_->GetSrc(mir, 0);
+      rl_src[1] = mir_graph_->GetSrc(mir, 1);
+      rl_src[2]= mir_graph_->GetSrc(mir, 2);
+      GenMaddMsubInt(rl_dest, rl_src[0], rl_src[1], rl_src[2],
+                     (opcode == kMirOpMsubInt) ? true : false);
+      break;
+    case kMirOpMaddLong:
+    case kMirOpMsubLong:
+      rl_dest = mir_graph_->GetDestWide(mir);
+      rl_src[0] = mir_graph_->GetSrcWide(mir, 0);
+      rl_src[1] = mir_graph_->GetSrcWide(mir, 2);
+      rl_src[2] = mir_graph_->GetSrcWide(mir, 4);
+      GenMaddMsubLong(rl_dest, rl_src[0], rl_src[1], rl_src[2],
+                      (opcode == kMirOpMsubLong) ? true : false);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected opcode: " << static_cast<int>(opcode);
+  }
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index 78a6df8..f48290d 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-#include "arm64_lir.h"
 #include "codegen_arm64.h"
+
+#include "arm64_lir.h"
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 
@@ -1062,9 +1064,11 @@
       opcode = WIDE(kA64Ldr4rXxG);
       expected_scale = 3;
       break;
-    case kSingle:     // Intentional fall-through.
-    case k32:         // Intentional fall-through.
     case kReference:
+      r_dest = As32BitReg(r_dest);
+      FALLTHROUGH_INTENDED;
+    case kSingle:     // Intentional fall-through.
+    case k32:
       r_dest = Check32BitReg(r_dest);
       opcode = kA64Ldr4rXxG;
       expected_scale = 2;
@@ -1105,11 +1109,6 @@
   return load;
 }
 
-LIR* Arm64Mir2Lir::LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
-                                  int scale) {
-  return LoadBaseIndexed(r_base, r_index, As32BitReg(r_dest), scale, kReference);
-}
-
 LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
                                     int scale, OpSize size) {
   LIR* store;
@@ -1150,9 +1149,11 @@
       opcode = WIDE(kA64Str4rXxG);
       expected_scale = 3;
       break;
-    case kSingle:     // Intentional fall-trough.
-    case k32:         // Intentional fall-trough.
     case kReference:
+      r_src = As32BitReg(r_src);
+      FALLTHROUGH_INTENDED;
+    case kSingle:     // Intentional fall-trough.
+    case k32:
       r_src = Check32BitReg(r_src);
       opcode = kA64Str4rXxG;
       expected_scale = 2;
@@ -1185,11 +1186,6 @@
   return store;
 }
 
-LIR* Arm64Mir2Lir::StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
-                                   int scale) {
-  return StoreBaseIndexed(r_base, r_index, As32BitReg(r_src), scale, kReference);
-}
-
 /*
  * Load value from base + displacement.  Optionally perform null check
  * on base (which must have an associated s_reg and MIR).  If not
@@ -1217,9 +1213,11 @@
         alt_opcode = WIDE(kA64Ldur3rXd);
       }
       break;
-    case kSingle:     // Intentional fall-through.
-    case k32:         // Intentional fall-trough.
     case kReference:
+      r_dest = As32BitReg(r_dest);
+      FALLTHROUGH_INTENDED;
+    case kSingle:     // Intentional fall-through.
+    case k32:
       r_dest = Check32BitReg(r_dest);
       scale = 2;
       if (r_dest.IsFloat()) {
@@ -1260,7 +1258,9 @@
     // TODO: cleaner support for index/displacement registers?  Not a reference, but must match width.
     RegStorage r_scratch = AllocTempWide();
     LoadConstantWide(r_scratch, displacement);
-    load = LoadBaseIndexed(r_base, r_scratch, r_dest, 0, size);
+    load = LoadBaseIndexed(r_base, r_scratch,
+                           (size == kReference) ? As64BitReg(r_dest) : r_dest,
+                           0, size);
     FreeTemp(r_scratch);
   }
 
@@ -1287,11 +1287,6 @@
   return load;
 }
 
-LIR* Arm64Mir2Lir::LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
-                               VolatileKind is_volatile) {
-  return LoadBaseDisp(r_base, displacement, As32BitReg(r_dest), kReference, is_volatile);
-}
-
 LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
                                      OpSize size) {
   LIR* store = NULL;
@@ -1314,9 +1309,11 @@
         alt_opcode = WIDE(kA64Stur3rXd);
       }
       break;
-    case kSingle:     // Intentional fall-through.
-    case k32:         // Intentional fall-trough.
     case kReference:
+      r_src = As32BitReg(r_src);
+      FALLTHROUGH_INTENDED;
+    case kSingle:     // Intentional fall-through.
+    case k32:
       r_src = Check32BitReg(r_src);
       scale = 2;
       if (r_src.IsFloat()) {
@@ -1351,7 +1348,9 @@
     // Use long sequence.
     RegStorage r_scratch = AllocTempWide();
     LoadConstantWide(r_scratch, displacement);
-    store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size);
+    store = StoreBaseIndexed(r_base, r_scratch,
+                             (size == kReference) ? As64BitReg(r_src) : r_src,
+                             0, size);
     FreeTemp(r_scratch);
   }
 
@@ -1385,11 +1384,6 @@
   return store;
 }
 
-LIR* Arm64Mir2Lir::StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
-                                VolatileKind is_volatile) {
-  return StoreBaseDisp(r_base, displacement, As32BitReg(r_src), kReference, is_volatile);
-}
-
 LIR* Arm64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
   UNUSED(r_dest, r_src);
   LOG(FATAL) << "Unexpected use of OpFpRegCopy for Arm64";
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 58bcee2..88a4605 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-#include "dex/compiler_internals.h"
+#include "mir_to_lir-inl.h"
+
+#include "dex/mir_graph.h"
+#include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
+#include "driver/dex_compilation_unit.h"
 #include "dex_file-inl.h"
 #include "gc_map.h"
 #include "gc_map_builder.h"
 #include "mapping_table.h"
-#include "mir_to_lir-inl.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/verification_results.h"
@@ -538,9 +541,12 @@
         bx_offset = tab_rec->anchor->offset + 4;
         break;
       case kX86:
-      case kX86_64:
         bx_offset = 0;
         break;
+      case kX86_64:
+        // RIP relative to switch table.
+        bx_offset = tab_rec->offset;
+        break;
       case kArm64:
       case kMips:
         bx_offset = tab_rec->anchor->offset;
@@ -551,29 +557,49 @@
       LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset;
     }
     if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
-      const int32_t* keys = reinterpret_cast<const int32_t*>(&(tab_rec->table[2]));
-      for (int elems = 0; elems < tab_rec->table[1]; elems++) {
-        int disp = tab_rec->targets[elems]->offset - bx_offset;
+      DCHECK(tab_rec->switch_mir != nullptr);
+      BasicBlock* bb = mir_graph_->GetBasicBlock(tab_rec->switch_mir->bb);
+      DCHECK(bb != nullptr);
+      int elems = 0;
+      for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
+        int key = successor_block_info->key;
+        int target = successor_block_info->block;
+        LIR* boundary_lir = InsertCaseLabel(target, key);
+        DCHECK(boundary_lir != nullptr);
+        int disp = boundary_lir->offset - bx_offset;
+        Push32(code_buffer_, key);
+        Push32(code_buffer_, disp);
         if (cu_->verbose) {
           LOG(INFO) << "  Case[" << elems << "] key: 0x"
-                    << std::hex << keys[elems] << ", disp: 0x"
+                    << std::hex << key << ", disp: 0x"
                     << std::hex << disp;
         }
-        Push32(code_buffer_, keys[elems]);
-        Push32(code_buffer_,
-          tab_rec->targets[elems]->offset - bx_offset);
+        elems++;
       }
+      DCHECK_EQ(elems, tab_rec->table[1]);
     } else {
       DCHECK_EQ(static_cast<int>(tab_rec->table[0]),
                 static_cast<int>(Instruction::kPackedSwitchSignature));
-      for (int elems = 0; elems < tab_rec->table[1]; elems++) {
-        int disp = tab_rec->targets[elems]->offset - bx_offset;
+      DCHECK(tab_rec->switch_mir != nullptr);
+      BasicBlock* bb = mir_graph_->GetBasicBlock(tab_rec->switch_mir->bb);
+      DCHECK(bb != nullptr);
+      int elems = 0;
+      int low_key = s4FromSwitchData(&tab_rec->table[2]);
+      for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
+        int key = successor_block_info->key;
+        DCHECK_EQ(elems + low_key, key);
+        int target = successor_block_info->block;
+        LIR* boundary_lir = InsertCaseLabel(target, key);
+        DCHECK(boundary_lir != nullptr);
+        int disp = boundary_lir->offset - bx_offset;
+        Push32(code_buffer_, disp);
         if (cu_->verbose) {
           LOG(INFO) << "  Case[" << elems << "] disp: 0x"
                     << std::hex << disp;
         }
-        Push32(code_buffer_, tab_rec->targets[elems]->offset - bx_offset);
+        elems++;
       }
+      DCHECK_EQ(elems, tab_rec->table[1]);
     }
   }
 }
@@ -775,6 +801,10 @@
         ": " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
     native_gc_map_builder.AddEntry(native_offset, references);
   }
+
+  // Maybe not necessary, but this could help prevent errors where we access the verified method
+  // after it has been deleted.
+  mir_graph_->GetCurrentDexCompilationUnit()->ClearVerifiedMethod();
 }
 
 /* Determine the offset of each literal field */
@@ -820,13 +850,15 @@
  * branch table during the assembly phase.  All resource flags
  * are set to prevent code motion.  KeyVal is just there for debugging.
  */
-LIR* Mir2Lir::InsertCaseLabel(DexOffset vaddr, int keyVal) {
-  LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id];
+LIR* Mir2Lir::InsertCaseLabel(uint32_t bbid, int keyVal) {
+  LIR* boundary_lir = &block_label_list_[bbid];
   LIR* res = boundary_lir;
   if (cu_->verbose) {
     // Only pay the expense if we're pretty-printing.
     LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocLIR));
-    new_label->dalvik_offset = vaddr;
+    BasicBlock* bb = mir_graph_->GetBasicBlock(bbid);
+    DCHECK(bb != nullptr);
+    new_label->dalvik_offset = bb->start_offset;
     new_label->opcode = kPseudoCaseLabel;
     new_label->operands[0] = keyVal;
     new_label->flags.fixup = kFixupLabel;
@@ -838,40 +870,6 @@
   return res;
 }
 
-void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
-  const uint16_t* table = tab_rec->table;
-  DexOffset base_vaddr = tab_rec->vaddr;
-  const int32_t *targets = reinterpret_cast<const int32_t*>(&table[4]);
-  int entries = table[1];
-  int low_key = s4FromSwitchData(&table[2]);
-  for (int i = 0; i < entries; i++) {
-    tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], i + low_key);
-  }
-}
-
-void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
-  const uint16_t* table = tab_rec->table;
-  DexOffset base_vaddr = tab_rec->vaddr;
-  int entries = table[1];
-  const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
-  const int32_t* targets = &keys[entries];
-  for (int i = 0; i < entries; i++) {
-    tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]);
-  }
-}
-
-void Mir2Lir::ProcessSwitchTables() {
-  for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
-    if (tab_rec->table[0] == Instruction::kPackedSwitchSignature) {
-      MarkPackedCaseLabels(tab_rec);
-    } else if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
-      MarkSparseCaseLabels(tab_rec);
-    } else {
-      LOG(FATAL) << "Invalid switch table";
-    }
-  }
-}
-
 void Mir2Lir::DumpSparseSwitchTable(const uint16_t* table) {
   /*
    * Sparse switch data format:
@@ -957,12 +955,12 @@
 
 // TODO: move to mir_to_lir.cc
 Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
-    : Backend(arena),
-      literal_list_(nullptr),
+    : literal_list_(nullptr),
       method_literal_list_(nullptr),
       class_literal_list_(nullptr),
       code_literal_list_(nullptr),
       first_fixup_(nullptr),
+      arena_(arena),
       cu_(cu),
       mir_graph_(mir_graph),
       switch_tables_(arena->Adapter(kArenaAllocSwitchTable)),
@@ -990,7 +988,8 @@
       last_lir_insn_(nullptr),
       slow_paths_(arena->Adapter(kArenaAllocSlowPaths)),
       mem_ref_type_(ResourceMask::kHeapRef),
-      mask_cache_(arena) {
+      mask_cache_(arena),
+      in_to_reg_storage_mapping_(arena) {
   switch_tables_.reserve(4);
   fill_array_data_.reserve(4);
   tempreg_info_.reserve(20);
@@ -1021,9 +1020,6 @@
 
   /* Method is not empty */
   if (first_lir_insn_) {
-    // mark the targets of switch statement case labels
-    ProcessSwitchTables();
-
     /* Convert LIR into machine code. */
     AssembleLIR();
 
@@ -1078,12 +1074,20 @@
   });
 
   std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnFrameDescriptionEntry());
-  CompiledMethod* result =
-      new CompiledMethod(cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
-                         core_spill_mask_, fp_spill_mask_, &src_mapping_table_, encoded_mapping_table_,
-                         vmap_encoder.GetData(), native_gc_map_, cfi_info.get(),
-                         ArrayRef<LinkerPatch>(patches_));
-  return result;
+  ArrayRef<const uint8_t> cfi_ref;
+  if (cfi_info.get() != nullptr) {
+    cfi_ref = ArrayRef<const uint8_t>(*cfi_info);
+  }
+  return CompiledMethod::SwapAllocCompiledMethod(
+      cu_->compiler_driver, cu_->instruction_set,
+      ArrayRef<const uint8_t>(code_buffer_),
+      frame_size_, core_spill_mask_, fp_spill_mask_,
+      &src_mapping_table_,
+      ArrayRef<const uint8_t>(encoded_mapping_table_),
+      ArrayRef<const uint8_t>(vmap_encoder.GetData()),
+      ArrayRef<const uint8_t>(native_gc_map_),
+      cfi_ref,
+      ArrayRef<LinkerPatch>(patches_));
 }
 
 size_t Mir2Lir::GetMaxPossibleCompilerTemps() const {
@@ -1159,24 +1163,6 @@
   new_lir->next->prev = new_lir;
 }
 
-bool Mir2Lir::IsPowerOfTwo(uint64_t x) {
-  return (x & (x - 1)) == 0;
-}
-
-// Returns the index of the lowest set bit in 'x'.
-int32_t Mir2Lir::LowestSetBit(uint64_t x) {
-  int bit_posn = 0;
-  while ((x & 0xf) == 0) {
-    bit_posn += 4;
-    x >>= 4;
-  }
-  while ((x & 1) == 0) {
-    bit_posn++;
-    x >>= 1;
-  }
-  return bit_posn;
-}
-
 bool Mir2Lir::PartiallyIntersects(RegLocation rl_src, RegLocation rl_dest) {
   DCHECK(rl_src.wide);
   DCHECK(rl_dest.wide);
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 3039852..7245853 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -18,18 +18,15 @@
 
 #include <algorithm>
 
+#include "base/logging.h"
 #include "base/macros.h"
-#include "base/mutex.h"
 #include "base/mutex-inl.h"
-#include "dex/frontend.h"
-#include "thread.h"
+#include "dex/compiler_ir.h"
 #include "thread-inl.h"
 #include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir.h"
-#include "dex_instruction.h"
 #include "dex_instruction-inl.h"
 #include "driver/dex_compilation_unit.h"
-#include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
 namespace art {
@@ -293,9 +290,9 @@
     { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } }
 
     INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
-    INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0),
+    INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, kIntrinsicFlagToFloatingPoint),
     INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
-    INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0),
+    INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint),
 
     INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32),
     INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64),
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 774176e..9f53b89 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -13,18 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include "mir_to_lir-inl.h"
+
+#include <functional>
+
 #include "arch/arm/instruction_set_features_arm.h"
+#include "base/macros.h"
 #include "dex/compiler_ir.h"
-#include "dex/compiler_internals.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/arm/arm_lir.h"
-#include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/array.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_reference.h"
+#include "utils.h"
 #include "verifier/method_verifier.h"
-#include <functional>
 
 namespace art {
 
@@ -38,6 +44,18 @@
  * and "op" calls may be used here.
  */
 
+ALWAYS_INLINE static inline bool ForceSlowFieldPath(CompilationUnit* cu) {
+  return (cu->enable_debug & (1 << kDebugSlowFieldPath)) != 0;
+}
+
+ALWAYS_INLINE static inline bool ForceSlowStringPath(CompilationUnit* cu) {
+  return (cu->enable_debug & (1 << kDebugSlowStringPath)) != 0;
+}
+
+ALWAYS_INLINE static inline bool ForceSlowTypePath(CompilationUnit* cu) {
+  return (cu->enable_debug & (1 << kDebugSlowTypePath)) != 0;
+}
+
 /*
  * Generate a kPseudoBarrier marker to indicate the boundary of special
  * blocks.
@@ -371,19 +389,19 @@
       // The fast path.
       if (!use_direct_type_ptr) {
         LoadClassType(*dex_file, type_idx, kArg0);
-        CallRuntimeHelperRegMethodRegLocation(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide),
+        CallRuntimeHelperRegRegLocationMethod(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide),
                                               rl_src, true);
       } else {
         // Use the direct pointer.
-        CallRuntimeHelperImmMethodRegLocation(kQuickAllocArrayResolved, direct_type_ptr, rl_src,
+        CallRuntimeHelperImmRegLocationMethod(kQuickAllocArrayResolved, direct_type_ptr, rl_src,
                                               true);
       }
     } else {
       // The slow path.
-      CallRuntimeHelperImmMethodRegLocation(kQuickAllocArray, type_idx, rl_src, true);
+      CallRuntimeHelperImmRegLocationMethod(kQuickAllocArray, type_idx, rl_src, true);
     }
   } else {
-    CallRuntimeHelperImmMethodRegLocation(kQuickAllocArrayWithAccessCheck, type_idx, rl_src, true);
+    CallRuntimeHelperImmRegLocationMethod(kQuickAllocArrayWithAccessCheck, type_idx, rl_src, true);
   }
   StoreValue(rl_dest, GetReturn(kRefReg));
 }
@@ -405,7 +423,7 @@
   } else {
     target = kQuickCheckAndAllocArrayWithAccessCheck;
   }
-  CallRuntimeHelperImmMethodImm(target, type_idx, elems, true);
+  CallRuntimeHelperImmImmMethod(target, type_idx, elems, true);
   FreeTemp(TargetReg(kArg2, kNotWide));
   FreeTemp(TargetReg(kArg1, kNotWide));
   /*
@@ -591,7 +609,7 @@
   const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
   DCHECK_EQ(SPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
   cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass());
-  if (!SLOW_FIELD_PATH && field_info.FastPut()) {
+  if (!ForceSlowFieldPath(cu_) && field_info.FastPut()) {
     DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
     RegStorage r_base;
     if (field_info.IsReferrersClass()) {
@@ -711,7 +729,7 @@
   DCHECK_EQ(SGetMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
   cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass());
 
-  if (!SLOW_FIELD_PATH && field_info.FastGet()) {
+  if (!ForceSlowFieldPath(cu_) && field_info.FastGet()) {
     DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
     RegStorage r_base;
     if (field_info.IsReferrersClass()) {
@@ -849,7 +867,7 @@
   const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
   DCHECK_EQ(IGetMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
   cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet());
-  if (!SLOW_FIELD_PATH && field_info.FastGet()) {
+  if (!ForceSlowFieldPath(cu_) && field_info.FastGet()) {
     RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
     // A load of the class will lead to an iget with offset 0.
     DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
@@ -923,7 +941,7 @@
   const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
   DCHECK_EQ(IPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
   cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut());
-  if (!SLOW_FIELD_PATH && field_info.FastPut()) {
+  if (!ForceSlowFieldPath(cu_) && field_info.FastPut()) {
     RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
     // Dex code never writes to the class field.
     DCHECK_GE(static_cast<uint32_t>(field_info.FieldOffset().Int32Value()),
@@ -936,15 +954,15 @@
     }
     GenNullCheck(rl_obj.reg, opt_flags);
     int field_offset = field_info.FieldOffset().Int32Value();
-    LIR* store;
+    LIR* null_ck_insn;
     if (IsRef(size)) {
-      store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
+      null_ck_insn = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
           kVolatile : kNotVolatile);
     } else {
-      store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
-                            field_info.IsVolatile() ? kVolatile : kNotVolatile);
+      null_ck_insn = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
+                                   field_info.IsVolatile() ? kVolatile : kNotVolatile);
     }
-    MarkPossibleNullPointerExceptionAfter(opt_flags, store);
+    MarkPossibleNullPointerExceptionAfter(opt_flags, null_ck_insn);
     if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
       MarkGCCard(opt_flags, rl_src.reg, rl_obj.reg);
     }
@@ -1013,7 +1031,7 @@
     int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
     LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
     if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
-        type_idx) || SLOW_TYPE_PATH) {
+        type_idx) || ForceSlowTypePath(cu_)) {
       // Slow path, at runtime test if type is null and if so initialize
       FlushAllRegs();
       LIR* branch = OpCmpImmBranch(kCondEq, rl_result.reg, 0, NULL);
@@ -1058,7 +1076,7 @@
   int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
                                                                                       Int32Value();
   if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
-      *cu_->dex_file, string_idx) || SLOW_STRING_PATH) {
+      *cu_->dex_file, string_idx) || ForceSlowStringPath(cu_)) {
     // slow path, resolve string if not in dex cache
     FlushAllRegs();
     LockCallTemps();  // Using explicit registers
@@ -1098,7 +1116,7 @@
 
         void Compile() {
           GenerateTargetLabel();
-          m2l_->CallRuntimeHelperRegImm(kQuickResolveString, r_method_, string_idx_, true);
+          m2l_->CallRuntimeHelperImmReg(kQuickResolveString, string_idx_, r_method_, true);
           m2l_->OpUnconditionalBranch(cont_);
         }
 
@@ -1676,7 +1694,7 @@
       rl_result = GenDivRem(rl_dest, rl_src1.reg, rl_src2.reg, op == kOpDiv);
       done = true;
     } else if (cu_->instruction_set == kThumb2) {
-      if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
+      if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
               HasDivideInstruction()) {
         // Use ARM SDIV instruction for division.  For remainder we also need to
         // calculate using a MUL and subtract.
@@ -1724,16 +1742,12 @@
 
 // Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
 // and store the result in 'rl_dest'.
-bool Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
+bool Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode ATTRIBUTE_UNUSED, bool is_div,
                                RegLocation rl_src, RegLocation rl_dest, int lit) {
-  if ((lit < 2) || ((cu_->instruction_set != kThumb2) && !IsPowerOfTwo(lit))) {
+  if ((lit < 2) || (!IsPowerOfTwo(lit))) {
     return false;
   }
-  // No divide instruction for Arm, so check for more special cases
-  if ((cu_->instruction_set == kThumb2) && !IsPowerOfTwo(lit)) {
-    return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
-  }
-  int k = LowestSetBit(lit);
+  int k = CTZ(lit);
   if (k >= 30) {
     // Avoid special cases.
     return false;
@@ -1813,18 +1827,18 @@
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   if (power_of_two) {
     // Shift.
-    OpRegRegImm(kOpLsl, rl_result.reg, rl_src.reg, LowestSetBit(lit));
+    OpRegRegImm(kOpLsl, rl_result.reg, rl_src.reg, CTZ(lit));
   } else if (pop_count_le2) {
     // Shift and add and shift.
-    int first_bit = LowestSetBit(lit);
-    int second_bit = LowestSetBit(lit ^ (1 << first_bit));
+    int first_bit = CTZ(lit);
+    int second_bit = CTZ(lit ^ (1 << first_bit));
     GenMultiplyByTwoBitMultiplier(rl_src, rl_result, lit, first_bit, second_bit);
   } else {
     // Reverse subtract: (src << (shift + 1)) - src.
     DCHECK(power_of_two_minus_one);
-    // TUNING: rsb dst, src, src lsl#LowestSetBit(lit + 1)
+    // TUNING: rsb dst, src, src lsl#CTZ(lit + 1)
     RegStorage t_reg = AllocTemp();
-    OpRegRegImm(kOpLsl, t_reg, rl_src.reg, LowestSetBit(lit + 1));
+    OpRegRegImm(kOpLsl, t_reg, rl_src.reg, CTZ(lit + 1));
     OpRegRegReg(kOpSub, rl_result.reg, t_reg, rl_src.reg);
   }
   StoreValue(rl_dest, rl_result);
@@ -1974,7 +1988,7 @@
         rl_result = GenDivRemLit(rl_dest, rl_src, lit, is_div);
         done = true;
       } else if (cu_->instruction_set == kThumb2) {
-        if (cu_->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
+        if (cu_->compiler_driver->GetInstructionSetFeatures()->AsArmInstructionSetFeatures()->
                 HasDivideInstruction()) {
           // Use ARM SDIV instruction for division.  For remainder we also need to
           // calculate using a MUL and subtract.
@@ -2163,18 +2177,15 @@
 
 /* Check if we need to check for pending suspend request */
 void Mir2Lir::GenSuspendTest(int opt_flags) {
+  if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK) != 0) {
+    return;
+  }
   if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) {
-    if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
-      return;
-    }
     FlushAllRegs();
     LIR* branch = OpTestSuspend(NULL);
     LIR* cont = NewLIR0(kPseudoTargetLabel);
     AddSlowPath(new (arena_) SuspendCheckSlowPath(this, branch, cont));
   } else {
-    if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
-      return;
-    }
     FlushAllRegs();     // TODO: needed?
     LIR* inst = CheckSuspendUsingLoad();
     MarkSafepointPC(inst);
@@ -2183,11 +2194,11 @@
 
 /* Check if we need to check for pending suspend request */
 void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) {
+  if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK) != 0) {
+    OpUnconditionalBranch(target);
+    return;
+  }
   if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) {
-    if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
-      OpUnconditionalBranch(target);
-      return;
-    }
     OpTestSuspend(target);
     FlushAllRegs();
     LIR* branch = OpUnconditionalBranch(nullptr);
@@ -2195,10 +2206,6 @@
   } else {
     // For the implicit suspend check, just perform the trigger
     // load and branch to the target.
-    if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
-      OpUnconditionalBranch(target);
-      return;
-    }
     FlushAllRegs();
     LIR* inst = CheckSuspendUsingLoad();
     MarkSafepointPC(inst);
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 31b81bf..bb5b0cd 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
+#include "mir_to_lir-inl.h"
+
 #include "arm/codegen_arm.h"
 #include "dex/compiler_ir.h"
-#include "dex/frontend.h"
+#include "dex/dex_flags.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex_file-inl.h"
+#include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "invoke_type.h"
 #include "mirror/array.h"
@@ -27,7 +31,6 @@
 #include "mirror/dex_cache.h"
 #include "mirror/object_array-inl.h"
 #include "mirror/string.h"
-#include "mir_to_lir-inl.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
@@ -201,16 +204,16 @@
   CallHelper(r_tgt, trampoline, safepoint_pc);
 }
 
-void Mir2Lir::CallRuntimeHelperRegMethodRegLocation(QuickEntrypointEnum trampoline, RegStorage arg0,
-                                                    RegLocation arg2, bool safepoint_pc) {
+void Mir2Lir::CallRuntimeHelperRegRegLocationMethod(QuickEntrypointEnum trampoline, RegStorage arg0,
+                                                    RegLocation arg1, bool safepoint_pc) {
   RegStorage r_tgt = CallHelperSetup(trampoline);
-  DCHECK(!IsSameReg(TargetReg(kArg1, arg0.GetWideKind()), arg0));
+  DCHECK(!IsSameReg(TargetReg(kArg2, arg0.GetWideKind()), arg0));
   RegStorage r_tmp = TargetReg(kArg0, arg0.GetWideKind());
   if (r_tmp.NotExactlyEquals(arg0)) {
     OpRegCopy(r_tmp, arg0);
   }
-  LoadCurrMethodDirect(TargetReg(kArg1, kRef));
-  LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2));
+  LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
+  LoadCurrMethodDirect(TargetReg(kArg2, kRef));
   ClobberCallerSave();
   CallHelper(r_tgt, trampoline, safepoint_pc);
 }
@@ -306,21 +309,21 @@
   CallHelper(r_tgt, trampoline, safepoint_pc);
 }
 
-void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(QuickEntrypointEnum trampoline, int arg0,
-                                                    RegLocation arg2, bool safepoint_pc) {
+void Mir2Lir::CallRuntimeHelperImmRegLocationMethod(QuickEntrypointEnum trampoline, int arg0,
+                                                    RegLocation arg1, bool safepoint_pc) {
   RegStorage r_tgt = CallHelperSetup(trampoline);
-  LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2));
-  LoadCurrMethodDirect(TargetReg(kArg1, kRef));
+  LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
+  LoadCurrMethodDirect(TargetReg(kArg2, kRef));
   LoadConstant(TargetReg(kArg0, kNotWide), arg0);
   ClobberCallerSave();
   CallHelper(r_tgt, trampoline, safepoint_pc);
 }
 
-void Mir2Lir::CallRuntimeHelperImmMethodImm(QuickEntrypointEnum trampoline, int arg0, int arg2,
+void Mir2Lir::CallRuntimeHelperImmImmMethod(QuickEntrypointEnum trampoline, int arg0, int arg1,
                                             bool safepoint_pc) {
   RegStorage r_tgt = CallHelperSetup(trampoline);
-  LoadCurrMethodDirect(TargetReg(kArg1, kRef));
-  LoadConstant(TargetReg(kArg2, kNotWide), arg2);
+  LoadCurrMethodDirect(TargetReg(kArg2, kRef));
+  LoadConstant(TargetReg(kArg1, kNotWide), arg1);
   LoadConstant(TargetReg(kArg0, kNotWide), arg0);
   ClobberCallerSave();
   CallHelper(r_tgt, trampoline, safepoint_pc);
@@ -401,59 +404,50 @@
    * half to memory as well.
    */
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
-    PromotionMap* v_map = &promotion_map_[start_vreg + i];
+  RegLocation* t_loc = nullptr;
+  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i += t_loc->wide ? 2 : 1) {
+    // get reg corresponding to input
     RegStorage reg = GetArgMappingToPhysicalReg(i);
+    t_loc = &ArgLocs[i];
+
+    // If the wide input appeared as single, flush it and go
+    // as it comes from memory.
+    if (t_loc->wide && reg.Valid() && !reg.Is64Bit()) {
+      // The memory already holds the half. Don't do anything.
+      reg = RegStorage::InvalidReg();
+    }
 
     if (reg.Valid()) {
-      // If arriving in register
-      bool need_flush = true;
-      RegLocation* t_loc = &ArgLocs[i];
-      if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) {
-        OpRegCopy(RegStorage::Solo32(v_map->core_reg), reg);
-        need_flush = false;
-      } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) {
-        OpRegCopy(RegStorage::Solo32(v_map->fp_reg), reg);
-        need_flush = false;
-      } else {
-        need_flush = true;
-      }
+      // If arriving in register.
 
-      // For wide args, force flush if not fully promoted
-      if (t_loc->wide) {
-        PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1);
-        // Is only half promoted?
-        need_flush |= (p_map->core_location != v_map->core_location) ||
-            (p_map->fp_location != v_map->fp_location);
-        if ((cu_->instruction_set == kThumb2) && t_loc->fp && !need_flush) {
-          /*
-           * In Arm, a double is represented as a pair of consecutive single float
-           * registers starting at an even number.  It's possible that both Dalvik vRegs
-           * representing the incoming double were independently promoted as singles - but
-           * not in a form usable as a double.  If so, we need to flush - even though the
-           * incoming arg appears fully in register.  At this point in the code, both
-           * halves of the double are promoted.  Make sure they are in a usable form.
-           */
-          int lowreg_index = start_vreg + i + (t_loc->high_word ? -1 : 0);
-          int low_reg = promotion_map_[lowreg_index].fp_reg;
-          int high_reg = promotion_map_[lowreg_index + 1].fp_reg;
-          if (((low_reg & 0x1) != 0) || (high_reg != (low_reg + 1))) {
-            need_flush = true;
-          }
+      // We have already updated the arg location with promoted info
+      // so we can be based on it.
+      if (t_loc->location == kLocPhysReg) {
+        // Just copy it.
+        if (t_loc->wide) {
+          OpRegCopyWide(t_loc->reg, reg);
+        } else {
+          OpRegCopy(t_loc->reg, reg);
+        }
+      } else {
+        // Needs flush.
+        int offset = SRegOffset(start_vreg + i);
+        if (t_loc->ref) {
+          StoreRefDisp(TargetPtrReg(kSp), offset, reg, kNotVolatile);
+        } else {
+          StoreBaseDisp(TargetPtrReg(kSp), offset, reg, t_loc->wide ? k64 : k32, kNotVolatile);
         }
       }
-      if (need_flush) {
-        Store32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i), reg);
-      }
     } else {
-      // If arriving in frame & promoted
-      if (v_map->core_location == kLocPhysReg) {
-        Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i),
-                   RegStorage::Solo32(v_map->core_reg));
-      }
-      if (v_map->fp_location == kLocPhysReg) {
-        Load32Disp(TargetPtrReg(kSp), SRegOffset(start_vreg + i),
-                   RegStorage::Solo32(v_map->fp_reg));
+      // If arriving in frame & promoted.
+      if (t_loc->location == kLocPhysReg) {
+        int offset = SRegOffset(start_vreg + i);
+        if (t_loc->ref) {
+          LoadRefDisp(TargetPtrReg(kSp), offset, t_loc->reg, kNotVolatile);
+        } else {
+          LoadBaseDisp(TargetPtrReg(kSp), offset, t_loc->reg, t_loc->wide ? k64 : k32,
+                       kNotVolatile);
+        }
       }
     }
   }
@@ -488,87 +482,10 @@
 
 /*
  * Bit of a hack here - in the absence of a real scheduling pass,
- * emit the next instruction in static & direct invoke sequences.
- */
-static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
-                          int state, const MethodReference& target_method,
-                          uint32_t,
-                          uintptr_t direct_code, uintptr_t direct_method,
-                          InvokeType type) {
-  UNUSED(info);
-  DCHECK(cu->instruction_set != kX86 && cu->instruction_set != kX86_64 &&
-         cu->instruction_set != kThumb2 && cu->instruction_set != kArm &&
-         cu->instruction_set != kArm64);
-  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
-  if (direct_code != 0 && direct_method != 0) {
-    switch (state) {
-    case 0:  // Get the current Method* [sets kArg0]
-      if (direct_code != static_cast<uintptr_t>(-1)) {
-        cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
-      } else {
-        cg->LoadCodeAddress(target_method, type, kInvokeTgt);
-      }
-      if (direct_method != static_cast<uintptr_t>(-1)) {
-        cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
-      } else {
-        cg->LoadMethodAddress(target_method, type, kArg0);
-      }
-      break;
-    default:
-      return -1;
-    }
-  } else {
-    RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
-    switch (state) {
-    case 0:  // Get the current Method* [sets kArg0]
-      // TUNING: we can save a reg copy if Method* has been promoted.
-      cg->LoadCurrMethodDirect(arg0_ref);
-      break;
-    case 1:  // Get method->dex_cache_resolved_methods_
-      cg->LoadRefDisp(arg0_ref,
-                      mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
-                      arg0_ref,
-                      kNotVolatile);
-      // Set up direct code if known.
-      if (direct_code != 0) {
-        if (direct_code != static_cast<uintptr_t>(-1)) {
-          cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
-        } else {
-          CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
-          cg->LoadCodeAddress(target_method, type, kInvokeTgt);
-        }
-      }
-      break;
-    case 2:  // Grab target method*
-      CHECK_EQ(cu->dex_file, target_method.dex_file);
-      cg->LoadRefDisp(arg0_ref,
-                      ObjArray::OffsetOfElement(target_method.dex_method_index).Int32Value(),
-                      arg0_ref,
-                      kNotVolatile);
-      break;
-    case 3:  // Grab the code from the method*
-      if (direct_code == 0) {
-        if (CommonCallCodeLoadCodePointerIntoInvokeTgt(&arg0_ref, cu, cg)) {
-          break;                                    // kInvokeTgt := arg0_ref->entrypoint
-        }
-      } else {
-        break;
-      }
-      DCHECK(cu->instruction_set == kX86 || cu->instruction_set == kX86_64);
-      FALLTHROUGH_INTENDED;
-    default:
-      return -1;
-    }
-  }
-  return state + 1;
-}
-
-/*
- * Bit of a hack here - in the absence of a real scheduling pass,
  * emit the next instruction in a virtual invoke sequence.
  * We can use kLr as a temp prior to target address loading
  * Note also that we'll load the first argument ("this") into
- * kArg1 here rather than the standard LoadArgRegs.
+ * kArg1 here rather than the standard GenDalvikArgs.
  */
 static int NextVCallInsn(CompilationUnit* cu, CallInfo* info,
                          int state, const MethodReference& target_method,
@@ -612,7 +529,7 @@
  * Emit the next instruction in an invoke interface sequence. This will do a lookup in the
  * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if
  * more than one interface method map to the same index. Note also that we'll load the first
- * argument ("this") into kArg1 here rather than the standard LoadArgRegs.
+ * argument ("this") into kArg1 here rather than the standard GenDalvikArgs.
  */
 static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
                                  const MethodReference& target_method,
@@ -719,158 +636,6 @@
                           target_method, 0);
 }
 
-int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state,
-                         NextCallInsn next_call_insn,
-                         const MethodReference& target_method,
-                         uint32_t vtable_idx, uintptr_t direct_code,
-                         uintptr_t direct_method, InvokeType type, bool skip_this) {
-  int last_arg_reg = 3 - 1;
-  int arg_regs[3] = {TargetReg(kArg1, kNotWide).GetReg(), TargetReg(kArg2, kNotWide).GetReg(),
-                     TargetReg(kArg3, kNotWide).GetReg()};
-
-  int next_reg = 0;
-  int next_arg = 0;
-  if (skip_this) {
-    next_reg++;
-    next_arg++;
-  }
-  for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) {
-    RegLocation rl_arg = info->args[next_arg++];
-    rl_arg = UpdateRawLoc(rl_arg);
-    if (rl_arg.wide && (next_reg <= last_arg_reg - 1)) {
-      RegStorage r_tmp(RegStorage::k64BitPair, arg_regs[next_reg], arg_regs[next_reg + 1]);
-      LoadValueDirectWideFixed(rl_arg, r_tmp);
-      next_reg++;
-      next_arg++;
-    } else {
-      if (rl_arg.wide) {
-        rl_arg = NarrowRegLoc(rl_arg);
-        rl_arg.is_const = false;
-      }
-      LoadValueDirectFixed(rl_arg, RegStorage::Solo32(arg_regs[next_reg]));
-    }
-    call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                                direct_code, direct_method, type);
-  }
-  return call_state;
-}
-
-/*
- * Load up to 5 arguments, the first three of which will be in
- * kArg1 .. kArg3.  On entry kArg0 contains the current method pointer,
- * and as part of the load sequence, it must be replaced with
- * the target method pointer.  Note, this may also be called
- * for "range" variants if the number of arguments is 5 or fewer.
- */
-int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
-                                  int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
-                                  const MethodReference& target_method,
-                                  uint32_t vtable_idx, uintptr_t direct_code,
-                                  uintptr_t direct_method, InvokeType type, bool skip_this) {
-  RegLocation rl_arg;
-
-  /* If no arguments, just return */
-  if (info->num_arg_words == 0)
-    return call_state;
-
-  call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                              direct_code, direct_method, type);
-
-  DCHECK_LE(info->num_arg_words, 5);
-  if (info->num_arg_words > 3) {
-    int32_t next_use = 3;
-    // Detect special case of wide arg spanning arg3/arg4
-    RegLocation rl_use0 = info->args[0];
-    RegLocation rl_use1 = info->args[1];
-    RegLocation rl_use2 = info->args[2];
-    if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) && rl_use2.wide) {
-      RegStorage reg;
-      // Wide spans, we need the 2nd half of uses[2].
-      rl_arg = UpdateLocWide(rl_use2);
-      if (rl_arg.location == kLocPhysReg) {
-        if (rl_arg.reg.IsPair()) {
-          reg = rl_arg.reg.GetHigh();
-        } else {
-          RegisterInfo* reg_info = GetRegInfo(rl_arg.reg);
-          reg_info = reg_info->FindMatchingView(RegisterInfo::kHighSingleStorageMask);
-          if (reg_info == nullptr) {
-            // NOTE: For hard float convention we won't split arguments across reg/mem.
-            UNIMPLEMENTED(FATAL) << "Needs hard float api.";
-          }
-          reg = reg_info->GetReg();
-        }
-      } else {
-        // kArg2 & rArg3 can safely be used here
-        reg = TargetReg(kArg3, kNotWide);
-        {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          Load32Disp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg);
-        }
-        call_state = next_call_insn(cu_, info, call_state, target_method,
-                                    vtable_idx, direct_code, direct_method, type);
-      }
-      {
-        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-        Store32Disp(TargetPtrReg(kSp), (next_use + 1) * 4, reg);
-      }
-      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                                  direct_code, direct_method, type);
-      next_use++;
-    }
-    // Loop through the rest
-    while (next_use < info->num_arg_words) {
-      RegStorage arg_reg;
-      rl_arg = info->args[next_use];
-      rl_arg = UpdateRawLoc(rl_arg);
-      if (rl_arg.location == kLocPhysReg) {
-        arg_reg = rl_arg.reg;
-      } else {
-        arg_reg = TargetReg(kArg2, rl_arg.wide ? kWide : kNotWide);
-        if (rl_arg.wide) {
-          LoadValueDirectWideFixed(rl_arg, arg_reg);
-        } else {
-          LoadValueDirectFixed(rl_arg, arg_reg);
-        }
-        call_state = next_call_insn(cu_, info, call_state, target_method,
-                                    vtable_idx, direct_code, direct_method, type);
-      }
-      int outs_offset = (next_use + 1) * 4;
-      {
-        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-        if (rl_arg.wide) {
-          StoreBaseDisp(TargetPtrReg(kSp), outs_offset, arg_reg, k64, kNotVolatile);
-          next_use += 2;
-        } else {
-          Store32Disp(TargetPtrReg(kSp), outs_offset, arg_reg);
-          next_use++;
-        }
-      }
-      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                               direct_code, direct_method, type);
-    }
-  }
-
-  call_state = LoadArgRegs(info, call_state, next_call_insn,
-                           target_method, vtable_idx, direct_code, direct_method,
-                           type, skip_this);
-
-  if (pcrLabel) {
-    if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
-      *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
-    } else {
-      *pcrLabel = nullptr;
-      if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
-          (info->opt_flags & MIR_IGNORE_NULL_CHECK)) {
-        return call_state;
-      }
-      // In lieu of generating a check for kArg1 being null, we need to
-      // perform a load when doing implicit checks.
-      GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
-    }
-  }
-  return call_state;
-}
-
 // Default implementation of implicit null pointer check.
 // Overridden by arch specific as necessary.
 void Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) {
@@ -883,209 +648,196 @@
   FreeTemp(tmp);
 }
 
-
-/*
- * May have 0+ arguments (also used for jumbo).  Note that
- * source virtual registers may be in physical registers, so may
- * need to be flushed to home location before copying.  This
- * applies to arg3 and above (see below).
- *
- * Two general strategies:
- *    If < 20 arguments
- *       Pass args 3-18 using vldm/vstm block copy
- *       Pass arg0, arg1 & arg2 in kArg1-kArg3
- *    If 20+ arguments
- *       Pass args arg19+ using memcpy block copy
- *       Pass arg0, arg1 & arg2 in kArg1-kArg3
- *
+/**
+ * @brief Used to flush promoted registers if they are used as argument
+ * in an invocation.
+ * @param info the infromation about arguments for invocation.
+ * @param start the first argument we should start to look from.
  */
-int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
-                                LIR** pcrLabel, NextCallInsn next_call_insn,
-                                const MethodReference& target_method,
-                                uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
-                                InvokeType type, bool skip_this) {
-  // If we can treat it as non-range (Jumbo ops will use range form)
-  if (info->num_arg_words <= 5)
-    return GenDalvikArgsNoRange(info, call_state, pcrLabel,
-                                next_call_insn, target_method, vtable_idx,
-                                direct_code, direct_method, type, skip_this);
-  /*
-   * First load the non-register arguments.  Both forms expect all
-   * of the source arguments to be in their home frame location, so
-   * scan the s_reg names and flush any that have been promoted to
-   * frame backing storage.
-   */
+void Mir2Lir::GenDalvikArgsFlushPromoted(CallInfo* info, int start) {
+  if (cu_->disable_opt & (1 << kPromoteRegs)) {
+    // This make sense only if promotion is enabled.
+    return;
+  }
+  ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
   // Scan the rest of the args - if in phys_reg flush to memory
-  for (int next_arg = 0; next_arg < info->num_arg_words;) {
+  for (int next_arg = start; next_arg < info->num_arg_words;) {
     RegLocation loc = info->args[next_arg];
     if (loc.wide) {
       loc = UpdateLocWide(loc);
-      if ((next_arg >= 2) && (loc.location == kLocPhysReg)) {
-        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+      if (loc.location == kLocPhysReg) {
         StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
       }
       next_arg += 2;
     } else {
       loc = UpdateLoc(loc);
-      if ((next_arg >= 3) && (loc.location == kLocPhysReg)) {
-        ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-        Store32Disp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg);
+      if (loc.location == kLocPhysReg) {
+        if (loc.ref) {
+          StoreRefDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, kNotVolatile);
+        } else {
+          StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(loc.s_reg_low), loc.reg, k32,
+                        kNotVolatile);
+        }
       }
       next_arg++;
     }
   }
+}
 
-  // The first 3 arguments are passed via registers.
-  // TODO: For 64-bit, instead of hardcoding 4 for Method* size, we should either
-  // get size of uintptr_t or size of object reference according to model being used.
-  int outs_offset = 4 /* Method* */ + (3 * sizeof(uint32_t));
-  int start_offset = SRegOffset(info->args[3].s_reg_low);
-  int regs_left_to_pass_via_stack = info->num_arg_words - 3;
-  DCHECK_GT(regs_left_to_pass_via_stack, 0);
+/**
+ * @brief Used to optimize the copying of VRs which are arguments of invocation.
+ * Please note that you should flush promoted registers first if you copy.
+ * If implementation does copying it may skip several of the first VRs but must copy
+ * till the end. Implementation must return the number of skipped VRs
+ * (it might be all VRs).
+ * @see GenDalvikArgsFlushPromoted
+ * @param info the information about arguments for invocation.
+ * @param first the first argument we should start to look from.
+ * @param count the number of remaining arguments we can handle.
+ * @return the number of arguments which we did not handle. Unhandled arguments
+ * must be attached to the first one.
+ */
+int Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) {
+  // call is pretty expensive, let's use it if count is big.
+  if (count > 16) {
+    GenDalvikArgsFlushPromoted(info, first);
+    int start_offset = SRegOffset(info->args[first].s_reg_low);
+    int outs_offset = StackVisitor::GetOutVROffset(first, cu_->instruction_set);
 
-  if (cu_->instruction_set == kThumb2 && regs_left_to_pass_via_stack <= 16) {
-    // Use vldm/vstm pair using kArg3 as a temp
-    call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                             direct_code, direct_method, type);
-    OpRegRegImm(kOpAdd, TargetReg(kArg3, kRef), TargetPtrReg(kSp), start_offset);
-    LIR* ld = nullptr;
-    {
-      ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-      ld = OpVldm(TargetReg(kArg3, kRef), regs_left_to_pass_via_stack);
-    }
-    // TUNING: loosen barrier
-    ld->u.m.def_mask = &kEncodeAll;
-    call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                             direct_code, direct_method, type);
-    OpRegRegImm(kOpAdd, TargetReg(kArg3, kRef), TargetPtrReg(kSp), 4 /* Method* */ + (3 * 4));
-    call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                             direct_code, direct_method, type);
-    LIR* st = nullptr;
-    {
-      ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-      st = OpVstm(TargetReg(kArg3, kRef), regs_left_to_pass_via_stack);
-    }
-    st->u.m.def_mask = &kEncodeAll;
-    call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                             direct_code, direct_method, type);
-  } else if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
-    int current_src_offset = start_offset;
-    int current_dest_offset = outs_offset;
-
-    // Only davik regs are accessed in this loop; no next_call_insn() calls.
-    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    while (regs_left_to_pass_via_stack > 0) {
-      // This is based on the knowledge that the stack itself is 16-byte aligned.
-      bool src_is_16b_aligned = (current_src_offset & 0xF) == 0;
-      bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0;
-      size_t bytes_to_move;
-
-      /*
-       * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a
-       * a 128-bit move because we won't get the chance to try to aligned. If there are more than
-       * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned.
-       * We do this because we could potentially do a smaller move to align.
-       */
-      if (regs_left_to_pass_via_stack == 4 ||
-          (regs_left_to_pass_via_stack > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) {
-        // Moving 128-bits via xmm register.
-        bytes_to_move = sizeof(uint32_t) * 4;
-
-        // Allocate a free xmm temp. Since we are working through the calling sequence,
-        // we expect to have an xmm temporary available.  AllocTempDouble will abort if
-        // there are no free registers.
-        RegStorage temp = AllocTempDouble();
-
-        LIR* ld1 = nullptr;
-        LIR* ld2 = nullptr;
-        LIR* st1 = nullptr;
-        LIR* st2 = nullptr;
-
-        /*
-         * The logic is similar for both loads and stores. If we have 16-byte alignment,
-         * do an aligned move. If we have 8-byte alignment, then do the move in two
-         * parts. This approach prevents possible cache line splits. Finally, fall back
-         * to doing an unaligned move. In most cases we likely won't split the cache
-         * line but we cannot prove it and thus take a conservative approach.
-         */
-        bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
-        bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
-
-        if (src_is_16b_aligned) {
-          ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovA128FP);
-        } else if (src_is_8b_aligned) {
-          ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovLo128FP);
-          ld2 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset + (bytes_to_move >> 1),
-                            kMovHi128FP);
-        } else {
-          ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovU128FP);
-        }
-
-        if (dest_is_16b_aligned) {
-          st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovA128FP);
-        } else if (dest_is_8b_aligned) {
-          st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovLo128FP);
-          st2 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset + (bytes_to_move >> 1),
-                            temp, kMovHi128FP);
-        } else {
-          st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovU128FP);
-        }
-
-        // TODO If we could keep track of aliasing information for memory accesses that are wider
-        // than 64-bit, we wouldn't need to set up a barrier.
-        if (ld1 != nullptr) {
-          if (ld2 != nullptr) {
-            // For 64-bit load we can actually set up the aliasing information.
-            AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true);
-            AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true,
-                                    true);
-          } else {
-            // Set barrier for 128-bit load.
-            ld1->u.m.def_mask = &kEncodeAll;
-          }
-        }
-        if (st1 != nullptr) {
-          if (st2 != nullptr) {
-            // For 64-bit store we can actually set up the aliasing information.
-            AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true);
-            AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false,
-                                    true);
-          } else {
-            // Set barrier for 128-bit store.
-            st1->u.m.def_mask = &kEncodeAll;
-          }
-        }
-
-        // Free the temporary used for the data movement.
-        FreeTemp(temp);
-      } else {
-        // Moving 32-bits via general purpose register.
-        bytes_to_move = sizeof(uint32_t);
-
-        // Instead of allocating a new temp, simply reuse one of the registers being used
-        // for argument passing.
-        RegStorage temp = TargetReg(kArg3, kNotWide);
-
-        // Now load the argument VR and store to the outs.
-        Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
-        Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
-      }
-
-      current_src_offset += bytes_to_move;
-      current_dest_offset += bytes_to_move;
-      regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
-    }
-  } else {
-    // Generate memcpy
     OpRegRegImm(kOpAdd, TargetReg(kArg0, kRef), TargetPtrReg(kSp), outs_offset);
     OpRegRegImm(kOpAdd, TargetReg(kArg1, kRef), TargetPtrReg(kSp), start_offset);
     CallRuntimeHelperRegRegImm(kQuickMemcpy, TargetReg(kArg0, kRef), TargetReg(kArg1, kRef),
-                               (info->num_arg_words - 3) * 4, false);
+                               count * 4, false);
+    count = 0;
+  }
+  return count;
+}
+
+int Mir2Lir::GenDalvikArgs(CallInfo* info, int call_state,
+                           LIR** pcrLabel, NextCallInsn next_call_insn,
+                           const MethodReference& target_method,
+                           uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
+                           InvokeType type, bool skip_this) {
+  // If no arguments, just return.
+  if (info->num_arg_words == 0)
+    return call_state;
+
+  const int start_index = skip_this ? 1 : 0;
+
+  // Get architecture dependent mapping between output VRs and physical registers
+  // basing on shorty of method to call.
+  InToRegStorageMapping in_to_reg_storage_mapping(arena_);
+  {
+    const char* target_shorty = mir_graph_->GetShortyFromMethodReference(target_method);
+    ShortyIterator shorty_iterator(target_shorty, type == kStatic);
+    in_to_reg_storage_mapping.Initialize(&shorty_iterator, GetResetedInToRegStorageMapper());
   }
 
-  call_state = LoadArgRegs(info, call_state, next_call_insn,
-                           target_method, vtable_idx, direct_code, direct_method,
-                           type, skip_this);
+  int stack_map_start = std::max(in_to_reg_storage_mapping.GetMaxMappedIn() + 1, start_index);
+  if ((stack_map_start < info->num_arg_words) && info->args[stack_map_start].high_word) {
+    // It is possible that the last mapped reg is 32 bit while arg is 64-bit.
+    // It will be handled together with low part mapped to register.
+    stack_map_start++;
+  }
+  int regs_left_to_pass_via_stack = info->num_arg_words - stack_map_start;
+
+  // If it is a range case we can try to copy remaining VRs (not mapped to physical registers)
+  // using more optimal algorithm.
+  if (info->is_range && regs_left_to_pass_via_stack > 1) {
+    regs_left_to_pass_via_stack = GenDalvikArgsBulkCopy(info, stack_map_start,
+                                                        regs_left_to_pass_via_stack);
+  }
+
+  // Now handle any remaining VRs mapped to stack.
+  if (in_to_reg_storage_mapping.HasArgumentsOnStack()) {
+    // Two temps but do not use kArg1, it might be this which we can skip.
+    // Separate single and wide - it can give some advantage.
+    RegStorage regRef = TargetReg(kArg3, kRef);
+    RegStorage regSingle = TargetReg(kArg3, kNotWide);
+    RegStorage regWide = TargetReg(kArg2, kWide);
+    for (int i = start_index;
+         i < stack_map_start + regs_left_to_pass_via_stack; i++) {
+      RegLocation rl_arg = info->args[i];
+      rl_arg = UpdateRawLoc(rl_arg);
+      RegStorage reg = in_to_reg_storage_mapping.Get(i);
+      if (!reg.Valid()) {
+        int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+        {
+          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+          if (rl_arg.wide) {
+            if (rl_arg.location == kLocPhysReg) {
+              StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
+            } else {
+              LoadValueDirectWideFixed(rl_arg, regWide);
+              StoreBaseDisp(TargetPtrReg(kSp), out_offset, regWide, k64, kNotVolatile);
+            }
+          } else {
+            if (rl_arg.location == kLocPhysReg) {
+              if (rl_arg.ref) {
+                StoreRefDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, kNotVolatile);
+              } else {
+                StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k32, kNotVolatile);
+              }
+            } else {
+              if (rl_arg.ref) {
+                LoadValueDirectFixed(rl_arg, regRef);
+                StoreRefDisp(TargetPtrReg(kSp), out_offset, regRef, kNotVolatile);
+              } else {
+                LoadValueDirectFixed(rl_arg, regSingle);
+                StoreBaseDisp(TargetPtrReg(kSp), out_offset, regSingle, k32, kNotVolatile);
+              }
+            }
+          }
+        }
+        call_state = next_call_insn(cu_, info, call_state, target_method,
+                                    vtable_idx, direct_code, direct_method, type);
+      }
+      if (rl_arg.wide) {
+        i++;
+      }
+    }
+  }
+
+  // Finish with VRs mapped to physical registers.
+  for (int i = start_index; i < stack_map_start; i++) {
+    RegLocation rl_arg = info->args[i];
+    rl_arg = UpdateRawLoc(rl_arg);
+    RegStorage reg = in_to_reg_storage_mapping.Get(i);
+    if (reg.Valid()) {
+      if (rl_arg.wide) {
+        // if reg is not 64-bit (it is half of 64-bit) then handle it separately.
+        if (!reg.Is64Bit()) {
+          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+          if (rl_arg.location == kLocPhysReg) {
+            int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+            // Dump it to memory.
+            StoreBaseDisp(TargetPtrReg(kSp), out_offset, rl_arg.reg, k64, kNotVolatile);
+            LoadBaseDisp(TargetPtrReg(kSp), out_offset, reg, k32, kNotVolatile);
+          } else {
+            int high_offset = StackVisitor::GetOutVROffset(i + 1, cu_->instruction_set);
+            // First, use target reg for high part.
+            LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low + 1), reg, k32,
+                         kNotVolatile);
+            StoreBaseDisp(TargetPtrReg(kSp), high_offset, reg, k32, kNotVolatile);
+            // Now, use target reg for low part.
+            LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_arg.s_reg_low), reg, k32, kNotVolatile);
+            int low_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
+            // And store it to the expected memory location.
+            StoreBaseDisp(TargetPtrReg(kSp), low_offset, reg, k32, kNotVolatile);
+          }
+        } else {
+          LoadValueDirectWideFixed(rl_arg, reg);
+        }
+      } else {
+        LoadValueDirectFixed(rl_arg, reg);
+      }
+      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+                               direct_code, direct_method, type);
+    }
+    if (rl_arg.wide) {
+      i++;
+    }
+  }
 
   call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
                            direct_code, direct_method, type);
@@ -1094,18 +846,20 @@
       *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
     } else {
       *pcrLabel = nullptr;
-      if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
-          (info->opt_flags & MIR_IGNORE_NULL_CHECK)) {
-        return call_state;
-      }
-      // In lieu of generating a check for kArg1 being null, we need to
-      // perform a load when doing implicit checks.
       GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
     }
   }
   return call_state;
 }
 
+RegStorage Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
+  if (!in_to_reg_storage_mapping_.IsInitialized()) {
+    ShortyIterator shorty_iterator(cu_->shorty, cu_->invoke_type == kStatic);
+    in_to_reg_storage_mapping_.Initialize(&shorty_iterator, GetResetedInToRegStorageMapper());
+  }
+  return in_to_reg_storage_mapping_.Get(arg_num);
+}
+
 RegLocation Mir2Lir::InlineTarget(CallInfo* info) {
   RegLocation res;
   if (info->result.location == kLocInvalid) {
@@ -1167,8 +921,8 @@
 
   RegStorage reg_slow_path = AllocTemp();
   RegStorage reg_disabled = AllocTemp();
-  Load8Disp(reg_class, slow_path_flag_offset, reg_slow_path);
-  Load8Disp(reg_class, disable_flag_offset, reg_disabled);
+  LoadBaseDisp(reg_class, slow_path_flag_offset, reg_slow_path, kSignedByte, kNotVolatile);
+  LoadBaseDisp(reg_class, disable_flag_offset, reg_disabled, kSignedByte, kNotVolatile);
   FreeTemp(reg_class);
   LIR* or_inst = OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled);
   FreeTemp(reg_disabled);
@@ -1200,10 +954,6 @@
 }
 
 bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
-  if (cu_->instruction_set == kMips) {
-    // TODO - add Mips implementation
-    return false;
-  }
   // Location of reference to data array
   int value_offset = mirror::String::ValueOffset().Int32Value();
   // Location of count
@@ -1301,9 +1051,13 @@
     // TODO - add Mips implementation.
     return false;
   }
+  RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
   RegLocation rl_src_i = info->args[0];
   RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
-  RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info);  // result reg
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   if (IsWide(size)) {
     if (cu_->instruction_set == kArm64 || cu_->instruction_set == kX86_64) {
@@ -1333,13 +1087,13 @@
 }
 
 bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
-  if (cu_->instruction_set == kMips) {
-    // TODO - add Mips implementation
-    return false;
+  RegLocation rl_dest = InlineTarget(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
   }
   RegLocation rl_src = info->args[0];
   rl_src = LoadValue(rl_src, kCoreReg);
-  RegLocation rl_dest = InlineTarget(info);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   RegStorage sign_reg = AllocTemp();
   // abs(x) = y<=x>>31, (x+y)^y.
@@ -1351,13 +1105,13 @@
 }
 
 bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
-  if (cu_->instruction_set == kMips) {
-    // TODO - add Mips implementation
-    return false;
+  RegLocation rl_dest = InlineTargetWide(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
   }
   RegLocation rl_src = info->args[0];
   rl_src = LoadValueWide(rl_src, kCoreReg);
-  RegLocation rl_dest = InlineTargetWide(info);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
 
   // If on x86 or if we would clobber a register needed later, just copy the source first.
@@ -1432,8 +1186,12 @@
     // TODO - add Mips implementation
     return false;
   }
-  RegLocation rl_src = info->args[0];
   RegLocation rl_dest = InlineTarget(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
+  RegLocation rl_src = info->args[0];
   StoreValue(rl_dest, rl_src);
   return true;
 }
@@ -1443,8 +1201,12 @@
     // TODO - add Mips implementation
     return false;
   }
-  RegLocation rl_src = info->args[0];
   RegLocation rl_dest = InlineTargetWide(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
+  RegLocation rl_src = info->args[0];
   StoreValueWide(rl_dest, rl_src);
   return true;
 }
@@ -1460,14 +1222,6 @@
  * otherwise bails to standard library code.
  */
 bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
-  if (cu_->instruction_set == kMips) {
-    // TODO - add Mips implementation
-    return false;
-  }
-  if (cu_->instruction_set == kX86_64) {
-    // TODO - add kX86_64 implementation
-    return false;
-  }
   RegLocation rl_obj = info->args[0];
   RegLocation rl_char = info->args[1];
   if (rl_char.is_const && (mir_graph_->ConstantValue(rl_char) & ~0xFFFF) != 0) {
@@ -1556,23 +1310,13 @@
 
   RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
 
-  switch (cu_->instruction_set) {
-    case kArm:
-      // Fall-through.
-    case kThumb2:
-      // Fall-through.
-    case kMips:
-      Load32Disp(TargetPtrReg(kSelf), Thread::PeerOffset<4>().Int32Value(), rl_result.reg);
-      break;
-
-    case kArm64:
-      LoadRefDisp(TargetPtrReg(kSelf), Thread::PeerOffset<8>().Int32Value(), rl_result.reg,
-                  kNotVolatile);
-      break;
-
-    default:
-      LOG(FATAL) << "Unexpected isa " << cu_->instruction_set;
+  if (Is64BitInstructionSet(cu_->instruction_set)) {
+    LoadRefDisp(TargetPtrReg(kSelf), Thread::PeerOffset<8>().Int32Value(), rl_result.reg,
+                kNotVolatile);
+  } else {
+    Load32Disp(TargetPtrReg(kSelf), Thread::PeerOffset<4>().Int32Value(), rl_result.reg);
   }
+
   StoreValue(rl_dest, rl_result);
   return true;
 }
@@ -1719,17 +1463,10 @@
     skip_this = fast_path;
   }
   MethodReference target_method = method_info.GetTargetMethod();
-  if (!info->is_range) {
-    call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck,
-                                      next_call_insn, target_method, method_info.VTableIndex(),
-                                      method_info.DirectCode(), method_info.DirectMethod(),
-                                      original_type, skip_this);
-  } else {
-    call_state = GenDalvikArgsRange(info, call_state, p_null_ck,
-                                    next_call_insn, target_method, method_info.VTableIndex(),
-                                    method_info.DirectCode(), method_info.DirectMethod(),
-                                    original_type, skip_this);
-  }
+  call_state = GenDalvikArgs(info, call_state, p_null_ck,
+                             next_call_insn, target_method, method_info.VTableIndex(),
+                             method_info.DirectCode(), method_info.DirectMethod(),
+                             original_type, skip_this);
   // Finish up any of the call sequence not interleaved in arg loading
   while (call_state >= 0) {
     call_state = next_call_insn(cu_, info, call_state, target_method, method_info.VTableIndex(),
@@ -1738,7 +1475,7 @@
   LIR* call_insn = GenCallInsn(method_info);
   MarkSafepointPC(call_insn);
 
-  ClobberCallerSave();
+  FreeCallTemps();
   if (info->result.location != kLocInvalid) {
     // We have a following MOVE_RESULT - do it now.
     if (info->result.wide) {
@@ -1751,16 +1488,4 @@
   }
 }
 
-NextCallInsn Mir2Lir::GetNextSDCallInsn() {
-  return NextSDCallInsn;
-}
-
-LIR* Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
-  UNUSED(method_info);
-  DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64 &&
-         cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm &&
-         cu_->instruction_set != kArm64);
-  return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
-}
-
 }  // namespace art
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
index d314601..9f36e35 100644
--- a/compiler/dex/quick/gen_loadstore.cc
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
+#include "mir_to_lir-inl.h"
+
 #include "dex/compiler_ir.h"
-#include "dex/compiler_internals.h"
-#include "dex/quick/mir_to_lir-inl.h"
+#include "dex/mir_graph.h"
 #include "invoke_type.h"
 
 namespace art {
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index e0f4691..e573899 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir-inl.h"
 
+#include "base/logging.h"
+
 namespace art {
 
 #define DEBUG_OPT(X)
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
index 0d1d9bf..5c98b10 100644
--- a/compiler/dex/quick/mips/assemble_mips.cc
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -16,6 +16,8 @@
 
 #include "codegen_mips.h"
 
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "mips_lir.h"
 
@@ -434,7 +436,7 @@
  * anchor:
  *      ori  rAT, rAT, ((target-anchor) & 0xffff)
  *      addu rAT, rAT, rRA
- *      jr   rAT
+ *      jalr rZERO, rAT
  * hop:
  *
  * Orig unconditional branch
@@ -448,7 +450,7 @@
  * anchor:
  *      ori  rAT, rAT, ((target-anchor) & 0xffff)
  *      addu rAT, rAT, rRA
- *      jr   rAT
+ *      jalr rZERO, rAT
  *
  *
  * NOTE: An out-of-range bal isn't supported because it should
@@ -482,7 +484,7 @@
   if (!unconditional) {
     hop_target = RawLIR(dalvik_offset, kPseudoTargetLabel);
     LIR* hop_branch = RawLIR(dalvik_offset, opcode, lir->operands[0],
-                            lir->operands[1], 0, 0, 0, hop_target);
+                             lir->operands[1], 0, 0, 0, hop_target);
     InsertLIRBefore(lir, hop_branch);
   }
   LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC);
@@ -497,8 +499,8 @@
   InsertLIRBefore(lir, delta_lo);
   LIR* addu = RawLIR(dalvik_offset, kMipsAddu, rAT, rAT, rRA);
   InsertLIRBefore(lir, addu);
-  LIR* jr = RawLIR(dalvik_offset, kMipsJr, rAT);
-  InsertLIRBefore(lir, jr);
+  LIR* jalr = RawLIR(dalvik_offset, kMipsJalr, rZERO, rAT);
+  InsertLIRBefore(lir, jalr);
   if (!unconditional) {
     InsertLIRBefore(lir, hop_target);
   }
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 3bb81bf..0719b52 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -18,10 +18,14 @@
 
 #include "codegen_mips.h"
 
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
 #include "mips_lir.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
 
 namespace art {
 
@@ -58,23 +62,19 @@
  *   bne   r_val, r_key, loop
  *   lw    r_disp, -4(r_base)
  *   addu  rRA, r_disp
- *   jr    rRA
+ *   jalr  rZERO, rRA
  * done:
  *
  */
 void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
-  if (cu_->verbose) {
-    DumpSparseSwitchTable(table);
-  }
   // Add the table to the list - we'll process it later
   SwitchTable* tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int elements = table[1];
-  tab_rec->targets =
-      static_cast<LIR**>(arena_->Alloc(elements * sizeof(LIR*), kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // The table is composed of 8-byte key/disp pairs
@@ -136,22 +136,18 @@
  *   bound check -> done
  *   lw    r_disp, [rRA, r_val]
  *   addu  rRA, r_disp
- *   jr    rRA
+ *   jalr  rZERO, rRA
  * done:
  */
 void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
-  if (cu_->verbose) {
-    DumpPackedSwitchTable(table);
-  }
   // Add the table to the list - we'll process it later
   SwitchTable* tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
-                                                      kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // Get the switch value
@@ -319,4 +315,84 @@
   OpReg(kOpBx, rs_rRA);
 }
 
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED,
+                          int state, const MethodReference& target_method,
+                          uint32_t,
+                          uintptr_t direct_code, uintptr_t direct_method,
+                          InvokeType type) {
+  Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+  if (direct_code != 0 && direct_method != 0) {
+    switch (state) {
+    case 0:  // Get the current Method* [sets kArg0]
+      if (direct_code != static_cast<uintptr_t>(-1)) {
+        cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+      } else {
+        cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+      }
+      if (direct_method != static_cast<uintptr_t>(-1)) {
+        cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+      } else {
+        cg->LoadMethodAddress(target_method, type, kArg0);
+      }
+      break;
+    default:
+      return -1;
+    }
+  } else {
+    RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+    switch (state) {
+    case 0:  // Get the current Method* [sets kArg0]
+      // TUNING: we can save a reg copy if Method* has been promoted.
+      cg->LoadCurrMethodDirect(arg0_ref);
+      break;
+    case 1:  // Get method->dex_cache_resolved_methods_
+      cg->LoadRefDisp(arg0_ref,
+                      mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+                      arg0_ref,
+                      kNotVolatile);
+      // Set up direct code if known.
+      if (direct_code != 0) {
+        if (direct_code != static_cast<uintptr_t>(-1)) {
+          cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+        } else {
+          CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+          cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+        }
+      }
+      break;
+    case 2:  // Grab target method*
+      CHECK_EQ(cu->dex_file, target_method.dex_file);
+      cg->LoadRefDisp(arg0_ref,
+                      mirror::ObjectArray<mirror::Object>::
+                          OffsetOfElement(target_method.dex_method_index).Int32Value(),
+                      arg0_ref,
+                      kNotVolatile);
+      break;
+    case 3:  // Grab the code from the method*
+      if (direct_code == 0) {
+        int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+            InstructionSetPointerSize(cu->instruction_set)).Int32Value();
+        // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt]
+        cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt));
+      }
+      break;
+    default:
+      return -1;
+    }
+  }
+  return state + 1;
+}
+
+NextCallInsn MipsMir2Lir::GetNextSDCallInsn() {
+  return NextSDCallInsn;
+}
+
+LIR* MipsMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) {
+  return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index e08846c..a37fe40 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -17,13 +17,34 @@
 #ifndef ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_
 #define ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_
 
-#include "dex/compiler_internals.h"
 #include "dex/quick/mir_to_lir.h"
 #include "mips_lir.h"
 
 namespace art {
 
+struct CompilationUnit;
+
 class MipsMir2Lir FINAL : public Mir2Lir {
+ protected:
+  class InToRegStorageMipsMapper : public InToRegStorageMapper {
+   public:
+    explicit InToRegStorageMipsMapper(Mir2Lir* m2l) : m2l_(m2l), cur_core_reg_(0) {}
+    virtual RegStorage GetNextReg(ShortyArg arg);
+    virtual void Reset() OVERRIDE {
+      cur_core_reg_ = 0;
+    }
+   protected:
+    Mir2Lir* m2l_;
+   private:
+    size_t cur_core_reg_;
+  };
+
+  InToRegStorageMipsMapper in_to_reg_storage_mips_mapper_;
+  InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+    in_to_reg_storage_mips_mapper_.Reset();
+    return &in_to_reg_storage_mips_mapper_;
+  }
+
   public:
     MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
@@ -56,7 +77,6 @@
     // Required for target - register utilities.
     RegStorage Solo64ToPair64(RegStorage reg);
     RegStorage TargetReg(SpecialTargetRegister reg);
-    RegStorage GetArgMappingToPhysicalReg(int arg_num);
     RegLocation GetReturnAlt();
     RegLocation GetReturnWideAlt();
     RegLocation LocCReturn();
@@ -187,6 +207,29 @@
 
     LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
 
+    RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
+                          RegLocation rl_src2, bool is_div, int flags) OVERRIDE;
+    RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div)
+        OVERRIDE;
+
+    NextCallInsn GetNextSDCallInsn() OVERRIDE;
+    LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
+
+    // Unimplemented intrinsics.
+    bool GenInlinedCharAt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE {
+      return false;
+    }
+    bool GenInlinedAbsInt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE {
+      return false;
+    }
+    bool GenInlinedAbsLong(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE {
+      return false;
+    }
+    bool GenInlinedIndexOf(CallInfo* info ATTRIBUTE_UNUSED, bool zero_based ATTRIBUTE_UNUSED)
+        OVERRIDE {
+      return false;
+    }
+
   private:
     void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
     void GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
@@ -195,9 +238,6 @@
                     RegLocation rl_src2);
 
     void ConvertShortToLongBranch(LIR* lir);
-    RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
-                          RegLocation rl_src2, bool is_div, int flags) OVERRIDE;
-    RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc
index 495d85e..d7ed7ac 100644
--- a/compiler/dex/quick/mips/fp_mips.cc
+++ b/compiler/dex/quick/mips/fp_mips.cc
@@ -16,6 +16,7 @@
 
 #include "codegen_mips.h"
 
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mips_lir.h"
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 0778c3b..17ac629 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -18,6 +18,8 @@
 
 #include "codegen_mips.h"
 
+#include "base/logging.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -172,7 +174,7 @@
   if (r_dest.IsFloat() || r_src.IsFloat())
     return OpFpRegCopy(r_dest, r_src);
   LIR* res = RawLIR(current_dalvik_offset_, kMipsMove,
-            r_dest.GetReg(), r_src.GetReg());
+                    r_dest.GetReg(), r_src.GetReg());
   if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
     res->flags.is_nop = true;
   }
@@ -194,7 +196,7 @@
       if (src_fp) {
         OpRegCopy(r_dest, r_src);
       } else {
-         /* note the operands are swapped for the mtc1 instr */
+        /* note the operands are swapped for the mtc1 instr */
         NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg());
         NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg());
       }
@@ -240,7 +242,7 @@
 }
 
 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2,
-                                    bool is_div) {
+                                   bool is_div) {
   NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg());
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   if (is_div) {
@@ -252,7 +254,7 @@
 }
 
 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit,
-                                       bool is_div) {
+                                      bool is_div) {
   RegStorage t_reg = AllocTemp();
   NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit);
   NewLIR2(kMipsDiv, reg1.GetReg(), t_reg.GetReg());
@@ -501,7 +503,7 @@
  * Generate array load
  */
 void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
-                          RegLocation rl_index, RegLocation rl_dest, int scale) {
+                              RegLocation rl_index, RegLocation rl_dest, int scale) {
   RegisterClass reg_class = RegClassBySize(size);
   int len_offset = mirror::Array::LengthOffset().Int32Value();
   int data_offset;
@@ -570,7 +572,7 @@
  *
  */
 void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
-                          RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
+                              RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
   RegisterClass reg_class = RegClassBySize(size);
   int len_offset = mirror::Array::LengthOffset().Int32Value();
   int data_offset;
@@ -632,7 +634,7 @@
   } else {
     rl_src = LoadValue(rl_src, reg_class);
     if (needs_range_check) {
-       GenArrayBoundsCheck(rl_index.reg, reg_len);
+      GenArrayBoundsCheck(rl_index.reg, reg_len);
       FreeTemp(reg_len);
     }
     StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size);
diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h
index 3df8f2e..66e3894 100644
--- a/compiler/dex/quick/mips/mips_lir.h
+++ b/compiler/dex/quick/mips/mips_lir.h
@@ -17,7 +17,8 @@
 #ifndef ART_COMPILER_DEX_QUICK_MIPS_MIPS_LIR_H_
 #define ART_COMPILER_DEX_QUICK_MIPS_MIPS_LIR_H_
 
-#include "dex/compiler_internals.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
 
 namespace art {
 
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 185112d..8574ffd 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -22,8 +22,10 @@
 
 #include "arch/mips/instruction_set_features_mips.h"
 #include "backend_mips.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 #include "mips_lir.h"
 
 namespace art {
@@ -89,9 +91,9 @@
 
 // Convert k64BitSolo into k64BitPair
 RegStorage MipsMir2Lir::Solo64ToPair64(RegStorage reg) {
-    DCHECK(reg.IsDouble());
-    int reg_num = (reg.GetRegNum() & ~1) | RegStorage::kFloatingPoint;
-    return RegStorage(RegStorage::k64BitPair, reg_num, reg_num + 1);
+  DCHECK(reg.IsDouble());
+  int reg_num = (reg.GetRegNum() & ~1) | RegStorage::kFloatingPoint;
+  return RegStorage(RegStorage::k64BitPair, reg_num, reg_num + 1);
 }
 
 // Return a target-dependent special register.
@@ -122,18 +124,20 @@
   return res_reg;
 }
 
-RegStorage MipsMir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
-  // For the 32-bit internal ABI, the first 3 arguments are passed in registers.
-  switch (arg_num) {
-    case 0:
-      return rs_rMIPS_ARG1;
-    case 1:
-      return rs_rMIPS_ARG2;
-    case 2:
-      return rs_rMIPS_ARG3;
-    default:
-      return RegStorage::InvalidReg();
+RegStorage MipsMir2Lir::InToRegStorageMipsMapper::GetNextReg(ShortyArg arg) {
+  const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3};
+  const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
+
+  RegStorage result = RegStorage::InvalidReg();
+  if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+    result = m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
+                             arg.IsRef() ? kRef : kNotWide);
+    if (arg.IsWide() && cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+      result = RegStorage::MakeRegPair(
+          result, m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++], kNotWide));
+    }
   }
+  return result;
 }
 
 /*
@@ -141,7 +145,8 @@
  */
 ResourceMask MipsMir2Lir::GetRegMaskCommon(const RegStorage& reg) const {
   if (reg.IsDouble()) {
-    if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint()) {
+    if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+        ->Is32BitFloatingPoint()) {
       return ResourceMask::TwoBits((reg.GetRegNum() & ~1) + kMipsFPReg0);
     } else {
       return ResourceMask::TwoBits(reg.GetRegNum() * 2 + kMipsFPReg0);
@@ -221,78 +226,78 @@
       if (nc == '!') {
         strcpy(tbuf, "!");
       } else {
-         DCHECK_LT(fmt, fmt_end);
-         DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
-         operand = lir->operands[nc-'0'];
-         switch (*fmt++) {
-           case 'b':
-             strcpy(tbuf, "0000");
-             for (i = 3; i >= 0; i--) {
-               tbuf[i] += operand & 1;
-               operand >>= 1;
-             }
-             break;
-           case 's':
-             snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
-             break;
-           case 'S':
-             DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0);
-             snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
-             break;
-           case 'h':
-             snprintf(tbuf, arraysize(tbuf), "%04x", operand);
-             break;
-           case 'M':
-           case 'd':
-             snprintf(tbuf, arraysize(tbuf), "%d", operand);
-             break;
-           case 'D':
-             snprintf(tbuf, arraysize(tbuf), "%d", operand+1);
-             break;
-           case 'E':
-             snprintf(tbuf, arraysize(tbuf), "%d", operand*4);
-             break;
-           case 'F':
-             snprintf(tbuf, arraysize(tbuf), "%d", operand*2);
-             break;
-           case 't':
-             snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)",
-                 reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
-                 lir->target);
-             break;
-           case 'T':
-             snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2);
-             break;
-           case 'u': {
-             int offset_1 = lir->operands[0];
-             int offset_2 = NEXT_LIR(lir)->operands[0];
-             uintptr_t target =
-                 (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
-                 (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
-             snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target));
-             break;
+        DCHECK_LT(fmt, fmt_end);
+        DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
+        operand = lir->operands[nc-'0'];
+        switch (*fmt++) {
+          case 'b':
+            strcpy(tbuf, "0000");
+            for (i = 3; i >= 0; i--) {
+              tbuf[i] += operand & 1;
+              operand >>= 1;
+            }
+            break;
+          case 's':
+            snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
+            break;
+          case 'S':
+            DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0);
+            snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand));
+            break;
+          case 'h':
+            snprintf(tbuf, arraysize(tbuf), "%04x", operand);
+            break;
+          case 'M':
+          case 'd':
+            snprintf(tbuf, arraysize(tbuf), "%d", operand);
+            break;
+          case 'D':
+            snprintf(tbuf, arraysize(tbuf), "%d", operand+1);
+            break;
+          case 'E':
+            snprintf(tbuf, arraysize(tbuf), "%d", operand*4);
+            break;
+          case 'F':
+            snprintf(tbuf, arraysize(tbuf), "%d", operand*2);
+            break;
+          case 't':
+            snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)",
+                     reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
+                     lir->target);
+            break;
+          case 'T':
+            snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2);
+            break;
+          case 'u': {
+            int offset_1 = lir->operands[0];
+            int offset_2 = NEXT_LIR(lir)->operands[0];
+            uintptr_t target =
+                (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
+                    (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
+            snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target));
+            break;
           }
 
-           /* Nothing to print for BLX_2 */
-           case 'v':
-             strcpy(tbuf, "see above");
-             break;
-           case 'r':
-             DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
-             strcpy(tbuf, mips_reg_name[operand]);
-             break;
-           case 'N':
-             // Placeholder for delay slot handling
-             strcpy(tbuf, ";  nop");
-             break;
-           default:
-             strcpy(tbuf, "DecodeError");
-             break;
-         }
-         buf += tbuf;
+          /* Nothing to print for BLX_2 */
+          case 'v':
+            strcpy(tbuf, "see above");
+            break;
+          case 'r':
+            DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
+            strcpy(tbuf, mips_reg_name[operand]);
+            break;
+          case 'N':
+            // Placeholder for delay slot handling
+            strcpy(tbuf, ";  nop");
+            break;
+          default:
+            strcpy(tbuf, "DecodeError");
+            break;
+        }
+        buf += tbuf;
       }
     } else {
-       buf += *fmt++;
+      buf += *fmt++;
     }
   }
   return buf;
@@ -396,7 +401,8 @@
   Clobber(rs_rF13);
   Clobber(rs_rF14);
   Clobber(rs_rF15);
-  if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint()) {
+  if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+      ->Is32BitFloatingPoint()) {
     Clobber(rs_rD0_fr0);
     Clobber(rs_rD1_fr0);
     Clobber(rs_rD2_fr0);
@@ -443,10 +449,11 @@
   FreeTemp(rs_rMIPS_ARG1);
   FreeTemp(rs_rMIPS_ARG2);
   FreeTemp(rs_rMIPS_ARG3);
+  FreeTemp(TargetReg(kHiddenArg));
 }
 
 bool MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind ATTRIBUTE_UNUSED) {
-  if (cu_->GetInstructionSetFeatures()->IsSmp()) {
+  if (cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
     NewLIR1(kMipsSync, 0 /* Only stype currently supported */);
     return true;
   } else {
@@ -456,7 +463,8 @@
 
 void MipsMir2Lir::CompilerInitializeRegAlloc() {
   const bool fpu_is_32bit =
-      cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint();
+      cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+      ->Is32BitFloatingPoint();
   reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */,
                                             sp_regs,
                                             fpu_is_32bit ? dp_fr0_regs : dp_fr1_regs,
@@ -602,7 +610,7 @@
 }
 
 MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
-    : Mir2Lir(cu, mir_graph, arena) {
+    : Mir2Lir(cu, mir_graph, arena), in_to_reg_storage_mips_mapper_(this) {
   for (int i = 0; i < kMipsLast; i++) {
     DCHECK_EQ(MipsMir2Lir::EncodingMap[i].opcode, i)
         << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 18f1cde..6f6bf68 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -17,8 +17,10 @@
 #include "codegen_mips.h"
 
 #include "arch/mips/instruction_set_features_mips.h"
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
 #include "mips_lir.h"
 
 namespace art {
@@ -125,7 +127,7 @@
       opcode = kMipsJalr;
       break;
     case kOpBx:
-      return NewLIR1(kMipsJr, r_dest_src.GetReg());
+      return NewLIR2(kMipsJalr, rZERO, r_dest_src.GetReg());
       break;
     default:
       LOG(FATAL) << "Bad case in OpReg";
@@ -228,17 +230,17 @@
       }
       break;
     case kOpLsl:
-        DCHECK(value >= 0 && value <= 31);
-        opcode = kMipsSll;
-        break;
+      DCHECK(value >= 0 && value <= 31);
+      opcode = kMipsSll;
+      break;
     case kOpLsr:
-        DCHECK(value >= 0 && value <= 31);
-        opcode = kMipsSrl;
-        break;
+      DCHECK(value >= 0 && value <= 31);
+      opcode = kMipsSrl;
+      break;
     case kOpAsr:
-        DCHECK(value >= 0 && value <= 31);
-        opcode = kMipsSra;
-        break;
+      DCHECK(value >= 0 && value <= 31);
+      opcode = kMipsSra;
+      break;
     case kOpAnd:
       if (IS_UIMM16((value))) {
         opcode = kMipsAndi;
@@ -306,7 +308,7 @@
     case kOpXor:
       return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2);
     case kOp2Byte:
-      if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+      if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
           ->IsMipsIsaRevGreaterThanEqual2()) {
         res = NewLIR2(kMipsSeb, r_dest_src1.GetReg(), r_src2.GetReg());
       } else {
@@ -315,7 +317,7 @@
       }
       return res;
     case kOp2Short:
-      if (cu_->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
+      if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures()
           ->IsMipsIsaRevGreaterThanEqual2()) {
         res = NewLIR2(kMipsSeh, r_dest_src1.GetReg(), r_src2.GetReg());
       } else {
@@ -324,7 +326,7 @@
       }
       return res;
     case kOp2Char:
-       return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
+      return NewLIR3(kMipsAndi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF);
     default:
       LOG(FATAL) << "Bad case in OpRegReg";
       UNREACHABLE();
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 0aefc2d..280dbbe 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -19,7 +19,8 @@
 
 #include "mir_to_lir.h"
 
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
 
 namespace art {
 
@@ -276,6 +277,24 @@
   }
 }
 
+inline Mir2Lir::ShortyIterator::ShortyIterator(const char* shorty, bool is_static)
+    : cur_(shorty + 1), pending_this_(!is_static), initialized_(false) {
+  DCHECK(shorty != nullptr);
+  DCHECK_NE(*shorty, 0);
+}
+
+inline bool Mir2Lir::ShortyIterator::Next() {
+  if (!initialized_) {
+    initialized_ = true;
+  } else if (pending_this_) {
+    pending_this_ = false;
+  } else if (*cur_ != 0) {
+    cur_++;
+  }
+
+  return *cur_ != 0 || pending_this_;
+}
+
 }  // namespace art
 
 #endif  // ART_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 320c0f4..274e078 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include "dex/compiler_internals.h"
+#include "mir_to_lir-inl.h"
+
 #include "dex/dataflow_iterator-inl.h"
 #include "dex/quick/dex_file_method_inliner.h"
-#include "mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 #include "primitive.h"
 #include "thread-inl.h"
 
@@ -53,20 +54,14 @@
   return res;
 }
 
-void Mir2Lir::LockArg(int in_position, bool wide) {
-  RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
-  RegStorage reg_arg_high = wide ? GetArgMappingToPhysicalReg(in_position + 1) :
-      RegStorage::InvalidReg();
+void Mir2Lir::LockArg(int in_position, bool) {
+  RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
 
-  if (reg_arg_low.Valid()) {
-    LockTemp(reg_arg_low);
-  }
-  if (reg_arg_high.Valid() && reg_arg_low.NotExactlyEquals(reg_arg_high)) {
-    LockTemp(reg_arg_high);
+  if (reg_arg.Valid()) {
+    LockTemp(reg_arg);
   }
 }
 
-// TODO: simplify when 32-bit targets go hard-float.
 RegStorage Mir2Lir::LoadArg(int in_position, RegisterClass reg_class, bool wide) {
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
   int offset = StackVisitor::GetOutVROffset(in_position, cu_->instruction_set);
@@ -87,81 +82,38 @@
     offset += sizeof(uint64_t);
   }
 
-  if (cu_->target64) {
-    RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
-    if (!reg_arg.Valid()) {
-      RegStorage new_reg =
-          wide ?  AllocTypedTempWide(false, reg_class) : AllocTypedTemp(false, reg_class);
-      LoadBaseDisp(TargetPtrReg(kSp), offset, new_reg, wide ? k64 : k32, kNotVolatile);
-      return new_reg;
-    } else {
-      // Check if we need to copy the arg to a different reg_class.
-      if (!RegClassMatches(reg_class, reg_arg)) {
-        if (wide) {
-          RegStorage new_reg = AllocTypedTempWide(false, reg_class);
-          OpRegCopyWide(new_reg, reg_arg);
-          reg_arg = new_reg;
-        } else {
-          RegStorage new_reg = AllocTypedTemp(false, reg_class);
-          OpRegCopy(new_reg, reg_arg);
-          reg_arg = new_reg;
-        }
+  RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
+
+  // TODO: REVISIT: This adds a spill of low part while we could just copy it.
+  if (reg_arg.Valid() && wide && (reg_arg.GetWideKind() == kNotWide)) {
+    // For wide register we've got only half of it.
+    // Flush it to memory then.
+    StoreBaseDisp(TargetPtrReg(kSp), offset, reg_arg, k32, kNotVolatile);
+    reg_arg = RegStorage::InvalidReg();
+  }
+
+  if (!reg_arg.Valid()) {
+    reg_arg = wide ?  AllocTypedTempWide(false, reg_class) : AllocTypedTemp(false, reg_class);
+    LoadBaseDisp(TargetPtrReg(kSp), offset, reg_arg, wide ? k64 : k32, kNotVolatile);
+  } else {
+    // Check if we need to copy the arg to a different reg_class.
+    if (!RegClassMatches(reg_class, reg_arg)) {
+      if (wide) {
+        RegStorage new_reg = AllocTypedTempWide(false, reg_class);
+        OpRegCopyWide(new_reg, reg_arg);
+        reg_arg = new_reg;
+      } else {
+        RegStorage new_reg = AllocTypedTemp(false, reg_class);
+        OpRegCopy(new_reg, reg_arg);
+        reg_arg = new_reg;
       }
     }
-    return reg_arg;
-  }
-
-  RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
-  RegStorage reg_arg_high = wide ? GetArgMappingToPhysicalReg(in_position + 1) :
-      RegStorage::InvalidReg();
-
-  // If the VR is wide and there is no register for high part, we need to load it.
-  if (wide && !reg_arg_high.Valid()) {
-    // If the low part is not in a reg, we allocate a pair. Otherwise, we just load to high reg.
-    if (!reg_arg_low.Valid()) {
-      RegStorage new_regs = AllocTypedTempWide(false, reg_class);
-      LoadBaseDisp(TargetPtrReg(kSp), offset, new_regs, k64, kNotVolatile);
-      return new_regs;  // The reg_class is OK, we can return.
-    } else {
-      // Assume that no ABI allows splitting a wide fp reg between a narrow fp reg and memory,
-      // i.e. the low part is in a core reg. Load the second part in a core reg as well for now.
-      DCHECK(!reg_arg_low.IsFloat());
-      reg_arg_high = AllocTemp();
-      int offset_high = offset + sizeof(uint32_t);
-      Load32Disp(TargetPtrReg(kSp), offset_high, reg_arg_high);
-      // Continue below to check the reg_class.
-    }
-  }
-
-  // If the low part is not in a register yet, we need to load it.
-  if (!reg_arg_low.Valid()) {
-    // Assume that if the low part of a wide arg is passed in memory, so is the high part,
-    // thus we don't get here for wide args as it's handled above. Big-endian ABIs could
-    // conceivably break this assumption but Android supports only little-endian architectures.
-    DCHECK(!wide);
-    reg_arg_low = AllocTypedTemp(false, reg_class);
-    Load32Disp(TargetPtrReg(kSp), offset, reg_arg_low);
-    return reg_arg_low;  // The reg_class is OK, we can return.
-  }
-
-  RegStorage reg_arg = wide ? RegStorage::MakeRegPair(reg_arg_low, reg_arg_high) : reg_arg_low;
-  // Check if we need to copy the arg to a different reg_class.
-  if (!RegClassMatches(reg_class, reg_arg)) {
-    if (wide) {
-      RegStorage new_regs = AllocTypedTempWide(false, reg_class);
-      OpRegCopyWide(new_regs, reg_arg);
-      reg_arg = new_regs;
-    } else {
-      RegStorage new_reg = AllocTypedTemp(false, reg_class);
-      OpRegCopy(new_reg, reg_arg);
-      reg_arg = new_reg;
-    }
   }
   return reg_arg;
 }
 
-// TODO: simpilfy when 32-bit targets go hard float.
 void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) {
+  DCHECK_EQ(rl_dest.location, kLocPhysReg);
   ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
   int offset = StackVisitor::GetOutVROffset(in_position, cu_->instruction_set);
   if (cu_->instruction_set == kX86) {
@@ -180,48 +132,23 @@
     offset += sizeof(uint64_t);
   }
 
-  if (!rl_dest.wide) {
-    RegStorage reg = GetArgMappingToPhysicalReg(in_position);
-    if (reg.Valid()) {
-      OpRegCopy(rl_dest.reg, reg);
-    } else {
-      Load32Disp(TargetPtrReg(kSp), offset, rl_dest.reg);
-    }
+  RegStorage reg_arg = GetArgMappingToPhysicalReg(in_position);
+
+  // TODO: REVISIT: This adds a spill of low part while we could just copy it.
+  if (reg_arg.Valid() && rl_dest.wide && (reg_arg.GetWideKind() == kNotWide)) {
+    // For wide register we've got only half of it.
+    // Flush it to memory then.
+    StoreBaseDisp(TargetPtrReg(kSp), offset, reg_arg, k32, kNotVolatile);
+    reg_arg = RegStorage::InvalidReg();
+  }
+
+  if (!reg_arg.Valid()) {
+    LoadBaseDisp(TargetPtrReg(kSp), offset, rl_dest.reg, rl_dest.wide ? k64 : k32, kNotVolatile);
   } else {
-    if (cu_->target64) {
-      RegStorage reg = GetArgMappingToPhysicalReg(in_position);
-      if (reg.Valid()) {
-        OpRegCopy(rl_dest.reg, reg);
-      } else {
-        LoadBaseDisp(TargetPtrReg(kSp), offset, rl_dest.reg, k64, kNotVolatile);
-      }
-      return;
-    }
-
-    RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
-    RegStorage reg_arg_high = GetArgMappingToPhysicalReg(in_position + 1);
-
-    if (cu_->instruction_set == kX86) {
-      // Can't handle double split between reg & memory.  Flush reg half to memory.
-      if (rl_dest.reg.IsDouble() && (reg_arg_low.Valid() != reg_arg_high.Valid())) {
-        DCHECK(reg_arg_low.Valid());
-        DCHECK(!reg_arg_high.Valid());
-        Store32Disp(TargetPtrReg(kSp), offset, reg_arg_low);
-        reg_arg_low = RegStorage::InvalidReg();
-      }
-    }
-
-    if (reg_arg_low.Valid() && reg_arg_high.Valid()) {
-      OpRegCopyWide(rl_dest.reg, RegStorage::MakeRegPair(reg_arg_low, reg_arg_high));
-    } else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) {
-      OpRegCopy(rl_dest.reg, reg_arg_low);
-      int offset_high = offset + sizeof(uint32_t);
-      Load32Disp(TargetPtrReg(kSp), offset_high, rl_dest.reg.GetHigh());
-    } else if (!reg_arg_low.Valid() && reg_arg_high.Valid()) {
-      OpRegCopy(rl_dest.reg.GetHigh(), reg_arg_high);
-      Load32Disp(TargetPtrReg(kSp), offset, rl_dest.reg.GetLow());
+    if (rl_dest.wide) {
+      OpRegCopyWide(rl_dest.reg, reg_arg);
     } else {
-      LoadBaseDisp(TargetPtrReg(kSp), offset, rl_dest.reg, k64, kNotVolatile);
+      OpRegCopy(rl_dest.reg, reg_arg);
     }
   }
 }
@@ -623,8 +550,7 @@
     case Instruction::GOTO:
     case Instruction::GOTO_16:
     case Instruction::GOTO_32:
-      if (mir_graph_->IsBackedge(bb, bb->taken) &&
-          (kLeafOptimization || !mir_graph_->HasSuspendTestBetween(bb, bb->taken))) {
+      if (mir_graph_->IsBackEdge(bb, bb->taken)) {
         GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken]);
       } else {
         OpUnconditionalBranch(&label_list[bb->taken]);
@@ -656,12 +582,10 @@
     case Instruction::IF_GE:
     case Instruction::IF_GT:
     case Instruction::IF_LE: {
-      LIR* taken = &label_list[bb->taken];
-      if (mir_graph_->IsBackwardsBranch(bb) &&
-          (kLeafOptimization || !mir_graph_->HasSuspendTestBetween(bb, bb->taken) ||
-           !mir_graph_->HasSuspendTestBetween(bb, bb->fall_through))) {
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
         GenSuspendTest(opt_flags);
       }
+      LIR* taken = &label_list[bb->taken];
       GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken);
       break;
     }
@@ -671,24 +595,22 @@
     case Instruction::IF_GEZ:
     case Instruction::IF_GTZ:
     case Instruction::IF_LEZ: {
-      LIR* taken = &label_list[bb->taken];
-      if (mir_graph_->IsBackwardsBranch(bb) &&
-          (kLeafOptimization || !mir_graph_->HasSuspendTestBetween(bb, bb->taken) ||
-           !mir_graph_->HasSuspendTestBetween(bb, bb->fall_through))) {
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
         GenSuspendTest(opt_flags);
       }
+      LIR* taken = &label_list[bb->taken];
       GenCompareZeroAndBranch(opcode, rl_src[0], taken);
       break;
     }
 
     case Instruction::AGET_WIDE:
-      GenArrayGet(opt_flags, k64, rl_src[0], rl_src[1], rl_dest, 3);
+      GenArrayGet(opt_flags, rl_dest.fp ? kDouble : k64, rl_src[0], rl_src[1], rl_dest, 3);
       break;
     case Instruction::AGET_OBJECT:
       GenArrayGet(opt_flags, kReference, rl_src[0], rl_src[1], rl_dest, 2);
       break;
     case Instruction::AGET:
-      GenArrayGet(opt_flags, k32, rl_src[0], rl_src[1], rl_dest, 2);
+      GenArrayGet(opt_flags, rl_dest.fp ? kSingle : k32, rl_src[0], rl_src[1], rl_dest, 2);
       break;
     case Instruction::AGET_BOOLEAN:
       GenArrayGet(opt_flags, kUnsignedByte, rl_src[0], rl_src[1], rl_dest, 0);
@@ -703,10 +625,10 @@
       GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
       break;
     case Instruction::APUT_WIDE:
-      GenArrayPut(opt_flags, k64, rl_src[1], rl_src[2], rl_src[0], 3, false);
+      GenArrayPut(opt_flags, rl_src[0].fp ? kDouble : k64, rl_src[1], rl_src[2], rl_src[0], 3, false);
       break;
     case Instruction::APUT:
-      GenArrayPut(opt_flags, k32, rl_src[1], rl_src[2], rl_src[0], 2, false);
+      GenArrayPut(opt_flags, rl_src[0].fp ? kSingle : k32, rl_src[1], rl_src[2], rl_src[0], 2, false);
       break;
     case Instruction::APUT_OBJECT: {
       bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]);
@@ -740,11 +662,19 @@
 
     case Instruction::IGET_WIDE:
       // kPrimLong and kPrimDouble share the same entrypoints.
-      GenIGet(mir, opt_flags, k64, Primitive::kPrimLong, rl_dest, rl_src[0]);
+      if (rl_dest.fp) {
+        GenIGet(mir, opt_flags, kDouble, Primitive::kPrimDouble, rl_dest, rl_src[0]);
+      } else {
+        GenIGet(mir, opt_flags, k64, Primitive::kPrimLong, rl_dest, rl_src[0]);
+      }
       break;
 
     case Instruction::IGET:
-      GenIGet(mir, opt_flags, k32, Primitive::kPrimInt, rl_dest, rl_src[0]);
+      if (rl_dest.fp) {
+        GenIGet(mir, opt_flags, kSingle, Primitive::kPrimFloat, rl_dest, rl_src[0]);
+      } else {
+        GenIGet(mir, opt_flags, k32, Primitive::kPrimInt, rl_dest, rl_src[0]);
+      }
       break;
 
     case Instruction::IGET_CHAR:
@@ -764,7 +694,7 @@
       break;
 
     case Instruction::IPUT_WIDE:
-      GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1]);
+      GenIPut(mir, opt_flags, rl_src[0].fp ? kDouble : k64, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::IPUT_OBJECT:
@@ -772,7 +702,7 @@
       break;
 
     case Instruction::IPUT:
-      GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1]);
+      GenIPut(mir, opt_flags, rl_src[0].fp ? kSingle : k32, rl_src[0], rl_src[1]);
       break;
 
     case Instruction::IPUT_BYTE:
@@ -793,7 +723,7 @@
       break;
 
     case Instruction::SGET:
-      GenSget(mir, rl_dest, k32, Primitive::kPrimInt);
+      GenSget(mir, rl_dest, rl_dest.fp ? kSingle : k32, Primitive::kPrimInt);
       break;
 
     case Instruction::SGET_CHAR:
@@ -814,7 +744,7 @@
 
     case Instruction::SGET_WIDE:
       // kPrimLong and kPrimDouble share the same entrypoints.
-      GenSget(mir, rl_dest, k64, Primitive::kPrimLong);
+      GenSget(mir, rl_dest, rl_dest.fp ? kDouble : k64, Primitive::kPrimDouble);
       break;
 
     case Instruction::SPUT_OBJECT:
@@ -822,7 +752,7 @@
       break;
 
     case Instruction::SPUT:
-      GenSput(mir, rl_src[0], k32);
+      GenSput(mir, rl_src[0], rl_src[0].fp ? kSingle : k32);
       break;
 
     case Instruction::SPUT_BYTE:
@@ -840,74 +770,42 @@
 
 
     case Instruction::SPUT_WIDE:
-      GenSput(mir, rl_src[0], k64);
+      GenSput(mir, rl_src[0], rl_src[0].fp ? kDouble : k64);
       break;
 
     case Instruction::INVOKE_STATIC_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, true));
-      if (!kLeafOptimization) {
-        // If the invocation is not inlined, we can assume there is already a
-        // suspend check at the return site
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
     case Instruction::INVOKE_STATIC:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, false));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
 
     case Instruction::INVOKE_DIRECT:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, false));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
     case Instruction::INVOKE_DIRECT_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, true));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
 
     case Instruction::INVOKE_VIRTUAL:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, false));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
     case Instruction::INVOKE_VIRTUAL_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, true));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
 
     case Instruction::INVOKE_SUPER:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, false));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
     case Instruction::INVOKE_SUPER_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, true));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
 
     case Instruction::INVOKE_INTERFACE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, false));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
     case Instruction::INVOKE_INTERFACE_RANGE:
       GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, true));
-      if (!kLeafOptimization) {
-        mir_graph_->AppendGenSuspendTestList(bb);
-      }
       break;
 
     case Instruction::NEG_INT:
@@ -1108,18 +1006,33 @@
       break;
     }
     case kMirOpFusedCmplFloat:
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+        GenSuspendTest(mir->optimization_flags);
+      }
       GenFusedFPCmpBranch(bb, mir, false /*gt bias*/, false /*double*/);
       break;
     case kMirOpFusedCmpgFloat:
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+        GenSuspendTest(mir->optimization_flags);
+      }
       GenFusedFPCmpBranch(bb, mir, true /*gt bias*/, false /*double*/);
       break;
     case kMirOpFusedCmplDouble:
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+        GenSuspendTest(mir->optimization_flags);
+      }
       GenFusedFPCmpBranch(bb, mir, false /*gt bias*/, true /*double*/);
       break;
     case kMirOpFusedCmpgDouble:
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+        GenSuspendTest(mir->optimization_flags);
+      }
       GenFusedFPCmpBranch(bb, mir, true /*gt bias*/, true /*double*/);
       break;
     case kMirOpFusedCmpLong:
+      if (mir_graph_->IsBackEdge(bb, bb->taken) || mir_graph_->IsBackEdge(bb, bb->fall_through)) {
+        GenSuspendTest(mir->optimization_flags);
+      }
       GenFusedLongCmpBranch(bb, mir);
       break;
     case kMirOpSelect:
@@ -1372,4 +1285,35 @@
   UNREACHABLE();
 }
 
+void Mir2Lir::InToRegStorageMapping::Initialize(ShortyIterator* shorty,
+                                                InToRegStorageMapper* mapper) {
+  DCHECK(mapper != nullptr);
+  DCHECK(shorty != nullptr);
+  max_mapped_in_ = -1;
+  has_arguments_on_stack_ = false;
+  while (shorty->Next()) {
+     ShortyArg arg = shorty->GetArg();
+     RegStorage reg = mapper->GetNextReg(arg);
+     if (reg.Valid()) {
+       mapping_.Put(count_, reg);
+       max_mapped_in_ = count_;
+       // If the VR is wide and was mapped as wide then account for it.
+       if (arg.IsWide() && reg.Is64Bit()) {
+         max_mapped_in_++;
+       }
+     } else {
+       has_arguments_on_stack_ = true;
+     }
+     count_ += arg.IsWide() ? 2 : 1;
+  }
+  initialized_ = true;
+}
+
+RegStorage Mir2Lir::InToRegStorageMapping::Get(int in_position) {
+  DCHECK(IsInitialized());
+  DCHECK_LT(in_position, count_);
+  auto res = mapping_.find(in_position);
+  return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 5d78a6e..9f1a497 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -17,15 +17,13 @@
 #ifndef ART_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
 #define ART_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
 
-#include "arch/instruction_set.h"
 #include "compiled_method.h"
 #include "dex/compiler_enums.h"
-#include "dex/compiler_ir.h"
+#include "dex/dex_flags.h"
+#include "dex/dex_types.h"
 #include "dex/reg_location.h"
 #include "dex/reg_storage.h"
-#include "dex/backend.h"
 #include "dex/quick/resource_mask.h"
-#include "driver/compiler_driver.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "invoke_type.h"
 #include "leb128.h"
@@ -125,10 +123,12 @@
 #define REG_USE23            (REG_USE2 | REG_USE3)
 #define REG_USE123           (REG_USE1 | REG_USE2 | REG_USE3)
 
-// TODO: #includes need a cleanup
-#ifndef INVALID_SREG
-#define INVALID_SREG (-1)
-#endif
+/*
+ * Assembly is an iterative process, and usually terminates within
+ * two or three passes.  This should be high enough to handle bizarre
+ * cases, but detect an infinite loop bug.
+ */
+#define MAX_ASSEMBLER_RETRIES 50
 
 class BasicBlock;
 struct CallInfo;
@@ -140,7 +140,6 @@
 class DexFileMethodInliner;
 class MIRGraph;
 class MirMethodLoweringInfo;
-class Mir2Lir;
 
 typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
                             const MethodReference& target_method,
@@ -148,6 +147,7 @@
                             uintptr_t direct_method, InvokeType type);
 
 typedef std::vector<uint8_t> CodeBuffer;
+typedef uint32_t CodeOffset;           // Native code offset in bytes.
 
 struct UseDefMasks {
   const ResourceMask* use_mask;        // Resource mask for use.
@@ -200,14 +200,7 @@
 // Mask to denote sreg as the start of a 64-bit item.  Must not interfere with low 16 bits.
 #define STARTING_WIDE_SREG 0x10000
 
-// TODO: replace these macros
-#define SLOW_FIELD_PATH (cu_->enable_debug & (1 << kDebugSlowFieldPath))
-#define SLOW_INVOKE_PATH (cu_->enable_debug & (1 << kDebugSlowInvokePath))
-#define SLOW_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowStringPath))
-#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
-#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
-
-class Mir2Lir : public Backend {
+class Mir2Lir {
   public:
     static constexpr bool kFailOnSizeError = true && kIsDebugBuild;
     static constexpr bool kReportSizeError = true && kIsDebugBuild;
@@ -231,7 +224,7 @@
 
     struct SwitchTable : EmbeddedData {
       LIR* anchor;                // Reference instruction for relative offsets.
-      LIR** targets;              // Array of case targets.
+      MIR* switch_mir;            // The switch mir.
     };
 
     /* Static register use counts */
@@ -660,7 +653,6 @@
     LIR* ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx);
     LIR* AddWordData(LIR* *constant_list_p, int value);
     LIR* AddWideData(LIR* *constant_list_p, int val_lo, int val_hi);
-    void ProcessSwitchTables();
     void DumpSparseSwitchTable(const uint16_t* table);
     void DumpPackedSwitchTable(const uint16_t* table);
     void MarkBoundary(DexOffset offset, const char* inst_str);
@@ -678,9 +670,7 @@
     int AssignLiteralOffset(CodeOffset offset);
     int AssignSwitchTablesOffset(CodeOffset offset);
     int AssignFillArrayDataOffset(CodeOffset offset);
-    virtual LIR* InsertCaseLabel(DexOffset vaddr, int keyVal);
-    virtual void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec);
-    void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec);
+    LIR* InsertCaseLabel(uint32_t bbid, int keyVal);
 
     // Handle bookkeeping to convert a wide RegLocation to a narrow RegLocation.  No code generated.
     virtual RegLocation NarrowRegLoc(RegLocation loc);
@@ -843,8 +833,8 @@
     virtual void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
                                 RegLocation rl_src1, RegLocation rl_src2, int flags);
     void GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, RegLocation rl_src);
-    virtual void GenSuspendTest(int opt_flags);
-    virtual void GenSuspendTestAndBranch(int opt_flags, LIR* target);
+    void GenSuspendTest(int opt_flags);
+    void GenSuspendTestAndBranch(int opt_flags, LIR* target);
 
     // This will be overridden by x86 implementation.
     virtual void GenConstWide(RegLocation rl_dest, int64_t value);
@@ -874,17 +864,17 @@
     void CallRuntimeHelperImmMethod(QuickEntrypointEnum trampoline, int arg0, bool safepoint_pc);
     void CallRuntimeHelperRegMethod(QuickEntrypointEnum trampoline, RegStorage arg0,
                                     bool safepoint_pc);
-    void CallRuntimeHelperRegMethodRegLocation(QuickEntrypointEnum trampoline, RegStorage arg0,
-                                               RegLocation arg2, bool safepoint_pc);
+    void CallRuntimeHelperRegRegLocationMethod(QuickEntrypointEnum trampoline, RegStorage arg0,
+                                               RegLocation arg1, bool safepoint_pc);
     void CallRuntimeHelperRegLocationRegLocation(QuickEntrypointEnum trampoline, RegLocation arg0,
                                                  RegLocation arg1, bool safepoint_pc);
     void CallRuntimeHelperRegReg(QuickEntrypointEnum trampoline, RegStorage arg0, RegStorage arg1,
                                  bool safepoint_pc);
     void CallRuntimeHelperRegRegImm(QuickEntrypointEnum trampoline, RegStorage arg0,
                                     RegStorage arg1, int arg2, bool safepoint_pc);
-    void CallRuntimeHelperImmMethodRegLocation(QuickEntrypointEnum trampoline, int arg0,
-                                               RegLocation arg2, bool safepoint_pc);
-    void CallRuntimeHelperImmMethodImm(QuickEntrypointEnum trampoline, int arg0, int arg2,
+    void CallRuntimeHelperImmRegLocationMethod(QuickEntrypointEnum trampoline, int arg0,
+                                               RegLocation arg1, bool safepoint_pc);
+    void CallRuntimeHelperImmImmMethod(QuickEntrypointEnum trampoline, int arg0, int arg1,
                                        bool safepoint_pc);
     void CallRuntimeHelperImmRegLocationRegLocation(QuickEntrypointEnum trampoline, int arg0,
                                                     RegLocation arg1, RegLocation arg2,
@@ -895,29 +885,24 @@
                                                             bool safepoint_pc);
     void GenInvoke(CallInfo* info);
     void GenInvokeNoInline(CallInfo* info);
-    virtual NextCallInsn GetNextSDCallInsn();
+    virtual NextCallInsn GetNextSDCallInsn() = 0;
 
     /*
      * @brief Generate the actual call insn based on the method info.
      * @param method_info the lowering info for the method call.
      * @returns Call instruction
      */
-    virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info);
+    virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) = 0;
 
     virtual void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
-    virtual int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                             NextCallInsn next_call_insn,
-                             const MethodReference& target_method,
-                             uint32_t vtable_idx,
-                             uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                             bool skip_this);
-    virtual int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                           NextCallInsn next_call_insn,
-                           const MethodReference& target_method,
-                           uint32_t vtable_idx,
-                           uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                           bool skip_this);
-
+    virtual int GenDalvikArgs(CallInfo* info, int call_state, LIR** pcrLabel,
+                      NextCallInsn next_call_insn,
+                      const MethodReference& target_method,
+                      uint32_t vtable_idx,
+                      uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+                      bool skip_this);
+    virtual int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count);
+    virtual void GenDalvikArgsFlushPromoted(CallInfo* info, int start);
     /**
      * @brief Used to determine the register location of destination.
      * @details This is needed during generation of inline intrinsics because it finds destination
@@ -958,36 +943,26 @@
     bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile);
     bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object,
                              bool is_volatile, bool is_ordered);
-    virtual int LoadArgRegs(CallInfo* info, int call_state,
-                    NextCallInsn next_call_insn,
-                    const MethodReference& target_method,
-                    uint32_t vtable_idx,
-                    uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                    bool skip_this);
 
     // Shared by all targets - implemented in gen_loadstore.cc.
     RegLocation LoadCurrMethod();
     void LoadCurrMethodDirect(RegStorage r_tgt);
     virtual LIR* LoadConstant(RegStorage r_dest, int value);
     // Natural word size.
-    virtual LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
+    LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
       return LoadBaseDisp(r_base, displacement, r_dest, kWord, kNotVolatile);
     }
-    // Load 8 bits, regardless of target.
-    virtual LIR* Load8Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
-      return LoadBaseDisp(r_base, displacement, r_dest, kSignedByte, kNotVolatile);
-    }
     // Load 32 bits, regardless of target.
-    virtual LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest)  {
+    LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest)  {
       return LoadBaseDisp(r_base, displacement, r_dest, k32, kNotVolatile);
     }
     // Load a reference at base + displacement and decompress into register.
-    virtual LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
+    LIR* LoadRefDisp(RegStorage r_base, int displacement, RegStorage r_dest,
                              VolatileKind is_volatile) {
       return LoadBaseDisp(r_base, displacement, r_dest, kReference, is_volatile);
     }
     // Load a reference at base + index and decompress into register.
-    virtual LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
+    LIR* LoadRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest,
                                 int scale) {
       return LoadBaseIndexed(r_base, r_index, r_dest, scale, kReference);
     }
@@ -1004,21 +979,21 @@
     // Load Dalvik value with 64-bit memory storage.
     virtual void LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest);
     // Store an item of natural word size.
-    virtual LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
+    LIR* StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
       return StoreBaseDisp(r_base, displacement, r_src, kWord, kNotVolatile);
     }
     // Store an uncompressed reference into a compressed 32-bit container.
-    virtual LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
+    LIR* StoreRefDisp(RegStorage r_base, int displacement, RegStorage r_src,
                               VolatileKind is_volatile) {
       return StoreBaseDisp(r_base, displacement, r_src, kReference, is_volatile);
     }
     // Store an uncompressed reference into a compressed 32-bit container by index.
-    virtual LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
+    LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
                                  int scale) {
       return StoreBaseIndexed(r_base, r_index, r_src, scale, kReference);
     }
     // Store 32 bits, regardless of target.
-    virtual LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) {
+    LIR* Store32Disp(RegStorage r_base, int displacement, RegStorage r_src) {
       return StoreBaseDisp(r_base, displacement, r_src, k32, kNotVolatile);
     }
 
@@ -1228,7 +1203,7 @@
       }
     }
 
-    virtual RegStorage GetArgMappingToPhysicalReg(int arg_num) = 0;
+    RegStorage GetArgMappingToPhysicalReg(int arg_num);
     virtual RegLocation GetReturnAlt() = 0;
     virtual RegLocation GetReturnWideAlt() = 0;
     virtual RegLocation LocCReturn() = 0;
@@ -1486,6 +1461,30 @@
 
     virtual LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) = 0;
 
+    // Queries for backend support for vectors
+    /*
+     * Return the number of bits in a vector register.
+     * @return 0 if vector registers are not supported, or the
+     * number of bits in the vector register if supported.
+     */
+    virtual int VectorRegisterSize() {
+      return 0;
+    }
+
+    /*
+     * Return the number of reservable vector registers supported
+     * @param long_or_fp, true if floating point computations will be
+     * executed or the operations will be long type while vector
+     * registers are reserved.
+     * @return the number of vector registers that are available
+     * @note The backend should ensure that sufficient vector registers
+     * are held back to generate scalar code without exhausting vector
+     * registers, if scalar code also uses the vector registers.
+     */
+    virtual int NumReservableVectorRegisters(bool long_or_fp ATTRIBUTE_UNUSED) {
+      return 0;
+    }
+
   protected:
     Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
@@ -1493,18 +1492,6 @@
       return cu_;
     }
     /*
-     * @brief Returns the index of the lowest set bit in 'x'.
-     * @param x Value to be examined.
-     * @returns The bit number of the lowest bit set in the value.
-     */
-    int32_t LowestSetBit(uint64_t x);
-    /*
-     * @brief Is this value a power of two?
-     * @param x Value to be examined.
-     * @returns 'true' if only 1 bit is set in the value.
-     */
-    bool IsPowerOfTwo(uint64_t x);
-    /*
      * @brief Do these SRs overlap?
      * @param rl_op1 One RegLocation
      * @param rl_op2 The other RegLocation
@@ -1720,6 +1707,7 @@
     LIR* first_fixup_;                         // Doubly-linked list of LIR nodes requiring fixups.
 
   protected:
+    ArenaAllocator* const arena_;
     CompilationUnit* const cu_;
     MIRGraph* const mir_graph_;
     ArenaVector<SwitchTable*> switch_tables_;
@@ -1752,7 +1740,7 @@
     int live_sreg_;
     CodeBuffer code_buffer_;
     // The source mapping table data (pc -> dex). More entries than in encoded_mapping_table_
-    SrcMap src_mapping_table_;
+    DefaultSrcMap src_mapping_table_;
     // The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix.
     std::vector<uint8_t> encoded_mapping_table_;
     ArenaVector<uint32_t> core_vmap_table_;
@@ -1780,6 +1768,63 @@
     // to deduplicate the masks.
     ResourceMaskCache mask_cache_;
 
+  protected:
+    // ABI support
+    class ShortyArg {
+      public:
+        explicit ShortyArg(char type) : type_(type) { }
+        bool IsFP() { return type_ == 'F' || type_ == 'D'; }
+        bool IsWide() { return type_ == 'J' || type_ == 'D'; }
+        bool IsRef() { return type_ == 'L'; }
+        char GetType() { return type_; }
+      private:
+        char type_;
+    };
+
+    class ShortyIterator {
+      public:
+        ShortyIterator(const char* shorty, bool is_static);
+        bool Next();
+        ShortyArg GetArg() { return ShortyArg(pending_this_ ? 'L' : *cur_); }
+      private:
+        const char* cur_;
+        bool pending_this_;
+        bool initialized_;
+    };
+
+    class InToRegStorageMapper {
+     public:
+      virtual RegStorage GetNextReg(ShortyArg arg) = 0;
+      virtual ~InToRegStorageMapper() {}
+      virtual void Reset() = 0;
+    };
+
+    class InToRegStorageMapping {
+     public:
+      explicit InToRegStorageMapping(ArenaAllocator* arena)
+          : mapping_(std::less<int>(), arena->Adapter()), count_(0),
+            max_mapped_in_(0), has_arguments_on_stack_(false),  initialized_(false) {}
+      void Initialize(ShortyIterator* shorty, InToRegStorageMapper* mapper);
+      /**
+       * @return the index of last VR mapped to physical register. In other words
+       * any VR starting from (return value + 1) index is mapped to memory.
+       */
+      int GetMaxMappedIn() { return max_mapped_in_; }
+      bool HasArgumentsOnStack() { return has_arguments_on_stack_; }
+      RegStorage Get(int in_position);
+      bool IsInitialized() { return initialized_; }
+     private:
+      ArenaSafeMap<int, RegStorage> mapping_;
+      int count_;
+      int max_mapped_in_;
+      bool has_arguments_on_stack_;
+      bool initialized_;
+    };
+
+  // Cached mapping of method input to reg storage according to ABI.
+  InToRegStorageMapping in_to_reg_storage_mapping_;
+  virtual InToRegStorageMapper* GetResetedInToRegStorageMapper() = 0;
+
   private:
     static bool SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type);
 };  // Class Mir2Lir
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 8d4cb3c..909077e 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -18,15 +18,28 @@
 
 #include <cstdint>
 
+#include "base/dumpable.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/timing_logger.h"
 #include "compiler.h"
-#include "dex/frontend.h"
+#include "dex_file-inl.h"
+#include "dex_file_to_method_inliner_map.h"
+#include "dex/compiler_ir.h"
+#include "dex/dex_flags.h"
 #include "dex/mir_graph.h"
+#include "dex/pass_driver_me_opts.h"
+#include "dex/pass_driver_me_post_opt.h"
+#include "dex/pass_manager.h"
 #include "dex/quick/mir_to_lir.h"
 #include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
 #include "elf_writer_quick.h"
 #include "jni/quick/jni_compiler.h"
+#include "mir_to_lir.h"
 #include "mirror/art_method-inl.h"
-#include "base/logging.h"
+#include "mirror/object.h"
+#include "runtime.h"
 
 // Specific compiler backends.
 #include "dex/quick/arm/backend_arm.h"
@@ -36,48 +49,6 @@
 
 namespace art {
 
-class QuickCompiler : public Compiler {
- public:
-  explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
-
-  void Init() const OVERRIDE;
-
-  void UnInit() const OVERRIDE;
-
-  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
-      OVERRIDE;
-
-  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
-                          uint32_t access_flags,
-                          InvokeType invoke_type,
-                          uint16_t class_def_idx,
-                          uint32_t method_idx,
-                          jobject class_loader,
-                          const DexFile& dex_file) const OVERRIDE;
-
-  CompiledMethod* JniCompile(uint32_t access_flags,
-                             uint32_t method_idx,
-                             const DexFile& dex_file) const OVERRIDE;
-
-  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  bool WriteElf(art::File* file,
-                OatWriter* oat_writer,
-                const std::vector<const art::DexFile*>& dex_files,
-                const std::string& android_root,
-                bool is_host) const
-    OVERRIDE
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
-
-  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
-};
-
 static_assert(0U == static_cast<size_t>(kNone),   "kNone not 0");
 static_assert(1U == static_cast<size_t>(kArm),    "kArm not 1");
 static_assert(2U == static_cast<size_t>(kArm64),  "kArm64 not 2");
@@ -392,10 +363,10 @@
     Instruction::IPUT_BYTE_QUICK,
     Instruction::IPUT_CHAR_QUICK,
     Instruction::IPUT_SHORT_QUICK,
-    Instruction::UNUSED_EF,
-    Instruction::UNUSED_F0,
-    Instruction::UNUSED_F1,
-    Instruction::UNUSED_F2,
+    Instruction::IGET_BOOLEAN_QUICK,
+    Instruction::IGET_BYTE_QUICK,
+    Instruction::IGET_CHAR_QUICK,
+    Instruction::IGET_SHORT_QUICK,
     Instruction::UNUSED_F3,
     Instruction::UNUSED_F4,
     Instruction::UNUSED_F5,
@@ -573,7 +544,7 @@
   cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
 }
 
-void QuickCompiler::Init() const {
+void QuickCompiler::Init() {
   CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
 }
 
@@ -581,6 +552,47 @@
   CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
 }
 
+/* Default optimizer/debug setting for the compiler. */
+static uint32_t kCompilerOptimizerDisableFlags = 0 |  // Disable specific optimizations
+  // (1 << kLoadStoreElimination) |
+  // (1 << kLoadHoisting) |
+  // (1 << kSuppressLoads) |
+  // (1 << kNullCheckElimination) |
+  // (1 << kClassInitCheckElimination) |
+  // (1 << kGlobalValueNumbering) |
+  // (1 << kLocalValueNumbering) |
+  // (1 << kPromoteRegs) |
+  // (1 << kTrackLiveTemps) |
+  // (1 << kSafeOptimizations) |
+  // (1 << kBBOpt) |
+  // (1 << kSuspendCheckElimination) |
+  // (1 << kMatch) |
+  // (1 << kPromoteCompilerTemps) |
+  // (1 << kSuppressExceptionEdges) |
+  // (1 << kSuppressMethodInlining) |
+  0;
+
+static uint32_t kCompilerDebugFlags = 0 |     // Enable debug/testing modes
+  // (1 << kDebugDisplayMissingTargets) |
+  // (1 << kDebugVerbose) |
+  // (1 << kDebugDumpCFG) |
+  // (1 << kDebugSlowFieldPath) |
+  // (1 << kDebugSlowInvokePath) |
+  // (1 << kDebugSlowStringPath) |
+  // (1 << kDebugSlowestFieldPath) |
+  // (1 << kDebugSlowestStringPath) |
+  // (1 << kDebugExerciseResolveMethod) |
+  // (1 << kDebugVerifyDataflow) |
+  // (1 << kDebugShowMemoryUsage) |
+  // (1 << kDebugShowNops) |
+  // (1 << kDebugCountOpcodes) |
+  // (1 << kDebugDumpCheckStats) |
+  // (1 << kDebugShowSummaryMemoryUsage) |
+  // (1 << kDebugShowFilterStats) |
+  // (1 << kDebugTimings) |
+  // (1 << kDebugCodegenDump) |
+  0;
+
 CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item,
                                        uint32_t access_flags,
                                        InvokeType invoke_type,
@@ -588,22 +600,163 @@
                                        uint32_t method_idx,
                                        jobject class_loader,
                                        const DexFile& dex_file) const {
-  CompiledMethod* method = TryCompileWithSeaIR(code_item,
-                                               access_flags,
-                                               invoke_type,
-                                               class_def_idx,
-                                               method_idx,
-                                               class_loader,
-                                               dex_file);
-  if (method != nullptr) {
-    return method;
-  }
-
   // TODO: check method fingerprint here to determine appropriate backend type.  Until then, use
   // build default.
   CompilerDriver* driver = GetCompilerDriver();
-  return CompileOneMethod(driver, this, code_item, access_flags, invoke_type, class_def_idx,
-                          method_idx, class_loader, dex_file, nullptr /* use thread llvm_info */);
+
+  VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
+  if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
+    return nullptr;
+  }
+
+  DCHECK(driver->GetCompilerOptions().IsCompilationEnabled());
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  InstructionSet instruction_set = driver->GetInstructionSet();
+  if (instruction_set == kArm) {
+    instruction_set = kThumb2;
+  }
+  CompilationUnit cu(driver->GetArenaPool(), instruction_set, driver, class_linker);
+
+  // TODO: Mips64 is not yet implemented.
+  CHECK((cu.instruction_set == kThumb2) ||
+        (cu.instruction_set == kArm64) ||
+        (cu.instruction_set == kX86) ||
+        (cu.instruction_set == kX86_64) ||
+        (cu.instruction_set == kMips));
+
+  // TODO: set this from command line
+  constexpr bool compiler_flip_match = false;
+  const std::string compiler_method_match = "";
+
+  bool use_match = !compiler_method_match.empty();
+  bool match = use_match && (compiler_flip_match ^
+      (PrettyMethod(method_idx, dex_file).find(compiler_method_match) != std::string::npos));
+  if (!use_match || match) {
+    cu.disable_opt = kCompilerOptimizerDisableFlags;
+    cu.enable_debug = kCompilerDebugFlags;
+    cu.verbose = VLOG_IS_ON(compiler) ||
+        (cu.enable_debug & (1 << kDebugVerbose));
+  }
+
+  if (driver->GetCompilerOptions().HasVerboseMethods()) {
+    cu.verbose = driver->GetCompilerOptions().IsVerboseMethod(PrettyMethod(method_idx, dex_file));
+  }
+
+  if (cu.verbose) {
+    cu.enable_debug |= (1 << kDebugCodegenDump);
+  }
+
+  /*
+   * TODO: rework handling of optimization and debug flags.  Should we split out
+   * MIR and backend flags?  Need command-line setting as well.
+   */
+
+  InitCompilationUnit(cu);
+
+  cu.StartTimingSplit("BuildMIRGraph");
+  cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
+
+  /*
+   * After creation of the MIR graph, also create the code generator.
+   * The reason we do this is that optimizations on the MIR graph may need to get information
+   * that is only available if a CG exists.
+   */
+  cu.cg.reset(GetCodeGenerator(&cu, nullptr));
+
+  /* Gathering opcode stats? */
+  if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
+    cu.mir_graph->EnableOpcodeCounting();
+  }
+
+  /* Build the raw MIR graph */
+  cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+                             class_loader, dex_file);
+
+  if (!CanCompileMethod(method_idx, dex_file, &cu)) {
+    VLOG(compiler)  << cu.instruction_set << ": Cannot compile method : "
+        << PrettyMethod(method_idx, dex_file);
+    cu.EndTiming();
+    return nullptr;
+  }
+
+  cu.NewTimingSplit("MIROpt:CheckFilters");
+  std::string skip_message;
+  if (cu.mir_graph->SkipCompilation(&skip_message)) {
+    VLOG(compiler) << cu.instruction_set << ": Skipping method : "
+        << PrettyMethod(method_idx, dex_file) << "  Reason = " << skip_message;
+    cu.EndTiming();
+    return nullptr;
+  }
+
+  /* Create the pass driver and launch it */
+  PassDriverMEOpts pass_driver(GetPreOptPassManager(), &cu);
+  pass_driver.Launch();
+
+  /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */
+  if (cu.compiler_driver->ProfilePresent()
+      && !cu.mir_graph->MethodIsLeaf()
+      && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) {
+    cu.EndTiming();
+    return nullptr;
+  }
+
+  if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
+    cu.mir_graph->DumpCheckStats();
+  }
+
+  if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
+    cu.mir_graph->ShowOpcodeStats();
+  }
+
+  /* Reassociate sreg names with original Dalvik vreg names. */
+  cu.mir_graph->RemapRegLocations();
+
+  /* Free Arenas from the cu.arena_stack for reuse by the cu.arena in the codegen. */
+  if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
+    if (cu.arena_stack.PeakBytesAllocated() > 1 * 1024 * 1024) {
+      MemStats stack_stats(cu.arena_stack.GetPeakStats());
+      LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(stack_stats);
+    }
+  }
+  cu.arena_stack.Reset();
+
+  CompiledMethod* result = nullptr;
+
+  if (cu.mir_graph->PuntToInterpreter()) {
+    VLOG(compiler) << cu.instruction_set << ": Punted method to interpreter: "
+        << PrettyMethod(method_idx, dex_file);
+    cu.EndTiming();
+    return nullptr;
+  }
+
+  cu.cg->Materialize();
+
+  cu.NewTimingSplit("Dedupe");  /* deduping takes up the vast majority of time in GetCompiledMethod(). */
+  result = cu.cg->GetCompiledMethod();
+  cu.NewTimingSplit("Cleanup");
+
+  if (result) {
+    VLOG(compiler) << cu.instruction_set << ": Compiled " << PrettyMethod(method_idx, dex_file);
+  } else {
+    VLOG(compiler) << cu.instruction_set << ": Deferred " << PrettyMethod(method_idx, dex_file);
+  }
+
+  if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
+    if (cu.arena.BytesAllocated() > (1 * 1024 *1024)) {
+      MemStats mem_stats(cu.arena.GetMemStats());
+      LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
+    }
+  }
+
+  if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
+    LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks()
+                    << " " << PrettyMethod(method_idx, dex_file);
+  }
+
+  cu.EndTiming();
+  driver->GetTimingsLogger()->AddLogger(cu.timings);
+  return result;
 }
 
 CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
@@ -626,7 +779,7 @@
                                        *GetCompilerDriver());
 }
 
-Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
   UNUSED(compilation_unit);
   Mir2Lir* mir_to_lir = nullptr;
   switch (cu->instruction_set) {
@@ -657,8 +810,34 @@
   return mir_to_lir;
 }
 
+QuickCompiler::QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {
+  const auto& compiler_options = driver->GetCompilerOptions();
+  auto* pass_manager_options = compiler_options.GetPassManagerOptions();
+  pre_opt_pass_manager_.reset(new PassManager(*pass_manager_options));
+  CHECK(pre_opt_pass_manager_.get() != nullptr);
+  PassDriverMEOpts::SetupPasses(pre_opt_pass_manager_.get());
+  pre_opt_pass_manager_->CreateDefaultPassList();
+  if (pass_manager_options->GetPrintPassOptions()) {
+    PassDriverMEOpts::PrintPassOptions(pre_opt_pass_manager_.get());
+  }
+  // TODO: Different options for pre vs post opts?
+  post_opt_pass_manager_.reset(new PassManager(PassManagerOptions()));
+  CHECK(post_opt_pass_manager_.get() != nullptr);
+  PassDriverMEPostOpt::SetupPasses(post_opt_pass_manager_.get());
+  post_opt_pass_manager_->CreateDefaultPassList();
+  if (pass_manager_options->GetPrintPassOptions()) {
+    PassDriverMEPostOpt::PrintPassOptions(post_opt_pass_manager_.get());
+  }
+}
+
+QuickCompiler::~QuickCompiler() {
+}
 
 Compiler* CreateQuickCompiler(CompilerDriver* driver) {
+  return QuickCompiler::Create(driver);
+}
+
+Compiler* QuickCompiler::Create(CompilerDriver* driver) {
   return new QuickCompiler(driver);
 }
 
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
index 10de5fb..5153a9e 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -17,12 +17,70 @@
 #ifndef ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
 #define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
 
+#include "compiler.h"
+
 namespace art {
 
 class Compiler;
 class CompilerDriver;
+class Mir2Lir;
+class PassManager;
 
-Compiler* CreateQuickCompiler(CompilerDriver* driver);
+class QuickCompiler : public Compiler {
+ public:
+  virtual ~QuickCompiler();
+
+  void Init() OVERRIDE;
+
+  void UnInit() const OVERRIDE;
+
+  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+      OVERRIDE;
+
+  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+                          uint32_t access_flags,
+                          InvokeType invoke_type,
+                          uint16_t class_def_idx,
+                          uint32_t method_idx,
+                          jobject class_loader,
+                          const DexFile& dex_file) const OVERRIDE;
+
+  CompiledMethod* JniCompile(uint32_t access_flags,
+                             uint32_t method_idx,
+                             const DexFile& dex_file) const OVERRIDE;
+
+  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool WriteElf(art::File* file,
+                OatWriter* oat_writer,
+                const std::vector<const art::DexFile*>& dex_files,
+                const std::string& android_root,
+                bool is_host) const
+    OVERRIDE
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  Mir2Lir* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const;
+
+  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+
+  static Compiler* Create(CompilerDriver* driver);
+
+  const PassManager* GetPreOptPassManager() const {
+    return pre_opt_pass_manager_.get();
+  }
+  const PassManager* GetPostOptPassManager() const {
+    return post_opt_pass_manager_.get();
+  }
+
+ protected:
+  explicit QuickCompiler(CompilerDriver* driver);
+
+ private:
+  std::unique_ptr<PassManager> pre_opt_pass_manager_;
+  std::unique_ptr<PassManager> post_opt_pass_manager_;
+  DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
+};
 
 }  // namespace art
 
diff --git a/compiler/llvm/llvm_compiler.h b/compiler/dex/quick/quick_compiler_factory.h
similarity index 66%
rename from compiler/llvm/llvm_compiler.h
rename to compiler/dex/quick/quick_compiler_factory.h
index da6d0e9..31ee1cf 100644
--- a/compiler/llvm/llvm_compiler.h
+++ b/compiler/dex/quick/quick_compiler_factory.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_LLVM_LLVM_COMPILER_H_
-#define ART_COMPILER_LLVM_LLVM_COMPILER_H_
+#ifndef ART_COMPILER_DEX_QUICK_QUICK_COMPILER_FACTORY_H_
+#define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_FACTORY_H_
 
 namespace art {
 
 class Compiler;
 class CompilerDriver;
 
-Compiler* CreateLLVMCompiler(CompilerDriver* driver);
+Compiler* CreateQuickCompiler(CompilerDriver* driver);
 
-}
+}  // namespace art
 
-#endif  // ART_COMPILER_LLVM_LLVM_COMPILER_H_
+#endif  // ART_COMPILER_DEX_QUICK_QUICK_COMPILER_FACTORY_H_
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index 0a98c80..8efafb2 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -16,10 +16,13 @@
 
 /* This file contains register alloction support. */
 
-#include "dex/compiler_ir.h"
-#include "dex/compiler_internals.h"
 #include "mir_to_lir-inl.h"
 
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
+
 namespace art {
 
 /*
diff --git a/compiler/dex/quick/resource_mask.cc b/compiler/dex/quick/resource_mask.cc
index 088bec8..8a27ecb 100644
--- a/compiler/dex/quick/resource_mask.cc
+++ b/compiler/dex/quick/resource_mask.cc
@@ -18,7 +18,9 @@
 
 #include "resource_mask.h"
 
+#include "base/logging.h"
 #include "utils/arena_allocator.h"
+#include "utils.h"
 
 namespace art {
 
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 84d68d2..6f26b78 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -15,7 +15,10 @@
  */
 
 #include "codegen_x86.h"
-#include "dex/quick/mir_to_lir-inl.h"
+
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/quick/mir_to_lir.h"
 #include "oat.h"
 #include "x86_lir.h"
 
@@ -553,7 +556,7 @@
 }
 
 static bool NeedsRex(int32_t raw_reg) {
-  return RegStorage::RegNum(raw_reg) > 7;
+  return raw_reg != kRIPReg && RegStorage::RegNum(raw_reg) > 7;
 }
 
 static uint8_t LowRegisterBits(int32_t raw_reg) {
@@ -689,7 +692,13 @@
           entry->opcode != kX86Lea32RM && entry->opcode != kX86Lea64RM) {
         DCHECK_NE(entry->flags & (IS_LOAD | IS_STORE), UINT64_C(0)) << entry->name;
       }
-      size += IS_SIMM8(displacement) ? 1 : 4;
+      if (raw_base == kRIPReg) {
+        DCHECK(cu_->target64) <<
+          "Attempt to use a 64-bit RIP adressing with instruction " << entry->name;
+        size += 4;
+      } else {
+        size += IS_SIMM8(displacement) ? 1 : 4;
+      }
     }
   }
   size += entry->skeleton.immediate_bytes;
@@ -1022,14 +1031,24 @@
 
 void X86Mir2Lir::EmitModrmDisp(uint8_t reg_or_opcode, uint8_t base, int32_t disp) {
   DCHECK_LT(reg_or_opcode, 8);
-  DCHECK_LT(base, 8);
-  uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | base;
-  code_buffer_.push_back(modrm);
-  if (base == rs_rX86_SP_32.GetRegNum()) {
-    // Special SIB for SP base
-    code_buffer_.push_back(0 << 6 | rs_rX86_SP_32.GetRegNum() << 3 | rs_rX86_SP_32.GetRegNum());
+  if (base == kRIPReg) {
+    // x86_64 RIP handling: always 32 bit displacement.
+    uint8_t modrm = (0x0 << 6) | (reg_or_opcode << 3) | 0x5;
+    code_buffer_.push_back(modrm);
+    code_buffer_.push_back(disp & 0xFF);
+    code_buffer_.push_back((disp >> 8) & 0xFF);
+    code_buffer_.push_back((disp >> 16) & 0xFF);
+    code_buffer_.push_back((disp >> 24) & 0xFF);
+  } else {
+    DCHECK_LT(base, 8);
+    uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg_or_opcode << 3) | base;
+    code_buffer_.push_back(modrm);
+    if (base == rs_rX86_SP_32.GetRegNum()) {
+      // Special SIB for SP base
+      code_buffer_.push_back(0 << 6 | rs_rX86_SP_32.GetRegNum() << 3 | rs_rX86_SP_32.GetRegNum());
+    }
+    EmitDisp(base, disp);
   }
-  EmitDisp(base, disp);
 }
 
 void X86Mir2Lir::EmitModrmSibDisp(uint8_t reg_or_opcode, uint8_t base, uint8_t index,
@@ -1141,7 +1160,7 @@
   CheckValidByteRegister(entry, raw_reg);
   EmitPrefixAndOpcode(entry, raw_reg, NO_REG, raw_base);
   uint8_t low_reg = LowRegisterBits(raw_reg);
-  uint8_t low_base = LowRegisterBits(raw_base);
+  uint8_t low_base = (raw_base == kRIPReg) ? raw_base : LowRegisterBits(raw_base);
   EmitModrmDisp(low_reg, low_base, disp);
   DCHECK_EQ(0, entry->skeleton.modrm_opcode);
   DCHECK_EQ(0, entry->skeleton.ax_opcode);
@@ -1758,12 +1777,29 @@
             LIR *target_lir = lir->target;
             DCHECK(target_lir != NULL);
             CodeOffset target = target_lir->offset;
-            lir->operands[2] = target;
-            int newSize = GetInsnSize(lir);
-            if (newSize != lir->flags.size) {
-              lir->flags.size = newSize;
-              res = kRetryAll;
+            // Handle 64 bit RIP addressing.
+            if (lir->operands[1] == kRIPReg) {
+              // Offset is relative to next instruction.
+              lir->operands[2] = target - (lir->offset + lir->flags.size);
+            } else {
+              lir->operands[2] = target;
+              int newSize = GetInsnSize(lir);
+              if (newSize != lir->flags.size) {
+                lir->flags.size = newSize;
+                res = kRetryAll;
+              }
             }
+          } else if (lir->flags.fixup == kFixupSwitchTable) {
+            DCHECK(cu_->target64);
+            DCHECK_EQ(lir->opcode, kX86Lea64RM) << "Unknown instruction: " << X86Mir2Lir::EncodingMap[lir->opcode].name;
+            DCHECK_EQ(lir->operands[1], static_cast<int>(kRIPReg));
+            // Grab the target offset from the saved data.
+            Mir2Lir::EmbeddedData* tab_rec =
+                reinterpret_cast<Mir2Lir::EmbeddedData*>(UnwrapPointer(lir->operands[4]));
+            CodeOffset target = tab_rec->offset;
+            // Handle 64 bit RIP addressing.
+            // Offset is relative to next instruction.
+            lir->operands[2] = target - (lir->offset + lir->flags.size);
           }
           break;
       }
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index be10d93..284e8f6 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -17,7 +17,10 @@
 /* This file contains codegen for the X86 ISA */
 
 #include "codegen_x86.h"
+
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
+#include "driver/compiler_driver.h"
 #include "gc/accounting/card_table.h"
 #include "mirror/art_method.h"
 #include "mirror/object_array-inl.h"
@@ -34,84 +37,6 @@
 }
 
 /*
- * We override InsertCaseLabel, because the first parameter represents
- * a basic block id, instead of a dex offset.
- */
-LIR* X86Mir2Lir::InsertCaseLabel(DexOffset bbid, int keyVal) {
-  LIR* boundary_lir = &block_label_list_[bbid];
-  LIR* res = boundary_lir;
-  if (cu_->verbose) {
-    // Only pay the expense if we're pretty-printing.
-    LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocLIR));
-    BasicBlock* bb = mir_graph_->GetBasicBlock(bbid);
-    DCHECK(bb != nullptr);
-    new_label->dalvik_offset = bb->start_offset;;
-    new_label->opcode = kPseudoCaseLabel;
-    new_label->operands[0] = keyVal;
-    new_label->flags.fixup = kFixupLabel;
-    DCHECK(!new_label->flags.use_def_invalid);
-    new_label->u.m.def_mask = &kEncodeAll;
-    InsertLIRAfter(boundary_lir, new_label);
-    res = new_label;
-  }
-  return res;
-}
-
-void X86Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
-  const uint16_t* table = tab_rec->table;
-  const int32_t *targets = reinterpret_cast<const int32_t*>(&table[4]);
-  int entries = table[1];
-  int low_key = s4FromSwitchData(&table[2]);
-  for (int i = 0; i < entries; i++) {
-    // The value at targets[i] is a basic block id, instead of a dex offset.
-    tab_rec->targets[i] = InsertCaseLabel(targets[i], i + low_key);
-  }
-}
-
-/*
- * We convert and create a new packed switch table that stores
- * basic block ids to targets[] by examining successor blocks.
- * Note that the original packed switch table stores dex offsets to targets[].
- */
-const uint16_t* X86Mir2Lir::ConvertPackedSwitchTable(MIR* mir, const uint16_t* table) {
-  /*
-   * The original packed switch data format:
-   *  ushort ident = 0x0100  magic value
-   *  ushort size            number of entries in the table
-   *  int first_key          first (and lowest) switch case value
-   *  int targets[size]      branch targets, relative to switch opcode
-   *
-   * Total size is (4+size*2) 16-bit code units.
-   *
-   * Note that the new packed switch data format is the same as the original
-   * format, except that targets[] are basic block ids.
-   *
-   */
-  BasicBlock* bb = mir_graph_->GetBasicBlock(mir->bb);
-  DCHECK(bb != nullptr);
-  // Get the number of entries.
-  int entries = table[1];
-  const int32_t* as_int32 = reinterpret_cast<const int32_t*>(&table[2]);
-  int32_t starting_key = as_int32[0];
-  // Create a new table.
-  int size = sizeof(uint16_t) * (4 + entries * 2);
-  uint16_t* new_table = reinterpret_cast<uint16_t*>(arena_->Alloc(size, kArenaAllocMisc));
-  // Copy ident, size, and first_key to the new table.
-  memcpy(new_table, table, sizeof(uint16_t) * 4);
-  // Get the new targets.
-  int32_t* new_targets = reinterpret_cast<int32_t*>(&new_table[4]);
-  // Find out targets for each entry.
-  int i = 0;
-  for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
-    DCHECK_EQ(starting_key + i, successor_block_info->key);
-    // Save target basic block id.
-    new_targets[i++] = successor_block_info->block;
-  }
-  DCHECK_EQ(i, entries);
-  return new_table;
-}
-
-/*
  * Code pattern will look something like:
  *
  * mov  r_val, ..
@@ -128,39 +53,19 @@
  * done:
  */
 void X86Mir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
-  const uint16_t* old_table = mir_graph_->GetTable(mir, table_offset);
-  const uint16_t* table = ConvertPackedSwitchTable(mir, old_table);
+  const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
   // Add the table to the list - we'll process it later
   SwitchTable* tab_rec =
       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
+  tab_rec->switch_mir = mir;
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
-                                                      kArenaAllocLIR));
   switch_tables_.push_back(tab_rec);
 
   // Get the switch value
   rl_src = LoadValue(rl_src, kCoreReg);
-  // NewLIR0(kX86Bkpt);
 
-  // Materialize a pointer to the switch table
-  RegStorage start_of_method_reg;
-  if (base_of_code_ != nullptr) {
-    // We can use the saved value.
-    RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-    if (rl_method.wide) {
-      rl_method = LoadValueWide(rl_method, kCoreReg);
-    } else {
-      rl_method = LoadValue(rl_method, kCoreReg);
-    }
-    start_of_method_reg = rl_method.reg;
-    store_method_addr_used_ = true;
-  } else {
-    start_of_method_reg = AllocTempRef();
-    NewLIR1(kX86StartOfMethod, start_of_method_reg.GetReg());
-  }
-  DCHECK_EQ(start_of_method_reg.Is64Bit(), cu_->target64);
   int low_key = s4FromSwitchData(&table[2]);
   RegStorage keyReg;
   // Remove the bias, if necessary
@@ -170,19 +75,49 @@
     keyReg = AllocTemp();
     OpRegRegImm(kOpSub, keyReg, rl_src.reg, low_key);
   }
+
   // Bounds check - if < 0 or >= size continue following switch
   OpRegImm(kOpCmp, keyReg, size - 1);
   LIR* branch_over = OpCondBranch(kCondHi, NULL);
 
-  // Load the displacement from the switch table
-  RegStorage disp_reg = AllocTemp();
-  NewLIR5(kX86PcRelLoadRA, disp_reg.GetReg(), start_of_method_reg.GetReg(), keyReg.GetReg(),
-          2, WrapPointer(tab_rec));
-  // Add displacement to start of method
-  OpRegReg(kOpAdd, start_of_method_reg, cu_->target64 ? As64BitReg(disp_reg) : disp_reg);
+  RegStorage addr_for_jump;
+  if (cu_->target64) {
+    RegStorage table_base = AllocTempWide();
+    // Load the address of the table into table_base.
+    LIR* lea = RawLIR(current_dalvik_offset_, kX86Lea64RM, table_base.GetReg(), kRIPReg,
+                      256, 0, WrapPointer(tab_rec));
+    lea->flags.fixup = kFixupSwitchTable;
+    AppendLIR(lea);
+
+    // Load the offset from the table out of the table.
+    addr_for_jump = AllocTempWide();
+    NewLIR5(kX86MovsxdRA, addr_for_jump.GetReg(), table_base.GetReg(), keyReg.GetReg(), 2, 0);
+
+    // Add the offset from the table to the table base.
+    OpRegReg(kOpAdd, addr_for_jump, table_base);
+  } else {
+    // Materialize a pointer to the switch table.
+    RegStorage start_of_method_reg;
+    if (base_of_code_ != nullptr) {
+      // We can use the saved value.
+      RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+      rl_method = LoadValue(rl_method, kCoreReg);
+      start_of_method_reg = rl_method.reg;
+      store_method_addr_used_ = true;
+    } else {
+      start_of_method_reg = AllocTempRef();
+      NewLIR1(kX86StartOfMethod, start_of_method_reg.GetReg());
+    }
+    // Load the displacement from the switch table.
+    addr_for_jump = AllocTemp();
+    NewLIR5(kX86PcRelLoadRA, addr_for_jump.GetReg(), start_of_method_reg.GetReg(), keyReg.GetReg(),
+            2, WrapPointer(tab_rec));
+    // Add displacement to start of method.
+    OpRegReg(kOpAdd, addr_for_jump, start_of_method_reg);
+  }
+
   // ..and go!
-  LIR* switch_branch = NewLIR1(kX86JmpR, start_of_method_reg.GetReg());
-  tab_rec->anchor = switch_branch;
+  tab_rec->anchor = NewLIR1(kX86JmpR, addr_for_jump.GetReg());
 
   /* branch_over target here */
   LIR* target = NewLIR0(kPseudoTargetLabel);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 9cb0bf5..ca60400 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -17,7 +17,9 @@
 #ifndef ART_COMPILER_DEX_QUICK_X86_CODEGEN_X86_H_
 #define ART_COMPILER_DEX_QUICK_X86_CODEGEN_X86_H_
 
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
 #include "dex/quick/mir_to_lir.h"
 #include "x86_lir.h"
 
@@ -28,40 +30,41 @@
 
 class X86Mir2Lir : public Mir2Lir {
  protected:
-  class InToRegStorageMapper {
-   public:
-    virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref) = 0;
-    virtual ~InToRegStorageMapper() {}
-  };
-
   class InToRegStorageX86_64Mapper : public InToRegStorageMapper {
    public:
-    explicit InToRegStorageX86_64Mapper(Mir2Lir* ml) : ml_(ml), cur_core_reg_(0), cur_fp_reg_(0) {}
-    virtual ~InToRegStorageX86_64Mapper() {}
-    virtual RegStorage GetNextReg(bool is_double_or_float, bool is_wide, bool is_ref);
+    explicit InToRegStorageX86_64Mapper(Mir2Lir* m2l)
+        : m2l_(m2l), cur_core_reg_(0), cur_fp_reg_(0) {}
+    virtual RegStorage GetNextReg(ShortyArg arg);
+    virtual void Reset() OVERRIDE {
+      cur_core_reg_ = 0;
+      cur_fp_reg_ = 0;
+    }
    protected:
-    Mir2Lir* ml_;
-   private:
-    int cur_core_reg_;
-    int cur_fp_reg_;
+    Mir2Lir* m2l_;
+    size_t cur_core_reg_;
+    size_t cur_fp_reg_;
   };
 
-  class InToRegStorageMapping {
+  class InToRegStorageX86Mapper : public InToRegStorageX86_64Mapper {
    public:
-    InToRegStorageMapping() : max_mapped_in_(0), is_there_stack_mapped_(false),
-    initialized_(false) {}
-    void Initialize(RegLocation* arg_locs, int count, InToRegStorageMapper* mapper);
-    int GetMaxMappedIn() { return max_mapped_in_; }
-    bool IsThereStackMapped() { return is_there_stack_mapped_; }
-    RegStorage Get(int in_position);
-    bool IsInitialized() { return initialized_; }
-   private:
-    std::map<int, RegStorage> mapping_;
-    int max_mapped_in_;
-    bool is_there_stack_mapped_;
-    bool initialized_;
+    explicit InToRegStorageX86Mapper(Mir2Lir* m2l)
+        : InToRegStorageX86_64Mapper(m2l) { }
+    virtual RegStorage GetNextReg(ShortyArg arg);
   };
 
+  InToRegStorageX86_64Mapper in_to_reg_storage_x86_64_mapper_;
+  InToRegStorageX86Mapper in_to_reg_storage_x86_mapper_;
+  InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE {
+    InToRegStorageMapper* res;
+    if (cu_->target64) {
+      res = &in_to_reg_storage_x86_64_mapper_;
+    } else {
+      res = &in_to_reg_storage_x86_mapper_;
+    }
+    res->Reset();
+    return res;
+  }
+
   class ExplicitTempRegisterLock {
   public:
     ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir, int n_regs, ...);
@@ -71,6 +74,8 @@
     X86Mir2Lir* const mir_to_lir_;
   };
 
+  virtual int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
+
  public:
   X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
@@ -108,9 +113,12 @@
       if (cu_->target64) {
         return As64BitReg(TargetReg32(symbolic_reg));
       } else {
+        if (symbolic_reg >= kFArg0 && symbolic_reg <= kFArg3) {
+          // We want an XMM, not a pair.
+          return As64BitReg(TargetReg32(symbolic_reg));
+        }
         // x86: construct a pair.
         DCHECK((kArg0 <= symbolic_reg && symbolic_reg < kArg3) ||
-               (kFArg0 <= symbolic_reg && symbolic_reg < kFArg3) ||
                (kRet0 == symbolic_reg));
         return RegStorage::MakeRegPair(TargetReg32(symbolic_reg),
                                  TargetReg32(static_cast<SpecialTargetRegister>(symbolic_reg + 1)));
@@ -125,8 +133,6 @@
     return TargetReg(symbolic_reg, cu_->target64 ? kWide : kNotWide);
   }
 
-  RegStorage GetArgMappingToPhysicalReg(int arg_num) OVERRIDE;
-
   RegLocation GetReturnAlt() OVERRIDE;
   RegLocation GetReturnWideAlt() OVERRIDE;
   RegLocation LocCReturn() OVERRIDE;
@@ -265,11 +271,8 @@
                                      int first_bit, int second_bit) OVERRIDE;
   void GenNegDouble(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
   void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
-  const uint16_t* ConvertPackedSwitchTable(MIR* mir, const uint16_t* table);
   void GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
   void GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
-  LIR* InsertCaseLabel(DexOffset vaddr, int keyVal) OVERRIDE;
-  void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) OVERRIDE;
 
   /**
    * @brief Implement instanceof a final class with x86 specific code.
@@ -350,22 +353,7 @@
   void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
                      SpecialTargetRegister symbolic_reg) OVERRIDE;
 
-  void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
-
   NextCallInsn GetNextSDCallInsn() OVERRIDE;
-  int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                           NextCallInsn next_call_insn,
-                           const MethodReference& target_method,
-                           uint32_t vtable_idx,
-                           uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                           bool skip_this) OVERRIDE;
-
-  int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
-                         NextCallInsn next_call_insn,
-                         const MethodReference& target_method,
-                         uint32_t vtable_idx,
-                         uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
-                         bool skip_this) OVERRIDE;
 
   /*
    * @brief Generate a relative call to the method that will be patched at link time.
@@ -439,8 +427,6 @@
   LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement,
                             RegStorage r_src, OpSize size, int opt_flags = 0);
 
-  RegStorage GetCoreArgMappingToPhysicalReg(int core_arg_num) const;
-
   int AssignInsnOffsets();
   void AssignOffsets();
   AssemblerStatus AssembleInstructions(CodeOffset start_addr);
@@ -1000,8 +986,6 @@
    */
   static void DumpRegLocation(RegLocation loc);
 
-  InToRegStorageMapping in_to_reg_storage_mapping_;
-
  private:
   void SwapBits(RegStorage result_reg, int shift, int32_t value);
   void SwapBits64(RegStorage result_reg, int shift, int64_t value);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index 4825db6..d8616a7 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -15,6 +15,8 @@
  */
 
 #include "codegen_x86.h"
+
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "x86_lir.h"
@@ -599,8 +601,12 @@
 }
 
 bool X86Mir2Lir::GenInlinedSqrt(CallInfo* info) {
-  RegLocation rl_src = info->args[0];
   RegLocation rl_dest = InlineTargetWide(info);  // double place for result
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
+  RegLocation rl_src = info->args[0];
   rl_src = LoadValueWide(rl_src, kFPReg);
   RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
   NewLIR2(kX86SqrtsdRR, rl_result.reg.GetReg(), rl_src.reg.GetReg());
@@ -722,9 +728,13 @@
 
 bool X86Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
   if (is_double) {
+    RegLocation rl_dest = InlineTargetWide(info);
+    if (rl_dest.s_reg_low == INVALID_SREG) {
+      // Result is unused, the code is dead. Inlining successful, no code generated.
+      return true;
+    }
     RegLocation rl_src1 = LoadValueWide(info->args[0], kFPReg);
     RegLocation rl_src2 = LoadValueWide(info->args[2], kFPReg);
-    RegLocation rl_dest = InlineTargetWide(info);
     RegLocation rl_result = EvalLocWide(rl_dest, kFPReg, true);
 
     // Avoid src2 corruption by OpRegCopyWide.
@@ -775,9 +785,13 @@
     branch_exit_equal->target = NewLIR0(kPseudoTargetLabel);
     StoreValueWide(rl_dest, rl_result);
   } else {
+    RegLocation rl_dest = InlineTarget(info);
+    if (rl_dest.s_reg_low == INVALID_SREG) {
+      // Result is unused, the code is dead. Inlining successful, no code generated.
+      return true;
+    }
     RegLocation rl_src1 = LoadValue(info->args[0], kFPReg);
     RegLocation rl_src2 = LoadValue(info->args[1], kFPReg);
-    RegLocation rl_dest = InlineTarget(info);
     RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
 
     // Avoid src2 corruption by OpRegCopyWide.
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 80cdc83..4fe7a43 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -17,10 +17,13 @@
 /* This file contains codegen for the X86 ISA */
 
 #include "codegen_x86.h"
+
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
 #include "mirror/art_method.h"
 #include "mirror/array-inl.h"
+#include "utils.h"
 #include "x86_lir.h"
 
 namespace art {
@@ -656,7 +659,7 @@
     NewLIR3(kX86Lea32RM, rl_result.reg.GetReg(), rl_src.reg.GetReg(), std::abs(imm) - 1);
     NewLIR2(kX86Test32RR, rl_src.reg.GetReg(), rl_src.reg.GetReg());
     OpCondRegReg(kOpCmov, kCondPl, rl_result.reg, rl_src.reg);
-    int shift_amount = LowestSetBit(imm);
+    int shift_amount = CTZ(imm);
     OpRegImm(kOpAsr, rl_result.reg, shift_amount);
     if (imm < 0) {
       OpReg(kOpNeg, rl_result.reg);
@@ -947,12 +950,16 @@
   }
 
   // Get the two arguments to the invoke and place them in GP registers.
+  RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
   RegLocation rl_src1 = info->args[0];
   RegLocation rl_src2 = (is_long) ? info->args[2] : info->args[1];
   rl_src1 = (is_long) ? LoadValueWide(rl_src1, kCoreReg) : LoadValue(rl_src1, kCoreReg);
   rl_src2 = (is_long) ? LoadValueWide(rl_src2, kCoreReg) : LoadValue(rl_src2, kCoreReg);
 
-  RegLocation rl_dest = (is_long) ? InlineTargetWide(info) : InlineTarget(info);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
 
   /*
@@ -987,6 +994,11 @@
 }
 
 bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+  RegLocation rl_dest = size == k64 ? InlineTargetWide(info) : InlineTarget(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
   RegLocation rl_src_address = info->args[0];  // long address
   RegLocation rl_address;
   if (!cu_->target64) {
@@ -995,7 +1007,6 @@
   } else {
     rl_address = LoadValueWide(rl_src_address, kCoreReg);
   }
-  RegLocation rl_dest = size == k64 ? InlineTargetWide(info) : InlineTarget(info);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   // Unaligned access is allowed on x86.
   LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
@@ -1237,10 +1248,14 @@
 }
 
 bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
+  RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
+  if (rl_dest.s_reg_low == INVALID_SREG) {
+    // Result is unused, the code is dead. Inlining successful, no code generated.
+    return true;
+  }
   RegLocation rl_src_i = info->args[0];
   RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg)
                                    : LoadValue(rl_src_i, kCoreReg);
-  RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   if (size == k64) {
     if (cu_->instruction_set == kX86_64) {
@@ -1289,6 +1304,18 @@
 }
 
 LIR* X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
+  if (cu_->target64) {
+    // We can do this directly using RIP addressing.
+    // We don't know the proper offset for the value, so pick one that will force
+    // 4 byte offset.  We will fix this up in the assembler later to have the right
+    // value.
+    ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
+    LIR* res = NewLIR3(kX86Mov32RM, reg.GetReg(), kRIPReg, 256);
+    res->target = target;
+    res->flags.fixup = kFixupLoad;
+    return res;
+  }
+
   CHECK(base_of_code_ != nullptr);
 
   // Address the start of the method
@@ -1309,7 +1336,6 @@
                     0, 0, target);
   res->target = target;
   res->flags.fixup = kFixupLoad;
-  store_method_addr_used_ = true;
   return res;
 }
 
@@ -1616,7 +1642,7 @@
     GenArithOpLong(Instruction::ADD_LONG, rl_dest, rl_src1, rl_src1, flags);
     return true;
   } else if (IsPowerOfTwo(val)) {
-    int shift_amount = LowestSetBit(val);
+    int shift_amount = CTZ(val);
     if (!PartiallyIntersects(rl_src1, rl_dest)) {
       rl_src1 = LoadValueWide(rl_src1, kCoreReg);
       RegLocation rl_result = GenShiftImmOpLong(Instruction::SHL_LONG, rl_dest, rl_src1,
@@ -2059,7 +2085,7 @@
     OpRegReg(kOpAdd, rl_result.reg, rl_src.reg);
     NewLIR2(kX86Test64RR, rl_src.reg.GetReg(), rl_src.reg.GetReg());
     OpCondRegReg(kOpCmov, kCondPl, rl_result.reg, rl_src.reg);
-    int shift_amount = LowestSetBit(imm);
+    int shift_amount = CTZ(imm);
     OpRegImm(kOpAsr, rl_result.reg, shift_amount);
     if (imm < 0) {
       OpReg(kOpNeg, rl_result.reg);
@@ -2308,7 +2334,7 @@
  */
 void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
                              RegLocation rl_index, RegLocation rl_dest, int scale) {
-  RegisterClass reg_class = RegClassBySize(size);
+  RegisterClass reg_class = RegClassForFieldLoadStore(size, false);
   int len_offset = mirror::Array::LengthOffset().Int32Value();
   RegLocation rl_result;
   rl_array = LoadValue(rl_array, kRefReg);
@@ -2357,7 +2383,7 @@
  */
 void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
                              RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
-  RegisterClass reg_class = RegClassBySize(size);
+  RegisterClass reg_class = RegClassForFieldLoadStore(size, false);
   int len_offset = mirror::Array::LengthOffset().Int32Value();
   int data_offset;
 
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 998aeff..c4adb09 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -14,16 +14,19 @@
  * limitations under the License.
  */
 
+#include "codegen_x86.h"
+
 #include <cstdarg>
 #include <inttypes.h>
 #include <string>
 
 #include "arch/instruction_set_features.h"
 #include "backend_x86.h"
-#include "codegen_x86.h"
-#include "dex/compiler_internals.h"
+#include "base/logging.h"
+#include "dex/compiler_ir.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_method.h"
 #include "mirror/string.h"
@@ -177,10 +180,10 @@
   RegStorage::InvalidReg(),  // kArg5
   RegStorage::InvalidReg(),  // kArg6
   RegStorage::InvalidReg(),  // kArg7
-  rs_rAX,                    // kFArg0
-  rs_rCX,                    // kFArg1
-  rs_rDX,                    // kFArg2
-  rs_rBX,                    // kFArg3
+  rs_fr0,                    // kFArg0
+  rs_fr1,                    // kFArg1
+  rs_fr2,                    // kFArg2
+  rs_fr3,                    // kFArg3
   RegStorage::InvalidReg(),  // kFArg4
   RegStorage::InvalidReg(),  // kFArg5
   RegStorage::InvalidReg(),  // kFArg6
@@ -197,7 +200,7 @@
   rs_rDX,                    // kRet1
   rs_rAX,                    // kInvokeTgt
   rs_rAX,                    // kHiddenArg - used to hold the method index before copying to fr0.
-  rs_fr0,                    // kHiddenFpArg
+  rs_fr7,                    // kHiddenFpArg
   rs_rCX,                    // kCount
 };
 
@@ -206,7 +209,7 @@
   RegStorage::InvalidReg(),  // kSelf - Thread pointer.
   RegStorage::InvalidReg(),  // kSuspend - Used to reduce suspend checks for some targets.
   RegStorage::InvalidReg(),  // kLr - no register as the return address is pushed on entry.
-  RegStorage::InvalidReg(),  // kPc - TODO: RIP based addressing.
+  RegStorage(kRIPReg),       // kPc
   rs_rX86_SP_32,             // kSp
   rs_rDI,                    // kArg0
   rs_rSI,                    // kArg1
@@ -542,13 +545,13 @@
   LockTemp(TargetReg32(kArg1));
   LockTemp(TargetReg32(kArg2));
   LockTemp(TargetReg32(kArg3));
+  LockTemp(TargetReg32(kFArg0));
+  LockTemp(TargetReg32(kFArg1));
+  LockTemp(TargetReg32(kFArg2));
+  LockTemp(TargetReg32(kFArg3));
   if (cu_->target64) {
     LockTemp(TargetReg32(kArg4));
     LockTemp(TargetReg32(kArg5));
-    LockTemp(TargetReg32(kFArg0));
-    LockTemp(TargetReg32(kFArg1));
-    LockTemp(TargetReg32(kFArg2));
-    LockTemp(TargetReg32(kFArg3));
     LockTemp(TargetReg32(kFArg4));
     LockTemp(TargetReg32(kFArg5));
     LockTemp(TargetReg32(kFArg6));
@@ -562,13 +565,14 @@
   FreeTemp(TargetReg32(kArg1));
   FreeTemp(TargetReg32(kArg2));
   FreeTemp(TargetReg32(kArg3));
+  FreeTemp(TargetReg32(kHiddenArg));
+  FreeTemp(TargetReg32(kFArg0));
+  FreeTemp(TargetReg32(kFArg1));
+  FreeTemp(TargetReg32(kFArg2));
+  FreeTemp(TargetReg32(kFArg3));
   if (cu_->target64) {
     FreeTemp(TargetReg32(kArg4));
     FreeTemp(TargetReg32(kArg5));
-    FreeTemp(TargetReg32(kFArg0));
-    FreeTemp(TargetReg32(kFArg1));
-    FreeTemp(TargetReg32(kFArg2));
-    FreeTemp(TargetReg32(kFArg3));
     FreeTemp(TargetReg32(kFArg4));
     FreeTemp(TargetReg32(kFArg5));
     FreeTemp(TargetReg32(kFArg6));
@@ -595,7 +599,7 @@
 }
 
 bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
-  if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+  if (!cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) {
     return false;
   }
   // Start off with using the last LIR as the barrier. If it is not enough, then we will update it.
@@ -662,6 +666,12 @@
     xp_reg_info->SetIsTemp(true);
   }
 
+  // Special Handling for x86_64 RIP addressing.
+  if (cu_->target64) {
+    RegisterInfo* info = new (arena_) RegisterInfo(RegStorage(kRIPReg), kEncodeNone);
+    reginfo_map_[kRIPReg] = info;
+  }
+
   // Alias single precision xmm to double xmms.
   // TODO: as needed, add larger vector sizes - alias all to the largest.
   for (RegisterInfo* info : reg_pool_->sp_regs_) {
@@ -791,6 +801,12 @@
 }
 
 RegisterClass X86Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) {
+  // Prefer XMM registers.  Fixes a problem with iget/iput to a FP when cached temporary
+  // with same VR is a Core register.
+  if (size == kSingle || size == kDouble) {
+    return kFPReg;
+  }
+
   // X86_64 can handle any size.
   if (cu_->target64) {
     return RegClassBySize(size);
@@ -808,6 +824,7 @@
 
 X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
     : Mir2Lir(cu, mir_graph, arena),
+      in_to_reg_storage_x86_64_mapper_(this), in_to_reg_storage_x86_mapper_(this),
       base_of_code_(nullptr), store_method_addr_(false), store_method_addr_used_(false),
       method_address_insns_(arena->Adapter()),
       class_type_address_insns_(arena->Adapter()),
@@ -1608,9 +1625,6 @@
 }
 
 void X86Mir2Lir::AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir) {
-  // The literal pool needs position independent logic.
-  store_method_addr_used_ = true;
-
   // To deal with correct memory ordering, reverse order of constants.
   int32_t constants[4];
   constants[3] = mir->dalvikInsn.arg[0];
@@ -1624,20 +1638,28 @@
     data_target = AddVectorLiteral(constants);
   }
 
-  // Address the start of the method.
-  RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-  if (rl_method.wide) {
-    rl_method = LoadValueWide(rl_method, kCoreReg);
-  } else {
-    rl_method = LoadValue(rl_method, kCoreReg);
-  }
-
   // Load the proper value from the literal area.
   // We don't know the proper offset for the value, so pick one that will force
-  // 4 byte offset.  We will fix this up in the assembler later to have the right
-  // value.
+  // 4 byte offset.  We will fix this up in the assembler later to have the
+  // right value.
+  LIR* load;
   ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-  LIR *load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
+  if (cu_->target64) {
+    load = NewLIR3(opcode, reg, kRIPReg, 256 /* bogus */);
+  } else {
+    // Address the start of the method.
+    RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+    if (rl_method.wide) {
+      rl_method = LoadValueWide(rl_method, kCoreReg);
+    } else {
+      rl_method = LoadValue(rl_method, kCoreReg);
+    }
+
+    load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
+
+    // The literal pool needs position independent logic.
+    store_method_addr_used_ = true;
+  }
   load->flags.fixup = kFixupLoad;
   load->target = data_target;
 }
@@ -2197,18 +2219,36 @@
     // Handle float case.
     // TODO Add support for fast math (not value safe) and do horizontal add in that case.
 
+    int extract_index = mir->dalvikInsn.arg[0];
+
     rl_result = EvalLoc(rl_dest, kFPReg, true);
     NewLIR2(kX86PxorRR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
-    NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
 
-    // Since FP must keep order of operation for value safety, we shift to low
-    // 32-bits and add to result.
-    for (int i = 0; i < 3; i++) {
-      NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), 0x39);
+    if (LIKELY(extract_index != 0)) {
+      // We know the index of element which we want to extract. We want to extract it and
+      // keep values in vector register correct for future use. So the way we act is:
+      // 1. Generate shuffle mask that allows to swap zeroth and required elements;
+      // 2. Shuffle vector register with this mask;
+      // 3. Extract zeroth element where required value lies;
+      // 4. Shuffle with same mask again to restore original values in vector register.
+      // The mask is generated from equivalence mask 0b11100100 swapping 0th and extracted
+      // element indices.
+      int shuffle[4] = {0b00, 0b01, 0b10, 0b11};
+      shuffle[0] = extract_index;
+      shuffle[extract_index] = 0;
+      int mask = 0;
+      for (int i = 0; i < 4; i++) {
+        mask |= (shuffle[i] << (2 * i));
+      }
+      NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), mask);
+      NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+      NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), mask);
+    } else {
+      // We need to extract zeroth element and don't need any complex stuff to do it.
       NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
     }
 
-    StoreValue(rl_dest, rl_result);
+    StoreFinalValue(rl_dest, rl_result);
   } else if (opsize == kDouble) {
     // TODO Handle double case.
     LOG(FATAL) << "Unsupported add reduce for double.";
@@ -2263,9 +2303,9 @@
       StoreFinalValue(rl_dest, rl_result);
     } else {
       int displacement = SRegOffset(rl_result.s_reg_low);
+      ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
       LIR *l = NewLIR4(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg(),
                        extract_index);
-      AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
       AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
     }
   }
@@ -2396,452 +2436,60 @@
 }
 
 // ------------ ABI support: mapping of args to physical registers -------------
-RegStorage X86Mir2Lir::InToRegStorageX86_64Mapper::GetNextReg(bool is_double_or_float, bool is_wide,
-                                                              bool is_ref) {
+RegStorage X86Mir2Lir::InToRegStorageX86_64Mapper::GetNextReg(ShortyArg arg) {
   const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3, kArg4, kArg5};
-  const int coreArgMappingToPhysicalRegSize = sizeof(coreArgMappingToPhysicalReg) /
-      sizeof(SpecialTargetRegister);
+  const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
   const SpecialTargetRegister fpArgMappingToPhysicalReg[] = {kFArg0, kFArg1, kFArg2, kFArg3,
                                                              kFArg4, kFArg5, kFArg6, kFArg7};
-  const int fpArgMappingToPhysicalRegSize = sizeof(fpArgMappingToPhysicalReg) /
-      sizeof(SpecialTargetRegister);
+  const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
 
-  if (is_double_or_float) {
+  if (arg.IsFP()) {
     if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
-      return ml_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++], is_wide ? kWide : kNotWide);
+      return m2l_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++],
+                             arg.IsWide() ? kWide : kNotWide);
     }
   } else {
     if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
-      return ml_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
-                            is_ref ? kRef : (is_wide ? kWide : kNotWide));
+      return m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
+                             arg.IsRef() ? kRef : (arg.IsWide() ? kWide : kNotWide));
     }
   }
   return RegStorage::InvalidReg();
 }
 
-RegStorage X86Mir2Lir::InToRegStorageMapping::Get(int in_position) {
-  DCHECK(IsInitialized());
-  auto res = mapping_.find(in_position);
-  return res != mapping_.end() ? res->second : RegStorage::InvalidReg();
-}
+RegStorage X86Mir2Lir::InToRegStorageX86Mapper::GetNextReg(ShortyArg arg) {
+  const SpecialTargetRegister coreArgMappingToPhysicalReg[] = {kArg1, kArg2, kArg3};
+  const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg);
+  const SpecialTargetRegister fpArgMappingToPhysicalReg[] = {kFArg0, kFArg1, kFArg2, kFArg3};
+  const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg);
 
-void X86Mir2Lir::InToRegStorageMapping::Initialize(RegLocation* arg_locs, int count,
-                                                   InToRegStorageMapper* mapper) {
-  DCHECK(mapper != nullptr);
-  max_mapped_in_ = -1;
-  is_there_stack_mapped_ = false;
-  for (int in_position = 0; in_position < count; in_position++) {
-     RegStorage reg = mapper->GetNextReg(arg_locs[in_position].fp,
-             arg_locs[in_position].wide, arg_locs[in_position].ref);
-     if (reg.Valid()) {
-       mapping_[in_position] = reg;
-       max_mapped_in_ = std::max(max_mapped_in_, in_position);
-       if (arg_locs[in_position].wide) {
-         // We covered 2 args, so skip the next one
-         in_position++;
-       }
-     } else {
-       is_there_stack_mapped_ = true;
-     }
+  RegStorage result = RegStorage::InvalidReg();
+  if (arg.IsFP()) {
+    if (cur_fp_reg_ < fpArgMappingToPhysicalRegSize) {
+      return m2l_->TargetReg(fpArgMappingToPhysicalReg[cur_fp_reg_++],
+                             arg.IsWide() ? kWide : kNotWide);
+    }
+  } else if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+    result = m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++],
+                             arg.IsRef() ? kRef : kNotWide);
+    if (arg.IsWide()) {
+      // This must be a long, as double is handled above.
+      // Ensure that we don't split a long across the last register and the stack.
+      if (cur_core_reg_ == coreArgMappingToPhysicalRegSize) {
+        // Leave the last core register unused and force the whole long to the stack.
+        cur_core_reg_++;
+        result = RegStorage::InvalidReg();
+      } else if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) {
+        result = RegStorage::MakeRegPair(
+            result, m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_core_reg_++], kNotWide));
+      }
+    }
   }
-  initialized_ = true;
-}
-
-RegStorage X86Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
-  if (!cu_->target64) {
-    return GetCoreArgMappingToPhysicalReg(arg_num);
-  }
-
-  if (!in_to_reg_storage_mapping_.IsInitialized()) {
-    int start_vreg = cu_->mir_graph->GetFirstInVR();
-    RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
-
-    InToRegStorageX86_64Mapper mapper(this);
-    in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
-  }
-  return in_to_reg_storage_mapping_.Get(arg_num);
-}
-
-RegStorage X86Mir2Lir::GetCoreArgMappingToPhysicalReg(int core_arg_num) const {
-  // For the 32-bit internal ABI, the first 3 arguments are passed in registers.
-  // Not used for 64-bit, TODO: Move X86_32 to the same framework
-  switch (core_arg_num) {
-    case 0: return TargetReg32(kArg1);
-    case 1: return TargetReg32(kArg2);
-    case 2: return TargetReg32(kArg3);
-    default: return RegStorage::InvalidReg();
-  }
+  return result;
 }
 
 // ---------End of ABI support: mapping of args to physical registers -------------
 
-/*
- * If there are any ins passed in registers that have not been promoted
- * to a callee-save register, flush them to the frame.  Perform initial
- * assignment of promoted arguments.
- *
- * ArgLocs is an array of location records describing the incoming arguments
- * with one location record per word of argument.
- */
-void X86Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method) {
-  if (!cu_->target64) return Mir2Lir::FlushIns(ArgLocs, rl_method);
-  /*
-   * Dummy up a RegLocation for the incoming Method*
-   * It will attempt to keep kArg0 live (or copy it to home location
-   * if promoted).
-   */
-
-  RegLocation rl_src = rl_method;
-  rl_src.location = kLocPhysReg;
-  rl_src.reg = TargetReg(kArg0, kRef);
-  rl_src.home = false;
-  MarkLive(rl_src);
-  StoreValue(rl_method, rl_src);
-  // If Method* has been promoted, explicitly flush
-  if (rl_method.location == kLocPhysReg) {
-    const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
-    StoreRefDisp(rs_rSP, 0, As32BitReg(TargetReg(kArg0, kRef)), kNotVolatile);
-  }
-
-  if (mir_graph_->GetNumOfInVRs() == 0) {
-    return;
-  }
-
-  int start_vreg = cu_->mir_graph->GetFirstInVR();
-  /*
-   * Copy incoming arguments to their proper home locations.
-   * NOTE: an older version of dx had an issue in which
-   * it would reuse static method argument registers.
-   * This could result in the same Dalvik virtual register
-   * being promoted to both core and fp regs. To account for this,
-   * we only copy to the corresponding promoted physical register
-   * if it matches the type of the SSA name for the incoming
-   * argument.  It is also possible that long and double arguments
-   * end up half-promoted.  In those cases, we must flush the promoted
-   * half to memory as well.
-   */
-  ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-  for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
-    // get reg corresponding to input
-    RegStorage reg = GetArgMappingToPhysicalReg(i);
-
-    RegLocation* t_loc = &ArgLocs[i];
-    if (reg.Valid()) {
-      // If arriving in register.
-
-      // We have already updated the arg location with promoted info
-      // so we can be based on it.
-      if (t_loc->location == kLocPhysReg) {
-        // Just copy it.
-        OpRegCopy(t_loc->reg, reg);
-      } else {
-        // Needs flush.
-        if (t_loc->ref) {
-          StoreRefDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), reg, kNotVolatile);
-        } else {
-          StoreBaseDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), reg, t_loc->wide ? k64 : k32,
-                        kNotVolatile);
-        }
-      }
-    } else {
-      // If arriving in frame & promoted.
-      if (t_loc->location == kLocPhysReg) {
-        if (t_loc->ref) {
-          LoadRefDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), t_loc->reg, kNotVolatile);
-        } else {
-          LoadBaseDisp(rs_rX86_SP_64, SRegOffset(start_vreg + i), t_loc->reg,
-                       t_loc->wide ? k64 : k32, kNotVolatile);
-        }
-      }
-    }
-    if (t_loc->wide) {
-      // Increment i to skip the next one.
-      i++;
-    }
-  }
-}
-
-/*
- * Load up to 5 arguments, the first three of which will be in
- * kArg1 .. kArg3.  On entry kArg0 contains the current method pointer,
- * and as part of the load sequence, it must be replaced with
- * the target method pointer.  Note, this may also be called
- * for "range" variants if the number of arguments is 5 or fewer.
- */
-int X86Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
-                                  int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
-                                  const MethodReference& target_method,
-                                  uint32_t vtable_idx, uintptr_t direct_code,
-                                  uintptr_t direct_method, InvokeType type, bool skip_this) {
-  if (!cu_->target64) {
-    return Mir2Lir::GenDalvikArgsNoRange(info,
-                                         call_state, pcrLabel, next_call_insn,
-                                         target_method,
-                                         vtable_idx, direct_code,
-                                         direct_method, type, skip_this);
-  }
-  return GenDalvikArgsRange(info,
-                            call_state, pcrLabel, next_call_insn,
-                            target_method,
-                            vtable_idx, direct_code,
-                            direct_method, type, skip_this);
-}
-
-/*
- * May have 0+ arguments (also used for jumbo).  Note that
- * source virtual registers may be in physical registers, so may
- * need to be flushed to home location before copying.  This
- * applies to arg3 and above (see below).
- *
- * Two general strategies:
- *    If < 20 arguments
- *       Pass args 3-18 using vldm/vstm block copy
- *       Pass arg0, arg1 & arg2 in kArg1-kArg3
- *    If 20+ arguments
- *       Pass args arg19+ using memcpy block copy
- *       Pass arg0, arg1 & arg2 in kArg1-kArg3
- *
- */
-int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
-                                LIR** pcrLabel, NextCallInsn next_call_insn,
-                                const MethodReference& target_method,
-                                uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
-                                InvokeType type, bool skip_this) {
-  if (!cu_->target64) {
-    return Mir2Lir::GenDalvikArgsRange(info, call_state,
-                                pcrLabel, next_call_insn,
-                                target_method,
-                                vtable_idx, direct_code, direct_method,
-                                type, skip_this);
-  }
-
-  /* If no arguments, just return */
-  if (info->num_arg_words == 0)
-    return call_state;
-
-  const int start_index = skip_this ? 1 : 0;
-
-  InToRegStorageX86_64Mapper mapper(this);
-  InToRegStorageMapping in_to_reg_storage_mapping;
-  in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
-  const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
-  const int size_of_the_last_mapped = last_mapped_in == -1 ? 1 :
-          info->args[last_mapped_in].wide ? 2 : 1;
-  int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + size_of_the_last_mapped);
-
-  // Fisrt of all, check whether it make sense to use bulk copying
-  // Optimization is aplicable only for range case
-  // TODO: make a constant instead of 2
-  if (info->is_range && regs_left_to_pass_via_stack >= 2) {
-    // Scan the rest of the args - if in phys_reg flush to memory
-    for (int next_arg = last_mapped_in + size_of_the_last_mapped; next_arg < info->num_arg_words;) {
-      RegLocation loc = info->args[next_arg];
-      if (loc.wide) {
-        loc = UpdateLocWide(loc);
-        if (loc.location == kLocPhysReg) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          StoreBaseDisp(rs_rX86_SP_64, SRegOffset(loc.s_reg_low), loc.reg, k64, kNotVolatile);
-        }
-        next_arg += 2;
-      } else {
-        loc = UpdateLoc(loc);
-        if (loc.location == kLocPhysReg) {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          StoreBaseDisp(rs_rX86_SP_64, SRegOffset(loc.s_reg_low), loc.reg, k32, kNotVolatile);
-        }
-        next_arg++;
-      }
-    }
-
-    // The rest can be copied together
-    int start_offset = SRegOffset(info->args[last_mapped_in + size_of_the_last_mapped].s_reg_low);
-    int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + size_of_the_last_mapped,
-                                                   cu_->instruction_set);
-
-    int current_src_offset = start_offset;
-    int current_dest_offset = outs_offset;
-
-    // Only davik regs are accessed in this loop; no next_call_insn() calls.
-    ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-    while (regs_left_to_pass_via_stack > 0) {
-      // This is based on the knowledge that the stack itself is 16-byte aligned.
-      bool src_is_16b_aligned = (current_src_offset & 0xF) == 0;
-      bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0;
-      size_t bytes_to_move;
-
-      /*
-       * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a
-       * a 128-bit move because we won't get the chance to try to aligned. If there are more than
-       * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned.
-       * We do this because we could potentially do a smaller move to align.
-       */
-      if (regs_left_to_pass_via_stack == 4 ||
-          (regs_left_to_pass_via_stack > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) {
-        // Moving 128-bits via xmm register.
-        bytes_to_move = sizeof(uint32_t) * 4;
-
-        // Allocate a free xmm temp. Since we are working through the calling sequence,
-        // we expect to have an xmm temporary available.  AllocTempDouble will abort if
-        // there are no free registers.
-        RegStorage temp = AllocTempDouble();
-
-        LIR* ld1 = nullptr;
-        LIR* ld2 = nullptr;
-        LIR* st1 = nullptr;
-        LIR* st2 = nullptr;
-
-        /*
-         * The logic is similar for both loads and stores. If we have 16-byte alignment,
-         * do an aligned move. If we have 8-byte alignment, then do the move in two
-         * parts. This approach prevents possible cache line splits. Finally, fall back
-         * to doing an unaligned move. In most cases we likely won't split the cache
-         * line but we cannot prove it and thus take a conservative approach.
-         */
-        bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
-        bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
-
-        ScopedMemRefType mem_ref_type2(this, ResourceMask::kDalvikReg);
-        if (src_is_16b_aligned) {
-          ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovA128FP);
-        } else if (src_is_8b_aligned) {
-          ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovLo128FP);
-          ld2 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset + (bytes_to_move >> 1),
-                            kMovHi128FP);
-        } else {
-          ld1 = OpMovRegMem(temp, rs_rX86_SP_64, current_src_offset, kMovU128FP);
-        }
-
-        if (dest_is_16b_aligned) {
-          st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovA128FP);
-        } else if (dest_is_8b_aligned) {
-          st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovLo128FP);
-          st2 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset + (bytes_to_move >> 1),
-                            temp, kMovHi128FP);
-        } else {
-          st1 = OpMovMemReg(rs_rX86_SP_64, current_dest_offset, temp, kMovU128FP);
-        }
-
-        // TODO If we could keep track of aliasing information for memory accesses that are wider
-        // than 64-bit, we wouldn't need to set up a barrier.
-        if (ld1 != nullptr) {
-          if (ld2 != nullptr) {
-            // For 64-bit load we can actually set up the aliasing information.
-            AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true);
-            AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true, true);
-          } else {
-            // Set barrier for 128-bit load.
-            ld1->u.m.def_mask = &kEncodeAll;
-          }
-        }
-        if (st1 != nullptr) {
-          if (st2 != nullptr) {
-            // For 64-bit store we can actually set up the aliasing information.
-            AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true);
-            AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false, true);
-          } else {
-            // Set barrier for 128-bit store.
-            st1->u.m.def_mask = &kEncodeAll;
-          }
-        }
-
-        // Free the temporary used for the data movement.
-        FreeTemp(temp);
-      } else {
-        // Moving 32-bits via general purpose register.
-        bytes_to_move = sizeof(uint32_t);
-
-        // Instead of allocating a new temp, simply reuse one of the registers being used
-        // for argument passing.
-        RegStorage temp = TargetReg(kArg3, kNotWide);
-
-        // Now load the argument VR and store to the outs.
-        Load32Disp(rs_rX86_SP_64, current_src_offset, temp);
-        Store32Disp(rs_rX86_SP_64, current_dest_offset, temp);
-      }
-
-      current_src_offset += bytes_to_move;
-      current_dest_offset += bytes_to_move;
-      regs_left_to_pass_via_stack -= (bytes_to_move >> 2);
-    }
-    DCHECK_EQ(regs_left_to_pass_via_stack, 0);
-  }
-
-  // Now handle rest not registers if they are
-  if (in_to_reg_storage_mapping.IsThereStackMapped()) {
-    RegStorage regSingle = TargetReg(kArg2, kNotWide);
-    RegStorage regWide = TargetReg(kArg3, kWide);
-    for (int i = start_index;
-         i < last_mapped_in + size_of_the_last_mapped + regs_left_to_pass_via_stack; i++) {
-      RegLocation rl_arg = info->args[i];
-      rl_arg = UpdateRawLoc(rl_arg);
-      RegStorage reg = in_to_reg_storage_mapping.Get(i);
-      if (!reg.Valid()) {
-        int out_offset = StackVisitor::GetOutVROffset(i, cu_->instruction_set);
-
-        {
-          ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
-          if (rl_arg.wide) {
-            if (rl_arg.location == kLocPhysReg) {
-              StoreBaseDisp(rs_rX86_SP_64, out_offset, rl_arg.reg, k64, kNotVolatile);
-            } else {
-              LoadValueDirectWideFixed(rl_arg, regWide);
-              StoreBaseDisp(rs_rX86_SP_64, out_offset, regWide, k64, kNotVolatile);
-            }
-          } else {
-            if (rl_arg.location == kLocPhysReg) {
-              StoreBaseDisp(rs_rX86_SP_64, out_offset, rl_arg.reg, k32, kNotVolatile);
-            } else {
-              LoadValueDirectFixed(rl_arg, regSingle);
-              StoreBaseDisp(rs_rX86_SP_64, out_offset, regSingle, k32, kNotVolatile);
-            }
-          }
-        }
-        call_state = next_call_insn(cu_, info, call_state, target_method,
-                                    vtable_idx, direct_code, direct_method, type);
-      }
-      if (rl_arg.wide) {
-        i++;
-      }
-    }
-  }
-
-  // Finish with mapped registers
-  for (int i = start_index; i <= last_mapped_in; i++) {
-    RegLocation rl_arg = info->args[i];
-    rl_arg = UpdateRawLoc(rl_arg);
-    RegStorage reg = in_to_reg_storage_mapping.Get(i);
-    if (reg.Valid()) {
-      if (rl_arg.wide) {
-        LoadValueDirectWideFixed(rl_arg, reg);
-      } else {
-        LoadValueDirectFixed(rl_arg, reg);
-      }
-      call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                               direct_code, direct_method, type);
-    }
-    if (rl_arg.wide) {
-      i++;
-    }
-  }
-
-  call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
-                           direct_code, direct_method, type);
-  if (pcrLabel) {
-    if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) {
-      *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags);
-    } else {
-      *pcrLabel = nullptr;
-      // In lieu of generating a check for kArg1 being null, we need to
-      // perform a load when doing implicit checks.
-      RegStorage tmp = AllocTemp();
-      Load32Disp(TargetReg(kArg1, kRef), 0, tmp);
-      MarkPossibleNullPointerException(info->opt_flags);
-      FreeTemp(tmp);
-    }
-  }
-  return call_state;
-}
-
 bool X86Mir2Lir::GenInlinedCharAt(CallInfo* info) {
   // Location of reference to data array
   int value_offset = mirror::String::ValueOffset().Int32Value();
@@ -2871,7 +2519,7 @@
     if (rl_idx.is_const) {
       LIR* comparison;
       range_check_branch = OpCmpMemImmBranch(
-          kCondUlt, RegStorage::InvalidReg(), rl_obj.reg, count_offset,
+          kCondLs, RegStorage::InvalidReg(), rl_obj.reg, count_offset,
           mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr, &comparison);
       MarkPossibleNullPointerExceptionAfter(0, comparison);
     } else {
@@ -2969,4 +2617,122 @@
   }
 }
 
+int X86Mir2Lir::GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) {
+  if (count < 4) {
+    // It does not make sense to use this utility if we have no chance to use
+    // 128-bit move.
+    return count;
+  }
+  GenDalvikArgsFlushPromoted(info, first);
+
+  // The rest can be copied together
+  int current_src_offset = SRegOffset(info->args[first].s_reg_low);
+  int current_dest_offset = StackVisitor::GetOutVROffset(first, cu_->instruction_set);
+
+  // Only davik regs are accessed in this loop; no next_call_insn() calls.
+  ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+  while (count > 0) {
+    // This is based on the knowledge that the stack itself is 16-byte aligned.
+    bool src_is_16b_aligned = (current_src_offset & 0xF) == 0;
+    bool dest_is_16b_aligned = (current_dest_offset & 0xF) == 0;
+    size_t bytes_to_move;
+
+    /*
+     * The amount to move defaults to 32-bit. If there are 4 registers left to move, then do a
+     * a 128-bit move because we won't get the chance to try to aligned. If there are more than
+     * 4 registers left to move, consider doing a 128-bit only if either src or dest are aligned.
+     * We do this because we could potentially do a smaller move to align.
+     */
+    if (count == 4 || (count > 4 && (src_is_16b_aligned || dest_is_16b_aligned))) {
+      // Moving 128-bits via xmm register.
+      bytes_to_move = sizeof(uint32_t) * 4;
+
+      // Allocate a free xmm temp. Since we are working through the calling sequence,
+      // we expect to have an xmm temporary available. AllocTempDouble will abort if
+      // there are no free registers.
+      RegStorage temp = AllocTempDouble();
+
+      LIR* ld1 = nullptr;
+      LIR* ld2 = nullptr;
+      LIR* st1 = nullptr;
+      LIR* st2 = nullptr;
+
+      /*
+       * The logic is similar for both loads and stores. If we have 16-byte alignment,
+       * do an aligned move. If we have 8-byte alignment, then do the move in two
+       * parts. This approach prevents possible cache line splits. Finally, fall back
+       * to doing an unaligned move. In most cases we likely won't split the cache
+       * line but we cannot prove it and thus take a conservative approach.
+       */
+      bool src_is_8b_aligned = (current_src_offset & 0x7) == 0;
+      bool dest_is_8b_aligned = (current_dest_offset & 0x7) == 0;
+
+      if (src_is_16b_aligned) {
+        ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovA128FP);
+      } else if (src_is_8b_aligned) {
+        ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovLo128FP);
+        ld2 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset + (bytes_to_move >> 1),
+                          kMovHi128FP);
+      } else {
+        ld1 = OpMovRegMem(temp, TargetPtrReg(kSp), current_src_offset, kMovU128FP);
+      }
+
+      if (dest_is_16b_aligned) {
+        st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovA128FP);
+      } else if (dest_is_8b_aligned) {
+        st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovLo128FP);
+        st2 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset + (bytes_to_move >> 1),
+                          temp, kMovHi128FP);
+      } else {
+        st1 = OpMovMemReg(TargetPtrReg(kSp), current_dest_offset, temp, kMovU128FP);
+      }
+
+      // TODO If we could keep track of aliasing information for memory accesses that are wider
+      // than 64-bit, we wouldn't need to set up a barrier.
+      if (ld1 != nullptr) {
+        if (ld2 != nullptr) {
+          // For 64-bit load we can actually set up the aliasing information.
+          AnnotateDalvikRegAccess(ld1, current_src_offset >> 2, true, true);
+          AnnotateDalvikRegAccess(ld2, (current_src_offset + (bytes_to_move >> 1)) >> 2, true,
+                                  true);
+        } else {
+          // Set barrier for 128-bit load.
+          ld1->u.m.def_mask = &kEncodeAll;
+        }
+      }
+      if (st1 != nullptr) {
+        if (st2 != nullptr) {
+          // For 64-bit store we can actually set up the aliasing information.
+          AnnotateDalvikRegAccess(st1, current_dest_offset >> 2, false, true);
+          AnnotateDalvikRegAccess(st2, (current_dest_offset + (bytes_to_move >> 1)) >> 2, false,
+                                  true);
+        } else {
+          // Set barrier for 128-bit store.
+          st1->u.m.def_mask = &kEncodeAll;
+        }
+      }
+
+      // Free the temporary used for the data movement.
+      FreeTemp(temp);
+    } else {
+      // Moving 32-bits via general purpose register.
+      bytes_to_move = sizeof(uint32_t);
+
+      // Instead of allocating a new temp, simply reuse one of the registers being used
+      // for argument passing.
+      RegStorage temp = TargetReg(kArg3, kNotWide);
+
+      // Now load the argument VR and store to the outs.
+      Load32Disp(TargetPtrReg(kSp), current_src_offset, temp);
+      Store32Disp(TargetPtrReg(kSp), current_dest_offset, temp);
+    }
+
+    current_src_offset += bytes_to_move;
+    current_dest_offset += bytes_to_move;
+    count -= (bytes_to_move >> 2);
+  }
+  DCHECK_EQ(count, 0);
+  return count;
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index ad3222c..893b98a 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -15,12 +15,15 @@
  */
 
 #include "codegen_x86.h"
+
+#include "base/logging.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "dex/dataflow_iterator-inl.h"
-#include "x86_lir.h"
 #include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/reg_storage_eq.h"
+#include "driver/compiler_driver.h"
+#include "x86_lir.h"
 
 namespace art {
 
@@ -509,7 +512,7 @@
     }
   }
   if (r_dest != r_src) {
-    if (false && op == kOpLsl && value >= 0 && value <= 3) {  // lea shift special case
+    if ((false) && op == kOpLsl && value >= 0 && value <= 3) {  // lea shift special case
       // TODO: fix bug in LEA encoding when disp == 0
       return NewLIR5(kX86Lea32RA, r_dest.GetReg(),  r5sib_no_base /* base */,
                      r_src.GetReg() /* index */, value /* scale */, 0 /* disp */);
@@ -570,32 +573,36 @@
     if (is_fp) {
       DCHECK(r_dest.IsDouble());
       if (value == 0) {
-        return NewLIR2(kX86XorpsRR, low_reg_val, low_reg_val);
-      } else if (base_of_code_ != nullptr) {
+        return NewLIR2(kX86XorpdRR, low_reg_val, low_reg_val);
+      } else if (base_of_code_ != nullptr || cu_->target64) {
         // We will load the value from the literal area.
         LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
         if (data_target == NULL) {
           data_target = AddWideData(&literal_list_, val_lo, val_hi);
         }
 
-        // Address the start of the method
-        RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
-        if (rl_method.wide) {
-          rl_method = LoadValueWide(rl_method, kCoreReg);
-        } else {
-          rl_method = LoadValue(rl_method, kCoreReg);
-        }
-
         // Load the proper value from the literal area.
-        // We don't know the proper offset for the value, so pick one that will force
-        // 4 byte offset.  We will fix this up in the assembler later to have the right
-        // value.
+        // We don't know the proper offset for the value, so pick one that
+        // will force 4 byte offset.  We will fix this up in the assembler
+        // later to have the right value.
         ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
-        res = LoadBaseDisp(rl_method.reg, 256 /* bogus */, RegStorage::FloatSolo64(low_reg_val),
-                           kDouble, kNotVolatile);
+        if (cu_->target64) {
+          res = NewLIR3(kX86MovsdRM, low_reg_val, kRIPReg, 256 /* bogus */);
+        } else {
+          // Address the start of the method.
+          RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+          if (rl_method.wide) {
+            rl_method = LoadValueWide(rl_method, kCoreReg);
+          } else {
+            rl_method = LoadValue(rl_method, kCoreReg);
+          }
+
+          res = LoadBaseDisp(rl_method.reg, 256 /* bogus */, RegStorage::FloatSolo64(low_reg_val),
+                             kDouble, kNotVolatile);
+          store_method_addr_used_ = true;
+        }
         res->target = data_target;
         res->flags.fixup = kFixupLoad;
-        store_method_addr_used_ = true;
       } else {
         if (r_dest.IsPair()) {
           if (val_lo == 0) {
@@ -960,12 +967,14 @@
     curr_bb = iter.Next();
   }
 
-  // Did we need a pointer to the method code?
+  // Did we need a pointer to the method code?  Not in 64 bit mode.
+  base_of_code_ = nullptr;
+
+  // store_method_addr_ must be false for x86_64, since RIP addressing is used.
+  CHECK(!(cu_->target64 && store_method_addr_));
   if (store_method_addr_) {
-    base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, cu_->target64 == true);
+    base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, false);
     DCHECK(base_of_code_ != nullptr);
-  } else {
-    base_of_code_ = nullptr;
   }
 }
 
@@ -994,19 +1003,22 @@
       AnalyzeFPInstruction(opcode, bb, mir);
       break;
     case kMirOpConstVector:
-      store_method_addr_ = true;
+      if (!cu_->target64) {
+        store_method_addr_ = true;
+      }
       break;
     case kMirOpPackedMultiply:
     case kMirOpPackedShiftLeft:
     case kMirOpPackedSignedShiftRight:
-    case kMirOpPackedUnsignedShiftRight: {
-      // Byte emulation requires constants from the literal pool.
-      OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
-      if (opsize == kSignedByte || opsize == kUnsignedByte) {
-        store_method_addr_ = true;
+    case kMirOpPackedUnsignedShiftRight:
+      if (!cu_->target64) {
+        // Byte emulation requires constants from the literal pool.
+        OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
+        if (opsize == kSignedByte || opsize == kUnsignedByte) {
+          store_method_addr_ = true;
+        }
       }
       break;
-    }
     default:
       // Ignore the rest.
       break;
@@ -1016,6 +1028,7 @@
 void X86Mir2Lir::AnalyzeMIR(int opcode, BasicBlock* bb, MIR* mir) {
   // Looking for
   // - Do we need a pointer to the code (used for packed switches and double lits)?
+  // 64 bit uses RIP addressing instead.
 
   switch (opcode) {
     // Instructions referencing doubles.
@@ -1038,7 +1051,9 @@
     // Packed switches and array fills need a pointer to the base of the method.
     case Instruction::FILL_ARRAY_DATA:
     case Instruction::PACKED_SWITCH:
-      store_method_addr_ = true;
+      if (!cu_->target64) {
+        store_method_addr_ = true;
+      }
       break;
     case Instruction::INVOKE_STATIC:
     case Instruction::INVOKE_STATIC_RANGE:
@@ -1115,7 +1130,8 @@
 
 void X86Mir2Lir::AnalyzeInvokeStatic(int opcode, BasicBlock* bb, MIR* mir) {
   UNUSED(opcode, bb);
-  // For now this is only actual for x86-32.
+
+  // 64 bit RIP addressing doesn't need store_method_addr_ set.
   if (cu_->target64) {
     return;
   }
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 76a67c4..7dea09a 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -17,7 +17,8 @@
 #ifndef ART_COMPILER_DEX_QUICK_X86_X86_LIR_H_
 #define ART_COMPILER_DEX_QUICK_X86_X86_LIR_H_
 
-#include "dex/compiler_internals.h"
+#include "dex/reg_location.h"
+#include "dex/reg_storage.h"
 
 namespace art {
 
@@ -56,15 +57,15 @@
  * x86-64/x32 gs: holds it.
  *
  * For floating point we don't support CPUs without SSE2 support (ie newer than PIII):
- *  Native: x86  | x86-64 / x32 | ART x86                    | ART x86-64
- *  XMM0: caller | caller, arg1 | caller, float return value | caller, arg1, float return value
- *  XMM1: caller | caller, arg2 | caller, scratch            | caller, arg2, scratch
- *  XMM2: caller | caller, arg3 | caller, scratch            | caller, arg3, scratch
- *  XMM3: caller | caller, arg4 | caller, scratch            | caller, arg4, scratch
- *  XMM4: caller | caller, arg5 | caller, scratch            | caller, arg5, scratch
- *  XMM5: caller | caller, arg6 | caller, scratch            | caller, arg6, scratch
- *  XMM6: caller | caller, arg7 | caller, scratch            | caller, arg7, scratch
- *  XMM7: caller | caller, arg8 | caller, scratch            | caller, arg8, scratch
+ *  Native: x86  | x86-64 / x32 | ART x86                          | ART x86-64
+ *  XMM0: caller | caller, arg1 | caller, arg1, float return value | caller, arg1, float return value
+ *  XMM1: caller | caller, arg2 | caller, arg2, scratch            | caller, arg2, scratch
+ *  XMM2: caller | caller, arg3 | caller, arg3, scratch            | caller, arg3, scratch
+ *  XMM3: caller | caller, arg4 | caller, arg4, scratch            | caller, arg4, scratch
+ *  XMM4: caller | caller, arg5 | caller, scratch                  | caller, arg5, scratch
+ *  XMM5: caller | caller, arg6 | caller, scratch                  | caller, arg6, scratch
+ *  XMM6: caller | caller, arg7 | caller, scratch                  | caller, arg7, scratch
+ *  XMM7: caller | caller, arg8 | caller, scratch                  | caller, arg8, scratch
  *  ---  x86-64/x32 registers
  *  XMM8 .. 11: caller save available as scratch registers for ART.
  *  XMM12 .. 15: callee save available as promoted registers for ART.
@@ -217,6 +218,9 @@
   xr14 = RegStorage::k128BitSolo | 14,
   xr15 = RegStorage::k128BitSolo | 15,
 
+  // Special value for RIP 64 bit addressing.
+  kRIPReg = 255,
+
   // TODO: as needed, add 256, 512 and 1024-bit xmm views.
 };
 
diff --git a/compiler/dex/reg_location.h b/compiler/dex/reg_location.h
index 38f59da..aa8ed46 100644
--- a/compiler/dex/reg_location.h
+++ b/compiler/dex/reg_location.h
@@ -21,6 +21,7 @@
 
 namespace art {
 
+static constexpr int16_t INVALID_SREG = -1;
 
 /*
  * Whereas a SSA name describes a definition of a Dalvik vreg, the RegLocation describes
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index ed33882..f15f9be 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -15,7 +15,8 @@
  */
 
 #include "base/bit_vector-inl.h"
-#include "compiler_internals.h"
+#include "base/logging.h"
+#include "compiler_ir.h"
 #include "dataflow_iterator-inl.h"
 #include "utils/scoped_arena_containers.h"
 
@@ -103,12 +104,12 @@
 
   num_reachable_blocks_ = dfs_order_.size();
 
-  if (num_reachable_blocks_ != num_blocks_) {
-    // Hide all unreachable blocks.
+  if (num_reachable_blocks_ != GetNumBlocks()) {
+    // Kill all unreachable blocks.
     AllNodesIterator iter(this);
     for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
       if (!bb->visited) {
-        bb->Hide(this);
+        bb->Kill(this);
       }
     }
   }
@@ -173,9 +174,9 @@
   dom_post_order_traversal_.reserve(num_reachable_blocks_);
 
   ClearAllVisitedFlags();
-  DCHECK(temp_scoped_alloc_.get() != nullptr);
+  ScopedArenaAllocator allocator(&cu_->arena_stack);
   ScopedArenaVector<std::pair<BasicBlock*, ArenaBitVector::IndexIterator>> work_stack(
-      temp_scoped_alloc_->Adapter());
+      allocator.Adapter());
   bb->visited = true;
   work_stack.push_back(std::make_pair(bb, bb->i_dominated->Indexes().begin()));
   while (!work_stack.empty()) {
@@ -197,12 +198,6 @@
         dom_post_order_traversal_.push_back(curr_bb->id);
       }
       work_stack.pop_back();
-
-      /* hacky loop detection */
-      if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) {
-        curr_bb->nesting_depth++;
-        attributes_ |= METHOD_HAS_LOOP;
-      }
     }
   }
 }
@@ -408,6 +403,8 @@
   for (BasicBlock* bb = iter5.Next(); bb != NULL; bb = iter5.Next()) {
     ComputeDominanceFrontier(bb);
   }
+
+  domination_up_to_date_ = true;
 }
 
 /*
@@ -466,24 +463,28 @@
   return false;
 }
 
-/* Insert phi nodes to for each variable to the dominance frontiers */
-void MIRGraph::InsertPhiNodes() {
-  int dalvik_reg;
-  ArenaBitVector* phi_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector(
-      temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapPhi);
-  ArenaBitVector* input_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector(
-      temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapInputBlocks);
-
+/* For each dalvik reg, find blocks that need phi nodes according to the dominance frontiers. */
+void MIRGraph::FindPhiNodeBlocks() {
   RepeatingPostOrderDfsIterator iter(this);
   bool change = false;
   for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
     change = ComputeBlockLiveIns(bb);
   }
 
+  ArenaBitVector* phi_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector(
+      temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapBMatrix);
+
+  // Reuse the def_block_matrix storage for phi_node_blocks.
+  ArenaBitVector** def_block_matrix = temp_.ssa.def_block_matrix;
+  ArenaBitVector** phi_node_blocks = def_block_matrix;
+  DCHECK(temp_.ssa.phi_node_blocks == nullptr);
+  temp_.ssa.phi_node_blocks = phi_node_blocks;
+  temp_.ssa.def_block_matrix = nullptr;
+
   /* Iterate through each Dalvik register */
-  for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
-    input_blocks->Copy(temp_.ssa.def_block_matrix[dalvik_reg]);
+  for (int dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
     phi_blocks->ClearAllBits();
+    ArenaBitVector* input_blocks = def_block_matrix[dalvik_reg];
     do {
       // TUNING: When we repeat this, we could skip indexes from the previous pass.
       for (uint32_t idx : input_blocks->Indexes()) {
@@ -494,23 +495,8 @@
       }
     } while (input_blocks->Union(phi_blocks));
 
-    /*
-     * Insert a phi node for dalvik_reg in the phi_blocks if the Dalvik
-     * register is in the live-in set.
-     */
-    for (uint32_t idx : phi_blocks->Indexes()) {
-      BasicBlock* phi_bb = GetBasicBlock(idx);
-      /* Variable will be clobbered before being used - no need for phi */
-      if (!phi_bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) {
-        continue;
-      }
-      MIR *phi = NewMIR();
-      phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi);
-      phi->dalvikInsn.vA = dalvik_reg;
-      phi->offset = phi_bb->start_offset;
-      phi->m_unit_index = 0;  // Arbitrarily assign all Phi nodes to outermost method.
-      phi_bb->PrependMIR(phi);
-    }
+    def_block_matrix[dalvik_reg] = phi_blocks;
+    phi_blocks = input_blocks;  // Reuse the bit vector in next iteration.
   }
 }
 
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 4929b5b..4ff173d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -16,15 +16,14 @@
 
 #include "verification_results.h"
 
+#include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/mutex.h"
 #include "base/mutex-inl.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "thread.h"
 #include "thread-inl.h"
 #include "verified_method.h"
-#include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
 namespace art {
@@ -57,8 +56,8 @@
 
   const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
   if (verified_method == nullptr) {
-    DCHECK(method_verifier->HasFailures());
-    return false;
+    // Do not report an error to the verifier. We'll just punt this later.
+    return true;
   }
 
   WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -84,6 +83,15 @@
   return (it != verified_methods_.end()) ? it->second : nullptr;
 }
 
+void VerificationResults::RemoveVerifiedMethod(MethodReference ref) {
+  WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
+  auto it = verified_methods_.find(ref);
+  if (it != verified_methods_.end()) {
+    delete it->second;
+    verified_methods_.erase(it);
+  }
+}
+
 void VerificationResults::AddRejectedClass(ClassReference ref) {
   {
     WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
@@ -97,18 +105,8 @@
   return (rejected_classes_.find(ref) != rejected_classes_.end());
 }
 
-bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref,
+bool VerificationResults::IsCandidateForCompilation(MethodReference&,
                                                     const uint32_t access_flags) {
-#ifdef ART_SEA_IR_MODE
-  bool use_sea = compiler_options_->GetSeaIrMode();
-  use_sea = use_sea && (std::string::npos != PrettyMethod(
-                        method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
-  if (use_sea) {
-    return true;
-  }
-#else
-  UNUSED(method_ref);
-#endif
   if (!compiler_options_->IsCompilationEnabled()) {
     return false;
   }
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 0e7923f..7fc2a23 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -48,6 +48,7 @@
 
     const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
         LOCKS_EXCLUDED(verified_methods_lock_);
+    void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_);
 
     void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
     bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 17328c4..21e965d 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -23,20 +23,13 @@
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "dex_file.h"
-#include "dex_instruction.h"
 #include "dex_instruction-inl.h"
-#include "base/mutex.h"
-#include "base/mutex-inl.h"
-#include "mirror/art_method.h"
 #include "mirror/art_method-inl.h"
-#include "mirror/class.h"
 #include "mirror/class-inl.h"
-#include "mirror/dex_cache.h"
 #include "mirror/dex_cache-inl.h"
-#include "mirror/object.h"
 #include "mirror/object-inl.h"
+#include "utils.h"
 #include "verifier/dex_gc_map.h"
-#include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 #include "verifier/reg_type-inl.h"
 #include "verifier/register_line-inl.h"
@@ -49,7 +42,6 @@
   if (compile) {
     /* Generate a register map. */
     if (!verified_method->GenerateGcMap(method_verifier)) {
-      CHECK(method_verifier->HasFailures());
       return nullptr;  // Not a real failure, but a failure to encode.
     }
     if (kIsDebugBuild) {
@@ -82,33 +74,33 @@
   size_t num_entries, ref_bitmap_bits, pc_bits;
   ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits);
   // There's a single byte to encode the size of each bitmap.
-  if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
-    // TODO: either a better GC map format or per method failures
-    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers";
+  if (ref_bitmap_bits >= kBitsPerByte * 8192 /* 13-bit size */) {
+    LOG(WARNING) << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers: "
+                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+                                 *method_verifier->GetMethodReference().dex_file);
     return false;
   }
-  size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
+  size_t ref_bitmap_bytes = RoundUp(ref_bitmap_bits, kBitsPerByte) / kBitsPerByte;
   // There are 2 bytes to encode the number of entries.
   if (num_entries >= 65536) {
-    // TODO: Either a better GC map format or per method failures.
-    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Cannot encode GC map for method with " << num_entries << " entries";
+    LOG(WARNING) << "Cannot encode GC map for method with " << num_entries << " entries: "
+                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+                                 *method_verifier->GetMethodReference().dex_file);
     return false;
   }
   size_t pc_bytes;
   verifier::RegisterMapFormat format;
-  if (pc_bits <= 8) {
+  if (pc_bits <= kBitsPerByte) {
     format = verifier::kRegMapFormatCompact8;
     pc_bytes = 1;
-  } else if (pc_bits <= 16) {
+  } else if (pc_bits <= kBitsPerByte * 2) {
     format = verifier::kRegMapFormatCompact16;
     pc_bytes = 2;
   } else {
-    // TODO: Either a better GC map format or per method failures.
-    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
-        << "Cannot encode GC map for method with "
-        << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
+    LOG(WARNING) << "Cannot encode GC map for method with "
+                 << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2): "
+                 << PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
+                                 *method_verifier->GetMethodReference().dex_file);
     return false;
   }
   size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
@@ -152,16 +144,16 @@
       verifier::RegisterLine* line = method_verifier->GetRegLine(i);
       for (size_t j = 0; j < code_item->registers_size_; j++) {
         if (line->GetRegisterType(method_verifier, j).IsNonZeroReferenceTypes()) {
-          DCHECK_LT(j / 8, map.RegWidth());
-          DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
-        } else if ((j / 8) < map.RegWidth()) {
-          DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
+          DCHECK_LT(j / kBitsPerByte, map.RegWidth());
+          DCHECK_EQ((reg_bitmap[j / kBitsPerByte] >> (j % kBitsPerByte)) & 1, 1);
+        } else if ((j / kBitsPerByte) < map.RegWidth()) {
+          DCHECK_EQ((reg_bitmap[j / kBitsPerByte] >> (j % kBitsPerByte)) & 1, 0);
         } else {
           // If a register doesn't contain a reference then the bitmap may be shorter than the line.
         }
       }
     } else {
-      DCHECK(reg_bitmap == NULL);
+      DCHECK(i >= 65536 || reg_bitmap == NULL);
     }
   }
 }
@@ -190,6 +182,31 @@
   *log2_max_gc_pc = i;
 }
 
+void VerifiedMethod::GenerateDeQuickenMap(verifier::MethodVerifier* method_verifier) {
+  if (method_verifier->HasFailures()) {
+    return;
+  }
+  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+  const uint16_t* insns = code_item->insns_;
+  const Instruction* inst = Instruction::At(insns);
+  const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
+  for (; inst < end; inst = inst->Next()) {
+    const bool is_virtual_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK;
+    const bool is_range_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
+    if (is_virtual_quick || is_range_quick) {
+      uint32_t dex_pc = inst->GetDexPc(insns);
+      verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
+      mirror::ArtMethod* method = method_verifier->GetQuickInvokedMethod(inst, line,
+                                                                         is_range_quick);
+      CHECK(method != nullptr);
+      // The verifier must know what the type of the object was or else we would have gotten a
+      // failure. Put the dex method index in the dequicken map since we need this to get number of
+      // arguments in the compiler.
+      dequicken_map_.Put(dex_pc, method->ToMethodReference());
+    }
+  }
+}
+
 void VerifiedMethod::GenerateDevirtMap(verifier::MethodVerifier* method_verifier) {
   // It is risky to rely on reg_types for sharpening in cases of soft
   // verification, we might end up sharpening to a wrong implementation. Just abort.
@@ -203,10 +220,10 @@
   const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
 
   for (; inst < end; inst = inst->Next()) {
-    bool is_virtual   = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
-        (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE);
-    bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
-        (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
+    const bool is_virtual = inst->Opcode() == Instruction::INVOKE_VIRTUAL ||
+        inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE;
+    const bool is_interface = inst->Opcode() == Instruction::INVOKE_INTERFACE ||
+        inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
 
     if (!is_interface && !is_virtual) {
       continue;
@@ -214,8 +231,8 @@
     // Get reg type for register holding the reference to the object that will be dispatched upon.
     uint32_t dex_pc = inst->GetDexPc(insns);
     verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
-    bool is_range = (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE) ||
-        (inst->Opcode() ==  Instruction::INVOKE_INTERFACE_RANGE);
+    const bool is_range = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE ||
+        inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE;
     const verifier::RegType&
         reg_type(line->GetRegisterType(method_verifier,
                                        is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
@@ -241,14 +258,14 @@
       continue;
     }
     // Find the concrete method.
-    mirror::ArtMethod* concrete_method = NULL;
+    mirror::ArtMethod* concrete_method = nullptr;
     if (is_interface) {
       concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
     }
     if (is_virtual) {
       concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
     }
-    if (concrete_method == NULL || concrete_method->IsAbstract()) {
+    if (concrete_method == nullptr || concrete_method->IsAbstract()) {
       // In cases where concrete_method is not found, or is abstract, continue to the next invoke.
       continue;
     }
@@ -256,10 +273,7 @@
         concrete_method->GetDeclaringClass()->IsFinal()) {
       // If we knew exactly the class being dispatched upon, or if the target method cannot be
       // overridden record the target to be used in the compiler driver.
-      MethodReference concrete_ref(
-          concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
-          concrete_method->GetDexMethodIndex());
-      devirt_map_.Put(dex_pc, concrete_ref);
+      devirt_map_.Put(dex_pc, concrete_method->ToMethodReference());
     }
   }
 }
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 257e70c..fe9dfd1 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -85,12 +85,19 @@
   void GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Generate dequickening map into dequicken_map_.
+  void GenerateDeQuickenMap(verifier::MethodVerifier* method_verifier)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Generate safe case set into safe_cast_set_.
   void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   std::vector<uint8_t> dex_gc_map_;
   DevirtualizationMap devirt_map_;
+  // Dequicken map is required for having the compiler compiled quickened invokes. The quicken map
+  // enables us to get the dex method index so that we can get the required argument count.
+  DevirtualizationMap dequicken_map_;
   SafeCastSet safe_cast_set_;
 };
 
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index a541c7d..f70850a 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-#include "compiler_internals.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "compiler_ir.h"
 #include "dex/dataflow_iterator-inl.h"
+#include "dex_flags.h"
 
 namespace art {
 
@@ -442,7 +445,7 @@
   for (int i = 0; i < GetNumSSARegs(); i++) {
     loc[i] = fresh_loc;
     loc[i].s_reg_low = i;
-    loc[i].is_const = is_constant_v_->IsBitSet(i);
+    loc[i].is_const = false;  // Constants will be marked by constant propagation pass later.
     loc[i].wide = false;
   }
 
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 3a91b08..9948c82 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -19,7 +19,6 @@
 
 #include "compiler_driver.h"
 
-#include "dex/compiler_ir.h"
 #include "dex_compilation_unit.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index ab9f41a..b8a8936 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -23,6 +23,10 @@
 #include <vector>
 #include <unistd.h>
 
+#ifndef __APPLE__
+#include <malloc.h>  // For mallinfo
+#endif
+
 #include "base/stl_util.h"
 #include "base/timing_logger.h"
 #include "class_linker.h"
@@ -58,6 +62,7 @@
 #include "thread_pool.h"
 #include "trampolines/trampoline_compiler.h"
 #include "transaction.h"
+#include "utils/swap_space.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
@@ -334,9 +339,12 @@
                                const InstructionSetFeatures* instruction_set_features,
                                bool image, std::set<std::string>* image_classes,
                                std::set<std::string>* compiled_classes, size_t thread_count,
-                               bool dump_stats, bool dump_passes, CumulativeLogger* timer,
-                               const std::string& profile_file)
-    : profile_present_(false), compiler_options_(compiler_options),
+                               bool dump_stats, bool dump_passes,
+                               const std::string& dump_cfg_file_name, CumulativeLogger* timer,
+                               int swap_fd, const std::string& profile_file)
+    : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+      swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
+      profile_present_(false), compiler_options_(compiler_options),
       verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
       compiler_(Compiler::Create(this, compiler_kind)),
@@ -345,7 +353,7 @@
       freezing_constructor_lock_("freezing constructor lock"),
       compiled_classes_lock_("compiled classes lock"),
       compiled_methods_lock_("compiled method lock"),
-      compiled_methods_(),
+      compiled_methods_(MethodTable::key_compare()),
       non_relative_linker_patch_count_(0u),
       image_(image),
       image_classes_(image_classes),
@@ -354,21 +362,20 @@
       stats_(new AOTCompilationStats),
       dump_stats_(dump_stats),
       dump_passes_(dump_passes),
+      dump_cfg_file_name_(dump_cfg_file_name),
       timings_logger_(timer),
       compiler_context_(nullptr),
-      support_boot_image_fixup_(instruction_set != kMips),
-      dedupe_code_("dedupe code"),
-      dedupe_src_mapping_table_("dedupe source mapping table"),
-      dedupe_mapping_table_("dedupe mapping table"),
-      dedupe_vmap_table_("dedupe vmap table"),
-      dedupe_gc_map_("dedupe gc map"),
-      dedupe_cfi_info_("dedupe cfi info") {
+      support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
+      dedupe_code_("dedupe code", *swap_space_allocator_),
+      dedupe_src_mapping_table_("dedupe source mapping table", *swap_space_allocator_),
+      dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_),
+      dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_),
+      dedupe_gc_map_("dedupe gc map", *swap_space_allocator_),
+      dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) {
   DCHECK(compiler_options_ != nullptr);
   DCHECK(verification_results_ != nullptr);
   DCHECK(method_inliner_map_ != nullptr);
 
-  CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, nullptr), "compiler tls key");
-
   dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX);
 
   compiler_->Init();
@@ -391,31 +398,28 @@
   }
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCode(const ArrayRef<const uint8_t>& code) {
   return dedupe_code_.Add(Thread::Current(), code);
 }
 
-SrcMap* CompilerDriver::DeduplicateSrcMappingTable(const SrcMap& src_map) {
+SwapSrcMap* CompilerDriver::DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map) {
   return dedupe_src_mapping_table_.Add(Thread::Current(), src_map);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const ArrayRef<const uint8_t>& code) {
   return dedupe_mapping_table_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const ArrayRef<const uint8_t>& code) {
   return dedupe_vmap_table_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateGCMap(const ArrayRef<const uint8_t>& code) {
   return dedupe_gc_map_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) {
-  if (cfi_info == nullptr) {
-    return nullptr;
-  }
-  return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info);
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info) {
+  return dedupe_cfi_info_.Add(Thread::Current(), cfi_info);
 }
 
 CompilerDriver::~CompilerDriver() {
@@ -426,22 +430,13 @@
   }
   {
     MutexLock mu(self, compiled_methods_lock_);
-    STLDeleteValues(&compiled_methods_);
+    for (auto& pair : compiled_methods_) {
+      CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, pair.second);
+    }
   }
-  CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key");
   compiler_->UnInit();
 }
 
-CompilerTls* CompilerDriver::GetTls() {
-  // Lazily create thread-local storage
-  CompilerTls* res = static_cast<CompilerTls*>(pthread_getspecific(tls_key_));
-  if (res == nullptr) {
-    res = compiler_->CreateNewCompilerTls();
-    CHECK_PTHREAD_CALL(pthread_setspecific, (tls_key_, res), "compiler tls");
-  }
-  return res;
-}
-
 #define CREATE_TRAMPOLINE(type, abi, offset) \
     if (Is64BitInstructionSet(instruction_set_)) { \
       return CreateTrampoline64(instruction_set_, abi, \
@@ -463,18 +458,6 @@
   CREATE_TRAMPOLINE(JNI, kJniAbi, pDlsymLookup)
 }
 
-const std::vector<uint8_t>* CompilerDriver::CreatePortableImtConflictTrampoline() const {
-  CREATE_TRAMPOLINE(PORTABLE, kPortableAbi, pPortableImtConflictTrampoline)
-}
-
-const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
-  CREATE_TRAMPOLINE(PORTABLE, kPortableAbi, pPortableResolutionTrampoline)
-}
-
-const std::vector<uint8_t>* CompilerDriver::CreatePortableToInterpreterBridge() const {
-  CREATE_TRAMPOLINE(PORTABLE, kPortableAbi, pPortableToInterpreterBridge)
-}
-
 const std::vector<uint8_t>* CompilerDriver::CreateQuickGenericJniTrampoline() const {
   CREATE_TRAMPOLINE(QUICK, kQuickAbi, pQuickGenericJniTrampoline)
 }
@@ -497,6 +480,7 @@
                                 TimingLogger* timings) {
   DCHECK(!Runtime::Current()->IsStarted());
   std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
+  VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
   PreCompile(class_loader, dex_files, thread_pool.get(), timings);
   Compile(class_loader, dex_files, thread_pool.get(), timings);
   if (dump_stats_) {
@@ -593,20 +577,25 @@
 void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                                 ThreadPool* thread_pool, TimingLogger* timings) {
   LoadImageClasses(timings);
+  VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
 
   Resolve(class_loader, dex_files, thread_pool, timings);
+  VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
 
   if (!compiler_options_->IsVerificationEnabled()) {
-    LOG(INFO) << "Verify none mode specified, skipping verification.";
+    VLOG(compiler) << "Verify none mode specified, skipping verification.";
     SetVerified(class_loader, dex_files, thread_pool, timings);
     return;
   }
 
   Verify(class_loader, dex_files, thread_pool, timings);
+  VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
 
   InitializeClasses(class_loader, dex_files, thread_pool, timings);
+  VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
 
   UpdateImageClasses(timings);
+  VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
 }
 
 bool CompilerDriver::IsImageClass(const char* descriptor) const {
@@ -1273,18 +1262,11 @@
   // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
   const bool force_relocations = (compiling_boot ||
                                   GetCompilerOptions().GetIncludePatchInformation());
-  if (compiler_->IsPortable()) {
-    if (sharp_type != kStatic && sharp_type != kDirect) {
-      return;
-    }
-    use_dex_cache = true;
-  } else {
-    if (sharp_type != kStatic && sharp_type != kDirect) {
-      return;
-    }
-    // TODO: support patching on all architectures.
-    use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
+  if (sharp_type != kStatic && sharp_type != kDirect) {
+    return;
   }
+  // TODO: support patching on all architectures.
+  use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
   bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
   if (!use_dex_cache) {
     if (!method_code_in_boot) {
@@ -1303,7 +1285,15 @@
     *stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
   }
   if (!use_dex_cache && force_relocations) {
-    if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
+    bool is_in_image;
+    if (IsImage()) {
+      is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
+    } else {
+      is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
+                    Runtime::Current()->GetHeap()->FindSpaceFromObject(method->GetDeclaringClass(),
+                                                                       false)->IsImageSpace();
+    }
+    if (!is_in_image) {
       // We can only branch directly to Methods that are resolved in the DexCache.
       // Otherwise we won't invoke the resolution trampoline.
       use_dex_cache = true;
@@ -1395,8 +1385,7 @@
   if (resolved_method != nullptr) {
     *vtable_idx = GetResolvedMethodVTableIndex(resolved_method, orig_invoke_type);
 
-    if (enable_devirtualization) {
-      DCHECK(mUnit->GetVerifiedMethod() != nullptr);
+    if (enable_devirtualization && mUnit->GetVerifiedMethod() != nullptr) {
       const MethodReference* devirt_target = mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc);
 
       stats_flags = IsFastInvoke(
@@ -1948,7 +1937,7 @@
                 *file_log << exception->Dump() << "\n";
               }
               soa.Self()->ClearException();
-              transaction.Abort();
+              transaction.Rollback();
               CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
             }
           }
@@ -2002,6 +1991,7 @@
     CHECK(dex_file != nullptr);
     CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
   }
+  VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
 }
 
 void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) {
@@ -2114,6 +2104,7 @@
     case kArm64:
     case kThumb2:
     case kMips:
+    case kMips64:
     case kX86:
     case kX86_64: return true;
     default: return false;
@@ -2128,6 +2119,7 @@
                                    bool compilation_enabled) {
   CompiledMethod* compiled_method = nullptr;
   uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
+  MethodReference method_ref(&dex_file, method_idx);
 
   if ((access_flags & kAccNative) != 0) {
     // Are we interpreting only and have support for generic JNI down calls?
@@ -2141,9 +2133,12 @@
   } else if ((access_flags & kAccAbstract) != 0) {
     // Abstract methods don't have code.
   } else {
-    MethodReference method_ref(&dex_file, method_idx);
+    bool has_verified_method = verification_results_->GetVerifiedMethod(method_ref) != nullptr;
     bool compile = compilation_enabled &&
-                   verification_results_->IsCandidateForCompilation(method_ref, access_flags);
+                   // Basic checks, e.g., not <clinit>.
+                   verification_results_->IsCandidateForCompilation(method_ref, access_flags) &&
+                   // Did not fail to create VerifiedMethod metadata.
+                   has_verified_method;
     if (compile) {
       // NOTE: if compiler declines to compile this method, it will return nullptr.
       compiled_method = compiler_->Compile(code_item, access_flags, invoke_type, class_def_idx,
@@ -2151,10 +2146,12 @@
     }
     if (compiled_method == nullptr && dex_to_dex_compilation_level != kDontDexToDexCompile) {
       // TODO: add a command-line option to disable DEX-to-DEX compilation ?
+      // Do not optimize if a VerifiedMethod is missing. SafeCast elision, for example, relies on
+      // it.
       (*dex_to_dex_compiler_)(*this, code_item, access_flags,
                               invoke_type, class_def_idx,
                               method_idx, class_loader, dex_file,
-                              dex_to_dex_compilation_level);
+                              has_verified_method ? dex_to_dex_compilation_level : kRequired);
     }
   }
   if (kTimeCompileMethod) {
@@ -2178,16 +2175,18 @@
     // When compiling with PIC, there should be zero non-relative linker patches
     CHECK(!compile_pic || non_relative_linker_patch_count == 0u);
 
-    MethodReference ref(&dex_file, method_idx);
-    DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file);
+    DCHECK(GetCompiledMethod(method_ref) == nullptr) << PrettyMethod(method_idx, dex_file);
     {
       MutexLock mu(self, compiled_methods_lock_);
-      compiled_methods_.Put(ref, compiled_method);
+      compiled_methods_.Put(method_ref, compiled_method);
       non_relative_linker_patch_count_ += non_relative_linker_patch_count;
     }
-    DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file);
+    DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file);
   }
 
+  // Done compiling, delete the verified method to reduce native memory usage.
+  verification_results_->RemoveVerifiedMethod(method_ref);
+
   if (self->IsExceptionPending()) {
     ScopedObjectAccess soa(self);
     LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n"
@@ -2337,4 +2336,31 @@
   }
   return !compile;
 }
+
+std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
+  std::ostringstream oss;
+  const ArenaPool* arena_pool = GetArenaPool();
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated());
+  oss << " java alloc=" << PrettySize(heap->GetBytesAllocated());
+#ifdef HAVE_MALLOC_H
+  struct mallinfo info = mallinfo();
+  const size_t allocated_space = static_cast<size_t>(info.uordblks);
+  const size_t free_space = static_cast<size_t>(info.fordblks);
+  oss << " native alloc=" << PrettySize(allocated_space) << " free="
+      << PrettySize(free_space);
+#endif
+  if (swap_space_.get() != nullptr) {
+    oss << " swap=" << PrettySize(swap_space_->GetSize());
+  }
+  if (extended) {
+    oss << "\nCode dedupe: " << dedupe_code_.DumpStats();
+    oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats();
+    oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats();
+    oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats();
+    oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats();
+  }
+  return oss.str();
+}
+
 }  // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index d837dbc..2fca2e5 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -28,6 +28,7 @@
 #include "compiled_method.h"
 #include "compiler.h"
 #include "dex_file.h"
+#include "dex/verified_method.h"
 #include "driver/compiler_options.h"
 #include "invoke_type.h"
 #include "method_reference.h"
@@ -39,6 +40,8 @@
 #include "thread_pool.h"
 #include "utils/arena_allocator.h"
 #include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
+#include "utils.h"
 
 namespace art {
 
@@ -65,8 +68,6 @@
   kInterpreterAbi,
   // ABI of calls to a method's native code, only used for native methods.
   kJniAbi,
-  // ABI of calls to a method's portable code entry point.
-  kPortableAbi,
   // ABI of calls to a method's quick code entry point.
   kQuickAbi
 };
@@ -78,6 +79,8 @@
 };
 std::ostream& operator<<(std::ostream& os, const DexToDexCompilationLevel& rhs);
 
+static constexpr bool kUseMurmur3Hash = true;
+
 class CompilerDriver {
  public:
   // Create a compiler targeting the requested "instruction_set".
@@ -94,7 +97,9 @@
                           bool image, std::set<std::string>* image_classes,
                           std::set<std::string>* compiled_classes,
                           size_t thread_count, bool dump_stats, bool dump_passes,
-                          CumulativeLogger* timer, const std::string& profile_file);
+                          const std::string& dump_cfg_file_name,
+                          CumulativeLogger* timer, int swap_fd,
+                          const std::string& profile_file);
 
   ~CompilerDriver();
 
@@ -143,8 +148,6 @@
     return image_classes_.get();
   }
 
-  CompilerTls* GetTls();
-
   // Generate the trampolines that are invoked by unresolved direct methods.
   const std::vector<uint8_t>* CreateInterpreterToInterpreterBridge() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -152,12 +155,6 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::vector<uint8_t>* CreateJniDlsymLookup() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreatePortableImtConflictTrampoline() const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const std::vector<uint8_t>* CreatePortableToInterpreterBridge() const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
@@ -343,6 +340,9 @@
   const ArenaPool* GetArenaPool() const {
     return &arena_pool_;
   }
+  SwapAllocator<void>& GetSwapSpaceAllocator() {
+    return *swap_space_allocator_.get();
+  }
 
   bool WriteElf(const std::string& android_root,
                 bool is_host,
@@ -372,6 +372,10 @@
     return dump_passes_;
   }
 
+  const std::string& GetDumpCfgFileName() const {
+    return dump_cfg_file_name_;
+  }
+
   CumulativeLogger* GetTimingsLogger() const {
     return timings_logger_;
   }
@@ -385,19 +389,19 @@
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
-  std::vector<uint8_t>* DeduplicateCode(const std::vector<uint8_t>& code);
-  SrcMap* DeduplicateSrcMappingTable(const SrcMap& src_map);
-  std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
-
-  ProfileFile profile_file_;
-  bool profile_present_;
+  SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+  SwapSrcMap* DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map);
+  SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
 
   // Should the compiler run on this method given profile information?
   bool SkipCompilation(const std::string& method_name);
 
+  // Get memory usage during compilation.
+  std::string GetMemoryUsageString(bool extended) const;
+
  private:
   // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics.
   // The only external contract is that unresolved method has flags 0 and resolved non-0.
@@ -490,6 +494,14 @@
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
+  // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+  // as other fields rely on this.
+  std::unique_ptr<SwapSpace> swap_space_;
+  std::unique_ptr<SwapAllocator<void> > swap_space_allocator_;
+
+  ProfileFile profile_file_;
+  bool profile_present_;
+
   const CompilerOptions* const compiler_options_;
   VerificationResults* const verification_results_;
   DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -535,6 +547,7 @@
 
   bool dump_stats_;
   const bool dump_passes_;
+  const std::string& dump_cfg_file_name_;
 
   CumulativeLogger* const timings_logger_;
 
@@ -551,55 +564,98 @@
 
   void* compiler_context_;
 
-  pthread_key_t tls_key_;
-
   // Arena pool used by the compiler.
   ArenaPool arena_pool_;
 
   bool support_boot_image_fixup_;
 
   // DeDuplication data structures, these own the corresponding byte arrays.
-  template <typename ByteArray>
+  template <typename ContentType>
   class DedupeHashFunc {
    public:
-    size_t operator()(const ByteArray& array) const {
-      // For small arrays compute a hash using every byte.
-      static const size_t kSmallArrayThreshold = 16;
-      size_t hash = 0x811c9dc5;
-      if (array.size() <= kSmallArrayThreshold) {
-        for (auto b : array) {
-          hash = (hash * 16777619) ^ static_cast<uint8_t>(b);
+    size_t operator()(const ArrayRef<ContentType>& array) const {
+      const uint8_t* data = reinterpret_cast<const uint8_t*>(array.data());
+      static_assert(IsPowerOfTwo(sizeof(ContentType)),
+          "ContentType is not power of two, don't know whether array layout is as assumed");
+      uint32_t len = sizeof(ContentType) * array.size();
+      if (kUseMurmur3Hash) {
+        static constexpr uint32_t c1 = 0xcc9e2d51;
+        static constexpr uint32_t c2 = 0x1b873593;
+        static constexpr uint32_t r1 = 15;
+        static constexpr uint32_t r2 = 13;
+        static constexpr uint32_t m = 5;
+        static constexpr uint32_t n = 0xe6546b64;
+
+        uint32_t hash = 0;
+
+        const int nblocks = len / 4;
+        typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+        const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(data);
+        int i;
+        for (i = 0; i < nblocks; i++) {
+          uint32_t k = blocks[i];
+          k *= c1;
+          k = (k << r1) | (k >> (32 - r1));
+          k *= c2;
+
+          hash ^= k;
+          hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
         }
+
+        const uint8_t *tail = reinterpret_cast<const uint8_t*>(data + nblocks * 4);
+        uint32_t k1 = 0;
+
+        switch (len & 3) {
+          case 3:
+            k1 ^= tail[2] << 16;
+            FALLTHROUGH_INTENDED;
+          case 2:
+            k1 ^= tail[1] << 8;
+            FALLTHROUGH_INTENDED;
+          case 1:
+            k1 ^= tail[0];
+
+            k1 *= c1;
+            k1 = (k1 << r1) | (k1 >> (32 - r1));
+            k1 *= c2;
+            hash ^= k1;
+        }
+
+        hash ^= len;
+        hash ^= (hash >> 16);
+        hash *= 0x85ebca6b;
+        hash ^= (hash >> 13);
+        hash *= 0xc2b2ae35;
+        hash ^= (hash >> 16);
+
+        return hash;
       } else {
-        // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
-        // instruction field for quick generated code on ARM) and then select a number of other
-        // values at random.
-        static const size_t kRandomHashCount = 16;
-        for (size_t i = 0; i < 2; ++i) {
-          uint8_t b = static_cast<uint8_t>(array[i + 6]);
-          hash = (hash * 16777619) ^ b;
+        size_t hash = 0x811c9dc5;
+        for (uint32_t i = 0; i < len; ++i) {
+          hash = (hash * 16777619) ^ data[i];
         }
-        for (size_t i = 2; i < kRandomHashCount; ++i) {
-          size_t r = i * 1103515245 + 12345;
-          uint8_t b = static_cast<uint8_t>(array[r % array.size()]);
-          hash = (hash * 16777619) ^ b;
-        }
+        hash += hash << 13;
+        hash ^= hash >> 7;
+        hash += hash << 3;
+        hash ^= hash >> 17;
+        hash += hash << 5;
+        return hash;
       }
-      hash += hash << 13;
-      hash ^= hash >> 7;
-      hash += hash << 3;
-      hash ^= hash >> 17;
-      hash += hash << 5;
-      return hash;
     }
   };
 
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_code_;
-  DedupeSet<SrcMap, size_t, DedupeHashFunc<SrcMap>, 4> dedupe_src_mapping_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_mapping_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_vmap_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_gc_map_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_cfi_info_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_code_;
+  DedupeSet<ArrayRef<SrcMapElem>,
+            SwapSrcMap, size_t, DedupeHashFunc<SrcMapElem>, 4> dedupe_src_mapping_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_mapping_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_vmap_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_gc_map_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_cfi_info_;
 
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 5a0ec2f..a02e25e 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -106,40 +106,37 @@
 
   // All libcore references should resolve
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* dex = java_lang_dex_file_;
-  mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
-  EXPECT_EQ(dex->NumStringIds(), dex_cache->NumStrings());
+  ASSERT_TRUE(java_lang_dex_file_ != NULL);
+  const DexFile& dex = *java_lang_dex_file_;
+  mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
+  EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings());
   for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
     const mirror::String* string = dex_cache->GetResolvedString(i);
     EXPECT_TRUE(string != NULL) << "string_idx=" << i;
   }
-  EXPECT_EQ(dex->NumTypeIds(), dex_cache->NumResolvedTypes());
+  EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
   for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
     mirror::Class* type = dex_cache->GetResolvedType(i);
     EXPECT_TRUE(type != NULL) << "type_idx=" << i
-                              << " " << dex->GetTypeDescriptor(dex->GetTypeId(i));
+                              << " " << dex.GetTypeDescriptor(dex.GetTypeId(i));
   }
-  EXPECT_EQ(dex->NumMethodIds(), dex_cache->NumResolvedMethods());
+  EXPECT_EQ(dex.NumMethodIds(), dex_cache->NumResolvedMethods());
   for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
     mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i);
     EXPECT_TRUE(method != NULL) << "method_idx=" << i
-                                << " " << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
-                                << " " << dex->GetMethodName(dex->GetMethodId(i));
+                                << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+                                << " " << dex.GetMethodName(dex.GetMethodId(i));
     EXPECT_TRUE(method->GetEntryPointFromQuickCompiledCode() != NULL) << "method_idx=" << i
                                            << " "
-                                           << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
-                                           << " " << dex->GetMethodName(dex->GetMethodId(i));
-    EXPECT_TRUE(method->GetEntryPointFromPortableCompiledCode() != NULL) << "method_idx=" << i
-                                           << " "
-                                           << dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
-                                           << " " << dex->GetMethodName(dex->GetMethodId(i));
+                                           << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+                                           << " " << dex.GetMethodName(dex.GetMethodId(i));
   }
-  EXPECT_EQ(dex->NumFieldIds(), dex_cache->NumResolvedFields());
+  EXPECT_EQ(dex.NumFieldIds(), dex_cache->NumResolvedFields());
   for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
     mirror::ArtField* field = dex_cache->GetResolvedField(i);
     EXPECT_TRUE(field != NULL) << "field_idx=" << i
-                               << " " << dex->GetFieldDeclaringClassDescriptor(dex->GetFieldId(i))
-                               << " " << dex->GetFieldName(dex->GetFieldId(i));
+                               << " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i))
+                               << " " << dex.GetFieldName(dex.GetFieldId(i));
   }
 
   // TODO check Class::IsVerified for all classes
@@ -148,7 +145,6 @@
 }
 
 TEST_F(CompilerDriverTest, AbstractMethodErrorStub) {
-  TEST_DISABLED_FOR_PORTABLE();
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
   jobject class_loader;
   {
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
new file mode 100644
index 0000000..09ec9a2
--- /dev/null
+++ b/compiler/driver/compiler_options.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiler_options.h"
+
+#include "dex/pass_manager.h"
+
+namespace art {
+
+CompilerOptions::CompilerOptions()
+    : compiler_filter_(kDefaultCompilerFilter),
+      huge_method_threshold_(kDefaultHugeMethodThreshold),
+      large_method_threshold_(kDefaultLargeMethodThreshold),
+      small_method_threshold_(kDefaultSmallMethodThreshold),
+      tiny_method_threshold_(kDefaultTinyMethodThreshold),
+      num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
+      generate_gdb_information_(false),
+      include_patch_information_(kDefaultIncludePatchInformation),
+      top_k_profile_threshold_(kDefaultTopKProfileThreshold),
+      include_debug_symbols_(kDefaultIncludeDebugSymbols),
+      implicit_null_checks_(true),
+      implicit_so_checks_(true),
+      implicit_suspend_checks_(false),
+      compile_pic_(false),
+      verbose_methods_(nullptr),
+      pass_manager_options_(new PassManagerOptions),
+      init_failure_output_(nullptr) {
+}
+
+CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
+                                 size_t huge_method_threshold,
+                                 size_t large_method_threshold,
+                                 size_t small_method_threshold,
+                                 size_t tiny_method_threshold,
+                                 size_t num_dex_methods_threshold,
+                                 bool generate_gdb_information,
+                                 bool include_patch_information,
+                                 double top_k_profile_threshold,
+                                 bool include_debug_symbols,
+                                 bool implicit_null_checks,
+                                 bool implicit_so_checks,
+                                 bool implicit_suspend_checks,
+                                 bool compile_pic,
+                                 const std::vector<std::string>* verbose_methods,
+                                 PassManagerOptions* pass_manager_options,
+                                 std::ostream* init_failure_output
+                                 ) :  // NOLINT(whitespace/parens)
+    compiler_filter_(compiler_filter),
+    huge_method_threshold_(huge_method_threshold),
+    large_method_threshold_(large_method_threshold),
+    small_method_threshold_(small_method_threshold),
+    tiny_method_threshold_(tiny_method_threshold),
+    num_dex_methods_threshold_(num_dex_methods_threshold),
+    generate_gdb_information_(generate_gdb_information),
+    include_patch_information_(include_patch_information),
+    top_k_profile_threshold_(top_k_profile_threshold),
+    include_debug_symbols_(include_debug_symbols),
+    implicit_null_checks_(implicit_null_checks),
+    implicit_so_checks_(implicit_so_checks),
+    implicit_suspend_checks_(implicit_suspend_checks),
+    compile_pic_(compile_pic),
+    verbose_methods_(verbose_methods),
+    pass_manager_options_(pass_manager_options),
+    init_failure_output_(init_failure_output) {
+}
+
+}  // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index aec7d24..122ae4b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -26,6 +26,8 @@
 
 namespace art {
 
+class PassManagerOptions;
+
 class CompilerOptions FINAL {
  public:
   enum CompilerFilter {
@@ -53,27 +55,7 @@
   static const bool kDefaultIncludeDebugSymbols = kIsDebugBuild;
   static const bool kDefaultIncludePatchInformation = false;
 
-  CompilerOptions() :
-    compiler_filter_(kDefaultCompilerFilter),
-    huge_method_threshold_(kDefaultHugeMethodThreshold),
-    large_method_threshold_(kDefaultLargeMethodThreshold),
-    small_method_threshold_(kDefaultSmallMethodThreshold),
-    tiny_method_threshold_(kDefaultTinyMethodThreshold),
-    num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
-    generate_gdb_information_(false),
-    include_patch_information_(kDefaultIncludePatchInformation),
-    top_k_profile_threshold_(kDefaultTopKProfileThreshold),
-    include_debug_symbols_(kDefaultIncludeDebugSymbols),
-    implicit_null_checks_(false),
-    implicit_so_checks_(false),
-    implicit_suspend_checks_(false),
-    compile_pic_(false),
-#ifdef ART_SEA_IR_MODE
-    sea_ir_mode_(false),
-#endif
-    verbose_methods_(nullptr),
-    init_failure_output_(nullptr) {
-  }
+  CompilerOptions();
 
   CompilerOptions(CompilerFilter compiler_filter,
                   size_t huge_method_threshold,
@@ -89,32 +71,9 @@
                   bool implicit_so_checks,
                   bool implicit_suspend_checks,
                   bool compile_pic,
-#ifdef ART_SEA_IR_MODE
-                  bool sea_ir_mode,
-#endif
                   const std::vector<std::string>* verbose_methods,
-                  std::ostream* init_failure_output
-                  ) :  // NOLINT(whitespace/parens)
-    compiler_filter_(compiler_filter),
-    huge_method_threshold_(huge_method_threshold),
-    large_method_threshold_(large_method_threshold),
-    small_method_threshold_(small_method_threshold),
-    tiny_method_threshold_(tiny_method_threshold),
-    num_dex_methods_threshold_(num_dex_methods_threshold),
-    generate_gdb_information_(generate_gdb_information),
-    include_patch_information_(include_patch_information),
-    top_k_profile_threshold_(top_k_profile_threshold),
-    include_debug_symbols_(include_debug_symbols),
-    implicit_null_checks_(implicit_null_checks),
-    implicit_so_checks_(implicit_so_checks),
-    implicit_suspend_checks_(implicit_suspend_checks),
-    compile_pic_(compile_pic),
-#ifdef ART_SEA_IR_MODE
-    sea_ir_mode_(sea_ir_mode),
-#endif
-    verbose_methods_(verbose_methods),
-    init_failure_output_(init_failure_output) {
-  }
+                  PassManagerOptions* pass_manager_options,
+                  std::ostream* init_failure_output);
 
   CompilerFilter GetCompilerFilter() const {
     return compiler_filter_;
@@ -189,12 +148,6 @@
     return implicit_suspend_checks_;
   }
 
-#ifdef ART_SEA_IR_MODE
-  bool GetSeaIrMode() const {
-    return sea_ir_mode_;
-  }
-#endif
-
   bool GetGenerateGDBInformation() const {
     return generate_gdb_information_;
   }
@@ -225,6 +178,10 @@
     return init_failure_output_;
   }
 
+  const PassManagerOptions* GetPassManagerOptions() const {
+    return pass_manager_options_.get();
+  }
+
  private:
   CompilerFilter compiler_filter_;
   const size_t huge_method_threshold_;
@@ -242,13 +199,11 @@
   const bool implicit_suspend_checks_;
   const bool compile_pic_;
 
-#ifdef ART_SEA_IR_MODE
-  const bool sea_ir_mode_;
-#endif
-
   // Vector of methods to have verbose output enabled for.
   const std::vector<std::string>* const verbose_methods_;
 
+  std::unique_ptr<PassManagerOptions> pass_manager_options_;
+
   // Log initialization of initialization failures to this stream if not null.
   std::ostream* const init_failure_output_;
 
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 986fc71..e6c8c18 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -18,7 +18,6 @@
 
 #include "base/stringprintf.h"
 #include "dex/compiler_ir.h"
-#include "dex/mir_graph.h"
 #include "utils.h"
 
 namespace art {
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 84f5799..03ae489 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -102,6 +102,10 @@
     return verified_method_;
   }
 
+  void ClearVerifiedMethod() {
+    verified_method_ = nullptr;
+  }
+
   const std::string& GetSymbol();
 
  private:
@@ -117,7 +121,7 @@
   const uint16_t class_def_idx_;
   const uint32_t dex_method_idx_;
   const uint32_t access_flags_;
-  const VerifiedMethod* const verified_method_;
+  const VerifiedMethod* verified_method_;
 
   std::string symbol_;
 };
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 273b62d..94268de 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -1108,6 +1108,14 @@
                                EF_MIPS_ARCH_32R2);
         break;
       }
+      case kMips64: {
+        elf_header_.e_machine = EM_MIPS;
+        elf_header_.e_flags = (EF_MIPS_NOREORDER |
+                               EF_MIPS_PIC       |
+                               EF_MIPS_CPIC      |
+                               EF_MIPS_ARCH_64R6);
+        break;
+      }
       default: {
         fatal_error_ = true;
         LOG(FATAL) << "Unknown instruction set: " << isa;
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
deleted file mode 100644
index 7705b9c..0000000
--- a/compiler/elf_writer_mclinker.cc
+++ /dev/null
@@ -1,411 +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 "elf_writer_mclinker.h"
-
-#include <llvm/Support/ELF.h>
-#include <llvm/Support/TargetSelect.h>
-
-#include <mcld/Environment.h>
-#include <mcld/IRBuilder.h>
-#include <mcld/Linker.h>
-#include <mcld/LinkerConfig.h>
-#include <mcld/LinkerScript.h>
-#include <mcld/MC/ZOption.h>
-#include <mcld/Module.h>
-#include <mcld/Support/Path.h>
-#include <mcld/Support/TargetSelect.h>
-
-#include "base/unix_file/fd_file.h"
-#include "class_linker.h"
-#include "dex_method_iterator.h"
-#include "driver/compiler_driver.h"
-#include "elf_file.h"
-#include "globals.h"
-#include "mirror/art_method.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-#include "oat_writer.h"
-#include "scoped_thread_state_change.h"
-#include "vector_output_stream.h"
-
-namespace art {
-
-ElfWriterMclinker::ElfWriterMclinker(const CompilerDriver& driver, File* elf_file)
-  : ElfWriter(driver, elf_file), oat_input_(nullptr) {
-}
-
-ElfWriterMclinker::~ElfWriterMclinker() {
-}
-
-bool ElfWriterMclinker::Create(File* elf_file,
-                               OatWriter* oat_writer,
-                               const std::vector<const DexFile*>& dex_files,
-                               const std::string& android_root,
-                               bool is_host,
-                               const CompilerDriver& driver) {
-  ElfWriterMclinker elf_writer(driver, elf_file);
-  return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
-}
-
-bool ElfWriterMclinker::Write(OatWriter* oat_writer,
-                              const std::vector<const DexFile*>& dex_files,
-                              const std::string& android_root,
-                              bool is_host) {
-  std::vector<uint8_t> oat_contents;
-  oat_contents.reserve(oat_writer->GetSize());
-
-  Init();
-  mcld::LDSection* oat_section = AddOatInput(oat_writer, &oat_contents);
-  if (kUsePortableCompiler) {
-    AddMethodInputs(dex_files);
-    AddRuntimeInputs(android_root, is_host);
-  }
-
-  // link inputs
-  if (!linker_->link(*module_.get(), *ir_builder_.get())) {
-    LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
-    return false;
-  }
-
-  // Fill oat_contents.
-  VectorOutputStream output_stream("oat contents", &oat_contents);
-  oat_writer->SetOatDataOffset(oat_section->offset());
-  CHECK(oat_writer->Write(&output_stream));
-  CHECK_EQ(oat_writer->GetSize(), oat_contents.size());
-
-  // emit linked output
-  // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
-  int fd = dup(elf_file_->Fd());
-  if (fd == -1) {
-    PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
-    return false;
-  }
-  if (!linker_->emit(*module_.get(), fd)) {
-    LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
-    return false;
-  }
-  mcld::Finalize();
-  LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
-
-  oat_contents.clear();
-  if (kUsePortableCompiler) {
-    FixupOatMethodOffsets(dex_files);
-  }
-  return true;
-}
-
-static void InitializeLLVM() {
-  // TODO: this is lifted from art's compiler_llvm.cc, should be factored out
-  if (kIsTargetBuild) {
-    llvm::InitializeNativeTarget();
-    // TODO: odd that there is no InitializeNativeTargetMC?
-  } else {
-    llvm::InitializeAllTargets();
-    llvm::InitializeAllTargetMCs();
-  }
-}
-
-void ElfWriterMclinker::Init() {
-  std::string target_triple;
-  std::string target_cpu;
-  std::string target_attr;
-  CompilerDriver::InstructionSetToLLVMTarget(compiler_driver_->GetInstructionSet(),
-                                             &target_triple,
-                                             &target_cpu,
-                                             &target_attr);
-
-  // Based on mclinker's llvm-mcld.cpp main() and LinkerTest
-  //
-  // TODO: LinkerTest uses mcld::Initialize(), but it does an
-  // llvm::InitializeAllTargets, which we don't want. Basically we
-  // want mcld::InitializeNative, but it doesn't exist yet, so we
-  // inline the minimal we need here.
-  InitializeLLVM();
-  mcld::InitializeAllTargets();
-  mcld::InitializeAllLinkers();
-  mcld::InitializeAllEmulations();
-  mcld::InitializeAllDiagnostics();
-
-  linker_config_.reset(new mcld::LinkerConfig(target_triple));
-  CHECK(linker_config_.get() != NULL);
-  linker_config_->setCodeGenType(mcld::LinkerConfig::DynObj);
-  linker_config_->options().setSOName(elf_file_->GetPath());
-
-  // error on undefined symbols.
-  // TODO: should this just be set if kIsDebugBuild?
-  linker_config_->options().setNoUndefined(true);
-
-  if (compiler_driver_->GetInstructionSet() == kMips) {
-     // MCLinker defaults MIPS section alignment to 0x10000, not
-     // 0x1000.  The ABI says this is because the max page size is
-     // general is 64k but that isn't true on Android.
-     mcld::ZOption z_option;
-     z_option.setKind(mcld::ZOption::MaxPageSize);
-     z_option.setPageSize(kPageSize);
-     linker_config_->options().addZOption(z_option);
-  }
-
-  // TODO: Wire up mcld DiagnosticEngine to LOG?
-  linker_config_->options().setColor(false);
-  if (false) {
-    // enables some tracing of input file processing
-    linker_config_->options().setTrace(true);
-  }
-
-  // Based on alone::Linker::config
-  linker_script_.reset(new mcld::LinkerScript());
-  module_.reset(new mcld::Module(linker_config_->options().soname(), *linker_script_.get()));
-  CHECK(module_.get() != NULL);
-  ir_builder_.reset(new mcld::IRBuilder(*module_.get(), *linker_config_.get()));
-  CHECK(ir_builder_.get() != NULL);
-  linker_.reset(new mcld::Linker());
-  CHECK(linker_.get() != NULL);
-  linker_->emulate(*linker_script_.get(), *linker_config_.get());
-}
-
-mcld::LDSection* ElfWriterMclinker::AddOatInput(OatWriter* oat_writer,
-                                                std::vector<uint8_t>* oat_contents) {
-  // NOTE: oat_contents has sufficient reserved space but it doesn't contain the data yet.
-  const char* oat_data_start = reinterpret_cast<const char*>(&(*oat_contents)[0]);
-  const size_t oat_data_length = oat_writer->GetOatHeader().GetExecutableOffset();
-  const char* oat_code_start = oat_data_start + oat_data_length;
-  const size_t oat_code_length = oat_writer->GetSize() - oat_data_length;
-
-  // TODO: ownership of oat_input?
-  oat_input_ = ir_builder_->CreateInput("oat contents",
-                                        mcld::sys::fs::Path("oat contents path"),
-                                        mcld::Input::Object);
-  CHECK(oat_input_ != NULL);
-
-  // TODO: ownership of null_section?
-  mcld::LDSection* null_section = ir_builder_->CreateELFHeader(*oat_input_,
-                                                               "",
-                                                               mcld::LDFileFormat::Null,
-                                                               SHT_NULL,
-                                                               0);
-  CHECK(null_section != NULL);
-
-  // TODO: we should split readonly data from readonly executable
-  // code like .oat does.  We need to control section layout with
-  // linker script like functionality to guarantee references
-  // between sections maintain relative position which isn't
-  // possible right now with the mclinker APIs.
-  CHECK(oat_code_start != NULL);
-
-  // we need to ensure that oatdata is page aligned so when we
-  // fixup the segment load addresses, they remain page aligned.
-  uint32_t alignment = kPageSize;
-
-  // TODO: ownership of text_section?
-  mcld::LDSection* text_section = ir_builder_->CreateELFHeader(*oat_input_,
-                                                               ".text",
-                                                               SHT_PROGBITS,
-                                                               SHF_EXECINSTR | SHF_ALLOC,
-                                                               alignment);
-  CHECK(text_section != NULL);
-
-  mcld::SectionData* text_sectiondata = ir_builder_->CreateSectionData(*text_section);
-  CHECK(text_sectiondata != NULL);
-
-  // TODO: why does IRBuilder::CreateRegion take a non-const pointer?
-  mcld::Fragment* text_fragment = ir_builder_->CreateRegion(const_cast<char*>(oat_data_start),
-                                                            oat_writer->GetSize());
-  CHECK(text_fragment != NULL);
-  ir_builder_->AppendFragment(*text_fragment, *text_sectiondata);
-
-  ir_builder_->AddSymbol(*oat_input_,
-                         "oatdata",
-                         mcld::ResolveInfo::Object,
-                         mcld::ResolveInfo::Define,
-                         mcld::ResolveInfo::Global,
-                         oat_data_length,  // size
-                         0,                // offset
-                         text_section);
-
-  ir_builder_->AddSymbol(*oat_input_,
-                         "oatexec",
-                         mcld::ResolveInfo::Function,
-                         mcld::ResolveInfo::Define,
-                         mcld::ResolveInfo::Global,
-                         oat_code_length,  // size
-                         oat_data_length,  // offset
-                         text_section);
-
-  ir_builder_->AddSymbol(*oat_input_,
-                         "oatlastword",
-                         mcld::ResolveInfo::Object,
-                         mcld::ResolveInfo::Define,
-                         mcld::ResolveInfo::Global,
-                         0,                // size
-                         // subtract a word so symbol is within section
-                         (oat_data_length + oat_code_length) - sizeof(uint32_t),  // offset
-                         text_section);
-
-  return text_section;
-}
-
-void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) {
-  DCHECK(oat_input_ != NULL);
-
-  DexMethodIterator it(dex_files);
-  while (it.HasNext()) {
-    const DexFile& dex_file = it.GetDexFile();
-    uint32_t method_idx = it.GetMemberIndex();
-    const CompiledMethod* compiled_method =
-      compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx));
-    if (compiled_method != NULL) {
-      AddCompiledCodeInput(*compiled_method);
-    }
-    it.Next();
-  }
-  added_symbols_.clear();
-}
-
-void ElfWriterMclinker::AddCompiledCodeInput(const CompiledCode& compiled_code) {
-  // Check if we've seen this compiled code before. If so skip
-  // it. This can happen for reused code such as invoke stubs.
-  const std::string& symbol = compiled_code.GetSymbol();
-  SafeMap<const std::string*, const std::string*>::iterator it = added_symbols_.find(&symbol);
-  if (it != added_symbols_.end()) {
-    return;
-  }
-  added_symbols_.Put(&symbol, &symbol);
-
-  // Add input to supply code for symbol
-  const std::vector<uint8_t>* code = compiled_code.GetPortableCode();
-  // TODO: ownership of code_input?
-  // TODO: why does IRBuilder::ReadInput take a non-const pointer?
-  mcld::Input* code_input = ir_builder_->ReadInput(symbol,
-                                                   const_cast<uint8_t*>(&(*code)[0]),
-                                                   code->size());
-  CHECK(code_input != NULL);
-}
-
-void ElfWriterMclinker::AddRuntimeInputs(const std::string& android_root, bool is_host) {
-  std::string libart_so(android_root);
-  libart_so += kIsDebugBuild ? "/lib/libartd.so" : "/lib/libart.so";
-  // TODO: ownership of libart_so_input?
-  mcld::Input* libart_so_input = ir_builder_->ReadInput(libart_so, libart_so);
-  CHECK(libart_so_input != NULL);
-
-  std::string host_prebuilt_dir("prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6");
-
-  std::string compiler_runtime_lib;
-  if (is_host) {
-    compiler_runtime_lib += host_prebuilt_dir;
-    compiler_runtime_lib += "/lib/gcc/i686-linux/4.6.x-google/libgcc.a";
-  } else {
-    compiler_runtime_lib += android_root;
-    compiler_runtime_lib += "/lib/libcompiler_rt.a";
-  }
-  // TODO: ownership of compiler_runtime_lib_input?
-  mcld::Input* compiler_runtime_lib_input = ir_builder_->ReadInput(compiler_runtime_lib,
-                                                                   compiler_runtime_lib);
-  CHECK(compiler_runtime_lib_input != NULL);
-
-  std::string libc_lib;
-  if (is_host) {
-    libc_lib += host_prebuilt_dir;
-    libc_lib += "/sysroot/usr/lib/libc.so.6";
-  } else {
-    libc_lib += android_root;
-    libc_lib += "/lib/libc.so";
-  }
-  // TODO: ownership of libc_lib_input?
-  mcld::Input* libc_lib_input_input = ir_builder_->ReadInput(libc_lib, libc_lib);
-  CHECK(libc_lib_input_input != NULL);
-
-  std::string libm_lib;
-  if (is_host) {
-    libm_lib += host_prebuilt_dir;
-    libm_lib += "/sysroot/usr/lib/libm.so";
-  } else {
-    libm_lib += android_root;
-    libm_lib += "/lib/libm.so";
-  }
-  // TODO: ownership of libm_lib_input?
-  mcld::Input* libm_lib_input_input = ir_builder_->ReadInput(libm_lib, libm_lib);
-  CHECK(libm_lib_input_input != NULL);
-}
-
-void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
-  std::string error_msg;
-  std::unique_ptr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg));
-  CHECK(elf_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
-
-  uint32_t oatdata_address = GetOatDataAddress(elf_file.get());
-  DexMethodIterator it(dex_files);
-  while (it.HasNext()) {
-    const DexFile& dex_file = it.GetDexFile();
-    uint32_t method_idx = it.GetMemberIndex();
-    InvokeType invoke_type = it.GetInvokeType();
-    mirror::ArtMethod* method = NULL;
-    if (compiler_driver_->IsImage()) {
-      ClassLinker* linker = Runtime::Current()->GetClassLinker();
-      // Unchecked as we hold mutator_lock_ on entry.
-      ScopedObjectAccessUnchecked soa(Thread::Current());
-      StackHandleScope<1> hs(soa.Self());
-      Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(dex_file)));
-      method = linker->ResolveMethod(dex_file, method_idx, dex_cache,
-                                     NullHandle<mirror::ClassLoader>(),
-                                     NullHandle<mirror::ArtMethod>(), invoke_type);
-      CHECK(method != NULL);
-    }
-    const CompiledMethod* compiled_method =
-      compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx));
-    if (compiled_method != NULL) {
-      uint32_t offset = FixupCompiledCodeOffset(*elf_file.get(), oatdata_address, *compiled_method);
-      // Don't overwrite static method trampoline
-      if (method != NULL &&
-          (!method->IsStatic() ||
-           method->IsConstructor() ||
-           method->GetDeclaringClass()->IsInitialized())) {
-        method->SetPortableOatCodeOffset(offset);
-      }
-    }
-    it.Next();
-  }
-  symbol_to_compiled_code_offset_.clear();
-}
-
-uint32_t ElfWriterMclinker::FixupCompiledCodeOffset(ElfFile& elf_file,
-                                                    Elf32_Addr oatdata_address,
-                                                    const CompiledCode& compiled_code) {
-  const std::string& symbol = compiled_code.GetSymbol();
-  SafeMap<const std::string*, uint32_t>::iterator it = symbol_to_compiled_code_offset_.find(&symbol);
-  if (it != symbol_to_compiled_code_offset_.end()) {
-    return it->second;
-  }
-
-  Elf32_Addr compiled_code_address = elf_file.FindSymbolAddress(SHT_SYMTAB,
-                                                                symbol,
-                                                                true);
-  CHECK_NE(0U, compiled_code_address) << symbol;
-  CHECK_LT(oatdata_address, compiled_code_address) << symbol;
-  uint32_t compiled_code_offset = compiled_code_address - oatdata_address;
-  symbol_to_compiled_code_offset_.Put(&symbol, compiled_code_offset);
-
-  const std::vector<uint32_t>& offsets = compiled_code.GetOatdataOffsetsToCompliledCodeOffset();
-  for (uint32_t i = 0; i < offsets.size(); i++) {
-    uint32_t oatdata_offset = oatdata_address + offsets[i];
-    uint32_t* addr = reinterpret_cast<uint32_t*>(elf_file.Begin() + oatdata_offset);
-    *addr = compiled_code_offset;
-  }
-  return compiled_code_offset;
-}
-
-}  // namespace art
diff --git a/compiler/elf_writer_mclinker.h b/compiler/elf_writer_mclinker.h
deleted file mode 100644
index 489fefb..0000000
--- a/compiler/elf_writer_mclinker.h
+++ /dev/null
@@ -1,98 +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_COMPILER_ELF_WRITER_MCLINKER_H_
-#define ART_COMPILER_ELF_WRITER_MCLINKER_H_
-
-#include <memory>
-
-#include "elf_writer.h"
-#include "safe_map.h"
-
-namespace mcld {
-class IRBuilder;
-class Input;
-class LDSection;
-class LDSymbol;
-class Linker;
-class LinkerConfig;
-class LinkerScript;
-class Module;
-}  // namespace mcld
-
-namespace art {
-
-class CompiledCode;
-
-class ElfWriterMclinker FINAL : public ElfWriter {
- public:
-  // Write an ELF file. Returns true on success, false on failure.
-  static bool Create(File* file,
-                     OatWriter* oat_writer,
-                     const std::vector<const DexFile*>& dex_files,
-                     const std::string& android_root,
-                     bool is_host,
-                     const CompilerDriver& driver)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- protected:
-  bool Write(OatWriter* oat_writer,
-             const std::vector<const DexFile*>& dex_files,
-             const std::string& android_root,
-             bool is_host)
-      OVERRIDE
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- private:
-  ElfWriterMclinker(const CompilerDriver& driver, File* elf_file);
-  ~ElfWriterMclinker();
-
-  void Init();
-  mcld::LDSection* AddOatInput(OatWriter* oat_writer, std::vector<uint8_t>* oat_contents);
-  void AddMethodInputs(const std::vector<const DexFile*>& dex_files);
-  void AddCompiledCodeInput(const CompiledCode& compiled_code);
-  void AddRuntimeInputs(const std::string& android_root, bool is_host);
-  void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  uint32_t FixupCompiledCodeOffset(ElfFile& elf_file,
-                                   uint32_t oatdata_address,
-                                   const CompiledCode& compiled_code);
-
-  // Setup by Init()
-  std::unique_ptr<mcld::LinkerConfig> linker_config_;
-  std::unique_ptr<mcld::LinkerScript> linker_script_;
-  std::unique_ptr<mcld::Module> module_;
-  std::unique_ptr<mcld::IRBuilder> ir_builder_;
-  std::unique_ptr<mcld::Linker> linker_;
-
-  // Setup by AddOatInput()
-  // TODO: ownership of oat_input_?
-  mcld::Input* oat_input_;
-
-  // Setup by AddCompiledCodeInput
-  // set of symbols for already added mcld::Inputs
-  SafeMap<const std::string*, const std::string*> added_symbols_;
-
-  // Setup by FixupCompiledCodeOffset
-  // map of symbol names to oatdata offset
-  SafeMap<const std::string*, uint32_t> symbol_to_compiled_code_offset_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterMclinker);
-};
-
-}  // namespace art
-
-#endif  // ART_COMPILER_ELF_WRITER_MCLINKER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 25cf086..9ec4f28 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -358,8 +358,8 @@
 };
 
 // TODO: rewriting it using DexFile::DecodeDebugInfo needs unneeded stuff.
-static void GetLineInfoForJava(const uint8_t* dbgstream, const SrcMap& pc2dex,
-                               SrcMap* result, uint32_t start_pc = 0) {
+static void GetLineInfoForJava(const uint8_t* dbgstream, const SwapSrcMap& pc2dex,
+                               DefaultSrcMap* result, uint32_t start_pc = 0) {
   if (dbgstream == nullptr) {
     return;
   }
@@ -415,7 +415,7 @@
       dex_offset += adjopcode / DexFile::DBG_LINE_RANGE;
       java_line += DexFile::DBG_LINE_BASE + (adjopcode % DexFile::DBG_LINE_RANGE);
 
-      for (SrcMap::const_iterator found = pc2dex.FindByTo(dex_offset);
+      for (SwapSrcMap::const_iterator found = pc2dex.FindByTo(dex_offset);
           found != pc2dex.end() && found->to_ == static_cast<int32_t>(dex_offset);
           found++) {
         result->push_back({found->from_ + start_pc, static_cast<int32_t>(java_line)});
@@ -615,7 +615,7 @@
     LineTableGenerator line_table_generator(LINE_BASE, LINE_RANGE, OPCODE_BASE,
                                             dbg_line, 0, 1);
 
-    SrcMap pc2java_map;
+    DefaultSrcMap pc2java_map;
     for (size_t i = 0; i < method_info.size(); ++i) {
       const OatWriter::DebugInfo &dbg = method_info[i];
       const char* file_name = (dbg.src_file_name_ == nullptr) ? "null" : dbg.src_file_name_;
@@ -669,6 +669,8 @@
 template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
           typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
           typename Elf_Phdr, typename Elf_Shdr>
+// Do not inline to avoid Clang stack frame problems. b/18738594
+NO_INLINE
 static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
                               ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
                                          Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
@@ -698,7 +700,7 @@
       DCHECK(it->compiled_method_ != nullptr);
 
       // Copy in the FDE, if present
-      const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+      const SwapVector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
       if (fde != nullptr) {
         // Copy the information into cfi_info and then fix the address in the new copy.
         int cur_offset = cfi_info->size();
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 2ffbd10..fd3a912 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -46,15 +46,12 @@
     EXPECT_EQ(expected_value, ef->FindDynamicSymbolAddress(symbol_name)); \
   } while (false)
 
+#if defined(ART_USE_OPTIMIZING_COMPILER)
+TEST_F(ElfWriterTest, DISABLED_dlsym) {
+#else
 TEST_F(ElfWriterTest, dlsym) {
-  std::string elf_location;
-  if (IsHost()) {
-    const char* host_dir = getenv("ANDROID_HOST_OUT");
-    CHECK(host_dir != NULL);
-    elf_location = StringPrintf("%s/framework/core.oat", host_dir);
-  } else {
-    elf_location = "/data/art-test/core.oat";
-  }
+#endif
+  std::string elf_location = GetCoreOatLocation();
   std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA);
   LOG(INFO) << "elf_filename=" << elf_filename;
 
@@ -63,28 +60,6 @@
   void* dl_oatexec = NULL;
   void* dl_oatlastword = NULL;
 
-#if defined(ART_USE_PORTABLE_COMPILER)
-  {
-    // We only use dlopen for loading with portable. See OatFile::Open.
-    void* dl_oat_so = dlopen(elf_filename.c_str(), RTLD_NOW);
-    ASSERT_TRUE(dl_oat_so != NULL) << dlerror();
-    dl_oatdata = dlsym(dl_oat_so, "oatdata");
-    ASSERT_TRUE(dl_oatdata != NULL);
-
-    OatHeader* dl_oat_header = reinterpret_cast<OatHeader*>(dl_oatdata);
-    ASSERT_TRUE(dl_oat_header->IsValid());
-    dl_oatexec = dlsym(dl_oat_so, "oatexec");
-    ASSERT_TRUE(dl_oatexec != NULL);
-    ASSERT_LT(dl_oatdata, dl_oatexec);
-
-    dl_oatlastword = dlsym(dl_oat_so, "oatlastword");
-    ASSERT_TRUE(dl_oatlastword != NULL);
-    ASSERT_LT(dl_oatexec, dl_oatlastword);
-
-    ASSERT_EQ(0, dlclose(dl_oat_so));
-  }
-#endif
-
   std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
   ASSERT_TRUE(file.get() != NULL);
   {
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index dac1ef4..cf97943 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -72,11 +72,6 @@
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       TimingLogger timings("ImageTest::WriteRead", false, false);
       TimingLogger::ScopedTiming t("CompileAll", &timings);
-      if (kUsePortableCompiler) {
-        // TODO: we disable this for portable so the test executes in a reasonable amount of time.
-        //       We shouldn't need to do this.
-        compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly);
-      }
       for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
         dex_file->EnableWrite();
       }
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 178ee43..c588e1a 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -77,6 +77,7 @@
     Thread::Current()->TransitionFromSuspendedToRunnable();
     PruneNonImageClasses();  // Remove junk
     ComputeLazyFieldsForImageClasses();  // Add useful information
+    ProcessStrings();
     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
   }
   gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -126,13 +127,6 @@
 
   jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset();
 
-  portable_imt_conflict_trampoline_offset_ =
-      oat_file_->GetOatHeader().GetPortableImtConflictTrampolineOffset();
-  portable_resolution_trampoline_offset_ =
-      oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
-  portable_to_interpreter_bridge_offset_ =
-      oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset();
-
   quick_generic_jni_trampoline_offset_ =
       oat_file_->GetOatHeader().GetQuickGenericJniTrampolineOffset();
   quick_imt_conflict_trampoline_offset_ =
@@ -279,13 +273,7 @@
 
 void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
   DCHECK(object != nullptr);
-  size_t object_size;
-  if (object->IsArtMethod()) {
-    // Methods are sized based on the target pointer size.
-    object_size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
-  } else {
-    object_size = object->SizeOf();
-  }
+  size_t object_size = object->SizeOf();
 
   // The magic happens here. We segregate objects into different bins based
   // on how likely they are to get dirty at runtime.
@@ -479,116 +467,93 @@
 };
 
 // Compare strings based on length, used for sorting strings by length / reverse length.
-class StringLengthComparator {
+class LexicographicalStringComparator {
  public:
-  explicit StringLengthComparator(Handle<mirror::ObjectArray<mirror::String>> strings)
-      : strings_(strings) {
+  bool operator()(const mirror::HeapReference<mirror::String>& lhs,
+                  const mirror::HeapReference<mirror::String>& rhs) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::String* lhs_s = lhs.AsMirrorPtr();
+    mirror::String* rhs_s = rhs.AsMirrorPtr();
+    uint16_t* lhs_begin = lhs_s->GetCharArray()->GetData() + lhs_s->GetOffset();
+    uint16_t* rhs_begin = rhs_s->GetCharArray()->GetData() + rhs_s->GetOffset();
+    return std::lexicographical_compare(lhs_begin, lhs_begin + lhs_s->GetLength(),
+                                        rhs_begin, rhs_begin + rhs_s->GetLength());
   }
-  bool operator()(size_t a, size_t b) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return strings_->GetWithoutChecks(a)->GetLength() < strings_->GetWithoutChecks(b)->GetLength();
-  }
-
- private:
-  Handle<mirror::ObjectArray<mirror::String>> strings_;
 };
 
-// Normal string < comparison through the chars_ array.
-class SubstringComparator {
- public:
-  explicit SubstringComparator(const std::vector<uint16_t>* const chars) : chars_(chars) {
+static bool IsPrefix(mirror::String* pref, mirror::String* full)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (pref->GetLength() > full->GetLength()) {
+    return false;
   }
-  bool operator()(const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b) {
-    return std::lexicographical_compare(chars_->begin() + a.first,
-                                        chars_->begin() + a.first + a.second,
-                                        chars_->begin() + b.first,
-                                        chars_->begin() + b.first + b.second);
-  }
-
- private:
-  const std::vector<uint16_t>* const chars_;
-};
+  uint16_t* pref_begin = pref->GetCharArray()->GetData() + pref->GetOffset();
+  uint16_t* full_begin = full->GetCharArray()->GetData() + full->GetOffset();
+  return std::equal(pref_begin, pref_begin + pref->GetLength(), full_begin);
+}
 
 void ImageWriter::ProcessStrings() {
   size_t total_strings = 0;
   gc::Heap* heap = Runtime::Current()->GetHeap();
   ClassLinker* cl = Runtime::Current()->GetClassLinker();
-  {
-    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
-    heap->VisitObjects(CountStringsCallback, &total_strings);  // Count the strings.
-  }
+  // Count the strings.
+  heap->VisitObjects(CountStringsCallback, &total_strings);
   Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
   auto strings = hs.NewHandle(cl->AllocStringArray(self, total_strings));
   StringCollector string_collector(strings, 0U);
-  {
-    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
-    // Read strings into the array.
-    heap->VisitObjects(StringCollector::Callback, &string_collector);
-  }
+  // Read strings into the array.
+  heap->VisitObjects(StringCollector::Callback, &string_collector);
   // Some strings could have gotten freed if AllocStringArray caused a GC.
   CHECK_LE(string_collector.GetIndex(), total_strings);
   total_strings = string_collector.GetIndex();
-  size_t total_length = 0;
-  std::vector<size_t> reverse_sorted_strings;
-  for (size_t i = 0; i < total_strings; ++i) {
-    mirror::String* s = strings->GetWithoutChecks(i);
-    // Look up the string in the array.
-    total_length += s->GetLength();
-    reverse_sorted_strings.push_back(i);
-  }
-  // Sort by reverse length.
-  StringLengthComparator comparator(strings);
-  std::sort(reverse_sorted_strings.rbegin(), reverse_sorted_strings.rend(), comparator);
-  // Deduplicate prefixes and add strings to the char array.
-  std::vector<uint16_t> combined_chars(total_length, 0U);
-  size_t num_chars = 0;
+  auto* strings_begin = reinterpret_cast<mirror::HeapReference<mirror::String>*>(
+          strings->GetRawData(sizeof(mirror::HeapReference<mirror::String>), 0));
+  std::sort(strings_begin, strings_begin + total_strings, LexicographicalStringComparator());
   // Characters of strings which are non equal prefix of another string (not the same string).
   // We don't count the savings from equal strings since these would get interned later anyways.
   size_t prefix_saved_chars = 0;
-  std::set<std::pair<size_t, size_t>, SubstringComparator> existing_strings((
-      SubstringComparator(&combined_chars)));
-  for (size_t i = 0; i < total_strings; ++i) {
-    mirror::String* s = strings->GetWithoutChecks(reverse_sorted_strings[i]);
-    // Add the string to the end of the char array.
+  // Count characters needed for the strings.
+  size_t num_chars = 0u;
+  mirror::String* prev_s = nullptr;
+  for (size_t idx = 0; idx != total_strings; ++idx) {
+    mirror::String* s = strings->GetWithoutChecks(idx);
     size_t length = s->GetLength();
-    for (size_t j = 0; j < length; ++j) {
-      combined_chars[num_chars++] = s->CharAt(j);
-    }
-    // Try to see if the string exists as a prefix of an existing string.
-    size_t new_offset = 0;
-    std::pair<size_t, size_t> new_string(num_chars - length, length);
-    auto it = existing_strings.lower_bound(new_string);
-    bool is_prefix = false;
-    if (it != existing_strings.end()) {
-      CHECK_LE(length, it->second);
-      is_prefix = std::equal(combined_chars.begin() + it->first,
-                             combined_chars.begin() + it->first + it->second,
-                             combined_chars.begin() + new_string.first);
-    }
-    if (is_prefix) {
-      // Shares a prefix, set the offset to where the new offset will be.
-      new_offset = it->first;
-      // Remove the added chars.
-      num_chars -= length;
-      if (it->second != length) {
-        prefix_saved_chars += length;
+    num_chars += length;
+    if (prev_s != nullptr && IsPrefix(prev_s, s)) {
+      size_t prev_length = prev_s->GetLength();
+      num_chars -= prev_length;
+      if (prev_length != length) {
+        prefix_saved_chars += prev_length;
       }
-    } else {
-      new_offset = new_string.first;
-      existing_strings.insert(new_string);
     }
-    s->SetOffset(new_offset);
+    prev_s = s;
   }
-  // Allocate and update the char arrays.
-  auto* array = mirror::CharArray::Alloc(self, num_chars);
-  for (size_t i = 0; i < num_chars; ++i) {
-    array->SetWithoutChecks<false>(i, combined_chars[i]);
+  // Create character array, copy characters and point the strings there.
+  mirror::CharArray* array = mirror::CharArray::Alloc(self, num_chars);
+  string_data_array_ = array;
+  uint16_t* array_data = array->GetData();
+  size_t pos = 0u;
+  prev_s = nullptr;
+  for (size_t idx = 0; idx != total_strings; ++idx) {
+    mirror::String* s = strings->GetWithoutChecks(idx);
+    uint16_t* s_data = s->GetCharArray()->GetData() + s->GetOffset();
+    int32_t s_length = s->GetLength();
+    int32_t prefix_length = 0u;
+    if (idx != 0u && IsPrefix(prev_s, s)) {
+      prefix_length = prev_s->GetLength();
+    }
+    memcpy(array_data + pos, s_data + prefix_length, (s_length - prefix_length) * sizeof(*s_data));
+    s->SetOffset(pos - prefix_length);
+    s->SetArray(array);
+    pos += s_length - prefix_length;
+    prev_s = s;
   }
-  for (size_t i = 0; i < total_strings; ++i) {
-    strings->GetWithoutChecks(i)->SetArray(array);
+  CHECK_EQ(pos, num_chars);
+
+  if (kIsDebugBuild || VLOG_IS_ON(compiler)) {
+    LOG(INFO) << "Total # image strings=" << total_strings << " combined length="
+        << num_chars << " prefix saved chars=" << prefix_saved_chars;
   }
-  LOG(INFO) << "Total # image strings=" << total_strings << " combined length="
-      << total_length << " prefix saved chars=" << prefix_saved_chars;
   ComputeEagerResolvedStrings();
 }
 
@@ -622,7 +587,6 @@
 }
 
 void ImageWriter::ComputeEagerResolvedStrings() {
-  ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
   Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this);
 }
 
@@ -695,7 +659,6 @@
 void ImageWriter::CheckNonImageClassesRemoved() {
   if (compiler_driver_.GetImageClasses() != nullptr) {
     gc::Heap* heap = Runtime::Current()->GetHeap();
-    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
     heap->VisitObjects(CheckNonImageClassesRemovedCallback, this);
   }
 }
@@ -896,17 +859,14 @@
   // know where image_roots is going to end up
   image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment);  // 64-bit-alignment
 
-  {
-    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    // TODO: Image spaces only?
-    DCHECK_LT(image_end_, image_->Size());
-    image_objects_offset_begin_ = image_end_;
-    // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
-    heap->VisitObjects(WalkFieldsCallback, this);
-    // Transform each object's bin slot into an offset which will be used to do the final copy.
-    heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
-    DCHECK(saved_hashes_map_.empty());  // All binslot hashes should've been put into vector by now.
-  }
+  // TODO: Image spaces only?
+  DCHECK_LT(image_end_, image_->Size());
+  image_objects_offset_begin_ = image_end_;
+  // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
+  heap->VisitObjects(WalkFieldsCallback, this);
+  // Transform each object's bin slot into an offset which will be used to do the final copy.
+  heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
+  DCHECK(saved_hashes_map_.empty());  // All binslot hashes should've been put into vector by now.
 
   DCHECK_GT(image_end_, GetBinSizeSum());
 
@@ -942,12 +902,10 @@
 }
 
 void ImageWriter::CopyAndFixupObjects() {
-  ScopedAssertNoThreadSuspension ants(Thread::Current(), "ImageWriter");
   gc::Heap* heap = Runtime::Current()->GetHeap();
   // TODO: heap validation can't handle this fix up pass
   heap->DisableObjectValidation();
   // TODO: Image spaces only?
-  WriterMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   heap->VisitObjects(CopyAndFixupObjectsCallback, this);
   // Fix up the object previously had hash codes.
   for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) {
@@ -968,7 +926,7 @@
   if (obj->IsArtMethod()) {
     // Size without pointer fields since we don't want to overrun the buffer if target art method
     // is 32 bits but source is 64 bits.
-    n = mirror::ArtMethod::SizeWithoutPointerFields(sizeof(void*));
+    n = mirror::ArtMethod::SizeWithoutPointerFields(image_writer->target_ptr_size_);
   } else {
     n = obj->SizeOf();
   }
@@ -1053,10 +1011,6 @@
   }
   if (orig->IsArtMethod<kVerifyNone>()) {
     FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
-  } else if (orig->IsClass() && orig->AsClass()->IsArtMethodClass()) {
-    // Set the right size for the target.
-    size_t size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
-    down_cast<mirror::Class*>(copy)->SetObjectSizeWithoutChecks(size);
   }
 }
 
@@ -1068,7 +1022,9 @@
   // trampoline.
 
   // Quick entrypoint:
-  const uint8_t* quick_code = GetOatAddress(method->GetQuickOatCodeOffset());
+  uint32_t quick_oat_code_offset = PointerToLowMemUInt32(
+      method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_));
+  const uint8_t* quick_code = GetOatAddress(quick_oat_code_offset);
   *quick_is_interpreted = false;
   if (quick_code != nullptr &&
       (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) {
@@ -1119,25 +1075,20 @@
   // locations.
   // Copy all of the fields from the runtime methods to the target methods first since we did a
   // bytewise copy earlier.
-  copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
-      orig->GetEntryPointFromPortableCompiledCode(), target_ptr_size_);
-  copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(orig->GetEntryPointFromInterpreter(),
-                                                         target_ptr_size_);
-  copy->SetEntryPointFromJniPtrSize<kVerifyNone>(orig->GetEntryPointFromJni(), target_ptr_size_);
+  copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
+      orig->GetEntryPointFromInterpreterPtrSize(target_ptr_size_), target_ptr_size_);
+  copy->SetEntryPointFromJniPtrSize<kVerifyNone>(
+      orig->GetEntryPointFromJniPtrSize(target_ptr_size_), target_ptr_size_);
   copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
-      orig->GetEntryPointFromQuickCompiledCode(), target_ptr_size_);
+      orig->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_), target_ptr_size_);
 
   // The resolution method has a special trampoline to call.
   Runtime* runtime = Runtime::Current();
   if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
-    copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
-        GetOatAddress(portable_resolution_trampoline_offset_), target_ptr_size_);
     copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
         GetOatAddress(quick_resolution_trampoline_offset_), target_ptr_size_);
   } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
                       orig == runtime->GetImtUnimplementedMethod())) {
-    copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
-        GetOatAddress(portable_imt_conflict_trampoline_offset_), target_ptr_size_);
     copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
         GetOatAddress(quick_imt_conflict_trampoline_offset_), target_ptr_size_);
   } else {
@@ -1145,8 +1096,6 @@
     // resolution trampoline. Abstract methods never have code and so we need to make sure their
     // use results in an AbstractMethodError. We use the interpreter to achieve this.
     if (UNLIKELY(orig->IsAbstract())) {
-      copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
-          GetOatAddress(portable_to_interpreter_bridge_offset_), target_ptr_size_);
       copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
           GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_);
       copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
@@ -1157,29 +1106,6 @@
       const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted);
       copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(quick_code, target_ptr_size_);
 
-      // Portable entrypoint:
-      const uint8_t* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
-      bool portable_is_interpreted = false;
-      if (portable_code != nullptr &&
-          (!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) {
-        // We have code for a non-static or initialized method, just use the code.
-      } else if (portable_code == nullptr && orig->IsNative() &&
-          (!orig->IsStatic() || orig->GetDeclaringClass()->IsInitialized())) {
-        // Non-static or initialized native method missing compiled code, use generic JNI version.
-        // TODO: generic JNI support for LLVM.
-        portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
-      } else if (portable_code == nullptr && !orig->IsNative()) {
-        // We don't have code at all for a non-native method, use the interpreter.
-        portable_code = GetOatAddress(portable_to_interpreter_bridge_offset_);
-        portable_is_interpreted = true;
-      } else {
-        CHECK(!orig->GetDeclaringClass()->IsInitialized());
-        // We have code for a static method, but need to go through the resolution stub for class
-        // initialization.
-        portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
-      }
-      copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
-          portable_code, target_ptr_size_);
       // JNI entrypoint:
       if (orig->IsNative()) {
         // The native method's pointer is set to a stub to lookup via dlsym.
@@ -1190,7 +1116,7 @@
 
       // Interpreter entrypoint:
       // Set the interpreter entrypoint depending on whether there is compiled code or not.
-      uint32_t interpreter_code = (quick_is_interpreted && portable_is_interpreted)
+      uint32_t interpreter_code = (quick_is_interpreted)
           ? interpreter_to_interpreter_bridge_offset_
           : interpreter_to_compiled_code_bridge_offset_;
       EntryPointFromInterpreter* interpreter_entrypoint =
@@ -1255,4 +1181,13 @@
   return lockword_ & ~kBinMask;
 }
 
+void ImageWriter::FreeStringDataArray() {
+  if (string_data_array_ != nullptr) {
+    gc::space::LargeObjectSpace* los = Runtime::Current()->GetHeap()->GetLargeObjectsSpace();
+    if (los != nullptr) {
+      los->Free(Thread::Current(), reinterpret_cast<mirror::Object*>(string_data_array_));
+    }
+  }
+}
+
 }  // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 8c84b68..53f5ce4 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_IMAGE_WRITER_H_
 
 #include <stdint.h>
+#include <valgrind.h>
 
 #include <cstddef>
 #include <memory>
@@ -47,8 +48,7 @@
         image_end_(0), image_objects_offset_begin_(0), image_roots_address_(0), oat_file_(nullptr),
         oat_data_begin_(nullptr), interpreter_to_interpreter_bridge_offset_(0),
         interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0),
-        portable_imt_conflict_trampoline_offset_(0), portable_resolution_trampoline_offset_(0),
-        portable_to_interpreter_bridge_offset_(0), quick_generic_jni_trampoline_offset_(0),
+        quick_generic_jni_trampoline_offset_(0),
         quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
         quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic),
         target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
@@ -56,7 +56,15 @@
     CHECK_NE(image_begin, 0U);
   }
 
-  ~ImageWriter() {}
+  ~ImageWriter() {
+    // For interned strings a large array is allocated to hold all the character data and avoid
+    // overhead. However, no GC is run anymore at this point. As the array is likely large, it
+    // will be allocated in the large object space, where valgrind can track every single
+    // allocation. Not explicitly freeing that array will be recognized as a leak.
+    if (RUNNING_ON_VALGRIND != 0) {
+      FreeStringDataArray();
+    }
+  }
 
   bool PrepareImageAddressSpace();
 
@@ -169,12 +177,9 @@
   }
 
   const uint8_t* GetOatAddress(uint32_t offset) const {
-#if !defined(ART_USE_PORTABLE_COMPILER)
     // With Quick, code is within the OatFile, as there are all in one
-    // .o ELF object. However with Portable, the code is always in
-    // different .o ELF objects.
+    // .o ELF object.
     DCHECK_LT(offset, oat_file_->Size());
-#endif
     if (offset == 0u) {
       return nullptr;
     }
@@ -254,6 +259,9 @@
   // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
   size_t GetBinSizeSum(Bin up_to = kBinSize) const;
 
+  // Release the string_data_array_.
+  void FreeStringDataArray();
+
   const CompilerDriver& compiler_driver_;
 
   // Beginning target image address for the output image.
@@ -290,9 +298,6 @@
   uint32_t interpreter_to_interpreter_bridge_offset_;
   uint32_t interpreter_to_compiled_code_bridge_offset_;
   uint32_t jni_dlsym_lookup_offset_;
-  uint32_t portable_imt_conflict_trampoline_offset_;
-  uint32_t portable_resolution_trampoline_offset_;
-  uint32_t portable_to_interpreter_bridge_offset_;
   uint32_t quick_generic_jni_trampoline_offset_;
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
@@ -306,6 +311,8 @@
   size_t bin_slot_sizes_[kBinSize];  // Number of bytes in a bin
   size_t bin_slot_count_[kBinSize];  // Number of objects in a bin
 
+  void* string_data_array_;  // The backing for the interned strings.
+
   friend class FixupVisitor;
   friend class FixupClassVisitor;
   DISALLOW_COPY_AND_ASSIGN(ImageWriter);
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 2755442..f513ea8 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -80,8 +80,6 @@
         CompileMethod(method);
         ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr)
             << method_name << " " << method_sig;
-        ASSERT_TRUE(method->GetEntryPointFromPortableCompiledCode() != nullptr)
-            << method_name << " " << method_sig;
       }
     }
   }
@@ -204,7 +202,6 @@
 }
 
 void JniCompilerTest::CompileAndRunNoArgMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo));
 
   EXPECT_EQ(0, gJava_MyClassNatives_foo_calls);
@@ -219,7 +216,6 @@
 JNI_TEST(CompileAndRunNoArgMethod)
 
 void JniCompilerTest::CompileAndRunIntMethodThroughStubImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "bar", "(I)I", nullptr);
   // calling through stub will link with &Java_MyClassNatives_bar
 
@@ -234,7 +230,6 @@
 JNI_TEST(CompileAndRunIntMethodThroughStub)
 
 void JniCompilerTest::CompileAndRunStaticIntMethodThroughStubImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "sbar", "(I)I", nullptr);
   // calling through stub will link with &Java_MyClassNatives_sbar
 
@@ -262,7 +257,6 @@
 }
 
 void JniCompilerTest::CompileAndRunIntMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooI", "(I)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooI));
 
@@ -293,7 +287,6 @@
 }
 
 void JniCompilerTest::CompileAndRunIntIntMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooII", "(II)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooII));
 
@@ -325,7 +318,6 @@
 }
 
 void JniCompilerTest::CompileAndRunLongLongMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooJJ", "(JJ)J",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ));
 
@@ -358,7 +350,6 @@
 }
 
 void JniCompilerTest::CompileAndRunDoubleDoubleMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooDD", "(DD)D",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooDD));
 
@@ -390,7 +381,6 @@
 }
 
 void JniCompilerTest::CompileAndRun_fooJJ_synchronizedImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooJJ_synchronized", "(JJ)J",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooJJ_synchronized));
 
@@ -430,7 +420,6 @@
 }
 
 void JniCompilerTest::CompileAndRunIntObjectObjectMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooIOO));
@@ -479,7 +468,6 @@
 }
 
 void JniCompilerTest::CompileAndRunStaticIntIntMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSII", "(II)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooSII));
 
@@ -507,7 +495,6 @@
 }
 
 void JniCompilerTest::CompileAndRunStaticDoubleDoubleMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSDD", "(DD)D",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooSDD));
 
@@ -535,7 +522,6 @@
 }
 
 void JniCompilerTest::RunStaticLogDoubleMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "logD", "(D)D", reinterpret_cast<void*>(&Java_MyClassNatives_logD));
 
   jdouble result = env_->CallStaticDoubleMethod(jklass_, jmethod_, 2.0);
@@ -549,7 +535,6 @@
 }
 
 void JniCompilerTest::RunStaticLogFloatMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "logF", "(F)F", reinterpret_cast<void*>(&Java_MyClassNatives_logF));
 
   jfloat result = env_->CallStaticFloatMethod(jklass_, jmethod_, 2.0);
@@ -571,7 +556,6 @@
 }
 
 void JniCompilerTest::RunStaticReturnTrueImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "returnTrue", "()Z", reinterpret_cast<void*>(&Java_MyClassNatives_returnTrue));
 
   jboolean result = env_->CallStaticBooleanMethod(jklass_, jmethod_);
@@ -581,7 +565,6 @@
 JNI_TEST(RunStaticReturnTrue)
 
 void JniCompilerTest::RunStaticReturnFalseImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "returnFalse", "()Z",
                reinterpret_cast<void*>(&Java_MyClassNatives_returnFalse));
 
@@ -592,7 +575,6 @@
 JNI_TEST(RunStaticReturnFalse)
 
 void JniCompilerTest::RunGenericStaticReturnIntImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "returnInt", "()I", reinterpret_cast<void*>(&Java_MyClassNatives_returnInt));
 
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_);
@@ -626,7 +608,6 @@
 
 
 void JniCompilerTest::CompileAndRunStaticIntObjectObjectMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooSIOO));
@@ -684,7 +665,6 @@
 }
 
 void JniCompilerTest::CompileAndRunStaticSynchronizedIntObjectObjectMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "fooSSIOO",
                "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooSSIOO));
@@ -725,7 +705,6 @@
 }
 
 void JniCompilerTest::ExceptionHandlingImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   {
     ASSERT_FALSE(runtime_->IsStarted());
     ScopedObjectAccess soa(Thread::Current());
@@ -810,7 +789,6 @@
 }
 
 void JniCompilerTest::NativeStackTraceElementImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooI", "(I)I",
                reinterpret_cast<void*>(&Java_MyClassNatives_nativeUpCall));
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 10);
@@ -824,7 +802,6 @@
 }
 
 void JniCompilerTest::ReturnGlobalRefImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_fooO));
   jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, jobj_);
@@ -844,7 +821,6 @@
 }
 
 void JniCompilerTest::LocalReferenceTableClearingTestImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "fooI", "(I)I", reinterpret_cast<void*>(&local_ref_test));
   // 1000 invocations of a method that adds 10 local references
   for (int i = 0; i < 1000; i++) {
@@ -865,7 +841,6 @@
 }
 
 void JniCompilerTest::JavaLangSystemArrayCopyImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V",
                reinterpret_cast<void*>(&my_arraycopy));
   env_->CallStaticVoidMethod(jklass_, jmethod_, jobj_, 1234, jklass_, 5678, 9876);
@@ -883,7 +858,6 @@
 }
 
 void JniCompilerTest::CompareAndSwapIntImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
                reinterpret_cast<void*>(&my_casi));
   jboolean result = env_->CallBooleanMethod(jobj_, jmethod_, jobj_, INT64_C(0x12345678ABCDEF88),
@@ -903,7 +877,6 @@
 }
 
 void JniCompilerTest::GetTextImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "getText", "(JLjava/lang/Object;JLjava/lang/Object;)I",
                reinterpret_cast<void*>(&my_gettext));
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 0x12345678ABCDEF88ll, jobj_,
@@ -931,7 +904,6 @@
 }
 
 void JniCompilerTest::GetSinkPropertiesNativeImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
                reinterpret_cast<void*>(&Java_MyClassNatives_GetSinkProperties));
 
@@ -957,12 +929,10 @@
 }
 
 void JniCompilerTest::UpcallReturnTypeChecking_InstanceImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "instanceMethodThatShouldReturnClass", "()Ljava/lang/Class;",
                reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldReturnClass));
 
   CheckJniAbortCatcher check_jni_abort_catcher;
-  // TODO: check type of returns with portable JNI compiler.
   // 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()");
@@ -977,12 +947,10 @@
 JNI_TEST(UpcallReturnTypeChecking_Instance)
 
 void JniCompilerTest::UpcallReturnTypeChecking_StaticImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "staticMethodThatShouldReturnClass", "()Ljava/lang/Class;",
                reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldReturnClass));
 
   CheckJniAbortCatcher check_jni_abort_catcher;
-  // TODO: check type of returns with portable JNI compiler.
   // 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()");
@@ -1005,7 +973,9 @@
 }
 
 void JniCompilerTest::UpcallArgumentTypeChecking_InstanceImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
+  // This will lead to error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   SetUpForTest(false, "instanceMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_instanceMethodThatShouldTakeClass));
 
@@ -1018,7 +988,9 @@
 JNI_TEST(UpcallArgumentTypeChecking_Instance)
 
 void JniCompilerTest::UpcallArgumentTypeChecking_StaticImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
+  // This will lead to error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   SetUpForTest(true, "staticMethodThatShouldTakeClass", "(ILjava/lang/Class;)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_staticMethodThatShouldTakeClass));
 
@@ -1041,7 +1013,6 @@
 }
 
 void JniCompilerTest::CompileAndRunFloatFloatMethodImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "checkFloats", "(FF)F",
                reinterpret_cast<void*>(&Java_MyClassNatives_checkFloats));
 
@@ -1071,7 +1042,6 @@
 }
 
 void JniCompilerTest::CheckParameterAlignImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "checkParameterAlign", "(IJ)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_checkParameterAlign));
 
@@ -1486,7 +1456,6 @@
     "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V";
 
 void JniCompilerTest::MaxParamNumberImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(false, "maxParamNumber", longSig,
                reinterpret_cast<void*>(&Java_MyClassNatives_maxParamNumber));
 
@@ -1512,7 +1481,9 @@
 JNI_TEST(MaxParamNumber)
 
 void JniCompilerTest::WithoutImplementationImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
+  // This will lead to error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   SetUpForTest(false, "withoutImplementation", "()V", nullptr);
 
   env_->CallVoidMethod(jobj_, jmethod_);
@@ -1562,7 +1533,6 @@
 }
 
 void JniCompilerTest::StackArgsIntsFirstImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "stackArgsIntsFirst", "(IIIIIIIIIIFFFFFFFFFF)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsIntsFirst));
 
@@ -1633,7 +1603,6 @@
 }
 
 void JniCompilerTest::StackArgsFloatsFirstImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "stackArgsFloatsFirst", "(FFFFFFFFFFIIIIIIIIII)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsFloatsFirst));
 
@@ -1703,7 +1672,6 @@
 }
 
 void JniCompilerTest::StackArgsMixedImpl() {
-  TEST_DISABLED_FOR_PORTABLE();
   SetUpForTest(true, "stackArgsMixed", "(IFIFIFIFIFIFIFIFIFIF)V",
                reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsMixed));
 
diff --git a/compiler/jni/portable/jni_compiler.cc b/compiler/jni/portable/jni_compiler.cc
deleted file mode 100644
index ff37d85..0000000
--- a/compiler/jni/portable/jni_compiler.cc
+++ /dev/null
@@ -1,322 +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 "jni_compiler.h"
-
-#include "base/logging.h"
-#include "class_linker.h"
-#include "compiled_method.h"
-#include "dex_file-inl.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "llvm/compiler_llvm.h"
-#include "llvm/ir_builder.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "llvm/runtime_support_llvm_func.h"
-#include "llvm/utils_llvm.h"
-#include "mirror/art_method.h"
-#include "runtime.h"
-#include "stack.h"
-#include "thread.h"
-
-#include <llvm/ADT/SmallVector.h>
-#include <llvm/IR/BasicBlock.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Type.h>
-
-namespace art {
-namespace llvm {
-
-using ::art::llvm::runtime_support::JniMethodEnd;
-using ::art::llvm::runtime_support::JniMethodEndSynchronized;
-using ::art::llvm::runtime_support::JniMethodEndWithReference;
-using ::art::llvm::runtime_support::JniMethodEndWithReferenceSynchronized;
-using ::art::llvm::runtime_support::JniMethodStart;
-using ::art::llvm::runtime_support::JniMethodStartSynchronized;
-using ::art::llvm::runtime_support::RuntimeId;
-
-JniCompiler::JniCompiler(LlvmCompilationUnit* cunit,
-                         CompilerDriver* driver,
-                         const DexCompilationUnit* dex_compilation_unit)
-    : cunit_(cunit), driver_(driver), module_(cunit_->GetModule()),
-      context_(cunit_->GetLLVMContext()), irb_(*cunit_->GetIRBuilder()),
-      dex_compilation_unit_(dex_compilation_unit),
-      func_(NULL), elf_func_idx_(0) {
-  // Check: Ensure that JNI compiler will only get "native" method
-  CHECK(dex_compilation_unit->IsNative());
-}
-
-CompiledMethod* JniCompiler::Compile() {
-  const bool is_static = dex_compilation_unit_->IsStatic();
-  const bool is_synchronized = dex_compilation_unit_->IsSynchronized();
-  const DexFile* dex_file = dex_compilation_unit_->GetDexFile();
-  DexFile::MethodId const& method_id =
-      dex_file->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
-  char const return_shorty = dex_file->GetMethodShorty(method_id)[0];
-  ::llvm::Value* this_object_or_class_object;
-
-  uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex();
-  std::string func_name(StringPrintf("jni_%s",
-                                     MangleForJni(PrettyMethod(method_idx, *dex_file)).c_str()));
-  CreateFunction(func_name);
-
-  // Set argument name
-  ::llvm::Function::arg_iterator arg_begin(func_->arg_begin());
-  ::llvm::Function::arg_iterator arg_end(func_->arg_end());
-  ::llvm::Function::arg_iterator arg_iter(arg_begin);
-
-  DCHECK_NE(arg_iter, arg_end);
-  arg_iter->setName("method");
-  ::llvm::Value* method_object_addr = arg_iter++;
-
-  if (!is_static) {
-    // Non-static, the second argument is "this object"
-    this_object_or_class_object = arg_iter++;
-  } else {
-    // Load class object
-    this_object_or_class_object =
-        irb_.LoadFromObjectOffset(method_object_addr,
-                                  mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
-                                  irb_.getJObjectTy(),
-                                  kTBAAConstJObject);
-  }
-  // Actual argument (ignore method and this object)
-  arg_begin = arg_iter;
-
-  // Count the number of Object* arguments
-  uint32_t handle_scope_size = 1;
-  // "this" object pointer for non-static
-  // "class" object pointer for static
-  for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
-#if !defined(NDEBUG)
-    arg_iter->setName(StringPrintf("a%u", i));
-#endif
-    if (arg_iter->getType() == irb_.getJObjectTy()) {
-      ++handle_scope_size;
-    }
-  }
-
-  // Shadow stack
-  ::llvm::StructType* shadow_frame_type = irb_.getShadowFrameTy(handle_scope_size);
-  ::llvm::AllocaInst* shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
-
-  // Store the dex pc
-  irb_.StoreToObjectOffset(shadow_frame_,
-                           ShadowFrame::DexPCOffset(),
-                           irb_.getInt32(DexFile::kDexNoIndex),
-                           kTBAAShadowFrame);
-
-  // Push the shadow frame
-  ::llvm::Value* shadow_frame_upcast = irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
-  ::llvm::Value* old_shadow_frame =
-      irb_.Runtime().EmitPushShadowFrame(shadow_frame_upcast, method_object_addr, handle_scope_size);
-
-  // Get JNIEnv
-  ::llvm::Value* jni_env_object_addr =
-      irb_.Runtime().EmitLoadFromThreadOffset(Thread::JniEnvOffset().Int32Value(),
-                                              irb_.getJObjectTy(),
-                                              kTBAARuntimeInfo);
-
-  // Get callee code_addr
-  ::llvm::Value* code_addr =
-      irb_.LoadFromObjectOffset(method_object_addr,
-                                mirror::ArtMethod::NativeMethodOffset().Int32Value(),
-                                GetFunctionType(dex_compilation_unit_->GetDexMethodIndex(),
-                                                is_static, true)->getPointerTo(),
-                                kTBAARuntimeInfo);
-
-  // Load actual parameters
-  std::vector< ::llvm::Value*> args;
-
-  // The 1st parameter: JNIEnv*
-  args.push_back(jni_env_object_addr);
-
-  // Variables for GetElementPtr
-  ::llvm::Value* gep_index[] = {
-    irb_.getInt32(0),  // No displacement for shadow frame pointer
-    irb_.getInt32(1),  // handle scope
-    NULL,
-  };
-
-  size_t handle_scope_member_index = 0;
-
-  // Store the "this object or class object" to handle scope
-  gep_index[2] = irb_.getInt32(handle_scope_member_index++);
-  ::llvm::Value* handle_scope_field_addr = irb_.CreateBitCast(irb_.CreateGEP(shadow_frame_, gep_index),
-                                                    irb_.getJObjectTy()->getPointerTo());
-  irb_.CreateStore(this_object_or_class_object, handle_scope_field_addr, kTBAAShadowFrame);
-  // Push the "this object or class object" to out args
-  this_object_or_class_object = irb_.CreateBitCast(handle_scope_field_addr, irb_.getJObjectTy());
-  args.push_back(this_object_or_class_object);
-  // Store arguments to handle scope, and push back to args
-  for (arg_iter = arg_begin; arg_iter != arg_end; ++arg_iter) {
-    if (arg_iter->getType() == irb_.getJObjectTy()) {
-      // Store the reference type arguments to handle scope
-      gep_index[2] = irb_.getInt32(handle_scope_member_index++);
-      ::llvm::Value* handle_scope_field_addr = irb_.CreateBitCast(irb_.CreateGEP(shadow_frame_, gep_index),
-                                                        irb_.getJObjectTy()->getPointerTo());
-      irb_.CreateStore(arg_iter, handle_scope_field_addr, kTBAAShadowFrame);
-      // Note null is placed in the handle scope but the jobject passed to the native code must be null
-      // (not a pointer into the handle scope as with regular references).
-      ::llvm::Value* equal_null = irb_.CreateICmpEQ(arg_iter, irb_.getJNull());
-      ::llvm::Value* arg =
-          irb_.CreateSelect(equal_null,
-                            irb_.getJNull(),
-                            irb_.CreateBitCast(handle_scope_field_addr, irb_.getJObjectTy()));
-      args.push_back(arg);
-    } else {
-      args.push_back(arg_iter);
-    }
-  }
-
-  ::llvm::Value* saved_local_ref_cookie;
-  {  // JniMethodStart
-    RuntimeId func_id = is_synchronized ? JniMethodStartSynchronized
-                                        : JniMethodStart;
-    ::llvm::SmallVector< ::llvm::Value*, 2> args;
-    if (is_synchronized) {
-      args.push_back(this_object_or_class_object);
-    }
-    args.push_back(irb_.Runtime().EmitGetCurrentThread());
-    saved_local_ref_cookie =
-        irb_.CreateCall(irb_.GetRuntime(func_id), args);
-  }
-
-  // Call!!!
-  ::llvm::Value* retval = irb_.CreateCall(code_addr, args);
-
-  {  // JniMethodEnd
-    bool is_return_ref = return_shorty == 'L';
-    RuntimeId func_id =
-        is_return_ref ? (is_synchronized ? JniMethodEndWithReferenceSynchronized
-                                         : JniMethodEndWithReference)
-                      : (is_synchronized ? JniMethodEndSynchronized
-                                         : JniMethodEnd);
-    ::llvm::SmallVector< ::llvm::Value*, 4> args;
-    if (is_return_ref) {
-      args.push_back(retval);
-    }
-    args.push_back(saved_local_ref_cookie);
-    if (is_synchronized) {
-      args.push_back(this_object_or_class_object);
-    }
-    args.push_back(irb_.Runtime().EmitGetCurrentThread());
-
-    ::llvm::Value* decoded_jobject =
-        irb_.CreateCall(irb_.GetRuntime(func_id), args);
-
-    // Return decoded jobject if return reference.
-    if (is_return_ref) {
-      retval = decoded_jobject;
-    }
-  }
-
-  // Pop the shadow frame
-  irb_.Runtime().EmitPopShadowFrame(old_shadow_frame);
-
-  // Return!
-  switch (return_shorty) {
-    case 'V':
-      irb_.CreateRetVoid();
-      break;
-    case 'Z':
-    case 'C':
-      irb_.CreateRet(irb_.CreateZExt(retval, irb_.getInt32Ty()));
-      break;
-    case 'B':
-    case 'S':
-      irb_.CreateRet(irb_.CreateSExt(retval, irb_.getInt32Ty()));
-      break;
-    default:
-      irb_.CreateRet(retval);
-      break;
-  }
-
-  // Verify the generated bitcode
-  VERIFY_LLVM_FUNCTION(*func_);
-
-  cunit_->Materialize();
-
-  return new CompiledMethod(*driver_, cunit_->GetInstructionSet(), cunit_->GetElfObject(),
-                            func_name);
-}
-
-
-void JniCompiler::CreateFunction(const std::string& func_name) {
-  CHECK_NE(0U, func_name.size());
-
-  const bool is_static = dex_compilation_unit_->IsStatic();
-
-  // Get function type
-  ::llvm::FunctionType* func_type =
-    GetFunctionType(dex_compilation_unit_->GetDexMethodIndex(), is_static, false);
-
-  // Create function
-  func_ = ::llvm::Function::Create(func_type, ::llvm::Function::InternalLinkage,
-                                   func_name, module_);
-
-  // Create basic block
-  ::llvm::BasicBlock* basic_block = ::llvm::BasicBlock::Create(*context_, "B0", func_);
-
-  // Set insert point
-  irb_.SetInsertPoint(basic_block);
-}
-
-
-::llvm::FunctionType* JniCompiler::GetFunctionType(uint32_t method_idx,
-                                                   bool is_static, bool is_native_function) {
-  // Get method signature
-  uint32_t shorty_size;
-  const char* shorty = dex_compilation_unit_->GetShorty(&shorty_size);
-  CHECK_GE(shorty_size, 1u);
-
-  // Get return type
-  ::llvm::Type* ret_type = NULL;
-  switch (shorty[0]) {
-    case 'V': ret_type =  irb_.getJVoidTy(); break;
-    case 'Z':
-    case 'B':
-    case 'C':
-    case 'S':
-    case 'I': ret_type =  irb_.getJIntTy(); break;
-    case 'F': ret_type =  irb_.getJFloatTy(); break;
-    case 'J': ret_type =  irb_.getJLongTy(); break;
-    case 'D': ret_type =  irb_.getJDoubleTy(); break;
-    case 'L': ret_type =  irb_.getJObjectTy(); break;
-    default: LOG(FATAL)  << "Unreachable: unexpected return type in shorty " << shorty;
-      UNREACHABLE();
-  }
-  // Get argument type
-  std::vector< ::llvm::Type*> args_type;
-
-  args_type.push_back(irb_.getJObjectTy());  // method object pointer
-
-  if (!is_static || is_native_function) {
-    // "this" object pointer for non-static
-    // "class" object pointer for static naitve
-    args_type.push_back(irb_.getJType('L'));
-  }
-
-  for (uint32_t i = 1; i < shorty_size; ++i) {
-    args_type.push_back(irb_.getJType(shorty[i]));
-  }
-
-  return ::llvm::FunctionType::get(ret_type, args_type, false);
-}
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/jni/portable/jni_compiler.h b/compiler/jni/portable/jni_compiler.h
deleted file mode 100644
index ffabfe6..0000000
--- a/compiler/jni/portable/jni_compiler.h
+++ /dev/null
@@ -1,87 +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_COMPILER_JNI_PORTABLE_JNI_COMPILER_H_
-#define ART_COMPILER_JNI_PORTABLE_JNI_COMPILER_H_
-
-#include <stdint.h>
-
-#include <string>
-
-namespace art {
-  class ClassLinker;
-  class CompiledMethod;
-  class CompilerDriver;
-  class DexFile;
-  class DexCompilationUnit;
-  namespace mirror {
-    class ArtMethod;
-    class ClassLoader;
-    class DexCache;
-  }  // namespace mirror
-}  // namespace art
-
-namespace llvm {
-  class AllocaInst;
-  class Function;
-  class FunctionType;
-  class BasicBlock;
-  class LLVMContext;
-  class Module;
-  class Type;
-  class Value;
-}  // namespace llvm
-
-namespace art {
-namespace llvm {
-
-class LlvmCompilationUnit;
-class IRBuilder;
-
-class JniCompiler {
- public:
-  JniCompiler(LlvmCompilationUnit* cunit,
-              CompilerDriver* driver,
-              const DexCompilationUnit* dex_compilation_unit);
-
-  CompiledMethod* Compile();
-
- private:
-  void CreateFunction(const std::string& symbol);
-
-  ::llvm::FunctionType* GetFunctionType(uint32_t method_idx,
-                                        bool is_static, bool is_target_function);
-
- private:
-  LlvmCompilationUnit* cunit_;
-  CompilerDriver* const driver_;
-
-  ::llvm::Module* module_;
-  ::llvm::LLVMContext* context_;
-  IRBuilder& irb_;
-
-  const DexCompilationUnit* const dex_compilation_unit_;
-
-  ::llvm::Function* func_;
-  uint16_t elf_func_idx_;
-};
-
-
-}  // namespace llvm
-}  // namespace art
-
-
-#endif  // ART_COMPILER_JNI_PORTABLE_JNI_COMPILER_H_
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index 769cd4c..669c3bb 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -16,6 +16,7 @@
 
 #include "base/logging.h"
 #include "calling_convention_arm.h"
+#include "handle_scope-inl.h"
 #include "utils/arm/managed_register_arm.h"
 
 namespace art {
@@ -167,9 +168,20 @@
         } else {
           // FIXME: Pointer this returns as both reference and long.
           if (IsCurrentParamALong() && !IsCurrentParamAReference()) {  // Long.
-            if (gpr_index < arraysize(kHFCoreArgumentRegisters)) {
+            if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) {
+              // Skip R1, and use R2_R3 if the long is the first parameter.
+              if (gpr_index == 1) {
+                gpr_index++;
+              }
+            }
+
+            // If it spans register and memory, we must use the value in memory.
+            if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) {
               entry_spills_.push_back(
                   ArmManagedRegister::FromCoreRegister(kHFCoreArgumentRegisters[gpr_index++]));
+            } else if (gpr_index == arraysize(kHFCoreArgumentRegisters) - 1) {
+              gpr_index++;
+              entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
             } else {
               entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
             }
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index 29763a2..b9c8178 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -16,6 +16,7 @@
 
 #include "base/logging.h"
 #include "calling_convention_arm64.h"
+#include "handle_scope-inl.h"
 #include "utils/arm64/managed_register_arm64.h"
 
 namespace art {
diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h
index 6db0c3b..0c64a36 100644
--- a/compiler/jni/quick/calling_convention.h
+++ b/compiler/jni/quick/calling_convention.h
@@ -141,7 +141,7 @@
     if (IsStatic()) {
       param++;  // 0th argument must skip return value at start of the shorty
     } else if (param == 0) {
-      return true;  // this argument
+      return false;  // this argument
     }
     return shorty_[param] == 'J';
   }
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index c3fe75b..ba73828 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -135,6 +135,7 @@
     FrameOffset handle_scope_offset = main_jni_conv->CurrentParamHandleScopeEntryOffset();
     // Check handle scope offset is within frame
     CHECK_LT(handle_scope_offset.Uint32Value(), frame_size);
+    // TODO: Insert the read barrier for this load.
     __ LoadRef(main_jni_conv->InterproceduralScratchRegister(),
                mr_conv->MethodRegister(), mirror::ArtMethod::DeclaringClassOffset());
     __ VerifyObject(main_jni_conv->InterproceduralScratchRegister(), false);
@@ -430,13 +431,18 @@
   MemoryRegion code(&managed_code[0], managed_code.size());
   __ FinalizeInstructions(code);
   jni_asm->FinalizeFrameDescriptionEntry();
-  return new CompiledMethod(driver,
-                            instruction_set,
-                            managed_code,
-                            frame_size,
-                            main_jni_conv->CoreSpillMask(),
-                            main_jni_conv->FpSpillMask(),
-                            jni_asm->GetFrameDescriptionEntry());
+  std::vector<uint8_t>* fde(jni_asm->GetFrameDescriptionEntry());
+  ArrayRef<const uint8_t> cfi_ref;
+  if (fde != nullptr) {
+    cfi_ref = ArrayRef<const uint8_t>(*fde);
+  }
+  return CompiledMethod::SwapAllocCompiledMethodCFI(driver,
+                                                    instruction_set,
+                                                    ArrayRef<const uint8_t>(managed_code),
+                                                    frame_size,
+                                                    main_jni_conv->CoreSpillMask(),
+                                                    main_jni_conv->FpSpillMask(),
+                                                    cfi_ref);
 }
 
 // Copy a single parameter from the managed to the JNI calling convention
diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc
index f7a7be7..aefbf06 100644
--- a/compiler/jni/quick/mips/calling_convention_mips.cc
+++ b/compiler/jni/quick/mips/calling_convention_mips.cc
@@ -17,6 +17,7 @@
 #include "calling_convention_mips.h"
 
 #include "base/logging.h"
+#include "handle_scope-inl.h"
 #include "utils/mips/managed_register_mips.h"
 
 namespace art {
diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc
index 9bf7d0f..8a45f0c 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.cc
+++ b/compiler/jni/quick/x86/calling_convention_x86.cc
@@ -17,6 +17,7 @@
 #include "calling_convention_x86.h"
 
 #include "base/logging.h"
+#include "handle_scope-inl.h"
 #include "utils/x86/managed_register_x86.h"
 #include "utils.h"
 
@@ -76,12 +77,44 @@
 }
 
 bool X86ManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
-  return true;  // Everything is passed by stack
+  // We assume all parameters are on stack, args coming via registers are spilled as entry_spills.
+  return true;
 }
 
 ManagedRegister X86ManagedRuntimeCallingConvention::CurrentParamRegister() {
-  LOG(FATAL) << "Should not reach here";
-  return ManagedRegister::NoRegister();
+  ManagedRegister res = ManagedRegister::NoRegister();
+  if (!IsCurrentParamAFloatOrDouble()) {
+    switch (gpr_arg_count_) {
+      case 0:
+        res = X86ManagedRegister::FromCpuRegister(ECX);
+        break;
+      case 1:
+        res = X86ManagedRegister::FromCpuRegister(EDX);
+        break;
+      case 2:
+        // Don't split a long between the last register and the stack.
+        if (IsCurrentParamALong()) {
+          return ManagedRegister::NoRegister();
+        }
+        res = X86ManagedRegister::FromCpuRegister(EBX);
+        break;
+    }
+  } else if (itr_float_and_doubles_ < 4) {
+    // First four float parameters are passed via XMM0..XMM3
+    res = X86ManagedRegister::FromXmmRegister(
+                                 static_cast<XmmRegister>(XMM0 + itr_float_and_doubles_));
+  }
+  return res;
+}
+
+ManagedRegister X86ManagedRuntimeCallingConvention::CurrentParamHighLongRegister() {
+  ManagedRegister res = ManagedRegister::NoRegister();
+  DCHECK(IsCurrentParamALong());
+  switch (gpr_arg_count_) {
+    case 0: res = X86ManagedRegister::FromCpuRegister(EDX); break;
+    case 1: res = X86ManagedRegister::FromCpuRegister(EBX); break;
+  }
+  return res;
 }
 
 FrameOffset X86ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
@@ -94,15 +127,39 @@
   // We spill the argument registers on X86 to free them up for scratch use, we then assume
   // all arguments are on the stack.
   if (entry_spills_.size() == 0) {
-    size_t num_spills = NumArgs() + NumLongOrDoubleArgs();
-    if (num_spills > 0) {
-      entry_spills_.push_back(X86ManagedRegister::FromCpuRegister(ECX));
-      if (num_spills > 1) {
-        entry_spills_.push_back(X86ManagedRegister::FromCpuRegister(EDX));
-        if (num_spills > 2) {
-          entry_spills_.push_back(X86ManagedRegister::FromCpuRegister(EBX));
+    ResetIterator(FrameOffset(0));
+    while (HasNext()) {
+      ManagedRegister in_reg = CurrentParamRegister();
+      bool is_long = IsCurrentParamALong();
+      if (!in_reg.IsNoRegister()) {
+        int32_t size = IsParamADouble(itr_args_) ? 8 : 4;
+        int32_t spill_offset = CurrentParamStackOffset().Uint32Value();
+        ManagedRegisterSpill spill(in_reg, size, spill_offset);
+        entry_spills_.push_back(spill);
+        if (is_long) {
+          // special case, as we need a second register here.
+          in_reg = CurrentParamHighLongRegister();
+          DCHECK(!in_reg.IsNoRegister());
+          // We have to spill the second half of the long.
+          ManagedRegisterSpill spill2(in_reg, size, spill_offset + 4);
+          entry_spills_.push_back(spill2);
         }
+
+        // Keep track of the number of GPRs allocated.
+        if (!IsCurrentParamAFloatOrDouble()) {
+          if (is_long) {
+            // Long was allocated in 2 registers.
+            gpr_arg_count_ += 2;
+          } else {
+            gpr_arg_count_++;
+          }
+        }
+      } else if (is_long) {
+        // We need to skip the unused last register, which is empty.
+        // If we are already out of registers, this is harmless.
+        gpr_arg_count_ += 2;
       }
+      Next();
     }
   }
   return entry_spills_;
diff --git a/compiler/jni/quick/x86/calling_convention_x86.h b/compiler/jni/quick/x86/calling_convention_x86.h
index 025eb6d..b1b3598 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.h
+++ b/compiler/jni/quick/x86/calling_convention_x86.h
@@ -28,7 +28,8 @@
  public:
   explicit X86ManagedRuntimeCallingConvention(bool is_static, bool is_synchronized,
                                               const char* shorty)
-      : ManagedRuntimeCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {}
+      : ManagedRuntimeCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize),
+        gpr_arg_count_(0) {}
   ~X86ManagedRuntimeCallingConvention() OVERRIDE {}
   // Calling convention
   ManagedRegister ReturnRegister() OVERRIDE;
@@ -40,7 +41,10 @@
   ManagedRegister CurrentParamRegister() OVERRIDE;
   FrameOffset CurrentParamStackOffset() OVERRIDE;
   const ManagedRegisterEntrySpills& EntrySpills() OVERRIDE;
+
  private:
+  int gpr_arg_count_;
+  ManagedRegister CurrentParamHighLongRegister();
   ManagedRegisterEntrySpills entry_spills_;
   DISALLOW_COPY_AND_ASSIGN(X86ManagedRuntimeCallingConvention);
 };
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 a100552..bbdf1fe 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -17,6 +17,7 @@
 #include "calling_convention_x86_64.h"
 
 #include "base/logging.h"
+#include "handle_scope-inl.h"
 #include "utils/x86_64/managed_register_x86_64.h"
 #include "utils.h"
 
diff --git a/compiler/llvm/art_module.ll b/compiler/llvm/art_module.ll
deleted file mode 100644
index 233692c..0000000
--- a/compiler/llvm/art_module.ll
+++ /dev/null
@@ -1,153 +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.
-;;
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Type
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-%JavaObject = type opaque
-
-%ShadowFrame = type { i32                  ; Number of VRegs
-                    , %ShadowFrame*        ; Previous frame
-                    , %JavaObject*         ; Method object pointer
-                    , i32                  ; Line number for stack backtrace
-                    ; [0 x i32]            ; VRegs
-                    }
-
-declare void @__art_type_list(%JavaObject*, %ShadowFrame*)
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Thread
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare %JavaObject* @art_portable_get_current_thread_from_code()
-declare %JavaObject* @art_portable_set_current_thread_from_code(%JavaObject*)
-
-declare void @art_portable_lock_object_from_code(%JavaObject*, %JavaObject*)
-declare void @art_portable_unlock_object_from_code(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_test_suspend_from_code(%JavaObject*)
-
-declare %ShadowFrame* @art_portable_push_shadow_frame_from_code(%JavaObject*, %ShadowFrame*, %JavaObject*, i32)
-declare void @art_portable_pop_shadow_frame_from_code(%ShadowFrame*)
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Exception
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare %JavaObject* @art_portable_get_and_clear_exception(%JavaObject*)
-declare void @art_portable_throw_div_zero_from_code()
-declare void @art_portable_throw_array_bounds_from_code(i32, i32)
-declare void @art_portable_throw_no_such_method_from_code(i32)
-declare void @art_portable_throw_null_pointer_exception_from_code(i32)
-declare void @art_portable_throw_stack_overflow_from_code()
-declare void @art_portable_throw_exception_from_code(%JavaObject*)
-
-declare i32 @art_portable_find_catch_block_from_code(%JavaObject*, i32)
-
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Object Space
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare %JavaObject* @art_portable_alloc_object_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_alloc_object_from_code_with_access_check(i32, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_alloc_array_from_code(i32, %JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_alloc_array_from_code_with_access_check(i32, %JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_check_and_alloc_array_from_code(i32, %JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_check_and_alloc_array_from_code_with_access_check(i32, %JavaObject*, i32, %JavaObject*)
-
-declare void @art_portable_find_instance_field_from_code(i32, %JavaObject*)
-declare void @art_portable_find_static_field_from_code(i32, %JavaObject*)
-
-declare %JavaObject* @art_portable_find_static_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_direct_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_virtual_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_super_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_interface_method_from_code_with_access_check(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_find_interface_method_from_code(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_initialize_static_storage_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_initialize_type_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_initialize_type_and_verify_access_from_code(i32, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_resolve_string_from_code(%JavaObject*, i32)
-
-declare i32 @art_portable_set32_static_from_code(i32, %JavaObject*, i32)
-declare i32 @art_portable_set64_static_from_code(i32, %JavaObject*, i64)
-declare i32 @art_portable_set_obj_static_from_code(i32, %JavaObject*, %JavaObject*)
-
-declare i32 @art_portable_get32_static_from_code(i32, %JavaObject*)
-declare i64 @art_portable_get64_static_from_code(i32, %JavaObject*)
-declare %JavaObject* @art_portable_get_obj_static_from_code(i32, %JavaObject*)
-
-declare i32 @art_portable_set32_instance_from_code(i32, %JavaObject*, %JavaObject*, i32)
-declare i32 @art_portable_set64_instance_from_code(i32, %JavaObject*, %JavaObject*, i64)
-declare i32 @art_portable_set_obj_instance_from_code(i32, %JavaObject*, %JavaObject*, %JavaObject*)
-
-declare i32 @art_portable_get32_instance_from_code(i32, %JavaObject*, %JavaObject*)
-declare i64 @art_portable_get64_instance_from_code(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_get_obj_instance_from_code(i32, %JavaObject*, %JavaObject*)
-
-declare %JavaObject* @art_portable_decode_jobject_in_thread(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_fill_array_data_from_code(%JavaObject*, i32, %JavaObject*, i32)
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Type Checking, in the nature of casting
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i32 @art_portable_is_assignable_from_code(%JavaObject*, %JavaObject*)
-declare void @art_portable_check_cast_from_code(%JavaObject*, %JavaObject*)
-declare void @art_portable_check_put_array_element_from_code(%JavaObject*, %JavaObject*)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Math
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i64 @art_d2l(double)
-declare i32 @art_d2i(double)
-declare i64 @art_f2l(float)
-declare i32 @art_f2i(float)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; JNI
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i32 @art_portable_jni_method_start(%JavaObject*)
-declare i32 @art_portable_jni_method_start_synchronized(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_jni_method_end(i32, %JavaObject*)
-declare void @art_portable_jni_method_end_synchronized(i32, %JavaObject*, %JavaObject*)
-declare %JavaObject* @art_portable_jni_method_end_with_reference(%JavaObject*, i32, %JavaObject*)
-declare %JavaObject* @art_portable_jni_method_end_with_reference_synchronized(%JavaObject*, i32, %JavaObject*, %JavaObject*)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Temporary runtime support, will be removed in the future
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-declare i1 @art_portable_is_exception_pending_from_code()
-
-declare void @art_portable_mark_gc_card_from_code(%JavaObject*, %JavaObject*)
-
-declare void @art_portable_proxy_invoke_handler_from_code(%JavaObject*, ...)
diff --git a/compiler/llvm/backend_options.h b/compiler/llvm/backend_options.h
deleted file mode 100644
index 2a08bda..0000000
--- a/compiler/llvm/backend_options.h
+++ /dev/null
@@ -1,50 +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_COMPILER_LLVM_BACKEND_OPTIONS_H_
-#define ART_COMPILER_LLVM_BACKEND_OPTIONS_H_
-
-#include <llvm/Support/CommandLine.h>
-
-#define DECLARE_ARM_BACKEND_OPTIONS \
-extern llvm::cl::opt<bool> EnableARMLongCalls; \
-extern llvm::cl::opt<bool> ReserveR9;
-
-#define INITIAL_ARM_BACKEND_OPTIONS \
-EnableARMLongCalls = true; \
-ReserveR9 = true;
-
-#define DECLARE_X86_BACKEND_OPTIONS
-#define INITIAL_X86_BACKEND_OPTIONS
-
-#define DECLARE_Mips_BACKEND_OPTIONS
-#define INITIAL_Mips_BACKEND_OPTIONS
-
-#define LLVM_TARGET(TargetName) DECLARE_##TargetName##_BACKEND_OPTIONS
-#include "llvm/Config/Targets.def"
-
-namespace art {
-namespace llvm {
-
-inline void InitialBackendOptions() {
-#define LLVM_TARGET(TargetName) INITIAL_##TargetName##_BACKEND_OPTIONS
-#include "llvm/Config/Targets.def"
-}
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_BACKEND_OPTIONS_H_
diff --git a/compiler/llvm/backend_types.h b/compiler/llvm/backend_types.h
deleted file mode 100644
index 8ca88dd..0000000
--- a/compiler/llvm/backend_types.h
+++ /dev/null
@@ -1,104 +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_COMPILER_LLVM_BACKEND_TYPES_H_
-#define ART_COMPILER_LLVM_BACKEND_TYPES_H_
-
-#include "base/logging.h"
-
-
-namespace art {
-namespace llvm {
-
-
-enum JType {
-  kVoid,
-  kBoolean,
-  kByte,
-  kChar,
-  kShort,
-  kInt,
-  kLong,
-  kFloat,
-  kDouble,
-  kObject,
-  MAX_JTYPE
-};
-
-enum TBAASpecialType {
-  kTBAARegister,
-  kTBAAStackTemp,
-  kTBAAHeapArray,
-  kTBAAHeapInstance,
-  kTBAAHeapStatic,
-  kTBAAJRuntime,
-  kTBAARuntimeInfo,
-  kTBAAShadowFrame,
-  kTBAAConstJObject,
-  MAX_TBAA_SPECIAL_TYPE
-};
-
-
-enum ExpectCond {
-  kLikely,
-  kUnlikely,
-  MAX_EXPECT
-};
-
-
-inline JType GetJTypeFromShorty(char shorty_jty) {
-  switch (shorty_jty) {
-  case 'V':
-    return kVoid;
-
-  case 'Z':
-    return kBoolean;
-
-  case 'B':
-    return kByte;
-
-  case 'C':
-    return kChar;
-
-  case 'S':
-    return kShort;
-
-  case 'I':
-    return kInt;
-
-  case 'J':
-    return kLong;
-
-  case 'F':
-    return kFloat;
-
-  case 'D':
-    return kDouble;
-
-  case 'L':
-    return kObject;
-
-  default:
-    LOG(FATAL) << "Unknown Dalvik shorty descriptor: " << shorty_jty;
-    return kVoid;
-  }
-}
-
-}  // namespace llvm
-}  // namespace art
-
-
-#endif  // ART_COMPILER_LLVM_BACKEND_TYPES_H_
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
deleted file mode 100644
index 3aeecad..0000000
--- a/compiler/llvm/compiler_llvm.cc
+++ /dev/null
@@ -1,233 +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 "compiler_llvm.h"
-
-#include "backend_options.h"
-#include "base/stl_util.h"
-#include "class_linker.h"
-#include "compiled_method.h"
-#include "dex/verification_results.h"
-#include "dex/verified_method.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "globals.h"
-#include "ir_builder.h"
-#include "jni/portable/jni_compiler.h"
-#include "llvm_compilation_unit.h"
-#include "thread-inl.h"
-#include "utils_llvm.h"
-#include "verifier/method_verifier.h"
-
-#include <llvm/LinkAllPasses.h>
-#include <llvm/Support/ManagedStatic.h>
-#include <llvm/Support/TargetSelect.h>
-#include <llvm/Support/Threading.h>
-
-namespace art {
-void CompileOneMethod(CompilerDriver& driver,
-                      Compiler* compiler,
-                      const DexFile::CodeItem* code_item,
-                      uint32_t access_flags, InvokeType invoke_type,
-                      uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
-                      const DexFile& dex_file,
-                      void* llvm_info);
-}
-
-namespace llvm {
-  extern bool TimePassesIsEnabled;
-}
-
-namespace {
-
-pthread_once_t llvm_initialized = PTHREAD_ONCE_INIT;
-
-void InitializeLLVM() {
-  // Initialize LLVM internal data structure for multithreading
-  llvm::llvm_start_multithreaded();
-
-  // NOTE: Uncomment following line to show the time consumption of LLVM passes
-  // llvm::TimePassesIsEnabled = true;
-
-  // Initialize LLVM target-specific options.
-  art::llvm::InitialBackendOptions();
-
-  // Initialize LLVM target, MC subsystem, asm printer, and asm parser.
-  if (art::kIsTargetBuild) {
-    // Don't initialize all targets on device. Just initialize the device's native target
-    llvm::InitializeNativeTarget();
-    llvm::InitializeNativeTargetAsmPrinter();
-    llvm::InitializeNativeTargetAsmParser();
-  } else {
-    llvm::InitializeAllTargets();
-    llvm::InitializeAllTargetMCs();
-    llvm::InitializeAllAsmPrinters();
-    llvm::InitializeAllAsmParsers();
-  }
-
-  // Initialize LLVM optimization passes
-  llvm::PassRegistry &registry = *llvm::PassRegistry::getPassRegistry();
-
-  llvm::initializeCore(registry);
-  llvm::initializeScalarOpts(registry);
-  llvm::initializeIPO(registry);
-  llvm::initializeAnalysis(registry);
-  llvm::initializeIPA(registry);
-  llvm::initializeTransformUtils(registry);
-  llvm::initializeInstCombine(registry);
-  llvm::initializeInstrumentation(registry);
-  llvm::initializeTarget(registry);
-}
-
-// The Guard to Shutdown LLVM
-// llvm::llvm_shutdown_obj llvm_guard;
-// TODO: We are commenting out this line because this will cause SEGV from
-// time to time.
-// Two reasons: (1) the order of the destruction of static objects, or
-//              (2) dlopen/dlclose side-effect on static objects.
-
-}  // anonymous namespace
-
-
-namespace art {
-namespace llvm {
-
-
-::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
-
-
-CompilerLLVM::CompilerLLVM(CompilerDriver* driver, InstructionSet insn_set)
-    : compiler_driver_(driver), insn_set_(insn_set),
-      next_cunit_id_lock_("compilation unit id lock"), next_cunit_id_(1) {
-
-  // Initialize LLVM libraries
-  pthread_once(&llvm_initialized, InitializeLLVM);
-}
-
-
-CompilerLLVM::~CompilerLLVM() {
-}
-
-
-LlvmCompilationUnit* CompilerLLVM::AllocateCompilationUnit() {
-  MutexLock GUARD(Thread::Current(), next_cunit_id_lock_);
-  LlvmCompilationUnit* cunit = new LlvmCompilationUnit(this, next_cunit_id_++);
-  if (!bitcode_filename_.empty()) {
-    cunit->SetBitcodeFileName(StringPrintf("%s-%u",
-                                           bitcode_filename_.c_str(),
-                                           cunit->GetCompilationUnitId()));
-  }
-  return cunit;
-}
-
-
-CompiledMethod* CompilerLLVM::
-CompileDexMethod(DexCompilationUnit* dex_compilation_unit, InvokeType invoke_type) {
-  std::unique_ptr<LlvmCompilationUnit> cunit(AllocateCompilationUnit());
-
-  cunit->SetDexCompilationUnit(dex_compilation_unit);
-  cunit->SetCompilerDriver(compiler_driver_);
-  // TODO: consolidate ArtCompileMethods
-  CompileOneMethod(compiler_driver_,
-                   compiler_driver_->GetCompiler(),
-                   dex_compilation_unit->GetCodeItem(),
-                   dex_compilation_unit->GetAccessFlags(),
-                   invoke_type,
-                   dex_compilation_unit->GetClassDefIndex(),
-                   dex_compilation_unit->GetDexMethodIndex(),
-                   dex_compilation_unit->GetClassLoader(),
-                   *dex_compilation_unit->GetDexFile(),
-                   cunit.get());
-
-  cunit->Materialize();
-
-  return new CompiledMethod(*compiler_driver_, compiler_driver_->GetInstructionSet(),
-                            cunit->GetElfObject(),
-                            dex_compilation_unit->GetVerifiedMethod()->GetDexGcMap(),
-                            cunit->GetDexCompilationUnit()->GetSymbol());
-}
-
-
-CompiledMethod* CompilerLLVM::
-CompileNativeMethod(DexCompilationUnit* dex_compilation_unit) {
-  std::unique_ptr<LlvmCompilationUnit> cunit(AllocateCompilationUnit());
-
-  std::unique_ptr<JniCompiler> jni_compiler(
-      new JniCompiler(cunit.get(), compiler_driver_, dex_compilation_unit));
-
-  return jni_compiler->Compile();
-}
-
-
-static CompilerLLVM* ContextOf(art::CompilerDriver* driver) {
-  void *compiler_context = driver->GetCompilerContext();
-  CHECK(compiler_context != NULL);
-  return reinterpret_cast<CompilerLLVM*>(compiler_context);
-}
-
-static CompilerLLVM* ContextOf(const art::CompilerDriver& driver) {
-  void *compiler_context = driver.GetCompilerContext();
-  CHECK(compiler_context != NULL);
-  return reinterpret_cast<CompilerLLVM*>(compiler_context);
-}
-
-void ArtInitCompilerContext(CompilerDriver* driver) {
-  CHECK(driver->GetCompilerContext() == nullptr);
-
-  CompilerLLVM* compiler_llvm = new CompilerLLVM(driver, driver->GetInstructionSet());
-
-  driver->SetCompilerContext(compiler_llvm);
-}
-
-void ArtUnInitCompilerContext(CompilerDriver* driver) {
-  delete ContextOf(driver);
-  driver->SetCompilerContext(nullptr);
-}
-
-CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
-                                 uint32_t access_flags, InvokeType invoke_type,
-                                 uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
-                                 const DexFile& dex_file) {
-  UNUSED(class_def_idx);  // TODO: this is used with Compiler::RequiresConstructorBarrier.
-  ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
-
-  DexCompilationUnit dex_compilation_unit(nullptr, class_loader, class_linker, dex_file, code_item,
-                                          class_def_idx, method_idx, access_flags,
-                                          driver->GetVerifiedMethod(&dex_file, method_idx));
-  CompilerLLVM* compiler_llvm = ContextOf(driver);
-  CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type);
-  return result;
-}
-
-CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
-                                        uint32_t method_idx, const DexFile& dex_file) {
-  ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
-
-  DexCompilationUnit dex_compilation_unit(nullptr, nullptr, class_linker, dex_file, nullptr,
-                                          0, method_idx, access_flags, nullptr);
-
-  CompilerLLVM* compiler_llvm = ContextOf(driver);
-  CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit);
-  return result;
-}
-
-void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
-  ContextOf(driver)->SetBitcodeFileName(filename);
-}
-
-}  // namespace llvm
-}  // namespace art
-
diff --git a/compiler/llvm/compiler_llvm.h b/compiler/llvm/compiler_llvm.h
deleted file mode 100644
index 7d29198..0000000
--- a/compiler/llvm/compiler_llvm.h
+++ /dev/null
@@ -1,115 +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_COMPILER_LLVM_COMPILER_LLVM_H_
-#define ART_COMPILER_LLVM_COMPILER_LLVM_H_
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/macros.h"
-#include "dex_file.h"
-#include "driver/compiler_driver.h"
-#include "instruction_set.h"
-#include "mirror/object.h"
-
-namespace art {
-  class CompiledMethod;
-  class CompilerDriver;
-  class DexCompilationUnit;
-  namespace mirror {
-    class ArtMethod;
-    class ClassLoader;
-  }  // namespace mirror
-}  // namespace art
-
-
-namespace llvm {
-  class Function;
-  class LLVMContext;
-  class Module;
-  class PointerType;
-  class StructType;
-  class Type;
-}  // namespace llvm
-
-
-namespace art {
-namespace llvm {
-
-class LlvmCompilationUnit;
-class IRBuilder;
-
-class CompilerLLVM {
- public:
-  CompilerLLVM(CompilerDriver* driver, InstructionSet insn_set);
-
-  ~CompilerLLVM();
-
-  CompilerDriver* GetCompiler() const {
-    return compiler_driver_;
-  }
-
-  InstructionSet GetInstructionSet() const {
-    return insn_set_;
-  }
-
-  void SetBitcodeFileName(const std::string& filename) {
-    bitcode_filename_ = filename;
-  }
-
-  CompiledMethod* CompileDexMethod(DexCompilationUnit* dex_compilation_unit,
-                                   InvokeType invoke_type);
-
-  CompiledMethod* CompileGBCMethod(DexCompilationUnit* dex_compilation_unit, std::string* func);
-
-  CompiledMethod* CompileNativeMethod(DexCompilationUnit* dex_compilation_unit);
-
- private:
-  LlvmCompilationUnit* AllocateCompilationUnit();
-
-  CompilerDriver* const compiler_driver_;
-
-  const InstructionSet insn_set_;
-
-  Mutex next_cunit_id_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  size_t next_cunit_id_ GUARDED_BY(next_cunit_id_lock_);
-
-  std::string bitcode_filename_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompilerLLVM);
-};
-
-void ArtInitCompilerContext(CompilerDriver* driver);
-
-void ArtUnInitCompilerContext(CompilerDriver* driver);
-
-CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
-                                 uint32_t access_flags, InvokeType invoke_type,
-                                 uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
-                                 const DexFile& dex_file);
-
-CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
-                                        uint32_t method_idx, const DexFile& dex_file);
-
-void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename);
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_COMPILER_LLVM_H_
diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc
deleted file mode 100644
index 902f8dd..0000000
--- a/compiler/llvm/gbc_expander.cc
+++ /dev/null
@@ -1,3796 +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 "dex_file.h"
-#include "dex_file-inl.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "intrinsic_helper.h"
-#include "ir_builder.h"
-#include "method_reference.h"
-#include "mirror/art_method.h"
-#include "mirror/array.h"
-#include "mirror/string.h"
-#include "thread.h"
-#include "utils_llvm.h"
-#include "verifier/method_verifier.h"
-
-#include "dex/compiler_ir.h"
-#include "dex/mir_graph.h"
-#include "dex/quick/mir_to_lir.h"
-
-#include <llvm/ADT/STLExtras.h>
-#include <llvm/IR/Intrinsics.h>
-#include <llvm/IR/Metadata.h>
-#include <llvm/Pass.h>
-#include <llvm/Support/CFG.h>
-#include <llvm/Support/InstIterator.h>
-
-#include <vector>
-#include <map>
-#include <utility>
-
-using ::art::kMIRIgnoreNullCheck;
-using ::art::kMIRIgnoreRangeCheck;
-using ::art::llvm::IRBuilder;
-using ::art::llvm::IntrinsicHelper;
-using ::art::llvm::JType;
-using ::art::llvm::RuntimeSupportBuilder;
-using ::art::llvm::kBoolean;
-using ::art::llvm::kByte;
-using ::art::llvm::kChar;
-using ::art::llvm::kDouble;
-using ::art::llvm::kFloat;
-using ::art::llvm::kInt;
-using ::art::llvm::kLikely;
-using ::art::llvm::kLong;
-using ::art::llvm::kObject;
-using ::art::llvm::kShort;
-using ::art::llvm::kTBAAConstJObject;
-using ::art::llvm::kTBAAHeapArray;
-using ::art::llvm::kTBAAHeapInstance;
-using ::art::llvm::kTBAAHeapStatic;
-using ::art::llvm::kTBAARegister;
-using ::art::llvm::kTBAARuntimeInfo;
-using ::art::llvm::kTBAAShadowFrame;
-using ::art::llvm::kUnlikely;
-using ::art::llvm::kVoid;
-using ::art::llvm::runtime_support::AllocArray;
-using ::art::llvm::runtime_support::AllocArrayWithAccessCheck;
-using ::art::llvm::runtime_support::AllocObject;
-using ::art::llvm::runtime_support::AllocObjectWithAccessCheck;
-using ::art::llvm::runtime_support::CheckAndAllocArray;
-using ::art::llvm::runtime_support::CheckAndAllocArrayWithAccessCheck;
-using ::art::llvm::runtime_support::CheckCast;
-using ::art::llvm::runtime_support::CheckPutArrayElement;
-using ::art::llvm::runtime_support::FillArrayData;
-using ::art::llvm::runtime_support::FindCatchBlock;
-using ::art::llvm::runtime_support::FindDirectMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindInterfaceMethod;
-using ::art::llvm::runtime_support::FindInterfaceMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindStaticMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindSuperMethodWithAccessCheck;
-using ::art::llvm::runtime_support::FindVirtualMethodWithAccessCheck;
-using ::art::llvm::runtime_support::Get32Instance;
-using ::art::llvm::runtime_support::Get32Static;
-using ::art::llvm::runtime_support::Get64Instance;
-using ::art::llvm::runtime_support::Get64Static;
-using ::art::llvm::runtime_support::GetObjectInstance;
-using ::art::llvm::runtime_support::GetObjectStatic;
-using ::art::llvm::runtime_support::InitializeStaticStorage;
-using ::art::llvm::runtime_support::InitializeType;
-using ::art::llvm::runtime_support::InitializeTypeAndVerifyAccess;
-using ::art::llvm::runtime_support::IsAssignable;
-using ::art::llvm::runtime_support::ResolveString;
-using ::art::llvm::runtime_support::RuntimeId;
-using ::art::llvm::runtime_support::Set32Instance;
-using ::art::llvm::runtime_support::Set32Static;
-using ::art::llvm::runtime_support::Set64Instance;
-using ::art::llvm::runtime_support::Set64Static;
-using ::art::llvm::runtime_support::SetObjectInstance;
-using ::art::llvm::runtime_support::SetObjectStatic;
-using ::art::llvm::runtime_support::ThrowDivZeroException;
-using ::art::llvm::runtime_support::ThrowException;
-using ::art::llvm::runtime_support::ThrowIndexOutOfBounds;
-using ::art::llvm::runtime_support::ThrowNullPointerException;
-using ::art::llvm::runtime_support::ThrowStackOverflowException;
-using ::art::llvm::runtime_support::art_d2i;
-using ::art::llvm::runtime_support::art_d2l;
-using ::art::llvm::runtime_support::art_f2i;
-using ::art::llvm::runtime_support::art_f2l;
-
-namespace art {
-extern char RemapShorty(char shortyType);
-}  // namespace art
-
-namespace {
-
-class GBCExpanderPass : public llvm::FunctionPass {
- private:
-  const IntrinsicHelper& intrinsic_helper_;
-  IRBuilder& irb_;
-
-  llvm::LLVMContext& context_;
-  RuntimeSupportBuilder& rtb_;
-
- private:
-  llvm::AllocaInst* shadow_frame_;
-  llvm::Value* old_shadow_frame_;
-
- private:
-  art::CompilerDriver* const driver_;
-
-  const art::DexCompilationUnit* const dex_compilation_unit_;
-
-  llvm::Function* func_;
-
-  std::vector<llvm::BasicBlock*> basic_blocks_;
-
-  std::vector<llvm::BasicBlock*> basic_block_landing_pads_;
-  llvm::BasicBlock* current_bb_;
-  std::map<llvm::BasicBlock*, std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>>>
-      landing_pad_phi_mapping_;
-  llvm::BasicBlock* basic_block_unwind_;
-
-  // Maps each vreg to its shadow frame address.
-  std::vector<llvm::Value*> shadow_frame_vreg_addresses_;
-
-  bool changed_;
-
- private:
-  //----------------------------------------------------------------------------
-  // Constant for GBC expansion
-  //----------------------------------------------------------------------------
-  enum IntegerShiftKind {
-    kIntegerSHL,
-    kIntegerSHR,
-    kIntegerUSHR,
-  };
-
- private:
-  //----------------------------------------------------------------------------
-  // Helper function for GBC expansion
-  //----------------------------------------------------------------------------
-
-  llvm::Value* ExpandToRuntime(RuntimeId rt, llvm::CallInst& inst);
-
-  uint64_t LV2UInt(llvm::Value* lv) {
-    return llvm::cast<llvm::ConstantInt>(lv)->getZExtValue();
-  }
-
-  int64_t LV2SInt(llvm::Value* lv) {
-    return llvm::cast<llvm::ConstantInt>(lv)->getSExtValue();
-  }
-
- private:
-  // TODO: Almost all Emit* are directly copy-n-paste from MethodCompiler.
-  // Refactor these utility functions from MethodCompiler to avoid forking.
-
-  void EmitStackOverflowCheck(llvm::Instruction* first_non_alloca);
-
-  void RewriteFunction();
-
-  void RewriteBasicBlock(llvm::BasicBlock* original_block);
-
-  void UpdatePhiInstruction(llvm::BasicBlock* old_basic_block,
-                            llvm::BasicBlock* new_basic_block);
-
-
-  // Sign or zero extend category 1 types < 32bits in size to 32bits.
-  llvm::Value* SignOrZeroExtendCat1Types(llvm::Value* value, JType jty);
-
-  // Truncate category 1 types from 32bits to the given JType size.
-  llvm::Value* TruncateCat1Types(llvm::Value* value, JType jty);
-
-  //----------------------------------------------------------------------------
-  // Dex cache code generation helper function
-  //----------------------------------------------------------------------------
-  llvm::Value* EmitLoadDexCacheAddr(art::MemberOffset dex_cache_offset);
-
-  llvm::Value* EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx);
-
-  llvm::Value* EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx);
-
-  llvm::Value* EmitLoadDexCacheStringFieldAddr(uint32_t string_idx);
-
-  //----------------------------------------------------------------------------
-  // Code generation helper function
-  //----------------------------------------------------------------------------
-  llvm::Value* EmitLoadMethodObjectAddr();
-
-  llvm::Value* EmitLoadArrayLength(llvm::Value* array);
-
-  llvm::Value* EmitLoadSDCalleeMethodObjectAddr(uint32_t callee_method_idx);
-
-  llvm::Value* EmitLoadVirtualCalleeMethodObjectAddr(int vtable_idx,
-                                                     llvm::Value* this_addr);
-
-  llvm::Value* EmitArrayGEP(llvm::Value* array_addr,
-                            llvm::Value* index_value,
-                            JType elem_jty);
-
-  //----------------------------------------------------------------------------
-  // Invoke helper function
-  //----------------------------------------------------------------------------
-  llvm::Value* EmitInvoke(llvm::CallInst& call_inst);
-
-  //----------------------------------------------------------------------------
-  // Inlining helper functions
-  //----------------------------------------------------------------------------
-  bool EmitIntrinsic(llvm::CallInst& call_inst, llvm::Value** result);
-
-  bool EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst,
-                                          llvm::Value** result, bool is_empty);
-
- private:
-  //----------------------------------------------------------------------------
-  // Expand Greenland intrinsics
-  //----------------------------------------------------------------------------
-  void Expand_TestSuspend(llvm::CallInst& call_inst);
-
-  void Expand_MarkGCCard(llvm::CallInst& call_inst);
-
-  llvm::Value* Expand_LoadStringFromDexCache(llvm::Value* string_idx_value);
-
-  llvm::Value* Expand_LoadTypeFromDexCache(llvm::Value* type_idx_value);
-
-  void Expand_LockObject(llvm::Value* obj);
-
-  void Expand_UnlockObject(llvm::Value* obj);
-
-  llvm::Value* Expand_ArrayGet(llvm::Value* array_addr,
-                               llvm::Value* index_value,
-                               JType elem_jty);
-
-  void Expand_ArrayPut(llvm::Value* new_value,
-                       llvm::Value* array_addr,
-                       llvm::Value* index_value,
-                       JType elem_jty);
-
-  void Expand_FilledNewArray(llvm::CallInst& call_inst);
-
-  llvm::Value* Expand_IGetFast(llvm::Value* field_offset_value,
-                               llvm::Value* is_volatile_value,
-                               llvm::Value* object_addr,
-                               JType field_jty);
-
-  void Expand_IPutFast(llvm::Value* field_offset_value,
-                       llvm::Value* is_volatile_value,
-                       llvm::Value* object_addr,
-                       llvm::Value* new_value,
-                       JType field_jty);
-
-  llvm::Value* Expand_SGetFast(llvm::Value* static_storage_addr,
-                               llvm::Value* field_offset_value,
-                               llvm::Value* is_volatile_value,
-                               JType field_jty);
-
-  void Expand_SPutFast(llvm::Value* static_storage_addr,
-                       llvm::Value* field_offset_value,
-                       llvm::Value* is_volatile_value,
-                       llvm::Value* new_value,
-                       JType field_jty);
-
-  llvm::Value* Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr);
-
-  llvm::Value*
-  Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value);
-
-  llvm::Value*
-  Expand_GetVirtualCalleeMethodObjAddrFast(llvm::Value* vtable_idx_value,
-                                           llvm::Value* this_addr);
-
-  llvm::Value* Expand_Invoke(llvm::CallInst& call_inst);
-
-  llvm::Value* Expand_DivRem(llvm::CallInst& call_inst, bool is_div, JType op_jty);
-
-  void Expand_AllocaShadowFrame(llvm::Value* num_vregs_value);
-
-  void Expand_SetVReg(llvm::Value* entry_idx, llvm::Value* obj);
-
-  void Expand_PopShadowFrame();
-
-  void Expand_UpdateDexPC(llvm::Value* dex_pc_value);
-
-  //----------------------------------------------------------------------------
-  // Quick
-  //----------------------------------------------------------------------------
-
-  llvm::Value* Expand_FPCompare(llvm::Value* src1_value,
-                                llvm::Value* src2_value,
-                                bool gt_bias);
-
-  llvm::Value* Expand_LongCompare(llvm::Value* src1_value, llvm::Value* src2_value);
-
-  llvm::Value* EmitCompareResultSelection(llvm::Value* cmp_eq,
-                                          llvm::Value* cmp_lt);
-
-  llvm::Value* EmitLoadConstantClass(uint32_t dex_pc, uint32_t type_idx);
-  llvm::Value* EmitLoadStaticStorage(uint32_t dex_pc, uint32_t type_idx);
-
-  llvm::Value* Expand_HLIGet(llvm::CallInst& call_inst, JType field_jty);
-  void Expand_HLIPut(llvm::CallInst& call_inst, JType field_jty);
-
-  llvm::Value* Expand_HLSget(llvm::CallInst& call_inst, JType field_jty);
-  void Expand_HLSput(llvm::CallInst& call_inst, JType field_jty);
-
-  llvm::Value* Expand_HLArrayGet(llvm::CallInst& call_inst, JType field_jty);
-  void Expand_HLArrayPut(llvm::CallInst& call_inst, JType field_jty);
-
-  llvm::Value* Expand_ConstString(llvm::CallInst& call_inst);
-  llvm::Value* Expand_ConstClass(llvm::CallInst& call_inst);
-
-  void Expand_MonitorEnter(llvm::CallInst& call_inst);
-  void Expand_MonitorExit(llvm::CallInst& call_inst);
-
-  void Expand_HLCheckCast(llvm::CallInst& call_inst);
-  llvm::Value* Expand_InstanceOf(llvm::CallInst& call_inst);
-
-  llvm::Value* Expand_NewInstance(llvm::CallInst& call_inst);
-
-  llvm::Value* Expand_HLInvoke(llvm::CallInst& call_inst);
-
-  llvm::Value* Expand_OptArrayLength(llvm::CallInst& call_inst);
-  llvm::Value* Expand_NewArray(llvm::CallInst& call_inst);
-  llvm::Value* Expand_HLFilledNewArray(llvm::CallInst& call_inst);
-  void Expand_HLFillArrayData(llvm::CallInst& call_inst);
-
-  llvm::Value* EmitAllocNewArray(uint32_t dex_pc,
-                                 llvm::Value* array_length_value,
-                                 uint32_t type_idx,
-                                 bool is_filled_new_array);
-
-  llvm::Value* EmitCallRuntimeForCalleeMethodObjectAddr(uint32_t callee_method_idx,
-                                                        art::InvokeType invoke_type,
-                                                        llvm::Value* this_addr,
-                                                        uint32_t dex_pc,
-                                                        bool is_fast_path);
-
-  void EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr);
-
-  void EmitUpdateDexPC(uint32_t dex_pc);
-
-  void EmitGuard_DivZeroException(uint32_t dex_pc,
-                                  llvm::Value* denominator,
-                                  JType op_jty);
-
-  void EmitGuard_NullPointerException(uint32_t dex_pc, llvm::Value* object,
-                                      int opt_flags);
-
-  void EmitGuard_ArrayIndexOutOfBoundsException(uint32_t dex_pc,
-                                                llvm::Value* array,
-                                                llvm::Value* index,
-                                                int opt_flags);
-
-  llvm::FunctionType* GetFunctionType(llvm::Type* ret_type, uint32_t method_idx, bool is_static);
-
-  llvm::BasicBlock* GetBasicBlock(uint32_t dex_pc);
-
-  llvm::BasicBlock* CreateBasicBlockWithDexPC(uint32_t dex_pc,
-                                              const char* postfix);
-
-  int32_t GetTryItemOffset(uint32_t dex_pc);
-
-  llvm::BasicBlock* GetLandingPadBasicBlock(uint32_t dex_pc);
-
-  llvm::BasicBlock* GetUnwindBasicBlock();
-
-  void EmitGuard_ExceptionLandingPad(uint32_t dex_pc);
-
-  void EmitBranchExceptionLandingPad(uint32_t dex_pc);
-
-  //----------------------------------------------------------------------------
-  // Expand Arithmetic Helper Intrinsics
-  //----------------------------------------------------------------------------
-
-  llvm::Value* Expand_IntegerShift(llvm::Value* src1_value,
-                                   llvm::Value* src2_value,
-                                   IntegerShiftKind kind,
-                                   JType op_jty);
-
- public:
-  static char ID;
-
-  GBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
-                  art::CompilerDriver* driver, const art::DexCompilationUnit* dex_compilation_unit)
-      : llvm::FunctionPass(ID), intrinsic_helper_(intrinsic_helper), irb_(irb),
-        context_(irb.getContext()), rtb_(irb.Runtime()),
-        shadow_frame_(NULL), old_shadow_frame_(NULL),
-        driver_(driver),
-        dex_compilation_unit_(dex_compilation_unit),
-        func_(NULL), current_bb_(NULL), basic_block_unwind_(NULL), changed_(false) {}
-
-  bool runOnFunction(llvm::Function& func);
-
- private:
-  void InsertStackOverflowCheck(llvm::Function& func);
-
-  llvm::Value* ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id,
-                               llvm::CallInst& call_inst);
-};
-
-char GBCExpanderPass::ID = 0;
-
-bool GBCExpanderPass::runOnFunction(llvm::Function& func) {
-  VLOG(compiler) << "GBC expansion on " << func.getName().str();
-
-  // Runtime support or stub
-  if (dex_compilation_unit_ == NULL) {
-    return false;
-  }
-
-  // Setup rewrite context
-  shadow_frame_ = NULL;
-  old_shadow_frame_ = NULL;
-  func_ = &func;
-  changed_ = false;  // Assume unchanged
-
-  shadow_frame_vreg_addresses_.resize(dex_compilation_unit_->GetCodeItem()->registers_size_, NULL);
-  basic_blocks_.resize(dex_compilation_unit_->GetCodeItem()->insns_size_in_code_units_);
-  basic_block_landing_pads_.resize(dex_compilation_unit_->GetCodeItem()->tries_size_, NULL);
-  basic_block_unwind_ = NULL;
-  for (llvm::Function::iterator bb_iter = func_->begin(), bb_end = func_->end();
-       bb_iter != bb_end;
-       ++bb_iter) {
-    if (bb_iter->begin()->getMetadata("DexOff") == NULL) {
-      continue;
-    }
-    uint32_t dex_pc = LV2UInt(bb_iter->begin()->getMetadata("DexOff")->getOperand(0));
-    basic_blocks_[dex_pc] = bb_iter;
-  }
-
-  // Insert stack overflow check
-  InsertStackOverflowCheck(func);  // TODO: Use intrinsic.
-
-  // Rewrite the intrinsics
-  RewriteFunction();
-
-  VERIFY_LLVM_FUNCTION(func);
-
-  return changed_;
-}
-
-void GBCExpanderPass::RewriteBasicBlock(llvm::BasicBlock* original_block) {
-  llvm::BasicBlock* curr_basic_block = original_block;
-
-  llvm::BasicBlock::iterator inst_iter = original_block->begin();
-  llvm::BasicBlock::iterator inst_end = original_block->end();
-
-  while (inst_iter != inst_end) {
-    llvm::CallInst* call_inst = llvm::dyn_cast<llvm::CallInst>(inst_iter);
-    IntrinsicHelper::IntrinsicId intr_id = IntrinsicHelper::UnknownId;
-
-    if (call_inst) {
-      llvm::Function* callee_func = call_inst->getCalledFunction();
-      intr_id = intrinsic_helper_.GetIntrinsicId(callee_func);
-    }
-
-    if (intr_id == IntrinsicHelper::UnknownId) {
-      // This is not intrinsic call.  Skip this instruction.
-      ++inst_iter;
-      continue;
-    }
-
-    // Rewrite the intrinsic and change the function
-    changed_ = true;
-    irb_.SetInsertPoint(inst_iter);
-
-    // Expand the intrinsic
-    if (llvm::Value* new_value = ExpandIntrinsic(intr_id, *call_inst)) {
-      inst_iter->replaceAllUsesWith(new_value);
-    }
-
-    // Remove the old intrinsic call instruction
-    llvm::BasicBlock::iterator old_inst = inst_iter++;
-    old_inst->eraseFromParent();
-
-    // Splice the instruction to the new basic block
-    llvm::BasicBlock* next_basic_block = irb_.GetInsertBlock();
-    if (next_basic_block != curr_basic_block) {
-      next_basic_block->getInstList().splice(
-          irb_.GetInsertPoint(), curr_basic_block->getInstList(),
-          inst_iter, inst_end);
-      curr_basic_block = next_basic_block;
-      inst_end = curr_basic_block->end();
-    }
-  }
-}
-
-
-void GBCExpanderPass::RewriteFunction() {
-  size_t num_basic_blocks = func_->getBasicBlockList().size();
-  // NOTE: We are not using (bb_iter != bb_end) as the for-loop condition,
-  // because we will create new basic block while expanding the intrinsics.
-  // We only want to iterate through the input basic blocks.
-
-  landing_pad_phi_mapping_.clear();
-
-  for (llvm::Function::iterator bb_iter = func_->begin();
-       num_basic_blocks > 0; ++bb_iter, --num_basic_blocks) {
-    // Set insert point to current basic block.
-    irb_.SetInsertPoint(bb_iter);
-
-    current_bb_ = bb_iter;
-
-    // Rewrite the basic block
-    RewriteBasicBlock(bb_iter);
-
-    // Update the phi-instructions in the successor basic block
-    llvm::BasicBlock* last_block = irb_.GetInsertBlock();
-    if (last_block != bb_iter) {
-      UpdatePhiInstruction(bb_iter, last_block);
-    }
-  }
-
-  typedef std::map<llvm::PHINode*, llvm::PHINode*> HandlerPHIMap;
-  HandlerPHIMap handler_phi;
-  // Iterate every used landing pad basic block
-  for (size_t i = 0, ei = basic_block_landing_pads_.size(); i != ei; ++i) {
-    llvm::BasicBlock* lbb = basic_block_landing_pads_[i];
-    if (lbb == NULL) {
-      continue;
-    }
-
-    llvm::TerminatorInst* term_inst = lbb->getTerminator();
-    std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>>& rewrite_pair
-        = landing_pad_phi_mapping_[lbb];
-    irb_.SetInsertPoint(lbb->begin());
-
-    // Iterate every succeeding basic block (catch block)
-    for (unsigned succ_iter = 0, succ_end = term_inst->getNumSuccessors();
-         succ_iter != succ_end; ++succ_iter) {
-      llvm::BasicBlock* succ_basic_block = term_inst->getSuccessor(succ_iter);
-
-      // Iterate every phi instructions in the succeeding basic block
-      for (llvm::BasicBlock::iterator
-           inst_iter = succ_basic_block->begin(),
-           inst_end = succ_basic_block->end();
-           inst_iter != inst_end; ++inst_iter) {
-        llvm::PHINode *phi = llvm::dyn_cast<llvm::PHINode>(inst_iter);
-
-        if (!phi) {
-          break;  // Meet non-phi instruction.  Done.
-        }
-
-        if (handler_phi[phi] == NULL) {
-          handler_phi[phi] = llvm::PHINode::Create(phi->getType(), 1);
-        }
-
-        // Create new_phi in landing pad
-        llvm::PHINode* new_phi = irb_.CreatePHI(phi->getType(), rewrite_pair.size());
-        // Insert all incoming value into new_phi by rewrite_pair
-        for (size_t j = 0, ej = rewrite_pair.size(); j != ej; ++j) {
-          llvm::BasicBlock* old_bb = rewrite_pair[j].first;
-          llvm::BasicBlock* new_bb = rewrite_pair[j].second;
-          new_phi->addIncoming(phi->getIncomingValueForBlock(old_bb), new_bb);
-        }
-        // Delete all incoming value from phi by rewrite_pair
-        for (size_t j = 0, ej = rewrite_pair.size(); j != ej; ++j) {
-          llvm::BasicBlock* old_bb = rewrite_pair[j].first;
-          int old_bb_idx = phi->getBasicBlockIndex(old_bb);
-          if (old_bb_idx >= 0) {
-            phi->removeIncomingValue(old_bb_idx, false);
-          }
-        }
-        // Insert new_phi into new handler phi
-        handler_phi[phi]->addIncoming(new_phi, lbb);
-      }
-    }
-  }
-
-  // Replace all handler phi
-  // We can't just use the old handler phi, because some exception edges will disappear after we
-  // compute fast-path.
-  for (HandlerPHIMap::iterator it = handler_phi.begin(); it != handler_phi.end(); ++it) {
-    llvm::PHINode* old_phi = it->first;
-    llvm::PHINode* new_phi = it->second;
-    new_phi->insertBefore(old_phi);
-    old_phi->replaceAllUsesWith(new_phi);
-    old_phi->eraseFromParent();
-  }
-}
-
-void GBCExpanderPass::UpdatePhiInstruction(llvm::BasicBlock* old_basic_block,
-                                           llvm::BasicBlock* new_basic_block) {
-  llvm::TerminatorInst* term_inst = new_basic_block->getTerminator();
-
-  if (!term_inst) {
-    return;  // No terminating instruction in new_basic_block.  Nothing to do.
-  }
-
-  // Iterate every succeeding basic block
-  for (unsigned succ_iter = 0, succ_end = term_inst->getNumSuccessors();
-       succ_iter != succ_end; ++succ_iter) {
-    llvm::BasicBlock* succ_basic_block = term_inst->getSuccessor(succ_iter);
-
-    // Iterate every phi instructions in the succeeding basic block
-    for (llvm::BasicBlock::iterator
-         inst_iter = succ_basic_block->begin(),
-         inst_end = succ_basic_block->end();
-         inst_iter != inst_end; ++inst_iter) {
-      llvm::PHINode *phi = llvm::dyn_cast<llvm::PHINode>(inst_iter);
-
-      if (!phi) {
-        break;  // Meet non-phi instruction.  Done.
-      }
-
-      // Update the incoming block of this phi instruction
-      for (llvm::PHINode::block_iterator
-           ibb_iter = phi->block_begin(), ibb_end = phi->block_end();
-           ibb_iter != ibb_end; ++ibb_iter) {
-        if (*ibb_iter == old_basic_block) {
-          *ibb_iter = new_basic_block;
-        }
-      }
-    }
-  }
-}
-
-llvm::Value* GBCExpanderPass::ExpandToRuntime(RuntimeId rt, llvm::CallInst& inst) {
-  // Some GBC intrinsic can directly replace with IBC runtime. "Directly" means
-  // the arguments passed to the GBC intrinsic are as the same as IBC runtime
-  // function, therefore only called function is needed to change.
-  unsigned num_args = inst.getNumArgOperands();
-
-  if (num_args <= 0) {
-    return irb_.CreateCall(irb_.GetRuntime(rt));
-  } else {
-    std::vector<llvm::Value*> args;
-    for (unsigned i = 0; i < num_args; i++) {
-      args.push_back(inst.getArgOperand(i));
-    }
-
-    return irb_.CreateCall(irb_.GetRuntime(rt), args);
-  }
-}
-
-void
-GBCExpanderPass::EmitStackOverflowCheck(llvm::Instruction* first_non_alloca) {
-  llvm::Function* func = first_non_alloca->getParent()->getParent();
-  llvm::Module* module = func->getParent();
-
-  // Call llvm intrinsic function to get frame address.
-  llvm::Function* frameaddress =
-      llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::frameaddress);
-
-  // The type of llvm::frameaddress is: i8* @llvm.frameaddress(i32)
-  llvm::Value* frame_address = irb_.CreateCall(frameaddress, irb_.getInt32(0));
-
-  // Cast i8* to int
-  frame_address = irb_.CreatePtrToInt(frame_address, irb_.getPtrEquivIntTy());
-
-  // Get thread.stack_end_
-  llvm::Value* stack_end =
-    irb_.Runtime().EmitLoadFromThreadOffset(art::Thread::StackEndOffset().Int32Value(),
-                                            irb_.getPtrEquivIntTy(),
-                                            kTBAARuntimeInfo);
-
-  // Check the frame address < thread.stack_end_ ?
-  llvm::Value* is_stack_overflow = irb_.CreateICmpULT(frame_address, stack_end);
-
-  llvm::BasicBlock* block_exception =
-      llvm::BasicBlock::Create(context_, "stack_overflow", func);
-
-  llvm::BasicBlock* block_continue =
-      llvm::BasicBlock::Create(context_, "stack_overflow_cont", func);
-
-  irb_.CreateCondBr(is_stack_overflow, block_exception, block_continue, kUnlikely);
-
-  // If stack overflow, throw exception.
-  irb_.SetInsertPoint(block_exception);
-  irb_.CreateCall(irb_.GetRuntime(ThrowStackOverflowException));
-
-  // Unwind.
-  llvm::Type* ret_type = func->getReturnType();
-  if (ret_type->isVoidTy()) {
-    irb_.CreateRetVoid();
-  } else {
-    // The return value is ignored when there's an exception. MethodCompiler
-    // returns zero value under the the corresponding return type  in this case.
-    // GBCExpander returns LLVM undef value here for brevity
-    irb_.CreateRet(llvm::UndefValue::get(ret_type));
-  }
-
-  irb_.SetInsertPoint(block_continue);
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadDexCacheAddr(art::MemberOffset offset) {
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-  return irb_.LoadFromObjectOffset(method_object_addr,
-                                   offset.Int32Value(),
-                                   irb_.getJObjectTy(),
-                                   kTBAAConstJObject);
-}
-
-llvm::Value*
-GBCExpanderPass::EmitLoadDexCacheResolvedTypeFieldAddr(uint32_t type_idx) {
-  llvm::Value* resolved_type_dex_cache_addr =
-    EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheResolvedTypesOffset());
-
-  llvm::Value* type_idx_value = irb_.getPtrEquivInt(type_idx);
-
-  return EmitArrayGEP(resolved_type_dex_cache_addr, type_idx_value, kObject);
-}
-
-llvm::Value* GBCExpanderPass::
-EmitLoadDexCacheResolvedMethodFieldAddr(uint32_t method_idx) {
-  llvm::Value* resolved_method_dex_cache_addr =
-    EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheResolvedMethodsOffset());
-
-  llvm::Value* method_idx_value = irb_.getPtrEquivInt(method_idx);
-
-  return EmitArrayGEP(resolved_method_dex_cache_addr, method_idx_value, kObject);
-}
-
-llvm::Value* GBCExpanderPass::
-EmitLoadDexCacheStringFieldAddr(uint32_t string_idx) {
-  llvm::Value* string_dex_cache_addr =
-    EmitLoadDexCacheAddr(art::mirror::ArtMethod::DexCacheStringsOffset());
-
-  llvm::Value* string_idx_value = irb_.getPtrEquivInt(string_idx);
-
-  return EmitArrayGEP(string_dex_cache_addr, string_idx_value, kObject);
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadMethodObjectAddr() {
-  llvm::Function* parent_func = irb_.GetInsertBlock()->getParent();
-  return parent_func->arg_begin();
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadArrayLength(llvm::Value* array) {
-  // Load array length
-  return irb_.LoadFromObjectOffset(array,
-                                   art::mirror::Array::LengthOffset().Int32Value(),
-                                   irb_.getJIntTy(),
-                                   kTBAAConstJObject);
-}
-
-llvm::Value*
-GBCExpanderPass::EmitLoadSDCalleeMethodObjectAddr(uint32_t callee_method_idx) {
-  llvm::Value* callee_method_object_field_addr =
-    EmitLoadDexCacheResolvedMethodFieldAddr(callee_method_idx);
-
-  return irb_.CreateLoad(callee_method_object_field_addr, kTBAARuntimeInfo);
-}
-
-llvm::Value* GBCExpanderPass::
-EmitLoadVirtualCalleeMethodObjectAddr(int vtable_idx, llvm::Value* this_addr) {
-  // Load class object of *this* pointer
-  llvm::Value* class_object_addr =
-    irb_.LoadFromObjectOffset(this_addr,
-                              art::mirror::Object::ClassOffset().Int32Value(),
-                              irb_.getJObjectTy(),
-                              kTBAAConstJObject);
-
-  // Load vtable address
-  llvm::Value* vtable_addr =
-    irb_.LoadFromObjectOffset(class_object_addr,
-                              art::mirror::Class::VTableOffset().Int32Value(),
-                              irb_.getJObjectTy(),
-                              kTBAAConstJObject);
-
-  // Load callee method object
-  llvm::Value* vtable_idx_value =
-    irb_.getPtrEquivInt(static_cast<uint64_t>(vtable_idx));
-
-  llvm::Value* method_field_addr =
-    EmitArrayGEP(vtable_addr, vtable_idx_value, kObject);
-
-  return irb_.CreateLoad(method_field_addr, kTBAAConstJObject);
-}
-
-// Emit Array GetElementPtr
-llvm::Value* GBCExpanderPass::EmitArrayGEP(llvm::Value* array_addr,
-                                           llvm::Value* index_value,
-                                           JType elem_jty) {
-  int data_offset;
-  if (elem_jty == kLong || elem_jty == kDouble ||
-      (elem_jty == kObject && sizeof(uint64_t) == sizeof(art::mirror::Object*))) {
-    data_offset = art::mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
-  } else {
-    data_offset = art::mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
-  }
-
-  llvm::Constant* data_offset_value =
-    irb_.getPtrEquivInt(data_offset);
-
-  llvm::Type* elem_type = irb_.getJType(elem_jty);
-
-  llvm::Value* array_data_addr =
-    irb_.CreatePtrDisp(array_addr, data_offset_value,
-                       elem_type->getPointerTo());
-
-  return irb_.CreateGEP(array_data_addr, index_value);
-}
-
-llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  art::InvokeType invoke_type =
-      static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
-  bool is_static = (invoke_type == art::kStatic);
-  art::MethodReference target_method(dex_compilation_unit_->GetDexFile(),
-                                     LV2UInt(call_inst.getArgOperand(1)));
-
-  // Load *this* actual parameter
-  llvm::Value* this_addr = (!is_static) ? call_inst.getArgOperand(3) : NULL;
-
-  // Compute invoke related information for compiler decision
-  int vtable_idx = -1;
-  uintptr_t direct_code = 0;
-  uintptr_t direct_method = 0;
-  bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc,
-                                                 true, true,
-                                                 &invoke_type, &target_method,
-                                                 &vtable_idx,
-                                                 &direct_code, &direct_method);
-  // Load the method object
-  llvm::Value* callee_method_object_addr = NULL;
-
-  if (!is_fast_path) {
-    callee_method_object_addr =
-        EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index, invoke_type,
-                                                 this_addr, dex_pc, is_fast_path);
-  } else {
-    switch (invoke_type) {
-      case art::kStatic:
-      case art::kDirect:
-        if (direct_method != 0u &&
-            direct_method != static_cast<uintptr_t>(-1)) {
-          callee_method_object_addr =
-              irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_method),
-                                  irb_.getJObjectTy());
-        } else {
-          callee_method_object_addr =
-              EmitLoadSDCalleeMethodObjectAddr(target_method.dex_method_index);
-        }
-        break;
-
-      case art::kVirtual:
-        DCHECK_NE(vtable_idx, -1);
-        callee_method_object_addr =
-            EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr);
-        break;
-
-      case art::kSuper:
-        LOG(FATAL) << "invoke-super should be promoted to invoke-direct in "
-        "the fast path.";
-        break;
-
-      case art::kInterface:
-        callee_method_object_addr =
-            EmitCallRuntimeForCalleeMethodObjectAddr(target_method.dex_method_index,
-                                                     invoke_type, this_addr,
-                                                     dex_pc, is_fast_path);
-        break;
-    }
-  }
-
-  // Load the actual parameter
-  std::vector<llvm::Value*> args;
-
-  args.push_back(callee_method_object_addr);  // method object for callee
-
-  for (uint32_t i = 3; i < call_inst.getNumArgOperands(); ++i) {
-    args.push_back(call_inst.getArgOperand(i));
-  }
-
-  llvm::Value* code_addr;
-  llvm::Type* func_type = GetFunctionType(call_inst.getType(),
-                                          target_method.dex_method_index, is_static);
-  if (direct_code != 0u && direct_code != static_cast<uintptr_t>(-1)) {
-    code_addr =
-        irb_.CreateIntToPtr(irb_.getPtrEquivInt(direct_code),
-                            func_type->getPointerTo());
-  } else {
-    code_addr =
-        irb_.LoadFromObjectOffset(callee_method_object_addr,
-                                  art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value(),
-                                  func_type->getPointerTo(), kTBAARuntimeInfo);
-  }
-
-  // Invoke callee
-  EmitUpdateDexPC(dex_pc);
-  llvm::Value* retval = irb_.CreateCall(code_addr, args);
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  return retval;
-}
-
-bool GBCExpanderPass::EmitIntrinsic(llvm::CallInst& call_inst,
-                                    llvm::Value** result) {
-  DCHECK(result != NULL);
-
-  uint32_t callee_method_idx = LV2UInt(call_inst.getArgOperand(1));
-  std::string callee_method_name(
-      PrettyMethod(callee_method_idx, *dex_compilation_unit_->GetDexFile()));
-
-  if (callee_method_name == "int java.lang.String.length()") {
-    return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result,
-                                              false /* is_empty */);
-  }
-  if (callee_method_name == "boolean java.lang.String.isEmpty()") {
-    return EmitIntrinsicStringLengthOrIsEmpty(call_inst, result,
-                                              true /* is_empty */);
-  }
-
-  *result = NULL;
-  return false;
-}
-
-bool GBCExpanderPass::EmitIntrinsicStringLengthOrIsEmpty(llvm::CallInst& call_inst,
-                                                         llvm::Value** result,
-                                                         bool is_empty) {
-  art::InvokeType invoke_type =
-        static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
-  DCHECK_NE(invoke_type, art::kStatic);
-  DCHECK_EQ(call_inst.getNumArgOperands(), 4U);
-
-  llvm::Value* this_object = call_inst.getArgOperand(3);
-  llvm::Value* string_count =
-      irb_.LoadFromObjectOffset(this_object,
-                                art::mirror::String::CountOffset().Int32Value(),
-                                irb_.getJIntTy(),
-                                kTBAAConstJObject);
-  if (is_empty) {
-    llvm::Value* count_equals_zero = irb_.CreateICmpEQ(string_count,
-                                                       irb_.getJInt(0));
-    llvm::Value* is_empty = irb_.CreateSelect(count_equals_zero,
-                                              irb_.getJBoolean(true),
-                                              irb_.getJBoolean(false));
-    is_empty = SignOrZeroExtendCat1Types(is_empty, kBoolean);
-    *result = is_empty;
-  } else {
-    *result = string_count;
-  }
-  return true;
-}
-
-void GBCExpanderPass::Expand_TestSuspend(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-
-  llvm::Value* suspend_count =
-      irb_.Runtime().EmitLoadFromThreadOffset(art::Thread::ThreadFlagsOffset().Int32Value(),
-                                              irb_.getInt16Ty(),
-                                              kTBAARuntimeInfo);
-  llvm::Value* is_suspend = irb_.CreateICmpNE(suspend_count, irb_.getInt16(0));
-
-  llvm::BasicBlock* basic_block_suspend = CreateBasicBlockWithDexPC(dex_pc, "suspend");
-  llvm::BasicBlock* basic_block_cont = CreateBasicBlockWithDexPC(dex_pc, "suspend_cont");
-
-  irb_.CreateCondBr(is_suspend, basic_block_suspend, basic_block_cont, kUnlikely);
-
-  irb_.SetInsertPoint(basic_block_suspend);
-  if (dex_pc != art::DexFile::kDexNoIndex) {
-    EmitUpdateDexPC(dex_pc);
-  }
-  irb_.Runtime().EmitTestSuspend();
-
-  llvm::BasicBlock* basic_block_exception = CreateBasicBlockWithDexPC(dex_pc, "exception");
-  llvm::Value* exception_pending = irb_.Runtime().EmitIsExceptionPending();
-  irb_.CreateCondBr(exception_pending, basic_block_exception, basic_block_cont, kUnlikely);
-
-  irb_.SetInsertPoint(basic_block_exception);
-  llvm::Type* ret_type = call_inst.getParent()->getParent()->getReturnType();
-  if (ret_type->isVoidTy()) {
-    irb_.CreateRetVoid();
-  } else {
-    // The return value is ignored when there's an exception.
-    irb_.CreateRet(llvm::UndefValue::get(ret_type));
-  }
-
-  irb_.SetInsertPoint(basic_block_cont);
-  return;
-}
-
-void GBCExpanderPass::Expand_MarkGCCard(llvm::CallInst& call_inst) {
-  irb_.Runtime().EmitMarkGCCard(call_inst.getArgOperand(0), call_inst.getArgOperand(1));
-  return;
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_LoadStringFromDexCache(llvm::Value* string_idx_value) {
-  uint32_t string_idx =
-    llvm::cast<llvm::ConstantInt>(string_idx_value)->getZExtValue();
-
-  llvm::Value* string_field_addr = EmitLoadDexCacheStringFieldAddr(string_idx);
-
-  return irb_.CreateLoad(string_field_addr, kTBAARuntimeInfo);
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_LoadTypeFromDexCache(llvm::Value* type_idx_value) {
-  uint32_t type_idx =
-    llvm::cast<llvm::ConstantInt>(type_idx_value)->getZExtValue();
-
-  llvm::Value* type_field_addr =
-    EmitLoadDexCacheResolvedTypeFieldAddr(type_idx);
-
-  return irb_.CreateLoad(type_field_addr, kTBAARuntimeInfo);
-}
-
-void GBCExpanderPass::Expand_LockObject(llvm::Value* obj) {
-  rtb_.EmitLockObject(obj);
-  return;
-}
-
-void GBCExpanderPass::Expand_UnlockObject(llvm::Value* obj) {
-  rtb_.EmitUnlockObject(obj);
-  return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_ArrayGet(llvm::Value* array_addr,
-                                              llvm::Value* index_value,
-                                              JType elem_jty) {
-  llvm::Value* array_elem_addr =
-    EmitArrayGEP(array_addr, index_value, elem_jty);
-
-  return irb_.CreateLoad(array_elem_addr, kTBAAHeapArray, elem_jty);
-}
-
-void GBCExpanderPass::Expand_ArrayPut(llvm::Value* new_value,
-                                      llvm::Value* array_addr,
-                                      llvm::Value* index_value,
-                                      JType elem_jty) {
-  llvm::Value* array_elem_addr =
-    EmitArrayGEP(array_addr, index_value, elem_jty);
-
-  irb_.CreateStore(new_value, array_elem_addr, kTBAAHeapArray, elem_jty);
-
-  return;
-}
-
-void GBCExpanderPass::Expand_FilledNewArray(llvm::CallInst& call_inst) {
-  // Most of the codes refer to MethodCompiler::EmitInsn_FilledNewArray
-  llvm::Value* array = call_inst.getArgOperand(0);
-
-  uint32_t element_jty =
-    llvm::cast<llvm::ConstantInt>(call_inst.getArgOperand(1))->getZExtValue();
-
-  DCHECK_GT(call_inst.getNumArgOperands(), 2U);
-  unsigned num_elements = (call_inst.getNumArgOperands() - 2);
-
-  bool is_elem_int_ty = (static_cast<JType>(element_jty) == kInt);
-
-  uint32_t alignment;
-  llvm::Constant* elem_size;
-  llvm::PointerType* field_type;
-
-  // NOTE: Currently filled-new-array only supports 'L', '[', and 'I'
-  // as the element, thus we are only checking 2 cases: primitive int and
-  // non-primitive type.
-  if (is_elem_int_ty) {
-    alignment = sizeof(int32_t);
-    elem_size = irb_.getPtrEquivInt(sizeof(int32_t));
-    field_type = irb_.getJIntTy()->getPointerTo();
-  } else {
-    alignment = irb_.getSizeOfPtrEquivInt();
-    elem_size = irb_.getSizeOfPtrEquivIntValue();
-    field_type = irb_.getJObjectTy()->getPointerTo();
-  }
-
-  llvm::Value* data_field_offset =
-    irb_.getPtrEquivInt(art::mirror::Array::DataOffset(alignment).Int32Value());
-
-  llvm::Value* data_field_addr =
-    irb_.CreatePtrDisp(array, data_field_offset, field_type);
-
-  for (unsigned i = 0; i < num_elements; ++i) {
-    // Values to fill the array begin at the 3rd argument
-    llvm::Value* reg_value = call_inst.getArgOperand(2 + i);
-
-    irb_.CreateStore(reg_value, data_field_addr, kTBAAHeapArray);
-
-    data_field_addr =
-      irb_.CreatePtrDisp(data_field_addr, elem_size, field_type);
-  }
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_IGetFast(llvm::Value* field_offset_value,
-                                              llvm::Value* /*is_volatile_value*/,
-                                              llvm::Value* object_addr,
-                                              JType field_jty) {
-  int field_offset =
-    llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
-  DCHECK_GE(field_offset, 0);
-
-  llvm::PointerType* field_type =
-    irb_.getJType(field_jty)->getPointerTo();
-
-  field_offset_value = irb_.getPtrEquivInt(field_offset);
-
-  llvm::Value* field_addr =
-    irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
-  // TODO: Check is_volatile.  We need to generate atomic load instruction
-  // when is_volatile is true.
-  return irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty);
-}
-
-void GBCExpanderPass::Expand_IPutFast(llvm::Value* field_offset_value,
-                                      llvm::Value* /* is_volatile_value */,
-                                      llvm::Value* object_addr,
-                                      llvm::Value* new_value,
-                                      JType field_jty) {
-  int field_offset =
-    llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
-  DCHECK_GE(field_offset, 0);
-
-  llvm::PointerType* field_type =
-    irb_.getJType(field_jty)->getPointerTo();
-
-  field_offset_value = irb_.getPtrEquivInt(field_offset);
-
-  llvm::Value* field_addr =
-    irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
-  // TODO: Check is_volatile.  We need to generate atomic store instruction
-  // when is_volatile is true.
-  irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty);
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_SGetFast(llvm::Value* static_storage_addr,
-                                              llvm::Value* field_offset_value,
-                                              llvm::Value* /*is_volatile_value*/,
-                                              JType field_jty) {
-  int field_offset =
-    llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
-  DCHECK_GE(field_offset, 0);
-
-  llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset);
-
-  llvm::Value* static_field_addr =
-    irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
-                       irb_.getJType(field_jty)->getPointerTo());
-
-  // TODO: Check is_volatile.  We need to generate atomic store instruction
-  // when is_volatile is true.
-  return irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty);
-}
-
-void GBCExpanderPass::Expand_SPutFast(llvm::Value* static_storage_addr,
-                                      llvm::Value* field_offset_value,
-                                      llvm::Value* /* is_volatile_value */,
-                                      llvm::Value* new_value,
-                                      JType field_jty) {
-  int field_offset =
-    llvm::cast<llvm::ConstantInt>(field_offset_value)->getSExtValue();
-
-  DCHECK_GE(field_offset, 0);
-
-  llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset);
-
-  llvm::Value* static_field_addr =
-    irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
-                       irb_.getJType(field_jty)->getPointerTo());
-
-  // TODO: Check is_volatile.  We need to generate atomic store instruction
-  // when is_volatile is true.
-  irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty);
-
-  return;
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_LoadDeclaringClassSSB(llvm::Value* method_object_addr) {
-  return irb_.LoadFromObjectOffset(method_object_addr,
-                                   art::mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
-                                   irb_.getJObjectTy(),
-                                   kTBAAConstJObject);
-}
-
-llvm::Value*
-GBCExpanderPass::Expand_GetSDCalleeMethodObjAddrFast(llvm::Value* callee_method_idx_value) {
-  uint32_t callee_method_idx =
-    llvm::cast<llvm::ConstantInt>(callee_method_idx_value)->getZExtValue();
-
-  return EmitLoadSDCalleeMethodObjectAddr(callee_method_idx);
-}
-
-llvm::Value* GBCExpanderPass::Expand_GetVirtualCalleeMethodObjAddrFast(
-    llvm::Value* vtable_idx_value,
-    llvm::Value* this_addr) {
-  int vtable_idx =
-    llvm::cast<llvm::ConstantInt>(vtable_idx_value)->getSExtValue();
-
-  return EmitLoadVirtualCalleeMethodObjectAddr(vtable_idx, this_addr);
-}
-
-llvm::Value* GBCExpanderPass::Expand_Invoke(llvm::CallInst& call_inst) {
-  // Most of the codes refer to MethodCompiler::EmitInsn_Invoke
-  llvm::Value* callee_method_object_addr = call_inst.getArgOperand(0);
-  unsigned num_args = call_inst.getNumArgOperands();
-  llvm::Type* ret_type = call_inst.getType();
-
-  // Determine the function type of the callee method
-  std::vector<llvm::Type*> args_type;
-  std::vector<llvm::Value*> args;
-  for (unsigned i = 0; i < num_args; i++) {
-    args.push_back(call_inst.getArgOperand(i));
-    args_type.push_back(args[i]->getType());
-  }
-
-  llvm::FunctionType* callee_method_type =
-    llvm::FunctionType::get(ret_type, args_type, false);
-
-  llvm::Value* code_addr =
-    irb_.LoadFromObjectOffset(callee_method_object_addr,
-                              art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value(),
-                              callee_method_type->getPointerTo(),
-                              kTBAARuntimeInfo);
-
-  // Invoke callee
-  llvm::Value* retval = irb_.CreateCall(code_addr, args);
-
-  return retval;
-}
-
-llvm::Value* GBCExpanderPass::Expand_DivRem(llvm::CallInst& call_inst,
-                                            bool is_div, JType op_jty) {
-  llvm::Value* dividend = call_inst.getArgOperand(0);
-  llvm::Value* divisor = call_inst.getArgOperand(1);
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  EmitGuard_DivZeroException(dex_pc, divisor, op_jty);
-  // Most of the codes refer to MethodCompiler::EmitIntDivRemResultComputation
-
-  // Check the special case: MININT / -1 = MININT
-  // That case will cause overflow, which is undefined behavior in llvm.
-  // So we check the divisor is -1 or not, if the divisor is -1, we do
-  // the special path to avoid undefined behavior.
-  llvm::Type* op_type = irb_.getJType(op_jty);
-  llvm::Value* zero = irb_.getJZero(op_jty);
-  llvm::Value* neg_one = llvm::ConstantInt::getSigned(op_type, -1);
-
-  llvm::Function* parent = irb_.GetInsertBlock()->getParent();
-  llvm::BasicBlock* eq_neg_one = llvm::BasicBlock::Create(context_, "", parent);
-  llvm::BasicBlock* ne_neg_one = llvm::BasicBlock::Create(context_, "", parent);
-  llvm::BasicBlock* neg_one_cont =
-    llvm::BasicBlock::Create(context_, "", parent);
-
-  llvm::Value* is_equal_neg_one = irb_.CreateICmpEQ(divisor, neg_one);
-  irb_.CreateCondBr(is_equal_neg_one, eq_neg_one, ne_neg_one, kUnlikely);
-
-  // If divisor == -1
-  irb_.SetInsertPoint(eq_neg_one);
-  llvm::Value* eq_result;
-  if (is_div) {
-    // We can just change from "dividend div -1" to "neg dividend". The sub
-    // don't care the sign/unsigned because of two's complement representation.
-    // And the behavior is what we want:
-    //  -(2^n)        (2^n)-1
-    //  MININT  < k <= MAXINT    ->     mul k -1  =  -k
-    //  MININT == k              ->     mul k -1  =   k
-    //
-    // LLVM use sub to represent 'neg'
-    eq_result = irb_.CreateSub(zero, dividend);
-  } else {
-    // Everything modulo -1 will be 0.
-    eq_result = zero;
-  }
-  irb_.CreateBr(neg_one_cont);
-
-  // If divisor != -1, just do the division.
-  irb_.SetInsertPoint(ne_neg_one);
-  llvm::Value* ne_result;
-  if (is_div) {
-    ne_result = irb_.CreateSDiv(dividend, divisor);
-  } else {
-    ne_result = irb_.CreateSRem(dividend, divisor);
-  }
-  irb_.CreateBr(neg_one_cont);
-
-  irb_.SetInsertPoint(neg_one_cont);
-  llvm::PHINode* result = irb_.CreatePHI(op_type, 2);
-  result->addIncoming(eq_result, eq_neg_one);
-  result->addIncoming(ne_result, ne_neg_one);
-
-  return result;
-}
-
-void GBCExpanderPass::Expand_AllocaShadowFrame(llvm::Value* num_vregs_value) {
-  // Most of the codes refer to MethodCompiler::EmitPrologueAllocShadowFrame and
-  // MethodCompiler::EmitPushShadowFrame
-  uint16_t num_vregs =
-    llvm::cast<llvm::ConstantInt>(num_vregs_value)->getZExtValue();
-
-  llvm::StructType* shadow_frame_type =
-    irb_.getShadowFrameTy(num_vregs);
-
-  // Create allocas at the start of entry block.
-  llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP();
-  llvm::BasicBlock* entry_block = &func_->front();
-  irb_.SetInsertPoint(&entry_block->front());
-
-  shadow_frame_ = irb_.CreateAlloca(shadow_frame_type);
-
-  // Alloca a pointer to old shadow frame
-  old_shadow_frame_ =
-    irb_.CreateAlloca(shadow_frame_type->getElementType(0)->getPointerTo());
-
-  irb_.restoreIP(irb_ip_original);
-
-  // Push the shadow frame
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-  llvm::Value* shadow_frame_upcast =
-    irb_.CreateConstGEP2_32(shadow_frame_, 0, 0);
-
-  llvm::Value* result = rtb_.EmitPushShadowFrame(shadow_frame_upcast,
-                                                 method_object_addr,
-                                                 num_vregs);
-
-  irb_.CreateStore(result, old_shadow_frame_, kTBAARegister);
-
-  return;
-}
-
-void GBCExpanderPass::Expand_SetVReg(llvm::Value* entry_idx,
-                                     llvm::Value* value) {
-  unsigned vreg_idx = LV2UInt(entry_idx);
-  DCHECK_LT(vreg_idx, dex_compilation_unit_->GetCodeItem()->registers_size_);
-
-  llvm::Value* vreg_addr = shadow_frame_vreg_addresses_[vreg_idx];
-  if (UNLIKELY(vreg_addr == NULL)) {
-    DCHECK(shadow_frame_ != NULL);
-
-    llvm::Value* gep_index[] = {
-      irb_.getInt32(0),  // No pointer displacement
-      irb_.getInt32(1),  // VRegs
-      entry_idx  // Pointer field
-    };
-
-    // A shadow frame address must dominate every use in the function so we
-    // place it in the entry block right after the allocas.
-    llvm::BasicBlock::iterator first_non_alloca = func_->getEntryBlock().begin();
-    while (llvm::isa<llvm::AllocaInst>(first_non_alloca)) {
-      ++first_non_alloca;
-    }
-
-    llvm::IRBuilderBase::InsertPoint ip = irb_.saveIP();
-    irb_.SetInsertPoint(static_cast<llvm::Instruction*>(first_non_alloca));
-    vreg_addr = irb_.CreateGEP(shadow_frame_, gep_index);
-    shadow_frame_vreg_addresses_[vreg_idx] = vreg_addr;
-    irb_.restoreIP(ip);
-  }
-
-  irb_.CreateStore(value,
-                   irb_.CreateBitCast(vreg_addr, value->getType()->getPointerTo()),
-                   kTBAAShadowFrame);
-  return;
-}
-
-void GBCExpanderPass::Expand_PopShadowFrame() {
-  if (old_shadow_frame_ == NULL) {
-    return;
-  }
-  rtb_.EmitPopShadowFrame(irb_.CreateLoad(old_shadow_frame_, kTBAARegister));
-  return;
-}
-
-void GBCExpanderPass::Expand_UpdateDexPC(llvm::Value* dex_pc_value) {
-  irb_.StoreToObjectOffset(shadow_frame_,
-                           art::ShadowFrame::DexPCOffset(),
-                           dex_pc_value,
-                           kTBAAShadowFrame);
-  return;
-}
-
-void GBCExpanderPass::InsertStackOverflowCheck(llvm::Function& func) {
-  // All alloca instructions are generated in the first basic block of the
-  // function, and there are no alloca instructions after the first non-alloca
-  // instruction.
-
-  llvm::BasicBlock* first_basic_block = &func.front();
-
-  // Look for first non-alloca instruction
-  llvm::BasicBlock::iterator first_non_alloca = first_basic_block->begin();
-  while (llvm::isa<llvm::AllocaInst>(first_non_alloca)) {
-    ++first_non_alloca;
-  }
-
-  irb_.SetInsertPoint(first_non_alloca);
-
-  // Insert stack overflow check codes before first_non_alloca (i.e., after all
-  // alloca instructions)
-  EmitStackOverflowCheck(&*first_non_alloca);
-
-  irb_.Runtime().EmitTestSuspend();
-
-  llvm::BasicBlock* next_basic_block = irb_.GetInsertBlock();
-  if (next_basic_block != first_basic_block) {
-    // Splice the rest of the instruction to the continuing basic block
-    next_basic_block->getInstList().splice(
-        irb_.GetInsertPoint(), first_basic_block->getInstList(),
-        first_non_alloca, first_basic_block->end());
-
-    // Rewrite the basic block
-    RewriteBasicBlock(next_basic_block);
-
-    // Update the phi-instructions in the successor basic block
-    UpdatePhiInstruction(first_basic_block, irb_.GetInsertBlock());
-  }
-
-  // We have changed the basic block
-  changed_ = true;
-}
-
-// ==== High-level intrinsic expander ==========================================
-
-llvm::Value* GBCExpanderPass::Expand_FPCompare(llvm::Value* src1_value,
-                                               llvm::Value* src2_value,
-                                               bool gt_bias) {
-  llvm::Value* cmp_eq = irb_.CreateFCmpOEQ(src1_value, src2_value);
-  llvm::Value* cmp_lt;
-
-  if (gt_bias) {
-    cmp_lt = irb_.CreateFCmpOLT(src1_value, src2_value);
-  } else {
-    cmp_lt = irb_.CreateFCmpULT(src1_value, src2_value);
-  }
-
-  return EmitCompareResultSelection(cmp_eq, cmp_lt);
-}
-
-llvm::Value* GBCExpanderPass::Expand_LongCompare(llvm::Value* src1_value, llvm::Value* src2_value) {
-  llvm::Value* cmp_eq = irb_.CreateICmpEQ(src1_value, src2_value);
-  llvm::Value* cmp_lt = irb_.CreateICmpSLT(src1_value, src2_value);
-
-  return EmitCompareResultSelection(cmp_eq, cmp_lt);
-}
-
-llvm::Value* GBCExpanderPass::EmitCompareResultSelection(llvm::Value* cmp_eq,
-                                                         llvm::Value* cmp_lt) {
-  llvm::Constant* zero = irb_.getJInt(0);
-  llvm::Constant* pos1 = irb_.getJInt(1);
-  llvm::Constant* neg1 = irb_.getJInt(-1);
-
-  llvm::Value* result_lt = irb_.CreateSelect(cmp_lt, neg1, pos1);
-  llvm::Value* result_eq = irb_.CreateSelect(cmp_eq, zero, result_lt);
-
-  return result_eq;
-}
-
-llvm::Value* GBCExpanderPass::Expand_IntegerShift(llvm::Value* src1_value,
-                                                  llvm::Value* src2_value,
-                                                  IntegerShiftKind kind,
-                                                  JType op_jty) {
-  DCHECK(op_jty == kInt || op_jty == kLong);
-
-  // Mask and zero-extend RHS properly
-  if (op_jty == kInt) {
-    src2_value = irb_.CreateAnd(src2_value, 0x1f);
-  } else {
-    llvm::Value* masked_src2_value = irb_.CreateAnd(src2_value, 0x3f);
-    src2_value = irb_.CreateZExt(masked_src2_value, irb_.getJLongTy());
-  }
-
-  // Create integer shift llvm instruction
-  switch (kind) {
-  case kIntegerSHL:
-    return irb_.CreateShl(src1_value, src2_value);
-
-  case kIntegerSHR:
-    return irb_.CreateAShr(src1_value, src2_value);
-
-  case kIntegerUSHR:
-    return irb_.CreateLShr(src1_value, src2_value);
-
-  default:
-    LOG(FATAL) << "Unknown integer shift kind: " << kind;
-    return NULL;
-  }
-}
-
-llvm::Value* GBCExpanderPass::SignOrZeroExtendCat1Types(llvm::Value* value, JType jty) {
-  switch (jty) {
-    case kBoolean:
-    case kChar:
-      return irb_.CreateZExt(value, irb_.getJType(kInt));
-    case kByte:
-    case kShort:
-      return irb_.CreateSExt(value, irb_.getJType(kInt));
-    case kVoid:
-    case kInt:
-    case kLong:
-    case kFloat:
-    case kDouble:
-    case kObject:
-      return value;  // Nothing to do.
-    default:
-      LOG(FATAL) << "Unknown java type: " << jty;
-      return NULL;
-  }
-}
-
-llvm::Value* GBCExpanderPass::TruncateCat1Types(llvm::Value* value, JType jty) {
-  switch (jty) {
-    case kBoolean:
-    case kChar:
-    case kByte:
-    case kShort:
-      return irb_.CreateTrunc(value, irb_.getJType(jty));
-    case kVoid:
-    case kInt:
-    case kLong:
-    case kFloat:
-    case kDouble:
-    case kObject:
-      return value;  // Nothing to do.
-    default:
-      LOG(FATAL) << "Unknown java type: " << jty;
-      return NULL;
-  }
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLArrayGet(llvm::CallInst& call_inst,
-                                                JType elem_jty) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  llvm::Value* array_addr = call_inst.getArgOperand(1);
-  llvm::Value* index_value = call_inst.getArgOperand(2);
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags);
-  EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, index_value,
-                                           opt_flags);
-
-  llvm::Value* array_elem_addr = EmitArrayGEP(array_addr, index_value, elem_jty);
-
-  llvm::Value* array_elem_value = irb_.CreateLoad(array_elem_addr, kTBAAHeapArray, elem_jty);
-
-  return SignOrZeroExtendCat1Types(array_elem_value, elem_jty);
-}
-
-
-void GBCExpanderPass::Expand_HLArrayPut(llvm::CallInst& call_inst,
-                                        JType elem_jty) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  llvm::Value* new_value = call_inst.getArgOperand(1);
-  llvm::Value* array_addr = call_inst.getArgOperand(2);
-  llvm::Value* index_value = call_inst.getArgOperand(3);
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags);
-  EmitGuard_ArrayIndexOutOfBoundsException(dex_pc, array_addr, index_value,
-                                           opt_flags);
-
-  new_value = TruncateCat1Types(new_value, elem_jty);
-
-  llvm::Value* array_elem_addr = EmitArrayGEP(array_addr, index_value, elem_jty);
-
-  if (elem_jty == kObject) {  // If put an object, check the type, and mark GC card table.
-    llvm::Function* runtime_func = irb_.GetRuntime(CheckPutArrayElement);
-
-    irb_.CreateCall2(runtime_func, new_value, array_addr);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-    EmitMarkGCCard(new_value, array_addr);
-  }
-
-  irb_.CreateStore(new_value, array_elem_addr, kTBAAHeapArray, elem_jty);
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst,
-                                            JType field_jty) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  llvm::Value* object_addr = call_inst.getArgOperand(1);
-  uint32_t field_idx = LV2UInt(call_inst.getArgOperand(2));
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
-  llvm::Value* field_value;
-
-  art::MemberOffset field_offset(0u);
-  bool is_volatile;
-  bool is_fast_path = driver_->ComputeInstanceFieldInfo(
-    field_idx, dex_compilation_unit_, false, &field_offset, &is_volatile);
-
-  if (!is_fast_path) {
-    llvm::Function* runtime_func;
-
-    if (field_jty == kObject) {
-      runtime_func = irb_.GetRuntime(GetObjectInstance);
-    } else if (field_jty == kLong || field_jty == kDouble) {
-      runtime_func = irb_.GetRuntime(Get64Instance);
-    } else {
-      runtime_func = irb_.GetRuntime(Get32Instance);
-    }
-
-    llvm::ConstantInt* field_idx_value = irb_.getInt32(field_idx);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    EmitUpdateDexPC(dex_pc);
-
-    field_value = irb_.CreateCall3(runtime_func, field_idx_value,
-                                   method_object_addr, object_addr);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-    if (field_jty == kFloat || field_jty == kDouble) {
-      field_value = irb_.CreateBitCast(field_value, irb_.getJType(field_jty));
-    }
-  } else {
-    DCHECK_GE(field_offset.Int32Value(), 0);
-
-    llvm::PointerType* field_type =
-      irb_.getJType(field_jty)->getPointerTo();
-
-    llvm::ConstantInt* field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
-    llvm::Value* field_addr =
-      irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
-    field_value = irb_.CreateLoad(field_addr, kTBAAHeapInstance, field_jty);
-    field_value = SignOrZeroExtendCat1Types(field_value, field_jty);
-
-    if (is_volatile) {
-      irb_.CreateMemoryBarrier(art::kLoadAny);
-    }
-  }
-
-  return field_value;
-}
-
-void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst,
-                                    JType field_jty) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  llvm::Value* new_value = call_inst.getArgOperand(1);
-  llvm::Value* object_addr = call_inst.getArgOperand(2);
-  uint32_t field_idx = LV2UInt(call_inst.getArgOperand(3));
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
-  art::MemberOffset field_offset(0u);
-  bool is_volatile;
-  bool is_fast_path = driver_->ComputeInstanceFieldInfo(
-    field_idx, dex_compilation_unit_, true, &field_offset, &is_volatile);
-
-  if (!is_fast_path) {
-    llvm::Function* runtime_func;
-
-    if (field_jty == kFloat) {
-      new_value = irb_.CreateBitCast(new_value, irb_.getJType(kInt));
-    } else if (field_jty == kDouble) {
-      new_value = irb_.CreateBitCast(new_value, irb_.getJType(kLong));
-    }
-
-    if (field_jty == kObject) {
-      runtime_func = irb_.GetRuntime(SetObjectInstance);
-    } else if (field_jty == kLong || field_jty == kDouble) {
-      runtime_func = irb_.GetRuntime(Set64Instance);
-    } else {
-      runtime_func = irb_.GetRuntime(Set32Instance);
-    }
-
-    llvm::Value* field_idx_value = irb_.getInt32(field_idx);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    EmitUpdateDexPC(dex_pc);
-
-    irb_.CreateCall4(runtime_func, field_idx_value,
-                     method_object_addr, object_addr, new_value);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-  } else {
-    DCHECK_GE(field_offset.Int32Value(), 0);
-
-    if (is_volatile) {
-      irb_.CreateMemoryBarrier(art::kAnyStore);
-    }
-
-    llvm::PointerType* field_type =
-      irb_.getJType(field_jty)->getPointerTo();
-
-    llvm::Value* field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
-    llvm::Value* field_addr =
-      irb_.CreatePtrDisp(object_addr, field_offset_value, field_type);
-
-    new_value = TruncateCat1Types(new_value, field_jty);
-    irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty);
-
-    if (is_volatile) {
-      irb_.CreateMemoryBarrier(art::kAnyAny);
-    }
-
-    if (field_jty == kObject) {  // If put an object, mark the GC card table.
-      EmitMarkGCCard(new_value, object_addr);
-    }
-  }
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadConstantClass(uint32_t dex_pc,
-                                                    uint32_t type_idx) {
-  if (!driver_->CanAccessTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(),
-                                           *dex_compilation_unit_->GetDexFile(), type_idx)) {
-    llvm::Value* type_idx_value = irb_.getInt32(type_idx);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-    llvm::Function* runtime_func = irb_.GetRuntime(InitializeTypeAndVerifyAccess);
-
-    EmitUpdateDexPC(dex_pc);
-
-    llvm::Value* type_object_addr =
-      irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-    return type_object_addr;
-
-  } else {
-    // Try to load the class (type) object from the test cache.
-    llvm::Value* type_field_addr =
-      EmitLoadDexCacheResolvedTypeFieldAddr(type_idx);
-
-    llvm::Value* type_object_addr = irb_.CreateLoad(type_field_addr, kTBAARuntimeInfo);
-
-    if (driver_->CanAssumeTypeIsPresentInDexCache(*dex_compilation_unit_->GetDexFile(), type_idx)) {
-      return type_object_addr;
-    }
-
-    llvm::BasicBlock* block_original = irb_.GetInsertBlock();
-
-    // Test whether class (type) object is in the dex cache or not
-    llvm::Value* equal_null =
-      irb_.CreateICmpEQ(type_object_addr, irb_.getJNull());
-
-    llvm::BasicBlock* block_cont =
-      CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-    llvm::BasicBlock* block_load_class =
-      CreateBasicBlockWithDexPC(dex_pc, "load_class");
-
-    irb_.CreateCondBr(equal_null, block_load_class, block_cont, kUnlikely);
-
-    // Failback routine to load the class object
-    irb_.SetInsertPoint(block_load_class);
-
-    llvm::Function* runtime_func = irb_.GetRuntime(InitializeType);
-
-    llvm::Constant* type_idx_value = irb_.getInt32(type_idx);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-    EmitUpdateDexPC(dex_pc);
-
-    llvm::Value* loaded_type_object_addr =
-      irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-    llvm::BasicBlock* block_after_load_class = irb_.GetInsertBlock();
-
-    irb_.CreateBr(block_cont);
-
-    // Now the class object must be loaded
-    irb_.SetInsertPoint(block_cont);
-
-    llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2);
-
-    phi->addIncoming(type_object_addr, block_original);
-    phi->addIncoming(loaded_type_object_addr, block_after_load_class);
-
-    return phi;
-  }
-}
-
-llvm::Value* GBCExpanderPass::EmitLoadStaticStorage(uint32_t dex_pc,
-                                                    uint32_t type_idx) {
-  llvm::BasicBlock* block_load_static =
-    CreateBasicBlockWithDexPC(dex_pc, "load_static");
-
-  llvm::BasicBlock* block_check_init = CreateBasicBlockWithDexPC(dex_pc, "init");
-  llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-  // Load static storage from dex cache
-  llvm::Value* storage_field_addr = EmitLoadDexCacheResolvedTypeFieldAddr(type_idx);
-
-  llvm::Value* storage_object_addr = irb_.CreateLoad(storage_field_addr, kTBAARuntimeInfo);
-
-  // Test: Is the class resolved?
-  llvm::Value* equal_null = irb_.CreateICmpEQ(storage_object_addr, irb_.getJNull());
-
-  irb_.CreateCondBr(equal_null, block_load_static, block_check_init, kUnlikely);
-
-  // storage_object_addr != null, so check if its initialized.
-  irb_.SetInsertPoint(block_check_init);
-
-  llvm::Value* class_status =
-      irb_.LoadFromObjectOffset(storage_object_addr,
-                                art::mirror::Class::StatusOffset().Int32Value(),
-                                irb_.getJIntTy(), kTBAAHeapInstance);
-
-  llvm::Value* is_not_initialized =
-      irb_.CreateICmpULT(class_status, irb_.getInt32(art::mirror::Class::kStatusInitialized));
-
-  irb_.CreateCondBr(is_not_initialized, block_load_static, block_cont, kUnlikely);
-
-  // Failback routine to load the class object
-  irb_.SetInsertPoint(block_load_static);
-
-  llvm::Function* runtime_func = irb_.GetRuntime(InitializeStaticStorage);
-
-  llvm::Constant* type_idx_value = irb_.getInt32(type_idx);
-
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-  EmitUpdateDexPC(dex_pc);
-
-  llvm::Value* loaded_storage_object_addr =
-    irb_.CreateCall3(runtime_func, type_idx_value, method_object_addr, thread_object_addr);
-
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  llvm::BasicBlock* block_after_load_static = irb_.GetInsertBlock();
-
-  irb_.CreateBr(block_cont);
-
-  // Now the class object must be loaded
-  irb_.SetInsertPoint(block_cont);
-
-  llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2);
-
-  phi->addIncoming(storage_object_addr, block_check_init);
-  phi->addIncoming(loaded_storage_object_addr, block_after_load_static);
-
-  // Ensure load of status and load of value don't re-order.
-  irb_.CreateMemoryBarrier(art::kLoadAny);
-
-  return phi;
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst,
-                                            JType field_jty) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0));
-
-  art::MemberOffset field_offset(0u);
-  uint32_t ssb_index;
-  bool is_referrers_class;
-  bool is_volatile;
-  bool is_initialized;
-
-  bool is_fast_path = driver_->ComputeStaticFieldInfo(
-    field_idx, dex_compilation_unit_, false,
-    &field_offset, &ssb_index, &is_referrers_class, &is_volatile, &is_initialized);
-
-  llvm::Value* static_field_value;
-
-  if (!is_fast_path) {
-    llvm::Function* runtime_func;
-
-    if (field_jty == kObject) {
-      runtime_func = irb_.GetRuntime(GetObjectStatic);
-    } else if (field_jty == kLong || field_jty == kDouble) {
-      runtime_func = irb_.GetRuntime(Get64Static);
-    } else {
-      runtime_func = irb_.GetRuntime(Get32Static);
-    }
-
-    llvm::Constant* field_idx_value = irb_.getInt32(field_idx);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    EmitUpdateDexPC(dex_pc);
-
-    static_field_value =
-      irb_.CreateCall2(runtime_func, field_idx_value, method_object_addr);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-    if (field_jty == kFloat || field_jty == kDouble) {
-      static_field_value = irb_.CreateBitCast(static_field_value, irb_.getJType(field_jty));
-    }
-  } else {
-    DCHECK_GE(field_offset.Int32Value(), 0);
-
-    llvm::Value* static_storage_addr = NULL;
-
-    if (is_referrers_class) {
-      // Fast path, static storage base is this method's class
-      llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-      static_storage_addr =
-        irb_.LoadFromObjectOffset(method_object_addr,
-                                  art::mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
-                                  irb_.getJObjectTy(),
-                                  kTBAAConstJObject);
-    } else {
-      // Medium path, static storage base in a different class which
-      // requires checks that the other class is initialized
-      DCHECK_NE(ssb_index, art::DexFile::kDexNoIndex);
-      static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index);
-    }
-
-    llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
-    llvm::Value* static_field_addr =
-      irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
-                         irb_.getJType(field_jty)->getPointerTo());
-
-    static_field_value = irb_.CreateLoad(static_field_addr, kTBAAHeapStatic, field_jty);
-    static_field_value = SignOrZeroExtendCat1Types(static_field_value, field_jty);
-
-    if (is_volatile) {
-      irb_.CreateMemoryBarrier(art::kLoadAny);
-    }
-  }
-
-  return static_field_value;
-}
-
-void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst,
-                                    JType field_jty) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t field_idx = LV2UInt(call_inst.getArgOperand(0));
-  llvm::Value* new_value = call_inst.getArgOperand(1);
-
-  if (field_jty == kFloat || field_jty == kDouble) {
-    new_value = irb_.CreateBitCast(new_value, irb_.getJType(field_jty));
-  }
-
-  art::MemberOffset field_offset(0u);
-  uint32_t ssb_index;
-  bool is_referrers_class;
-  bool is_volatile;
-  bool is_initialized;
-
-  bool is_fast_path = driver_->ComputeStaticFieldInfo(
-    field_idx, dex_compilation_unit_, true,
-    &field_offset, &ssb_index, &is_referrers_class, &is_volatile, &is_initialized);
-
-  if (!is_fast_path) {
-    llvm::Function* runtime_func;
-
-    if (field_jty == kObject) {
-      runtime_func = irb_.GetRuntime(SetObjectStatic);
-    } else if (field_jty == kLong || field_jty == kDouble) {
-      runtime_func = irb_.GetRuntime(Set64Static);
-    } else {
-      runtime_func = irb_.GetRuntime(Set32Static);
-    }
-
-    if (field_jty == kFloat) {
-      new_value = irb_.CreateBitCast(new_value, irb_.getJType(kInt));
-    } else if (field_jty == kDouble) {
-      new_value = irb_.CreateBitCast(new_value, irb_.getJType(kLong));
-    }
-
-    llvm::Constant* field_idx_value = irb_.getInt32(field_idx);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    EmitUpdateDexPC(dex_pc);
-
-    irb_.CreateCall3(runtime_func, field_idx_value,
-                     method_object_addr, new_value);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-  } else {
-    DCHECK_GE(field_offset.Int32Value(), 0);
-
-    llvm::Value* static_storage_addr = NULL;
-
-    if (is_referrers_class) {
-      // Fast path, static storage base is this method's class
-      llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-      static_storage_addr =
-        irb_.LoadFromObjectOffset(method_object_addr,
-                                  art::mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
-                                  irb_.getJObjectTy(),
-                                  kTBAAConstJObject);
-    } else {
-      // Medium path, static storage base in a different class which
-      // requires checks that the other class is initialized
-      DCHECK_NE(ssb_index, art::DexFile::kDexNoIndex);
-      static_storage_addr = EmitLoadStaticStorage(dex_pc, ssb_index);
-    }
-
-    if (is_volatile) {
-      irb_.CreateMemoryBarrier(art::kAnyStore);
-    }
-
-    llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
-
-    llvm::Value* static_field_addr =
-      irb_.CreatePtrDisp(static_storage_addr, static_field_offset_value,
-                         irb_.getJType(field_jty)->getPointerTo());
-
-    new_value = TruncateCat1Types(new_value, field_jty);
-    irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty);
-
-    if (is_volatile) {
-      irb_.CreateMemoryBarrier(art::kAnyAny);
-    }
-
-    if (field_jty == kObject) {  // If put an object, mark the GC card table.
-      EmitMarkGCCard(new_value, static_storage_addr);
-    }
-  }
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_ConstString(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t string_idx = LV2UInt(call_inst.getArgOperand(0));
-
-  llvm::Value* string_field_addr = EmitLoadDexCacheStringFieldAddr(string_idx);
-
-  llvm::Value* string_addr = irb_.CreateLoad(string_field_addr, kTBAARuntimeInfo);
-
-  if (!driver_->CanAssumeStringIsPresentInDexCache(*dex_compilation_unit_->GetDexFile(),
-                                                   string_idx)) {
-    llvm::BasicBlock* block_str_exist =
-      CreateBasicBlockWithDexPC(dex_pc, "str_exist");
-
-    llvm::BasicBlock* block_str_resolve =
-      CreateBasicBlockWithDexPC(dex_pc, "str_resolve");
-
-    llvm::BasicBlock* block_cont =
-      CreateBasicBlockWithDexPC(dex_pc, "str_cont");
-
-    // Test: Is the string resolved and in the dex cache?
-    llvm::Value* equal_null = irb_.CreateICmpEQ(string_addr, irb_.getJNull());
-
-    irb_.CreateCondBr(equal_null, block_str_resolve, block_str_exist, kUnlikely);
-
-    // String is resolved, go to next basic block.
-    irb_.SetInsertPoint(block_str_exist);
-    irb_.CreateBr(block_cont);
-
-    // String is not resolved yet, resolve it now.
-    irb_.SetInsertPoint(block_str_resolve);
-
-    llvm::Function* runtime_func = irb_.GetRuntime(ResolveString);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    llvm::Value* string_idx_value = irb_.getInt32(string_idx);
-
-    EmitUpdateDexPC(dex_pc);
-
-    llvm::Value* result = irb_.CreateCall2(runtime_func, method_object_addr,
-                                           string_idx_value);
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-
-    irb_.CreateBr(block_cont);
-
-
-    llvm::BasicBlock* block_pre_cont = irb_.GetInsertBlock();
-
-    irb_.SetInsertPoint(block_cont);
-
-    llvm::PHINode* phi = irb_.CreatePHI(irb_.getJObjectTy(), 2);
-
-    phi->addIncoming(string_addr, block_str_exist);
-    phi->addIncoming(result, block_pre_cont);
-
-    string_addr = phi;
-  }
-
-  return string_addr;
-}
-
-llvm::Value* GBCExpanderPass::Expand_ConstClass(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-
-  llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx);
-
-  return type_object_addr;
-}
-
-void GBCExpanderPass::Expand_MonitorEnter(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  llvm::Value* object_addr = call_inst.getArgOperand(1);
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
-  EmitUpdateDexPC(dex_pc);
-
-  irb_.Runtime().EmitLockObject(object_addr);
-
-  return;
-}
-
-void GBCExpanderPass::Expand_MonitorExit(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  llvm::Value* object_addr = call_inst.getArgOperand(1);
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, object_addr, opt_flags);
-
-  EmitUpdateDexPC(dex_pc);
-
-  irb_.Runtime().EmitUnlockObject(object_addr);
-
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  return;
-}
-
-void GBCExpanderPass::Expand_HLCheckCast(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-  llvm::Value* object_addr = call_inst.getArgOperand(1);
-
-  llvm::BasicBlock* block_test_class =
-    CreateBasicBlockWithDexPC(dex_pc, "test_class");
-
-  llvm::BasicBlock* block_test_sub_class =
-    CreateBasicBlockWithDexPC(dex_pc, "test_sub_class");
-
-  llvm::BasicBlock* block_cont =
-    CreateBasicBlockWithDexPC(dex_pc, "checkcast_cont");
-
-  // Test: Is the reference equal to null?  Act as no-op when it is null.
-  llvm::Value* equal_null = irb_.CreateICmpEQ(object_addr, irb_.getJNull());
-
-  irb_.CreateCondBr(equal_null, block_cont, block_test_class, kUnlikely);
-
-  // Test: Is the object instantiated from the given class?
-  irb_.SetInsertPoint(block_test_class);
-  llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx);
-  DCHECK_EQ(art::mirror::Object::ClassOffset().Int32Value(), 0);
-
-  llvm::PointerType* jobject_ptr_ty = irb_.getJObjectTy();
-
-  llvm::Value* object_type_field_addr =
-    irb_.CreateBitCast(object_addr, jobject_ptr_ty->getPointerTo());
-
-  llvm::Value* object_type_object_addr =
-    irb_.CreateLoad(object_type_field_addr, kTBAAConstJObject);
-
-  llvm::Value* equal_class =
-    irb_.CreateICmpEQ(type_object_addr, object_type_object_addr);
-
-  irb_.CreateCondBr(equal_class, block_cont, block_test_sub_class, kLikely);
-
-  // Test: Is the object instantiated from the subclass of the given class?
-  irb_.SetInsertPoint(block_test_sub_class);
-
-  EmitUpdateDexPC(dex_pc);
-
-  irb_.CreateCall2(irb_.GetRuntime(CheckCast),
-                   type_object_addr, object_type_object_addr);
-
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  irb_.CreateBr(block_cont);
-
-  irb_.SetInsertPoint(block_cont);
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::Expand_InstanceOf(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-  llvm::Value* object_addr = call_inst.getArgOperand(1);
-
-  llvm::BasicBlock* block_nullp =
-      CreateBasicBlockWithDexPC(dex_pc, "nullp");
-
-  llvm::BasicBlock* block_test_class =
-      CreateBasicBlockWithDexPC(dex_pc, "test_class");
-
-  llvm::BasicBlock* block_class_equals =
-      CreateBasicBlockWithDexPC(dex_pc, "class_eq");
-
-  llvm::BasicBlock* block_test_sub_class =
-      CreateBasicBlockWithDexPC(dex_pc, "test_sub_class");
-
-  llvm::BasicBlock* block_cont =
-      CreateBasicBlockWithDexPC(dex_pc, "instance_of_cont");
-
-  // Overview of the following code :
-  // We check for null, if so, then false, otherwise check for class == . If so
-  // then true, otherwise do callout slowpath.
-  //
-  // Test: Is the reference equal to null?  Set 0 when it is null.
-  llvm::Value* equal_null = irb_.CreateICmpEQ(object_addr, irb_.getJNull());
-
-  irb_.CreateCondBr(equal_null, block_nullp, block_test_class, kUnlikely);
-
-  irb_.SetInsertPoint(block_nullp);
-  irb_.CreateBr(block_cont);
-
-  // Test: Is the object instantiated from the given class?
-  irb_.SetInsertPoint(block_test_class);
-  llvm::Value* type_object_addr = EmitLoadConstantClass(dex_pc, type_idx);
-  DCHECK_EQ(art::mirror::Object::ClassOffset().Int32Value(), 0);
-
-  llvm::PointerType* jobject_ptr_ty = irb_.getJObjectTy();
-
-  llvm::Value* object_type_field_addr =
-    irb_.CreateBitCast(object_addr, jobject_ptr_ty->getPointerTo());
-
-  llvm::Value* object_type_object_addr =
-    irb_.CreateLoad(object_type_field_addr, kTBAAConstJObject);
-
-  llvm::Value* equal_class =
-    irb_.CreateICmpEQ(type_object_addr, object_type_object_addr);
-
-  irb_.CreateCondBr(equal_class, block_class_equals, block_test_sub_class, kLikely);
-
-  irb_.SetInsertPoint(block_class_equals);
-  irb_.CreateBr(block_cont);
-
-  // Test: Is the object instantiated from the subclass of the given class?
-  irb_.SetInsertPoint(block_test_sub_class);
-  llvm::Value* result =
-    irb_.CreateCall2(irb_.GetRuntime(IsAssignable),
-                     type_object_addr, object_type_object_addr);
-  irb_.CreateBr(block_cont);
-
-  irb_.SetInsertPoint(block_cont);
-
-  llvm::PHINode* phi = irb_.CreatePHI(irb_.getJIntTy(), 3);
-
-  phi->addIncoming(irb_.getJInt(0), block_nullp);
-  phi->addIncoming(irb_.getJInt(1), block_class_equals);
-  phi->addIncoming(result, block_test_sub_class);
-
-  return phi;
-}
-
-llvm::Value* GBCExpanderPass::Expand_NewInstance(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-
-  llvm::Function* runtime_func;
-  if (driver_->CanAccessInstantiableTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(),
-                                                      *dex_compilation_unit_->GetDexFile(),
-                                                      type_idx)) {
-    runtime_func = irb_.GetRuntime(AllocObject);
-  } else {
-    runtime_func = irb_.GetRuntime(AllocObjectWithAccessCheck);
-  }
-
-  llvm::Constant* type_index_value = irb_.getInt32(type_idx);
-
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-  EmitUpdateDexPC(dex_pc);
-
-  llvm::Value* object_addr =
-    irb_.CreateCall3(runtime_func, type_index_value, method_object_addr, thread_object_addr);
-
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  return object_addr;
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLInvoke(llvm::CallInst& call_inst) {
-  art::InvokeType invoke_type = static_cast<art::InvokeType>(LV2UInt(call_inst.getArgOperand(0)));
-  bool is_static = (invoke_type == art::kStatic);
-
-  if (!is_static) {
-    // Test: Is *this* parameter equal to null?
-    uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-    llvm::Value* this_addr = call_inst.getArgOperand(3);
-    int opt_flags = LV2UInt(call_inst.getArgOperand(2));
-
-    EmitGuard_NullPointerException(dex_pc, this_addr, opt_flags);
-  }
-
-  llvm::Value* result = NULL;
-  if (EmitIntrinsic(call_inst, &result)) {
-    return result;
-  }
-
-  return EmitInvoke(call_inst);
-}
-
-llvm::Value* GBCExpanderPass::Expand_OptArrayLength(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  // Get the array object address
-  llvm::Value* array_addr = call_inst.getArgOperand(1);
-  int opt_flags = LV2UInt(call_inst.getArgOperand(0));
-
-  EmitGuard_NullPointerException(dex_pc, array_addr, opt_flags);
-
-  // Get the array length and store it to the register
-  return EmitLoadArrayLength(array_addr);
-}
-
-llvm::Value* GBCExpanderPass::Expand_NewArray(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t type_idx = LV2UInt(call_inst.getArgOperand(0));
-  llvm::Value* length = call_inst.getArgOperand(1);
-
-  return EmitAllocNewArray(dex_pc, length, type_idx, false);
-}
-
-llvm::Value* GBCExpanderPass::Expand_HLFilledNewArray(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  uint32_t type_idx = LV2UInt(call_inst.getArgOperand(1));
-  uint32_t length = call_inst.getNumArgOperands() - 3;
-
-  llvm::Value* object_addr =
-    EmitAllocNewArray(dex_pc, irb_.getInt32(length), type_idx, true);
-
-  if (length > 0) {
-    // Check for the element type
-    uint32_t type_desc_len = 0;
-    const char* type_desc =
-        dex_compilation_unit_->GetDexFile()->StringByTypeIdx(type_idx, &type_desc_len);
-
-    DCHECK_GE(type_desc_len, 2u);  // should be guaranteed by verifier
-    DCHECK_EQ(type_desc[0], '[');  // should be guaranteed by verifier
-    bool is_elem_int_ty = (type_desc[1] == 'I');
-
-    uint32_t alignment;
-    llvm::Constant* elem_size;
-    llvm::PointerType* field_type;
-
-    // NOTE: Currently filled-new-array only supports 'L', '[', and 'I'
-    // as the element, thus we are only checking 2 cases: primitive int and
-    // non-primitive type.
-    if (is_elem_int_ty) {
-      alignment = sizeof(int32_t);
-      elem_size = irb_.getPtrEquivInt(sizeof(int32_t));
-      field_type = irb_.getJIntTy()->getPointerTo();
-    } else {
-      alignment = irb_.getSizeOfPtrEquivInt();
-      elem_size = irb_.getSizeOfPtrEquivIntValue();
-      field_type = irb_.getJObjectTy()->getPointerTo();
-    }
-
-    llvm::Value* data_field_offset =
-      irb_.getPtrEquivInt(art::mirror::Array::DataOffset(alignment).Int32Value());
-
-    llvm::Value* data_field_addr =
-      irb_.CreatePtrDisp(object_addr, data_field_offset, field_type);
-
-    // TODO: Tune this code.  Currently we are generating one instruction for
-    // one element which may be very space consuming.  Maybe changing to use
-    // memcpy may help; however, since we can't guarantee that the alloca of
-    // dalvik register are continuous, we can't perform such optimization yet.
-    for (uint32_t i = 0; i < length; ++i) {
-      llvm::Value* reg_value = call_inst.getArgOperand(i+3);
-
-      irb_.CreateStore(reg_value, data_field_addr, kTBAAHeapArray);
-
-      data_field_addr =
-        irb_.CreatePtrDisp(data_field_addr, elem_size, field_type);
-    }
-  }
-
-  return object_addr;
-}
-
-void GBCExpanderPass::Expand_HLFillArrayData(llvm::CallInst& call_inst) {
-  uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-  int32_t payload_offset = static_cast<int32_t>(dex_pc) +
-                           LV2SInt(call_inst.getArgOperand(0));
-  llvm::Value* array_addr = call_inst.getArgOperand(1);
-
-  const art::Instruction::ArrayDataPayload* payload =
-    reinterpret_cast<const art::Instruction::ArrayDataPayload*>(
-        dex_compilation_unit_->GetCodeItem()->insns_ + payload_offset);
-
-  if (payload->element_count == 0) {
-    // When the number of the elements in the payload is zero, we don't have
-    // to copy any numbers.  However, we should check whether the array object
-    // address is equal to null or not.
-    EmitGuard_NullPointerException(dex_pc, array_addr, 0);
-  } else {
-    // To save the code size, we are going to call the runtime function to
-    // copy the content from DexFile.
-
-    // NOTE: We will check for the NullPointerException in the runtime.
-
-    llvm::Function* runtime_func = irb_.GetRuntime(FillArrayData);
-
-    llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-    EmitUpdateDexPC(dex_pc);
-
-    irb_.CreateCall4(runtime_func,
-                     method_object_addr, irb_.getInt32(dex_pc),
-                     array_addr, irb_.getInt32(payload_offset));
-
-    EmitGuard_ExceptionLandingPad(dex_pc);
-  }
-
-  return;
-}
-
-llvm::Value* GBCExpanderPass::EmitAllocNewArray(uint32_t dex_pc,
-                                                llvm::Value* array_length_value,
-                                                uint32_t type_idx,
-                                                bool is_filled_new_array) {
-  llvm::Function* runtime_func;
-
-  bool skip_access_check =
-    driver_->CanAccessTypeWithoutChecks(dex_compilation_unit_->GetDexMethodIndex(),
-                                        *dex_compilation_unit_->GetDexFile(), type_idx);
-
-
-  if (is_filled_new_array) {
-    runtime_func = skip_access_check ?
-      irb_.GetRuntime(CheckAndAllocArray) :
-      irb_.GetRuntime(CheckAndAllocArrayWithAccessCheck);
-  } else {
-    runtime_func = skip_access_check ?
-      irb_.GetRuntime(AllocArray) :
-      irb_.GetRuntime(AllocArrayWithAccessCheck);
-  }
-
-  llvm::Constant* type_index_value = irb_.getInt32(type_idx);
-
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-  EmitUpdateDexPC(dex_pc);
-
-  llvm::Value* object_addr =
-    irb_.CreateCall4(runtime_func, type_index_value, method_object_addr,
-                     array_length_value, thread_object_addr);
-
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  return object_addr;
-}
-
-llvm::Value* GBCExpanderPass::
-EmitCallRuntimeForCalleeMethodObjectAddr(uint32_t callee_method_idx,
-                                         art::InvokeType invoke_type,
-                                         llvm::Value* this_addr,
-                                         uint32_t dex_pc,
-                                         bool is_fast_path) {
-  llvm::Function* runtime_func = NULL;
-
-  switch (invoke_type) {
-  case art::kStatic:
-    runtime_func = irb_.GetRuntime(FindStaticMethodWithAccessCheck);
-    break;
-
-  case art::kDirect:
-    runtime_func = irb_.GetRuntime(FindDirectMethodWithAccessCheck);
-    break;
-
-  case art::kVirtual:
-    runtime_func = irb_.GetRuntime(FindVirtualMethodWithAccessCheck);
-    break;
-
-  case art::kSuper:
-    runtime_func = irb_.GetRuntime(FindSuperMethodWithAccessCheck);
-    break;
-
-  case art::kInterface:
-    if (is_fast_path) {
-      runtime_func = irb_.GetRuntime(FindInterfaceMethod);
-    } else {
-      runtime_func = irb_.GetRuntime(FindInterfaceMethodWithAccessCheck);
-    }
-    break;
-  }
-
-  llvm::Value* callee_method_idx_value = irb_.getInt32(callee_method_idx);
-
-  if (this_addr == NULL) {
-    DCHECK_EQ(invoke_type, art::kStatic);
-    this_addr = irb_.getJNull();
-  }
-
-  llvm::Value* caller_method_object_addr = EmitLoadMethodObjectAddr();
-
-  llvm::Value* thread_object_addr = irb_.Runtime().EmitGetCurrentThread();
-
-  EmitUpdateDexPC(dex_pc);
-
-  llvm::Value* callee_method_object_addr =
-    irb_.CreateCall4(runtime_func,
-                     callee_method_idx_value,
-                     this_addr,
-                     caller_method_object_addr,
-                     thread_object_addr);
-
-  EmitGuard_ExceptionLandingPad(dex_pc);
-
-  return callee_method_object_addr;
-}
-
-void GBCExpanderPass::EmitMarkGCCard(llvm::Value* value, llvm::Value* target_addr) {
-  // Using runtime support, let the target can override by InlineAssembly.
-  irb_.Runtime().EmitMarkGCCard(value, target_addr);
-}
-
-void GBCExpanderPass::EmitUpdateDexPC(uint32_t dex_pc) {
-  if (shadow_frame_ == NULL) {
-    return;
-  }
-  irb_.StoreToObjectOffset(shadow_frame_,
-                           art::ShadowFrame::DexPCOffset(),
-                           irb_.getInt32(dex_pc),
-                           kTBAAShadowFrame);
-}
-
-void GBCExpanderPass::EmitGuard_DivZeroException(uint32_t dex_pc,
-                                                 llvm::Value* denominator,
-                                                 JType op_jty) {
-  DCHECK(op_jty == kInt || op_jty == kLong) << op_jty;
-
-  llvm::Constant* zero = irb_.getJZero(op_jty);
-
-  llvm::Value* equal_zero = irb_.CreateICmpEQ(denominator, zero);
-
-  llvm::BasicBlock* block_exception = CreateBasicBlockWithDexPC(dex_pc, "div0");
-
-  llvm::BasicBlock* block_continue = CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-  irb_.CreateCondBr(equal_zero, block_exception, block_continue, kUnlikely);
-
-  irb_.SetInsertPoint(block_exception);
-  EmitUpdateDexPC(dex_pc);
-  irb_.CreateCall(irb_.GetRuntime(ThrowDivZeroException));
-  EmitBranchExceptionLandingPad(dex_pc);
-
-  irb_.SetInsertPoint(block_continue);
-}
-
-void GBCExpanderPass::EmitGuard_NullPointerException(uint32_t dex_pc,
-                                                     llvm::Value* object,
-                                                     int opt_flags) {
-  bool ignore_null_check = ((opt_flags & MIR_IGNORE_NULL_CHECK) != 0);
-  if (ignore_null_check) {
-    llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc);
-    if (lpad) {
-      // There is at least one catch: create a "fake" conditional branch to
-      // keep the exception edge to the catch block.
-      landing_pad_phi_mapping_[lpad].push_back(
-          std::make_pair(current_bb_->getUniquePredecessor(),
-                         irb_.GetInsertBlock()));
-
-      llvm::BasicBlock* block_continue =
-          CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-      irb_.CreateCondBr(irb_.getFalse(), lpad, block_continue, kUnlikely);
-
-      irb_.SetInsertPoint(block_continue);
-    }
-  } else {
-    llvm::Value* equal_null = irb_.CreateICmpEQ(object, irb_.getJNull());
-
-    llvm::BasicBlock* block_exception =
-        CreateBasicBlockWithDexPC(dex_pc, "nullp");
-
-    llvm::BasicBlock* block_continue =
-        CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-    irb_.CreateCondBr(equal_null, block_exception, block_continue, kUnlikely);
-
-    irb_.SetInsertPoint(block_exception);
-    EmitUpdateDexPC(dex_pc);
-    irb_.CreateCall(irb_.GetRuntime(ThrowNullPointerException),
-                    irb_.getInt32(dex_pc));
-    EmitBranchExceptionLandingPad(dex_pc);
-
-    irb_.SetInsertPoint(block_continue);
-  }
-}
-
-void
-GBCExpanderPass::EmitGuard_ArrayIndexOutOfBoundsException(uint32_t dex_pc,
-                                                          llvm::Value* array,
-                                                          llvm::Value* index,
-                                                          int opt_flags) {
-  bool ignore_range_check = ((opt_flags & MIR_IGNORE_RANGE_CHECK) != 0);
-  if (ignore_range_check) {
-    llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc);
-    if (lpad) {
-      // There is at least one catch: create a "fake" conditional branch to
-      // keep the exception edge to the catch block.
-      landing_pad_phi_mapping_[lpad].push_back(
-          std::make_pair(current_bb_->getUniquePredecessor(),
-                         irb_.GetInsertBlock()));
-
-      llvm::BasicBlock* block_continue =
-          CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-      irb_.CreateCondBr(irb_.getFalse(), lpad, block_continue, kUnlikely);
-
-      irb_.SetInsertPoint(block_continue);
-    }
-  } else {
-    llvm::Value* array_len = EmitLoadArrayLength(array);
-
-    llvm::Value* cmp = irb_.CreateICmpUGE(index, array_len);
-
-    llvm::BasicBlock* block_exception =
-        CreateBasicBlockWithDexPC(dex_pc, "overflow");
-
-    llvm::BasicBlock* block_continue =
-        CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-    irb_.CreateCondBr(cmp, block_exception, block_continue, kUnlikely);
-
-    irb_.SetInsertPoint(block_exception);
-
-    EmitUpdateDexPC(dex_pc);
-    irb_.CreateCall2(irb_.GetRuntime(ThrowIndexOutOfBounds), index, array_len);
-    EmitBranchExceptionLandingPad(dex_pc);
-
-    irb_.SetInsertPoint(block_continue);
-  }
-}
-
-llvm::FunctionType* GBCExpanderPass::GetFunctionType(llvm::Type* ret_type, uint32_t method_idx,
-                                                     bool is_static) {
-  // Get method signature
-  art::DexFile::MethodId const& method_id =
-      dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx);
-
-  uint32_t shorty_size;
-  const char* shorty = dex_compilation_unit_->GetDexFile()->GetMethodShorty(method_id, &shorty_size);
-  CHECK_GE(shorty_size, 1u);
-
-  // Get argument type
-  std::vector<llvm::Type*> args_type;
-
-  args_type.push_back(irb_.getJObjectTy());  // method object pointer
-
-  if (!is_static) {
-    args_type.push_back(irb_.getJType('L'));  // "this" object pointer
-  }
-
-  for (uint32_t i = 1; i < shorty_size; ++i) {
-    char shorty_type = art::RemapShorty(shorty[i]);
-    args_type.push_back(irb_.getJType(shorty_type));
-  }
-
-  return llvm::FunctionType::get(ret_type, args_type, false);
-}
-
-
-llvm::BasicBlock* GBCExpanderPass::
-CreateBasicBlockWithDexPC(uint32_t dex_pc, const char* postfix) {
-  std::string name;
-
-#if !defined(NDEBUG)
-  art::StringAppendF(&name, "B%04x.%s", dex_pc, postfix);
-#endif
-
-  return llvm::BasicBlock::Create(context_, name, func_);
-}
-
-llvm::BasicBlock* GBCExpanderPass::GetBasicBlock(uint32_t dex_pc) {
-  DCHECK(dex_pc < dex_compilation_unit_->GetCodeItem()->insns_size_in_code_units_);
-  CHECK(basic_blocks_[dex_pc] != NULL);
-  return basic_blocks_[dex_pc];
-}
-
-int32_t GBCExpanderPass::GetTryItemOffset(uint32_t dex_pc) {
-  int32_t min = 0;
-  int32_t max = dex_compilation_unit_->GetCodeItem()->tries_size_ - 1;
-
-  while (min <= max) {
-    int32_t mid = min + (max - min) / 2;
-
-    const art::DexFile::TryItem* ti =
-        art::DexFile::GetTryItems(*dex_compilation_unit_->GetCodeItem(), mid);
-    uint32_t start = ti->start_addr_;
-    uint32_t end = start + ti->insn_count_;
-
-    if (dex_pc < start) {
-      max = mid - 1;
-    } else if (dex_pc >= end) {
-      min = mid + 1;
-    } else {
-      return mid;  // found
-    }
-  }
-
-  return -1;  // not found
-}
-
-llvm::BasicBlock* GBCExpanderPass::GetLandingPadBasicBlock(uint32_t dex_pc) {
-  // Find the try item for this address in this method
-  int32_t ti_offset = GetTryItemOffset(dex_pc);
-
-  if (ti_offset == -1) {
-    return NULL;  // No landing pad is available for this address.
-  }
-
-  // Check for the existing landing pad basic block
-  DCHECK_GT(basic_block_landing_pads_.size(), static_cast<size_t>(ti_offset));
-  llvm::BasicBlock* block_lpad = basic_block_landing_pads_[ti_offset];
-
-  if (block_lpad) {
-    // We have generated landing pad for this try item already.  Return the
-    // same basic block.
-    return block_lpad;
-  }
-
-  // Get try item from code item
-  const art::DexFile::TryItem* ti = art::DexFile::GetTryItems(*dex_compilation_unit_->GetCodeItem(),
-                                                              ti_offset);
-
-  std::string lpadname;
-
-#if !defined(NDEBUG)
-  art::StringAppendF(&lpadname, "lpad%d_%04x_to_%04x", ti_offset, ti->start_addr_, ti->handler_off_);
-#endif
-
-  // Create landing pad basic block
-  block_lpad = llvm::BasicBlock::Create(context_, lpadname, func_);
-
-  // Change IRBuilder insert point
-  llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP();
-  irb_.SetInsertPoint(block_lpad);
-
-  // Find catch block with matching type
-  llvm::Value* method_object_addr = EmitLoadMethodObjectAddr();
-
-  llvm::Value* ti_offset_value = irb_.getInt32(ti_offset);
-
-  llvm::Value* catch_handler_index_value =
-    irb_.CreateCall2(irb_.GetRuntime(FindCatchBlock),
-                     method_object_addr, ti_offset_value);
-
-  // Switch instruction (Go to unwind basic block by default)
-  llvm::SwitchInst* sw =
-    irb_.CreateSwitch(catch_handler_index_value, GetUnwindBasicBlock());
-
-  // Cases with matched catch block
-  art::CatchHandlerIterator iter(*dex_compilation_unit_->GetCodeItem(), ti->start_addr_);
-
-  for (uint32_t c = 0; iter.HasNext(); iter.Next(), ++c) {
-    sw->addCase(irb_.getInt32(c), GetBasicBlock(iter.GetHandlerAddress()));
-  }
-
-  // Restore the orignal insert point for IRBuilder
-  irb_.restoreIP(irb_ip_original);
-
-  // Cache this landing pad
-  DCHECK_GT(basic_block_landing_pads_.size(), static_cast<size_t>(ti_offset));
-  basic_block_landing_pads_[ti_offset] = block_lpad;
-
-  return block_lpad;
-}
-
-llvm::BasicBlock* GBCExpanderPass::GetUnwindBasicBlock() {
-  // Check the existing unwinding baisc block block
-  if (basic_block_unwind_ != NULL) {
-    return basic_block_unwind_;
-  }
-
-  // Create new basic block for unwinding
-  basic_block_unwind_ =
-    llvm::BasicBlock::Create(context_, "exception_unwind", func_);
-
-  // Change IRBuilder insert point
-  llvm::IRBuilderBase::InsertPoint irb_ip_original = irb_.saveIP();
-  irb_.SetInsertPoint(basic_block_unwind_);
-
-  // Pop the shadow frame
-  Expand_PopShadowFrame();
-
-  // Emit the code to return default value (zero) for the given return type.
-  char ret_shorty = dex_compilation_unit_->GetShorty()[0];
-  ret_shorty = art::RemapShorty(ret_shorty);
-  if (ret_shorty == 'V') {
-    irb_.CreateRetVoid();
-  } else {
-    irb_.CreateRet(irb_.getJZero(ret_shorty));
-  }
-
-  // Restore the orignal insert point for IRBuilder
-  irb_.restoreIP(irb_ip_original);
-
-  return basic_block_unwind_;
-}
-
-void GBCExpanderPass::EmitBranchExceptionLandingPad(uint32_t dex_pc) {
-  if (llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc)) {
-    landing_pad_phi_mapping_[lpad].push_back(std::make_pair(current_bb_->getUniquePredecessor(),
-                                                            irb_.GetInsertBlock()));
-    irb_.CreateBr(lpad);
-  } else {
-    irb_.CreateBr(GetUnwindBasicBlock());
-  }
-}
-
-void GBCExpanderPass::EmitGuard_ExceptionLandingPad(uint32_t dex_pc) {
-  llvm::Value* exception_pending = irb_.Runtime().EmitIsExceptionPending();
-
-  llvm::BasicBlock* block_cont = CreateBasicBlockWithDexPC(dex_pc, "cont");
-
-  if (llvm::BasicBlock* lpad = GetLandingPadBasicBlock(dex_pc)) {
-    landing_pad_phi_mapping_[lpad].push_back(std::make_pair(current_bb_->getUniquePredecessor(),
-                                                            irb_.GetInsertBlock()));
-    irb_.CreateCondBr(exception_pending, lpad, block_cont, kUnlikely);
-  } else {
-    irb_.CreateCondBr(exception_pending, GetUnwindBasicBlock(), block_cont, kUnlikely);
-  }
-
-  irb_.SetInsertPoint(block_cont);
-}
-
-llvm::Value*
-GBCExpanderPass::ExpandIntrinsic(IntrinsicHelper::IntrinsicId intr_id,
-                                 llvm::CallInst& call_inst) {
-  switch (intr_id) {
-    //==- Thread -----------------------------------------------------------==//
-    case IntrinsicHelper::GetCurrentThread: {
-      return irb_.Runtime().EmitGetCurrentThread();
-    }
-    case IntrinsicHelper::CheckSuspend: {
-      Expand_TestSuspend(call_inst);
-      return NULL;
-    }
-    case IntrinsicHelper::TestSuspend: {
-      Expand_TestSuspend(call_inst);
-      return NULL;
-    }
-    case IntrinsicHelper::MarkGCCard: {
-      Expand_MarkGCCard(call_inst);
-      return NULL;
-    }
-
-    //==- Exception --------------------------------------------------------==//
-    case IntrinsicHelper::ThrowException: {
-      return ExpandToRuntime(ThrowException, call_inst);
-    }
-    case IntrinsicHelper::HLThrowException: {
-      uint32_t dex_pc = LV2UInt(call_inst.getMetadata("DexOff")->getOperand(0));
-
-      EmitUpdateDexPC(dex_pc);
-
-      irb_.CreateCall(irb_.GetRuntime(ThrowException),
-                      call_inst.getArgOperand(0));
-
-      EmitGuard_ExceptionLandingPad(dex_pc);
-      return NULL;
-    }
-    case IntrinsicHelper::GetException: {
-      return irb_.Runtime().EmitGetAndClearException();
-    }
-    case IntrinsicHelper::IsExceptionPending: {
-      return irb_.Runtime().EmitIsExceptionPending();
-    }
-    case IntrinsicHelper::FindCatchBlock: {
-      return ExpandToRuntime(FindCatchBlock, call_inst);
-    }
-    case IntrinsicHelper::ThrowDivZeroException: {
-      return ExpandToRuntime(ThrowDivZeroException, call_inst);
-    }
-    case IntrinsicHelper::ThrowNullPointerException: {
-      return ExpandToRuntime(ThrowNullPointerException, call_inst);
-    }
-    case IntrinsicHelper::ThrowIndexOutOfBounds: {
-      return ExpandToRuntime(ThrowIndexOutOfBounds, call_inst);
-    }
-
-    //==- Const String -----------------------------------------------------==//
-    case IntrinsicHelper::ConstString: {
-      return Expand_ConstString(call_inst);
-    }
-    case IntrinsicHelper::LoadStringFromDexCache: {
-      return Expand_LoadStringFromDexCache(call_inst.getArgOperand(0));
-    }
-    case IntrinsicHelper::ResolveString: {
-      return ExpandToRuntime(ResolveString, call_inst);
-    }
-
-    //==- Const Class ------------------------------------------------------==//
-    case IntrinsicHelper::ConstClass: {
-      return Expand_ConstClass(call_inst);
-    }
-    case IntrinsicHelper::InitializeTypeAndVerifyAccess: {
-      return ExpandToRuntime(InitializeTypeAndVerifyAccess, call_inst);
-    }
-    case IntrinsicHelper::LoadTypeFromDexCache: {
-      return Expand_LoadTypeFromDexCache(call_inst.getArgOperand(0));
-    }
-    case IntrinsicHelper::InitializeType: {
-      return ExpandToRuntime(InitializeType, call_inst);
-    }
-
-    //==- Lock -------------------------------------------------------------==//
-    case IntrinsicHelper::LockObject: {
-      Expand_LockObject(call_inst.getArgOperand(0));
-      return NULL;
-    }
-    case IntrinsicHelper::UnlockObject: {
-      Expand_UnlockObject(call_inst.getArgOperand(0));
-      return NULL;
-    }
-
-    //==- Cast -------------------------------------------------------------==//
-    case IntrinsicHelper::CheckCast: {
-      return ExpandToRuntime(CheckCast, call_inst);
-    }
-    case IntrinsicHelper::HLCheckCast: {
-      Expand_HLCheckCast(call_inst);
-      return NULL;
-    }
-    case IntrinsicHelper::IsAssignable: {
-      return ExpandToRuntime(IsAssignable, call_inst);
-    }
-
-    //==- Alloc ------------------------------------------------------------==//
-    case IntrinsicHelper::AllocObject: {
-      return ExpandToRuntime(AllocObject, call_inst);
-    }
-    case IntrinsicHelper::AllocObjectWithAccessCheck: {
-      return ExpandToRuntime(AllocObjectWithAccessCheck, call_inst);
-    }
-
-    //==- Instance ---------------------------------------------------------==//
-    case IntrinsicHelper::NewInstance: {
-      return Expand_NewInstance(call_inst);
-    }
-    case IntrinsicHelper::InstanceOf: {
-      return Expand_InstanceOf(call_inst);
-    }
-
-    //==- Array ------------------------------------------------------------==//
-    case IntrinsicHelper::NewArray: {
-      return Expand_NewArray(call_inst);
-    }
-    case IntrinsicHelper::OptArrayLength: {
-      return Expand_OptArrayLength(call_inst);
-    }
-    case IntrinsicHelper::ArrayLength: {
-      return EmitLoadArrayLength(call_inst.getArgOperand(0));
-    }
-    case IntrinsicHelper::AllocArray: {
-      return ExpandToRuntime(AllocArray, call_inst);
-    }
-    case IntrinsicHelper::AllocArrayWithAccessCheck: {
-      return ExpandToRuntime(AllocArrayWithAccessCheck,
-                             call_inst);
-    }
-    case IntrinsicHelper::CheckAndAllocArray: {
-      return ExpandToRuntime(CheckAndAllocArray, call_inst);
-    }
-    case IntrinsicHelper::CheckAndAllocArrayWithAccessCheck: {
-      return ExpandToRuntime(CheckAndAllocArrayWithAccessCheck,
-                             call_inst);
-    }
-    case IntrinsicHelper::ArrayGet: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kInt);
-    }
-    case IntrinsicHelper::ArrayGetWide: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kLong);
-    }
-    case IntrinsicHelper::ArrayGetObject: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kObject);
-    }
-    case IntrinsicHelper::ArrayGetBoolean: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kBoolean);
-    }
-    case IntrinsicHelper::ArrayGetByte: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kByte);
-    }
-    case IntrinsicHelper::ArrayGetChar: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kChar);
-    }
-    case IntrinsicHelper::ArrayGetShort: {
-      return Expand_ArrayGet(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             kShort);
-    }
-    case IntrinsicHelper::ArrayPut: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kInt);
-      return NULL;
-    }
-    case IntrinsicHelper::ArrayPutWide: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kLong);
-      return NULL;
-    }
-    case IntrinsicHelper::ArrayPutObject: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kObject);
-      return NULL;
-    }
-    case IntrinsicHelper::ArrayPutBoolean: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kBoolean);
-      return NULL;
-    }
-    case IntrinsicHelper::ArrayPutByte: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kByte);
-      return NULL;
-    }
-    case IntrinsicHelper::ArrayPutChar: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kChar);
-      return NULL;
-    }
-    case IntrinsicHelper::ArrayPutShort: {
-      Expand_ArrayPut(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      kShort);
-      return NULL;
-    }
-    case IntrinsicHelper::CheckPutArrayElement: {
-      return ExpandToRuntime(CheckPutArrayElement, call_inst);
-    }
-    case IntrinsicHelper::FilledNewArray: {
-      Expand_FilledNewArray(call_inst);
-      return NULL;
-    }
-    case IntrinsicHelper::FillArrayData: {
-      return ExpandToRuntime(FillArrayData, call_inst);
-    }
-    case IntrinsicHelper::HLFillArrayData: {
-      Expand_HLFillArrayData(call_inst);
-      return NULL;
-    }
-    case IntrinsicHelper::HLFilledNewArray: {
-      return Expand_HLFilledNewArray(call_inst);
-    }
-
-    //==- Instance Field ---------------------------------------------------==//
-    case IntrinsicHelper::InstanceFieldGet:
-    case IntrinsicHelper::InstanceFieldGetBoolean:
-    case IntrinsicHelper::InstanceFieldGetByte:
-    case IntrinsicHelper::InstanceFieldGetChar:
-    case IntrinsicHelper::InstanceFieldGetShort: {
-      return ExpandToRuntime(Get32Instance, call_inst);
-    }
-    case IntrinsicHelper::InstanceFieldGetWide: {
-      return ExpandToRuntime(Get64Instance, call_inst);
-    }
-    case IntrinsicHelper::InstanceFieldGetObject: {
-      return ExpandToRuntime(GetObjectInstance, call_inst);
-    }
-    case IntrinsicHelper::InstanceFieldGetFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kInt);
-    }
-    case IntrinsicHelper::InstanceFieldGetWideFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kLong);
-    }
-    case IntrinsicHelper::InstanceFieldGetObjectFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kObject);
-    }
-    case IntrinsicHelper::InstanceFieldGetBooleanFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kBoolean);
-    }
-    case IntrinsicHelper::InstanceFieldGetByteFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kByte);
-    }
-    case IntrinsicHelper::InstanceFieldGetCharFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kChar);
-    }
-    case IntrinsicHelper::InstanceFieldGetShortFast: {
-      return Expand_IGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kShort);
-    }
-    case IntrinsicHelper::InstanceFieldPut:
-    case IntrinsicHelper::InstanceFieldPutBoolean:
-    case IntrinsicHelper::InstanceFieldPutByte:
-    case IntrinsicHelper::InstanceFieldPutChar:
-    case IntrinsicHelper::InstanceFieldPutShort: {
-      return ExpandToRuntime(Set32Instance, call_inst);
-    }
-    case IntrinsicHelper::InstanceFieldPutWide: {
-      return ExpandToRuntime(Set64Instance, call_inst);
-    }
-    case IntrinsicHelper::InstanceFieldPutObject: {
-      return ExpandToRuntime(SetObjectInstance, call_inst);
-    }
-    case IntrinsicHelper::InstanceFieldPutFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kInt);
-      return NULL;
-    }
-    case IntrinsicHelper::InstanceFieldPutWideFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kLong);
-      return NULL;
-    }
-    case IntrinsicHelper::InstanceFieldPutObjectFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kObject);
-      return NULL;
-    }
-    case IntrinsicHelper::InstanceFieldPutBooleanFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kBoolean);
-      return NULL;
-    }
-    case IntrinsicHelper::InstanceFieldPutByteFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kByte);
-      return NULL;
-    }
-    case IntrinsicHelper::InstanceFieldPutCharFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kChar);
-      return NULL;
-    }
-    case IntrinsicHelper::InstanceFieldPutShortFast: {
-      Expand_IPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kShort);
-      return NULL;
-    }
-
-    //==- Static Field -----------------------------------------------------==//
-    case IntrinsicHelper::StaticFieldGet:
-    case IntrinsicHelper::StaticFieldGetBoolean:
-    case IntrinsicHelper::StaticFieldGetByte:
-    case IntrinsicHelper::StaticFieldGetChar:
-    case IntrinsicHelper::StaticFieldGetShort: {
-      return ExpandToRuntime(Get32Static, call_inst);
-    }
-    case IntrinsicHelper::StaticFieldGetWide: {
-      return ExpandToRuntime(Get64Static, call_inst);
-    }
-    case IntrinsicHelper::StaticFieldGetObject: {
-      return ExpandToRuntime(GetObjectStatic, call_inst);
-    }
-    case IntrinsicHelper::StaticFieldGetFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kInt);
-    }
-    case IntrinsicHelper::StaticFieldGetWideFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kLong);
-    }
-    case IntrinsicHelper::StaticFieldGetObjectFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kObject);
-    }
-    case IntrinsicHelper::StaticFieldGetBooleanFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kBoolean);
-    }
-    case IntrinsicHelper::StaticFieldGetByteFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kByte);
-    }
-    case IntrinsicHelper::StaticFieldGetCharFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kChar);
-    }
-    case IntrinsicHelper::StaticFieldGetShortFast: {
-      return Expand_SGetFast(call_inst.getArgOperand(0),
-                             call_inst.getArgOperand(1),
-                             call_inst.getArgOperand(2),
-                             kShort);
-    }
-    case IntrinsicHelper::StaticFieldPut:
-    case IntrinsicHelper::StaticFieldPutBoolean:
-    case IntrinsicHelper::StaticFieldPutByte:
-    case IntrinsicHelper::StaticFieldPutChar:
-    case IntrinsicHelper::StaticFieldPutShort: {
-      return ExpandToRuntime(Set32Static, call_inst);
-    }
-    case IntrinsicHelper::StaticFieldPutWide: {
-      return ExpandToRuntime(Set64Static, call_inst);
-    }
-    case IntrinsicHelper::StaticFieldPutObject: {
-      return ExpandToRuntime(SetObjectStatic, call_inst);
-    }
-    case IntrinsicHelper::StaticFieldPutFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kInt);
-      return NULL;
-    }
-    case IntrinsicHelper::StaticFieldPutWideFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kLong);
-      return NULL;
-    }
-    case IntrinsicHelper::StaticFieldPutObjectFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kObject);
-      return NULL;
-    }
-    case IntrinsicHelper::StaticFieldPutBooleanFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kBoolean);
-      return NULL;
-    }
-    case IntrinsicHelper::StaticFieldPutByteFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kByte);
-      return NULL;
-    }
-    case IntrinsicHelper::StaticFieldPutCharFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kChar);
-      return NULL;
-    }
-    case IntrinsicHelper::StaticFieldPutShortFast: {
-      Expand_SPutFast(call_inst.getArgOperand(0),
-                      call_inst.getArgOperand(1),
-                      call_inst.getArgOperand(2),
-                      call_inst.getArgOperand(3),
-                      kShort);
-      return NULL;
-    }
-    case IntrinsicHelper::LoadDeclaringClassSSB: {
-      return Expand_LoadDeclaringClassSSB(call_inst.getArgOperand(0));
-    }
-    case IntrinsicHelper::InitializeAndLoadClassSSB: {
-      return ExpandToRuntime(InitializeStaticStorage, call_inst);
-    }
-
-    //==- High-level Array -------------------------------------------------==//
-    case IntrinsicHelper::HLArrayGet: {
-      return Expand_HLArrayGet(call_inst, kInt);
-    }
-    case IntrinsicHelper::HLArrayGetBoolean: {
-      return Expand_HLArrayGet(call_inst, kBoolean);
-    }
-    case IntrinsicHelper::HLArrayGetByte: {
-      return Expand_HLArrayGet(call_inst, kByte);
-    }
-    case IntrinsicHelper::HLArrayGetChar: {
-      return Expand_HLArrayGet(call_inst, kChar);
-    }
-    case IntrinsicHelper::HLArrayGetShort: {
-      return Expand_HLArrayGet(call_inst, kShort);
-    }
-    case IntrinsicHelper::HLArrayGetFloat: {
-      return Expand_HLArrayGet(call_inst, kFloat);
-    }
-    case IntrinsicHelper::HLArrayGetWide: {
-      return Expand_HLArrayGet(call_inst, kLong);
-    }
-    case IntrinsicHelper::HLArrayGetDouble: {
-      return Expand_HLArrayGet(call_inst, kDouble);
-    }
-    case IntrinsicHelper::HLArrayGetObject: {
-      return Expand_HLArrayGet(call_inst, kObject);
-    }
-    case IntrinsicHelper::HLArrayPut: {
-      Expand_HLArrayPut(call_inst, kInt);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutBoolean: {
-      Expand_HLArrayPut(call_inst, kBoolean);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutByte: {
-      Expand_HLArrayPut(call_inst, kByte);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutChar: {
-      Expand_HLArrayPut(call_inst, kChar);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutShort: {
-      Expand_HLArrayPut(call_inst, kShort);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutFloat: {
-      Expand_HLArrayPut(call_inst, kFloat);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutWide: {
-      Expand_HLArrayPut(call_inst, kLong);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutDouble: {
-      Expand_HLArrayPut(call_inst, kDouble);
-      return NULL;
-    }
-    case IntrinsicHelper::HLArrayPutObject: {
-      Expand_HLArrayPut(call_inst, kObject);
-      return NULL;
-    }
-
-    //==- High-level Instance ----------------------------------------------==//
-    case IntrinsicHelper::HLIGet: {
-      return Expand_HLIGet(call_inst, kInt);
-    }
-    case IntrinsicHelper::HLIGetBoolean: {
-      return Expand_HLIGet(call_inst, kBoolean);
-    }
-    case IntrinsicHelper::HLIGetByte: {
-      return Expand_HLIGet(call_inst, kByte);
-    }
-    case IntrinsicHelper::HLIGetChar: {
-      return Expand_HLIGet(call_inst, kChar);
-    }
-    case IntrinsicHelper::HLIGetShort: {
-      return Expand_HLIGet(call_inst, kShort);
-    }
-    case IntrinsicHelper::HLIGetFloat: {
-      return Expand_HLIGet(call_inst, kFloat);
-    }
-    case IntrinsicHelper::HLIGetWide: {
-      return Expand_HLIGet(call_inst, kLong);
-    }
-    case IntrinsicHelper::HLIGetDouble: {
-      return Expand_HLIGet(call_inst, kDouble);
-    }
-    case IntrinsicHelper::HLIGetObject: {
-      return Expand_HLIGet(call_inst, kObject);
-    }
-    case IntrinsicHelper::HLIPut: {
-      Expand_HLIPut(call_inst, kInt);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutBoolean: {
-      Expand_HLIPut(call_inst, kBoolean);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutByte: {
-      Expand_HLIPut(call_inst, kByte);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutChar: {
-      Expand_HLIPut(call_inst, kChar);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutShort: {
-      Expand_HLIPut(call_inst, kShort);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutFloat: {
-      Expand_HLIPut(call_inst, kFloat);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutWide: {
-      Expand_HLIPut(call_inst, kLong);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutDouble: {
-      Expand_HLIPut(call_inst, kDouble);
-      return NULL;
-    }
-    case IntrinsicHelper::HLIPutObject: {
-      Expand_HLIPut(call_inst, kObject);
-      return NULL;
-    }
-
-    //==- High-level Invoke ------------------------------------------------==//
-    case IntrinsicHelper::HLInvokeVoid:
-    case IntrinsicHelper::HLInvokeObj:
-    case IntrinsicHelper::HLInvokeInt:
-    case IntrinsicHelper::HLInvokeFloat:
-    case IntrinsicHelper::HLInvokeLong:
-    case IntrinsicHelper::HLInvokeDouble: {
-      return Expand_HLInvoke(call_inst);
-    }
-
-    //==- Invoke -----------------------------------------------------------==//
-    case IntrinsicHelper::FindStaticMethodWithAccessCheck: {
-      return ExpandToRuntime(FindStaticMethodWithAccessCheck, call_inst);
-    }
-    case IntrinsicHelper::FindDirectMethodWithAccessCheck: {
-      return ExpandToRuntime(FindDirectMethodWithAccessCheck, call_inst);
-    }
-    case IntrinsicHelper::FindVirtualMethodWithAccessCheck: {
-      return ExpandToRuntime(FindVirtualMethodWithAccessCheck, call_inst);
-    }
-    case IntrinsicHelper::FindSuperMethodWithAccessCheck: {
-      return ExpandToRuntime(FindSuperMethodWithAccessCheck, call_inst);
-    }
-    case IntrinsicHelper::FindInterfaceMethodWithAccessCheck: {
-      return ExpandToRuntime(FindInterfaceMethodWithAccessCheck, call_inst);
-    }
-    case IntrinsicHelper::GetSDCalleeMethodObjAddrFast: {
-      return Expand_GetSDCalleeMethodObjAddrFast(call_inst.getArgOperand(0));
-    }
-    case IntrinsicHelper::GetVirtualCalleeMethodObjAddrFast: {
-      return Expand_GetVirtualCalleeMethodObjAddrFast(
-                call_inst.getArgOperand(0), call_inst.getArgOperand(1));
-    }
-    case IntrinsicHelper::GetInterfaceCalleeMethodObjAddrFast: {
-      return ExpandToRuntime(FindInterfaceMethod, call_inst);
-    }
-    case IntrinsicHelper::InvokeRetVoid:
-    case IntrinsicHelper::InvokeRetBoolean:
-    case IntrinsicHelper::InvokeRetByte:
-    case IntrinsicHelper::InvokeRetChar:
-    case IntrinsicHelper::InvokeRetShort:
-    case IntrinsicHelper::InvokeRetInt:
-    case IntrinsicHelper::InvokeRetLong:
-    case IntrinsicHelper::InvokeRetFloat:
-    case IntrinsicHelper::InvokeRetDouble:
-    case IntrinsicHelper::InvokeRetObject: {
-      return Expand_Invoke(call_inst);
-    }
-
-    //==- Math -------------------------------------------------------------==//
-    case IntrinsicHelper::DivInt: {
-      return Expand_DivRem(call_inst, /* is_div */true, kInt);
-    }
-    case IntrinsicHelper::RemInt: {
-      return Expand_DivRem(call_inst, /* is_div */false, kInt);
-    }
-    case IntrinsicHelper::DivLong: {
-      return Expand_DivRem(call_inst, /* is_div */true, kLong);
-    }
-    case IntrinsicHelper::RemLong: {
-      return Expand_DivRem(call_inst, /* is_div */false, kLong);
-    }
-    case IntrinsicHelper::D2L: {
-      return ExpandToRuntime(art_d2l, call_inst);
-    }
-    case IntrinsicHelper::D2I: {
-      return ExpandToRuntime(art_d2i, call_inst);
-    }
-    case IntrinsicHelper::F2L: {
-      return ExpandToRuntime(art_f2l, call_inst);
-    }
-    case IntrinsicHelper::F2I: {
-      return ExpandToRuntime(art_f2i, call_inst);
-    }
-
-    //==- High-level Static ------------------------------------------------==//
-    case IntrinsicHelper::HLSget: {
-      return Expand_HLSget(call_inst, kInt);
-    }
-    case IntrinsicHelper::HLSgetBoolean: {
-      return Expand_HLSget(call_inst, kBoolean);
-    }
-    case IntrinsicHelper::HLSgetByte: {
-      return Expand_HLSget(call_inst, kByte);
-    }
-    case IntrinsicHelper::HLSgetChar: {
-      return Expand_HLSget(call_inst, kChar);
-    }
-    case IntrinsicHelper::HLSgetShort: {
-      return Expand_HLSget(call_inst, kShort);
-    }
-    case IntrinsicHelper::HLSgetFloat: {
-      return Expand_HLSget(call_inst, kFloat);
-    }
-    case IntrinsicHelper::HLSgetWide: {
-      return Expand_HLSget(call_inst, kLong);
-    }
-    case IntrinsicHelper::HLSgetDouble: {
-      return Expand_HLSget(call_inst, kDouble);
-    }
-    case IntrinsicHelper::HLSgetObject: {
-      return Expand_HLSget(call_inst, kObject);
-    }
-    case IntrinsicHelper::HLSput: {
-      Expand_HLSput(call_inst, kInt);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputBoolean: {
-      Expand_HLSput(call_inst, kBoolean);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputByte: {
-      Expand_HLSput(call_inst, kByte);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputChar: {
-      Expand_HLSput(call_inst, kChar);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputShort: {
-      Expand_HLSput(call_inst, kShort);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputFloat: {
-      Expand_HLSput(call_inst, kFloat);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputWide: {
-      Expand_HLSput(call_inst, kLong);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputDouble: {
-      Expand_HLSput(call_inst, kDouble);
-      return NULL;
-    }
-    case IntrinsicHelper::HLSputObject: {
-      Expand_HLSput(call_inst, kObject);
-      return NULL;
-    }
-
-    //==- High-level Monitor -----------------------------------------------==//
-    case IntrinsicHelper::MonitorEnter: {
-      Expand_MonitorEnter(call_inst);
-      return NULL;
-    }
-    case IntrinsicHelper::MonitorExit: {
-      Expand_MonitorExit(call_inst);
-      return NULL;
-    }
-
-    //==- Shadow Frame -----------------------------------------------------==//
-    case IntrinsicHelper::AllocaShadowFrame: {
-      Expand_AllocaShadowFrame(call_inst.getArgOperand(0));
-      return NULL;
-    }
-    case IntrinsicHelper::SetVReg: {
-      Expand_SetVReg(call_inst.getArgOperand(0),
-                     call_inst.getArgOperand(1));
-      return NULL;
-    }
-    case IntrinsicHelper::PopShadowFrame: {
-      Expand_PopShadowFrame();
-      return NULL;
-    }
-    case IntrinsicHelper::UpdateDexPC: {
-      Expand_UpdateDexPC(call_inst.getArgOperand(0));
-      return NULL;
-    }
-
-    //==- Comparison -------------------------------------------------------==//
-    case IntrinsicHelper::CmplFloat:
-    case IntrinsicHelper::CmplDouble: {
-      return Expand_FPCompare(call_inst.getArgOperand(0),
-                              call_inst.getArgOperand(1),
-                              false);
-    }
-    case IntrinsicHelper::CmpgFloat:
-    case IntrinsicHelper::CmpgDouble: {
-      return Expand_FPCompare(call_inst.getArgOperand(0),
-                              call_inst.getArgOperand(1),
-                              true);
-    }
-    case IntrinsicHelper::CmpLong: {
-      return Expand_LongCompare(call_inst.getArgOperand(0),
-                                call_inst.getArgOperand(1));
-    }
-
-    //==- Const ------------------------------------------------------------==//
-    case IntrinsicHelper::ConstInt:
-    case IntrinsicHelper::ConstLong: {
-      return call_inst.getArgOperand(0);
-    }
-    case IntrinsicHelper::ConstFloat: {
-      return irb_.CreateBitCast(call_inst.getArgOperand(0),
-                                irb_.getJFloatTy());
-    }
-    case IntrinsicHelper::ConstDouble: {
-      return irb_.CreateBitCast(call_inst.getArgOperand(0),
-                                irb_.getJDoubleTy());
-    }
-    case IntrinsicHelper::ConstObj: {
-      CHECK_EQ(LV2UInt(call_inst.getArgOperand(0)), 0U);
-      return irb_.getJNull();
-    }
-
-    //==- Method Info ------------------------------------------------------==//
-    case IntrinsicHelper::MethodInfo: {
-      // Nothing to be done, because MethodInfo carries optional hints that are
-      // not needed by the portable path.
-      return NULL;
-    }
-
-    //==- Copy -------------------------------------------------------------==//
-    case IntrinsicHelper::CopyInt:
-    case IntrinsicHelper::CopyFloat:
-    case IntrinsicHelper::CopyLong:
-    case IntrinsicHelper::CopyDouble:
-    case IntrinsicHelper::CopyObj: {
-      return call_inst.getArgOperand(0);
-    }
-
-    //==- Shift ------------------------------------------------------------==//
-    case IntrinsicHelper::SHLLong: {
-      return Expand_IntegerShift(call_inst.getArgOperand(0),
-                                 call_inst.getArgOperand(1),
-                                 kIntegerSHL, kLong);
-    }
-    case IntrinsicHelper::SHRLong: {
-      return Expand_IntegerShift(call_inst.getArgOperand(0),
-                                 call_inst.getArgOperand(1),
-                                 kIntegerSHR, kLong);
-    }
-    case IntrinsicHelper::USHRLong: {
-      return Expand_IntegerShift(call_inst.getArgOperand(0),
-                                 call_inst.getArgOperand(1),
-                                 kIntegerUSHR, kLong);
-    }
-    case IntrinsicHelper::SHLInt: {
-      return Expand_IntegerShift(call_inst.getArgOperand(0),
-                                 call_inst.getArgOperand(1),
-                                 kIntegerSHL, kInt);
-    }
-    case IntrinsicHelper::SHRInt: {
-      return Expand_IntegerShift(call_inst.getArgOperand(0),
-                                 call_inst.getArgOperand(1),
-                                 kIntegerSHR, kInt);
-    }
-    case IntrinsicHelper::USHRInt: {
-      return Expand_IntegerShift(call_inst.getArgOperand(0),
-                                 call_inst.getArgOperand(1),
-                                 kIntegerUSHR, kInt);
-    }
-
-    //==- Conversion -------------------------------------------------------==//
-    case IntrinsicHelper::IntToChar: {
-      return irb_.CreateZExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJCharTy()),
-                             irb_.getJIntTy());
-    }
-    case IntrinsicHelper::IntToShort: {
-      return irb_.CreateSExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJShortTy()),
-                             irb_.getJIntTy());
-    }
-    case IntrinsicHelper::IntToByte: {
-      return irb_.CreateSExt(irb_.CreateTrunc(call_inst.getArgOperand(0), irb_.getJByteTy()),
-                             irb_.getJIntTy());
-    }
-
-    //==- Exception --------------------------------------------------------==//
-    case IntrinsicHelper::CatchTargets: {
-      UpdatePhiInstruction(current_bb_, irb_.GetInsertBlock());
-      llvm::SwitchInst* si = llvm::dyn_cast<llvm::SwitchInst>(call_inst.getNextNode());
-      CHECK(si != NULL);
-      irb_.CreateBr(si->getDefaultDest());
-      si->eraseFromParent();
-      return call_inst.getArgOperand(0);
-    }
-
-    //==- Constructor barrier-----------------------------------------------==//
-    case IntrinsicHelper::ConstructorBarrier: {
-      irb_.CreateMemoryBarrier(art::kStoreStore);
-      return NULL;
-    }
-
-    //==- Unknown Cases ----------------------------------------------------==//
-    case IntrinsicHelper::MaxIntrinsicId:
-    case IntrinsicHelper::UnknownId:
-    // default:
-      // NOTE: "default" is intentionally commented so that C/C++ compiler will
-      // give some warning on unmatched cases.
-      // NOTE: We should not implement these cases.
-      break;
-  }
-  UNIMPLEMENTED(FATAL) << "Unexpected GBC intrinsic: " << static_cast<int>(intr_id);
-  return NULL;
-}  // NOLINT(readability/fn_size)
-
-}  // anonymous namespace
-
-namespace art {
-namespace llvm {
-
-::llvm::FunctionPass*
-CreateGBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
-                      CompilerDriver* driver, const DexCompilationUnit* dex_compilation_unit) {
-  return new GBCExpanderPass(intrinsic_helper, irb, driver, dex_compilation_unit);
-}
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/generated/art_module.cc b/compiler/llvm/generated/art_module.cc
deleted file mode 100644
index f3c5a5a..0000000
--- a/compiler/llvm/generated/art_module.cc
+++ /dev/null
@@ -1,1096 +0,0 @@
-// Generated with ./gen_art_module_cc.sh
-
-
-#pragma GCC diagnostic ignored "-Wframe-larger-than="
-// TODO: Remove this pragma after llc can generate makeLLVMModuleContents()
-// with smaller frame size.
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using namespace llvm;
-
-namespace art {
-namespace llvm {
-
-
-// Generated by llvm2cpp - DO NOT MODIFY!
-
-
-Module* makeLLVMModuleContents(Module *mod) {
-
-mod->setModuleIdentifier("art_module.ll");
-
-// Type Definitions
-std::vector<Type*>FuncTy_0_args;
-StructType *StructTy_JavaObject = mod->getTypeByName("JavaObject");
-if (!StructTy_JavaObject) {
-StructTy_JavaObject = StructType::create(mod->getContext(), "JavaObject");
-}
-std::vector<Type*>StructTy_JavaObject_fields;
-if (StructTy_JavaObject->isOpaque()) {
-StructTy_JavaObject->setBody(StructTy_JavaObject_fields, /*isPacked=*/false);
-}
-
-PointerType* PointerTy_1 = PointerType::get(StructTy_JavaObject, 0);
-
-FuncTy_0_args.push_back(PointerTy_1);
-StructType *StructTy_ShadowFrame = mod->getTypeByName("ShadowFrame");
-if (!StructTy_ShadowFrame) {
-StructTy_ShadowFrame = StructType::create(mod->getContext(), "ShadowFrame");
-}
-std::vector<Type*>StructTy_ShadowFrame_fields;
-StructTy_ShadowFrame_fields.push_back(IntegerType::get(mod->getContext(), 32));
-PointerType* PointerTy_2 = PointerType::get(StructTy_ShadowFrame, 0);
-
-StructTy_ShadowFrame_fields.push_back(PointerTy_2);
-StructTy_ShadowFrame_fields.push_back(PointerTy_1);
-StructTy_ShadowFrame_fields.push_back(IntegerType::get(mod->getContext(), 32));
-if (StructTy_ShadowFrame->isOpaque()) {
-StructTy_ShadowFrame->setBody(StructTy_ShadowFrame_fields, /*isPacked=*/false);
-}
-
-
-FuncTy_0_args.push_back(PointerTy_2);
-FunctionType* FuncTy_0 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_0_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_3_args;
-FunctionType* FuncTy_3 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_3_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_4_args;
-FuncTy_4_args.push_back(PointerTy_1);
-FunctionType* FuncTy_4 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_4_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_5_args;
-FuncTy_5_args.push_back(PointerTy_1);
-FuncTy_5_args.push_back(PointerTy_1);
-FunctionType* FuncTy_5 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_5_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_6_args;
-FuncTy_6_args.push_back(PointerTy_1);
-FunctionType* FuncTy_6 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_6_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_7_args;
-FuncTy_7_args.push_back(PointerTy_1);
-FuncTy_7_args.push_back(PointerTy_2);
-FuncTy_7_args.push_back(PointerTy_1);
-FuncTy_7_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_7 = FunctionType::get(
- /*Result=*/PointerTy_2,
- /*Params=*/FuncTy_7_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_8_args;
-FuncTy_8_args.push_back(PointerTy_2);
-FunctionType* FuncTy_8 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_8_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_9_args;
-FunctionType* FuncTy_9 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_9_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_10_args;
-FuncTy_10_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_10_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_10 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_10_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_11_args;
-FuncTy_11_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_11 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_11_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_12_args;
-FuncTy_12_args.push_back(PointerTy_1);
-FuncTy_12_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_12 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_12_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_13_args;
-FuncTy_13_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_13_args.push_back(PointerTy_1);
-FuncTy_13_args.push_back(PointerTy_1);
-FunctionType* FuncTy_13 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_13_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_14_args;
-FuncTy_14_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_14_args.push_back(PointerTy_1);
-FuncTy_14_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_14_args.push_back(PointerTy_1);
-FunctionType* FuncTy_14 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_14_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_15_args;
-FuncTy_15_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_15_args.push_back(PointerTy_1);
-FunctionType* FuncTy_15 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_15_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_16_args;
-FuncTy_16_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_16_args.push_back(PointerTy_1);
-FuncTy_16_args.push_back(PointerTy_1);
-FuncTy_16_args.push_back(PointerTy_1);
-FunctionType* FuncTy_16 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_16_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_17_args;
-FuncTy_17_args.push_back(PointerTy_1);
-FuncTy_17_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_17 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_17_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_18_args;
-FuncTy_18_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_18_args.push_back(PointerTy_1);
-FuncTy_18_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_18 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_18_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_19_args;
-FuncTy_19_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_19_args.push_back(PointerTy_1);
-FuncTy_19_args.push_back(IntegerType::get(mod->getContext(), 64));
-FunctionType* FuncTy_19 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_19_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_20_args;
-FuncTy_20_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_20_args.push_back(PointerTy_1);
-FuncTy_20_args.push_back(PointerTy_1);
-FunctionType* FuncTy_20 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_20_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_21_args;
-FuncTy_21_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_21_args.push_back(PointerTy_1);
-FunctionType* FuncTy_21 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_21_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_22_args;
-FuncTy_22_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_22_args.push_back(PointerTy_1);
-FunctionType* FuncTy_22 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_22_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_23_args;
-FuncTy_23_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_23_args.push_back(PointerTy_1);
-FunctionType* FuncTy_23 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_23_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_24_args;
-FuncTy_24_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_24_args.push_back(PointerTy_1);
-FuncTy_24_args.push_back(PointerTy_1);
-FuncTy_24_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_24 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_24_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_25_args;
-FuncTy_25_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_25_args.push_back(PointerTy_1);
-FuncTy_25_args.push_back(PointerTy_1);
-FuncTy_25_args.push_back(IntegerType::get(mod->getContext(), 64));
-FunctionType* FuncTy_25 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_25_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_26_args;
-FuncTy_26_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_26_args.push_back(PointerTy_1);
-FuncTy_26_args.push_back(PointerTy_1);
-FuncTy_26_args.push_back(PointerTy_1);
-FunctionType* FuncTy_26 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_26_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_27_args;
-FuncTy_27_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_27_args.push_back(PointerTy_1);
-FuncTy_27_args.push_back(PointerTy_1);
-FunctionType* FuncTy_27 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_27_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_28_args;
-FuncTy_28_args.push_back(PointerTy_1);
-FuncTy_28_args.push_back(PointerTy_1);
-FunctionType* FuncTy_28 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_28_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_29_args;
-FuncTy_29_args.push_back(PointerTy_1);
-FuncTy_29_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_29_args.push_back(PointerTy_1);
-FuncTy_29_args.push_back(IntegerType::get(mod->getContext(), 32));
-FunctionType* FuncTy_29 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_29_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_30_args;
-FuncTy_30_args.push_back(PointerTy_1);
-FuncTy_30_args.push_back(PointerTy_1);
-FunctionType* FuncTy_30 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_30_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_31_args;
-FuncTy_31_args.push_back(Type::getDoubleTy(mod->getContext()));
-FunctionType* FuncTy_31 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_31_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_32_args;
-FuncTy_32_args.push_back(Type::getDoubleTy(mod->getContext()));
-FunctionType* FuncTy_32 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_32_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_33_args;
-FuncTy_33_args.push_back(Type::getFloatTy(mod->getContext()));
-FunctionType* FuncTy_33 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 64),
- /*Params=*/FuncTy_33_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_34_args;
-FuncTy_34_args.push_back(Type::getFloatTy(mod->getContext()));
-FunctionType* FuncTy_34 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_34_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_35_args;
-FuncTy_35_args.push_back(PointerTy_1);
-FunctionType* FuncTy_35 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 32),
- /*Params=*/FuncTy_35_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_36_args;
-FuncTy_36_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_36_args.push_back(PointerTy_1);
-FuncTy_36_args.push_back(PointerTy_1);
-FunctionType* FuncTy_36 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_36_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_37_args;
-FuncTy_37_args.push_back(PointerTy_1);
-FuncTy_37_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_37_args.push_back(PointerTy_1);
-FunctionType* FuncTy_37 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_37_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_38_args;
-FuncTy_38_args.push_back(PointerTy_1);
-FuncTy_38_args.push_back(IntegerType::get(mod->getContext(), 32));
-FuncTy_38_args.push_back(PointerTy_1);
-FuncTy_38_args.push_back(PointerTy_1);
-FunctionType* FuncTy_38 = FunctionType::get(
- /*Result=*/PointerTy_1,
- /*Params=*/FuncTy_38_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_39_args;
-FunctionType* FuncTy_39 = FunctionType::get(
- /*Result=*/IntegerType::get(mod->getContext(), 1),
- /*Params=*/FuncTy_39_args,
- /*isVarArg=*/false);
-
-std::vector<Type*>FuncTy_40_args;
-FuncTy_40_args.push_back(PointerTy_1);
-FunctionType* FuncTy_40 = FunctionType::get(
- /*Result=*/Type::getVoidTy(mod->getContext()),
- /*Params=*/FuncTy_40_args,
- /*isVarArg=*/true);
-
-
-// Function Declarations
-
-Function* func___art_type_list = mod->getFunction("__art_type_list");
-if (!func___art_type_list) {
-func___art_type_list = Function::Create(
- /*Type=*/FuncTy_0,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"__art_type_list", mod);  // (external, no body)
-func___art_type_list->setCallingConv(CallingConv::C);
-}
-AttributeSet func___art_type_list_PAL;
-func___art_type_list->setAttributes(func___art_type_list_PAL);
-
-Function* func_art_portable_get_current_thread_from_code = mod->getFunction("art_portable_get_current_thread_from_code");
-if (!func_art_portable_get_current_thread_from_code) {
-func_art_portable_get_current_thread_from_code = Function::Create(
- /*Type=*/FuncTy_3,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_current_thread_from_code", mod);  // (external, no body)
-func_art_portable_get_current_thread_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_current_thread_from_code_PAL;
-func_art_portable_get_current_thread_from_code->setAttributes(func_art_portable_get_current_thread_from_code_PAL);
-
-Function* func_art_portable_set_current_thread_from_code = mod->getFunction("art_portable_set_current_thread_from_code");
-if (!func_art_portable_set_current_thread_from_code) {
-func_art_portable_set_current_thread_from_code = Function::Create(
- /*Type=*/FuncTy_4,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set_current_thread_from_code", mod);  // (external, no body)
-func_art_portable_set_current_thread_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set_current_thread_from_code_PAL;
-func_art_portable_set_current_thread_from_code->setAttributes(func_art_portable_set_current_thread_from_code_PAL);
-
-Function* func_art_portable_lock_object_from_code = mod->getFunction("art_portable_lock_object_from_code");
-if (!func_art_portable_lock_object_from_code) {
-func_art_portable_lock_object_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_lock_object_from_code", mod);  // (external, no body)
-func_art_portable_lock_object_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_lock_object_from_code_PAL;
-func_art_portable_lock_object_from_code->setAttributes(func_art_portable_lock_object_from_code_PAL);
-
-Function* func_art_portable_unlock_object_from_code = mod->getFunction("art_portable_unlock_object_from_code");
-if (!func_art_portable_unlock_object_from_code) {
-func_art_portable_unlock_object_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_unlock_object_from_code", mod);  // (external, no body)
-func_art_portable_unlock_object_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_unlock_object_from_code_PAL;
-func_art_portable_unlock_object_from_code->setAttributes(func_art_portable_unlock_object_from_code_PAL);
-
-Function* func_art_portable_test_suspend_from_code = mod->getFunction("art_portable_test_suspend_from_code");
-if (!func_art_portable_test_suspend_from_code) {
-func_art_portable_test_suspend_from_code = Function::Create(
- /*Type=*/FuncTy_6,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_test_suspend_from_code", mod);  // (external, no body)
-func_art_portable_test_suspend_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_test_suspend_from_code_PAL;
-func_art_portable_test_suspend_from_code->setAttributes(func_art_portable_test_suspend_from_code_PAL);
-
-Function* func_art_portable_push_shadow_frame_from_code = mod->getFunction("art_portable_push_shadow_frame_from_code");
-if (!func_art_portable_push_shadow_frame_from_code) {
-func_art_portable_push_shadow_frame_from_code = Function::Create(
- /*Type=*/FuncTy_7,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_push_shadow_frame_from_code", mod);  // (external, no body)
-func_art_portable_push_shadow_frame_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_push_shadow_frame_from_code_PAL;
-func_art_portable_push_shadow_frame_from_code->setAttributes(func_art_portable_push_shadow_frame_from_code_PAL);
-
-Function* func_art_portable_pop_shadow_frame_from_code = mod->getFunction("art_portable_pop_shadow_frame_from_code");
-if (!func_art_portable_pop_shadow_frame_from_code) {
-func_art_portable_pop_shadow_frame_from_code = Function::Create(
- /*Type=*/FuncTy_8,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_pop_shadow_frame_from_code", mod);  // (external, no body)
-func_art_portable_pop_shadow_frame_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_pop_shadow_frame_from_code_PAL;
-func_art_portable_pop_shadow_frame_from_code->setAttributes(func_art_portable_pop_shadow_frame_from_code_PAL);
-
-Function* func_art_portable_get_and_clear_exception = mod->getFunction("art_portable_get_and_clear_exception");
-if (!func_art_portable_get_and_clear_exception) {
-func_art_portable_get_and_clear_exception = Function::Create(
- /*Type=*/FuncTy_4,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_and_clear_exception", mod);  // (external, no body)
-func_art_portable_get_and_clear_exception->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_and_clear_exception_PAL;
-func_art_portable_get_and_clear_exception->setAttributes(func_art_portable_get_and_clear_exception_PAL);
-
-Function* func_art_portable_throw_div_zero_from_code = mod->getFunction("art_portable_throw_div_zero_from_code");
-if (!func_art_portable_throw_div_zero_from_code) {
-func_art_portable_throw_div_zero_from_code = Function::Create(
- /*Type=*/FuncTy_9,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_div_zero_from_code", mod);  // (external, no body)
-func_art_portable_throw_div_zero_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_div_zero_from_code_PAL;
-func_art_portable_throw_div_zero_from_code->setAttributes(func_art_portable_throw_div_zero_from_code_PAL);
-
-Function* func_art_portable_throw_array_bounds_from_code = mod->getFunction("art_portable_throw_array_bounds_from_code");
-if (!func_art_portable_throw_array_bounds_from_code) {
-func_art_portable_throw_array_bounds_from_code = Function::Create(
- /*Type=*/FuncTy_10,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_array_bounds_from_code", mod);  // (external, no body)
-func_art_portable_throw_array_bounds_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_array_bounds_from_code_PAL;
-func_art_portable_throw_array_bounds_from_code->setAttributes(func_art_portable_throw_array_bounds_from_code_PAL);
-
-Function* func_art_portable_throw_no_such_method_from_code = mod->getFunction("art_portable_throw_no_such_method_from_code");
-if (!func_art_portable_throw_no_such_method_from_code) {
-func_art_portable_throw_no_such_method_from_code = Function::Create(
- /*Type=*/FuncTy_11,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_no_such_method_from_code", mod);  // (external, no body)
-func_art_portable_throw_no_such_method_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_no_such_method_from_code_PAL;
-func_art_portable_throw_no_such_method_from_code->setAttributes(func_art_portable_throw_no_such_method_from_code_PAL);
-
-Function* func_art_portable_throw_null_pointer_exception_from_code = mod->getFunction("art_portable_throw_null_pointer_exception_from_code");
-if (!func_art_portable_throw_null_pointer_exception_from_code) {
-func_art_portable_throw_null_pointer_exception_from_code = Function::Create(
- /*Type=*/FuncTy_11,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_null_pointer_exception_from_code", mod);  // (external, no body)
-func_art_portable_throw_null_pointer_exception_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_null_pointer_exception_from_code_PAL;
-func_art_portable_throw_null_pointer_exception_from_code->setAttributes(func_art_portable_throw_null_pointer_exception_from_code_PAL);
-
-Function* func_art_portable_throw_stack_overflow_from_code = mod->getFunction("art_portable_throw_stack_overflow_from_code");
-if (!func_art_portable_throw_stack_overflow_from_code) {
-func_art_portable_throw_stack_overflow_from_code = Function::Create(
- /*Type=*/FuncTy_9,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_stack_overflow_from_code", mod);  // (external, no body)
-func_art_portable_throw_stack_overflow_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_stack_overflow_from_code_PAL;
-func_art_portable_throw_stack_overflow_from_code->setAttributes(func_art_portable_throw_stack_overflow_from_code_PAL);
-
-Function* func_art_portable_throw_exception_from_code = mod->getFunction("art_portable_throw_exception_from_code");
-if (!func_art_portable_throw_exception_from_code) {
-func_art_portable_throw_exception_from_code = Function::Create(
- /*Type=*/FuncTy_6,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_throw_exception_from_code", mod);  // (external, no body)
-func_art_portable_throw_exception_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_throw_exception_from_code_PAL;
-func_art_portable_throw_exception_from_code->setAttributes(func_art_portable_throw_exception_from_code_PAL);
-
-Function* func_art_portable_find_catch_block_from_code = mod->getFunction("art_portable_find_catch_block_from_code");
-if (!func_art_portable_find_catch_block_from_code) {
-func_art_portable_find_catch_block_from_code = Function::Create(
- /*Type=*/FuncTy_12,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_catch_block_from_code", mod);  // (external, no body)
-func_art_portable_find_catch_block_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_catch_block_from_code_PAL;
-func_art_portable_find_catch_block_from_code->setAttributes(func_art_portable_find_catch_block_from_code_PAL);
-
-Function* func_art_portable_alloc_object_from_code = mod->getFunction("art_portable_alloc_object_from_code");
-if (!func_art_portable_alloc_object_from_code) {
-func_art_portable_alloc_object_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_object_from_code", mod);  // (external, no body)
-func_art_portable_alloc_object_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_object_from_code_PAL;
-func_art_portable_alloc_object_from_code->setAttributes(func_art_portable_alloc_object_from_code_PAL);
-
-Function* func_art_portable_alloc_object_from_code_with_access_check = mod->getFunction("art_portable_alloc_object_from_code_with_access_check");
-if (!func_art_portable_alloc_object_from_code_with_access_check) {
-func_art_portable_alloc_object_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_object_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_alloc_object_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_object_from_code_with_access_check_PAL;
-func_art_portable_alloc_object_from_code_with_access_check->setAttributes(func_art_portable_alloc_object_from_code_with_access_check_PAL);
-
-Function* func_art_portable_alloc_array_from_code = mod->getFunction("art_portable_alloc_array_from_code");
-if (!func_art_portable_alloc_array_from_code) {
-func_art_portable_alloc_array_from_code = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_array_from_code", mod);  // (external, no body)
-func_art_portable_alloc_array_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_array_from_code_PAL;
-func_art_portable_alloc_array_from_code->setAttributes(func_art_portable_alloc_array_from_code_PAL);
-
-Function* func_art_portable_alloc_array_from_code_with_access_check = mod->getFunction("art_portable_alloc_array_from_code_with_access_check");
-if (!func_art_portable_alloc_array_from_code_with_access_check) {
-func_art_portable_alloc_array_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_alloc_array_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_alloc_array_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_alloc_array_from_code_with_access_check_PAL;
-func_art_portable_alloc_array_from_code_with_access_check->setAttributes(func_art_portable_alloc_array_from_code_with_access_check_PAL);
-
-Function* func_art_portable_check_and_alloc_array_from_code = mod->getFunction("art_portable_check_and_alloc_array_from_code");
-if (!func_art_portable_check_and_alloc_array_from_code) {
-func_art_portable_check_and_alloc_array_from_code = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_and_alloc_array_from_code", mod);  // (external, no body)
-func_art_portable_check_and_alloc_array_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_and_alloc_array_from_code_PAL;
-func_art_portable_check_and_alloc_array_from_code->setAttributes(func_art_portable_check_and_alloc_array_from_code_PAL);
-
-Function* func_art_portable_check_and_alloc_array_from_code_with_access_check = mod->getFunction("art_portable_check_and_alloc_array_from_code_with_access_check");
-if (!func_art_portable_check_and_alloc_array_from_code_with_access_check) {
-func_art_portable_check_and_alloc_array_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_14,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_and_alloc_array_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_check_and_alloc_array_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_and_alloc_array_from_code_with_access_check_PAL;
-func_art_portable_check_and_alloc_array_from_code_with_access_check->setAttributes(func_art_portable_check_and_alloc_array_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_instance_field_from_code = mod->getFunction("art_portable_find_instance_field_from_code");
-if (!func_art_portable_find_instance_field_from_code) {
-func_art_portable_find_instance_field_from_code = Function::Create(
- /*Type=*/FuncTy_15,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_instance_field_from_code", mod);  // (external, no body)
-func_art_portable_find_instance_field_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_instance_field_from_code_PAL;
-func_art_portable_find_instance_field_from_code->setAttributes(func_art_portable_find_instance_field_from_code_PAL);
-
-Function* func_art_portable_find_static_field_from_code = mod->getFunction("art_portable_find_static_field_from_code");
-if (!func_art_portable_find_static_field_from_code) {
-func_art_portable_find_static_field_from_code = Function::Create(
- /*Type=*/FuncTy_15,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_static_field_from_code", mod);  // (external, no body)
-func_art_portable_find_static_field_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_static_field_from_code_PAL;
-func_art_portable_find_static_field_from_code->setAttributes(func_art_portable_find_static_field_from_code_PAL);
-
-Function* func_art_portable_find_static_method_from_code_with_access_check = mod->getFunction("art_portable_find_static_method_from_code_with_access_check");
-if (!func_art_portable_find_static_method_from_code_with_access_check) {
-func_art_portable_find_static_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_static_method_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_find_static_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_static_method_from_code_with_access_check_PAL;
-func_art_portable_find_static_method_from_code_with_access_check->setAttributes(func_art_portable_find_static_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_direct_method_from_code_with_access_check = mod->getFunction("art_portable_find_direct_method_from_code_with_access_check");
-if (!func_art_portable_find_direct_method_from_code_with_access_check) {
-func_art_portable_find_direct_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_direct_method_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_find_direct_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_direct_method_from_code_with_access_check_PAL;
-func_art_portable_find_direct_method_from_code_with_access_check->setAttributes(func_art_portable_find_direct_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_virtual_method_from_code_with_access_check = mod->getFunction("art_portable_find_virtual_method_from_code_with_access_check");
-if (!func_art_portable_find_virtual_method_from_code_with_access_check) {
-func_art_portable_find_virtual_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_virtual_method_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_find_virtual_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_virtual_method_from_code_with_access_check_PAL;
-func_art_portable_find_virtual_method_from_code_with_access_check->setAttributes(func_art_portable_find_virtual_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_super_method_from_code_with_access_check = mod->getFunction("art_portable_find_super_method_from_code_with_access_check");
-if (!func_art_portable_find_super_method_from_code_with_access_check) {
-func_art_portable_find_super_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_super_method_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_find_super_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_super_method_from_code_with_access_check_PAL;
-func_art_portable_find_super_method_from_code_with_access_check->setAttributes(func_art_portable_find_super_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_interface_method_from_code_with_access_check = mod->getFunction("art_portable_find_interface_method_from_code_with_access_check");
-if (!func_art_portable_find_interface_method_from_code_with_access_check) {
-func_art_portable_find_interface_method_from_code_with_access_check = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_interface_method_from_code_with_access_check", mod);  // (external, no body)
-func_art_portable_find_interface_method_from_code_with_access_check->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_interface_method_from_code_with_access_check_PAL;
-func_art_portable_find_interface_method_from_code_with_access_check->setAttributes(func_art_portable_find_interface_method_from_code_with_access_check_PAL);
-
-Function* func_art_portable_find_interface_method_from_code = mod->getFunction("art_portable_find_interface_method_from_code");
-if (!func_art_portable_find_interface_method_from_code) {
-func_art_portable_find_interface_method_from_code = Function::Create(
- /*Type=*/FuncTy_16,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_find_interface_method_from_code", mod);  // (external, no body)
-func_art_portable_find_interface_method_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_find_interface_method_from_code_PAL;
-func_art_portable_find_interface_method_from_code->setAttributes(func_art_portable_find_interface_method_from_code_PAL);
-
-Function* func_art_portable_initialize_static_storage_from_code = mod->getFunction("art_portable_initialize_static_storage_from_code");
-if (!func_art_portable_initialize_static_storage_from_code) {
-func_art_portable_initialize_static_storage_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_initialize_static_storage_from_code", mod);  // (external, no body)
-func_art_portable_initialize_static_storage_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_initialize_static_storage_from_code_PAL;
-func_art_portable_initialize_static_storage_from_code->setAttributes(func_art_portable_initialize_static_storage_from_code_PAL);
-
-Function* func_art_portable_initialize_type_from_code = mod->getFunction("art_portable_initialize_type_from_code");
-if (!func_art_portable_initialize_type_from_code) {
-func_art_portable_initialize_type_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_initialize_type_from_code", mod);  // (external, no body)
-func_art_portable_initialize_type_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_initialize_type_from_code_PAL;
-func_art_portable_initialize_type_from_code->setAttributes(func_art_portable_initialize_type_from_code_PAL);
-
-Function* func_art_portable_initialize_type_and_verify_access_from_code = mod->getFunction("art_portable_initialize_type_and_verify_access_from_code");
-if (!func_art_portable_initialize_type_and_verify_access_from_code) {
-func_art_portable_initialize_type_and_verify_access_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_initialize_type_and_verify_access_from_code", mod);  // (external, no body)
-func_art_portable_initialize_type_and_verify_access_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_initialize_type_and_verify_access_from_code_PAL;
-func_art_portable_initialize_type_and_verify_access_from_code->setAttributes(func_art_portable_initialize_type_and_verify_access_from_code_PAL);
-
-Function* func_art_portable_resolve_string_from_code = mod->getFunction("art_portable_resolve_string_from_code");
-if (!func_art_portable_resolve_string_from_code) {
-func_art_portable_resolve_string_from_code = Function::Create(
- /*Type=*/FuncTy_17,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_resolve_string_from_code", mod);  // (external, no body)
-func_art_portable_resolve_string_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_resolve_string_from_code_PAL;
-func_art_portable_resolve_string_from_code->setAttributes(func_art_portable_resolve_string_from_code_PAL);
-
-Function* func_art_portable_set32_static_from_code = mod->getFunction("art_portable_set32_static_from_code");
-if (!func_art_portable_set32_static_from_code) {
-func_art_portable_set32_static_from_code = Function::Create(
- /*Type=*/FuncTy_18,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set32_static_from_code", mod);  // (external, no body)
-func_art_portable_set32_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set32_static_from_code_PAL;
-func_art_portable_set32_static_from_code->setAttributes(func_art_portable_set32_static_from_code_PAL);
-
-Function* func_art_portable_set64_static_from_code = mod->getFunction("art_portable_set64_static_from_code");
-if (!func_art_portable_set64_static_from_code) {
-func_art_portable_set64_static_from_code = Function::Create(
- /*Type=*/FuncTy_19,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set64_static_from_code", mod);  // (external, no body)
-func_art_portable_set64_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set64_static_from_code_PAL;
-func_art_portable_set64_static_from_code->setAttributes(func_art_portable_set64_static_from_code_PAL);
-
-Function* func_art_portable_set_obj_static_from_code = mod->getFunction("art_portable_set_obj_static_from_code");
-if (!func_art_portable_set_obj_static_from_code) {
-func_art_portable_set_obj_static_from_code = Function::Create(
- /*Type=*/FuncTy_20,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set_obj_static_from_code", mod);  // (external, no body)
-func_art_portable_set_obj_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set_obj_static_from_code_PAL;
-func_art_portable_set_obj_static_from_code->setAttributes(func_art_portable_set_obj_static_from_code_PAL);
-
-Function* func_art_portable_get32_static_from_code = mod->getFunction("art_portable_get32_static_from_code");
-if (!func_art_portable_get32_static_from_code) {
-func_art_portable_get32_static_from_code = Function::Create(
- /*Type=*/FuncTy_21,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get32_static_from_code", mod);  // (external, no body)
-func_art_portable_get32_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get32_static_from_code_PAL;
-func_art_portable_get32_static_from_code->setAttributes(func_art_portable_get32_static_from_code_PAL);
-
-Function* func_art_portable_get64_static_from_code = mod->getFunction("art_portable_get64_static_from_code");
-if (!func_art_portable_get64_static_from_code) {
-func_art_portable_get64_static_from_code = Function::Create(
- /*Type=*/FuncTy_22,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get64_static_from_code", mod);  // (external, no body)
-func_art_portable_get64_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get64_static_from_code_PAL;
-func_art_portable_get64_static_from_code->setAttributes(func_art_portable_get64_static_from_code_PAL);
-
-Function* func_art_portable_get_obj_static_from_code = mod->getFunction("art_portable_get_obj_static_from_code");
-if (!func_art_portable_get_obj_static_from_code) {
-func_art_portable_get_obj_static_from_code = Function::Create(
- /*Type=*/FuncTy_23,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_obj_static_from_code", mod);  // (external, no body)
-func_art_portable_get_obj_static_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_obj_static_from_code_PAL;
-func_art_portable_get_obj_static_from_code->setAttributes(func_art_portable_get_obj_static_from_code_PAL);
-
-Function* func_art_portable_set32_instance_from_code = mod->getFunction("art_portable_set32_instance_from_code");
-if (!func_art_portable_set32_instance_from_code) {
-func_art_portable_set32_instance_from_code = Function::Create(
- /*Type=*/FuncTy_24,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set32_instance_from_code", mod);  // (external, no body)
-func_art_portable_set32_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set32_instance_from_code_PAL;
-func_art_portable_set32_instance_from_code->setAttributes(func_art_portable_set32_instance_from_code_PAL);
-
-Function* func_art_portable_set64_instance_from_code = mod->getFunction("art_portable_set64_instance_from_code");
-if (!func_art_portable_set64_instance_from_code) {
-func_art_portable_set64_instance_from_code = Function::Create(
- /*Type=*/FuncTy_25,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set64_instance_from_code", mod);  // (external, no body)
-func_art_portable_set64_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set64_instance_from_code_PAL;
-func_art_portable_set64_instance_from_code->setAttributes(func_art_portable_set64_instance_from_code_PAL);
-
-Function* func_art_portable_set_obj_instance_from_code = mod->getFunction("art_portable_set_obj_instance_from_code");
-if (!func_art_portable_set_obj_instance_from_code) {
-func_art_portable_set_obj_instance_from_code = Function::Create(
- /*Type=*/FuncTy_26,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_set_obj_instance_from_code", mod);  // (external, no body)
-func_art_portable_set_obj_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_set_obj_instance_from_code_PAL;
-func_art_portable_set_obj_instance_from_code->setAttributes(func_art_portable_set_obj_instance_from_code_PAL);
-
-Function* func_art_portable_get32_instance_from_code = mod->getFunction("art_portable_get32_instance_from_code");
-if (!func_art_portable_get32_instance_from_code) {
-func_art_portable_get32_instance_from_code = Function::Create(
- /*Type=*/FuncTy_20,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get32_instance_from_code", mod);  // (external, no body)
-func_art_portable_get32_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get32_instance_from_code_PAL;
-func_art_portable_get32_instance_from_code->setAttributes(func_art_portable_get32_instance_from_code_PAL);
-
-Function* func_art_portable_get64_instance_from_code = mod->getFunction("art_portable_get64_instance_from_code");
-if (!func_art_portable_get64_instance_from_code) {
-func_art_portable_get64_instance_from_code = Function::Create(
- /*Type=*/FuncTy_27,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get64_instance_from_code", mod);  // (external, no body)
-func_art_portable_get64_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get64_instance_from_code_PAL;
-func_art_portable_get64_instance_from_code->setAttributes(func_art_portable_get64_instance_from_code_PAL);
-
-Function* func_art_portable_get_obj_instance_from_code = mod->getFunction("art_portable_get_obj_instance_from_code");
-if (!func_art_portable_get_obj_instance_from_code) {
-func_art_portable_get_obj_instance_from_code = Function::Create(
- /*Type=*/FuncTy_13,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_get_obj_instance_from_code", mod);  // (external, no body)
-func_art_portable_get_obj_instance_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_get_obj_instance_from_code_PAL;
-func_art_portable_get_obj_instance_from_code->setAttributes(func_art_portable_get_obj_instance_from_code_PAL);
-
-Function* func_art_portable_decode_jobject_in_thread = mod->getFunction("art_portable_decode_jobject_in_thread");
-if (!func_art_portable_decode_jobject_in_thread) {
-func_art_portable_decode_jobject_in_thread = Function::Create(
- /*Type=*/FuncTy_28,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_decode_jobject_in_thread", mod);  // (external, no body)
-func_art_portable_decode_jobject_in_thread->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_decode_jobject_in_thread_PAL;
-func_art_portable_decode_jobject_in_thread->setAttributes(func_art_portable_decode_jobject_in_thread_PAL);
-
-Function* func_art_portable_fill_array_data_from_code = mod->getFunction("art_portable_fill_array_data_from_code");
-if (!func_art_portable_fill_array_data_from_code) {
-func_art_portable_fill_array_data_from_code = Function::Create(
- /*Type=*/FuncTy_29,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_fill_array_data_from_code", mod);  // (external, no body)
-func_art_portable_fill_array_data_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_fill_array_data_from_code_PAL;
-func_art_portable_fill_array_data_from_code->setAttributes(func_art_portable_fill_array_data_from_code_PAL);
-
-Function* func_art_portable_is_assignable_from_code = mod->getFunction("art_portable_is_assignable_from_code");
-if (!func_art_portable_is_assignable_from_code) {
-func_art_portable_is_assignable_from_code = Function::Create(
- /*Type=*/FuncTy_30,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_is_assignable_from_code", mod);  // (external, no body)
-func_art_portable_is_assignable_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_is_assignable_from_code_PAL;
-func_art_portable_is_assignable_from_code->setAttributes(func_art_portable_is_assignable_from_code_PAL);
-
-Function* func_art_portable_check_cast_from_code = mod->getFunction("art_portable_check_cast_from_code");
-if (!func_art_portable_check_cast_from_code) {
-func_art_portable_check_cast_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_cast_from_code", mod);  // (external, no body)
-func_art_portable_check_cast_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_cast_from_code_PAL;
-func_art_portable_check_cast_from_code->setAttributes(func_art_portable_check_cast_from_code_PAL);
-
-Function* func_art_portable_check_put_array_element_from_code = mod->getFunction("art_portable_check_put_array_element_from_code");
-if (!func_art_portable_check_put_array_element_from_code) {
-func_art_portable_check_put_array_element_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_check_put_array_element_from_code", mod);  // (external, no body)
-func_art_portable_check_put_array_element_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_check_put_array_element_from_code_PAL;
-func_art_portable_check_put_array_element_from_code->setAttributes(func_art_portable_check_put_array_element_from_code_PAL);
-
-Function* func_art_d2l = mod->getFunction("art_d2l");
-if (!func_art_d2l) {
-func_art_d2l = Function::Create(
- /*Type=*/FuncTy_31,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_d2l", mod);  // (external, no body)
-func_art_d2l->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_d2l_PAL;
-func_art_d2l->setAttributes(func_art_d2l_PAL);
-
-Function* func_art_d2i = mod->getFunction("art_d2i");
-if (!func_art_d2i) {
-func_art_d2i = Function::Create(
- /*Type=*/FuncTy_32,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_d2i", mod);  // (external, no body)
-func_art_d2i->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_d2i_PAL;
-func_art_d2i->setAttributes(func_art_d2i_PAL);
-
-Function* func_art_f2l = mod->getFunction("art_f2l");
-if (!func_art_f2l) {
-func_art_f2l = Function::Create(
- /*Type=*/FuncTy_33,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_f2l", mod);  // (external, no body)
-func_art_f2l->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_f2l_PAL;
-func_art_f2l->setAttributes(func_art_f2l_PAL);
-
-Function* func_art_f2i = mod->getFunction("art_f2i");
-if (!func_art_f2i) {
-func_art_f2i = Function::Create(
- /*Type=*/FuncTy_34,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_f2i", mod);  // (external, no body)
-func_art_f2i->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_f2i_PAL;
-func_art_f2i->setAttributes(func_art_f2i_PAL);
-
-Function* func_art_portable_jni_method_start = mod->getFunction("art_portable_jni_method_start");
-if (!func_art_portable_jni_method_start) {
-func_art_portable_jni_method_start = Function::Create(
- /*Type=*/FuncTy_35,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_start", mod);  // (external, no body)
-func_art_portable_jni_method_start->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_start_PAL;
-func_art_portable_jni_method_start->setAttributes(func_art_portable_jni_method_start_PAL);
-
-Function* func_art_portable_jni_method_start_synchronized = mod->getFunction("art_portable_jni_method_start_synchronized");
-if (!func_art_portable_jni_method_start_synchronized) {
-func_art_portable_jni_method_start_synchronized = Function::Create(
- /*Type=*/FuncTy_30,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_start_synchronized", mod);  // (external, no body)
-func_art_portable_jni_method_start_synchronized->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_start_synchronized_PAL;
-func_art_portable_jni_method_start_synchronized->setAttributes(func_art_portable_jni_method_start_synchronized_PAL);
-
-Function* func_art_portable_jni_method_end = mod->getFunction("art_portable_jni_method_end");
-if (!func_art_portable_jni_method_end) {
-func_art_portable_jni_method_end = Function::Create(
- /*Type=*/FuncTy_15,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end", mod);  // (external, no body)
-func_art_portable_jni_method_end->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_PAL;
-func_art_portable_jni_method_end->setAttributes(func_art_portable_jni_method_end_PAL);
-
-Function* func_art_portable_jni_method_end_synchronized = mod->getFunction("art_portable_jni_method_end_synchronized");
-if (!func_art_portable_jni_method_end_synchronized) {
-func_art_portable_jni_method_end_synchronized = Function::Create(
- /*Type=*/FuncTy_36,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end_synchronized", mod);  // (external, no body)
-func_art_portable_jni_method_end_synchronized->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_synchronized_PAL;
-func_art_portable_jni_method_end_synchronized->setAttributes(func_art_portable_jni_method_end_synchronized_PAL);
-
-Function* func_art_portable_jni_method_end_with_reference = mod->getFunction("art_portable_jni_method_end_with_reference");
-if (!func_art_portable_jni_method_end_with_reference) {
-func_art_portable_jni_method_end_with_reference = Function::Create(
- /*Type=*/FuncTy_37,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end_with_reference", mod);  // (external, no body)
-func_art_portable_jni_method_end_with_reference->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_with_reference_PAL;
-func_art_portable_jni_method_end_with_reference->setAttributes(func_art_portable_jni_method_end_with_reference_PAL);
-
-Function* func_art_portable_jni_method_end_with_reference_synchronized = mod->getFunction("art_portable_jni_method_end_with_reference_synchronized");
-if (!func_art_portable_jni_method_end_with_reference_synchronized) {
-func_art_portable_jni_method_end_with_reference_synchronized = Function::Create(
- /*Type=*/FuncTy_38,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_jni_method_end_with_reference_synchronized", mod);  // (external, no body)
-func_art_portable_jni_method_end_with_reference_synchronized->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_jni_method_end_with_reference_synchronized_PAL;
-func_art_portable_jni_method_end_with_reference_synchronized->setAttributes(func_art_portable_jni_method_end_with_reference_synchronized_PAL);
-
-Function* func_art_portable_is_exception_pending_from_code = mod->getFunction("art_portable_is_exception_pending_from_code");
-if (!func_art_portable_is_exception_pending_from_code) {
-func_art_portable_is_exception_pending_from_code = Function::Create(
- /*Type=*/FuncTy_39,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_is_exception_pending_from_code", mod);  // (external, no body)
-func_art_portable_is_exception_pending_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_is_exception_pending_from_code_PAL;
-func_art_portable_is_exception_pending_from_code->setAttributes(func_art_portable_is_exception_pending_from_code_PAL);
-
-Function* func_art_portable_mark_gc_card_from_code = mod->getFunction("art_portable_mark_gc_card_from_code");
-if (!func_art_portable_mark_gc_card_from_code) {
-func_art_portable_mark_gc_card_from_code = Function::Create(
- /*Type=*/FuncTy_5,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_mark_gc_card_from_code", mod);  // (external, no body)
-func_art_portable_mark_gc_card_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_mark_gc_card_from_code_PAL;
-func_art_portable_mark_gc_card_from_code->setAttributes(func_art_portable_mark_gc_card_from_code_PAL);
-
-Function* func_art_portable_proxy_invoke_handler_from_code = mod->getFunction("art_portable_proxy_invoke_handler_from_code");
-if (!func_art_portable_proxy_invoke_handler_from_code) {
-func_art_portable_proxy_invoke_handler_from_code = Function::Create(
- /*Type=*/FuncTy_40,
- /*Linkage=*/GlobalValue::ExternalLinkage,
- /*Name=*/"art_portable_proxy_invoke_handler_from_code", mod);  // (external, no body)
-func_art_portable_proxy_invoke_handler_from_code->setCallingConv(CallingConv::C);
-}
-AttributeSet func_art_portable_proxy_invoke_handler_from_code_PAL;
-func_art_portable_proxy_invoke_handler_from_code->setAttributes(func_art_portable_proxy_invoke_handler_from_code_PAL);
-
-// Global Variable Declarations
-
-
-// Constant Definitions
-
-// Global Variable Definitions
-
-// Function Definitions
-
-return mod;
-
-}
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/intrinsic_func_list.def b/compiler/llvm/intrinsic_func_list.def
deleted file mode 100644
index 887a626..0000000
--- a/compiler/llvm/intrinsic_func_list.def
+++ /dev/null
@@ -1,1796 +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.
- */
-
-// DEF_INTRINSICS_FUNC(ID, NAME, ATTR, RET_TYPE,
-//                     ARG1_TYPE, ARG2_TYPE, ARG3_TYPE, ARG4_TYPE, ARG5_TYPE)
-#ifndef DEF_INTRINSICS_FUNC
-#  error "missing DEF_INTRINSICS_FUNC definition!"
-#endif
-
-#define _EVAL_DEF_INTRINSICS_FUNC(ID, NAME, ATTR, RET_TYPE, ...) \
-    DEF_INTRINSICS_FUNC(ID, NAME, ATTR, RET_TYPE, __VA_ARGS__)
-
-#define _EXPAND_ARG0()                         kNone, kNone, kNone, kNone, kNone
-#define _EXPAND_ARG1(ARG1)                      ARG1, kNone, kNone, kNone, kNone
-#define _EXPAND_ARG2(ARG1, ARG2)                ARG1,  ARG2, kNone, kNone, kNone
-#define _EXPAND_ARG3(ARG1, ARG2, ARG3)          ARG1,  ARG2,  ARG3, kNone, kNone
-#define _EXPAND_ARG4(ARG1, ARG2, ARG3, ARG4)    ARG1,  ARG2,  ARG3,  ARG4, kNone
-#define _EXPAND_ARG5(ARG1, ARG2, ARG3, ARG4, ARG5) \
-                                                ARG1,  ARG2,  ARG3,  ARG4,  ARG5
-
-#define _JTYPE(TYPE, SPACE) _JTYPE_OF_ ## TYPE ## _UNDER_ ## SPACE
-
-// Note: These should be consistent with the type return from
-// IRBuilder::GetJType([type], kArray).
-#define _JTYPE_OF_kInt1Ty_UNDER_kArray        kInt8Ty
-#define _JTYPE_OF_kInt8Ty_UNDER_kArray        kInt8Ty
-#define _JTYPE_OF_kInt16Ty_UNDER_kArray       kInt16Ty
-#define _JTYPE_OF_kInt32Ty_UNDER_kArray       kInt32Ty
-#define _JTYPE_OF_kInt64Ty_UNDER_kArray       kInt64Ty
-#define _JTYPE_OF_kJavaObjectTy_UNDER_kArray  kJavaObjectTy
-
-// Note: These should be consistent with the type return from
-// IRBuilder::GetJType([type], kField).
-#define _JTYPE_OF_kInt1Ty_UNDER_kField        kInt32Ty
-#define _JTYPE_OF_kInt8Ty_UNDER_kField        kInt32Ty
-#define _JTYPE_OF_kInt16Ty_UNDER_kField       kInt32Ty
-#define _JTYPE_OF_kInt32Ty_UNDER_kField       kInt32Ty
-#define _JTYPE_OF_kInt64Ty_UNDER_kField       kInt64Ty
-#define _JTYPE_OF_kJavaObjectTy_UNDER_kField  kJavaObjectTy
-
-//----------------------------------------------------------------------------
-// Thread
-//----------------------------------------------------------------------------
-
-// Thread* art_portable_get_current_thread()
-_EVAL_DEF_INTRINSICS_FUNC(GetCurrentThread,
-                          art_portable_get_current_thread,
-                          kAttrReadNone | kAttrNoThrow,
-                          kJavaThreadTy,
-                          _EXPAND_ARG0())
-
-// void art_portable_test_suspend(Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(TestSuspend,
-                          art_portable_test_suspend,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG1(kJavaThreadTy))
-
-// void art_portable_check_suspend() /* Expands to GetCurrentThread/TestSuspend */
-_EVAL_DEF_INTRINSICS_FUNC(CheckSuspend,
-                          art_portable_check_suspend,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG0())
-
-// void art_portable_mark_gc_card(Object* new_value, Object* object)
-_EVAL_DEF_INTRINSICS_FUNC(MarkGCCard,
-                          art_portable_mark_gc_card,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Exception
-//----------------------------------------------------------------------------
-
-// Should not expand - introduces the catch targets for a potentially
-// throwing instruction.  The result is a switch key and this
-// instruction will be followed by a switch statement.  The catch
-// targets will be enumerated as cases of the switch, with the fallthrough
-// designating the block containing the potentially throwing instruction.
-// bool art_portable_catch_targets(int dex_pc)
-_EVAL_DEF_INTRINSICS_FUNC(CatchTargets,
-                          art_portable_catch_targets,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// void art_portable_throw_exception(JavaObject* exception)
-_EVAL_DEF_INTRINSICS_FUNC(ThrowException,
-                          art_portable_throw_exception,
-                          kAttrDoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG1(kJavaObjectTy))
-
-// void art_portable_hl_throw_exception(JavaObject* exception)
-_EVAL_DEF_INTRINSICS_FUNC(HLThrowException,
-                          art_portable_hl_throw_exception,
-                          kAttrDoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG1(kJavaObjectTy))
-
-// JavaObject* art_portable_get_current_exception()
-_EVAL_DEF_INTRINSICS_FUNC(GetException,
-                          art_portable_get_current_exception,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG0())
-
-// bool art_portable_is_exception_pending()
-_EVAL_DEF_INTRINSICS_FUNC(IsExceptionPending,
-                          art_portable_is_exception_pending,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt1Ty,
-                          _EXPAND_ARG0())
-
-// int art_portable_find_catch_block(Method* method, int try_item_offset)
-_EVAL_DEF_INTRINSICS_FUNC(FindCatchBlock,
-                          art_portable_find_catch_block,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kInt32ConstantTy))
-
-// void art_portable_throw_div_zero()
-_EVAL_DEF_INTRINSICS_FUNC(ThrowDivZeroException,
-                          art_portable_throw_div_zero,
-                          kAttrDoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG0())
-
-// void art_portable_throw_null_pointer_exception(uint32_t dex_pc)
-_EVAL_DEF_INTRINSICS_FUNC(ThrowNullPointerException,
-                          art_portable_throw_null_pointer_exception,
-                          kAttrDoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// void art_portable_throw_array_bounds(int index, int array_len)
-_EVAL_DEF_INTRINSICS_FUNC(ThrowIndexOutOfBounds,
-                          art_portable_throw_array_bounds,
-                          kAttrDoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-//----------------------------------------------------------------------------
-// ConstString
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_const_string(uint32_t string_idx)
-_EVAL_DEF_INTRINSICS_FUNC(ConstString,
-                          art_portable_const_string,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_load_string_from_dex_cache(Method* method, uint32_t string_idx)
-_EVAL_DEF_INTRINSICS_FUNC(LoadStringFromDexCache,
-                          art_portable_load_string_from_dex_cache,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_resolve_string(Method* method, uint32_t string_idx)
-_EVAL_DEF_INTRINSICS_FUNC(ResolveString,
-                          art_portable_resolve_string,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG2(kJavaMethodTy, kInt32ConstantTy))
-
-//----------------------------------------------------------------------------
-// ConstClass
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_const_class(uint32_t type_idx)
-_EVAL_DEF_INTRINSICS_FUNC(ConstClass,
-                          art_portable_const_class,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_initialize_type_and_verify_access(uint32_t type_idx,
-//                                                        Method* referrer,
-//                                                        Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(InitializeTypeAndVerifyAccess,
-                          art_portable_initialize_type_and_verify_access,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-// JavaObject* art_portable_load_type_from_dex_cache(uint32_t type_idx)
-_EVAL_DEF_INTRINSICS_FUNC(LoadTypeFromDexCache,
-                          art_portable_load_type_from_dex_cache,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// JavaObject* art_portable_initialize_type(uint32_t type_idx,
-//                                      Method* referrer,
-//                                      Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(InitializeType,
-                          art_portable_initialize_type,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// Lock
-//----------------------------------------------------------------------------
-
-// void art_portable_lock_object(JavaObject* obj, Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(LockObject,
-                          art_portable_lock_object,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kJavaObjectTy, kJavaThreadTy))
-
-// void art_portable_unlock_object(JavaObject* obj, Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(UnlockObject,
-                          art_portable_unlock_object,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG2(kJavaObjectTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// Cast
-//----------------------------------------------------------------------------
-
-// void art_portable_check_cast(JavaObject* dest_type, JavaObject* src_type)
-_EVAL_DEF_INTRINSICS_FUNC(CheckCast,
-                          art_portable_check_cast,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-// void art_portable_hl_check_cast(uint32_t type_idx, JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(HLCheckCast,
-                          art_portable_hl_check_cast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaObjectTy))
-
-// int art_portable_is_assignable(JavaObject* dest_type, JavaObject* src_type)
-_EVAL_DEF_INTRINSICS_FUNC(IsAssignable,
-                          art_portable_is_assignable,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Allocation
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_alloc_object(uint32_t type_idx,
-//                                   Method* referrer,
-//                                   Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocObject,
-                          art_portable_alloc_object,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-// JavaObject* art_portable_alloc_object_with_access_check(uint32_t type_idx,
-//                                                     Method* referrer,
-//                                                     Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocObjectWithAccessCheck,
-                          art_portable_alloc_object_with_access_check,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// Instance
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_new_instance(uint32_t type_idx)
-_EVAL_DEF_INTRINSICS_FUNC(NewInstance,
-                          art_portable_new_instance,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// bool art_portable_instance_of(uint32_t type_idx, JavaObject* ref)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceOf,
-                          art_portable_instance_of,
-                          kAttrNone,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Array
-//----------------------------------------------------------------------------
-
-// JavaObject* art_portable_new_array(uint32_t type_idx, uint32_t array_size)
-_EVAL_DEF_INTRINSICS_FUNC(NewArray,
-                          art_portable_new_array,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG2(kInt32ConstantTy, kInt32Ty))
-
-// uint32_t art_portable_opt_array_length(int32_t opt_flags, JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(OptArrayLength,
-                          art_portable_opt_array_length,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-// uint32_t art_portable_array_length(JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(ArrayLength,
-                          art_portable_array_length,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kJavaObjectTy))
-
-// JavaObject* art_portable_alloc_array(uint32_t type_idx,
-//                                  Method* referrer,
-//                                  uint32_t length,
-//                                  Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocArray,
-                          art_portable_alloc_array,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32Ty, kJavaThreadTy))
-
-// JavaObject* art_portable_alloc_array_with_access_check(uint32_t type_idx,
-//                                                    Method* referrer,
-//                                                    uint32_t length,
-//                                                    Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(AllocArrayWithAccessCheck,
-                          art_portable_alloc_array_with_access_check,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32Ty, kJavaThreadTy))
-
-// JavaObject* art_portable_check_and_alloc_array(uint32_t type_idx,
-//                                            Method* referrer,
-//                                            uint32_t length,
-//                                            Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(CheckAndAllocArray,
-                          art_portable_check_and_alloc_array,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32ConstantTy, kJavaThreadTy))
-
-// JavaObject* art_portable_check_and_alloc_array_with_access_check(uint32_t type_idx,
-//                                                              Method* referrer,
-//                                                              uint32_t length,
-//                                                              Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(CheckAndAllocArrayWithAccessCheck,
-                          art_portable_check_and_alloc_array_with_access_check,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kInt32ConstantTy, kJavaThreadTy))
-
-// art_portable_aget_* and art_portable_aput_* never generate exception since the
-// necessary checking on arguments (e.g., array and index) has already done
-// before invocation of these intrinsics.
-//
-// [type] void art_portable_aget_[type](JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGet,
-                          art_portable_aget,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt32Ty, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetWide,
-                          art_portable_aget_wide,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt64Ty, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetObject,
-                          art_portable_aget_object,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kJavaObjectTy, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetBoolean,
-                          art_portable_aget_boolean,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt1Ty, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetByte,
-                          art_portable_aget_byte,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt8Ty, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetChar,
-                          art_portable_aget_char,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt16Ty, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayGetShort,
-                          art_portable_aget_short,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt16Ty, kArray),
-                          _EXPAND_ARG2(kJavaObjectTy, kInt32Ty))
-
-// void art_portable_aput_[type]([type] value, JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPut,
-                          art_portable_aput,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kInt32Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutWide,
-                          art_portable_aput_wide,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kInt64Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutObject,
-                          art_portable_aput_object,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kJavaObjectTy, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutBoolean,
-                          art_portable_aput_boolean,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kInt1Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutByte,
-                          art_portable_aput_byte,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kInt8Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutChar,
-                          art_portable_aput_char,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kInt16Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(ArrayPutShort,
-                          art_portable_aput_short,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(_JTYPE(kInt16Ty, kArray), kJavaObjectTy, kInt32Ty))
-
-// void art_portable_check_put_array_element(JavaObject* value, JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(CheckPutArrayElement,
-                          art_portable_check_put_array_element,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG2(kJavaObjectTy, kJavaObjectTy))
-
-// void art_portable_filled_new_array(Array* array,
-//                                uint32_t elem_jty, ...)
-_EVAL_DEF_INTRINSICS_FUNC(FilledNewArray,
-                          art_portable_filled_new_array,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kVarArgTy))
-
-// void art_portable_fill_array_data(Method* referrer,
-//                               uint32_t dex_pc,
-//                               Array* array,
-//                               uint32_t payload_offset)
-_EVAL_DEF_INTRINSICS_FUNC(FillArrayData,
-                          art_portable_fill_array_data,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaMethodTy, kInt32ConstantTy, kJavaObjectTy, kInt32ConstantTy))
-
-// void art_portable_hl_fill_array_data(int32_t offset, JavaObject* array)
-_EVAL_DEF_INTRINSICS_FUNC(HLFillArrayData,
-                          art_portable_hl_fill_array_data,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Instance Field
-//----------------------------------------------------------------------------
-
-// [type] art_portable_iget_[type](uint32_t field_idx,
-//                             Method* referrer,
-//                             JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGet,
-                          art_portable_iget,
-                          kAttrNone,
-                          _JTYPE(kInt32Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetWide,
-                          art_portable_iget_wide,
-                          kAttrNone,
-                          _JTYPE(kInt64Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetObject,
-                          art_portable_iget_object,
-                          kAttrNone,
-                          _JTYPE(kJavaObjectTy, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetBoolean,
-                          art_portable_iget_boolean,
-                          kAttrNone,
-                          _JTYPE(kInt1Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetByte,
-                          art_portable_iget_byte,
-                          kAttrNone,
-                          _JTYPE(kInt8Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetChar,
-                          art_portable_iget_char,
-                          kAttrNone,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetShort,
-                          art_portable_iget_short,
-                          kAttrNone,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy))
-
-// [type] art_portable_iget_[type].fast(int field_offset,
-//                                  bool is_volatile,
-//                                  JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetFast,
-                          art_portable_iget.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt32Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetWideFast,
-                          art_portable_iget_wide.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt64Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetObjectFast,
-                          art_portable_iget_object.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kJavaObjectTy, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetBooleanFast,
-                          art_portable_iget_boolean.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt1Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetByteFast,
-                          art_portable_iget_byte.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt8Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetCharFast,
-                          art_portable_iget_char.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldGetShortFast,
-                          art_portable_iget_short.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG3(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy))
-
-// void art_portable_iput_[type](uint32_t field_idx,
-//                           Method* referrer,
-//                           JavaObject* obj,
-//                           [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPut,
-                          art_portable_iput,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutWide,
-                          art_portable_iput_wide,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutObject,
-                          art_portable_iput_object,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutBoolean,
-                          art_portable_iput_boolean,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutByte,
-                          art_portable_iput_byte,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutChar,
-                          art_portable_iput_char,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutShort,
-                          art_portable_iput_short,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaMethodTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-// void art_portable_iput_[type].fast(int field_offset,
-//                                bool is_volatile,
-//                                JavaObject* obj,
-//                                [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutFast,
-                          art_portable_iput.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutWideFast,
-                          art_portable_iput_wide.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutObjectFast,
-                          art_portable_iput_object.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutBooleanFast,
-                          art_portable_iput_boolean.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutByteFast,
-                          art_portable_iput_byte.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutCharFast,
-                          art_portable_iput_char.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(InstanceFieldPutShortFast,
-                          art_portable_iput_short.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kInt1ConstantTy, kJavaObjectTy, _JTYPE(kInt16Ty, kField)))
-
-//----------------------------------------------------------------------------
-// Static Field
-//----------------------------------------------------------------------------
-
-// [type] art_portable_sget_[type](uint32_t field_idx, Method* referrer)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGet,
-                          art_portable_sget,
-                          kAttrNone,
-                          _JTYPE(kInt32Ty, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetWide,
-                          art_portable_sget_wide,
-                          kAttrNone,
-                          _JTYPE(kInt64Ty, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetObject,
-                          art_portable_sget_object,
-                          kAttrNone,
-                          _JTYPE(kJavaObjectTy, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetBoolean,
-                          art_portable_sget_boolean,
-                          kAttrNone,
-                          _JTYPE(kInt1Ty, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetByte,
-                          art_portable_sget_byte,
-                          kAttrNone,
-                          _JTYPE(kInt8Ty, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetChar,
-                          art_portable_sget_char,
-                          kAttrNone,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetShort,
-                          art_portable_sget_short,
-                          kAttrNone,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaMethodTy))
-
-// [type] art_portable_sget_[type].fast(JavaObject* ssb,
-//                                  int field_offset,
-//                                  bool is_volatile)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetFast,
-                          art_portable_sget.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt32Ty, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetWideFast,
-                          art_portable_sget_wide.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt64Ty, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetObjectFast,
-                          art_portable_sget_object.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kJavaObjectTy, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetBooleanFast,
-                          art_portable_sget_boolean.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt1Ty, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetByteFast,
-                          art_portable_sget_byte.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt8Ty, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetCharFast,
-                          art_portable_sget_char.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldGetShortFast,
-                          art_portable_sget_short.fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          _JTYPE(kInt16Ty, kField),
-                          _EXPAND_ARG3(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy))
-
-// void art_portable_sput_[type](uint32_t field_idx,
-//                           Method* referrer,
-//                           [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPut,
-                          art_portable_sput,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutWide,
-                          art_portable_sput_wide,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutObject,
-                          art_portable_sput_object,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutBoolean,
-                          art_portable_sput_boolean,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutByte,
-                          art_portable_sput_byte,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutChar,
-                          art_portable_sput_char,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutShort,
-                          art_portable_sput_short,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, _JTYPE(kInt16Ty, kField)))
-
-// void art_portable_sput_[type].fast(JavaObject* ssb,
-//                                int field_offset,
-//                                bool is_volatile,
-//                                [type] new_value)
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutFast,
-                          art_portable_sput.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt32Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutWideFast,
-                          art_portable_sput_wide.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt64Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutObjectFast,
-                          art_portable_sput_object.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kJavaObjectTy, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutBooleanFast,
-                          art_portable_sput_boolean.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt1Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutByteFast,
-                          art_portable_sput_byte.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt8Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutCharFast,
-                          art_portable_sput_char.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt16Ty, kField)))
-
-_EVAL_DEF_INTRINSICS_FUNC(StaticFieldPutShortFast,
-                          art_portable_sput_short.fast,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kJavaObjectTy, kInt32ConstantTy, kInt1ConstantTy, _JTYPE(kInt16Ty, kField)))
-
-// JavaObject* art_portable_load_declaring_class_ssb(Method* method)
-// Load the static storage base of the class that given method resides
-_EVAL_DEF_INTRINSICS_FUNC(LoadDeclaringClassSSB,
-                          art_portable_load_declaring_class_ssb,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kJavaMethodTy))
-
-// JavaObject* art_portable_init_and_load_class_ssb(uint32_t type_idx,
-//                                              Method* referrer,
-//                                              Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(InitializeAndLoadClassSSB,
-                          art_portable_init_and_load_class_ssb,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32ConstantTy, kJavaMethodTy, kJavaThreadTy))
-
-//----------------------------------------------------------------------------
-// High-level Array get/put
-//
-// Similar to art_portable_aget/aput_xxx, but checks not yet performed.
-// OptFlags contain info describing whether frontend has determined that
-// null check and/or array bounds check may be skipped.
-//
-// [type] void art_portable_hl_aget_[type](int optFlags, JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGet,
-                          art_portable_hl_aget,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetFloat,
-                          art_portable_hl_aget_float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kFloatTy,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetWide,
-                          art_portable_hl_aget_wide,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetDouble,
-                          art_portable_hl_aget_double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kDoubleTy,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetObject,
-                          art_portable_hl_aget_object,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetBoolean,
-                          art_portable_hl_aget_boolean,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetByte,
-                          art_portable_hl_aget_byte,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetChar,
-                          art_portable_hl_aget_char,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayGetShort,
-                          art_portable_hl_aget_short,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-// void art_portable_aput_[type](int optFlags, [type] value, JavaObject* array, uint32_t index)
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPut,
-                          art_portable_hl_aput,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutFloat,
-                          art_portable_hl_aput_float,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kFloatTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutWide,
-                          art_portable_hl_aput_wide,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt64Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutDouble,
-                          art_portable_hl_aput_double,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kDoubleTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutObject,
-                          art_portable_hl_aput_object,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kJavaObjectTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutBoolean,
-                          art_portable_hl_aput_boolean,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutByte,
-                          art_portable_hl_aput_byte,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutChar,
-                          art_portable_hl_aput_char,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLArrayPutShort,
-                          art_portable_hl_aput_short,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-//----------------------------------------------------------------------------
-// High-level Instance get/put
-//
-// Similar to art_portable_iget/iput_xxx, but checks not yet performed.
-// OptFlags contain info describing whether frontend has determined that
-// null check may be skipped.
-//
-// [type] void art_portable_hl_iget_[type](int optFlags, JavaObject* obj, uint32_t field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLIGet,
-                          art_portable_hl_iget,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetFloat,
-                          art_portable_hl_iget_float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kFloatTy,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetWide,
-                          art_portable_hl_iget_wide,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetDouble,
-                          art_portable_hl_iget_double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kDoubleTy,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetObject,
-                          art_portable_hl_iget_object,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetBoolean,
-                          art_portable_hl_iget_boolean,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetByte,
-                          art_portable_hl_iget_byte,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetChar,
-                          art_portable_hl_iget_char,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIGetShort,
-                          art_portable_hl_iget_short,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG3(kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-// void art_portable_iput_[type](int optFlags, [type] value, JavaObject* obj, uint32_t field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLIPut,
-                          art_portable_hl_iput,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutFloat,
-                          art_portable_hl_iput_float,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kFloatTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutWide,
-                          art_portable_hl_iput_wide,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt64Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutDouble,
-                          art_portable_hl_iput_double,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kDoubleTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutObject,
-                          art_portable_hl_iput_object,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kJavaObjectTy, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutBoolean,
-                          art_portable_hl_iput_boolean,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutByte,
-                          art_portable_hl_iput_byte,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutChar,
-                          art_portable_hl_iput_char,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(HLIPutShort,
-                          art_portable_hl_iput_short,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG4(kInt32Ty, kInt32Ty, kJavaObjectTy, kInt32Ty))
-
-//----------------------------------------------------------------------------
-// High-level Invokes (fast-path determination not yet performed)
-//
-// NOTE: We expect these intrinsics to be temporary.  Once calling conventions are
-//       fully merged, the unified front end will lower down to the
-//       InvokeRetxxx() intrinsics in the next section and these will be
-//       removed.
-//
-// arg0: InvokeType [ignored if FilledNewArray]
-// arg1: method_idx [ignored if FilledNewArray]
-// arg2: optimization_flags (primary to note whether null checking is needed)
-// [arg3..argN]: actual arguments
-//----------------------------------------------------------------------------
-// INVOKE method returns void
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeVoid,
-                          art_portable_hl_invoke.void,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns object
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeObj,
-                          art_portable_hl_invoke.obj,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns int
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeInt,
-                          art_portable_hl_invoke.i32,
-                          kAttrNone,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns float
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeFloat,
-                          art_portable_hl_invoke.f32,
-                          kAttrNone,
-                          kFloatTy,
-                          _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns long
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeLong,
-                          art_portable_hl_invoke.i64,
-                          kAttrNone,
-                          kInt64Ty,
-                          _EXPAND_ARG1(kVarArgTy))
-
-// INVOKE method returns double
-_EVAL_DEF_INTRINSICS_FUNC(HLInvokeDouble,
-                          art_portable_hl_invoke.f64,
-                          kAttrNone,
-                          kDoubleTy,
-                          _EXPAND_ARG1(kVarArgTy))
-
-// FILLED_NEW_ARRAY returns object
-_EVAL_DEF_INTRINSICS_FUNC(HLFilledNewArray,
-                          art_portable_hl_filled_new_array,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kVarArgTy))
-
-//----------------------------------------------------------------------------
-// Invoke
-//----------------------------------------------------------------------------
-
-// Method* art_portable_find_static_method_with_access_check(uint32_t method_idx,
-//                                                       JavaObject* this,
-//                                                       Method* referrer,
-//                                                       Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindStaticMethodWithAccessCheck,
-                          art_portable_find_static_method_with_access_check,
-                          kAttrNone,
-                          kJavaMethodTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_direct_method_with_access_check(uint32_t method_idx,
-//                                                       JavaObject* this,
-//                                                       Method* referrer,
-//                                                       Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindDirectMethodWithAccessCheck,
-                          art_portable_find_direct_method_with_access_check,
-                          kAttrNone,
-                          kJavaMethodTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_virtual_method_with_access_check(uint32_t method_idx,
-//                                                        JavaObject* this,
-//                                                        Method* referrer,
-//                                                        Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindVirtualMethodWithAccessCheck,
-                          art_portable_find_virtual_method_with_access_check,
-                          kAttrNone,
-                          kJavaMethodTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_super_method_with_access_check(uint32_t method_idx,
-//                                                      JavaObject* this,
-//                                                      Method* referrer,
-//                                                      Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindSuperMethodWithAccessCheck,
-                          art_portable_find_super_method_with_access_check,
-                          kAttrNone,
-                          kJavaMethodTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_find_interface_method_with_access_check(uint32_t method_idx,
-//                                                          JavaObject* this,
-//                                                          Method* referrer,
-//                                                          Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(FindInterfaceMethodWithAccessCheck,
-                          art_portable_find_interface_method_with_access_check,
-                          kAttrNone,
-                          kJavaMethodTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// Method* art_portable_get_sd_callee_method_obj_addr(uint32_t method_idx)
-_EVAL_DEF_INTRINSICS_FUNC(GetSDCalleeMethodObjAddrFast,
-                          art_portable_get_sd_callee_method_obj_addr_fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaMethodTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// Method* art_portable_get_virtual_callee_method_obj_addr(uint32_t vtable_idx,
-//                                                     JavaObject* this)
-_EVAL_DEF_INTRINSICS_FUNC(GetVirtualCalleeMethodObjAddrFast,
-                          art_portable_get_virtual_callee_method_obj_addr_fast,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaMethodTy,
-                          _EXPAND_ARG2(kInt32ConstantTy, kJavaObjectTy))
-
-// Method* art_portable_get_interface_callee_method_obj_addr(uint32_t method_idx,
-//                                                       JavaObject* this,
-//                                                       Method* referrer,
-//                                                       Thread* thread)
-_EVAL_DEF_INTRINSICS_FUNC(GetInterfaceCalleeMethodObjAddrFast,
-                          art_portable_get_interface_callee_method_obj_addr_fast,
-                          kAttrNone,
-                          kJavaMethodTy,
-                          _EXPAND_ARG4(kInt32ConstantTy, kJavaObjectTy, kJavaMethodTy, kJavaThreadTy))
-
-// [type] art_portable_invoke.[type](Method* callee, ...)
-// INVOKE method returns void
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetVoid,
-                          art_portable_invoke.void,
-                          kAttrNone,
-                          kVoidTy,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type boolean
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetBoolean,
-                          art_portable_invoke.bool,
-                          kAttrNone,
-                          kInt1Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type byte
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetByte,
-                          art_portable_invoke.byte,
-                          kAttrNone,
-                          kInt8Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type char
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetChar,
-                          art_portable_invoke.char,
-                          kAttrNone,
-                          kInt16Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type short
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetShort,
-                          art_portable_invoke.short,
-                          kAttrNone,
-                          kInt16Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type int
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetInt,
-                          art_portable_invoke.int,
-                          kAttrNone,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type long
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetLong,
-                          art_portable_invoke.long,
-                          kAttrNone,
-                          kInt64Ty,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type float
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetFloat,
-                          art_portable_invoke.float,
-                          kAttrNone,
-                          kFloatTy,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type double
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetDouble,
-                          art_portable_invoke.double,
-                          kAttrNone,
-                          kDoubleTy,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-// INVOKE method returns the value of type "object"
-_EVAL_DEF_INTRINSICS_FUNC(InvokeRetObject,
-                          art_portable_invoke.object,
-                          kAttrNone,
-                          kJavaObjectTy,
-                          _EXPAND_ARG2(kJavaMethodTy, kVarArgTy))
-
-//----------------------------------------------------------------------------
-// Math
-//----------------------------------------------------------------------------
-
-// int art_portable_{div,rem}_int(int a, int b)
-_EVAL_DEF_INTRINSICS_FUNC(DivInt,
-                          art_portable_div_int,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(RemInt,
-                          art_portable_rem_int,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// long art_portable_{div,rem}_long(long a, long b)
-_EVAL_DEF_INTRINSICS_FUNC(DivLong,
-                          art_portable_div_long,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG2(kInt64Ty, kInt64Ty))
-
-_EVAL_DEF_INTRINSICS_FUNC(RemLong,
-                          art_portable_rem_long,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG2(kInt64Ty, kInt64Ty))
-
-// int64_t art_portable_d2l(double f)
-_EVAL_DEF_INTRINSICS_FUNC(D2L,
-                          art_portable_d2l,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG1(kDoubleTy))
-
-// int32_t art_portable_d2l(double f)
-_EVAL_DEF_INTRINSICS_FUNC(D2I,
-                          art_portable_d2i,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kDoubleTy))
-
-// int64_t art_portable_f2l(float f)
-_EVAL_DEF_INTRINSICS_FUNC(F2L,
-                          art_portable_f2l,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG1(kFloatTy))
-
-// int32_t art_portable_f2i(float f)
-_EVAL_DEF_INTRINSICS_FUNC(F2I,
-                          art_portable_f2i,
-                          kAttrReadNone | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kFloatTy))
-
-//----------------------------------------------------------------------------
-// sput intrinsics to assist MIR to Greenland_ir conversion.
-// "HL" versions - will be deprecated when fast/slow path handling done
-// in the common frontend.
-//----------------------------------------------------------------------------
-
-// void sput_hl(int field_idx, int val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSput,
-                          art_portable_hl_sput,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_object(int field_idx, object* val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputObject,
-                          art_portable_hl_sput_object,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-// void sput_hl_boolean(int field_idx, kInt1Ty)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputBoolean,
-                          art_portable_hl_sput_boolean,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_byte(int field_idx, int val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputByte,
-                          art_portable_hl_sput_byte,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_char(int field_idx, kInt16Ty val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputChar,
-                          art_portable_hl_sput_char,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_short(int field_idx, int val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputShort,
-                          art_portable_hl_sput_short,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt32Ty))
-
-// void sput_hl_wide(int field_idx, long val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputWide,
-                          art_portable_hl_sput_wide,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kInt64Ty))
-
-// void sput_hl_double(int field_idx, double val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputDouble,
-                          art_portable_hl_sput_double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kDoubleTy))
-
-// void sput_hl_float(int field_idx, float val)
-_EVAL_DEF_INTRINSICS_FUNC(HLSputFloat,
-                          art_portable_hl_sput_float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kFloatTy))
-
-//----------------------------------------------------------------------------
-// sget intrinsics to assist MIR to Greenland_ir conversion.
-// "HL" versions - will be deprecated when fast/slow path handling done
-// in the common frontend.
-//----------------------------------------------------------------------------
-
-// int sget_hl(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSget,
-                          art_portable_hl_sget,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// object* sget_hl_object(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetObject,
-                          art_portable_hl_sget_object,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// boolean sget_hl_boolean(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetBoolean,
-                          art_portable_hl_sget_boolean,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// byte sget_hl_byte(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetByte,
-                          art_portable_hl_sget_byte,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_char(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetChar,
-                          art_portable_hl_sget_char,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_short(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetShort,
-                          art_portable_hl_sget_short,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_wide(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetWide,
-                          art_portable_hl_sget_wide,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_double(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetDouble,
-                          art_portable_hl_sget_double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kDoubleTy,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// char sget_hl_float(int field_idx)
-_EVAL_DEF_INTRINSICS_FUNC(HLSgetFloat,
-                          art_portable_hl_sget_float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kFloatTy,
-                          _EXPAND_ARG1(kInt32Ty))
-//----------------------------------------------------------------------------
-// Monitor enter/exit
-//----------------------------------------------------------------------------
-// uint32_t art_portable_monitor_enter(int optFlags, JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(MonitorEnter,
-                          art_portable_monitor_enter,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-// uint32_t art_portable_monitor_exit(int optFlags, JavaObject* obj)
-_EVAL_DEF_INTRINSICS_FUNC(MonitorExit,
-                          art_portable_monitor_exit,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32Ty, kJavaObjectTy))
-
-//----------------------------------------------------------------------------
-// Shadow Frame
-//----------------------------------------------------------------------------
-
-// void art_portable_alloca_shadow_frame(int num_entry)
-_EVAL_DEF_INTRINSICS_FUNC(AllocaShadowFrame,
-                          art_portable_alloca_shadow_frame,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-// void art_portable_set_vreg(int entry_idx, ...)
-_EVAL_DEF_INTRINSICS_FUNC(SetVReg,
-                          art_portable_set_vreg,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG2(kInt32ConstantTy, kVarArgTy))
-
-// void art_portable_pop_shadow_frame()
-_EVAL_DEF_INTRINSICS_FUNC(PopShadowFrame,
-                          art_portable_pop_shadow_frame,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG0())
-
-// void art_portable_update_dex_pc(uint32_t dex_pc)
-_EVAL_DEF_INTRINSICS_FUNC(UpdateDexPC,
-                          art_portable_update_dex_pc,
-                          kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG1(kInt32ConstantTy))
-
-//----------------------------------------------------------------------------
-// FP Comparison
-//----------------------------------------------------------------------------
-// int cmpl_float(float, float)
-_EVAL_DEF_INTRINSICS_FUNC(CmplFloat,
-                          art_portable_cmpl_float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kFloatTy, kFloatTy))
-
-// int cmpg_float(float, float)
-_EVAL_DEF_INTRINSICS_FUNC(CmpgFloat,
-                          art_portable_cmpg_float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kFloatTy, kFloatTy))
-
-// int cmpl_double(double, double)
-_EVAL_DEF_INTRINSICS_FUNC(CmplDouble,
-                          art_portable_cmpl_double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kDoubleTy, kDoubleTy))
-
-// int cmpg_double(double, double)
-_EVAL_DEF_INTRINSICS_FUNC(CmpgDouble,
-                          art_portable_cmpg_double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kDoubleTy, kDoubleTy))
-
-//----------------------------------------------------------------------------
-// Long Comparison
-//----------------------------------------------------------------------------
-// int cmp_long(long, long)
-_EVAL_DEF_INTRINSICS_FUNC(CmpLong,
-                          art_portable_cmp_long,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt64Ty, kInt64Ty))
-
-//----------------------------------------------------------------------------
-// Const intrinsics to assist MIR to Greenland_ir conversion.  Should not materialize
-// For simplicity, all use integer input
-//----------------------------------------------------------------------------
-// int const_int(int)
-_EVAL_DEF_INTRINSICS_FUNC(ConstInt,
-                          art_portable_const_int,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// JavaObject* const_obj(int)
-_EVAL_DEF_INTRINSICS_FUNC(ConstObj,
-                          art_portable_const_obj,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// long const_long(long)
-_EVAL_DEF_INTRINSICS_FUNC(ConstLong,
-                          art_portable_const_long,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG1(kInt64Ty))
-
-// float const_float(int)
-_EVAL_DEF_INTRINSICS_FUNC(ConstFloat,
-                          art_portable_const_Float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kFloatTy,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// double const_double(long)
-_EVAL_DEF_INTRINSICS_FUNC(ConstDouble,
-                          art_portable_const_Double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kDoubleTy,
-                          _EXPAND_ARG1(kInt64Ty))
-
-
-//----------------------------------------------------------------------------
-// Copy intrinsics to assist MIR to Greenland_ir conversion.  Should not materialize
-//----------------------------------------------------------------------------
-
-// void method_info(void)
-_EVAL_DEF_INTRINSICS_FUNC(MethodInfo,
-                          art_portable_method_info,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG0())
-
-// int copy_int(int)
-_EVAL_DEF_INTRINSICS_FUNC(CopyInt,
-                          art_portable_copy_int,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// JavaObject* copy_obj(obj)
-_EVAL_DEF_INTRINSICS_FUNC(CopyObj,
-                          art_portable_copy_obj,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kJavaObjectTy,
-                          _EXPAND_ARG1(kJavaObjectTy))
-
-// long copy_long(long)
-_EVAL_DEF_INTRINSICS_FUNC(CopyLong,
-                          art_portable_copy_long,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG1(kInt64Ty))
-
-// float copy_float(float)
-_EVAL_DEF_INTRINSICS_FUNC(CopyFloat,
-                          art_portable_copy_Float,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kFloatTy,
-                          _EXPAND_ARG1(kFloatTy))
-
-// double copy_double(double)
-_EVAL_DEF_INTRINSICS_FUNC(CopyDouble,
-                          art_portable_copy_Double,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kDoubleTy,
-                          _EXPAND_ARG1(kDoubleTy))
-
-//----------------------------------------------------------------------------
-// Shift intrinsics.  Shift semantics for Dalvik are a bit different than
-// the llvm shift operators.  For 32-bit shifts, the shift count is constrained
-// to the range of 0..31, while for 64-bit shifts we limit to 0..63.
-// Further, the shift count for Long shifts in Dalvik is 32 bits, while
-// llvm requires a 64-bit shift count. For GBC, we represent shifts as an
-//  intrinsic to allow most efficient target-dependent lowering.
-//----------------------------------------------------------------------------
-// long shl_long(long,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHLLong,
-                          art_portable_shl_long,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG2(kInt64Ty,kInt32Ty))
-// long shr_long(long,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHRLong,
-                          art_portable_shr_long,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG2(kInt64Ty,kInt32Ty))
-// long ushr_long(long,int)
-_EVAL_DEF_INTRINSICS_FUNC(USHRLong,
-                          art_portable_ushl_long,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt64Ty,
-                          _EXPAND_ARG2(kInt64Ty,kInt32Ty))
-// int shl_int(int,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHLInt,
-                          art_portable_shl_int,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty,kInt32Ty))
-// long shr_int(int,int)
-_EVAL_DEF_INTRINSICS_FUNC(SHRInt,
-                          art_portable_shr_int,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty,kInt32Ty))
-// int ushr_long(int,int)
-_EVAL_DEF_INTRINSICS_FUNC(USHRInt,
-                          art_portable_ushl_int,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG2(kInt32Ty,kInt32Ty))
-//----------------------------------------------------------------------------
-// Conversion instrinsics.  Note: these should eventually be removed.  We
-// can express these directly in bitcode, but by using intrinsics the
-// Quick compiler can be more efficient.  Some extra optimization infrastructure
-// will have to be developed to undo the bitcode verbosity when these are
-// done inline.
-//----------------------------------------------------------------------------
-// int int_to_byte(int)
-_EVAL_DEF_INTRINSICS_FUNC(IntToByte,
-                          art_portable_int_to_byte,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// int int_to_char(int)
-_EVAL_DEF_INTRINSICS_FUNC(IntToChar,
-                          art_portable_int_to_char,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-// int int_to_short(int)
-_EVAL_DEF_INTRINSICS_FUNC(IntToShort,
-                          art_portable_int_to_short,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kInt32Ty,
-                          _EXPAND_ARG1(kInt32Ty))
-
-//----------------------------------------------------------------------------
-// Memory barrier
-//----------------------------------------------------------------------------
-// void constructor_barrier()
-_EVAL_DEF_INTRINSICS_FUNC(ConstructorBarrier,
-                          art_portable_constructor_barrier,
-                          kAttrReadOnly | kAttrNoThrow,
-                          kVoidTy,
-                          _EXPAND_ARG0())
-
-// Clean up all internal used macros
-#undef _EXPAND_ARG0
-#undef _EXPAND_ARG1
-#undef _EXPAND_ARG2
-#undef _EXPAND_ARG3
-#undef _EXPAND_ARG4
-#undef _EXPAND_ARG5
-
-#undef _JTYPE_OF_kInt1Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt8Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt16Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt32Ty_UNDER_kArray
-#undef _JTYPE_OF_kInt64Ty_UNDER_kArray
-#undef _JTYPE_OF_kJavaObjectTy_UNDER_kArray
-
-#undef _JTYPE_OF_kInt1Ty_UNDER_kField
-#undef _JTYPE_OF_kInt8Ty_UNDER_kField
-#undef _JTYPE_OF_kInt16Ty_UNDER_kField
-#undef _JTYPE_OF_kInt32Ty_UNDER_kField
-#undef _JTYPE_OF_kInt64Ty_UNDER_kField
-#undef _JTYPE_OF_kJavaObjectTy_UNDER_kField
-
-#undef DEF_INTRINSICS_FUNC
diff --git a/compiler/llvm/intrinsic_helper.cc b/compiler/llvm/intrinsic_helper.cc
deleted file mode 100644
index e5e7998..0000000
--- a/compiler/llvm/intrinsic_helper.cc
+++ /dev/null
@@ -1,178 +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 "intrinsic_helper.h"
-
-#include "ir_builder.h"
-
-#include <llvm/IR/Attributes.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/IRBuilder.h>
-#include <llvm/IR/Intrinsics.h>
-
-namespace art {
-namespace llvm {
-
-const IntrinsicHelper::IntrinsicInfo IntrinsicHelper::Info[] = {
-#define DEF_INTRINSICS_FUNC(_, NAME, ATTR, RET_TYPE, ARG1_TYPE, ARG2_TYPE, \
-                                                     ARG3_TYPE, ARG4_TYPE, \
-                                                     ARG5_TYPE) \
-  { #NAME, ATTR, RET_TYPE, { ARG1_TYPE, ARG2_TYPE, \
-                             ARG3_TYPE, ARG4_TYPE, \
-                             ARG5_TYPE} },
-#include "intrinsic_func_list.def"
-};
-
-static ::llvm::Type* GetLLVMTypeOfIntrinsicValType(IRBuilder& irb,
-                                                   IntrinsicHelper::IntrinsicValType type) {
-  switch (type) {
-    case IntrinsicHelper::kVoidTy: {
-      return irb.getVoidTy();
-    }
-    case IntrinsicHelper::kJavaObjectTy: {
-      return irb.getJObjectTy();
-    }
-    case IntrinsicHelper::kJavaMethodTy: {
-      return irb.getJMethodTy();
-    }
-    case IntrinsicHelper::kJavaThreadTy: {
-      return irb.getJThreadTy();
-    }
-    case IntrinsicHelper::kInt1Ty:
-    case IntrinsicHelper::kInt1ConstantTy: {
-      return irb.getInt1Ty();
-    }
-    case IntrinsicHelper::kInt8Ty:
-    case IntrinsicHelper::kInt8ConstantTy: {
-      return irb.getInt8Ty();
-    }
-    case IntrinsicHelper::kInt16Ty:
-    case IntrinsicHelper::kInt16ConstantTy: {
-      return irb.getInt16Ty();
-    }
-    case IntrinsicHelper::kInt32Ty:
-    case IntrinsicHelper::kInt32ConstantTy: {
-      return irb.getInt32Ty();
-    }
-    case IntrinsicHelper::kInt64Ty:
-    case IntrinsicHelper::kInt64ConstantTy: {
-      return irb.getInt64Ty();
-    }
-    case IntrinsicHelper::kFloatTy:
-    case IntrinsicHelper::kFloatConstantTy: {
-      return irb.getFloatTy();
-    }
-    case IntrinsicHelper::kDoubleTy:
-    case IntrinsicHelper::kDoubleConstantTy: {
-      return irb.getDoubleTy();
-    }
-    case IntrinsicHelper::kNone:
-    case IntrinsicHelper::kVarArgTy:
-    default: {
-      LOG(FATAL) << "Invalid intrinsic type " << type << "to get LLVM type!";
-      return NULL;
-    }
-  }
-  // unreachable
-}
-
-IntrinsicHelper::IntrinsicHelper(::llvm::LLVMContext& context,
-                                 ::llvm::Module& module) {
-  IRBuilder irb(context, module, *this);
-
-  ::memset(intrinsic_funcs_, 0, sizeof(intrinsic_funcs_));
-
-  // This loop does the following things:
-  // 1. Introduce the intrinsic function into the module
-  // 2. Add "nocapture" and "noalias" attribute to the arguments in all
-  //    intrinsics functions.
-  // 3. Initialize intrinsic_funcs_map_.
-  for (unsigned i = 0; i < MaxIntrinsicId; i++) {
-    IntrinsicId id = static_cast<IntrinsicId>(i);
-    const IntrinsicInfo& info = Info[i];
-
-    // Parse and construct the argument type from IntrinsicInfo
-    ::llvm::Type* arg_type[kIntrinsicMaxArgc];
-    unsigned num_args = 0;
-    bool is_var_arg = false;
-    for (unsigned arg_iter = 0; arg_iter < kIntrinsicMaxArgc; arg_iter++) {
-      IntrinsicValType type = info.arg_type_[arg_iter];
-
-      if (type == kNone) {
-        break;
-      } else if (type == kVarArgTy) {
-        // Variable argument type must be the last argument
-        is_var_arg = true;
-        break;
-      }
-
-      arg_type[num_args++] = GetLLVMTypeOfIntrinsicValType(irb, type);
-    }
-
-    // Construct the function type
-    ::llvm::Type* ret_type =
-        GetLLVMTypeOfIntrinsicValType(irb, info.ret_val_type_);
-
-    ::llvm::FunctionType* type =
-        ::llvm::FunctionType::get(ret_type,
-                                  ::llvm::ArrayRef< ::llvm::Type*>(arg_type, num_args),
-                                  is_var_arg);
-
-    // Declare the function
-    ::llvm::Function *fn = ::llvm::Function::Create(type,
-                                                    ::llvm::Function::ExternalLinkage,
-                                                     info.name_, &module);
-
-    if (info.attr_ & kAttrReadOnly) {
-        fn->setOnlyReadsMemory();
-    }
-    if (info.attr_ & kAttrReadNone) {
-        fn->setDoesNotAccessMemory();
-    }
-    // None of the intrinsics throws exception
-    fn->setDoesNotThrow();
-
-    intrinsic_funcs_[id] = fn;
-
-    DCHECK_NE(fn, static_cast< ::llvm::Function*>(NULL)) << "Intrinsic `"
-        << GetName(id) << "' was not defined!";
-
-    // Add "noalias" and "nocapture" attribute to all arguments of pointer type
-    for (::llvm::Function::arg_iterator arg_iter = fn->arg_begin(),
-            arg_end = fn->arg_end(); arg_iter != arg_end; arg_iter++) {
-      if (arg_iter->getType()->isPointerTy()) {
-        std::vector< ::llvm::Attribute::AttrKind> attributes;
-        attributes.push_back(::llvm::Attribute::NoCapture);
-        attributes.push_back(::llvm::Attribute::NoAlias);
-        ::llvm::AttributeSet attribute_set = ::llvm::AttributeSet::get(fn->getContext(),
-                                                                       arg_iter->getArgNo(),
-                                                                       attributes);
-        arg_iter->addAttr(attribute_set);
-      }
-    }
-
-    // Insert the newly created intrinsic to intrinsic_funcs_map_
-    if (!intrinsic_funcs_map_.insert(std::make_pair(fn, id)).second) {
-      LOG(FATAL) << "Duplicate entry in intrinsic functions map?";
-    }
-  }
-
-  return;
-}
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/intrinsic_helper.h b/compiler/llvm/intrinsic_helper.h
deleted file mode 100644
index 657db40..0000000
--- a/compiler/llvm/intrinsic_helper.h
+++ /dev/null
@@ -1,157 +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_COMPILER_LLVM_INTRINSIC_HELPER_H_
-#define ART_COMPILER_LLVM_INTRINSIC_HELPER_H_
-
-#include "base/logging.h"
-
-#include <llvm/ADT/DenseMap.h>
-
-namespace llvm {
-  class Function;
-  class FunctionType;
-  class LLVMContext;
-  class Module;
-}  // namespace llvm
-
-namespace art {
-namespace llvm {
-
-class IRBuilder;
-
-class IntrinsicHelper {
- public:
-  enum IntrinsicId {
-#define DEF_INTRINSICS_FUNC(ID, ...) ID,
-#include "intrinsic_func_list.def"
-    MaxIntrinsicId,
-
-    // Pseudo-intrinsics Id
-    UnknownId
-  };
-
-  enum IntrinsicAttribute {
-    kAttrNone     = 0,
-
-    // Intrinsic that neither modified the memory state nor refer to the global
-    // state
-    kAttrReadNone = 1 << 0,
-
-    // Intrinsic that doesn't modify the memory state. Note that one should set
-    // this flag carefully when the intrinsic may throw exception. Since the
-    // thread state is implicitly modified when an exception is thrown.
-    kAttrReadOnly = 1 << 1,
-
-    // Note that intrinsic without kAttrNoThrow and kAttrDoThrow set means that
-    // intrinsic generates exception in some cases
-
-    // Intrinsic that never generates exception
-    kAttrNoThrow  = 1 << 2,
-    // Intrinsic that always generate exception
-    kAttrDoThrow  = 1 << 3,
-  };
-
-  enum IntrinsicValType {
-    kNone,
-
-    kVoidTy,
-
-    kJavaObjectTy,
-    kJavaMethodTy,
-    kJavaThreadTy,
-
-    kInt1Ty,
-    kInt8Ty,
-    kInt16Ty,
-    kInt32Ty,
-    kInt64Ty,
-    kFloatTy,
-    kDoubleTy,
-
-    kInt1ConstantTy,
-    kInt8ConstantTy,
-    kInt16ConstantTy,
-    kInt32ConstantTy,
-    kInt64ConstantTy,
-    kFloatConstantTy,
-    kDoubleConstantTy,
-
-    kVarArgTy,
-  };
-
-  enum {
-    kIntrinsicMaxArgc = 5
-  };
-
-  typedef struct IntrinsicInfo {
-    const char* name_;
-    unsigned attr_;
-    IntrinsicValType ret_val_type_;
-    IntrinsicValType arg_type_[kIntrinsicMaxArgc];
-  } IntrinsicInfo;
-
- private:
-  static const IntrinsicInfo Info[];
-
- public:
-  static const IntrinsicInfo& GetInfo(IntrinsicId id) {
-    DCHECK(id >= 0 && id < MaxIntrinsicId) << "Unknown ART intrinsics ID: "
-                                           << id;
-    return Info[id];
-  }
-
-  static const char* GetName(IntrinsicId id) {
-    return (id <= MaxIntrinsicId) ? GetInfo(id).name_ : "InvalidIntrinsic";
-  }
-
-  static unsigned GetAttr(IntrinsicId id) {
-    return GetInfo(id).attr_;
-  }
-
- public:
-  IntrinsicHelper(::llvm::LLVMContext& context, ::llvm::Module& module);
-
-  ::llvm::Function* GetIntrinsicFunction(IntrinsicId id) {
-    DCHECK(id >= 0 && id < MaxIntrinsicId) << "Unknown ART intrinsics ID: "
-                                           << id;
-    return intrinsic_funcs_[id];
-  }
-
-  IntrinsicId GetIntrinsicId(const ::llvm::Function* func) const {
-    ::llvm::DenseMap<const ::llvm::Function*, IntrinsicId>::const_iterator
-        i = intrinsic_funcs_map_.find(func);
-    if (i == intrinsic_funcs_map_.end()) {
-      return UnknownId;
-    } else {
-      return i->second;
-    }
-  }
-
- private:
-  // FIXME: "+1" is to workaround the GCC bugs:
-  // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
-  // Remove this when uses newer GCC (> 4.4.3)
-  ::llvm::Function* intrinsic_funcs_[MaxIntrinsicId + 1];
-
-  // Map a llvm::Function to its intrinsic id
-  ::llvm::DenseMap<const ::llvm::Function*, IntrinsicId> intrinsic_funcs_map_;
-};
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_INTRINSIC_HELPER_H_
diff --git a/compiler/llvm/ir_builder.cc b/compiler/llvm/ir_builder.cc
deleted file mode 100644
index 9644ebd..0000000
--- a/compiler/llvm/ir_builder.cc
+++ /dev/null
@@ -1,130 +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 "ir_builder.h"
-
-#include "base/stringprintf.h"
-
-#include <llvm/IR/Module.h>
-
-namespace art {
-namespace llvm {
-
-
-//----------------------------------------------------------------------------
-// General
-//----------------------------------------------------------------------------
-
-IRBuilder::IRBuilder(::llvm::LLVMContext& context, ::llvm::Module& module,
-                     IntrinsicHelper& intrinsic_helper)
-    : LLVMIRBuilder(context), module_(&module), mdb_(context), java_object_type_(NULL),
-      java_method_type_(NULL), java_thread_type_(NULL), intrinsic_helper_(intrinsic_helper) {
-  // Get java object type from module
-  ::llvm::Type* jobject_struct_type = module.getTypeByName("JavaObject");
-  CHECK(jobject_struct_type != NULL);
-  java_object_type_ = jobject_struct_type->getPointerTo();
-
-  // If type of Method is not explicitly defined in the module, use JavaObject*
-  ::llvm::Type* type = module.getTypeByName("Method");
-  if (type != NULL) {
-    java_method_type_ = type->getPointerTo();
-  } else {
-    java_method_type_ = java_object_type_;
-  }
-
-  // If type of Thread is not explicitly defined in the module, use JavaObject*
-  type = module.getTypeByName("Thread");
-  if (type != NULL) {
-    java_thread_type_ = type->getPointerTo();
-  } else {
-    java_thread_type_ = java_object_type_;
-  }
-
-  // Create JEnv* type
-  ::llvm::Type* jenv_struct_type = ::llvm::StructType::create(context, "JEnv");
-  jenv_type_ = jenv_struct_type->getPointerTo();
-
-  // Get Art shadow frame struct type from module
-  art_frame_type_ = module.getTypeByName("ShadowFrame");
-  CHECK(art_frame_type_ != NULL);
-
-  runtime_support_ = NULL;
-}
-
-
-//----------------------------------------------------------------------------
-// Type Helper Function
-//----------------------------------------------------------------------------
-
-::llvm::Type* IRBuilder::getJType(JType jty) {
-  switch (jty) {
-  case kVoid:
-    return getJVoidTy();
-
-  case kBoolean:
-    return getJBooleanTy();
-
-  case kByte:
-    return getJByteTy();
-
-  case kChar:
-    return getJCharTy();
-
-  case kShort:
-    return getJShortTy();
-
-  case kInt:
-    return getJIntTy();
-
-  case kLong:
-    return getJLongTy();
-
-  case kFloat:
-    return getJFloatTy();
-
-  case kDouble:
-    return getJDoubleTy();
-
-  case kObject:
-    return getJObjectTy();
-
-  default:
-    LOG(FATAL) << "Unknown java type: " << jty;
-    return NULL;
-  }
-}
-
-::llvm::StructType* IRBuilder::getShadowFrameTy(uint32_t vreg_size) {
-  std::string name(StringPrintf("ShadowFrame%u", vreg_size));
-
-  // Try to find the existing struct type definition
-  if (::llvm::Type* type = module_->getTypeByName(name)) {
-    CHECK(::llvm::isa< ::llvm::StructType>(type));
-    return static_cast< ::llvm::StructType*>(type);
-  }
-
-  // Create new struct type definition
-  ::llvm::Type* elem_types[] = {
-    art_frame_type_,
-    ::llvm::ArrayType::get(getInt32Ty(), vreg_size),
-  };
-
-  return ::llvm::StructType::create(elem_types, name);
-}
-
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/ir_builder.h b/compiler/llvm/ir_builder.h
deleted file mode 100644
index 990ba02..0000000
--- a/compiler/llvm/ir_builder.h
+++ /dev/null
@@ -1,486 +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_COMPILER_LLVM_IR_BUILDER_H_
-#define ART_COMPILER_LLVM_IR_BUILDER_H_
-
-#include "backend_types.h"
-#include "dex/compiler_enums.h"
-#include "intrinsic_helper.h"
-#include "md_builder.h"
-#include "runtime_support_builder.h"
-#include "runtime_support_llvm_func.h"
-
-#include <llvm/IR/Constants.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/IRBuilder.h>
-#include <llvm/IR/LLVMContext.h>
-#include <llvm/IR/Type.h>
-#include <llvm/Support/NoFolder.h>
-
-#include <stdint.h>
-
-
-namespace art {
-namespace llvm {
-
-class InserterWithDexOffset : public ::llvm::IRBuilderDefaultInserter<true> {
-  public:
-    InserterWithDexOffset() : node_(NULL) {}
-
-    void InsertHelper(::llvm::Instruction *I, const ::llvm::Twine &Name,
-                      ::llvm::BasicBlock *BB,
-                      ::llvm::BasicBlock::iterator InsertPt) const {
-      ::llvm::IRBuilderDefaultInserter<true>::InsertHelper(I, Name, BB, InsertPt);
-      if (node_ != NULL) {
-        I->setMetadata("DexOff", node_);
-      }
-    }
-
-    void SetDexOffset(::llvm::MDNode* node) {
-      node_ = node;
-    }
-  private:
-    ::llvm::MDNode* node_;
-};
-
-typedef ::llvm::IRBuilder<true, ::llvm::ConstantFolder, InserterWithDexOffset> LLVMIRBuilder;
-// NOTE: Here we define our own LLVMIRBuilder type alias, so that we can
-// switch "preserveNames" template parameter easily.
-
-
-class IRBuilder : public LLVMIRBuilder {
- public:
-  //--------------------------------------------------------------------------
-  // General
-  //--------------------------------------------------------------------------
-
-  IRBuilder(::llvm::LLVMContext& context, ::llvm::Module& module,
-            IntrinsicHelper& intrinsic_helper);
-
-
-  //--------------------------------------------------------------------------
-  // Extend load & store for TBAA
-  //--------------------------------------------------------------------------
-
-  ::llvm::LoadInst* CreateLoad(::llvm::Value* ptr, ::llvm::MDNode* tbaa_info) {
-    ::llvm::LoadInst* inst = LLVMIRBuilder::CreateLoad(ptr);
-    inst->setMetadata(::llvm::LLVMContext::MD_tbaa, tbaa_info);
-    return inst;
-  }
-
-  ::llvm::StoreInst* CreateStore(::llvm::Value* val, ::llvm::Value* ptr, ::llvm::MDNode* tbaa_info) {
-    ::llvm::StoreInst* inst = LLVMIRBuilder::CreateStore(val, ptr);
-    inst->setMetadata(::llvm::LLVMContext::MD_tbaa, tbaa_info);
-    return inst;
-  }
-
-  ::llvm::AtomicCmpXchgInst*
-  CreateAtomicCmpXchgInst(::llvm::Value* ptr, ::llvm::Value* cmp, ::llvm::Value* val,
-                          ::llvm::MDNode* tbaa_info) {
-    ::llvm::AtomicCmpXchgInst* inst =
-        LLVMIRBuilder::CreateAtomicCmpXchg(ptr, cmp, val, ::llvm::Acquire);
-    inst->setMetadata(::llvm::LLVMContext::MD_tbaa, tbaa_info);
-    return inst;
-  }
-
-  //--------------------------------------------------------------------------
-  // Extend memory barrier
-  //--------------------------------------------------------------------------
-  void CreateMemoryBarrier(MemBarrierKind barrier_kind) {
-    // TODO: select atomic ordering according to given barrier kind.
-    CreateFence(::llvm::SequentiallyConsistent);
-  }
-
-  //--------------------------------------------------------------------------
-  // TBAA
-  //--------------------------------------------------------------------------
-
-  // TODO: After we design the non-special TBAA info, re-design the TBAA interface.
-  ::llvm::LoadInst* CreateLoad(::llvm::Value* ptr, TBAASpecialType special_ty) {
-    return CreateLoad(ptr, mdb_.GetTBAASpecialType(special_ty));
-  }
-
-  ::llvm::StoreInst* CreateStore(::llvm::Value* val, ::llvm::Value* ptr, TBAASpecialType special_ty) {
-    DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
-    return CreateStore(val, ptr, mdb_.GetTBAASpecialType(special_ty));
-  }
-
-  ::llvm::LoadInst* CreateLoad(::llvm::Value* ptr, TBAASpecialType special_ty, JType j_ty) {
-    return CreateLoad(ptr, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
-  }
-
-  ::llvm::StoreInst* CreateStore(::llvm::Value* val, ::llvm::Value* ptr,
-                               TBAASpecialType special_ty, JType j_ty) {
-    DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
-    return CreateStore(val, ptr, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
-  }
-
-  ::llvm::LoadInst* LoadFromObjectOffset(::llvm::Value* object_addr,
-                                       int64_t offset,
-                                       ::llvm::Type* type,
-                                       TBAASpecialType special_ty) {
-    return LoadFromObjectOffset(object_addr, offset, type, mdb_.GetTBAASpecialType(special_ty));
-  }
-
-  void StoreToObjectOffset(::llvm::Value* object_addr,
-                           int64_t offset,
-                           ::llvm::Value* new_value,
-                           TBAASpecialType special_ty) {
-    DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
-    StoreToObjectOffset(object_addr, offset, new_value, mdb_.GetTBAASpecialType(special_ty));
-  }
-
-  ::llvm::LoadInst* LoadFromObjectOffset(::llvm::Value* object_addr,
-                                       int64_t offset,
-                                       ::llvm::Type* type,
-                                       TBAASpecialType special_ty, JType j_ty) {
-    return LoadFromObjectOffset(object_addr, offset, type, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
-  }
-
-  void StoreToObjectOffset(::llvm::Value* object_addr,
-                           int64_t offset,
-                           ::llvm::Value* new_value,
-                           TBAASpecialType special_ty, JType j_ty) {
-    DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
-    StoreToObjectOffset(object_addr, offset, new_value, mdb_.GetTBAAMemoryJType(special_ty, j_ty));
-  }
-
-  ::llvm::AtomicCmpXchgInst*
-  CompareExchangeObjectOffset(::llvm::Value* object_addr,
-                              int64_t offset,
-                              ::llvm::Value* cmp_value,
-                              ::llvm::Value* new_value,
-                              TBAASpecialType special_ty) {
-    DCHECK_NE(special_ty, kTBAAConstJObject) << "ConstJObject is read only!";
-    return CompareExchangeObjectOffset(object_addr, offset, cmp_value, new_value,
-                                       mdb_.GetTBAASpecialType(special_ty));
-  }
-
-  void SetTBAA(::llvm::Instruction* inst, TBAASpecialType special_ty) {
-    inst->setMetadata(::llvm::LLVMContext::MD_tbaa, mdb_.GetTBAASpecialType(special_ty));
-  }
-
-
-  //--------------------------------------------------------------------------
-  // Static Branch Prediction
-  //--------------------------------------------------------------------------
-
-  // Import the orignal conditional branch
-  using LLVMIRBuilder::CreateCondBr;
-  ::llvm::BranchInst* CreateCondBr(::llvm::Value *cond,
-                                 ::llvm::BasicBlock* true_bb,
-                                 ::llvm::BasicBlock* false_bb,
-                                 ExpectCond expect) {
-    ::llvm::BranchInst* branch_inst = CreateCondBr(cond, true_bb, false_bb);
-    if (false) {
-      // TODO: http://b/8511695 Restore branch weight metadata
-      branch_inst->setMetadata(::llvm::LLVMContext::MD_prof, mdb_.GetBranchWeights(expect));
-    }
-    return branch_inst;
-  }
-
-
-  //--------------------------------------------------------------------------
-  // Pointer Arithmetic Helper Function
-  //--------------------------------------------------------------------------
-
-  ::llvm::IntegerType* getPtrEquivIntTy() {
-    return getInt32Ty();
-  }
-
-  size_t getSizeOfPtrEquivInt() {
-    return 4;
-  }
-
-  ::llvm::ConstantInt* getSizeOfPtrEquivIntValue() {
-    return getPtrEquivInt(getSizeOfPtrEquivInt());
-  }
-
-  ::llvm::ConstantInt* getPtrEquivInt(int64_t i) {
-    return ::llvm::ConstantInt::get(getPtrEquivIntTy(), i);
-  }
-
-  ::llvm::Value* CreatePtrDisp(::llvm::Value* base,
-                             ::llvm::Value* offset,
-                             ::llvm::PointerType* ret_ty) {
-    ::llvm::Value* base_int = CreatePtrToInt(base, getPtrEquivIntTy());
-    ::llvm::Value* result_int = CreateAdd(base_int, offset);
-    ::llvm::Value* result = CreateIntToPtr(result_int, ret_ty);
-
-    return result;
-  }
-
-  ::llvm::Value* CreatePtrDisp(::llvm::Value* base,
-                             ::llvm::Value* bs,
-                             ::llvm::Value* count,
-                             ::llvm::Value* offset,
-                             ::llvm::PointerType* ret_ty) {
-    ::llvm::Value* block_offset = CreateMul(bs, count);
-    ::llvm::Value* total_offset = CreateAdd(block_offset, offset);
-
-    return CreatePtrDisp(base, total_offset, ret_ty);
-  }
-
-  ::llvm::LoadInst* LoadFromObjectOffset(::llvm::Value* object_addr,
-                                       int64_t offset,
-                                       ::llvm::Type* type,
-                                       ::llvm::MDNode* tbaa_info) {
-    // Convert offset to ::llvm::value
-    ::llvm::Value* llvm_offset = getPtrEquivInt(offset);
-    // Calculate the value's address
-    ::llvm::Value* value_addr = CreatePtrDisp(object_addr, llvm_offset, type->getPointerTo());
-    // Load
-    return CreateLoad(value_addr, tbaa_info);
-  }
-
-  void StoreToObjectOffset(::llvm::Value* object_addr,
-                           int64_t offset,
-                           ::llvm::Value* new_value,
-                           ::llvm::MDNode* tbaa_info) {
-    // Convert offset to ::llvm::value
-    ::llvm::Value* llvm_offset = getPtrEquivInt(offset);
-    // Calculate the value's address
-    ::llvm::Value* value_addr = CreatePtrDisp(object_addr,
-                                            llvm_offset,
-                                            new_value->getType()->getPointerTo());
-    // Store
-    CreateStore(new_value, value_addr, tbaa_info);
-  }
-
-  ::llvm::AtomicCmpXchgInst* CompareExchangeObjectOffset(::llvm::Value* object_addr,
-                                                       int64_t offset,
-                                                       ::llvm::Value* cmp_value,
-                                                       ::llvm::Value* new_value,
-                                                       ::llvm::MDNode* tbaa_info) {
-    // Convert offset to ::llvm::value
-    ::llvm::Value* llvm_offset = getPtrEquivInt(offset);
-    // Calculate the value's address
-    ::llvm::Value* value_addr = CreatePtrDisp(object_addr,
-                                            llvm_offset,
-                                            new_value->getType()->getPointerTo());
-    // Atomic compare and exchange
-    return CreateAtomicCmpXchgInst(value_addr, cmp_value, new_value, tbaa_info);
-  }
-
-
-  //--------------------------------------------------------------------------
-  // Runtime Helper Function
-  //--------------------------------------------------------------------------
-
-  RuntimeSupportBuilder& Runtime() {
-    return *runtime_support_;
-  }
-
-  // TODO: Deprecate
-  ::llvm::Function* GetRuntime(runtime_support::RuntimeId rt) {
-    return runtime_support_->GetRuntimeSupportFunction(rt);
-  }
-
-  // TODO: Deprecate
-  void SetRuntimeSupport(RuntimeSupportBuilder* runtime_support) {
-    // Can only set once. We can't do this on constructor, because RuntimeSupportBuilder needs
-    // IRBuilder.
-    if (runtime_support_ == NULL && runtime_support != NULL) {
-      runtime_support_ = runtime_support;
-    }
-  }
-
-
-  //--------------------------------------------------------------------------
-  // Type Helper Function
-  //--------------------------------------------------------------------------
-
-  ::llvm::Type* getJType(char shorty_jty) {
-    return getJType(GetJTypeFromShorty(shorty_jty));
-  }
-
-  ::llvm::Type* getJType(JType jty);
-
-  ::llvm::Type* getJVoidTy() {
-    return getVoidTy();
-  }
-
-  ::llvm::IntegerType* getJBooleanTy() {
-    return getInt8Ty();
-  }
-
-  ::llvm::IntegerType* getJByteTy() {
-    return getInt8Ty();
-  }
-
-  ::llvm::IntegerType* getJCharTy() {
-    return getInt16Ty();
-  }
-
-  ::llvm::IntegerType* getJShortTy() {
-    return getInt16Ty();
-  }
-
-  ::llvm::IntegerType* getJIntTy() {
-    return getInt32Ty();
-  }
-
-  ::llvm::IntegerType* getJLongTy() {
-    return getInt64Ty();
-  }
-
-  ::llvm::Type* getJFloatTy() {
-    return getFloatTy();
-  }
-
-  ::llvm::Type* getJDoubleTy() {
-    return getDoubleTy();
-  }
-
-  ::llvm::PointerType* getJObjectTy() {
-    return java_object_type_;
-  }
-
-  ::llvm::PointerType* getJMethodTy() {
-    return java_method_type_;
-  }
-
-  ::llvm::PointerType* getJThreadTy() {
-    return java_thread_type_;
-  }
-
-  ::llvm::Type* getArtFrameTy() {
-    return art_frame_type_;
-  }
-
-  ::llvm::PointerType* getJEnvTy() {
-    return jenv_type_;
-  }
-
-  ::llvm::Type* getJValueTy() {
-    // NOTE: JValue is an union type, which may contains boolean, byte, char,
-    // short, int, long, float, double, Object.  However, LLVM itself does
-    // not support union type, so we have to return a type with biggest size,
-    // then bitcast it before we use it.
-    return getJLongTy();
-  }
-
-  ::llvm::StructType* getShadowFrameTy(uint32_t vreg_size);
-
-
-  //--------------------------------------------------------------------------
-  // Constant Value Helper Function
-  //--------------------------------------------------------------------------
-
-  ::llvm::ConstantInt* getJBoolean(bool is_true) {
-    return (is_true) ? getTrue() : getFalse();
-  }
-
-  ::llvm::ConstantInt* getJByte(int8_t i) {
-    return ::llvm::ConstantInt::getSigned(getJByteTy(), i);
-  }
-
-  ::llvm::ConstantInt* getJChar(int16_t i) {
-    return ::llvm::ConstantInt::getSigned(getJCharTy(), i);
-  }
-
-  ::llvm::ConstantInt* getJShort(int16_t i) {
-    return ::llvm::ConstantInt::getSigned(getJShortTy(), i);
-  }
-
-  ::llvm::ConstantInt* getJInt(int32_t i) {
-    return ::llvm::ConstantInt::getSigned(getJIntTy(), i);
-  }
-
-  ::llvm::ConstantInt* getJLong(int64_t i) {
-    return ::llvm::ConstantInt::getSigned(getJLongTy(), i);
-  }
-
-  ::llvm::Constant* getJFloat(float f) {
-    return ::llvm::ConstantFP::get(getJFloatTy(), f);
-  }
-
-  ::llvm::Constant* getJDouble(double d) {
-    return ::llvm::ConstantFP::get(getJDoubleTy(), d);
-  }
-
-  ::llvm::ConstantPointerNull* getJNull() {
-    return ::llvm::ConstantPointerNull::get(getJObjectTy());
-  }
-
-  ::llvm::Constant* getJZero(char shorty_jty) {
-    return getJZero(GetJTypeFromShorty(shorty_jty));
-  }
-
-  ::llvm::Constant* getJZero(JType jty) {
-    switch (jty) {
-    case kVoid:
-      LOG(FATAL) << "Zero is not a value of void type";
-      return NULL;
-
-    case kBoolean:
-      return getJBoolean(false);
-
-    case kByte:
-      return getJByte(0);
-
-    case kChar:
-      return getJChar(0);
-
-    case kShort:
-      return getJShort(0);
-
-    case kInt:
-      return getJInt(0);
-
-    case kLong:
-      return getJLong(0);
-
-    case kFloat:
-      return getJFloat(0.0f);
-
-    case kDouble:
-      return getJDouble(0.0);
-
-    case kObject:
-      return getJNull();
-
-    default:
-      LOG(FATAL) << "Unknown java type: " << jty;
-      return NULL;
-    }
-  }
-
-
- private:
-  ::llvm::Module* module_;
-
-  MDBuilder mdb_;
-
-  ::llvm::PointerType* java_object_type_;
-  ::llvm::PointerType* java_method_type_;
-  ::llvm::PointerType* java_thread_type_;
-
-  ::llvm::PointerType* jenv_type_;
-
-  ::llvm::StructType* art_frame_type_;
-
-  RuntimeSupportBuilder* runtime_support_;
-
-  IntrinsicHelper& intrinsic_helper_;
-};
-
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_IR_BUILDER_H_
diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc
deleted file mode 100644
index 741c2d7..0000000
--- a/compiler/llvm/llvm_compilation_unit.cc
+++ /dev/null
@@ -1,323 +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.
- */
-
-// TODO: TargetLibraryInfo is included before sys/... because on Android bionic does #define tricks like:
-//
-// #define  stat64    stat
-// #define  fstat64   fstat
-// #define  lstat64   lstat
-//
-// which causes grief. bionic probably should not do that.
-#include <llvm/Target/TargetLibraryInfo.h>
-
-#include "llvm_compilation_unit.h"
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <llvm/ADT/OwningPtr.h>
-#include <llvm/ADT/StringSet.h>
-#include <llvm/ADT/Triple.h>
-#include <llvm/Analysis/CallGraph.h>
-#include <llvm/Analysis/CallGraphSCCPass.h>
-#include <llvm/Analysis/Dominators.h>
-#include <llvm/Analysis/LoopInfo.h>
-#include <llvm/Analysis/LoopPass.h>
-#include <llvm/Analysis/RegionPass.h>
-#include <llvm/Analysis/ScalarEvolution.h>
-#include <llvm/Analysis/Verifier.h>
-#include <llvm/Assembly/PrintModulePass.h>
-#include <llvm/Bitcode/ReaderWriter.h>
-#include <llvm/CodeGen/MachineFrameInfo.h>
-#include <llvm/CodeGen/MachineFunction.h>
-#include <llvm/CodeGen/MachineFunctionPass.h>
-#include <llvm/DebugInfo.h>
-#include <llvm/IR/DataLayout.h>
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/LLVMContext.h>
-#include <llvm/IR/Module.h>
-#include <llvm/Object/ObjectFile.h>
-#include <llvm/PassManager.h>
-#include <llvm/Support/Debug.h>
-#include <llvm/Support/ELF.h>
-#include <llvm/Support/FormattedStream.h>
-#include <llvm/Support/ManagedStatic.h>
-#include <llvm/Support/MemoryBuffer.h>
-#include <llvm/Support/PassNameParser.h>
-#include <llvm/Support/PluginLoader.h>
-#include <llvm/Support/PrettyStackTrace.h>
-#include <llvm/Support/Signals.h>
-#include <llvm/Support/SystemUtils.h>
-#include <llvm/Support/TargetRegistry.h>
-#include <llvm/Support/TargetSelect.h>
-#include <llvm/Support/ToolOutputFile.h>
-#include <llvm/Support/raw_ostream.h>
-#include <llvm/Support/system_error.h>
-#include <llvm/Target/TargetMachine.h>
-#include <llvm/Transforms/IPO.h>
-#include <llvm/Transforms/IPO/PassManagerBuilder.h>
-#include <llvm/Transforms/Scalar.h>
-
-#include "base/logging.h"
-#include "base/unix_file/fd_file.h"
-#include "compiled_method.h"
-#include "compiler_llvm.h"
-#include "instruction_set.h"
-#include "ir_builder.h"
-#include "os.h"
-#include "runtime_support_builder_arm.h"
-#include "runtime_support_builder_x86.h"
-#include "utils_llvm.h"
-
-namespace art {
-namespace llvm {
-
-::llvm::FunctionPass*
-CreateGBCExpanderPass(const IntrinsicHelper& intrinsic_helper, IRBuilder& irb,
-                      CompilerDriver* compiler, const DexCompilationUnit* dex_compilation_unit);
-
-::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
-
-
-LlvmCompilationUnit::LlvmCompilationUnit(const CompilerLLVM* compiler_llvm, size_t cunit_id)
-    : compiler_llvm_(compiler_llvm), cunit_id_(cunit_id) {
-  driver_ = NULL;
-  dex_compilation_unit_ = NULL;
-  llvm_info_.reset(new LLVMInfo());
-  context_.reset(llvm_info_->GetLLVMContext());
-  module_ = llvm_info_->GetLLVMModule();
-
-  // Include the runtime function declaration
-  makeLLVMModuleContents(module_);
-
-
-  intrinsic_helper_.reset(new IntrinsicHelper(*context_, *module_));
-
-  // Create IRBuilder
-  irb_.reset(new IRBuilder(*context_, *module_, *intrinsic_helper_));
-
-  // We always need a switch case, so just use a normal function.
-  switch (GetInstructionSet()) {
-  default:
-    runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
-    break;
-  case kThumb2:
-  case kArm:
-    runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
-    break;
-  case kX86:
-    runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
-    break;
-  }
-
-  irb_->SetRuntimeSupport(runtime_support_.get());
-}
-
-
-LlvmCompilationUnit::~LlvmCompilationUnit() {
-  ::llvm::LLVMContext* llvm_context = context_.release();  // Managed by llvm_info_
-  CHECK(llvm_context != NULL);
-}
-
-
-InstructionSet LlvmCompilationUnit::GetInstructionSet() const {
-  return compiler_llvm_->GetInstructionSet();
-}
-
-
-static std::string DumpDirectory() {
-  if (kIsTargetBuild) {
-    return GetDalvikCacheOrDie("llvm-dump");
-  }
-  return "/tmp";
-}
-
-void LlvmCompilationUnit::DumpBitcodeToFile() {
-  std::string bitcode;
-  DumpBitcodeToString(bitcode);
-  std::string filename(StringPrintf("%s/Art%zu.bc", DumpDirectory().c_str(), cunit_id_));
-  std::unique_ptr<File> output(OS::CreateEmptyFile(filename.c_str()));
-  output->WriteFully(bitcode.data(), bitcode.size());
-  LOG(INFO) << ".bc file written successfully: " << filename;
-}
-
-void LlvmCompilationUnit::DumpBitcodeToString(std::string& str_buffer) {
-  ::llvm::raw_string_ostream str_os(str_buffer);
-  ::llvm::WriteBitcodeToFile(module_, str_os);
-}
-
-bool LlvmCompilationUnit::Materialize() {
-  const bool kDumpBitcode = false;
-  if (kDumpBitcode) {
-    // Dump the bitcode for debugging
-    DumpBitcodeToFile();
-  }
-
-  // Compile and prelink ::llvm::Module
-  if (!MaterializeToString(elf_object_)) {
-    LOG(ERROR) << "Failed to materialize compilation unit " << cunit_id_;
-    return false;
-  }
-
-  const bool kDumpELF = false;
-  if (kDumpELF) {
-    // Dump the ELF image for debugging
-    std::string filename(StringPrintf("%s/Art%zu.o", DumpDirectory().c_str(), cunit_id_));
-    std::unique_ptr<File> output(OS::CreateEmptyFile(filename.c_str()));
-    output->WriteFully(elf_object_.data(), elf_object_.size());
-    LOG(INFO) << ".o file written successfully: " << filename;
-  }
-
-  return true;
-}
-
-
-bool LlvmCompilationUnit::MaterializeToString(std::string& str_buffer) {
-  ::llvm::raw_string_ostream str_os(str_buffer);
-  return MaterializeToRawOStream(str_os);
-}
-
-
-bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_stream) {
-  // Lookup the LLVM target
-  std::string target_triple;
-  std::string target_cpu;
-  std::string target_attr;
-  CompilerDriver::InstructionSetToLLVMTarget(GetInstructionSet(), &target_triple, &target_cpu,
-                                             &target_attr);
-
-  std::string errmsg;
-  const ::llvm::Target* target =
-    ::llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
-
-  CHECK(target != NULL) << errmsg;
-
-  // Target options
-  ::llvm::TargetOptions target_options;
-  target_options.FloatABIType = ::llvm::FloatABI::Soft;
-  target_options.NoFramePointerElim = true;
-  target_options.UseSoftFloat = false;
-  target_options.EnableFastISel = false;
-
-  // Create the ::llvm::TargetMachine
-  ::llvm::OwningPtr< ::llvm::TargetMachine> target_machine(
-    target->createTargetMachine(target_triple, target_cpu, target_attr, target_options,
-                                ::llvm::Reloc::Static, ::llvm::CodeModel::Small,
-                                ::llvm::CodeGenOpt::Aggressive));
-
-  CHECK(target_machine.get() != NULL) << "Failed to create target machine";
-
-  // Add target data
-  const ::llvm::DataLayout* data_layout = target_machine->getDataLayout();
-
-  // PassManager for code generation passes
-  ::llvm::PassManager pm;
-  pm.add(new ::llvm::DataLayout(*data_layout));
-
-  // FunctionPassManager for optimization pass
-  ::llvm::FunctionPassManager fpm(module_);
-  fpm.add(new ::llvm::DataLayout(*data_layout));
-
-  if (bitcode_filename_.empty()) {
-    // If we don't need write the bitcode to file, add the AddSuspendCheckToLoopLatchPass to the
-    // regular FunctionPass.
-    fpm.add(CreateGBCExpanderPass(*llvm_info_->GetIntrinsicHelper(), *irb_.get(),
-                                  driver_, dex_compilation_unit_));
-  } else {
-    ::llvm::FunctionPassManager fpm2(module_);
-    fpm2.add(CreateGBCExpanderPass(*llvm_info_->GetIntrinsicHelper(), *irb_.get(),
-                                   driver_, dex_compilation_unit_));
-    fpm2.doInitialization();
-    for (::llvm::Module::iterator F = module_->begin(), E = module_->end();
-         F != E; ++F) {
-      fpm2.run(*F);
-    }
-    fpm2.doFinalization();
-
-    // Write bitcode to file
-    std::string errmsg;
-
-    ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
-      new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg,
-                                 ::llvm::sys::fs::F_Binary));
-
-
-    if (!errmsg.empty()) {
-      LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
-      return false;
-    }
-
-    ::llvm::WriteBitcodeToFile(module_, out_file->os());
-    out_file->keep();
-  }
-
-  // Add optimization pass
-  ::llvm::PassManagerBuilder pm_builder;
-  // TODO: Use inliner after we can do IPO.
-  pm_builder.Inliner = NULL;
-  // pm_builder.Inliner = ::llvm::createFunctionInliningPass();
-  // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass();
-  // pm_builder.Inliner = ::llvm::createPartialInliningPass();
-  pm_builder.OptLevel = 3;
-  pm_builder.DisableUnitAtATime = 1;
-  pm_builder.populateFunctionPassManager(fpm);
-  pm_builder.populateModulePassManager(pm);
-  pm.add(::llvm::createStripDeadPrototypesPass());
-
-  // Add passes to emit ELF image
-  {
-    ::llvm::formatted_raw_ostream formatted_os(out_stream, false);
-
-    // Ask the target to add backend passes as necessary.
-    if (target_machine->addPassesToEmitFile(pm,
-                                            formatted_os,
-                                            ::llvm::TargetMachine::CGFT_ObjectFile,
-                                            true)) {
-      LOG(FATAL) << "Unable to generate ELF for this target";
-      return false;
-    }
-
-    // Run the per-function optimization
-    fpm.doInitialization();
-    for (::llvm::Module::iterator F = module_->begin(), E = module_->end();
-         F != E; ++F) {
-      fpm.run(*F);
-    }
-    fpm.doFinalization();
-
-    // Run the code generation passes
-    pm.run(*module_);
-  }
-
-  return true;
-}
-
-// Check whether the align is less than or equal to the code alignment of
-// that architecture.  Since the Oat writer only guarantee that the compiled
-// method being aligned to kArchAlignment, we have no way to align the ELf
-// section if the section alignment is greater than kArchAlignment.
-void LlvmCompilationUnit::CheckCodeAlign(uint32_t align) const {
-  InstructionSet insn_set = GetInstructionSet();
-  size_t insn_set_align = GetInstructionSetAlignment(insn_set);
-  CHECK_LE(align, static_cast<uint32_t>(insn_set_align));
-}
-
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/llvm_compilation_unit.h b/compiler/llvm/llvm_compilation_unit.h
deleted file mode 100644
index f11fb6e..0000000
--- a/compiler/llvm/llvm_compilation_unit.h
+++ /dev/null
@@ -1,138 +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_COMPILER_LLVM_LLVM_COMPILATION_UNIT_H_
-#define ART_COMPILER_LLVM_LLVM_COMPILATION_UNIT_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/mutex.h"
-#include "dex/compiler_internals.h"
-#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "globals.h"
-#include "instruction_set.h"
-#include "runtime_support_builder.h"
-#include "runtime_support_llvm_func.h"
-#include "safe_map.h"
-
-namespace art {
-  class CompiledMethod;
-}
-
-namespace llvm {
-  class Function;
-  class LLVMContext;
-  class Module;
-  class raw_ostream;
-}
-
-namespace art {
-namespace llvm {
-
-class CompilerLLVM;
-class IRBuilder;
-
-class LlvmCompilationUnit {
- public:
-  ~LlvmCompilationUnit();
-
-  uint32_t GetCompilationUnitId() const {
-    return cunit_id_;
-  }
-
-  InstructionSet GetInstructionSet() const;
-
-  ::llvm::LLVMContext* GetLLVMContext() const {
-    return context_.get();
-  }
-
-  ::llvm::Module* GetModule() const {
-    return module_;
-  }
-
-  IRBuilder* GetIRBuilder() const {
-    return irb_.get();
-  }
-
-  void SetBitcodeFileName(const std::string& bitcode_filename) {
-    bitcode_filename_ = bitcode_filename;
-  }
-
-  LLVMInfo* GetQuickContext() const {
-    return llvm_info_.get();
-  }
-  void SetCompilerDriver(CompilerDriver* driver) {
-    driver_ = driver;
-  }
-  DexCompilationUnit* GetDexCompilationUnit() {
-    return dex_compilation_unit_;
-  }
-  void SetDexCompilationUnit(DexCompilationUnit* dex_compilation_unit) {
-    dex_compilation_unit_ = dex_compilation_unit;
-  }
-
-  bool Materialize();
-
-  bool IsMaterialized() const {
-    return !elf_object_.empty();
-  }
-
-  const std::string& GetElfObject() const {
-    DCHECK(IsMaterialized());
-    return elf_object_;
-  }
-
- private:
-  LlvmCompilationUnit(const CompilerLLVM* compiler_llvm,
-                      size_t cunit_id);
-
-  const CompilerLLVM* compiler_llvm_;
-  const size_t cunit_id_;
-
-  std::unique_ptr< ::llvm::LLVMContext> context_;
-  std::unique_ptr<IRBuilder> irb_;
-  std::unique_ptr<RuntimeSupportBuilder> runtime_support_;
-  ::llvm::Module* module_;  // Managed by context_
-  std::unique_ptr<IntrinsicHelper> intrinsic_helper_;
-  std::unique_ptr<LLVMInfo> llvm_info_;
-  CompilerDriver* driver_;
-  DexCompilationUnit* dex_compilation_unit_;
-
-  std::string bitcode_filename_;
-
-  std::string elf_object_;
-
-  SafeMap<const ::llvm::Function*, CompiledMethod*> compiled_methods_map_;
-
-  void CheckCodeAlign(uint32_t offset) const;
-
-  void DumpBitcodeToFile();
-  void DumpBitcodeToString(std::string& str_buffer);
-
-  bool MaterializeToString(std::string& str_buffer);
-  bool MaterializeToRawOStream(::llvm::raw_ostream& out_stream);
-
-  friend class CompilerLLVM;  // For LlvmCompilationUnit constructor
-};
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_LLVM_COMPILATION_UNIT_H_
diff --git a/compiler/llvm/llvm_compiler.cc b/compiler/llvm/llvm_compiler.cc
deleted file mode 100644
index fa93e00..0000000
--- a/compiler/llvm/llvm_compiler.cc
+++ /dev/null
@@ -1,163 +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.
- */
-
-#include "llvm_compiler.h"
-
-#include "base/macros.h"
-#ifdef ART_USE_PORTABLE_COMPILER
-#include "compiler.h"
-#include "compiler_llvm.h"
-#include "dex/portable/mir_to_gbc.h"
-#include "dex_file.h"
-#include "elf_writer_mclinker.h"
-#include "mirror/art_method-inl.h"
-#endif
-
-namespace art {
-
-#ifdef ART_USE_PORTABLE_COMPILER
-
-namespace llvm {
-
-// Thread-local storage compiler worker threads
-class LLVMCompilerTls : public CompilerTls {
-  public:
-    LLVMCompilerTls() : llvm_info_(nullptr) {}
-    ~LLVMCompilerTls() {}
-
-    void* GetLLVMInfo() { return llvm_info_; }
-
-    void SetLLVMInfo(void* llvm_info) { llvm_info_ = llvm_info; }
-
-  private:
-    void* llvm_info_;
-};
-
-
-
-class LLVMCompiler FINAL : public Compiler {
- public:
-  explicit LLVMCompiler(CompilerDriver* driver) : Compiler(driver, 1000) {}
-
-  CompilerTls* CreateNewCompilerTls() {
-    return new LLVMCompilerTls();
-  }
-
-  void Init() const OVERRIDE {
-    ArtInitCompilerContext(GetCompilerDriver());
-  }
-
-  void UnInit() const OVERRIDE {
-    ArtUnInitCompilerContext(GetCompilerDriver());
-  }
-
-  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
-      OVERRIDE {
-    return true;
-  }
-
-  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
-                          uint32_t access_flags,
-                          InvokeType invoke_type,
-                          uint16_t class_def_idx,
-                          uint32_t method_idx,
-                          jobject class_loader,
-                          const DexFile& dex_file) const OVERRIDE {
-    CompiledMethod* method = TryCompileWithSeaIR(code_item,
-                                                 access_flags,
-                                                 invoke_type,
-                                                 class_def_idx,
-                                                 method_idx,
-                                                 class_loader,
-                                                 dex_file);
-    if (method != nullptr) {
-      return method;
-    }
-
-    return ArtCompileMethod(GetCompilerDriver(),
-                            code_item,
-                            access_flags,
-                            invoke_type,
-                            class_def_idx,
-                            method_idx,
-                            class_loader,
-                            dex_file);
-  }
-
-  CompiledMethod* JniCompile(uint32_t access_flags,
-                             uint32_t method_idx,
-                             const DexFile& dex_file) const OVERRIDE {
-    return ArtLLVMJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
-  }
-
-  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const {
-    return reinterpret_cast<uintptr_t>(method->GetEntryPointFromPortableCompiledCode());
-  }
-
-  bool WriteElf(art::File* file,
-                OatWriter* oat_writer,
-                const std::vector<const art::DexFile*>& dex_files,
-                const std::string& android_root,
-                bool is_host, const CompilerDriver& driver) const
-      OVERRIDE
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return art::ElfWriterMclinker::Create(
-        file, oat_writer, dex_files, android_root, is_host, driver);
-  }
-
-  Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
-    return PortableCodeGenerator(
-        cu, cu->mir_graph.get(), &cu->arena,
-        reinterpret_cast<art::llvm::LlvmCompilationUnit*>(compilation_unit));
-  }
-
-  void InitCompilationUnit(CompilationUnit& cu) const {
-      // Fused long branches not currently useful in bitcode.
-    cu.disable_opt |=
-        (1 << kBranchFusing) |
-        (1 << kSuppressExceptionEdges);
-  }
-
-  bool IsPortable() const OVERRIDE {
-    return true;
-  }
-
-  void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
-    typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&);
-
-    SetBitcodeFileNameFn set_bitcode_file_name =
-      reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName);
-
-    set_bitcode_file_name(driver, filename);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LLVMCompiler);
-};
-
-}  // namespace llvm
-#endif
-
-Compiler* CreateLLVMCompiler(CompilerDriver* driver) {
-#ifdef ART_USE_PORTABLE_COMPILER
-  return new llvm::LLVMCompiler(driver);
-#else
-  UNUSED(driver);
-  return nullptr;
-#endif
-}
-
-}  // namespace art
diff --git a/compiler/llvm/md_builder.cc b/compiler/llvm/md_builder.cc
deleted file mode 100644
index 4331557..0000000
--- a/compiler/llvm/md_builder.cc
+++ /dev/null
@@ -1,117 +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 "md_builder.h"
-
-#include "llvm/IR/MDBuilder.h"
-
-#include <string>
-
-namespace art {
-namespace llvm {
-
-
-::llvm::MDNode* MDBuilder::GetTBAASpecialType(TBAASpecialType sty_id) {
-  DCHECK_GE(sty_id, 0) << "Unknown TBAA special type: " << sty_id;
-  DCHECK_LT(sty_id, MAX_TBAA_SPECIAL_TYPE) << "Unknown TBAA special type: " << sty_id;
-  DCHECK(tbaa_root_ != NULL);
-
-  ::llvm::MDNode*& spec_ty = tbaa_special_type_[sty_id];
-  if (spec_ty == NULL) {
-    switch (sty_id) {
-    case kTBAARegister:
-      spec_ty = createTBAANode("Register", tbaa_root_);
-      break;
-    case kTBAAStackTemp:
-      spec_ty = createTBAANode("StackTemp", tbaa_root_);
-      break;
-    case kTBAAHeapArray:
-      spec_ty = createTBAANode("HeapArray", tbaa_root_);
-      break;
-    case kTBAAHeapInstance:
-      spec_ty = createTBAANode("HeapInstance", tbaa_root_);
-      break;
-    case kTBAAHeapStatic:
-      spec_ty = createTBAANode("HeapStatic", tbaa_root_);
-      break;
-    case kTBAAJRuntime:
-      spec_ty = createTBAANode("JRuntime", tbaa_root_);
-      break;
-    case kTBAARuntimeInfo:
-      spec_ty = createTBAANode("RuntimeInfo", GetTBAASpecialType(kTBAAJRuntime));
-      break;
-    case kTBAAShadowFrame:
-      spec_ty = createTBAANode("ShadowFrame", GetTBAASpecialType(kTBAAJRuntime));
-      break;
-    case kTBAAConstJObject:
-      spec_ty = createTBAANode("ConstJObject", tbaa_root_, true);
-      break;
-    default:
-      LOG(FATAL) << "Unknown TBAA special type: " << sty_id;
-      break;
-    }
-  }
-  return spec_ty;
-}
-
-::llvm::MDNode* MDBuilder::GetTBAAMemoryJType(TBAASpecialType sty_id, JType jty_id) {
-  DCHECK(sty_id == kTBAAHeapArray ||
-         sty_id == kTBAAHeapInstance ||
-         sty_id == kTBAAHeapStatic) << "SpecialType must be array, instance, or static";
-
-  DCHECK_GE(jty_id, 0) << "Unknown JType: " << jty_id;
-  DCHECK_LT(jty_id, MAX_JTYPE) << "Unknown JType: " << jty_id;
-  DCHECK_NE(jty_id, kVoid) << "Can't load/store Void type!";
-
-  std::string name;
-  size_t sty_mapped_index = 0;
-  switch (sty_id) {
-  case kTBAAHeapArray:    sty_mapped_index = 0; name = "HeapArray "; break;
-  case kTBAAHeapInstance: sty_mapped_index = 1; name = "HeapInstance "; break;
-  case kTBAAHeapStatic:   sty_mapped_index = 2; name = "HeapStatic "; break;
-  default:
-    LOG(FATAL) << "Unknown TBAA special type: " << sty_id;
-    break;
-  }
-
-  ::llvm::MDNode*& spec_ty = tbaa_memory_jtype_[sty_mapped_index][jty_id];
-  if (spec_ty != NULL) {
-    return spec_ty;
-  }
-
-  switch (jty_id) {
-  case kBoolean: name += "Boolean"; break;
-  case kByte:    name += "Byte"; break;
-  case kChar:    name += "Char"; break;
-  case kShort:   name += "Short"; break;
-  case kInt:     name += "Int"; break;
-  case kLong:    name += "Long"; break;
-  case kFloat:   name += "Float"; break;
-  case kDouble:  name += "Double"; break;
-  case kObject:  name += "Object"; break;
-  default:
-    LOG(FATAL) << "Unknown JType: " << jty_id;
-    break;
-  }
-
-  spec_ty = createTBAANode(name, GetTBAASpecialType(sty_id));
-  return spec_ty;
-}
-
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/md_builder.h b/compiler/llvm/md_builder.h
deleted file mode 100644
index 1246f9b..0000000
--- a/compiler/llvm/md_builder.h
+++ /dev/null
@@ -1,71 +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_COMPILER_LLVM_MD_BUILDER_H_
-#define ART_COMPILER_LLVM_MD_BUILDER_H_
-
-#include "backend_types.h"
-
-#include "llvm/IR/MDBuilder.h"
-
-#include <cstring>
-
-namespace llvm {
-  class LLVMContext;
-  class MDNode;
-}
-
-namespace art {
-namespace llvm {
-
-typedef ::llvm::MDBuilder LLVMMDBuilder;
-
-class MDBuilder : public LLVMMDBuilder {
- public:
-  explicit MDBuilder(::llvm::LLVMContext& context)
-     : LLVMMDBuilder(context), tbaa_root_(createTBAARoot("Art TBAA Root")) {
-    std::memset(tbaa_special_type_, 0, sizeof(tbaa_special_type_));
-    std::memset(tbaa_memory_jtype_, 0, sizeof(tbaa_memory_jtype_));
-
-    // Pre-generate the MDNode for static branch prediction
-    // 64 and 4 are the llvm.expect's default values
-    expect_cond_[kLikely] = createBranchWeights(64, 4);
-    expect_cond_[kUnlikely] = createBranchWeights(4, 64);
-  }
-
-  ::llvm::MDNode* GetTBAASpecialType(TBAASpecialType special_ty);
-  ::llvm::MDNode* GetTBAAMemoryJType(TBAASpecialType special_ty, JType j_ty);
-
-  ::llvm::MDNode* GetBranchWeights(ExpectCond expect) {
-    DCHECK_LT(expect, MAX_EXPECT) << "MAX_EXPECT is not for branch weight";
-    return expect_cond_[expect];
-  }
-
- private:
-  ::llvm::MDNode* const tbaa_root_;
-  ::llvm::MDNode* tbaa_special_type_[MAX_TBAA_SPECIAL_TYPE];
-  // There are 3 categories of memory types will not alias: array element, instance field, and
-  // static field.
-  ::llvm::MDNode* tbaa_memory_jtype_[3][MAX_JTYPE];
-
-  ::llvm::MDNode* expect_cond_[MAX_EXPECT];
-};
-
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_MD_BUILDER_H_
diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc
deleted file mode 100644
index c825fbf..0000000
--- a/compiler/llvm/runtime_support_builder.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.
- */
-
-#include "runtime_support_builder.h"
-
-#include "gc/accounting/card_table.h"
-#include "ir_builder.h"
-#include "monitor.h"
-#include "mirror/object.h"
-#include "runtime_support_llvm_func_list.h"
-#include "thread.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-using ::llvm::BasicBlock;
-using ::llvm::CallInst;
-using ::llvm::Function;
-using ::llvm::Value;
-
-namespace art {
-namespace llvm {
-
-RuntimeSupportBuilder::RuntimeSupportBuilder(::llvm::LLVMContext& context,
-                                             ::llvm::Module& module,
-                                             IRBuilder& irb)
-    : context_(context), module_(module), irb_(irb) {
-  memset(target_runtime_support_func_, 0, sizeof(target_runtime_support_func_));
-#define GET_RUNTIME_SUPPORT_FUNC_DECL(ID, NAME) \
-  do { \
-    ::llvm::Function* fn = module_.getFunction(#NAME); \
-    DCHECK(fn != NULL) << "Function not found: " << #NAME; \
-    runtime_support_func_decls_[runtime_support::ID] = fn; \
-  } while (0);
-
-  RUNTIME_SUPPORT_FUNC_LIST(GET_RUNTIME_SUPPORT_FUNC_DECL)
-}
-
-
-/* Thread */
-
-::llvm::Value* RuntimeSupportBuilder::EmitGetCurrentThread() {
-  Function* func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
-  CallInst* call_inst = irb_.CreateCall(func);
-  call_inst->setOnlyReadsMemory();
-  irb_.SetTBAA(call_inst, kTBAAConstJObject);
-  return call_inst;
-}
-
-::llvm::Value* RuntimeSupportBuilder::EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
-                                                             TBAASpecialType s_ty) {
-  Value* thread = EmitGetCurrentThread();
-  return irb_.LoadFromObjectOffset(thread, offset, type, s_ty);
-}
-
-void RuntimeSupportBuilder::EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
-                                                    TBAASpecialType s_ty) {
-  Value* thread = EmitGetCurrentThread();
-  irb_.StoreToObjectOffset(thread, offset, value, s_ty);
-}
-
-::llvm::Value* RuntimeSupportBuilder::EmitSetCurrentThread(::llvm::Value* thread) {
-  Function* func = GetRuntimeSupportFunction(runtime_support::SetCurrentThread);
-  return irb_.CreateCall(func, thread);
-}
-
-
-/* ShadowFrame */
-
-::llvm::Value* RuntimeSupportBuilder::EmitPushShadowFrame(::llvm::Value* new_shadow_frame,
-                                                        ::llvm::Value* method,
-                                                        uint32_t num_vregs) {
-  Value* old_shadow_frame = EmitLoadFromThreadOffset(Thread::TopShadowFrameOffset().Int32Value(),
-                                                     irb_.getArtFrameTy()->getPointerTo(),
-                                                     kTBAARuntimeInfo);
-  EmitStoreToThreadOffset(Thread::TopShadowFrameOffset().Int32Value(),
-                          new_shadow_frame,
-                          kTBAARuntimeInfo);
-
-  // Store the method pointer
-  irb_.StoreToObjectOffset(new_shadow_frame,
-                           ShadowFrame::MethodOffset(),
-                           method,
-                           kTBAAShadowFrame);
-
-  // Store the number of vregs
-  irb_.StoreToObjectOffset(new_shadow_frame,
-                           ShadowFrame::NumberOfVRegsOffset(),
-                           irb_.getInt32(num_vregs),
-                           kTBAAShadowFrame);
-
-  // Store the link to previous shadow frame
-  irb_.StoreToObjectOffset(new_shadow_frame,
-                           ShadowFrame::LinkOffset(),
-                           old_shadow_frame,
-                           kTBAAShadowFrame);
-
-  return old_shadow_frame;
-}
-
-::llvm::Value*
-RuntimeSupportBuilder::EmitPushShadowFrameNoInline(::llvm::Value* new_shadow_frame,
-                                                   ::llvm::Value* method,
-                                                   uint32_t num_vregs) {
-  Function* func = GetRuntimeSupportFunction(runtime_support::PushShadowFrame);
-  ::llvm::CallInst* call_inst =
-      irb_.CreateCall4(func,
-                       EmitGetCurrentThread(),
-                       new_shadow_frame,
-                       method,
-                       irb_.getInt32(num_vregs));
-  irb_.SetTBAA(call_inst, kTBAARuntimeInfo);
-  return call_inst;
-}
-
-void RuntimeSupportBuilder::EmitPopShadowFrame(::llvm::Value* old_shadow_frame) {
-  // Store old shadow frame to TopShadowFrame
-  EmitStoreToThreadOffset(Thread::TopShadowFrameOffset().Int32Value(),
-                          old_shadow_frame,
-                          kTBAARuntimeInfo);
-}
-
-
-/* Exception */
-
-::llvm::Value* RuntimeSupportBuilder::EmitGetAndClearException() {
-  Function* slow_func = GetRuntimeSupportFunction(runtime_support::GetAndClearException);
-  return irb_.CreateCall(slow_func, EmitGetCurrentThread());
-}
-
-::llvm::Value* RuntimeSupportBuilder::EmitIsExceptionPending() {
-  Value* exception = EmitLoadFromThreadOffset(Thread::ExceptionOffset().Int32Value(),
-                                              irb_.getJObjectTy(),
-                                              kTBAARuntimeInfo);
-  // If exception not null
-  return irb_.CreateIsNotNull(exception);
-}
-
-
-/* Suspend */
-
-void RuntimeSupportBuilder::EmitTestSuspend() {
-  Function* slow_func = GetRuntimeSupportFunction(runtime_support::TestSuspend);
-  CallInst* call_inst = irb_.CreateCall(slow_func, EmitGetCurrentThread());
-  irb_.SetTBAA(call_inst, kTBAAJRuntime);
-}
-
-
-/* Monitor */
-
-void RuntimeSupportBuilder::EmitLockObject(::llvm::Value* object) {
-  Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
-  irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
-}
-
-void RuntimeSupportBuilder::EmitUnlockObject(::llvm::Value* object) {
-  Function* slow_func = GetRuntimeSupportFunction(runtime_support::UnlockObject);
-  irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
-}
-
-
-void RuntimeSupportBuilder::EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr) {
-  Function* parent_func = irb_.GetInsertBlock()->getParent();
-  BasicBlock* bb_mark_gc_card = BasicBlock::Create(context_, "mark_gc_card", parent_func);
-  BasicBlock* bb_cont = BasicBlock::Create(context_, "mark_gc_card_cont", parent_func);
-
-  ::llvm::Value* not_null = irb_.CreateIsNotNull(value);
-  irb_.CreateCondBr(not_null, bb_mark_gc_card, bb_cont);
-
-  irb_.SetInsertPoint(bb_mark_gc_card);
-  Value* card_table = EmitLoadFromThreadOffset(Thread::CardTableOffset().Int32Value(),
-                                               irb_.getInt8Ty()->getPointerTo(),
-                                               kTBAAConstJObject);
-  Value* target_addr_int = irb_.CreatePtrToInt(target_addr, irb_.getPtrEquivIntTy());
-  Value* card_no = irb_.CreateLShr(target_addr_int,
-                                   irb_.getPtrEquivInt(gc::accounting::CardTable::kCardShift));
-  Value* card_table_entry = irb_.CreateGEP(card_table, card_no);
-  irb_.CreateStore(irb_.getInt8(gc::accounting::CardTable::kCardDirty), card_table_entry,
-                   kTBAARuntimeInfo);
-  irb_.CreateBr(bb_cont);
-
-  irb_.SetInsertPoint(bb_cont);
-}
-
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/runtime_support_builder.h b/compiler/llvm/runtime_support_builder.h
deleted file mode 100644
index 898611a..0000000
--- a/compiler/llvm/runtime_support_builder.h
+++ /dev/null
@@ -1,98 +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_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_H_
-
-#include "backend_types.h"
-#include "base/logging.h"
-#include "runtime_support_llvm_func.h"
-
-#include <stdint.h>
-
-namespace llvm {
-  class LLVMContext;
-  class Module;
-  class Function;
-  class Type;
-  class Value;
-}
-
-namespace art {
-namespace llvm {
-
-class IRBuilder;
-
-
-class RuntimeSupportBuilder {
- public:
-  RuntimeSupportBuilder(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb);
-
-  /* Thread */
-  virtual ::llvm::Value* EmitGetCurrentThread();
-  virtual ::llvm::Value* EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
-                                                TBAASpecialType s_ty);
-  virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
-                                       TBAASpecialType s_ty);
-  virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-
-  /* ShadowFrame */
-  virtual ::llvm::Value* EmitPushShadowFrame(::llvm::Value* new_shadow_frame,
-                                           ::llvm::Value* method, uint32_t num_vregs);
-  virtual ::llvm::Value* EmitPushShadowFrameNoInline(::llvm::Value* new_shadow_frame,
-                                                   ::llvm::Value* method, uint32_t num_vregs);
-  virtual void EmitPopShadowFrame(::llvm::Value* old_shadow_frame);
-
-  /* Exception */
-  virtual ::llvm::Value* EmitGetAndClearException();
-  virtual ::llvm::Value* EmitIsExceptionPending();
-
-  /* Suspend */
-  virtual void EmitTestSuspend();
-
-  /* Monitor */
-  void EmitLockObject(::llvm::Value* object);
-  void EmitUnlockObject(::llvm::Value* object);
-
-  /* MarkGCCard */
-  virtual void EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr);
-
-  ::llvm::Function* GetRuntimeSupportFunction(runtime_support::RuntimeId id) {
-    if (id >= 0 && id < runtime_support::MAX_ID) {
-      return runtime_support_func_decls_[id];
-    } else {
-      LOG(ERROR) << "Unknown runtime function id: " << id;
-      return NULL;
-    }
-  }
-
-  virtual ~RuntimeSupportBuilder() {}
-
- protected:
-  ::llvm::LLVMContext& context_;
-  ::llvm::Module& module_;
-  IRBuilder& irb_;
-
- private:
-  ::llvm::Function* runtime_support_func_decls_[runtime_support::MAX_ID];
-  bool target_runtime_support_func_[runtime_support::MAX_ID];
-};
-
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_H_
diff --git a/compiler/llvm/runtime_support_builder_arm.cc b/compiler/llvm/runtime_support_builder_arm.cc
deleted file mode 100644
index cad4624..0000000
--- a/compiler/llvm/runtime_support_builder_arm.cc
+++ /dev/null
@@ -1,120 +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 "runtime_support_builder_arm.h"
-
-#include "ir_builder.h"
-#include "thread.h"
-#include "utils_llvm.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/InlineAsm.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using ::llvm::CallInst;
-using ::llvm::Function;
-using ::llvm::FunctionType;
-using ::llvm::InlineAsm;
-using ::llvm::IntegerType;
-using ::llvm::Type;
-using ::llvm::Value;
-
-namespace {
-
-char LDRSTRSuffixByType(art::llvm::IRBuilder& irb, Type* type) {
-  int width = type->isPointerTy() ?
-              irb.getSizeOfPtrEquivInt()*8 :
-              ::llvm::cast<IntegerType>(type)->getBitWidth();
-  switch (width) {
-    case 8:  return 'b';
-    case 16: return 'h';
-    case 32: return ' ';
-    default:
-      LOG(FATAL) << "Unsupported width: " << width;
-      return ' ';
-  }
-}
-
-}  // namespace
-
-namespace art {
-namespace llvm {
-
-/* Thread */
-
-Value* RuntimeSupportBuilderARM::EmitGetCurrentThread() {
-  Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
-  InlineAsm* func = InlineAsm::get(ori_func->getFunctionType(), "mov $0, r9", "=r", false);
-  CallInst* thread = irb_.CreateCall(func);
-  thread->setDoesNotAccessMemory();
-  irb_.SetTBAA(thread, kTBAAConstJObject);
-  return thread;
-}
-
-Value* RuntimeSupportBuilderARM::EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
-                                                          TBAASpecialType s_ty) {
-  FunctionType* func_ty = FunctionType::get(/*Result=*/type,
-                                            /*isVarArg=*/false);
-  std::string inline_asm(StringPrintf("ldr%c $0, [r9, #%d]",
-                                      LDRSTRSuffixByType(irb_, type),
-                                      static_cast<int>(offset)));
-  InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "=r", true);
-  CallInst* result = irb_.CreateCall(func);
-  result->setOnlyReadsMemory();
-  irb_.SetTBAA(result, s_ty);
-  return result;
-}
-
-void RuntimeSupportBuilderARM::EmitStoreToThreadOffset(int64_t offset, Value* value,
-                                                       TBAASpecialType s_ty) {
-  FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
-                                            /*Params=*/value->getType(),
-                                            /*isVarArg=*/false);
-  std::string inline_asm(StringPrintf("str%c $0, [r9, #%d]",
-                                      LDRSTRSuffixByType(irb_, value->getType()),
-                                      static_cast<int>(offset)));
-  InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "r", true);
-  CallInst* call_inst = irb_.CreateCall(func, value);
-  irb_.SetTBAA(call_inst, s_ty);
-}
-
-Value* RuntimeSupportBuilderARM::EmitSetCurrentThread(Value* thread) {
-  // Separate to two InlineAsm: The first one produces the return value, while the second,
-  // sets the current thread.
-  // LLVM can delete the first one if the caller in LLVM IR doesn't use the return value.
-  //
-  // Here we don't call EmitGetCurrentThread, because we mark it as DoesNotAccessMemory and
-  // ConstJObject. We denote side effect to "true" below instead, so LLVM won't
-  // reorder these instructions incorrectly.
-  Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
-  InlineAsm* func = InlineAsm::get(ori_func->getFunctionType(), "mov $0, r9", "=r", true);
-  CallInst* old_thread_register = irb_.CreateCall(func);
-  old_thread_register->setOnlyReadsMemory();
-
-  FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
-                                            /*Params=*/irb_.getJObjectTy(),
-                                            /*isVarArg=*/false);
-  func = InlineAsm::get(func_ty, "mov r9, $0", "r", true);
-  irb_.CreateCall(func, thread);
-  return old_thread_register;
-}
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/runtime_support_builder_arm.h b/compiler/llvm/runtime_support_builder_arm.h
deleted file mode 100644
index 0d01509..0000000
--- a/compiler/llvm/runtime_support_builder_arm.h
+++ /dev/null
@@ -1,42 +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_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_ARM_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_ARM_H_
-
-#include "runtime_support_builder.h"
-
-namespace art {
-namespace llvm {
-
-class RuntimeSupportBuilderARM : public RuntimeSupportBuilder {
- public:
-  RuntimeSupportBuilderARM(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb)
-    : RuntimeSupportBuilder(context, module, irb) {}
-
-  /* Thread */
-  virtual ::llvm::Value* EmitGetCurrentThread();
-  virtual ::llvm::Value* EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
-                                                TBAASpecialType s_ty);
-  virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
-                                       TBAASpecialType s_ty);
-  virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-};
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_ARM_H_
diff --git a/compiler/llvm/runtime_support_builder_x86.cc b/compiler/llvm/runtime_support_builder_x86.cc
deleted file mode 100644
index 3d11f9d..0000000
--- a/compiler/llvm/runtime_support_builder_x86.cc
+++ /dev/null
@@ -1,84 +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 "runtime_support_builder_x86.h"
-
-#include "base/stringprintf.h"
-#include "ir_builder.h"
-#include "thread.h"
-#include "utils_llvm.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/InlineAsm.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using ::llvm::CallInst;
-using ::llvm::Function;
-using ::llvm::FunctionType;
-using ::llvm::InlineAsm;
-using ::llvm::Type;
-using ::llvm::UndefValue;
-using ::llvm::Value;
-
-namespace art {
-namespace llvm {
-
-
-Value* RuntimeSupportBuilderX86::EmitGetCurrentThread() {
-  Function* ori_func = GetRuntimeSupportFunction(runtime_support::GetCurrentThread);
-  std::string inline_asm(StringPrintf("mov %%fs:%d, $0", Thread::SelfOffset().Int32Value()));
-  InlineAsm* func = InlineAsm::get(ori_func->getFunctionType(), inline_asm, "=r", false);
-  CallInst* thread = irb_.CreateCall(func);
-  thread->setDoesNotAccessMemory();
-  irb_.SetTBAA(thread, kTBAAConstJObject);
-  return thread;
-}
-
-Value* RuntimeSupportBuilderX86::EmitLoadFromThreadOffset(int64_t offset, Type* type,
-                                                          TBAASpecialType s_ty) {
-  FunctionType* func_ty = FunctionType::get(/*Result=*/type,
-                                            /*isVarArg=*/false);
-  std::string inline_asm(StringPrintf("mov %%fs:%d, $0", static_cast<int>(offset)));
-  InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "=r", true);
-  CallInst* result = irb_.CreateCall(func);
-  result->setOnlyReadsMemory();
-  irb_.SetTBAA(result, s_ty);
-  return result;
-}
-
-void RuntimeSupportBuilderX86::EmitStoreToThreadOffset(int64_t offset, Value* value,
-                                                       TBAASpecialType s_ty) {
-  FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
-                                            /*Params=*/value->getType(),
-                                            /*isVarArg=*/false);
-  std::string inline_asm(StringPrintf("mov $0, %%fs:%d", static_cast<int>(offset)));
-  InlineAsm* func = InlineAsm::get(func_ty, inline_asm, "r", true);
-  CallInst* call_inst = irb_.CreateCall(func, value);
-  irb_.SetTBAA(call_inst, s_ty);
-}
-
-Value* RuntimeSupportBuilderX86::EmitSetCurrentThread(Value*) {
-  /* Nothing to be done. */
-  return UndefValue::get(irb_.getJObjectTy());
-}
-
-
-}  // namespace llvm
-}  // namespace art
diff --git a/compiler/llvm/runtime_support_builder_x86.h b/compiler/llvm/runtime_support_builder_x86.h
deleted file mode 100644
index 5f36e7c..0000000
--- a/compiler/llvm/runtime_support_builder_x86.h
+++ /dev/null
@@ -1,42 +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_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_X86_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_X86_H_
-
-#include "runtime_support_builder.h"
-
-namespace art {
-namespace llvm {
-
-class RuntimeSupportBuilderX86 : public RuntimeSupportBuilder {
- public:
-  RuntimeSupportBuilderX86(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb)
-    : RuntimeSupportBuilder(context, module, irb) {}
-
-  /* Thread */
-  virtual ::llvm::Value* EmitGetCurrentThread();
-  virtual ::llvm::Value* EmitLoadFromThreadOffset(int64_t offset, ::llvm::Type* type,
-                                                TBAASpecialType s_ty);
-  virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
-                                       TBAASpecialType s_ty);
-  virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-};
-
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_X86_H_
diff --git a/compiler/llvm/runtime_support_llvm_func.h b/compiler/llvm/runtime_support_llvm_func.h
deleted file mode 100644
index a5ad852..0000000
--- a/compiler/llvm/runtime_support_llvm_func.h
+++ /dev/null
@@ -1,37 +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_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_
-
-#include "runtime_support_llvm_func_list.h"
-
-namespace art {
-namespace llvm {
-namespace runtime_support {
-
-  enum RuntimeId {
-#define DEFINE_RUNTIME_SUPPORT_FUNC_ID(ID, NAME) ID,
-    RUNTIME_SUPPORT_FUNC_LIST(DEFINE_RUNTIME_SUPPORT_FUNC_ID)
-
-    MAX_ID
-  };
-
-}  // namespace runtime_support
-}  // namespace llvm
-}  // namespace art
-
-#endif  // ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_
diff --git a/compiler/llvm/runtime_support_llvm_func_list.h b/compiler/llvm/runtime_support_llvm_func_list.h
deleted file mode 100644
index b5ac1ff..0000000
--- a/compiler/llvm/runtime_support_llvm_func_list.h
+++ /dev/null
@@ -1,81 +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_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_
-
-#define RUNTIME_SUPPORT_FUNC_LIST(V) \
-  V(LockObject, art_portable_lock_object_from_code) \
-  V(UnlockObject, art_portable_unlock_object_from_code) \
-  V(GetCurrentThread, art_portable_get_current_thread_from_code) \
-  V(SetCurrentThread, art_portable_set_current_thread_from_code) \
-  V(PushShadowFrame, art_portable_push_shadow_frame_from_code) \
-  V(PopShadowFrame, art_portable_pop_shadow_frame_from_code) \
-  V(TestSuspend, art_portable_test_suspend_from_code) \
-  V(ThrowException, art_portable_throw_exception_from_code) \
-  V(ThrowStackOverflowException, art_portable_throw_stack_overflow_from_code) \
-  V(ThrowNullPointerException, art_portable_throw_null_pointer_exception_from_code) \
-  V(ThrowDivZeroException, art_portable_throw_div_zero_from_code) \
-  V(ThrowIndexOutOfBounds, art_portable_throw_array_bounds_from_code) \
-  V(InitializeTypeAndVerifyAccess, art_portable_initialize_type_and_verify_access_from_code) \
-  V(InitializeType, art_portable_initialize_type_from_code) \
-  V(IsAssignable, art_portable_is_assignable_from_code) \
-  V(CheckCast, art_portable_check_cast_from_code) \
-  V(CheckPutArrayElement, art_portable_check_put_array_element_from_code) \
-  V(AllocObject, art_portable_alloc_object_from_code) \
-  V(AllocObjectWithAccessCheck, art_portable_alloc_object_from_code_with_access_check) \
-  V(AllocArray, art_portable_alloc_array_from_code) \
-  V(AllocArrayWithAccessCheck, art_portable_alloc_array_from_code_with_access_check) \
-  V(CheckAndAllocArray, art_portable_check_and_alloc_array_from_code) \
-  V(CheckAndAllocArrayWithAccessCheck, art_portable_check_and_alloc_array_from_code_with_access_check) \
-  V(FindStaticMethodWithAccessCheck, art_portable_find_static_method_from_code_with_access_check) \
-  V(FindDirectMethodWithAccessCheck, art_portable_find_direct_method_from_code_with_access_check) \
-  V(FindVirtualMethodWithAccessCheck, art_portable_find_virtual_method_from_code_with_access_check) \
-  V(FindSuperMethodWithAccessCheck, art_portable_find_super_method_from_code_with_access_check) \
-  V(FindInterfaceMethodWithAccessCheck, art_portable_find_interface_method_from_code_with_access_check) \
-  V(FindInterfaceMethod, art_portable_find_interface_method_from_code) \
-  V(ResolveString, art_portable_resolve_string_from_code) \
-  V(Set32Static, art_portable_set32_static_from_code) \
-  V(Set64Static, art_portable_set64_static_from_code) \
-  V(SetObjectStatic, art_portable_set_obj_static_from_code) \
-  V(Get32Static, art_portable_get32_static_from_code) \
-  V(Get64Static, art_portable_get64_static_from_code) \
-  V(GetObjectStatic, art_portable_get_obj_static_from_code) \
-  V(Set32Instance, art_portable_set32_instance_from_code) \
-  V(Set64Instance, art_portable_set64_instance_from_code) \
-  V(SetObjectInstance, art_portable_set_obj_instance_from_code) \
-  V(Get32Instance, art_portable_get32_instance_from_code) \
-  V(Get64Instance, art_portable_get64_instance_from_code) \
-  V(GetObjectInstance, art_portable_get_obj_instance_from_code) \
-  V(InitializeStaticStorage, art_portable_initialize_static_storage_from_code) \
-  V(FillArrayData, art_portable_fill_array_data_from_code) \
-  V(GetAndClearException, art_portable_get_and_clear_exception) \
-  V(IsExceptionPending, art_portable_is_exception_pending_from_code) \
-  V(FindCatchBlock, art_portable_find_catch_block_from_code) \
-  V(MarkGCCard, art_portable_mark_gc_card_from_code) \
-  V(ProxyInvokeHandler, art_portable_proxy_invoke_handler_from_code) \
-  V(art_d2l, art_d2l) \
-  V(art_d2i, art_d2i) \
-  V(art_f2l, art_f2l) \
-  V(art_f2i, art_f2i) \
-  V(JniMethodStart,                        art_portable_jni_method_start) \
-  V(JniMethodStartSynchronized,            art_portable_jni_method_start_synchronized) \
-  V(JniMethodEnd,                          art_portable_jni_method_end) \
-  V(JniMethodEndSynchronized,              art_portable_jni_method_end_synchronized) \
-  V(JniMethodEndWithReference,             art_portable_jni_method_end_with_reference) \
-  V(JniMethodEndWithReferenceSynchronized, art_portable_jni_method_end_with_reference_synchronized)
-
-#endif  // ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_
diff --git a/compiler/llvm/tools/gen_art_module_cc.sh b/compiler/llvm/tools/gen_art_module_cc.sh
deleted file mode 100755
index c5df333..0000000
--- a/compiler/llvm/tools/gen_art_module_cc.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/bash -e
-
-# 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.
-
-SCRIPTDIR=`dirname "$0"`
-cd "${SCRIPTDIR}/.."
-
-mkdir -p generated
-
-OUTPUT_FILE=generated/art_module.cc
-
-echo "// Generated with ${0}" > ${OUTPUT_FILE}
-
-echo '
-
-#pragma GCC diagnostic ignored "-Wframe-larger-than="
-// TODO: Remove this pragma after llc can generate makeLLVMModuleContents()
-// with smaller frame size.
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <vector>
-
-using namespace llvm;
-
-namespace art {
-namespace llvm {
-
-' >> ${OUTPUT_FILE}
-
-llc -march=cpp -cppgen=contents art_module.ll -o - >> ${OUTPUT_FILE}
-
-echo '
-} // namespace llvm
-} // namespace art' >> ${OUTPUT_FILE}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 9fe98e3..46aed60 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -18,9 +18,10 @@
 #include "class_linker.h"
 #include "common_compiler_test.h"
 #include "compiler.h"
-#include "dex/verification_results.h"
+#include "dex/pass_manager.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "dex/quick_compiler_callbacks.h"
+#include "dex/verification_results.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
@@ -39,49 +40,32 @@
 
   void CheckMethod(mirror::ArtMethod* method,
                    const OatFile::OatMethod& oat_method,
-                   const DexFile* dex_file)
+                   const DexFile& dex_file)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     const CompiledMethod* compiled_method =
-        compiler_driver_->GetCompiledMethod(MethodReference(dex_file,
+        compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
                                                             method->GetDexMethodIndex()));
 
     if (compiled_method == nullptr) {
       EXPECT_TRUE(oat_method.GetQuickCode() == nullptr) << PrettyMethod(method) << " "
                                                         << oat_method.GetQuickCode();
-      EXPECT_TRUE(oat_method.GetPortableCode() == nullptr) << PrettyMethod(method) << " "
-                                                           << oat_method.GetPortableCode();
       EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U);
       EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
       EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
     } else {
       const void* quick_oat_code = oat_method.GetQuickCode();
-      if (quick_oat_code != nullptr) {
-        EXPECT_EQ(oat_method.GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes());
-        EXPECT_EQ(oat_method.GetCoreSpillMask(), compiled_method->GetCoreSpillMask());
-        EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
-        uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
-        quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
-        const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
-        EXPECT_TRUE(quick_code != nullptr);
-        size_t code_size = quick_code->size() * sizeof(quick_code[0]);
-        EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
-            << PrettyMethod(method) << " " << code_size;
-        CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
-      } else {
-        const void* portable_oat_code = oat_method.GetPortableCode();
-        EXPECT_TRUE(portable_oat_code != nullptr) << PrettyMethod(method);
-        EXPECT_EQ(oat_method.GetFrameSizeInBytes(), 0U);
-        EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
-        EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
-        uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(portable_oat_code), 2);
-        portable_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
-        const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
-        EXPECT_TRUE(portable_code != nullptr);
-        size_t code_size = portable_code->size() * sizeof(portable_code[0]);
-        EXPECT_EQ(0, memcmp(quick_oat_code, &portable_code[0], code_size))
-            << PrettyMethod(method) << " " << code_size;
-        CHECK_EQ(0, memcmp(quick_oat_code, &portable_code[0], code_size));
-      }
+      EXPECT_TRUE(quick_oat_code != nullptr) << PrettyMethod(method);
+      EXPECT_EQ(oat_method.GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes());
+      EXPECT_EQ(oat_method.GetCoreSpillMask(), compiled_method->GetCoreSpillMask());
+      EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
+      uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
+      quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      EXPECT_TRUE(quick_code != nullptr);
+      size_t code_size = quick_code->size() * sizeof(quick_code[0]);
+      EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
+          << PrettyMethod(method) << " " << code_size;
+      CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
     }
   }
 };
@@ -91,9 +75,7 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
   // TODO: make selectable.
-  Compiler::Kind compiler_kind = kUsePortableCompiler
-      ? Compiler::kPortable
-      : Compiler::kQuick;
+  Compiler::Kind compiler_kind = Compiler::kQuick;
   InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
 
   std::string error_msg;
@@ -111,7 +93,7 @@
                                             method_inliner_map_.get(),
                                             compiler_kind, insn_set,
                                             insn_features.get(), false, nullptr, nullptr, 2, true,
-                                            true, timer_.get(), ""));
+                                            true, "", timer_.get(), -1, ""));
   jobject class_loader = nullptr;
   if (kCompile) {
     TimingLogger timings2("OatTest::WriteRead", false, false);
@@ -149,22 +131,23 @@
   ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
   ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
 
-  const DexFile* dex_file = java_lang_dex_file_;
-  uint32_t dex_file_checksum = dex_file->GetLocationChecksum();
-  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation().c_str(),
+  ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+  const DexFile& dex_file = *java_lang_dex_file_;
+  uint32_t dex_file_checksum = dex_file.GetLocationChecksum();
+  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
                                                                     &dex_file_checksum);
   ASSERT_TRUE(oat_dex_file != nullptr);
-  CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
+  CHECK_EQ(dex_file.GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
   ScopedObjectAccess soa(Thread::Current());
-  for (size_t i = 0; i < dex_file->NumClassDefs(); i++) {
-    const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
-    const uint8_t* class_data = dex_file->GetClassData(class_def);
+  for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+    const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+    const uint8_t* class_data = dex_file.GetClassData(class_def);
     size_t num_virtual_methods = 0;
     if (class_data != nullptr) {
-      ClassDataItemIterator it(*dex_file, class_data);
+      ClassDataItemIterator it(dex_file, class_data);
       num_virtual_methods = it.NumVirtualMethods();
     }
-    const char* descriptor = dex_file->GetClassDescriptor(class_def);
+    const char* descriptor = dex_file.GetClassDescriptor(class_def);
     StackHandleScope<1> hs(soa.Self());
     mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor,
                                                    NullHandle<mirror::ClassLoader>());
@@ -189,7 +172,7 @@
 TEST_F(OatTest, OatHeaderSizeCheck) {
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
-  EXPECT_EQ(84U, sizeof(OatHeader));
+  EXPECT_EQ(72U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
   EXPECT_EQ(91 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 8a7abb4..9c0157e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -417,9 +417,6 @@
     size_interpreter_to_interpreter_bridge_(0),
     size_interpreter_to_compiled_code_bridge_(0),
     size_jni_dlsym_lookup_(0),
-    size_portable_imt_conflict_trampoline_(0),
-    size_portable_resolution_trampoline_(0),
-    size_portable_to_interpreter_bridge_(0),
     size_quick_generic_jni_trampoline_(0),
     size_quick_imt_conflict_trampoline_(0),
     size_quick_resolution_trampoline_(0),
@@ -507,7 +504,7 @@
 }
 
 struct OatWriter::GcMapDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return compiled_method->GetGcMap();
   }
 
@@ -529,8 +526,8 @@
 };
 
 struct OatWriter::MappingTableDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
-    return &compiled_method->GetMappingTable();
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+    return compiled_method->GetMappingTable();
   }
 
   static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
@@ -551,7 +548,7 @@
 };
 
 struct OatWriter::VmapTableDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetVmapTable();
   }
 
@@ -722,110 +719,101 @@
       // Derived from CompiledMethod.
       uint32_t quick_code_offset = 0;
 
-      const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
-      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
-      if (portable_code != nullptr) {
-        CHECK(quick_code == nullptr);
-        size_t oat_method_offsets_offset =
-            oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
-        compiled_method->AddOatdataOffsetToCompliledCodeOffset(
-            oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      CHECK(quick_code != nullptr);
+      offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
+      offset_ = compiled_method->AlignCode(offset_);
+      DCHECK_ALIGNED_PARAM(offset_,
+                           GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+      uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+      CHECK_NE(code_size, 0U);
+      uint32_t thumb_offset = compiled_method->CodeDelta();
+      quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
+
+      bool deduped = false;
+
+      // Deduplicate code arrays.
+      auto lb = dedupe_map_.lower_bound(compiled_method);
+      if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) {
+        quick_code_offset = lb->second;
+        deduped = true;
       } else {
-        CHECK(quick_code != nullptr);
-        offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
-        offset_ = compiled_method->AlignCode(offset_);
-        DCHECK_ALIGNED_PARAM(offset_,
-                             GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
-        uint32_t code_size = quick_code->size() * sizeof(uint8_t);
-        CHECK_NE(code_size, 0U);
-        uint32_t thumb_offset = compiled_method->CodeDelta();
-        quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
+        dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
+      }
 
-        bool deduped = false;
+      MethodReference method_ref(dex_file_, it.GetMemberIndex());
+      auto method_lb = writer_->method_offset_map_.lower_bound(method_ref);
+      if (method_lb != writer_->method_offset_map_.end() &&
+          !writer_->method_offset_map_.key_comp()(method_ref, method_lb->first)) {
+        // TODO: Should this be a hard failure?
+        LOG(WARNING) << "Multiple definitions of "
+            << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
+            << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : "");
+      } else {
+        writer_->method_offset_map_.PutBefore(method_lb, method_ref, quick_code_offset);
+      }
 
-        // Deduplicate code arrays.
-        auto lb = dedupe_map_.lower_bound(compiled_method);
-        if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) {
-          quick_code_offset = lb->second;
-          deduped = true;
-        } else {
-          dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
-        }
+      // Update quick method header.
+      DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+      OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+      uint32_t mapping_table_offset = method_header->mapping_table_offset_;
+      uint32_t vmap_table_offset = method_header->vmap_table_offset_;
+      uint32_t gc_map_offset = method_header->gc_map_offset_;
+      // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+      // to 0-offset and we need to adjust it by code_offset.
+      uint32_t code_offset = quick_code_offset - thumb_offset;
+      if (mapping_table_offset != 0u) {
+        mapping_table_offset += code_offset;
+        DCHECK_LT(mapping_table_offset, code_offset);
+      }
+      if (vmap_table_offset != 0u) {
+        vmap_table_offset += code_offset;
+        DCHECK_LT(vmap_table_offset, code_offset);
+      }
+      if (gc_map_offset != 0u) {
+        gc_map_offset += code_offset;
+        DCHECK_LT(gc_map_offset, code_offset);
+      }
+      uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
+      uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
+      uint32_t fp_spill_mask = compiled_method->GetFpSpillMask();
+      *method_header = OatQuickMethodHeader(mapping_table_offset, vmap_table_offset,
+                                            gc_map_offset, frame_size_in_bytes, core_spill_mask,
+                                            fp_spill_mask, code_size);
 
-        MethodReference method_ref(dex_file_, it.GetMemberIndex());
-        auto method_lb = writer_->method_offset_map_.lower_bound(method_ref);
-        if (method_lb != writer_->method_offset_map_.end() &&
-            !writer_->method_offset_map_.key_comp()(method_ref, method_lb->first)) {
-          // TODO: Should this be a hard failure?
-          LOG(WARNING) << "Multiple definitions of "
-              << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
-              << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : "");
-        } else {
-          writer_->method_offset_map_.PutBefore(method_lb, method_ref, quick_code_offset);
-        }
-
-        // Update quick method header.
-        DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
-        OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
-        uint32_t mapping_table_offset = method_header->mapping_table_offset_;
-        uint32_t vmap_table_offset = method_header->vmap_table_offset_;
-        uint32_t gc_map_offset = method_header->gc_map_offset_;
-        // The code offset was 0 when the mapping/vmap table offset was set, so it's set
-        // to 0-offset and we need to adjust it by code_offset.
-        uint32_t code_offset = quick_code_offset - thumb_offset;
-        if (mapping_table_offset != 0u) {
-          mapping_table_offset += code_offset;
-          DCHECK_LT(mapping_table_offset, code_offset);
-        }
-        if (vmap_table_offset != 0u) {
-          vmap_table_offset += code_offset;
-          DCHECK_LT(vmap_table_offset, code_offset);
-        }
-        if (gc_map_offset != 0u) {
-          gc_map_offset += code_offset;
-          DCHECK_LT(gc_map_offset, code_offset);
-        }
-        uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
-        uint32_t core_spill_mask = compiled_method->GetCoreSpillMask();
-        uint32_t fp_spill_mask = compiled_method->GetFpSpillMask();
-        *method_header = OatQuickMethodHeader(mapping_table_offset, vmap_table_offset,
-                                              gc_map_offset, frame_size_in_bytes, core_spill_mask,
-                                              fp_spill_mask, code_size);
-
-        if (!deduped) {
-          // Update offsets. (Checksum is updated when writing.)
-          offset_ += sizeof(*method_header);  // Method header is prepended before code.
-          offset_ += code_size;
-          // Record absolute patch locations.
-          if (!compiled_method->GetPatches().empty()) {
-            uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
-            for (const LinkerPatch& patch : compiled_method->GetPatches()) {
-              if (patch.Type() != kLinkerPatchCallRelative) {
-                writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
-              }
+      if (!deduped) {
+        // Update offsets. (Checksum is updated when writing.)
+        offset_ += sizeof(*method_header);  // Method header is prepended before code.
+        offset_ += code_size;
+        // Record absolute patch locations.
+        if (!compiled_method->GetPatches().empty()) {
+          uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
+          for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+            if (patch.Type() != kLinkerPatchCallRelative) {
+              writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
             }
           }
         }
+      }
 
-        if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
-          // Record debug information for this function if we are doing that.
+      if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+        // Record debug information for this function if we are doing that.
 
-          std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
-          if (deduped) {
-            // TODO We should place the DEDUPED tag on the first instance of a deduplicated symbol
-            // so that it will show up in a debuggerd crash report.
-            name += " [ DEDUPED ]";
-          }
-
-          const uint32_t quick_code_start = quick_code_offset -
-              writer_->oat_header_->GetExecutableOffset();
-          const DexFile::CodeItem *code_item = it.GetMethodCodeItem();
-          writer_->method_info_.push_back(DebugInfo(name,
-                dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)),
-                quick_code_start, quick_code_start + code_size,
-                code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item),
-                compiled_method));
+        std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+        if (deduped) {
+          // TODO We should place the DEDUPED tag on the first instance of a deduplicated symbol
+          // so that it will show up in a debuggerd crash report.
+          name += " [ DEDUPED ]";
         }
+
+        const uint32_t quick_code_start = quick_code_offset -
+            writer_->oat_header_->GetExecutableOffset();
+        const DexFile::CodeItem *code_item = it.GetMethodCodeItem();
+        writer_->method_info_.push_back(DebugInfo(name,
+              dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)),
+              quick_code_start, quick_code_start + code_size,
+              code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item),
+              compiled_method));
       }
 
       if (kIsDebugBuild) {
@@ -841,7 +829,7 @@
         } else {
           status = mirror::Class::kStatusNotReady;
         }
-        std::vector<uint8_t> const * gc_map = compiled_method->GetGcMap();
+        const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap();
         if (gc_map != nullptr) {
           size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
           bool is_native = it.MemberIsNative();
@@ -883,7 +871,7 @@
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
       DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
 
-      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
       uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
       if (map_size != 0u) {
         auto lb = dedupe_map_.lower_bound(map);
@@ -905,13 +893,14 @@
  private:
   // Deduplication is already done on a pointer basis by the compiler driver,
   // so we can simply compare the pointers to find out if things are duplicated.
-  SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+  SafeMap<const SwapVector<uint8_t>*, uint32_t> dedupe_map_;
 };
 
 class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
  public:
   InitImageMethodVisitor(OatWriter* writer, size_t offset)
-    : OatDexMethodVisitor(writer, offset) {
+    : OatDexMethodVisitor(writer, offset),
+      pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())) {
   }
 
   bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
@@ -944,11 +933,14 @@
       std::string dump = exc->Dump();
       LOG(FATAL) << dump;
     }
-    // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
-    method->SetQuickOatCodeOffset(offsets.code_offset_);
+    method->SetEntryPointFromQuickCompiledCodePtrSize(reinterpret_cast<void*>(offsets.code_offset_),
+                                                      pointer_size_);
 
     return true;
   }
+
+ protected:
+  const size_t pointer_size_;
 };
 
 class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
@@ -1003,9 +995,11 @@
       size_t file_offset = file_offset_;
       OutputStream* out = out_;
 
-      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       if (quick_code != nullptr) {
-        CHECK(compiled_method->GetPortableCode() == nullptr);
+        // Need a wrapper if we create a copy for patching.
+        ArrayRef<const uint8_t> wrapped(*quick_code);
+
         offset_ = writer_->relative_call_patcher_->WriteThunks(out, offset_);
         if (offset_ == 0u) {
           ReportWriteFailure("relative call thunk", it);
@@ -1044,8 +1038,8 @@
           DCHECK_OFFSET_();
 
           if (!compiled_method->GetPatches().empty()) {
-            patched_code_ =  *quick_code;
-            quick_code = &patched_code_;
+            patched_code_ = std::vector<uint8_t>(quick_code->begin(), quick_code->end());
+            wrapped = ArrayRef<const uint8_t>(patched_code_);
             for (const LinkerPatch& patch : compiled_method->GetPatches()) {
               if (patch.Type() == kLinkerPatchCallRelative) {
                 // NOTE: Relative calls across oat files are not supported.
@@ -1066,8 +1060,8 @@
             }
           }
 
-          writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
-          if (!out->WriteFully(&(*quick_code)[0], code_size)) {
+          writer_->oat_header_->UpdateChecksum(wrapped.data(), code_size);
+          if (!out->WriteFully(wrapped.data(), code_size)) {
             ReportWriteFailure("method code", it);
             return false;
           }
@@ -1114,10 +1108,18 @@
     if (UNLIKELY(target_offset == 0)) {
       mirror::ArtMethod* target = GetTargetMethod(patch);
       DCHECK(target != nullptr);
-      DCHECK_EQ(target->GetQuickOatCodeOffset(), 0u);
-      target_offset = target->IsNative()
-          ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
-          : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+      size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet());
+      const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size);
+      if (oat_code_offset != 0) {
+        DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
+        DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
+        DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset));
+        target_offset = PointerToLowMemUInt32(oat_code_offset);
+      } else {
+        target_offset = target->IsNative()
+            ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+            : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+      }
     }
     return target_offset;
   }
@@ -1149,10 +1151,9 @@
 
   void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // NOTE: Direct calls across oat files don't use linker patches.
-    DCHECK(writer_->image_writer_ != nullptr);
-    uint32_t address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
-                                             writer_->oat_data_offset_ + target_offset);
+    uint32_t address = writer_->image_writer_ == nullptr ? target_offset :
+        PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+                              writer_->oat_data_offset_ + target_offset);
     DCHECK_LE(offset + 4, code->size());
     uint8_t* data = &(*code)[offset];
     data[0] = address & 0xffu;
@@ -1184,7 +1185,7 @@
       ++method_offsets_index_;
 
       // Write deduplicated map.
-      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
       size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
       DCHECK((map_size == 0u && map_offset == 0u) ||
             (map_size != 0u && map_offset != 0u && map_offset <= offset_))
@@ -1357,9 +1358,6 @@
     DO_TRAMPOLINE(interpreter_to_interpreter_bridge_, InterpreterToInterpreterBridge);
     DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_, InterpreterToCompiledCodeBridge);
     DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
-    DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline);
-    DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline);
-    DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge);
     DO_TRAMPOLINE(quick_generic_jni_trampoline_, QuickGenericJniTrampoline);
     DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
     DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
@@ -1370,9 +1368,6 @@
     oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
     oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
     oat_header_->SetJniDlsymLookupOffset(0);
-    oat_header_->SetPortableImtConflictTrampolineOffset(0);
-    oat_header_->SetPortableResolutionTrampolineOffset(0);
-    oat_header_->SetPortableToInterpreterBridgeOffset(0);
     oat_header_->SetQuickGenericJniTrampolineOffset(0);
     oat_header_->SetQuickImtConflictTrampolineOffset(0);
     oat_header_->SetQuickResolutionTrampolineOffset(0);
@@ -1467,9 +1462,6 @@
     DO_STAT(size_interpreter_to_interpreter_bridge_);
     DO_STAT(size_interpreter_to_compiled_code_bridge_);
     DO_STAT(size_jni_dlsym_lookup_);
-    DO_STAT(size_portable_imt_conflict_trampoline_);
-    DO_STAT(size_portable_resolution_trampoline_);
-    DO_STAT(size_portable_to_interpreter_bridge_);
     DO_STAT(size_quick_generic_jni_trampoline_);
     DO_STAT(size_quick_imt_conflict_trampoline_);
     DO_STAT(size_quick_resolution_trampoline_);
@@ -1612,9 +1604,6 @@
     DO_TRAMPOLINE(interpreter_to_interpreter_bridge_);
     DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_);
     DO_TRAMPOLINE(jni_dlsym_lookup_);
-    DO_TRAMPOLINE(portable_imt_conflict_trampoline_);
-    DO_TRAMPOLINE(portable_resolution_trampoline_);
-    DO_TRAMPOLINE(portable_to_interpreter_bridge_);
     DO_TRAMPOLINE(quick_generic_jni_trampoline_);
     DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
     DO_TRAMPOLINE(quick_resolution_trampoline_);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index b3ac7ff..e020d31 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -214,10 +214,7 @@
     }
 
     // Offset of start of OatClass from beginning of OatHeader. It is
-    // used to validate file position when writing. For Portable, it
-    // is also used to calculate the position of the OatMethodOffsets
-    // so that code pointers within the OatMethodOffsets can be
-    // patched to point to code in the Portable .o ELF objects.
+    // used to validate file position when writing.
     size_t offset_;
 
     // CompiledMethods for each class_def_method_index, or NULL if no method is available.
@@ -285,9 +282,6 @@
   std::unique_ptr<const std::vector<uint8_t>> interpreter_to_interpreter_bridge_;
   std::unique_ptr<const std::vector<uint8_t>> interpreter_to_compiled_code_bridge_;
   std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
-  std::unique_ptr<const std::vector<uint8_t>> portable_imt_conflict_trampoline_;
-  std::unique_ptr<const std::vector<uint8_t>> portable_resolution_trampoline_;
-  std::unique_ptr<const std::vector<uint8_t>> portable_to_interpreter_bridge_;
   std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
   std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_;
   std::unique_ptr<const std::vector<uint8_t>> quick_resolution_trampoline_;
@@ -302,9 +296,6 @@
   uint32_t size_interpreter_to_interpreter_bridge_;
   uint32_t size_interpreter_to_compiled_code_bridge_;
   uint32_t size_jni_dlsym_lookup_;
-  uint32_t size_portable_imt_conflict_trampoline_;
-  uint32_t size_portable_resolution_trampoline_;
-  uint32_t size_portable_to_interpreter_bridge_;
   uint32_t size_quick_generic_jni_trampoline_;
   uint32_t size_quick_imt_conflict_trampoline_;
   uint32_t size_quick_resolution_trampoline_;
@@ -347,8 +338,8 @@
         return lhs->GetQuickCode() < rhs->GetQuickCode();
       }
       // If the code is the same, all other fields are likely to be the same as well.
-      if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) {
-        return &lhs->GetMappingTable() < &rhs->GetMappingTable();
+      if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
+        return lhs->GetMappingTable() < rhs->GetMappingTable();
       }
       if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
         return &lhs->GetVmapTable() < &rhs->GetVmapTable();
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
new file mode 100644
index 0000000..bcee563
--- /dev/null
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -0,0 +1,768 @@
+/*
+ * 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.
+ */
+
+#include "bounds_check_elimination.h"
+#include "nodes.h"
+#include "utils/arena_containers.h"
+
+namespace art {
+
+class MonotonicValueRange;
+
+/**
+ * A value bound is represented as a pair of value and constant,
+ * e.g. array.length - 1.
+ */
+class ValueBound : public ValueObject {
+ public:
+  ValueBound(HInstruction* instruction, int32_t constant) {
+    if (instruction != nullptr && instruction->IsIntConstant()) {
+      // Normalize ValueBound with constant instruction.
+      int32_t instr_const = instruction->AsIntConstant()->GetValue();
+      if (constant >= 0 && (instr_const <= INT_MAX - constant)) {
+        // No overflow.
+        instruction_ = nullptr;
+        constant_ = instr_const + constant;
+        return;
+      }
+      if (constant < 0 && (instr_const >= INT_MIN - constant)) {
+        // No underflow.
+        instruction_ = nullptr;
+        constant_ = instr_const + constant;
+        return;
+      }
+    }
+    instruction_ = instruction;
+    constant_ = constant;
+  }
+
+  static bool IsAddOrSubAConstant(HInstruction* instruction,
+                                  HInstruction** left_instruction,
+                                  int* right_constant) {
+    if (instruction->IsAdd() || instruction->IsSub()) {
+      HBinaryOperation* bin_op = instruction->AsBinaryOperation();
+      HInstruction* left = bin_op->GetLeft();
+      HInstruction* right = bin_op->GetRight();
+      if (right->IsIntConstant()) {
+        *left_instruction = left;
+        int32_t c = right->AsIntConstant()->GetValue();
+        *right_constant = instruction->IsAdd() ? c : -c;
+        return true;
+      }
+    }
+    *left_instruction = nullptr;
+    *right_constant = 0;
+    return false;
+  }
+
+  // Try to detect useful value bound format from an instruction, e.g.
+  // a constant or array length related value.
+  static ValueBound DetectValueBoundFromValue(HInstruction* instruction, bool* found) {
+    DCHECK(instruction != nullptr);
+    if (instruction->IsIntConstant()) {
+      *found = true;
+      return ValueBound(nullptr, instruction->AsIntConstant()->GetValue());
+    }
+
+    if (instruction->IsArrayLength()) {
+      *found = true;
+      return ValueBound(instruction, 0);
+    }
+    // Try to detect (array.length + c) format.
+    HInstruction *left;
+    int32_t right;
+    if (IsAddOrSubAConstant(instruction, &left, &right)) {
+      if (left->IsArrayLength()) {
+        *found = true;
+        return ValueBound(left, right);
+      }
+    }
+
+    // No useful bound detected.
+    *found = false;
+    return ValueBound::Max();
+  }
+
+  HInstruction* GetInstruction() const { return instruction_; }
+  int32_t GetConstant() const { return constant_; }
+
+  bool IsRelatedToArrayLength() const {
+    // Some bounds are created with HNewArray* as the instruction instead
+    // of HArrayLength*. They are treated the same.
+    return (instruction_ != nullptr) &&
+           (instruction_->IsArrayLength() || instruction_->IsNewArray());
+  }
+
+  bool IsConstant() const {
+    return instruction_ == nullptr;
+  }
+
+  static ValueBound Min() { return ValueBound(nullptr, INT_MIN); }
+  static ValueBound Max() { return ValueBound(nullptr, INT_MAX); }
+
+  bool Equals(ValueBound bound) const {
+    return instruction_ == bound.instruction_ && constant_ == bound.constant_;
+  }
+
+  static HInstruction* FromArrayLengthToNewArrayIfPossible(HInstruction* instruction) {
+    // Null check on the NewArray should have been eliminated by instruction
+    // simplifier already.
+    if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
+      return instruction->InputAt(0)->AsNewArray();
+    }
+    return instruction;
+  }
+
+  static bool Equal(HInstruction* instruction1, HInstruction* instruction2) {
+    if (instruction1 == instruction2) {
+      return true;
+    }
+
+    if (instruction1 == nullptr || instruction2 == nullptr) {
+      return false;
+    }
+
+    // Some bounds are created with HNewArray* as the instruction instead
+    // of HArrayLength*. They are treated the same.
+    instruction1 = FromArrayLengthToNewArrayIfPossible(instruction1);
+    instruction2 = FromArrayLengthToNewArrayIfPossible(instruction2);
+    return instruction1 == instruction2;
+  }
+
+  // Returns if it's certain this->bound >= `bound`.
+  bool GreaterThanOrEqualTo(ValueBound bound) const {
+    if (Equal(instruction_, bound.instruction_)) {
+      return constant_ >= bound.constant_;
+    }
+    // Not comparable. Just return false.
+    return false;
+  }
+
+  // Returns if it's certain this->bound <= `bound`.
+  bool LessThanOrEqualTo(ValueBound bound) const {
+    if (Equal(instruction_, bound.instruction_)) {
+      return constant_ <= bound.constant_;
+    }
+    // Not comparable. Just return false.
+    return false;
+  }
+
+  // Try to narrow lower bound. Returns the greatest of the two if possible.
+  // Pick one if they are not comparable.
+  static ValueBound NarrowLowerBound(ValueBound bound1, ValueBound bound2) {
+    if (bound1.GreaterThanOrEqualTo(bound2)) {
+      return bound1;
+    }
+    if (bound2.GreaterThanOrEqualTo(bound1)) {
+      return bound2;
+    }
+
+    // Not comparable. Just pick one. We may lose some info, but that's ok.
+    // Favor constant as lower bound.
+    return bound1.IsConstant() ? bound1 : bound2;
+  }
+
+  // Try to narrow upper bound. Returns the lowest of the two if possible.
+  // Pick one if they are not comparable.
+  static ValueBound NarrowUpperBound(ValueBound bound1, ValueBound bound2) {
+    if (bound1.LessThanOrEqualTo(bound2)) {
+      return bound1;
+    }
+    if (bound2.LessThanOrEqualTo(bound1)) {
+      return bound2;
+    }
+
+    // Not comparable. Just pick one. We may lose some info, but that's ok.
+    // Favor array length as upper bound.
+    return bound1.IsRelatedToArrayLength() ? bound1 : bound2;
+  }
+
+  // Add a constant to a ValueBound.
+  // `overflow` or `underflow` will return whether the resulting bound may
+  // overflow or underflow an int.
+  ValueBound Add(int32_t c, bool* overflow, bool* underflow) const {
+    *overflow = *underflow = false;
+    if (c == 0) {
+      return *this;
+    }
+
+    int32_t new_constant;
+    if (c > 0) {
+      if (constant_ > INT_MAX - c) {
+        *overflow = true;
+        return Max();
+      }
+
+      new_constant = constant_ + c;
+      // (array.length + non-positive-constant) won't overflow an int.
+      if (IsConstant() || (IsRelatedToArrayLength() && new_constant <= 0)) {
+        return ValueBound(instruction_, new_constant);
+      }
+      // Be conservative.
+      *overflow = true;
+      return Max();
+    } else {
+      if (constant_ < INT_MIN - c) {
+        *underflow = true;
+        return Min();
+      }
+
+      new_constant = constant_ + c;
+      // Regardless of the value new_constant, (array.length+new_constant) will
+      // never underflow since array.length is no less than 0.
+      if (IsConstant() || IsRelatedToArrayLength()) {
+        return ValueBound(instruction_, new_constant);
+      }
+      // Be conservative.
+      *underflow = true;
+      return Min();
+    }
+    return ValueBound(instruction_, new_constant);
+  }
+
+ private:
+  HInstruction* instruction_;
+  int32_t constant_;
+};
+
+/**
+ * Represent a range of lower bound and upper bound, both being inclusive.
+ * Currently a ValueRange may be generated as a result of the following:
+ * comparisons related to array bounds, array bounds check, add/sub on top
+ * of an existing value range, NewArray or a loop phi corresponding to an
+ * incrementing/decrementing array index (MonotonicValueRange).
+ */
+class ValueRange : public ArenaObject<kArenaAllocMisc> {
+ public:
+  ValueRange(ArenaAllocator* allocator, ValueBound lower, ValueBound upper)
+      : allocator_(allocator), lower_(lower), upper_(upper) {}
+
+  virtual ~ValueRange() {}
+
+  virtual const MonotonicValueRange* AsMonotonicValueRange() const { return nullptr; }
+  bool IsMonotonicValueRange() const {
+    return AsMonotonicValueRange() != nullptr;
+  }
+
+  ArenaAllocator* GetAllocator() const { return allocator_; }
+  ValueBound GetLower() const { return lower_; }
+  ValueBound GetUpper() const { return upper_; }
+
+  // If it's certain that this value range fits in other_range.
+  virtual bool FitsIn(ValueRange* other_range) const {
+    if (other_range == nullptr) {
+      return true;
+    }
+    DCHECK(!other_range->IsMonotonicValueRange());
+    return lower_.GreaterThanOrEqualTo(other_range->lower_) &&
+           upper_.LessThanOrEqualTo(other_range->upper_);
+  }
+
+  // Returns the intersection of this and range.
+  // If it's not possible to do intersection because some
+  // bounds are not comparable, it's ok to pick either bound.
+  virtual ValueRange* Narrow(ValueRange* range) {
+    if (range == nullptr) {
+      return this;
+    }
+
+    if (range->IsMonotonicValueRange()) {
+      return this;
+    }
+
+    return new (allocator_) ValueRange(
+        allocator_,
+        ValueBound::NarrowLowerBound(lower_, range->lower_),
+        ValueBound::NarrowUpperBound(upper_, range->upper_));
+  }
+
+  // Shift a range by a constant.
+  ValueRange* Add(int32_t constant) const {
+    bool overflow, underflow;
+    ValueBound lower = lower_.Add(constant, &overflow, &underflow);
+    if (underflow) {
+      // Lower bound underflow will wrap around to positive values
+      // and invalidate the upper bound.
+      return nullptr;
+    }
+    ValueBound upper = upper_.Add(constant, &overflow, &underflow);
+    if (overflow) {
+      // Upper bound overflow will wrap around to negative values
+      // and invalidate the lower bound.
+      return nullptr;
+    }
+    return new (allocator_) ValueRange(allocator_, lower, upper);
+  }
+
+ private:
+  ArenaAllocator* const allocator_;
+  const ValueBound lower_;  // inclusive
+  const ValueBound upper_;  // inclusive
+
+  DISALLOW_COPY_AND_ASSIGN(ValueRange);
+};
+
+/**
+ * A monotonically incrementing/decrementing value range, e.g.
+ * the variable i in "for (int i=0; i<array.length; i++)".
+ * Special care needs to be taken to account for overflow/underflow
+ * of such value ranges.
+ */
+class MonotonicValueRange : public ValueRange {
+ public:
+  MonotonicValueRange(ArenaAllocator* allocator,
+                      HInstruction* initial,
+                      int32_t increment,
+                      ValueBound bound)
+      // To be conservative, give it full range [INT_MIN, INT_MAX] in case it's
+      // used as a regular value range, due to possible overflow/underflow.
+      : ValueRange(allocator, ValueBound::Min(), ValueBound::Max()),
+        initial_(initial),
+        increment_(increment),
+        bound_(bound) {}
+
+  virtual ~MonotonicValueRange() {}
+
+  const MonotonicValueRange* AsMonotonicValueRange() const OVERRIDE { return this; }
+
+  // If it's certain that this value range fits in other_range.
+  bool FitsIn(ValueRange* other_range) const OVERRIDE {
+    if (other_range == nullptr) {
+      return true;
+    }
+    DCHECK(!other_range->IsMonotonicValueRange());
+    return false;
+  }
+
+  // Try to narrow this MonotonicValueRange given another range.
+  // Ideally it will return a normal ValueRange. But due to
+  // possible overflow/underflow, that may not be possible.
+  ValueRange* Narrow(ValueRange* range) OVERRIDE {
+    if (range == nullptr) {
+      return this;
+    }
+    DCHECK(!range->IsMonotonicValueRange());
+
+    if (increment_ > 0) {
+      // Monotonically increasing.
+      ValueBound lower = ValueBound::NarrowLowerBound(bound_, range->GetLower());
+
+      // We currently conservatively assume max array length is INT_MAX. If we can
+      // make assumptions about the max array length, e.g. due to the max heap size,
+      // divided by the element size (such as 4 bytes for each integer array), we can
+      // lower this number and rule out some possible overflows.
+      int32_t max_array_len = INT_MAX;
+
+      // max possible integer value of range's upper value.
+      int32_t upper = INT_MAX;
+      // Try to lower upper.
+      ValueBound upper_bound = range->GetUpper();
+      if (upper_bound.IsConstant()) {
+        upper = upper_bound.GetConstant();
+      } else if (upper_bound.IsRelatedToArrayLength() && upper_bound.GetConstant() <= 0) {
+        // Normal case. e.g. <= array.length - 1.
+        upper = max_array_len + upper_bound.GetConstant();
+      }
+
+      // If we can prove for the last number in sequence of initial_,
+      // initial_ + increment_, initial_ + 2 x increment_, ...
+      // that's <= upper, (last_num_in_sequence + increment_) doesn't trigger overflow,
+      // then this MonoticValueRange is narrowed to a normal value range.
+
+      // Be conservative first, assume last number in the sequence hits upper.
+      int32_t last_num_in_sequence = upper;
+      if (initial_->IsIntConstant()) {
+        int32_t initial_constant = initial_->AsIntConstant()->GetValue();
+        if (upper <= initial_constant) {
+          last_num_in_sequence = upper;
+        } else {
+          // Cast to int64_t for the substraction part to avoid int32_t overflow.
+          last_num_in_sequence = initial_constant +
+              ((int64_t)upper - (int64_t)initial_constant) / increment_ * increment_;
+        }
+      }
+      if (last_num_in_sequence <= INT_MAX - increment_) {
+        // No overflow. The sequence will be stopped by the upper bound test as expected.
+        return new (GetAllocator()) ValueRange(GetAllocator(), lower, range->GetUpper());
+      }
+
+      // There might be overflow. Give up narrowing.
+      return this;
+    } else {
+      DCHECK_NE(increment_, 0);
+      // Monotonically decreasing.
+      ValueBound upper = ValueBound::NarrowUpperBound(bound_, range->GetUpper());
+
+      // Need to take care of underflow. Try to prove underflow won't happen
+      // for common cases.
+      if (range->GetLower().IsConstant()) {
+        int32_t constant = range->GetLower().GetConstant();
+        if (constant >= INT_MIN - increment_) {
+          return new (GetAllocator()) ValueRange(GetAllocator(), range->GetLower(), upper);
+        }
+      }
+
+      // For non-constant lower bound, just assume might be underflow. Give up narrowing.
+      return this;
+    }
+  }
+
+ private:
+  HInstruction* const initial_;
+  const int32_t increment_;
+  ValueBound bound_;  // Additional value bound info for initial_;
+
+  DISALLOW_COPY_AND_ASSIGN(MonotonicValueRange);
+};
+
+class BCEVisitor : public HGraphVisitor {
+ public:
+  explicit BCEVisitor(HGraph* graph)
+      : HGraphVisitor(graph),
+        maps_(graph->GetBlocks().Size()) {}
+
+ private:
+  // Return the map of proven value ranges at the beginning of a basic block.
+  ArenaSafeMap<int, ValueRange*>* GetValueRangeMap(HBasicBlock* basic_block) {
+    int block_id = basic_block->GetBlockId();
+    if (maps_.at(block_id) == nullptr) {
+      std::unique_ptr<ArenaSafeMap<int, ValueRange*>> map(
+          new ArenaSafeMap<int, ValueRange*>(
+              std::less<int>(), GetGraph()->GetArena()->Adapter()));
+      maps_.at(block_id) = std::move(map);
+    }
+    return maps_.at(block_id).get();
+  }
+
+  // Traverse up the dominator tree to look for value range info.
+  ValueRange* LookupValueRange(HInstruction* instruction, HBasicBlock* basic_block) {
+    while (basic_block != nullptr) {
+      ArenaSafeMap<int, ValueRange*>* map = GetValueRangeMap(basic_block);
+      if (map->find(instruction->GetId()) != map->end()) {
+        return map->Get(instruction->GetId());
+      }
+      basic_block = basic_block->GetDominator();
+    }
+    // Didn't find any.
+    return nullptr;
+  }
+
+  // Narrow the value range of `instruction` at the end of `basic_block` with `range`,
+  // and push the narrowed value range to `successor`.
+  void ApplyRangeFromComparison(HInstruction* instruction, HBasicBlock* basic_block,
+                  HBasicBlock* successor, ValueRange* range) {
+    ValueRange* existing_range = LookupValueRange(instruction, basic_block);
+    ValueRange* narrowed_range = (existing_range == nullptr) ?
+        range : existing_range->Narrow(range);
+    if (narrowed_range != nullptr) {
+      GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
+    }
+  }
+
+  // Handle "if (left cmp_cond right)".
+  void HandleIf(HIf* instruction, HInstruction* left, HInstruction* right, IfCondition cond) {
+    HBasicBlock* block = instruction->GetBlock();
+
+    HBasicBlock* true_successor = instruction->IfTrueSuccessor();
+    // There should be no critical edge at this point.
+    DCHECK_EQ(true_successor->GetPredecessors().Size(), 1u);
+
+    HBasicBlock* false_successor = instruction->IfFalseSuccessor();
+    // There should be no critical edge at this point.
+    DCHECK_EQ(false_successor->GetPredecessors().Size(), 1u);
+
+    bool found;
+    ValueBound bound = ValueBound::DetectValueBoundFromValue(right, &found);
+    // Each comparison can establish a lower bound and an upper bound
+    // for the left hand side.
+    ValueBound lower = bound;
+    ValueBound upper = bound;
+    if (!found) {
+      // No constant or array.length+c format bound found.
+      // For i<j, we can still use j's upper bound as i's upper bound. Same for lower.
+      ValueRange* range = LookupValueRange(right, block);
+      if (range != nullptr) {
+        lower = range->GetLower();
+        upper = range->GetUpper();
+      } else {
+        lower = ValueBound::Min();
+        upper = ValueBound::Max();
+      }
+    }
+
+    bool overflow, underflow;
+    if (cond == kCondLT || cond == kCondLE) {
+      if (!upper.Equals(ValueBound::Max())) {
+        int32_t compensation = (cond == kCondLT) ? -1 : 0;  // upper bound is inclusive
+        ValueBound new_upper = upper.Add(compensation, &overflow, &underflow);
+        if (overflow || underflow) {
+          return;
+        }
+        ValueRange* new_range = new (GetGraph()->GetArena())
+            ValueRange(GetGraph()->GetArena(), ValueBound::Min(), new_upper);
+        ApplyRangeFromComparison(left, block, true_successor, new_range);
+      }
+
+      // array.length as a lower bound isn't considered useful.
+      if (!lower.Equals(ValueBound::Min()) && !lower.IsRelatedToArrayLength()) {
+        int32_t compensation = (cond == kCondLE) ? 1 : 0;  // lower bound is inclusive
+        ValueBound new_lower = lower.Add(compensation, &overflow, &underflow);
+        if (overflow || underflow) {
+          return;
+        }
+        ValueRange* new_range = new (GetGraph()->GetArena())
+            ValueRange(GetGraph()->GetArena(), new_lower, ValueBound::Max());
+        ApplyRangeFromComparison(left, block, false_successor, new_range);
+      }
+    } else if (cond == kCondGT || cond == kCondGE) {
+      // array.length as a lower bound isn't considered useful.
+      if (!lower.Equals(ValueBound::Min()) && !lower.IsRelatedToArrayLength()) {
+        int32_t compensation = (cond == kCondGT) ? 1 : 0;  // lower bound is inclusive
+        ValueBound new_lower = lower.Add(compensation, &overflow, &underflow);
+        if (overflow || underflow) {
+          return;
+        }
+        ValueRange* new_range = new (GetGraph()->GetArena())
+            ValueRange(GetGraph()->GetArena(), new_lower, ValueBound::Max());
+        ApplyRangeFromComparison(left, block, true_successor, new_range);
+      }
+
+      if (!upper.Equals(ValueBound::Max())) {
+        int32_t compensation = (cond == kCondGE) ? -1 : 0;  // upper bound is inclusive
+        ValueBound new_upper = upper.Add(compensation, &overflow, &underflow);
+        if (overflow || underflow) {
+          return;
+        }
+        ValueRange* new_range = new (GetGraph()->GetArena())
+            ValueRange(GetGraph()->GetArena(), ValueBound::Min(), new_upper);
+        ApplyRangeFromComparison(left, block, false_successor, new_range);
+      }
+    }
+  }
+
+  void VisitBoundsCheck(HBoundsCheck* bounds_check) {
+    HBasicBlock* block = bounds_check->GetBlock();
+    HInstruction* index = bounds_check->InputAt(0);
+    HInstruction* array_length = bounds_check->InputAt(1);
+    DCHECK(array_length->IsIntConstant() || array_length->IsArrayLength());
+
+    if (!index->IsIntConstant()) {
+      ValueRange* index_range = LookupValueRange(index, block);
+      if (index_range != nullptr) {
+        ValueBound lower = ValueBound(nullptr, 0);        // constant 0
+        ValueBound upper = ValueBound(array_length, -1);  // array_length - 1
+        ValueRange* array_range = new (GetGraph()->GetArena())
+            ValueRange(GetGraph()->GetArena(), lower, upper);
+        if (index_range->FitsIn(array_range)) {
+          ReplaceBoundsCheck(bounds_check, index);
+          return;
+        }
+      }
+    } else {
+      int32_t constant = index->AsIntConstant()->GetValue();
+      if (constant < 0) {
+        // Will always throw exception.
+        return;
+      }
+      if (array_length->IsIntConstant()) {
+        if (constant < array_length->AsIntConstant()->GetValue()) {
+          ReplaceBoundsCheck(bounds_check, index);
+        }
+        return;
+      }
+
+      DCHECK(array_length->IsArrayLength());
+      ValueRange* existing_range = LookupValueRange(array_length, block);
+      if (existing_range != nullptr) {
+        ValueBound lower = existing_range->GetLower();
+        DCHECK(lower.IsConstant());
+        if (constant < lower.GetConstant()) {
+          ReplaceBoundsCheck(bounds_check, index);
+          return;
+        } else {
+          // Existing range isn't strong enough to eliminate the bounds check.
+          // Fall through to update the array_length range with info from this
+          // bounds check.
+        }
+      }
+
+      // Once we have an array access like 'array[5] = 1', we record array.length >= 6.
+      // We currently don't do it for non-constant index since a valid array[i] can't prove
+      // a valid array[i-1] yet due to the lower bound side.
+      ValueBound lower = ValueBound(nullptr, constant + 1);
+      ValueBound upper = ValueBound::Max();
+      ValueRange* range = new (GetGraph()->GetArena())
+          ValueRange(GetGraph()->GetArena(), lower, upper);
+      GetValueRangeMap(block)->Overwrite(array_length->GetId(), range);
+    }
+  }
+
+  void ReplaceBoundsCheck(HInstruction* bounds_check, HInstruction* index) {
+    bounds_check->ReplaceWith(index);
+    bounds_check->GetBlock()->RemoveInstruction(bounds_check);
+  }
+
+  void VisitPhi(HPhi* phi) {
+    if (phi->IsLoopHeaderPhi() && phi->GetType() == Primitive::kPrimInt) {
+      DCHECK_EQ(phi->InputCount(), 2U);
+      HInstruction* instruction = phi->InputAt(1);
+      HInstruction *left;
+      int32_t increment;
+      if (ValueBound::IsAddOrSubAConstant(instruction, &left, &increment)) {
+        if (left == phi) {
+          HInstruction* initial_value = phi->InputAt(0);
+          ValueRange* range = nullptr;
+          if (increment == 0) {
+            // Add constant 0. It's really a fixed value.
+            range = new (GetGraph()->GetArena()) ValueRange(
+                GetGraph()->GetArena(),
+                ValueBound(initial_value, 0),
+                ValueBound(initial_value, 0));
+          } else {
+            // Monotonically increasing/decreasing.
+            bool found;
+            ValueBound bound = ValueBound::DetectValueBoundFromValue(
+                initial_value, &found);
+            if (!found) {
+              // No constant or array.length+c bound found.
+              // For i=j, we can still use j's upper bound as i's upper bound.
+              // Same for lower.
+              ValueRange* initial_range = LookupValueRange(initial_value, phi->GetBlock());
+              if (initial_range != nullptr) {
+                bound = increment > 0 ? initial_range->GetLower() :
+                                        initial_range->GetUpper();
+              } else {
+                bound = increment > 0 ? ValueBound::Min() : ValueBound::Max();
+              }
+            }
+            range = new (GetGraph()->GetArena()) MonotonicValueRange(
+                GetGraph()->GetArena(),
+                initial_value,
+                increment,
+                bound);
+          }
+          GetValueRangeMap(phi->GetBlock())->Overwrite(phi->GetId(), range);
+        }
+      }
+    }
+  }
+
+  void VisitIf(HIf* instruction) {
+    if (instruction->InputAt(0)->IsCondition()) {
+      HCondition* cond = instruction->InputAt(0)->AsCondition();
+      IfCondition cmp = cond->GetCondition();
+      if (cmp == kCondGT || cmp == kCondGE ||
+          cmp == kCondLT || cmp == kCondLE) {
+        HInstruction* left = cond->GetLeft();
+        HInstruction* right = cond->GetRight();
+        HandleIf(instruction, left, right, cmp);
+      }
+    }
+  }
+
+  void VisitAdd(HAdd* add) {
+    HInstruction* right = add->GetRight();
+    if (right->IsIntConstant()) {
+      ValueRange* left_range = LookupValueRange(add->GetLeft(), add->GetBlock());
+      if (left_range == nullptr) {
+        return;
+      }
+      ValueRange* range = left_range->Add(right->AsIntConstant()->GetValue());
+      if (range != nullptr) {
+        GetValueRangeMap(add->GetBlock())->Overwrite(add->GetId(), range);
+      }
+    }
+  }
+
+  void VisitSub(HSub* sub) {
+    HInstruction* left = sub->GetLeft();
+    HInstruction* right = sub->GetRight();
+    if (right->IsIntConstant()) {
+      ValueRange* left_range = LookupValueRange(left, sub->GetBlock());
+      if (left_range == nullptr) {
+        return;
+      }
+      ValueRange* range = left_range->Add(-right->AsIntConstant()->GetValue());
+      if (range != nullptr) {
+        GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+        return;
+      }
+    }
+
+    // Here we are interested in the typical triangular case of nested loops,
+    // such as the inner loop 'for (int j=0; j<array.length-i; j++)' where i
+    // is the index for outer loop. In this case, we know j is bounded by array.length-1.
+    if (left->IsArrayLength()) {
+      HInstruction* array_length = left->AsArrayLength();
+      ValueRange* right_range = LookupValueRange(right, sub->GetBlock());
+      if (right_range != nullptr) {
+        ValueBound lower = right_range->GetLower();
+        ValueBound upper = right_range->GetUpper();
+        if (lower.IsConstant() && upper.IsRelatedToArrayLength()) {
+          HInstruction* upper_inst = upper.GetInstruction();
+          // Make sure it's the same array.
+          if (ValueBound::Equal(array_length, upper_inst)) {
+            // (array.length - v) where v is in [c1, array.length + c2]
+            // gets [-c2, array.length - c1] as its value range.
+            ValueRange* range = new (GetGraph()->GetArena()) ValueRange(
+                GetGraph()->GetArena(),
+                ValueBound(nullptr, - upper.GetConstant()),
+                ValueBound(array_length, - lower.GetConstant()));
+            GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+          }
+        }
+      }
+    }
+  }
+
+  void VisitNewArray(HNewArray* new_array) {
+    HInstruction* len = new_array->InputAt(0);
+    if (!len->IsIntConstant()) {
+      HInstruction *left;
+      int32_t right_const;
+      if (ValueBound::IsAddOrSubAConstant(len, &left, &right_const)) {
+        // (left + right_const) is used as size to new the array.
+        // We record "-right_const <= left <= new_array - right_const";
+        ValueBound lower = ValueBound(nullptr, -right_const);
+        // We use new_array for the bound instead of new_array.length,
+        // which isn't available as an instruction yet. new_array will
+        // be treated the same as new_array.length when it's used in a ValueBound.
+        ValueBound upper = ValueBound(new_array, -right_const);
+        ValueRange* range = new (GetGraph()->GetArena())
+            ValueRange(GetGraph()->GetArena(), lower, upper);
+        GetValueRangeMap(new_array->GetBlock())->Overwrite(left->GetId(), range);
+      }
+    }
+  }
+
+  std::vector<std::unique_ptr<ArenaSafeMap<int, ValueRange*>>> maps_;
+
+  DISALLOW_COPY_AND_ASSIGN(BCEVisitor);
+};
+
+void BoundsCheckElimination::Run() {
+  BCEVisitor visitor(graph_);
+  // Reverse post order guarantees a node's dominators are visited first.
+  // We want to visit in the dominator-based order since if a value is known to
+  // be bounded by a range at one instruction, it must be true that all uses of
+  // that value dominated by that instruction fits in that range. Range of that
+  // value can be narrowed further down in the dominator tree.
+  //
+  // TODO: only visit blocks that dominate some array accesses.
+  visitor.VisitReversePostOrder();
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h
new file mode 100644
index 0000000..05cb185
--- /dev/null
+++ b/compiler/optimizing/bounds_check_elimination.h
@@ -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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
+
+#include "optimization.h"
+
+namespace art {
+
+class BoundsCheckElimination : public HOptimization {
+ public:
+  explicit BoundsCheckElimination(HGraph* graph) : HOptimization(graph, true, "BCE") {}
+
+  void Run() OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BoundsCheckElimination);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
new file mode 100644
index 0000000..662834a
--- /dev/null
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -0,0 +1,1055 @@
+/*
+ * 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.
+ */
+
+#include "bounds_check_elimination.h"
+#include "builder.h"
+#include "gvn.h"
+#include "instruction_simplifier.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "side_effects_analysis.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void RunSimplifierAndGvn(HGraph* graph) {
+  InstructionSimplifier simplify(graph);
+  simplify.Run();
+  SideEffectsAnalysis side_effects(graph);
+  side_effects.Run();
+  GVNOptimization(graph, side_effects).Run();
+}
+
+// if (i < 0) { array[i] = 1; // Can't eliminate. }
+// else if (i >= array.length) { array[i] = 1; // Can't eliminate. }
+// else { array[i] = 1; // Can eliminate. }
+TEST(BoundsCheckEliminationTest, NarrowingRangeArrayBoundsElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter1 = new (&allocator)
+      HParameterValue(0, Primitive::kPrimNot);  // array
+  HInstruction* parameter2 = new (&allocator)
+      HParameterValue(0, Primitive::kPrimInt);  // i
+  HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+  HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+  entry->AddInstruction(parameter1);
+  entry->AddInstruction(parameter2);
+  entry->AddInstruction(constant_1);
+  entry->AddInstruction(constant_0);
+
+  HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block1);
+  HInstruction* cmp = new (&allocator) HGreaterThanOrEqual(parameter2, constant_0);
+  HIf* if_inst = new (&allocator) HIf(cmp);
+  block1->AddInstruction(cmp);
+  block1->AddInstruction(if_inst);
+  entry->AddSuccessor(block1);
+
+  HBasicBlock* block2 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block2);
+  HNullCheck* null_check = new (&allocator) HNullCheck(parameter1, 0);
+  HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check2 = new (&allocator)
+      HBoundsCheck(parameter2, array_length, 0);
+  HArraySet* array_set = new (&allocator) HArraySet(
+    null_check, bounds_check2, constant_1, Primitive::kPrimInt, 0);
+  block2->AddInstruction(null_check);
+  block2->AddInstruction(array_length);
+  block2->AddInstruction(bounds_check2);
+  block2->AddInstruction(array_set);
+
+  HBasicBlock* block3 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block3);
+  null_check = new (&allocator) HNullCheck(parameter1, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  cmp = new (&allocator) HLessThan(parameter2, array_length);
+  if_inst = new (&allocator) HIf(cmp);
+  block3->AddInstruction(null_check);
+  block3->AddInstruction(array_length);
+  block3->AddInstruction(cmp);
+  block3->AddInstruction(if_inst);
+
+  HBasicBlock* block4 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block4);
+  null_check = new (&allocator) HNullCheck(parameter1, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check4 = new (&allocator)
+      HBoundsCheck(parameter2, array_length, 0);
+  array_set = new (&allocator) HArraySet(
+    null_check, bounds_check4, constant_1, Primitive::kPrimInt, 0);
+  block4->AddInstruction(null_check);
+  block4->AddInstruction(array_length);
+  block4->AddInstruction(bounds_check4);
+  block4->AddInstruction(array_set);
+
+  HBasicBlock* block5 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block5);
+  null_check = new (&allocator) HNullCheck(parameter1, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check5 = new (&allocator)
+      HBoundsCheck(parameter2, array_length, 0);
+  array_set = new (&allocator) HArraySet(
+    null_check, bounds_check5, constant_1, Primitive::kPrimInt, 0);
+  block5->AddInstruction(null_check);
+  block5->AddInstruction(array_length);
+  block5->AddInstruction(bounds_check5);
+  block5->AddInstruction(array_set);
+
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit);
+  block2->AddSuccessor(exit);
+  block4->AddSuccessor(exit);
+  block5->AddSuccessor(exit);
+  exit->AddInstruction(new (&allocator) HExit());
+
+  block1->AddSuccessor(block3);  // True successor
+  block1->AddSuccessor(block2);  // False successor
+
+  block3->AddSuccessor(block5);  // True successor
+  block3->AddSuccessor(block4);  // False successor
+
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check2));
+  ASSERT_FALSE(IsRemoved(bounds_check4));
+  ASSERT_TRUE(IsRemoved(bounds_check5));
+}
+
+// if (i > 0) {
+//   // Positive number plus MAX_INT will overflow and be negative.
+//   int j = i + Integer.MAX_VALUE;
+//   if (j < array.length) array[j] = 1;  // Can't eliminate.
+// }
+TEST(BoundsCheckEliminationTest, OverflowArrayBoundsElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter1 = new (&allocator)
+      HParameterValue(0, Primitive::kPrimNot);  // array
+  HInstruction* parameter2 = new (&allocator)
+      HParameterValue(0, Primitive::kPrimInt);  // i
+  HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+  HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+  HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX);
+  entry->AddInstruction(parameter1);
+  entry->AddInstruction(parameter2);
+  entry->AddInstruction(constant_1);
+  entry->AddInstruction(constant_0);
+  entry->AddInstruction(constant_max_int);
+
+  HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block1);
+  HInstruction* cmp = new (&allocator) HLessThanOrEqual(parameter2, constant_0);
+  HIf* if_inst = new (&allocator) HIf(cmp);
+  block1->AddInstruction(cmp);
+  block1->AddInstruction(if_inst);
+  entry->AddSuccessor(block1);
+
+  HBasicBlock* block2 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block2);
+  HInstruction* add = new (&allocator) HAdd(Primitive::kPrimInt, parameter2, constant_max_int);
+  HNullCheck* null_check = new (&allocator) HNullCheck(parameter1, 0);
+  HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+  HInstruction* cmp2 = new (&allocator) HGreaterThanOrEqual(add, array_length);
+  if_inst = new (&allocator) HIf(cmp2);
+  block2->AddInstruction(add);
+  block2->AddInstruction(null_check);
+  block2->AddInstruction(array_length);
+  block2->AddInstruction(cmp2);
+  block2->AddInstruction(if_inst);
+
+  HBasicBlock* block3 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block3);
+  HBoundsCheck* bounds_check = new (&allocator)
+      HBoundsCheck(add, array_length, 0);
+  HArraySet* array_set = new (&allocator) HArraySet(
+    null_check, bounds_check, constant_1, Primitive::kPrimInt, 0);
+  block3->AddInstruction(bounds_check);
+  block3->AddInstruction(array_set);
+
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit);
+  exit->AddInstruction(new (&allocator) HExit());
+  block1->AddSuccessor(exit);    // true successor
+  block1->AddSuccessor(block2);  // false successor
+  block2->AddSuccessor(exit);    // true successor
+  block2->AddSuccessor(block3);  // false successor
+  block3->AddSuccessor(exit);
+
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+}
+
+// if (i < array.length) {
+//   int j = i - Integer.MAX_VALUE;
+//   j = j - Integer.MAX_VALUE;  // j is (i+2) after substracting MAX_INT twice
+//   if (j > 0) array[j] = 1;    // Can't eliminate.
+// }
+TEST(BoundsCheckEliminationTest, UnderflowArrayBoundsElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter1 = new (&allocator)
+      HParameterValue(0, Primitive::kPrimNot);  // array
+  HInstruction* parameter2 = new (&allocator)
+      HParameterValue(0, Primitive::kPrimInt);  // i
+  HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+  HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+  HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX);
+  entry->AddInstruction(parameter1);
+  entry->AddInstruction(parameter2);
+  entry->AddInstruction(constant_1);
+  entry->AddInstruction(constant_0);
+  entry->AddInstruction(constant_max_int);
+
+  HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block1);
+  HNullCheck* null_check = new (&allocator) HNullCheck(parameter1, 0);
+  HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+  HInstruction* cmp = new (&allocator) HGreaterThanOrEqual(parameter2, array_length);
+  HIf* if_inst = new (&allocator) HIf(cmp);
+  block1->AddInstruction(null_check);
+  block1->AddInstruction(array_length);
+  block1->AddInstruction(cmp);
+  block1->AddInstruction(if_inst);
+  entry->AddSuccessor(block1);
+
+  HBasicBlock* block2 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block2);
+  HInstruction* sub1 = new (&allocator) HSub(Primitive::kPrimInt, parameter2, constant_max_int);
+  HInstruction* sub2 = new (&allocator) HSub(Primitive::kPrimInt, sub1, constant_max_int);
+  HInstruction* cmp2 = new (&allocator) HLessThanOrEqual(sub2, constant_0);
+  if_inst = new (&allocator) HIf(cmp2);
+  block2->AddInstruction(sub1);
+  block2->AddInstruction(sub2);
+  block2->AddInstruction(cmp2);
+  block2->AddInstruction(if_inst);
+
+  HBasicBlock* block3 = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block3);
+  HBoundsCheck* bounds_check = new (&allocator)
+      HBoundsCheck(sub2, array_length, 0);
+  HArraySet* array_set = new (&allocator) HArraySet(
+    null_check, bounds_check, constant_1, Primitive::kPrimInt, 0);
+  block3->AddInstruction(bounds_check);
+  block3->AddInstruction(array_set);
+
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit);
+  exit->AddInstruction(new (&allocator) HExit());
+  block1->AddSuccessor(exit);    // true successor
+  block1->AddSuccessor(block2);  // false successor
+  block2->AddSuccessor(exit);    // true successor
+  block2->AddSuccessor(block3);  // false successor
+  block3->AddSuccessor(exit);
+
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+}
+
+// array[5] = 1; // Can't eliminate.
+// array[4] = 1; // Can eliminate.
+// array[6] = 1; // Can't eliminate.
+TEST(BoundsCheckEliminationTest, ConstantArrayBoundsElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  HInstruction* constant_5 = new (&allocator) HIntConstant(5);
+  HInstruction* constant_4 = new (&allocator) HIntConstant(4);
+  HInstruction* constant_6 = new (&allocator) HIntConstant(6);
+  HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(constant_5);
+  entry->AddInstruction(constant_4);
+  entry->AddInstruction(constant_6);
+  entry->AddInstruction(constant_1);
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+
+  HNullCheck* null_check = new (&allocator) HNullCheck(parameter, 0);
+  HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check5 = new (&allocator)
+      HBoundsCheck(constant_5, array_length, 0);
+  HInstruction* array_set = new (&allocator) HArraySet(
+    null_check, bounds_check5, constant_1, Primitive::kPrimInt, 0);
+  block->AddInstruction(null_check);
+  block->AddInstruction(array_length);
+  block->AddInstruction(bounds_check5);
+  block->AddInstruction(array_set);
+
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check4 = new (&allocator)
+      HBoundsCheck(constant_4, array_length, 0);
+  array_set = new (&allocator) HArraySet(
+    null_check, bounds_check4, constant_1, Primitive::kPrimInt, 0);
+  block->AddInstruction(null_check);
+  block->AddInstruction(array_length);
+  block->AddInstruction(bounds_check4);
+  block->AddInstruction(array_set);
+
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check6 = new (&allocator)
+      HBoundsCheck(constant_6, array_length, 0);
+  array_set = new (&allocator) HArraySet(
+    null_check, bounds_check6, constant_1, Primitive::kPrimInt, 0);
+  block->AddInstruction(null_check);
+  block->AddInstruction(array_length);
+  block->AddInstruction(bounds_check6);
+  block->AddInstruction(array_set);
+
+  block->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit);
+  block->AddSuccessor(exit);
+  exit->AddInstruction(new (&allocator) HExit());
+
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check5));
+  ASSERT_TRUE(IsRemoved(bounds_check4));
+  ASSERT_FALSE(IsRemoved(bounds_check6));
+}
+
+// for (int i=initial; i<array.length; i+=increment) { array[i] = 10; }
+static HGraph* BuildSSAGraph1(ArenaAllocator* allocator,
+                              HInstruction** bounds_check,
+                              int initial,
+                              int increment,
+                              IfCondition cond = kCondGE) {
+  HGraph* graph = new (allocator) HGraph(allocator);
+
+  HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
+  HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+  HInstruction* constant_increment = new (allocator) HIntConstant(increment);
+  HInstruction* constant_10 = new (allocator) HIntConstant(10);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(constant_initial);
+  entry->AddInstruction(constant_increment);
+  entry->AddInstruction(constant_10);
+
+  HBasicBlock* block = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  block->AddInstruction(new (allocator) HGoto());
+
+  HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+  HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+  HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+  graph->AddBlock(loop_header);
+  graph->AddBlock(loop_body);
+  graph->AddBlock(exit);
+  block->AddSuccessor(loop_header);
+  loop_header->AddSuccessor(exit);       // true successor
+  loop_header->AddSuccessor(loop_body);  // false successor
+  loop_body->AddSuccessor(loop_header);
+
+  HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+  phi->AddInput(constant_initial);
+  HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
+  HInstruction* array_length = new (allocator) HArrayLength(null_check);
+  HInstruction* cmp = nullptr;
+  if (cond == kCondGE) {
+    cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
+  } else {
+    DCHECK(cond == kCondGT);
+    cmp = new (allocator) HGreaterThan(phi, array_length);
+  }
+  HInstruction* if_inst = new (allocator) HIf(cmp);
+  loop_header->AddPhi(phi);
+  loop_header->AddInstruction(null_check);
+  loop_header->AddInstruction(array_length);
+  loop_header->AddInstruction(cmp);
+  loop_header->AddInstruction(if_inst);
+
+  null_check = new (allocator) HNullCheck(parameter, 0);
+  array_length = new (allocator) HArrayLength(null_check);
+  *bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
+  HInstruction* array_set = new (allocator) HArraySet(
+      null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+
+  HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_increment);
+  loop_body->AddInstruction(null_check);
+  loop_body->AddInstruction(array_length);
+  loop_body->AddInstruction(*bounds_check);
+  loop_body->AddInstruction(array_set);
+  loop_body->AddInstruction(add);
+  loop_body->AddInstruction(new (allocator) HGoto());
+  phi->AddInput(add);
+
+  exit->AddInstruction(new (allocator) HExit());
+
+  return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination1) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  // for (int i=0; i<array.length; i++) { array[i] = 10; // Can eliminate with gvn. }
+  HInstruction* bounds_check = nullptr;
+  HGraph* graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1);
+  graph->BuildDominatorTree();
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // This time add gvn. Need gvn to eliminate the second
+  // HArrayLength which uses the null check as its input.
+  graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+  bounds_check_elimination_after_gvn.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // for (int i=1; i<array.length; i++) { array[i] = 10; // Can eliminate. }
+  graph = BuildSSAGraph1(&allocator, &bounds_check, 1, 1);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+  bounds_check_elimination_with_initial_1.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // for (int i=-1; i<array.length; i++) { array[i] = 10; // Can't eliminate. }
+  graph = BuildSSAGraph1(&allocator, &bounds_check, -1, 1);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_initial_minus_1(graph);
+  bounds_check_elimination_with_initial_minus_1.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // for (int i=0; i<=array.length; i++) { array[i] = 10; // Can't eliminate. }
+  graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1, kCondGT);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
+  bounds_check_elimination_with_greater_than.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // for (int i=0; i<array.length; i += 2) {
+  //   array[i] = 10; // Can't eliminate due to overflow concern. }
+  graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 2);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_increment_2(graph);
+  bounds_check_elimination_with_increment_2.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // for (int i=1; i<array.length; i += 2) { array[i] = 10; // Can eliminate. }
+  graph = BuildSSAGraph1(&allocator, &bounds_check, 1, 2);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_increment_2_from_1(graph);
+  bounds_check_elimination_with_increment_2_from_1.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+}
+
+// for (int i=array.length; i>0; i+=increment) { array[i-1] = 10; }
+static HGraph* BuildSSAGraph2(ArenaAllocator* allocator,
+                              HInstruction** bounds_check,
+                              int initial,
+                              int increment = -1,
+                              IfCondition cond = kCondLE) {
+  HGraph* graph = new (allocator) HGraph(allocator);
+
+  HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
+  HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+  HInstruction* constant_increment = new (allocator) HIntConstant(increment);
+  HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1);
+  HInstruction* constant_10 = new (allocator) HIntConstant(10);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(constant_initial);
+  entry->AddInstruction(constant_increment);
+  entry->AddInstruction(constant_minus_1);
+  entry->AddInstruction(constant_10);
+
+  HBasicBlock* block = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
+  HInstruction* array_length = new (allocator) HArrayLength(null_check);
+  block->AddInstruction(null_check);
+  block->AddInstruction(array_length);
+  block->AddInstruction(new (allocator) HGoto());
+
+  HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+  HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+  HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+  graph->AddBlock(loop_header);
+  graph->AddBlock(loop_body);
+  graph->AddBlock(exit);
+  block->AddSuccessor(loop_header);
+  loop_header->AddSuccessor(exit);       // true successor
+  loop_header->AddSuccessor(loop_body);  // false successor
+  loop_body->AddSuccessor(loop_header);
+
+  HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+  phi->AddInput(array_length);
+  HInstruction* cmp = nullptr;
+  if (cond == kCondLE) {
+    cmp = new (allocator) HLessThanOrEqual(phi, constant_initial);
+  } else {
+    DCHECK(cond == kCondLT);
+    cmp = new (allocator) HLessThan(phi, constant_initial);
+  }
+  HInstruction* if_inst = new (allocator) HIf(cmp);
+  loop_header->AddPhi(phi);
+  loop_header->AddInstruction(cmp);
+  loop_header->AddInstruction(if_inst);
+
+  HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_minus_1);
+  null_check = new (allocator) HNullCheck(parameter, 0);
+  array_length = new (allocator) HArrayLength(null_check);
+  *bounds_check = new (allocator) HBoundsCheck(add, array_length, 0);
+  HInstruction* array_set = new (allocator) HArraySet(
+      null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+  HInstruction* add_phi = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_increment);
+  loop_body->AddInstruction(add);
+  loop_body->AddInstruction(null_check);
+  loop_body->AddInstruction(array_length);
+  loop_body->AddInstruction(*bounds_check);
+  loop_body->AddInstruction(array_set);
+  loop_body->AddInstruction(add_phi);
+  loop_body->AddInstruction(new (allocator) HGoto());
+  phi->AddInput(add);
+
+  exit->AddInstruction(new (allocator) HExit());
+
+  return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination2) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  // for (int i=array.length; i>0; i--) { array[i-1] = 10; // Can eliminate with gvn. }
+  HInstruction* bounds_check = nullptr;
+  HGraph* graph = BuildSSAGraph2(&allocator, &bounds_check, 0);
+  graph->BuildDominatorTree();
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // This time add gvn. Need gvn to eliminate the second
+  // HArrayLength which uses the null check as its input.
+  graph = BuildSSAGraph2(&allocator, &bounds_check, 0);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+  bounds_check_elimination_after_gvn.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // for (int i=array.length; i>1; i--) { array[i-1] = 10; // Can eliminate. }
+  graph = BuildSSAGraph2(&allocator, &bounds_check, 1);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+  bounds_check_elimination_with_initial_1.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // for (int i=array.length; i>-1; i--) { array[i-1] = 10; // Can't eliminate. }
+  graph = BuildSSAGraph2(&allocator, &bounds_check, -1);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_initial_minus_1(graph);
+  bounds_check_elimination_with_initial_minus_1.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // for (int i=array.length; i>=0; i--) { array[i-1] = 10; // Can't eliminate. }
+  graph = BuildSSAGraph2(&allocator, &bounds_check, 0, -1, kCondLT);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_less_than(graph);
+  bounds_check_elimination_with_less_than.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // for (int i=array.length; i>0; i-=2) { array[i-1] = 10; // Can eliminate. }
+  graph = BuildSSAGraph2(&allocator, &bounds_check, 0, -2);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_increment_minus_2(graph);
+  bounds_check_elimination_increment_minus_2.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+}
+
+// int[] array = new array[10];
+// for (int i=0; i<10; i+=increment) { array[i] = 10; }
+static HGraph* BuildSSAGraph3(ArenaAllocator* allocator,
+                              HInstruction** bounds_check,
+                              int initial,
+                              int increment,
+                              IfCondition cond) {
+  HGraph* graph = new (allocator) HGraph(allocator);
+
+  HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* constant_10 = new (allocator) HIntConstant(10);
+  HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+  HInstruction* constant_increment = new (allocator) HIntConstant(increment);
+  entry->AddInstruction(constant_10);
+  entry->AddInstruction(constant_initial);
+  entry->AddInstruction(constant_increment);
+
+  HBasicBlock* block = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  HInstruction* new_array = new (allocator)
+      HNewArray(constant_10, 0, Primitive::kPrimInt, kQuickAllocArray);
+  block->AddInstruction(new_array);
+  block->AddInstruction(new (allocator) HGoto());
+
+  HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+  HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+  HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+  graph->AddBlock(loop_header);
+  graph->AddBlock(loop_body);
+  graph->AddBlock(exit);
+  block->AddSuccessor(loop_header);
+  loop_header->AddSuccessor(exit);       // true successor
+  loop_header->AddSuccessor(loop_body);  // false successor
+  loop_body->AddSuccessor(loop_header);
+
+  HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+  phi->AddInput(constant_initial);
+  HInstruction* cmp = nullptr;
+  if (cond == kCondGE) {
+    cmp = new (allocator) HGreaterThanOrEqual(phi, constant_10);
+  } else {
+    DCHECK(cond == kCondGT);
+    cmp = new (allocator) HGreaterThan(phi, constant_10);
+  }
+  HInstruction* if_inst = new (allocator) HIf(cmp);
+  loop_header->AddPhi(phi);
+  loop_header->AddInstruction(cmp);
+  loop_header->AddInstruction(if_inst);
+
+  HNullCheck* null_check = new (allocator) HNullCheck(new_array, 0);
+  HArrayLength* array_length = new (allocator) HArrayLength(null_check);
+  *bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
+  HInstruction* array_set = new (allocator) HArraySet(
+      null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+  HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_increment);
+  loop_body->AddInstruction(null_check);
+  loop_body->AddInstruction(array_length);
+  loop_body->AddInstruction(*bounds_check);
+  loop_body->AddInstruction(array_set);
+  loop_body->AddInstruction(add);
+  loop_body->AddInstruction(new (allocator) HGoto());
+  phi->AddInput(add);
+
+  exit->AddInstruction(new (allocator) HExit());
+
+  return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination3) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  // int[] array = new array[10];
+  // for (int i=0; i<10; i++) { array[i] = 10; // Can eliminate. }
+  HInstruction* bounds_check = nullptr;
+  HGraph* graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGE);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+  bounds_check_elimination_after_gvn.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // int[] array = new array[10];
+  // for (int i=1; i<10; i++) { array[i] = 10; // Can eliminate. }
+  graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 1, kCondGE);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+  bounds_check_elimination_with_initial_1.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // int[] array = new array[10];
+  // for (int i=0; i<=10; i++) { array[i] = 10; // Can't eliminate. }
+  graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGT);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
+  bounds_check_elimination_with_greater_than.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // int[] array = new array[10];
+  // for (int i=1; i<10; i+=8) { array[i] = 10; // Can eliminate. }
+  graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 8, kCondGE);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_increment_8(graph);
+  bounds_check_elimination_increment_8.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+}
+
+// for (int i=initial; i<array.length; i++) { array[array.length-i-1] = 10; }
+static HGraph* BuildSSAGraph4(ArenaAllocator* allocator,
+                              HInstruction** bounds_check,
+                              int initial,
+                              IfCondition cond = kCondGE) {
+  HGraph* graph = new (allocator) HGraph(allocator);
+
+  HBasicBlock* entry = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
+  HInstruction* constant_initial = new (allocator) HIntConstant(initial);
+  HInstruction* constant_1 = new (allocator) HIntConstant(1);
+  HInstruction* constant_10 = new (allocator) HIntConstant(10);
+  HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(constant_initial);
+  entry->AddInstruction(constant_1);
+  entry->AddInstruction(constant_10);
+  entry->AddInstruction(constant_minus_1);
+
+  HBasicBlock* block = new (allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  block->AddInstruction(new (allocator) HGoto());
+
+  HBasicBlock* loop_header = new (allocator) HBasicBlock(graph);
+  HBasicBlock* loop_body = new (allocator) HBasicBlock(graph);
+  HBasicBlock* exit = new (allocator) HBasicBlock(graph);
+
+  graph->AddBlock(loop_header);
+  graph->AddBlock(loop_body);
+  graph->AddBlock(exit);
+  block->AddSuccessor(loop_header);
+  loop_header->AddSuccessor(exit);       // true successor
+  loop_header->AddSuccessor(loop_body);  // false successor
+  loop_body->AddSuccessor(loop_header);
+
+  HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
+  phi->AddInput(constant_initial);
+  HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
+  HInstruction* array_length = new (allocator) HArrayLength(null_check);
+  HInstruction* cmp = nullptr;
+  if (cond == kCondGE) {
+    cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
+  } else if (cond == kCondGT) {
+    cmp = new (allocator) HGreaterThan(phi, array_length);
+  }
+  HInstruction* if_inst = new (allocator) HIf(cmp);
+  loop_header->AddPhi(phi);
+  loop_header->AddInstruction(null_check);
+  loop_header->AddInstruction(array_length);
+  loop_header->AddInstruction(cmp);
+  loop_header->AddInstruction(if_inst);
+
+  null_check = new (allocator) HNullCheck(parameter, 0);
+  array_length = new (allocator) HArrayLength(null_check);
+  HInstruction* sub = new (allocator) HSub(Primitive::kPrimInt, array_length, phi);
+  HInstruction* add_minus_1 = new (allocator)
+      HAdd(Primitive::kPrimInt, sub, constant_minus_1);
+  *bounds_check = new (allocator) HBoundsCheck(add_minus_1, array_length, 0);
+  HInstruction* array_set = new (allocator) HArraySet(
+      null_check, *bounds_check, constant_10, Primitive::kPrimInt, 0);
+  HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_1);
+  loop_body->AddInstruction(null_check);
+  loop_body->AddInstruction(array_length);
+  loop_body->AddInstruction(sub);
+  loop_body->AddInstruction(add_minus_1);
+  loop_body->AddInstruction(*bounds_check);
+  loop_body->AddInstruction(array_set);
+  loop_body->AddInstruction(add);
+  loop_body->AddInstruction(new (allocator) HGoto());
+  phi->AddInput(add);
+
+  exit->AddInstruction(new (allocator) HExit());
+
+  return graph;
+}
+
+TEST(BoundsCheckEliminationTest, LoopArrayBoundsElimination4) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  // for (int i=0; i<array.length; i++) { array[array.length-i-1] = 10; // Can eliminate with gvn. }
+  HInstruction* bounds_check = nullptr;
+  HGraph* graph = BuildSSAGraph4(&allocator, &bounds_check, 0);
+  graph->BuildDominatorTree();
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+
+  // This time add gvn. Need gvn to eliminate the second
+  // HArrayLength which uses the null check as its input.
+  graph = BuildSSAGraph4(&allocator, &bounds_check, 0);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
+  bounds_check_elimination_after_gvn.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // for (int i=1; i<array.length; i++) { array[array.length-i-1] = 10; // Can eliminate. }
+  graph = BuildSSAGraph4(&allocator, &bounds_check, 1);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
+  bounds_check_elimination_with_initial_1.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check));
+
+  // for (int i=0; i<=array.length; i++) { array[array.length-i] = 10; // Can't eliminate. }
+  graph = BuildSSAGraph4(&allocator, &bounds_check, 0, kCondGT);
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
+  bounds_check_elimination_with_greater_than.Run();
+  ASSERT_FALSE(IsRemoved(bounds_check));
+}
+
+// Bubble sort:
+// (Every array access bounds-check can be eliminated.)
+// for (int i=0; i<array.length-1; i++) {
+//  for (int j=0; j<array.length-i-1; j++) {
+//     if (array[j] > array[j+1]) {
+//       int temp = array[j+1];
+//       array[j+1] = array[j];
+//       array[j] = temp;
+//     }
+//  }
+// }
+TEST(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+  HInstruction* constant_0 = new (&allocator) HIntConstant(0);
+  HInstruction* constant_minus_1 = new (&allocator) HIntConstant(-1);
+  HInstruction* constant_1 = new (&allocator) HIntConstant(1);
+  entry->AddInstruction(parameter);
+  entry->AddInstruction(constant_0);
+  entry->AddInstruction(constant_minus_1);
+  entry->AddInstruction(constant_1);
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  block->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(exit);
+  exit->AddInstruction(new (&allocator) HExit());
+
+  HBasicBlock* outer_header = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(outer_header);
+  HPhi* phi_i = new (&allocator) HPhi(&allocator, 0, 0, Primitive::kPrimInt);
+  phi_i->AddInput(constant_0);
+  HNullCheck* null_check = new (&allocator) HNullCheck(parameter, 0);
+  HArrayLength* array_length = new (&allocator) HArrayLength(null_check);
+  HAdd* add = new (&allocator) HAdd(Primitive::kPrimInt, array_length, constant_minus_1);
+  HInstruction* cmp = new (&allocator) HGreaterThanOrEqual(phi_i, add);
+  HIf* if_inst = new (&allocator) HIf(cmp);
+  outer_header->AddPhi(phi_i);
+  outer_header->AddInstruction(null_check);
+  outer_header->AddInstruction(array_length);
+  outer_header->AddInstruction(add);
+  outer_header->AddInstruction(cmp);
+  outer_header->AddInstruction(if_inst);
+
+  HBasicBlock* inner_header = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(inner_header);
+  HPhi* phi_j = new (&allocator) HPhi(&allocator, 0, 0, Primitive::kPrimInt);
+  phi_j->AddInput(constant_0);
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HSub* sub = new (&allocator) HSub(Primitive::kPrimInt, array_length, phi_i);
+  add = new (&allocator) HAdd(Primitive::kPrimInt, sub, constant_minus_1);
+  cmp = new (&allocator) HGreaterThanOrEqual(phi_j, add);
+  if_inst = new (&allocator) HIf(cmp);
+  inner_header->AddPhi(phi_j);
+  inner_header->AddInstruction(null_check);
+  inner_header->AddInstruction(array_length);
+  inner_header->AddInstruction(sub);
+  inner_header->AddInstruction(add);
+  inner_header->AddInstruction(cmp);
+  inner_header->AddInstruction(if_inst);
+
+  HBasicBlock* inner_body_compare = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(inner_body_compare);
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check1 = new (&allocator) HBoundsCheck(phi_j, array_length, 0);
+  HArrayGet* array_get_j = new (&allocator)
+      HArrayGet(null_check, bounds_check1, Primitive::kPrimInt);
+  inner_body_compare->AddInstruction(null_check);
+  inner_body_compare->AddInstruction(array_length);
+  inner_body_compare->AddInstruction(bounds_check1);
+  inner_body_compare->AddInstruction(array_get_j);
+  HInstruction* j_plus_1 = new (&allocator) HAdd(Primitive::kPrimInt, phi_j, constant_1);
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HBoundsCheck* bounds_check2 = new (&allocator) HBoundsCheck(j_plus_1, array_length, 0);
+  HArrayGet* array_get_j_plus_1 = new (&allocator)
+      HArrayGet(null_check, bounds_check2, Primitive::kPrimInt);
+  cmp = new (&allocator) HGreaterThanOrEqual(array_get_j, array_get_j_plus_1);
+  if_inst = new (&allocator) HIf(cmp);
+  inner_body_compare->AddInstruction(j_plus_1);
+  inner_body_compare->AddInstruction(null_check);
+  inner_body_compare->AddInstruction(array_length);
+  inner_body_compare->AddInstruction(bounds_check2);
+  inner_body_compare->AddInstruction(array_get_j_plus_1);
+  inner_body_compare->AddInstruction(cmp);
+  inner_body_compare->AddInstruction(if_inst);
+
+  HBasicBlock* inner_body_swap = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(inner_body_swap);
+  j_plus_1 = new (&allocator) HAdd(Primitive::kPrimInt, phi_j, constant_1);
+  // temp = array[j+1]
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HInstruction* bounds_check3 = new (&allocator) HBoundsCheck(j_plus_1, array_length, 0);
+  array_get_j_plus_1 = new (&allocator)
+      HArrayGet(null_check, bounds_check3, Primitive::kPrimInt);
+  inner_body_swap->AddInstruction(j_plus_1);
+  inner_body_swap->AddInstruction(null_check);
+  inner_body_swap->AddInstruction(array_length);
+  inner_body_swap->AddInstruction(bounds_check3);
+  inner_body_swap->AddInstruction(array_get_j_plus_1);
+  // array[j+1] = array[j]
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HInstruction* bounds_check4 = new (&allocator) HBoundsCheck(phi_j, array_length, 0);
+  array_get_j = new (&allocator)
+      HArrayGet(null_check, bounds_check4, Primitive::kPrimInt);
+  inner_body_swap->AddInstruction(null_check);
+  inner_body_swap->AddInstruction(array_length);
+  inner_body_swap->AddInstruction(bounds_check4);
+  inner_body_swap->AddInstruction(array_get_j);
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HInstruction* bounds_check5 = new (&allocator) HBoundsCheck(j_plus_1, array_length, 0);
+  HArraySet* array_set_j_plus_1 = new (&allocator)
+      HArraySet(null_check, bounds_check5, array_get_j, Primitive::kPrimInt, 0);
+  inner_body_swap->AddInstruction(null_check);
+  inner_body_swap->AddInstruction(array_length);
+  inner_body_swap->AddInstruction(bounds_check5);
+  inner_body_swap->AddInstruction(array_set_j_plus_1);
+  // array[j] = temp
+  null_check = new (&allocator) HNullCheck(parameter, 0);
+  array_length = new (&allocator) HArrayLength(null_check);
+  HInstruction* bounds_check6 = new (&allocator) HBoundsCheck(phi_j, array_length, 0);
+  HArraySet* array_set_j = new (&allocator)
+      HArraySet(null_check, bounds_check6, array_get_j_plus_1, Primitive::kPrimInt, 0);
+  inner_body_swap->AddInstruction(null_check);
+  inner_body_swap->AddInstruction(array_length);
+  inner_body_swap->AddInstruction(bounds_check6);
+  inner_body_swap->AddInstruction(array_set_j);
+  inner_body_swap->AddInstruction(new (&allocator) HGoto());
+
+  HBasicBlock* inner_body_add = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(inner_body_add);
+  add = new (&allocator) HAdd(Primitive::kPrimInt, phi_j, constant_1);
+  inner_body_add->AddInstruction(add);
+  inner_body_add->AddInstruction(new (&allocator) HGoto());
+  phi_j->AddInput(add);
+
+  HBasicBlock* outer_body_add = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(outer_body_add);
+  add = new (&allocator) HAdd(Primitive::kPrimInt, phi_i, constant_1);
+  outer_body_add->AddInstruction(add);
+  outer_body_add->AddInstruction(new (&allocator) HGoto());
+  phi_i->AddInput(add);
+
+  block->AddSuccessor(outer_header);
+  outer_header->AddSuccessor(exit);
+  outer_header->AddSuccessor(inner_header);
+  inner_header->AddSuccessor(outer_body_add);
+  inner_header->AddSuccessor(inner_body_compare);
+  inner_body_compare->AddSuccessor(inner_body_add);
+  inner_body_compare->AddSuccessor(inner_body_swap);
+  inner_body_swap->AddSuccessor(inner_body_add);
+  inner_body_add->AddSuccessor(inner_header);
+  outer_body_add->AddSuccessor(outer_header);
+
+  graph->BuildDominatorTree();
+  RunSimplifierAndGvn(graph);
+  // gvn should remove the same bounds check.
+  ASSERT_FALSE(IsRemoved(bounds_check1));
+  ASSERT_FALSE(IsRemoved(bounds_check2));
+  ASSERT_TRUE(IsRemoved(bounds_check3));
+  ASSERT_TRUE(IsRemoved(bounds_check4));
+  ASSERT_TRUE(IsRemoved(bounds_check5));
+  ASSERT_TRUE(IsRemoved(bounds_check6));
+
+  BoundsCheckElimination bounds_check_elimination(graph);
+  bounds_check_elimination.Run();
+  ASSERT_TRUE(IsRemoved(bounds_check1));
+  ASSERT_TRUE(IsRemoved(bounds_check2));
+  ASSERT_TRUE(IsRemoved(bounds_check3));
+  ASSERT_TRUE(IsRemoved(bounds_check4));
+  ASSERT_TRUE(IsRemoved(bounds_check5));
+  ASSERT_TRUE(IsRemoved(bounds_check6));
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index eb6181c..20a1b03 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -16,6 +16,7 @@
 
 #include "builder.h"
 
+#include "base/logging.h"
 #include "class_linker.h"
 #include "dex_file.h"
 #include "dex_file-inl.h"
@@ -68,6 +69,74 @@
   size_t index_;
 };
 
+class SwitchTable : public ValueObject {
+ public:
+  SwitchTable(const Instruction& instruction, uint32_t dex_pc, bool sparse)
+      : instruction_(instruction), dex_pc_(dex_pc), sparse_(sparse) {
+    int32_t table_offset = instruction.VRegB_31t();
+    const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset;
+    if (sparse) {
+      CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
+    } else {
+      CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
+    }
+    num_entries_ = table[1];
+    values_ = reinterpret_cast<const int32_t*>(&table[2]);
+  }
+
+  uint16_t GetNumEntries() const {
+    return num_entries_;
+  }
+
+  void CheckIndex(size_t index) const {
+    if (sparse_) {
+      // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+      DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_));
+    } else {
+      // In a packed table, we have the starting key and num_entries_ values.
+      DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_));
+    }
+  }
+
+  int32_t GetEntryAt(size_t index) const {
+    CheckIndex(index);
+    return values_[index];
+  }
+
+  uint32_t GetDexPcForIndex(size_t index) const {
+    CheckIndex(index);
+    return dex_pc_ +
+        (reinterpret_cast<const int16_t*>(values_ + index) -
+         reinterpret_cast<const int16_t*>(&instruction_));
+  }
+
+  // Index of the first value in the table.
+  size_t GetFirstValueIndex() const {
+    if (sparse_) {
+      // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+      return num_entries_;
+    } else {
+      // In a packed table, we have the starting key and num_entries_ values.
+      return 1;
+    }
+  }
+
+ private:
+  const Instruction& instruction_;
+  const uint32_t dex_pc_;
+
+  // Whether this is a sparse-switch table (or a packed-switch one).
+  const bool sparse_;
+
+  // This can't be const as it needs to be computed off of the given instruction, and complicated
+  // expressions in the initializer list seemed very ugly.
+  uint16_t num_entries_;
+
+  const int32_t* values_;
+
+  DISALLOW_COPY_AND_ASSIGN(SwitchTable);
+};
+
 void HGraphBuilder::InitializeLocals(uint16_t count) {
   graph_->SetNumberOfVRegs(count);
   locals_.SetSize(count);
@@ -92,7 +161,7 @@
   if (!dex_compilation_unit_->IsStatic()) {
     // Add the implicit 'this' argument, not expressed in the signature.
     HParameterValue* parameter =
-        new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot);
+        new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot, true);
     entry_block_->AddInstruction(parameter);
     HLocal* local = GetLocalAt(locals_index++);
     entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
@@ -121,78 +190,83 @@
 template<typename T>
 void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
   int32_t target_offset = instruction.GetTargetOffset();
-  PotentiallyAddSuspendCheck(target_offset, dex_pc);
+  HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+  HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+  DCHECK(branch_target != nullptr);
+  DCHECK(fallthrough_target != nullptr);
+  PotentiallyAddSuspendCheck(branch_target, dex_pc);
   HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
   HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
   T* comparison = new (arena_) T(first, second);
   current_block_->AddInstruction(comparison);
   HInstruction* ifinst = new (arena_) HIf(comparison);
   current_block_->AddInstruction(ifinst);
-  HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
-  target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
+  current_block_->AddSuccessor(branch_target);
+  current_block_->AddSuccessor(fallthrough_target);
   current_block_ = nullptr;
 }
 
 template<typename T>
 void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
   int32_t target_offset = instruction.GetTargetOffset();
-  PotentiallyAddSuspendCheck(target_offset, dex_pc);
+  HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+  HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+  DCHECK(branch_target != nullptr);
+  DCHECK(fallthrough_target != nullptr);
+  PotentiallyAddSuspendCheck(branch_target, dex_pc);
   HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
   T* comparison = new (arena_) T(value, GetIntConstant(0));
   current_block_->AddInstruction(comparison);
   HInstruction* ifinst = new (arena_) HIf(comparison);
   current_block_->AddInstruction(ifinst);
-  HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
-  target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
-  DCHECK(target != nullptr);
-  current_block_->AddSuccessor(target);
+  current_block_->AddSuccessor(branch_target);
+  current_block_->AddSuccessor(fallthrough_target);
   current_block_ = nullptr;
 }
 
-static bool ShouldSkipCompilation(const CompilerDriver& compiler_driver,
-                                  const DexCompilationUnit& dex_compilation_unit,
-                                  size_t number_of_dex_instructions,
-                                  size_t number_of_blocks ATTRIBUTE_UNUSED,
-                                  size_t number_of_branches) {
-  const CompilerOptions& compiler_options = compiler_driver.GetCompilerOptions();
+void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
+  if (compilation_stats_ != nullptr) {
+    compilation_stats_->RecordStat(compilation_stat);
+  }
+}
+
+bool HGraphBuilder::SkipCompilation(size_t number_of_dex_instructions,
+                                    size_t number_of_blocks ATTRIBUTE_UNUSED,
+                                    size_t number_of_branches) {
+  const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
   CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
   if (compiler_filter == CompilerOptions::kEverything) {
     return false;
   }
 
   if (compiler_options.IsHugeMethod(number_of_dex_instructions)) {
-    LOG(INFO) << "Skip compilation of huge method "
-              << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
-                              *dex_compilation_unit.GetDexFile())
-              << ": " << number_of_dex_instructions << " dex instructions";
+    VLOG(compiler) << "Skip compilation of huge method "
+                   << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+                   << ": " << number_of_dex_instructions << " dex instructions";
+    MaybeRecordStat(MethodCompilationStat::kNotCompiledHugeMethod);
     return true;
   }
 
   // If it's large and contains no branches, it's likely to be machine generated initialization.
   if (compiler_options.IsLargeMethod(number_of_dex_instructions) && (number_of_branches == 0)) {
-    LOG(INFO) << "Skip compilation of large method with no branch "
-              << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
-                              *dex_compilation_unit.GetDexFile())
-              << ": " << number_of_dex_instructions << " dex instructions";
+    VLOG(compiler) << "Skip compilation of large method with no branch "
+                   << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+                   << ": " << number_of_dex_instructions << " dex instructions";
+    MaybeRecordStat(MethodCompilationStat::kNotCompiledLargeMethodNoBranches);
     return true;
   }
 
   return false;
 }
 
-HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+  DCHECK(graph_->GetBlocks().IsEmpty());
+
   const uint16_t* code_ptr = code_item.insns_;
   const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
   code_start_ = code_ptr;
 
   // Setup the graph with the entry block and exit block.
-  graph_ = new (arena_) HGraph(arena_);
   entry_block_ = new (arena_) HBasicBlock(graph_, 0);
   graph_->AddBlock(entry_block_);
   exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
@@ -200,7 +274,7 @@
   graph_->SetExitBlock(exit_block_);
 
   InitializeLocals(code_item.registers_size_);
-  graph_->UpdateMaximumNumberOfOutVRegs(code_item.outs_size_);
+  graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_);
 
   // Compute the number of dex instructions, blocks, and branches. We will
   // check these values against limits given to the compiler.
@@ -214,14 +288,9 @@
       code_ptr, code_end, &number_of_dex_instructions, &number_of_blocks, &number_of_branches);
 
   // Note that the compiler driver is null when unit testing.
-  if (compiler_driver_ != nullptr) {
-    if (ShouldSkipCompilation(*compiler_driver_,
-                              *dex_compilation_unit_,
-                              number_of_dex_instructions,
-                              number_of_blocks,
-                              number_of_branches)) {
-      return nullptr;
-    }
+  if ((compiler_driver_ != nullptr)
+      && SkipCompilation(number_of_dex_instructions, number_of_blocks, number_of_branches)) {
+    return false;
   }
 
   // Also create blocks for catch handlers.
@@ -250,7 +319,9 @@
     // Update the current block if dex_pc starts a new block.
     MaybeUpdateCurrentBlock(dex_pc);
     const Instruction& instruction = *Instruction::At(code_ptr);
-    if (!AnalyzeDexInstruction(instruction, dex_pc)) return nullptr;
+    if (!AnalyzeDexInstruction(instruction, dex_pc)) {
+      return false;
+    }
     dex_pc += instruction.SizeInCodeUnits();
     code_ptr += instruction.SizeInCodeUnits();
   }
@@ -261,7 +332,8 @@
   // Add the suspend check to the entry block.
   entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
   entry_block_->AddInstruction(new (arena_) HGoto());
-  return graph_;
+
+  return true;
 }
 
 void HGraphBuilder::MaybeUpdateCurrentBlock(size_t index) {
@@ -286,7 +358,6 @@
                                          size_t* number_of_dex_instructions,
                                          size_t* number_of_blocks,
                                          size_t* number_of_branches) {
-  // TODO: Support switch instructions.
   branch_targets_.SetSize(code_end - code_ptr);
 
   // Create the first block for the dex instructions, single successor of the entry block.
@@ -296,7 +367,7 @@
 
   // Iterate over all instructions and find branching instructions. Create blocks for
   // the locations these instructions branch to.
-  size_t dex_pc = 0;
+  uint32_t dex_pc = 0;
   while (code_ptr < code_end) {
     (*number_of_dex_instructions)++;
     const Instruction& instruction = *Instruction::At(code_ptr);
@@ -316,6 +387,41 @@
         branch_targets_.Put(dex_pc, block);
         (*number_of_blocks)++;
       }
+    } else if (instruction.IsSwitch()) {
+      SwitchTable table(instruction, dex_pc, instruction.Opcode() == Instruction::SPARSE_SWITCH);
+
+      uint16_t num_entries = table.GetNumEntries();
+
+      // In a packed-switch, the entry at index 0 is the starting key. In a sparse-switch, the
+      // entry at index 0 is the first key, and values are after *all* keys.
+      size_t offset = table.GetFirstValueIndex();
+
+      // Use a larger loop counter type to avoid overflow issues.
+      for (size_t i = 0; i < num_entries; ++i) {
+        // The target of the case.
+        uint32_t target = dex_pc + table.GetEntryAt(i + offset);
+        if (FindBlockStartingAt(target) == nullptr) {
+          block = new (arena_) HBasicBlock(graph_, target);
+          branch_targets_.Put(target, block);
+          (*number_of_blocks)++;
+        }
+
+        // The next case gets its own block.
+        if (i < num_entries) {
+          block = new (arena_) HBasicBlock(graph_, target);
+          branch_targets_.Put(table.GetDexPcForIndex(i), block);
+          (*number_of_blocks)++;
+        }
+      }
+
+      // Fall-through. Add a block if there is more code afterwards.
+      dex_pc += instruction.SizeInCodeUnits();
+      code_ptr += instruction.SizeInCodeUnits();
+      if ((code_ptr < code_end) && (FindBlockStartingAt(dex_pc) == nullptr)) {
+        block = new (arena_) HBasicBlock(graph_, dex_pc);
+        branch_targets_.Put(dex_pc, block);
+        (*number_of_blocks)++;
+      }
     } else {
       code_ptr += instruction.SizeInCodeUnits();
       dex_pc += instruction.SizeInCodeUnits();
@@ -337,9 +443,10 @@
 
 void HGraphBuilder::Conversion_12x(const Instruction& instruction,
                                    Primitive::Type input_type,
-                                   Primitive::Type result_type) {
+                                   Primitive::Type result_type,
+                                   uint32_t dex_pc) {
   HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
-  current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first));
+  current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
   UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
 }
 
@@ -489,8 +596,9 @@
   if (!compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc, true, true,
                                            &optimized_invoke_type, &target_method, &table_index,
                                            &direct_code, &direct_method)) {
-    LOG(INFO) << "Did not compile " << PrettyMethod(method_idx, *dex_file_)
-              << " because a method call could not be resolved";
+    VLOG(compiler) << "Did not compile " << PrettyMethod(method_idx, *dex_file_)
+                   << " because a method call could not be resolved";
+    MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedMethod);
     return false;
   }
   DCHECK(optimized_invoke_type != kSuper);
@@ -498,7 +606,7 @@
   HInvoke* invoke = nullptr;
   if (optimized_invoke_type == kVirtual) {
     invoke = new (arena_) HInvokeVirtual(
-        arena_, number_of_arguments, return_type, dex_pc, table_index);
+        arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
   } else if (optimized_invoke_type == kInterface) {
     invoke = new (arena_) HInvokeInterface(
         arena_, number_of_arguments, return_type, dex_pc, method_idx, table_index);
@@ -507,9 +615,12 @@
     // Sharpening to kDirect only works if we compile PIC.
     DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
            || compiler_driver_->GetCompilerOptions().GetCompilePic());
-    // Treat invoke-direct like static calls for now.
-    invoke = new (arena_) HInvokeStatic(
-        arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index);
+    bool is_recursive =
+        (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex());
+    DCHECK(!is_recursive || (target_method.dex_file == outer_compilation_unit_->GetDexFile()));
+    invoke = new (arena_) HInvokeStaticOrDirect(
+        arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
+        is_recursive, optimized_invoke_type);
   }
 
   size_t start_index = 0;
@@ -532,6 +643,7 @@
       LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
                    << " at " << dex_pc;
       // We do not implement non sequential register pair.
+      MaybeRecordStat(MethodCompilationStat::kNotCompiledNonSequentialRegPair);
       return false;
     }
     HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
@@ -560,9 +672,7 @@
       compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa)));
 
   if (resolved_field.Get() == nullptr) {
-    return false;
-  }
-  if (resolved_field->IsVolatile()) {
+    MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
     return false;
   }
 
@@ -580,60 +690,68 @@
         null_check,
         value,
         field_type,
-        resolved_field->GetOffset()));
+        resolved_field->GetOffset(),
+        resolved_field->IsVolatile()));
   } else {
     current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
         current_block_->GetLastInstruction(),
         field_type,
-        resolved_field->GetOffset()));
+        resolved_field->GetOffset(),
+        resolved_field->IsVolatile()));
 
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
   return true;
 }
 
-
-
 bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
                                            uint32_t dex_pc,
                                            bool is_put) {
   uint32_t source_or_dest_reg = instruction.VRegA_21c();
   uint16_t field_index = instruction.VRegB_21c();
 
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<4> hs(soa.Self());
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+      dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::ArtField> resolved_field(hs.NewHandle(compiler_driver_->ResolveField(
+      soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true)));
+
+  if (resolved_field.Get() == nullptr) {
+    MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
+    return false;
+  }
+
+  Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
+      soa, dex_cache, class_loader, outer_compilation_unit_)));
+
+  // The index at which the field's class is stored in the DexCache's type array.
   uint32_t storage_index;
-  bool is_referrers_class;
-  bool is_initialized;
-  bool is_volatile;
-  MemberOffset field_offset(0u);
-  Primitive::Type field_type;
-
-  bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index,
-                                                            dex_compilation_unit_,
-                                                            is_put,
-                                                            &field_offset,
-                                                            &storage_index,
-                                                            &is_referrers_class,
-                                                            &is_volatile,
-                                                            &is_initialized,
-                                                            &field_type);
-  if (!fast_path) {
+  std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+      dex_cache.Get(), referrer_class.Get(), resolved_field.Get(), field_index, &storage_index);
+  bool can_easily_access = is_put ? pair.second : pair.first;
+  if (!can_easily_access) {
     return false;
   }
 
-  if (is_volatile) {
-    return false;
-  }
+  // TODO: find out why this check is needed.
+  bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
+      *outer_compilation_unit_->GetDexFile(), storage_index);
+  bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
+  bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
 
-  HLoadClass* constant = new (arena_) HLoadClass(
-      storage_index, is_referrers_class, dex_pc);
+  HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
   current_block_->AddInstruction(constant);
 
   HInstruction* cls = constant;
-  if (!is_initialized) {
+  if (!is_initialized && !is_referrer_class) {
     cls = new (arena_) HClinitCheck(constant, dex_pc);
     current_block_->AddInstruction(cls);
   }
 
+  Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
   if (is_put) {
     // We need to keep the class alive before loading the value.
     Temporaries temps(graph_);
@@ -641,9 +759,12 @@
     HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
     DCHECK_EQ(value->GetType(), field_type);
     current_block_->AddInstruction(
-        new (arena_) HStaticFieldSet(cls, value, field_type, field_offset));
+        new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset(),
+            resolved_field->IsVolatile()));
   } else {
-    current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset));
+    current_block_->AddInstruction(
+        new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset(),
+            resolved_field->IsVolatile()));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
   return true;
@@ -728,7 +849,10 @@
                                         uint32_t* args,
                                         uint32_t register_index) {
   HInstruction* length = GetIntConstant(number_of_vreg_arguments);
-  HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index);
+  QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+      ? kQuickAllocArrayWithAccessCheck
+      : kQuickAllocArray;
+  HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index, entrypoint);
   current_block_->AddInstruction(object);
 
   const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -838,15 +962,20 @@
                                    uint32_t dex_pc) {
   bool type_known_final;
   bool type_known_abstract;
-  bool is_referrers_class;
+  // `CanAccessTypeWithoutChecks` will tell whether the method being
+  // built is trying to access its own class, so that the generated
+  // code can optimize for this case. However, the optimization does not
+  // work for inlining, so we use `IsCompilingClass` instead.
+  bool dont_use_is_referrers_class;
   bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
       dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
-      &type_known_final, &type_known_abstract, &is_referrers_class);
+      &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
   if (!can_access) {
+    MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
     return false;
   }
   HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
-  HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc);
+  HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc);
   current_block_->AddInstruction(cls);
   // The class needs a temporary before being used by the type check.
   Temporaries temps(graph_);
@@ -863,10 +992,101 @@
   return true;
 }
 
-void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc) {
+bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const {
+  return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+      dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index);
+}
+
+void HGraphBuilder::BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc) {
+  SwitchTable table(instruction, dex_pc, false);
+
+  // Value to test against.
+  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+
+  uint16_t num_entries = table.GetNumEntries();
+  // There should be at least one entry here.
+  DCHECK_GT(num_entries, 0U);
+
+  // Chained cmp-and-branch, starting from starting_key.
+  int32_t starting_key = table.GetEntryAt(0);
+
+  for (size_t i = 1; i <= num_entries; i++) {
+    BuildSwitchCaseHelper(instruction, i, i == num_entries, table, value, starting_key + i - 1,
+                          table.GetEntryAt(i), dex_pc);
+  }
+}
+
+void HGraphBuilder::BuildSparseSwitch(const Instruction& instruction, uint32_t dex_pc) {
+  SwitchTable table(instruction, dex_pc, true);
+
+  // Value to test against.
+  HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+
+  uint16_t num_entries = table.GetNumEntries();
+  // There should be at least one entry here.
+  DCHECK_GT(num_entries, 0U);
+
+  for (size_t i = 0; i < num_entries; i++) {
+    BuildSwitchCaseHelper(instruction, i, i == static_cast<size_t>(num_entries) - 1, table, value,
+                          table.GetEntryAt(i), table.GetEntryAt(i + num_entries), dex_pc);
+  }
+}
+
+void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t index,
+                                          bool is_last_case, const SwitchTable& table,
+                                          HInstruction* value, int32_t case_value_int,
+                                          int32_t target_offset, uint32_t dex_pc) {
+  HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
+  DCHECK(case_target != nullptr);
+  PotentiallyAddSuspendCheck(case_target, dex_pc);
+
+  // The current case's value.
+  HInstruction* this_case_value = GetIntConstant(case_value_int);
+
+  // Compare value and this_case_value.
+  HEqual* comparison = new (arena_) HEqual(value, this_case_value);
+  current_block_->AddInstruction(comparison);
+  HInstruction* ifinst = new (arena_) HIf(comparison);
+  current_block_->AddInstruction(ifinst);
+
+  // Case hit: use the target offset to determine where to go.
+  current_block_->AddSuccessor(case_target);
+
+  // Case miss: go to the next case (or default fall-through).
+  // When there is a next case, we use the block stored with the table offset representing this
+  // case (that is where we registered them in ComputeBranchTargets).
+  // When there is no next case, we use the following instruction.
+  // TODO: Find a good way to peel the last iteration to avoid conditional, but still have re-use.
+  if (!is_last_case) {
+    HBasicBlock* next_case_target = FindBlockStartingAt(table.GetDexPcForIndex(index));
+    DCHECK(next_case_target != nullptr);
+    current_block_->AddSuccessor(next_case_target);
+
+    // Need to manually add the block, as there is no dex-pc transition for the cases.
+    graph_->AddBlock(next_case_target);
+
+    current_block_ = next_case_target;
+  } else {
+    HBasicBlock* default_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+    DCHECK(default_target != nullptr);
+    current_block_->AddSuccessor(default_target);
+    current_block_ = nullptr;
+  }
+}
+
+void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) {
+  int32_t target_offset = target->GetDexPc() - dex_pc;
   if (target_offset <= 0) {
-    // Unconditionnally add a suspend check to backward branches. We can remove
-    // them after we recognize loops in the graph.
+    // DX generates back edges to the first encountered return. We can save
+    // time of later passes by not adding redundant suspend checks.
+    HInstruction* last_in_target = target->GetLastInstruction();
+    if (last_in_target != nullptr &&
+        (last_in_target->IsReturn() || last_in_target->IsReturnVoid())) {
+      return;
+    }
+
+    // Add a suspend check to backward branches which may potentially loop. We
+    // can remove them after we recognize loops in the graph.
     current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_pc));
   }
 }
@@ -988,9 +1208,9 @@
     case Instruction::GOTO_16:
     case Instruction::GOTO_32: {
       int32_t offset = instruction.GetTargetOffset();
-      PotentiallyAddSuspendCheck(offset, dex_pc);
       HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
       DCHECK(target != nullptr);
+      PotentiallyAddSuspendCheck(target, dex_pc);
       current_block_->AddInstruction(new (arena_) HGoto());
       current_block_->AddSuccessor(target);
       current_block_ = nullptr;
@@ -1079,52 +1299,77 @@
     }
 
     case Instruction::INT_TO_LONG: {
-      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong);
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong, dex_pc);
       break;
     }
 
     case Instruction::INT_TO_FLOAT: {
-      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat);
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::INT_TO_DOUBLE: {
-      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble);
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::LONG_TO_INT: {
-      Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt);
+      Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt, dex_pc);
       break;
     }
 
     case Instruction::LONG_TO_FLOAT: {
-      Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat);
+      Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::LONG_TO_DOUBLE: {
-      Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble);
+      Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble, dex_pc);
       break;
     }
 
     case Instruction::FLOAT_TO_INT: {
-      Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt);
+      Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt, dex_pc);
+      break;
+    }
+
+    case Instruction::FLOAT_TO_LONG: {
+      Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimLong, dex_pc);
+      break;
+    }
+
+    case Instruction::FLOAT_TO_DOUBLE: {
+      Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimDouble, dex_pc);
+      break;
+    }
+
+    case Instruction::DOUBLE_TO_INT: {
+      Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc);
+      break;
+    }
+
+    case Instruction::DOUBLE_TO_LONG: {
+      Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc);
+      break;
+    }
+
+    case Instruction::DOUBLE_TO_FLOAT: {
+      Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc);
       break;
     }
 
     case Instruction::INT_TO_BYTE: {
-      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte);
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte, dex_pc);
       break;
     }
 
     case Instruction::INT_TO_SHORT: {
-      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort);
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort, dex_pc);
       break;
     }
 
     case Instruction::INT_TO_CHAR: {
-      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar);
+      Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar, dex_pc);
       break;
     }
 
@@ -1227,6 +1472,16 @@
       break;
     }
 
+    case Instruction::REM_FLOAT: {
+      Binop_23x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+      break;
+    }
+
+    case Instruction::REM_DOUBLE: {
+      Binop_23x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+      break;
+    }
+
     case Instruction::AND_INT: {
       Binop_23x<HAnd>(instruction, Primitive::kPrimInt);
       break;
@@ -1366,6 +1621,16 @@
       break;
     }
 
+    case Instruction::REM_FLOAT_2ADDR: {
+      Binop_12x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+      break;
+    }
+
+    case Instruction::REM_DOUBLE_2ADDR: {
+      Binop_12x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+      break;
+    }
+
     case Instruction::SHL_INT_2ADDR: {
       Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt);
       break;
@@ -1526,16 +1791,24 @@
     }
 
     case Instruction::NEW_INSTANCE: {
-      current_block_->AddInstruction(
-          new (arena_) HNewInstance(dex_pc, instruction.VRegB_21c()));
+      uint16_t type_index = instruction.VRegB_21c();
+      QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+          ? kQuickAllocObjectWithAccessCheck
+          : kQuickAllocObject;
+
+      current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint));
       UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
       break;
     }
 
     case Instruction::NEW_ARRAY: {
+      uint16_t type_index = instruction.VRegC_22c();
       HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+      QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+          ? kQuickAllocArrayWithAccessCheck
+          : kQuickAllocArray;
       current_block_->AddInstruction(
-          new (arena_) HNewArray(length, dex_pc, instruction.VRegC_22c()));
+          new (arena_) HNewArray(length, dex_pc, type_index, entrypoint));
       UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
       break;
     }
@@ -1695,15 +1968,20 @@
       uint16_t type_index = instruction.VRegB_21c();
       bool type_known_final;
       bool type_known_abstract;
-      bool is_referrers_class;
+      bool dont_use_is_referrers_class;
+      // `CanAccessTypeWithoutChecks` will tell whether the method being
+      // built is trying to access its own class, so that the generated
+      // code can optimize for this case. However, the optimization does not
+      // work for inlining, so we use `IsCompilingClass` instead.
       bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
           dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
-          &type_known_final, &type_known_abstract, &is_referrers_class);
+          &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
       if (!can_access) {
+        MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
         return false;
       }
       current_block_->AddInstruction(
-          new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc));
+          new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
     }
@@ -1760,7 +2038,22 @@
       break;
     }
 
+    case Instruction::PACKED_SWITCH: {
+      BuildPackedSwitch(instruction, dex_pc);
+      break;
+    }
+
+    case Instruction::SPARSE_SWITCH: {
+      BuildSparseSwitch(instruction, dex_pc);
+      break;
+    }
+
     default:
+      VLOG(compiler) << "Did not compile "
+                     << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+                     << " because of unhandled instruction "
+                     << instruction.Name();
+      MaybeRecordStat(MethodCompilationStat::kNotCompiledUnhandledInstruction);
       return false;
   }
   return true;
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 8519bcb..c510136 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -21,6 +21,7 @@
 #include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
+#include "optimizing_compiler_stats.h"
 #include "primitive.h"
 #include "utils/arena_object.h"
 #include "utils/growable_array.h"
@@ -29,48 +30,55 @@
 namespace art {
 
 class Instruction;
+class SwitchTable;
 
 class HGraphBuilder : public ValueObject {
  public:
-  HGraphBuilder(ArenaAllocator* arena,
+  HGraphBuilder(HGraph* graph,
                 DexCompilationUnit* dex_compilation_unit,
+                const DexCompilationUnit* const outer_compilation_unit,
                 const DexFile* dex_file,
-                CompilerDriver* driver)
-      : arena_(arena),
-        branch_targets_(arena, 0),
-        locals_(arena, 0),
+                CompilerDriver* driver,
+                OptimizingCompilerStats* compiler_stats)
+      : arena_(graph->GetArena()),
+        branch_targets_(graph->GetArena(), 0),
+        locals_(graph->GetArena(), 0),
         entry_block_(nullptr),
         exit_block_(nullptr),
         current_block_(nullptr),
-        graph_(nullptr),
+        graph_(graph),
         constant0_(nullptr),
         constant1_(nullptr),
         dex_file_(dex_file),
         dex_compilation_unit_(dex_compilation_unit),
         compiler_driver_(driver),
+        outer_compilation_unit_(outer_compilation_unit),
         return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
         code_start_(nullptr),
-        latest_result_(nullptr) {}
+        latest_result_(nullptr),
+        compilation_stats_(compiler_stats) {}
 
   // Only for unit testing.
-  HGraphBuilder(ArenaAllocator* arena, Primitive::Type return_type = Primitive::kPrimInt)
-      : arena_(arena),
-        branch_targets_(arena, 0),
-        locals_(arena, 0),
+  HGraphBuilder(HGraph* graph, Primitive::Type return_type = Primitive::kPrimInt)
+      : arena_(graph->GetArena()),
+        branch_targets_(graph->GetArena(), 0),
+        locals_(graph->GetArena(), 0),
         entry_block_(nullptr),
         exit_block_(nullptr),
         current_block_(nullptr),
-        graph_(nullptr),
+        graph_(graph),
         constant0_(nullptr),
         constant1_(nullptr),
         dex_file_(nullptr),
         dex_compilation_unit_(nullptr),
         compiler_driver_(nullptr),
+        outer_compilation_unit_(nullptr),
         return_type_(return_type),
         code_start_(nullptr),
-        latest_result_(nullptr) {}
+        latest_result_(nullptr),
+        compilation_stats_(nullptr) {}
 
-  HGraph* BuildGraph(const DexFile::CodeItem& code);
+  bool BuildGraph(const DexFile::CodeItem& code);
 
  private:
   // Analyzes the dex instruction and adds HInstruction to the graph
@@ -98,8 +106,9 @@
   HLocal* GetLocalAt(int register_index) const;
   void UpdateLocal(int register_index, HInstruction* instruction) const;
   HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
-  void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc);
+  void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
   void InitializeParameters(uint16_t number_of_parameters);
+  bool NeedsAccessCheck(uint32_t type_index) const;
 
   template<typename T>
   void Unop_12x(const Instruction& instruction, Primitive::Type type);
@@ -135,7 +144,8 @@
 
   void Conversion_12x(const Instruction& instruction,
                       Primitive::Type input_type,
-                      Primitive::Type result_type);
+                      Primitive::Type result_type,
+                      uint32_t dex_pc);
 
   void BuildCheckedDivRem(uint16_t out_reg,
                           uint16_t first_reg,
@@ -202,6 +212,31 @@
                       uint16_t type_index,
                       uint32_t dex_pc);
 
+  // Builds an instruction sequence for a packed switch statement.
+  void BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc);
+
+  // Builds an instruction sequence for a sparse switch statement.
+  void BuildSparseSwitch(const Instruction& instruction, uint32_t dex_pc);
+
+  void BuildSwitchCaseHelper(const Instruction& instruction, size_t index,
+                             bool is_last_case, const SwitchTable& table,
+                             HInstruction* value, int32_t case_value_int,
+                             int32_t target_offset, uint32_t dex_pc);
+
+  bool SkipCompilation(size_t number_of_dex_instructions,
+                       size_t number_of_blocks,
+                       size_t number_of_branches);
+
+  void MaybeRecordStat(MethodCompilationStat compilation_stat);
+
+  // Returns whether `type_index` points to the outer-most compiling method's class.
+  bool IsCompilingClass(uint16_t type_index) const {
+    uint32_t referrer_index = outer_compilation_unit_->GetDexMethodIndex();
+    const DexFile::MethodId& method_id =
+        outer_compilation_unit_->GetDexFile()->GetMethodId(referrer_index);
+    return method_id.class_idx_ == type_index;
+  }
+
   ArenaAllocator* const arena_;
 
   // A list of the size of the dex code holding block information for
@@ -214,14 +249,26 @@
   HBasicBlock* entry_block_;
   HBasicBlock* exit_block_;
   HBasicBlock* current_block_;
-  HGraph* graph_;
+  HGraph* const graph_;
 
   HIntConstant* constant0_;
   HIntConstant* constant1_;
 
+  // The dex file where the method being compiled is.
   const DexFile* const dex_file_;
+
+  // The compilation unit of the current method being compiled. Note that
+  // it can be an inlined method.
   DexCompilationUnit* const dex_compilation_unit_;
+
   CompilerDriver* const compiler_driver_;
+
+  // The compilation unit of the outermost method being compiled. That is the
+  // method being compiled (and not inlined), and potentially inlining other
+  // methods.
+  const DexCompilationUnit* const outer_compilation_unit_;
+
+  // The return type of the method being compiled.
   const Primitive::Type return_type_;
 
   // The pointer in the dex file where the instructions of the code item
@@ -232,6 +279,8 @@
   // used by move-result instructions.
   HInstruction* latest_result_;
 
+  OptimizingCompilerStats* compilation_stats_;
+
   DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);
 };
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index e581af2..d0739a6 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,59 +41,52 @@
 }
 
 void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
-  const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
-  DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
-  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
   Initialize();
-
-  DCHECK_EQ(frame_size_, kUninitializedFrameSize);
   if (!is_leaf) {
     MarkNotLeaf();
   }
-  ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
-                     + GetGraph()->GetTemporariesVRegSlots()
-                     + 1 /* filler */,
-                   0, /* the baseline compiler does not have live registers at slow path */
-                   GetGraph()->GetMaximumNumberOfOutVRegs()
-                     + 1 /* current method */);
-  GenerateFrameEntry();
+  InitializeCodeGeneration(GetGraph()->GetNumberOfLocalVRegs()
+                             + GetGraph()->GetTemporariesVRegSlots()
+                             + 1 /* filler */,
+                           0, /* the baseline compiler does not have live registers at slow path */
+                           0, /* the baseline compiler does not have live registers at slow path */
+                           GetGraph()->GetMaximumNumberOfOutVRegs()
+                             + 1 /* current method */,
+                           GetGraph()->GetBlocks());
+  CompileInternal(allocator, /* is_baseline */ true);
+}
 
-  HGraphVisitor* location_builder = GetLocationBuilder();
+void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) {
   HGraphVisitor* instruction_visitor = GetInstructionVisitor();
-  for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
-    HBasicBlock* block = blocks.Get(i);
+  DCHECK_EQ(current_block_index_, 0u);
+  GenerateFrameEntry();
+  for (size_t e = block_order_->Size(); current_block_index_ < e; ++current_block_index_) {
+    HBasicBlock* block = block_order_->Get(current_block_index_);
     Bind(block);
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       HInstruction* current = it.Current();
-      current->Accept(location_builder);
-      InitLocations(current);
+      if (is_baseline) {
+        InitLocationsBaseline(current);
+      }
       current->Accept(instruction_visitor);
     }
   }
-  GenerateSlowPaths();
+
+  // Generate the slow paths.
+  for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
+    slow_paths_.Get(i)->EmitNativeCode(this);
+  }
+
+  // Finalize instructions in assember;
   Finalize(allocator);
 }
 
 void CodeGenerator::CompileOptimized(CodeAllocator* allocator) {
-  // The frame size has already been computed during register allocation.
-  DCHECK_NE(frame_size_, kUninitializedFrameSize);
-  const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
-  DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
-  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
+  // The register allocator already called `InitializeCodeGeneration`,
+  // where the frame size has been computed.
+  DCHECK(block_order_ != nullptr);
   Initialize();
-
-  GenerateFrameEntry();
-  HGraphVisitor* instruction_visitor = GetInstructionVisitor();
-  for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
-    HBasicBlock* block = blocks.Get(i);
-    Bind(block);
-    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-      HInstruction* current = it.Current();
-      current->Accept(instruction_visitor);
-    }
-  }
-  GenerateSlowPaths();
-  Finalize(allocator);
+  CompileInternal(allocator, /* is_baseline */ false);
 }
 
 void CodeGenerator::Finalize(CodeAllocator* allocator) {
@@ -104,12 +97,6 @@
   GetAssembler()->FinalizeInstructions(code);
 }
 
-void CodeGenerator::GenerateSlowPaths() {
-  for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
-    slow_paths_.Get(i)->EmitNativeCode(this);
-  }
-}
-
 size_t CodeGenerator::FindFreeEntry(bool* array, size_t length) {
   for (size_t i = 0; i < length; ++i) {
     if (!array[i]) {
@@ -135,17 +122,33 @@
   return -1;
 }
 
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
-                                     size_t maximum_number_of_live_registers,
-                                     size_t number_of_out_slots) {
+void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
+                                             size_t maximum_number_of_live_core_registers,
+                                             size_t maximum_number_of_live_fp_registers,
+                                             size_t number_of_out_slots,
+                                             const GrowableArray<HBasicBlock*>& block_order) {
+  block_order_ = &block_order;
+  DCHECK(block_order_->Get(0) == GetGraph()->GetEntryBlock());
+  DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), block_order_->Get(1)));
+  ComputeSpillMask();
   first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
 
-  SetFrameSize(RoundUp(
-      number_of_spill_slots * kVRegSize
-      + number_of_out_slots * kVRegSize
-      + maximum_number_of_live_registers * GetWordSize()
-      + FrameEntrySpillSize(),
-      kStackAlignment));
+  if (number_of_spill_slots == 0
+      && !HasAllocatedCalleeSaveRegisters()
+      && IsLeafMethod()
+      && !RequiresCurrentMethod()) {
+    DCHECK_EQ(maximum_number_of_live_core_registers, 0u);
+    DCHECK_EQ(maximum_number_of_live_fp_registers, 0u);
+    SetFrameSize(CallPushesPC() ? GetWordSize() : 0);
+  } else {
+    SetFrameSize(RoundUp(
+        number_of_spill_slots * kVRegSize
+        + number_of_out_slots * kVRegSize
+        + maximum_number_of_live_core_registers * GetWordSize()
+        + maximum_number_of_live_fp_registers * GetFloatingPointSpillSlotSize()
+        + FrameEntrySpillSize(),
+        kStackAlignment));
+  }
 }
 
 Location CodeGenerator::GetTemporaryLocation(HTemporary* temp) const {
@@ -233,7 +236,8 @@
     }
   }
 
-  SetupBlockedRegisters();
+  static constexpr bool kBaseline = true;
+  SetupBlockedRegisters(kBaseline);
 
   // Allocate all unallocated input locations.
   for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
@@ -290,11 +294,12 @@
         result_location = locations->InAt(0);
         break;
     }
-    locations->SetOut(result_location);
+    locations->UpdateOut(result_location);
   }
 }
 
-void CodeGenerator::InitLocations(HInstruction* instruction) {
+void CodeGenerator::InitLocationsBaseline(HInstruction* instruction) {
+  AllocateLocations(instruction);
   if (instruction->GetLocations() == nullptr) {
     if (instruction->IsTemporary()) {
       HInstruction* previous = instruction->GetPrevious();
@@ -320,29 +325,46 @@
   }
 }
 
-bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
-  // We currently iterate over the block in insertion order.
-  return current->GetBlockId() + 1 == next->GetBlockId();
+void CodeGenerator::AllocateLocations(HInstruction* instruction) {
+  instruction->Accept(GetLocationBuilder());
+  LocationSummary* locations = instruction->GetLocations();
+  if (!instruction->IsSuspendCheckEntry()) {
+    if (locations != nullptr && locations->CanCall()) {
+      MarkNotLeaf();
+    }
+    if (instruction->NeedsCurrentMethod()) {
+      SetRequiresCurrentMethod();
+    }
+  }
 }
 
-CodeGenerator* CodeGenerator::Create(ArenaAllocator* allocator,
-                                     HGraph* graph,
-                                     InstructionSet instruction_set) {
+bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
+  DCHECK_EQ(block_order_->Get(current_block_index_), current);
+  return (current_block_index_ < block_order_->Size() - 1)
+      && (block_order_->Get(current_block_index_ + 1) == next);
+}
+
+CodeGenerator* CodeGenerator::Create(HGraph* graph,
+                                     InstructionSet instruction_set,
+                                     const InstructionSetFeatures& isa_features,
+                                     const CompilerOptions& compiler_options) {
   switch (instruction_set) {
     case kArm:
     case kThumb2: {
-      return new (allocator) arm::CodeGeneratorARM(graph);
+      return new arm::CodeGeneratorARM(graph,
+          *isa_features.AsArmInstructionSetFeatures(),
+          compiler_options);
     }
     case kArm64: {
-      return new (allocator) arm64::CodeGeneratorARM64(graph);
+      return new arm64::CodeGeneratorARM64(graph, compiler_options);
     }
     case kMips:
       return nullptr;
     case kX86: {
-      return new (allocator) x86::CodeGeneratorX86(graph);
+      return new x86::CodeGeneratorX86(graph, compiler_options);
     }
     case kX86_64: {
-      return new (allocator) x86_64::CodeGeneratorX86_64(graph);
+      return new x86_64::CodeGeneratorX86_64(graph, compiler_options);
     }
     default:
       return nullptr;
@@ -369,12 +391,12 @@
     uint32_t native_offset = pc_info.native_pc;
     uint32_t dex_pc = pc_info.dex_pc;
     const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
-    CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
+    CHECK(references != nullptr) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
     builder.AddEntry(native_offset, references);
   }
 }
 
-void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data, SrcMap* src_map) const {
+void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data, DefaultSrcMap* src_map) const {
   uint32_t pc2dex_data_size = 0u;
   uint32_t pc2dex_entries = pc_infos_.Size();
   uint32_t pc2dex_offset = 0u;
@@ -499,6 +521,29 @@
 }
 
 void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) {
+  if (instruction != nullptr) {
+    // The code generated for some type conversions may call the
+    // runtime, thus normally requiring a subsequent call to this
+    // method.  However, the method verifier does not produce PC
+    // information for certain instructions, which are considered "atomic"
+    // (they cannot join a GC).
+    // Therefore we do not currently record PC information for such
+    // instructions.  As this may change later, we added this special
+    // case so that code generators may nevertheless call
+    // CodeGenerator::RecordPcInfo without triggering an error in
+    // CodeGenerator::BuildNativeGCMap ("Missing ref for dex pc 0x")
+    // thereafter.
+    if (instruction->IsTypeConversion()) {
+      return;
+    }
+    if (instruction->IsRem()) {
+      Primitive::Type type = instruction->AsRem()->GetResultType();
+      if ((type == Primitive::kPrimFloat) || (type == Primitive::kPrimDouble)) {
+        return;
+      }
+    }
+  }
+
   // Collect PC infos for the mapping table.
   struct PcInfo pc_info;
   pc_info.dex_pc = dex_pc;
@@ -518,8 +563,18 @@
 
   size_t environment_size = instruction->EnvironmentSize();
 
-  size_t register_mask = 0;
   size_t inlining_depth = 0;
+  uint32_t register_mask = locations->GetRegisterMask();
+  if (locations->OnlyCallsOnSlowPath()) {
+    // In case of slow path, we currently set the location of caller-save registers
+    // to register (instead of their stack location when pushed before the slow-path
+    // call). Therefore register_mask contains both callee-save and caller-save
+    // registers that hold objects. We must remove the caller-save from the mask, since
+    // they will be overwritten by the callee.
+    register_mask &= core_callee_save_mask_;
+  }
+  // The register mask must be a subset of callee-save registers.
+  DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask);
   stack_map_stream_.AddStackMapEntry(
       dex_pc, pc_info.native_pc, register_mask,
       locations->GetStackMask(), environment_size, inlining_depth);
@@ -542,10 +597,19 @@
           stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
           ++i;
           DCHECK_LT(i, environment_size);
-        } else {
-          DCHECK(current->IsIntConstant());
+        } else if (current->IsDoubleConstant()) {
+          int64_t value = bit_cast<double, int64_t>(current->AsDoubleConstant()->GetValue());
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+          ++i;
+          DCHECK_LT(i, environment_size);
+        } else if (current->IsIntConstant()) {
           int32_t value = current->AsIntConstant()->GetValue();
           stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+        } else {
+          DCHECK(current->IsFloatConstant());
+          int32_t value = bit_cast<float, int32_t>(current->AsFloatConstant()->GetValue());
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
         }
         break;
       }
@@ -586,30 +650,84 @@
         break;
       }
 
+      case Location::kFpuRegisterPair : {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, location.low());
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, location.high());
+        ++i;
+        DCHECK_LT(i, environment_size);
+        break;
+      }
+
+      case Location::kRegisterPair : {
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, location.low());
+        stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, location.high());
+        ++i;
+        DCHECK_LT(i, environment_size);
+        break;
+      }
+
       default:
         LOG(FATAL) << "Unexpected kind " << location.GetKind();
     }
   }
 }
 
+bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) {
+  HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves();
+  return (first_next_not_move != nullptr) && first_next_not_move->CanDoImplicitNullCheck();
+}
+
+void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) {
+  // If we are from a static path don't record the pc as we can't throw NPE.
+  // NB: having the checks here makes the code much less verbose in the arch
+  // specific code generators.
+  if (instr->IsStaticFieldSet() || instr->IsStaticFieldGet()) {
+    return;
+  }
+
+  if (!compiler_options_.GetImplicitNullChecks()) {
+    return;
+  }
+
+  if (!instr->CanDoImplicitNullCheck()) {
+    return;
+  }
+
+  // Find the first previous instruction which is not a move.
+  HInstruction* first_prev_not_move = instr->GetPreviousDisregardingMoves();
+
+  // If the instruction is a null check it means that `instr` is the first user
+  // and needs to record the pc.
+  if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) {
+    HNullCheck* null_check = first_prev_not_move->AsNullCheck();
+    // TODO: The parallel moves modify the environment. Their changes need to be reverted
+    // otherwise the stack maps at the throw point will not be correct.
+    RecordPcInfo(null_check, null_check->GetDexPc());
+  }
+}
+
 void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) {
   RegisterSet* register_set = locations->GetLiveRegisters();
   size_t stack_offset = first_register_slot_in_slow_path_;
   for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
-    if (register_set->ContainsCoreRegister(i)) {
-      // If the register holds an object, update the stack mask.
-      if (locations->RegisterContainsObject(i)) {
-        locations->SetStackBit(stack_offset / kVRegSize);
+    if (!IsCoreCalleeSaveRegister(i)) {
+      if (register_set->ContainsCoreRegister(i)) {
+        // If the register holds an object, update the stack mask.
+        if (locations->RegisterContainsObject(i)) {
+          locations->SetStackBit(stack_offset / kVRegSize);
+        }
+        DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+        stack_offset += SaveCoreRegister(stack_offset, i);
       }
-      DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
-      stack_offset += SaveCoreRegister(stack_offset, i);
     }
   }
 
   for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
-    if (register_set->ContainsFloatingPointRegister(i)) {
-      DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
-      stack_offset += SaveFloatingPointRegister(stack_offset, i);
+    if (!IsFloatingPointCalleeSaveRegister(i)) {
+      if (register_set->ContainsFloatingPointRegister(i)) {
+        DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+        stack_offset += SaveFloatingPointRegister(stack_offset, i);
+      }
     }
   }
 }
@@ -618,16 +736,20 @@
   RegisterSet* register_set = locations->GetLiveRegisters();
   size_t stack_offset = first_register_slot_in_slow_path_;
   for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
-    if (register_set->ContainsCoreRegister(i)) {
-      DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
-      stack_offset += RestoreCoreRegister(stack_offset, i);
+    if (!IsCoreCalleeSaveRegister(i)) {
+      if (register_set->ContainsCoreRegister(i)) {
+        DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+        stack_offset += RestoreCoreRegister(stack_offset, i);
+      }
     }
   }
 
   for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
-    if (register_set->ContainsFloatingPointRegister(i)) {
-      DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
-      stack_offset += RestoreFloatingPointRegister(stack_offset, i);
+    if (!IsFloatingPointCalleeSaveRegister(i)) {
+      if (register_set->ContainsFloatingPointRegister(i)) {
+        DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
+        stack_offset += RestoreFloatingPointRegister(stack_offset, i);
+      }
     }
   }
 }
@@ -652,11 +774,9 @@
 }
 
 void CodeGenerator::EmitParallelMoves(Location from1, Location to1, Location from2, Location to2) {
-  MoveOperands move1(from1, to1, nullptr);
-  MoveOperands move2(from2, to2, nullptr);
   HParallelMove parallel_move(GetGraph()->GetArena());
-  parallel_move.AddMove(&move1);
-  parallel_move.AddMove(&move2);
+  parallel_move.AddMove(from1, to1, nullptr);
+  parallel_move.AddMove(from2, to2, nullptr);
   GetMoveResolver()->EmitNativeCode(&parallel_move);
 }
 
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 7c8f6a2..efd0c84 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -18,7 +18,9 @@
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_H_
 
 #include "arch/instruction_set.h"
+#include "arch/instruction_set_features.h"
 #include "base/bit_field.h"
+#include "driver/compiler_options.h"
 #include "globals.h"
 #include "locations.h"
 #include "memory_region.h"
@@ -28,7 +30,6 @@
 namespace art {
 
 static size_t constexpr kVRegSize = 4;
-static size_t constexpr kUninitializedFrameSize = 0;
 
 // Binary encoding of 2^32 for type double.
 static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000);
@@ -37,12 +38,17 @@
 
 // Maximum value for a primitive integer.
 static int32_t constexpr kPrimIntMax = 0x7fffffff;
+// Maximum value for a primitive long.
+static int64_t constexpr kPrimLongMax = 0x7fffffffffffffff;
 
 class Assembler;
 class CodeGenerator;
 class DexCompilationUnit;
 class ParallelMoveResolver;
+class SrcMapElem;
+template <class Alloc>
 class SrcMap;
+using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>;
 
 class CodeAllocator {
  public:
@@ -71,15 +77,17 @@
   DISALLOW_COPY_AND_ASSIGN(SlowPathCode);
 };
 
-class CodeGenerator : public ArenaObject<kArenaAllocMisc> {
+class CodeGenerator {
  public:
   // Compiles the graph to executable instructions. Returns whether the compilation
   // succeeded.
   void CompileBaseline(CodeAllocator* allocator, bool is_leaf = false);
   void CompileOptimized(CodeAllocator* allocator);
-  static CodeGenerator* Create(ArenaAllocator* allocator,
-                               HGraph* graph,
-                               InstructionSet instruction_set);
+  static CodeGenerator* Create(HGraph* graph,
+                               InstructionSet instruction_set,
+                               const InstructionSetFeatures& isa_features,
+                               const CompilerOptions& compiler_options);
+  virtual ~CodeGenerator() {}
 
   HGraph* GetGraph() const { return graph_; }
 
@@ -98,29 +106,47 @@
   virtual void GenerateFrameExit() = 0;
   virtual void Bind(HBasicBlock* block) = 0;
   virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
-  virtual HGraphVisitor* GetLocationBuilder() = 0;
-  virtual HGraphVisitor* GetInstructionVisitor() = 0;
   virtual Assembler* GetAssembler() = 0;
   virtual size_t GetWordSize() const = 0;
+  virtual size_t GetFloatingPointSpillSlotSize() const = 0;
   virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
-  void ComputeFrameSize(size_t number_of_spill_slots,
-                        size_t maximum_number_of_live_registers,
-                        size_t number_of_out_slots);
-  virtual size_t FrameEntrySpillSize() const = 0;
+  void InitializeCodeGeneration(size_t number_of_spill_slots,
+                                size_t maximum_number_of_live_core_registers,
+                                size_t maximum_number_of_live_fp_registers,
+                                size_t number_of_out_slots,
+                                const GrowableArray<HBasicBlock*>& block_order);
   int32_t GetStackSlot(HLocal* local) const;
   Location GetTemporaryLocation(HTemporary* temp) const;
 
   uint32_t GetFrameSize() const { return frame_size_; }
   void SetFrameSize(uint32_t size) { frame_size_ = size; }
   uint32_t GetCoreSpillMask() const { return core_spill_mask_; }
+  uint32_t GetFpuSpillMask() const { return fpu_spill_mask_; }
 
   size_t GetNumberOfCoreRegisters() const { return number_of_core_registers_; }
   size_t GetNumberOfFloatingPointRegisters() const { return number_of_fpu_registers_; }
-  virtual void SetupBlockedRegisters() const = 0;
+  virtual void SetupBlockedRegisters(bool is_baseline) const = 0;
+
+  virtual void ComputeSpillMask() {
+    core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
+    DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
+    fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
+  }
+
+  static uint32_t ComputeRegisterMask(const int* registers, size_t length) {
+    uint32_t mask = 0;
+    for (size_t i = 0, e = length; i < e; ++i) {
+      mask |= (1 << registers[i]);
+    }
+    return mask;
+  }
 
   virtual void DumpCoreRegister(std::ostream& stream, int reg) const = 0;
   virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0;
   virtual InstructionSet GetInstructionSet() const = 0;
+
+  const CompilerOptions& GetCompilerOptions() const { return compiler_options_; }
+
   // Saves the register in the stack. Returns the size taken on stack.
   virtual size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) = 0;
   // Restores the register from the stack. Returns the size taken on stack.
@@ -135,16 +161,25 @@
     UNIMPLEMENTED(FATAL);
     UNREACHABLE();
   }
+  virtual bool NeedsTwoRegisters(Primitive::Type type) const = 0;
+
+  bool IsCoreCalleeSaveRegister(int reg) const {
+    return (core_callee_save_mask_ & (1 << reg)) != 0;
+  }
+
+  bool IsFloatingPointCalleeSaveRegister(int reg) const {
+    return (fpu_callee_save_mask_ & (1 << reg)) != 0;
+  }
 
   void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
+  bool CanMoveNullCheckToUser(HNullCheck* null_check);
+  void MaybeRecordImplicitNullCheck(HInstruction* instruction);
 
   void AddSlowPath(SlowPathCode* slow_path) {
     slow_paths_.Add(slow_path);
   }
 
-  void GenerateSlowPaths();
-
-  void BuildMappingTable(std::vector<uint8_t>* vector, SrcMap* src_map) const;
+  void BuildMappingTable(std::vector<uint8_t>* vector, DefaultSrcMap* src_map) const;
   void BuildVMapTable(std::vector<uint8_t>* vector) const;
   void BuildNativeGCMap(
       std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
@@ -158,6 +193,15 @@
 
   void MarkNotLeaf() {
     is_leaf_ = false;
+    requires_current_method_ = true;
+  }
+
+  void SetRequiresCurrentMethod() {
+    requires_current_method_ = true;
+  }
+
+  bool RequiresCurrentMethod() const {
+    return requires_current_method_;
   }
 
   // Clears the spill slots taken by loop phis in the `LocationSummary` of the
@@ -186,13 +230,23 @@
     return type == Primitive::kPrimNot && !value->IsIntConstant();
   }
 
+  void AddAllocatedRegister(Location location) {
+    allocated_registers_.Add(location);
+  }
+
+  void AllocateLocations(HInstruction* instruction);
+
  protected:
   CodeGenerator(HGraph* graph,
                 size_t number_of_core_registers,
                 size_t number_of_fpu_registers,
-                size_t number_of_register_pairs)
-      : frame_size_(kUninitializedFrameSize),
+                size_t number_of_register_pairs,
+                uint32_t core_callee_save_mask,
+                uint32_t fpu_callee_save_mask,
+                const CompilerOptions& compiler_options)
+      : frame_size_(0),
         core_spill_mask_(0),
+        fpu_spill_mask_(0),
         first_register_slot_in_slow_path_(0),
         blocked_core_registers_(graph->GetArena()->AllocArray<bool>(number_of_core_registers)),
         blocked_fpu_registers_(graph->GetArena()->AllocArray<bool>(number_of_fpu_registers)),
@@ -200,12 +254,17 @@
         number_of_core_registers_(number_of_core_registers),
         number_of_fpu_registers_(number_of_fpu_registers),
         number_of_register_pairs_(number_of_register_pairs),
+        core_callee_save_mask_(core_callee_save_mask),
+        fpu_callee_save_mask_(fpu_callee_save_mask),
         graph_(graph),
+        compiler_options_(compiler_options),
         pc_infos_(graph->GetArena(), 32),
         slow_paths_(graph->GetArena(), 8),
+        block_order_(nullptr),
+        current_block_index_(0),
         is_leaf_(true),
+        requires_current_method_(false),
         stack_map_stream_(graph->GetArena()) {}
-  ~CodeGenerator() {}
 
   // Register allocation logic.
   void AllocateRegistersLocally(HInstruction* instruction) const;
@@ -219,12 +278,51 @@
   virtual Location GetStackLocation(HLoadLocal* load) const = 0;
 
   virtual ParallelMoveResolver* GetMoveResolver() = 0;
+  virtual HGraphVisitor* GetLocationBuilder() = 0;
+  virtual HGraphVisitor* GetInstructionVisitor() = 0;
+
+  // Returns the location of the first spilled entry for floating point registers,
+  // relative to the stack pointer.
+  uint32_t GetFpuSpillStart() const {
+    return GetFrameSize() - FrameEntrySpillSize();
+  }
+
+  uint32_t GetFpuSpillSize() const {
+    return POPCOUNT(fpu_spill_mask_) * GetFloatingPointSpillSlotSize();
+  }
+
+  uint32_t GetCoreSpillSize() const {
+    return POPCOUNT(core_spill_mask_) * GetWordSize();
+  }
+
+  uint32_t FrameEntrySpillSize() const {
+    return GetFpuSpillSize() + GetCoreSpillSize();
+  }
+
+  bool HasAllocatedCalleeSaveRegisters() const {
+    // We check the core registers against 1 because it always comprises the return PC.
+    return (POPCOUNT(allocated_registers_.GetCoreRegisters() & core_callee_save_mask_) != 1)
+      || (POPCOUNT(allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_) != 0);
+  }
+
+  bool CallPushesPC() const {
+    InstructionSet instruction_set = GetInstructionSet();
+    return instruction_set == kX86 || instruction_set == kX86_64;
+  }
+
+  bool HasEmptyFrame() const {
+    return GetFrameSize() == (CallPushesPC() ? GetWordSize() : 0);
+  }
 
   // Frame size required for this method.
   uint32_t frame_size_;
   uint32_t core_spill_mask_;
+  uint32_t fpu_spill_mask_;
   uint32_t first_register_slot_in_slow_path_;
 
+  // Registers that were allocated during linear scan.
+  RegisterSet allocated_registers_;
+
   // Arrays used when doing register allocation to know which
   // registers we can allocate. `SetupBlockedRegisters` updates the
   // arrays.
@@ -234,18 +332,33 @@
   size_t number_of_core_registers_;
   size_t number_of_fpu_registers_;
   size_t number_of_register_pairs_;
+  const uint32_t core_callee_save_mask_;
+  const uint32_t fpu_callee_save_mask_;
 
  private:
-  void InitLocations(HInstruction* instruction);
+  void InitLocationsBaseline(HInstruction* instruction);
   size_t GetStackOffsetOfSavedRegister(size_t index);
+  void CompileInternal(CodeAllocator* allocator, bool is_baseline);
 
   HGraph* const graph_;
+  const CompilerOptions& compiler_options_;
 
   GrowableArray<PcInfo> pc_infos_;
   GrowableArray<SlowPathCode*> slow_paths_;
 
+  // The order to use for code generation.
+  const GrowableArray<HBasicBlock*>* block_order_;
+
+  // The current block index in `block_order_` of the block
+  // we are generating code for.
+  size_t current_block_index_;
+
+  // Whether the method is a leaf method.
   bool is_leaf_;
 
+  // Whether an instruction in the graph accesses the current method.
+  bool requires_current_method_;
+
   StackMapStream stack_map_stream_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 448a5a0..e5de2ab 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -16,8 +16,11 @@
 
 #include "code_generator_arm.h"
 
+#include "arch/arm/instruction_set_features_arm.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
+#include "intrinsics.h"
+#include "intrinsics_arm.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_method.h"
 #include "mirror/class.h"
@@ -31,21 +34,26 @@
 
 namespace arm {
 
-static DRegister FromLowSToD(SRegister reg) {
-  DCHECK_EQ(reg % 2, 0);
-  return static_cast<DRegister>(reg / 2);
+static bool ExpectedPairLayout(Location location) {
+  // We expected this for both core and fpu register pairs.
+  return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
 }
 
-static constexpr bool kExplicitStackOverflowCheck = false;
-
-static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2;  // LR, R6, R7
 static constexpr int kCurrentMethodStackOffset = 0;
 
 static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
-static constexpr SRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+    arraysize(kRuntimeParameterFpuRegisters);
+// We unconditionally allocate R5 to ensure we can do long operations
+// with baseline.
+static constexpr Register kCoreSavedRegisterForBaseline = R5;
+static constexpr Register kCoreCalleeSaves[] =
+    { R5, R6, R7, R8, R10, R11, PC };
+static constexpr SRegister kFpuCalleeSaves[] =
+    { S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 };
 
 class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
  public:
@@ -62,20 +70,6 @@
 #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
 
-class SlowPathCodeARM : public SlowPathCode {
- public:
-  SlowPathCodeARM() : entry_label_(), exit_label_() {}
-
-  Label* GetEntryLabel() { return &entry_label_; }
-  Label* GetExitLabel() { return &exit_label_; }
-
- private:
-  Label entry_label_;
-  Label exit_label_;
-
-  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
-};
-
 class NullCheckSlowPathARM : public SlowPathCodeARM {
  public:
   explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
@@ -108,20 +102,6 @@
   DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM);
 };
 
-class StackOverflowCheckSlowPathARM : public SlowPathCodeARM {
- public:
-  StackOverflowCheckSlowPathARM() {}
-
-  void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    __ Bind(GetEntryLabel());
-    __ LoadFromOffset(kLoadWord, PC, TR,
-        QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value());
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
-};
-
 class SuspendCheckSlowPathARM : public SlowPathCodeARM {
  public:
   SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
@@ -254,8 +234,8 @@
     codegen->SaveLiveRegisters(locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0));
-    __ LoadImmediate(calling_convention.GetRegisterAt(1), instruction_->GetStringIndex());
+    arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+    __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex());
     arm_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc());
     arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0));
@@ -371,16 +351,36 @@
   return kArmWordSize;
 }
 
-CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
-    : CodeGenerator(graph, kNumberOfCoreRegisters, kNumberOfSRegisters, kNumberOfRegisterPairs),
+size_t CodeGeneratorARM::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  __ StoreSToOffset(static_cast<SRegister>(reg_id), SP, stack_index);
+  return kArmWordSize;
+}
+
+size_t CodeGeneratorARM::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  __ LoadSFromOffset(static_cast<SRegister>(reg_id), SP, stack_index);
+  return kArmWordSize;
+}
+
+CodeGeneratorARM::CodeGeneratorARM(HGraph* graph,
+                                   const ArmInstructionSetFeatures& isa_features,
+                                   const CompilerOptions& compiler_options)
+    : CodeGenerator(graph,
+                    kNumberOfCoreRegisters,
+                    kNumberOfSRegisters,
+                    kNumberOfRegisterPairs,
+                    ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
+                                        arraysize(kCoreCalleeSaves)),
+                    ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
+                                        arraysize(kFpuCalleeSaves)),
+                    compiler_options),
       block_labels_(graph->GetArena(), 0),
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
       move_resolver_(graph->GetArena(), this),
-      assembler_(true) {}
-
-size_t CodeGeneratorARM::FrameEntrySpillSize() const {
-  return kNumberOfPushedRegistersAtEntry * kArmWordSize;
+      assembler_(true),
+      isa_features_(isa_features) {
+  // Save the PC register to mimic Quick.
+  AddAllocatedRegister(Location::RegisterLocation(PC));
 }
 
 Location CodeGeneratorARM::AllocateFreeRegister(Primitive::Type type) const {
@@ -434,7 +434,7 @@
   return Location();
 }
 
-void CodeGeneratorARM::SetupBlockedRegisters() const {
+void CodeGeneratorARM::SetupBlockedRegisters(bool is_baseline) const {
   // Don't allocate the dalvik style register pair passing.
   blocked_register_pairs_[R1_R2] = true;
 
@@ -449,31 +449,17 @@
   // Reserve temp register.
   blocked_core_registers_[IP] = true;
 
-  // TODO: We currently don't use Quick's callee saved registers.
-  // We always save and restore R6 and R7 to make sure we can use three
-  // register pairs for long operations.
-  blocked_core_registers_[R4] = true;
-  blocked_core_registers_[R5] = true;
-  blocked_core_registers_[R8] = true;
-  blocked_core_registers_[R10] = true;
-  blocked_core_registers_[R11] = true;
+  if (is_baseline) {
+    for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+      blocked_core_registers_[kCoreCalleeSaves[i]] = true;
+    }
 
-  blocked_fpu_registers_[S16] = true;
-  blocked_fpu_registers_[S17] = true;
-  blocked_fpu_registers_[S18] = true;
-  blocked_fpu_registers_[S19] = true;
-  blocked_fpu_registers_[S20] = true;
-  blocked_fpu_registers_[S21] = true;
-  blocked_fpu_registers_[S22] = true;
-  blocked_fpu_registers_[S23] = true;
-  blocked_fpu_registers_[S24] = true;
-  blocked_fpu_registers_[S25] = true;
-  blocked_fpu_registers_[S26] = true;
-  blocked_fpu_registers_[S27] = true;
-  blocked_fpu_registers_[S28] = true;
-  blocked_fpu_registers_[S29] = true;
-  blocked_fpu_registers_[S30] = true;
-  blocked_fpu_registers_[S31] = true;
+    blocked_core_registers_[kCoreSavedRegisterForBaseline] = false;
+
+    for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+      blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
+    }
+  }
 
   UpdateBlockedPairRegisters();
 }
@@ -494,35 +480,70 @@
         assembler_(codegen->GetAssembler()),
         codegen_(codegen) {}
 
+static uint32_t LeastSignificantBit(uint32_t mask) {
+  // ffs starts at 1.
+  return ffs(mask) - 1;
+}
+
+void CodeGeneratorARM::ComputeSpillMask() {
+  core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
+  // Save one extra register for baseline. Note that on thumb2, there is no easy
+  // instruction to restore just the PC, so this actually helps both baseline
+  // and non-baseline to save and restore at least two registers at entry and exit.
+  core_spill_mask_ |= (1 << kCoreSavedRegisterForBaseline);
+  DCHECK_NE(core_spill_mask_, 0u) << "At least the return address register must be saved";
+  fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
+  // We use vpush and vpop for saving and restoring floating point registers, which take
+  // a SRegister and the number of registers to save/restore after that SRegister. We
+  // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
+  // but in the range.
+  if (fpu_spill_mask_ != 0) {
+    uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
+    uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
+    for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
+      fpu_spill_mask_ |= (1 << i);
+    }
+  }
+}
+
 void CodeGeneratorARM::GenerateFrameEntry() {
   bool skip_overflow_check =
       IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
-  if (!skip_overflow_check) {
-    if (kExplicitStackOverflowCheck) {
-      SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
-      AddSlowPath(slow_path);
+  DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+  __ Bind(&frame_entry_label_);
 
-      __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
-      __ cmp(SP, ShifterOperand(IP));
-      __ b(slow_path->GetEntryLabel(), CC);
-    } else {
-      __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
-      __ LoadFromOffset(kLoadWord, IP, IP, 0);
-      RecordPcInfo(nullptr, 0);
-    }
+  if (HasEmptyFrame()) {
+    return;
   }
 
-  core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
-  __ PushList(1 << LR | 1 << R6 | 1 << R7);
+  if (!skip_overflow_check) {
+    __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+    __ LoadFromOffset(kLoadWord, IP, IP, 0);
+    RecordPcInfo(nullptr, 0);
+  }
 
-  // The return PC has already been pushed on the stack.
-  __ AddConstant(SP, -(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize));
+  // PC is in the list of callee-save to mimic Quick, but we need to push
+  // LR at entry instead.
+  __ PushList((core_spill_mask_ & (~(1 << PC))) | 1 << LR);
+  if (fpu_spill_mask_ != 0) {
+    SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
+    __ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
+  }
+  __ AddConstant(SP, -(GetFrameSize() - FrameEntrySpillSize()));
   __ StoreToOffset(kStoreWord, R0, SP, 0);
 }
 
 void CodeGeneratorARM::GenerateFrameExit() {
-  __ AddConstant(SP, GetFrameSize() - kNumberOfPushedRegistersAtEntry * kArmWordSize);
-  __ PopList(1 << PC | 1 << R6 | 1 << R7);
+  if (HasEmptyFrame()) {
+    __ bx(LR);
+    return;
+  }
+  __ AddConstant(SP, GetFrameSize() - FrameEntrySpillSize());
+  if (fpu_spill_mask_ != 0) {
+    SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
+    __ vpops(start_register, POPCOUNT(fpu_spill_mask_));
+  }
+  __ PopList(core_spill_mask_);
 }
 
 void CodeGeneratorARM::Bind(HBasicBlock* block) {
@@ -576,11 +597,17 @@
       gp_index_ += 2;
       stack_index_ += 2;
       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
-        ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair(
-            calling_convention.GetRegisterPairAt(index));
-        return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
-      } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
-        return Location::QuickParameter(index, stack_index);
+        if (calling_convention.GetRegisterAt(index) == R1) {
+          // Skip R1, and use R2_R3 instead.
+          gp_index_++;
+          index++;
+        }
+      }
+      if (index + 1 < calling_convention.GetNumberOfRegisters()) {
+        DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1,
+                  calling_convention.GetRegisterAt(index + 1));
+        return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index),
+                                              calling_convention.GetRegisterAt(index + 1));
       } else {
         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
       }
@@ -605,9 +632,11 @@
       if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
         uint32_t index = double_index_;
         double_index_ += 2;
-        return Location::FpuRegisterPairLocation(
+        Location result = Location::FpuRegisterPairLocation(
           calling_convention.GetFpuRegisterAt(index),
           calling_convention.GetFpuRegisterAt(index + 1));
+        DCHECK(ExpectedPairLayout(result));
+        return result;
       } else {
         return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
       }
@@ -697,27 +726,11 @@
           Location::RegisterLocation(destination.AsRegisterPairLow<Register>()));
     } else if (source.IsFpuRegister()) {
       UNIMPLEMENTED(FATAL);
-    } else if (source.IsQuickParameter()) {
-      uint16_t register_index = source.GetQuickParameterRegisterIndex();
-      uint16_t stack_index = source.GetQuickParameterStackIndex();
-      InvokeDexCallingConvention calling_convention;
-      EmitParallelMoves(
-          Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
-          Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
-          Location::StackSlot(
-              calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()),
-          Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()));
     } else {
-      // No conflict possible, so just do the moves.
       DCHECK(source.IsDoubleStackSlot());
-      if (destination.AsRegisterPairLow<Register>() == R1) {
-        DCHECK_EQ(destination.AsRegisterPairHigh<Register>(), R2);
-        __ LoadFromOffset(kLoadWord, R1, SP, source.GetStackIndex());
-        __ LoadFromOffset(kLoadWord, R2, SP, source.GetHighStackIndex(kArmWordSize));
-      } else {
-        __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
-                          SP, source.GetStackIndex());
-      }
+      DCHECK(ExpectedPairLayout(destination));
+      __ LoadFromOffset(kLoadWordPair, destination.AsRegisterPairLow<Register>(),
+                        SP, source.GetStackIndex());
     }
   } else if (destination.IsFpuRegisterPair()) {
     if (source.IsDoubleStackSlot()) {
@@ -727,22 +740,6 @@
     } else {
       UNIMPLEMENTED(FATAL);
     }
-  } else if (destination.IsQuickParameter()) {
-    InvokeDexCallingConvention calling_convention;
-    uint16_t register_index = destination.GetQuickParameterRegisterIndex();
-    uint16_t stack_index = destination.GetQuickParameterStackIndex();
-    if (source.IsRegisterPair()) {
-      UNIMPLEMENTED(FATAL);
-    } else if (source.IsFpuRegister()) {
-      UNIMPLEMENTED(FATAL);
-    } else {
-      DCHECK(source.IsDoubleStackSlot());
-      EmitParallelMoves(
-          Location::StackSlot(source.GetStackIndex()),
-          Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
-          Location::StackSlot(source.GetHighStackIndex(kArmWordSize)),
-          Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index + 1)));
-    }
   } else {
     DCHECK(destination.IsDoubleStackSlot());
     if (source.IsRegisterPair()) {
@@ -755,17 +752,6 @@
         __ StoreToOffset(kStoreWordPair, source.AsRegisterPairLow<Register>(),
                          SP, destination.GetStackIndex());
       }
-    } else if (source.IsQuickParameter()) {
-      InvokeDexCallingConvention calling_convention;
-      uint16_t register_index = source.GetQuickParameterRegisterIndex();
-      uint16_t stack_index = source.GetQuickParameterStackIndex();
-      // Just move the low part. The only time a source is a quick parameter is
-      // when moving the parameter to its stack locations. And the (Java) caller
-      // of this method has already done that.
-      __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(register_index),
-                       SP, destination.GetStackIndex());
-      DCHECK_EQ(calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize(),
-                static_cast<size_t>(destination.GetHighStackIndex(kArmWordSize)));
     } else if (source.IsFpuRegisterPair()) {
       __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
                         SP,
@@ -798,7 +784,8 @@
         __ LoadImmediate(IP, value);
         __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
       }
-    } else if (const_to_move->IsLongConstant()) {
+    } else {
+      DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName();
       int64_t value = const_to_move->AsLongConstant()->GetValue();
       if (location.IsRegisterPair()) {
         __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value));
@@ -874,6 +861,7 @@
       || instruction->IsBoundsCheck()
       || instruction->IsNullCheck()
       || instruction->IsDivZeroCheck()
+      || instruction->GetLocations()->CanCall()
       || !IsLeafMethod());
 }
 
@@ -949,6 +937,7 @@
       // Condition has not been materialized, use its inputs as the
       // comparison and its condition as the branch condition.
       LocationSummary* locations = cond->GetLocations();
+      DCHECK(locations->InAt(0).IsRegister()) << locations->InAt(0);
       Register left = locations->InAt(0).AsRegister<Register>();
       if (locations->InAt(1).IsRegister()) {
         __ cmp(left, ShifterOperand(locations->InAt(1).AsRegister<Register>()));
@@ -1169,41 +1158,38 @@
   codegen_->GenerateFrameExit();
 }
 
-void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+  IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
+                                         codegen_->GetInstructionSetFeatures());
+  if (intrinsic.TryDispatch(invoke)) {
+    return;
+  }
+
   HandleInvoke(invoke);
 }
 
 void CodeGeneratorARM::LoadCurrentMethod(Register reg) {
+  DCHECK(RequiresCurrentMethod());
   __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
 }
 
-void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) {
+  if (invoke->GetLocations()->Intrinsified()) {
+    IntrinsicCodeGeneratorARM intrinsic(codegen);
+    intrinsic.Dispatch(invoke);
+    return true;
+  }
+  return false;
+}
+
+void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
+
   Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
 
-  // TODO: Implement all kinds of calls:
-  // 1) boot -> boot
-  // 2) app -> boot
-  // 3) app -> app
-  //
-  // Currently we implement the app -> app logic, which looks up in the resolve cache.
-
-  // temp = method;
-  codegen_->LoadCurrentMethod(temp);
-  // temp = temp->dex_cache_resolved_methods_;
-  __ LoadFromOffset(
-      kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
-  // temp = temp[index_in_cache]
-  __ LoadFromOffset(
-      kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()));
-  // LR = temp[offset_of_quick_compiled_code]
-  __ LoadFromOffset(kLoadWord, LR, temp,
-                     mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-                         kArmWordSize).Int32Value());
-  // LR()
-  __ blx(LR);
-
-  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
-  DCHECK(!codegen_->IsLeafMethod());
+  codegen_->GenerateStaticOrDirectCall(invoke, temp);
 }
 
 void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
@@ -1221,10 +1207,20 @@
 }
 
 void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(),
+                                         codegen_->GetInstructionSetFeatures());
+  if (intrinsic.TryDispatch(invoke)) {
+    return;
+  }
+
   HandleInvoke(invoke);
 }
 
 void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
+
   Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
   uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
           invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
@@ -1238,6 +1234,7 @@
   } else {
     __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
       kArmWordSize).Int32Value();
@@ -1276,6 +1273,7 @@
   } else {
     __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetImtEntryAt(method_offset);
   uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
       kArmWordSize).Int32Value();
@@ -1292,11 +1290,14 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
   switch (neg->GetResultType()) {
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong: {
-      bool output_overlaps = (neg->GetResultType() == Primitive::kPrimLong);
+    case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister(), output_overlaps);
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
       break;
     }
 
@@ -1359,11 +1360,20 @@
 }
 
 void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
   Primitive::Type result_type = conversion->GetResultType();
   Primitive::Type input_type = conversion->GetInputType();
   DCHECK_NE(result_type, input_type);
+
+  // The float-to-long and double-to-long type conversions rely on a
+  // call to the runtime.
+  LocationSummary::CallKind call_kind =
+      ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+       && result_type == Primitive::kPrimLong)
+      ? LocationSummary::kCall
+      : LocationSummary::kNoCall;
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+
   switch (result_type) {
     case Primitive::kPrimByte:
       switch (input_type) {
@@ -1413,8 +1423,10 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-int' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
           break;
 
         default:
@@ -1434,11 +1446,24 @@
           locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
           break;
 
-        case Primitive::kPrimFloat:
-        case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type << " to "
-                     << result_type << " not yet implemented";
+        case Primitive::kPrimFloat: {
+          // Processing a Dex `float-to-long' instruction.
+          InvokeRuntimeCallingConvention calling_convention;
+          locations->SetInAt(0, Location::FpuRegisterLocation(
+              calling_convention.GetFpuRegisterAt(0)));
+          locations->SetOut(Location::RegisterPairLocation(R0, R1));
           break;
+        }
+
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-long' instruction.
+          InvokeRuntimeCallingConvention calling_convention;
+          locations->SetInAt(0, Location::FpuRegisterPairLocation(
+              calling_convention.GetFpuRegisterAt(0),
+              calling_convention.GetFpuRegisterAt(1)));
+          locations->SetOut(Location::RegisterPairLocation(R0, R1));
+          break;
+        }
 
         default:
           LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1484,8 +1509,9 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-float' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
           break;
 
         default:
@@ -1515,8 +1541,9 @@
           break;
 
         case Primitive::kPrimFloat:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `float-to-double' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
           break;
 
         default:
@@ -1595,10 +1622,15 @@
           break;
         }
 
-        case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-int' instruction.
+          SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
+          DRegister temp_d = FromLowSToD(temp_s);
+          __ vmovd(temp_d, FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
+          __ vcvtid(temp_s, temp_d);
+          __ vmovrs(out.AsRegister<Register>(), temp_s);
           break;
+        }
 
         default:
           LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1623,9 +1655,17 @@
           break;
 
         case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-long' instruction.
+          codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pF2l),
+                                  conversion,
+                                  conversion->GetDexPc());
+          break;
+
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type << " to "
-                     << result_type << " not yet implemented";
+          // Processing a Dex `double-to-long' instruction.
+          codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pD2l),
+                                  conversion,
+                                  conversion->GetDexPc());
           break;
 
         default:
@@ -1704,8 +1744,9 @@
         }
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-float' instruction.
+          __ vcvtsd(out.AsFpuRegister<SRegister>(),
+                    FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
           break;
 
         default:
@@ -1760,8 +1801,9 @@
         }
 
         case Primitive::kPrimFloat:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `float-to-double' instruction.
+          __ vcvtds(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+                    in.AsFpuRegister<SRegister>());
           break;
 
         default:
@@ -1780,12 +1822,17 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
   switch (add->GetResultType()) {
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong: {
-      bool output_overlaps = (add->GetResultType() == Primitive::kPrimLong);
+    case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister(), output_overlaps);
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
     }
 
@@ -1820,7 +1867,8 @@
       }
       break;
 
-    case Primitive::kPrimLong:
+    case Primitive::kPrimLong: {
+      DCHECK(second.IsRegisterPair());
       __ adds(out.AsRegisterPairLow<Register>(),
               first.AsRegisterPairLow<Register>(),
               ShifterOperand(second.AsRegisterPairLow<Register>()));
@@ -1828,6 +1876,7 @@
              first.AsRegisterPairHigh<Register>(),
              ShifterOperand(second.AsRegisterPairHigh<Register>()));
       break;
+    }
 
     case Primitive::kPrimFloat:
       __ vadds(out.AsFpuRegister<SRegister>(),
@@ -1850,12 +1899,17 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
   switch (sub->GetResultType()) {
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong: {
-      bool output_overlaps = (sub->GetResultType() == Primitive::kPrimLong);
+    case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister(), output_overlaps);
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
     }
     case Primitive::kPrimFloat:
@@ -1890,6 +1944,7 @@
     }
 
     case Primitive::kPrimLong: {
+      DCHECK(second.IsRegisterPair());
       __ subs(out.AsRegisterPairLow<Register>(),
               first.AsRegisterPairLow<Register>(),
               ShifterOperand(second.AsRegisterPairLow<Register>()));
@@ -2025,8 +2080,7 @@
           calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
       locations->SetInAt(1, Location::RegisterPairLocation(
           calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
-      // The runtime helper puts the output in R0,R2.
-      locations->SetOut(Location::RegisterPairLocation(R0, R2));
+      locations->SetOut(Location::RegisterPairLocation(R0, R1));
       break;
     }
     case Primitive::kPrimFloat:
@@ -2063,7 +2117,7 @@
       DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
       DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
       DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
-      DCHECK_EQ(R2, out.AsRegisterPairHigh<Register>());
+      DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
 
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv), div, div->GetDexPc());
       break;
@@ -2089,12 +2143,13 @@
 }
 
 void LocationsBuilderARM::VisitRem(HRem* rem) {
-  LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong
-      ? LocationSummary::kCall
-      : LocationSummary::kNoCall;
+  Primitive::Type type = rem->GetResultType();
+  LocationSummary::CallKind call_kind = type == Primitive::kPrimInt
+      ? LocationSummary::kNoCall
+      : LocationSummary::kCall;
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
 
-  switch (rem->GetResultType()) {
+  switch (type) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RequiresRegister());
@@ -2112,14 +2167,26 @@
       locations->SetOut(Location::RegisterPairLocation(R2, R3));
       break;
     }
-    case Primitive::kPrimFloat:
+    case Primitive::kPrimFloat: {
+      InvokeRuntimeCallingConvention calling_convention;
+      locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+      locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+      locations->SetOut(Location::FpuRegisterLocation(S0));
+      break;
+    }
+
     case Primitive::kPrimDouble: {
-      LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+      InvokeRuntimeCallingConvention calling_convention;
+      locations->SetInAt(0, Location::FpuRegisterPairLocation(
+          calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
+      locations->SetInAt(1, Location::FpuRegisterPairLocation(
+          calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
+      locations->SetOut(Location::Location::FpuRegisterPairLocation(S0, S1));
       break;
     }
 
     default:
-      LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+      LOG(FATAL) << "Unexpected rem type " << type;
   }
 }
 
@@ -2129,7 +2196,8 @@
   Location first = locations->InAt(0);
   Location second = locations->InAt(1);
 
-  switch (rem->GetResultType()) {
+  Primitive::Type type = rem->GetResultType();
+  switch (type) {
     case Primitive::kPrimInt: {
       Register reg1 = first.AsRegister<Register>();
       Register reg2 = second.AsRegister<Register>();
@@ -2145,26 +2213,22 @@
     }
 
     case Primitive::kPrimLong: {
-      InvokeRuntimeCallingConvention calling_convention;
-      DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
-      DCHECK_EQ(R2, out.AsRegisterPairLow<Register>());
-      DCHECK_EQ(R3, out.AsRegisterPairHigh<Register>());
-
       codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc());
       break;
     }
 
-    case Primitive::kPrimFloat:
+    case Primitive::kPrimFloat: {
+      codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmodf), rem, rem->GetDexPc());
+      break;
+    }
+
     case Primitive::kPrimDouble: {
-      LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+      codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pFmod), rem, rem->GetDexPc());
       break;
     }
 
     default:
-      LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+      LOG(FATAL) << "Unexpected rem type " << type;
   }
 }
 
@@ -2228,7 +2292,7 @@
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RegisterOrConstant(op->InputAt(1)));
-      locations->SetOut(Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
     }
     case Primitive::kPrimLong: {
@@ -2236,8 +2300,8 @@
       locations->SetInAt(0, Location::RegisterPairLocation(
           calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
       locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-      // The runtime helper puts the output in R0,R2.
-      locations->SetOut(Location::RegisterPairLocation(R0, R2));
+      // The runtime helper puts the output in R0,R1.
+      locations->SetOut(Location::RegisterPairLocation(R0, R1));
       break;
     }
     default:
@@ -2291,7 +2355,7 @@
       DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
       DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegister<Register>());
       DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
-      DCHECK_EQ(R2, out.AsRegisterPairHigh<Register>());
+      DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
 
       int32_t entry_point_offset;
       if (op->IsShl()) {
@@ -2347,8 +2411,9 @@
   InvokeRuntimeCallingConvention calling_convention;
   codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
-  codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+  codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+                          instruction,
+                          instruction->GetDexPc());
 }
 
 void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
@@ -2356,17 +2421,18 @@
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
   locations->SetOut(Location::RegisterLocation(R0));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
   __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
-  codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+  codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+                          instruction,
+                          instruction->GetDexPc());
 }
 
 void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
@@ -2398,10 +2464,6 @@
   Location out = locations->Out();
   Location in = locations->InAt(0);
   switch (not_->InputAt(0)->GetType()) {
-    case Primitive::kPrimBoolean:
-      __ eor(out.AsRegister<Register>(), in.AsRegister<Register>(), ShifterOperand(1));
-      break;
-
     case Primitive::kPrimInt:
       __ mvn(out.AsRegister<Register>(), ShifterOperand(in.AsRegister<Register>()));
       break;
@@ -2425,7 +2487,8 @@
     case Primitive::kPrimLong: {
       locations->SetInAt(0, Location::RequiresRegister());
       locations->SetInAt(1, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+      // Output overlaps because it is written before doing the low comparison.
+      locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
       break;
     }
     case Primitive::kPrimFloat:
@@ -2503,68 +2566,172 @@
   LOG(FATAL) << "Unreachable";
 }
 
-void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
+  // TODO (ported from quick): revisit Arm barrier kinds
+  DmbOptions flavour = DmbOptions::ISH;  // quiet c++ warnings
+  switch (kind) {
+    case MemBarrierKind::kAnyStore:
+    case MemBarrierKind::kLoadAny:
+    case MemBarrierKind::kAnyAny: {
+      flavour = DmbOptions::ISH;
+      break;
+    }
+    case MemBarrierKind::kStoreStore: {
+      flavour = DmbOptions::ISHST;
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected memory barrier " << kind;
+  }
+  __ dmb(flavour);
+}
+
+void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr,
+                                                         uint32_t offset,
+                                                         Register out_lo,
+                                                         Register out_hi) {
+  if (offset != 0) {
+    __ LoadImmediate(out_lo, offset);
+    __ add(IP, addr, ShifterOperand(out_lo));
+    addr = IP;
+  }
+  __ ldrexd(out_lo, out_hi, addr);
+}
+
+void InstructionCodeGeneratorARM::GenerateWideAtomicStore(Register addr,
+                                                          uint32_t offset,
+                                                          Register value_lo,
+                                                          Register value_hi,
+                                                          Register temp1,
+                                                          Register temp2,
+                                                          HInstruction* instruction) {
+  Label fail;
+  if (offset != 0) {
+    __ LoadImmediate(temp1, offset);
+    __ add(IP, addr, ShifterOperand(temp1));
+    addr = IP;
+  }
+  __ Bind(&fail);
+  // We need a load followed by store. (The address used in a STREX instruction must
+  // be the same as the address in the most recently executed LDREX instruction.)
+  __ ldrexd(temp1, temp2, addr);
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
+  __ strexd(temp1, value_lo, value_hi, addr);
+  __ cmp(temp1, ShifterOperand(0));
+  __ b(&fail, NE);
+}
+
+void LocationsBuilderARM::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue());
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
+
+
+  Primitive::Type field_type = field_info.GetFieldType();
+  bool is_wide = field_type == Primitive::kPrimLong || field_type == Primitive::kPrimDouble;
+  bool generate_volatile = field_info.IsVolatile()
+      && is_wide
+      && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
   // Temporary registers for the write barrier.
-  if (needs_write_barrier) {
+  // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
     locations->AddTemp(Location::RequiresRegister());
     locations->AddTemp(Location::RequiresRegister());
+  } else if (generate_volatile) {
+    // Arm encoding have some additional constraints for ldrexd/strexd:
+    // - registers need to be consecutive
+    // - the first register should be even but not R14.
+    // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever
+    // enable Arm encoding.
+    DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
+
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+    if (field_type == Primitive::kPrimDouble) {
+      // For doubles we need two more registers to copy the value.
+      locations->AddTemp(Location::RegisterLocation(R2));
+      locations->AddTemp(Location::RegisterLocation(R3));
+    }
   }
 }
 
-void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction,
+                                                 const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
   LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->GetFieldType();
+  Register base = locations->InAt(0).AsRegister<Register>();
+  Location value = locations->InAt(1);
+
+  bool is_volatile = field_info.IsVolatile();
+  bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
+  Primitive::Type field_type = field_info.GetFieldType();
+  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+  }
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ StoreToOffset(kStoreByte, value, obj, offset);
+      __ StoreToOffset(kStoreByte, value.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ StoreToOffset(kStoreHalfword, value, obj, offset);
+      __ StoreToOffset(kStoreHalfword, value.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ StoreToOffset(kStoreWord, value, obj, offset);
-      if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-        Register card = locations->GetTemp(1).AsRegister<Register>();
-        codegen_->MarkGCCard(temp, card, obj, value);
-      }
+      __ StoreToOffset(kStoreWord, value.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimLong: {
-      Location value = locations->InAt(1);
-      __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), obj, offset);
+      if (is_volatile && !atomic_ldrd_strd) {
+        GenerateWideAtomicStore(base, offset,
+                                value.AsRegisterPairLow<Register>(),
+                                value.AsRegisterPairHigh<Register>(),
+                                locations->GetTemp(0).AsRegister<Register>(),
+                                locations->GetTemp(1).AsRegister<Register>(),
+                                instruction);
+      } else {
+        __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), base, offset);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+      }
       break;
     }
 
     case Primitive::kPrimFloat: {
-      SRegister value = locations->InAt(1).AsFpuRegister<SRegister>();
-      __ StoreSToOffset(value, obj, offset);
+      __ StoreSToOffset(value.AsFpuRegister<SRegister>(), base, offset);
       break;
     }
 
     case Primitive::kPrimDouble: {
-      DRegister value = FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>());
-      __ StoreDToOffset(value, obj, offset);
+      DRegister value_reg = FromLowSToD(value.AsFpuRegisterPairLow<SRegister>());
+      if (is_volatile && !atomic_ldrd_strd) {
+        Register value_reg_lo = locations->GetTemp(0).AsRegister<Register>();
+        Register value_reg_hi = locations->GetTemp(1).AsRegister<Register>();
+
+        __ vmovrrd(value_reg_lo, value_reg_hi, value_reg);
+
+        GenerateWideAtomicStore(base, offset,
+                                value_reg_lo,
+                                value_reg_hi,
+                                locations->GetTemp(2).AsRegister<Register>(),
+                                locations->GetTemp(3).AsRegister<Register>(),
+                                instruction);
+      } else {
+        __ StoreDToOffset(value_reg, base, offset);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+      }
       break;
     }
 
@@ -2572,75 +2739,162 @@
       LOG(FATAL) << "Unreachable type " << field_type;
       UNREACHABLE();
   }
+
+  // Longs and doubles are handled in the switch.
+  if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
+
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+    Register temp = locations->GetTemp(0).AsRegister<Register>();
+    Register card = locations->GetTemp(1).AsRegister<Register>();
+    codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>());
+  }
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+  }
 }
 
-void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+void LocationsBuilderARM::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+  bool volatile_for_double = field_info.IsVolatile()
+      && (field_info.GetFieldType() == Primitive::kPrimDouble)
+      && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
+  bool overlap = field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong);
+  locations->SetOut(Location::RequiresRegister(),
+                    (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
+  if (volatile_for_double) {
+    // Arm encoding have some additional constraints for ldrexd/strexd:
+    // - registers need to be consecutive
+    // - the first register should be even but not R14.
+    // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever
+    // enable Arm encoding.
+    DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
 }
 
-void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+void InstructionCodeGeneratorARM::HandleFieldGet(HInstruction* instruction,
+                                                 const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
 
-  switch (instruction->GetType()) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register base = locations->InAt(0).AsRegister<Register>();
+  Location out = locations->Out();
+  bool is_volatile = field_info.IsVolatile();
+  bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
+  Primitive::Type field_type = field_info.GetFieldType();
+  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+  switch (field_type) {
     case Primitive::kPrimBoolean: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset);
+      __ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimByte: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadSignedByte, out, obj, offset);
+      __ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimShort: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset);
+      __ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimChar: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset);
+      __ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadWord, out, obj, offset);
+      __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
       break;
     }
 
     case Primitive::kPrimLong: {
-      // TODO: support volatile.
-      Location out = locations->Out();
-      __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset);
+      if (is_volatile && !atomic_ldrd_strd) {
+        GenerateWideAtomicLoad(base, offset,
+                               out.AsRegisterPairLow<Register>(),
+                               out.AsRegisterPairHigh<Register>());
+      } else {
+        __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset);
+      }
       break;
     }
 
     case Primitive::kPrimFloat: {
-      SRegister out = locations->Out().AsFpuRegister<SRegister>();
-      __ LoadSFromOffset(out, obj, offset);
+      __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset);
       break;
     }
 
     case Primitive::kPrimDouble: {
-      DRegister out = FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>());
-      __ LoadDFromOffset(out, obj, offset);
+      DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>());
+      if (is_volatile && !atomic_ldrd_strd) {
+        Register lo = locations->GetTemp(0).AsRegister<Register>();
+        Register hi = locations->GetTemp(1).AsRegister<Register>();
+        GenerateWideAtomicLoad(base, offset, lo, hi);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ vmovdrr(out_reg, lo, hi);
+      } else {
+        __ LoadDFromOffset(out_reg, base, offset);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+      }
       break;
     }
 
     case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      LOG(FATAL) << "Unreachable type " << field_type;
       UNREACHABLE();
   }
+
+  // Doubles are handled in the switch.
+  if (field_type != Primitive::kPrimDouble) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+  }
+}
+
+void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
 }
 
 void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
@@ -2652,20 +2906,32 @@
   }
 }
 
-void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
+  if (codegen_->CanMoveNullCheckToUser(instruction)) {
+    return;
+  }
+  Location obj = instruction->GetLocations()->InAt(0);
+
+  __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0);
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
   SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
   Location obj = locations->InAt(0);
 
-  if (obj.IsRegister()) {
-    __ cmp(obj.AsRegister<Register>(), ShifterOperand(0));
-    __ b(slow_path->GetEntryLabel(), EQ);
+  __ cmp(obj.AsRegister<Register>(), ShifterOperand(0));
+  __ b(slow_path->GetEntryLabel(), EQ);
+}
+
+void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
+  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+    GenerateImplicitNullCheck(instruction);
   } else {
-    DCHECK(obj.IsConstant()) << obj;
-    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
-    __ b(slow_path->GetEntryLabel());
+    GenerateExplicitNullCheck(instruction);
   }
 }
 
@@ -2769,14 +3035,39 @@
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-      UNREACHABLE();
+    case Primitive::kPrimFloat: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+      Location out = locations->Out();
+      DCHECK(out.IsFpuRegister());
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+        __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+      Location out = locations->Out();
+      DCHECK(out.IsFpuRegisterPair());
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
+        __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
+      }
+      break;
+    }
+
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
       UNREACHABLE();
   }
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
@@ -2860,6 +3151,7 @@
           __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
           __ StoreToOffset(kStoreWord, value, IP, data_offset);
         }
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
         if (needs_write_barrier) {
           DCHECK_EQ(value_type, Primitive::kPrimNot);
           Register temp = locations->GetTemp(0).AsRegister<Register>();
@@ -2889,14 +3181,44 @@
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-      UNREACHABLE();
+    case Primitive::kPrimFloat: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+      Location value = locations->InAt(2);
+      DCHECK(value.IsFpuRegister());
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ StoreSToOffset(value.AsFpuRegister<SRegister>(), obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+        __ StoreSToOffset(value.AsFpuRegister<SRegister>(), IP, data_offset);
+      }
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+      Location value = locations->InAt(2);
+      DCHECK(value.IsFpuRegisterPair());
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), obj, offset);
+      } else {
+        __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
+        __ StoreDToOffset(FromLowSToD(value.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
+      }
+
+      break;
+    }
+
     case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      LOG(FATAL) << "Unreachable type " << value_type;
       UNREACHABLE();
   }
+
+  // Ints and objects are handled in the switch.
+  if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
 }
 
 void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
@@ -2912,6 +3234,7 @@
   Register obj = locations->InAt(0).AsRegister<Register>();
   Register out = locations->Out().AsRegister<Register>();
   __ LoadFromOffset(kLoadWord, out, obj, offset);
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -3022,21 +3345,102 @@
     if (destination.IsRegister()) {
       __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(),
                         SP, source.GetStackIndex());
+    } else if (destination.IsFpuRegister()) {
+      __ LoadSFromOffset(destination.AsFpuRegister<SRegister>(), SP, source.GetStackIndex());
     } else {
       DCHECK(destination.IsStackSlot());
       __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
       __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
     }
-  } else {
-    DCHECK(source.IsConstant());
-    DCHECK(source.GetConstant()->IsIntConstant());
-    int32_t value = source.GetConstant()->AsIntConstant()->GetValue();
-    if (destination.IsRegister()) {
-      __ LoadImmediate(destination.AsRegister<Register>(), value);
+  } else if (source.IsFpuRegister()) {
+    if (destination.IsFpuRegister()) {
+      __ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
     } else {
       DCHECK(destination.IsStackSlot());
-      __ LoadImmediate(IP, value);
+      __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
+    }
+  } else if (source.IsDoubleStackSlot()) {
+    if (destination.IsDoubleStackSlot()) {
+      __ LoadFromOffset(kLoadWord, IP, SP, source.GetStackIndex());
       __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+      __ LoadFromOffset(kLoadWord, IP, SP, source.GetHighStackIndex(kArmWordSize));
+      __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
+    } else if (destination.IsRegisterPair()) {
+      DCHECK(ExpectedPairLayout(destination));
+      __ LoadFromOffset(
+          kLoadWordPair, destination.AsRegisterPairLow<Register>(), SP, source.GetStackIndex());
+    } else {
+      DCHECK(destination.IsFpuRegisterPair()) << destination;
+      __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+                         SP,
+                         source.GetStackIndex());
+    }
+  } else if (source.IsRegisterPair()) {
+    if (destination.IsRegisterPair()) {
+      __ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
+      __ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+    } else {
+      DCHECK(destination.IsDoubleStackSlot()) << destination;
+      DCHECK(ExpectedPairLayout(source));
+      __ StoreToOffset(
+          kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex());
+    }
+  } else if (source.IsFpuRegisterPair()) {
+    if (destination.IsFpuRegisterPair()) {
+      __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+               FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
+    } else {
+      DCHECK(destination.IsDoubleStackSlot()) << destination;
+      __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
+                        SP,
+                        destination.GetStackIndex());
+    }
+  } else {
+    DCHECK(source.IsConstant()) << source;
+    HInstruction* constant = source.GetConstant();
+    if (constant->IsIntConstant()) {
+      int32_t value = constant->AsIntConstant()->GetValue();
+      if (destination.IsRegister()) {
+        __ LoadImmediate(destination.AsRegister<Register>(), value);
+      } else {
+        DCHECK(destination.IsStackSlot());
+        __ LoadImmediate(IP, value);
+        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+      }
+    } else if (constant->IsLongConstant()) {
+      int64_t value = constant->AsLongConstant()->GetValue();
+      if (destination.IsRegisterPair()) {
+        __ LoadImmediate(destination.AsRegisterPairLow<Register>(), Low32Bits(value));
+        __ LoadImmediate(destination.AsRegisterPairHigh<Register>(), High32Bits(value));
+      } else {
+        DCHECK(destination.IsDoubleStackSlot()) << destination;
+        __ LoadImmediate(IP, Low32Bits(value));
+        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+        __ LoadImmediate(IP, High32Bits(value));
+        __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
+      }
+    } else if (constant->IsDoubleConstant()) {
+      double value = constant->AsDoubleConstant()->GetValue();
+      if (destination.IsFpuRegisterPair()) {
+        __ LoadDImmediate(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), value);
+      } else {
+        DCHECK(destination.IsDoubleStackSlot()) << destination;
+        uint64_t int_value = bit_cast<uint64_t, double>(value);
+        __ LoadImmediate(IP, Low32Bits(int_value));
+        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+        __ LoadImmediate(IP, High32Bits(int_value));
+        __ StoreToOffset(kStoreWord, IP, SP, destination.GetHighStackIndex(kArmWordSize));
+      }
+    } else {
+      DCHECK(constant->IsFloatConstant()) << constant->DebugName();
+      float value = constant->AsFloatConstant()->GetValue();
+      if (destination.IsFpuRegister()) {
+        __ LoadSImmediate(destination.AsFpuRegister<SRegister>(), value);
+      } else {
+        DCHECK(destination.IsStackSlot());
+        __ LoadImmediate(IP, bit_cast<int32_t, float>(value));
+        __ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
+      }
     }
   }
 }
@@ -3075,8 +3479,80 @@
     Exchange(destination.AsRegister<Register>(), source.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
     Exchange(source.GetStackIndex(), destination.GetStackIndex());
+  } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
+    __ vmovrs(IP, source.AsFpuRegister<SRegister>());
+    __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
+    __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
+  } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
+    __ Mov(IP, source.AsRegisterPairLow<Register>());
+    __ Mov(source.AsRegisterPairLow<Register>(), destination.AsRegisterPairLow<Register>());
+    __ Mov(destination.AsRegisterPairLow<Register>(), IP);
+    __ Mov(IP, source.AsRegisterPairHigh<Register>());
+    __ Mov(source.AsRegisterPairHigh<Register>(), destination.AsRegisterPairHigh<Register>());
+    __ Mov(destination.AsRegisterPairHigh<Register>(), IP);
+  } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
+    // TODO: Find a D register available in the parallel moves,
+    // or reserve globally a D register.
+    DRegister tmp = D0;
+    Register low_reg = source.IsRegisterPair()
+        ? source.AsRegisterPairLow<Register>()
+        : destination.AsRegisterPairLow<Register>();
+    int mem = source.IsRegisterPair()
+        ? destination.GetStackIndex()
+        : source.GetStackIndex();
+    DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
+    // Make room for the pushed DRegister.
+    mem += 8;
+    __ vpushd(tmp, 1);
+    __ vmovdrr(tmp, low_reg, static_cast<Register>(low_reg + 1));
+    __ LoadFromOffset(kLoadWordPair, low_reg, SP, mem);
+    __ StoreDToOffset(tmp, SP, mem);
+    __ vpopd(tmp, 1);
+  } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
+    // TODO: Find a D register available in the parallel moves,
+    // or reserve globally a D register.
+    DRegister tmp = D0;
+    DRegister first = FromLowSToD(source.AsFpuRegisterPairLow<SRegister>());
+    DRegister second = FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
+    while (tmp == first || tmp == second) {
+      tmp = static_cast<DRegister>(tmp + 1);
+    }
+    __ vpushd(tmp, 1);
+    __ vmovd(tmp, first);
+    __ vmovd(first, second);
+    __ vmovd(second, tmp);
+    __ vpopd(tmp, 1);
+  } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
+    DRegister reg = source.IsFpuRegisterPair()
+        ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
+        : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
+    int mem = source.IsFpuRegisterPair()
+        ? destination.GetStackIndex()
+        : source.GetStackIndex();
+    // TODO: Find or reserve a D register.
+    DRegister tmp = reg == D0 ? D1 : D0;
+    // Make room for the pushed DRegister.
+    mem += 8;
+    __ vpushd(tmp, 1);
+    __ vmovd(tmp, reg);
+    __ LoadDFromOffset(reg, SP, mem);
+    __ StoreDToOffset(tmp, SP, mem);
+    __ vpopd(tmp, 1);
+  } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
+    SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
+                                           : destination.AsFpuRegister<SRegister>();
+    int mem = source.IsFpuRegister()
+        ? destination.GetStackIndex()
+        : source.GetStackIndex();
+
+    __ vmovrs(IP, reg);
+    __ LoadSFromOffset(reg, SP, mem);
+    __ StoreToOffset(kStoreWord, IP, SP, mem);
+  } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
+    Exchange(source.GetStackIndex(), destination.GetStackIndex());
+    Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
   } else {
-    LOG(FATAL) << "Unimplemented";
+    LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
   }
 }
 
@@ -3153,146 +3629,6 @@
   __ Bind(slow_path->GetExitLabel());
 }
 
-void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register cls = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-
-  switch (instruction->GetType()) {
-    case Primitive::kPrimBoolean: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadUnsignedByte, out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimByte: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadSignedByte, out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimShort: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadSignedHalfword, out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimChar: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadUnsignedHalfword, out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ LoadFromOffset(kLoadWord, out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      // TODO: support volatile.
-      Location out = locations->Out();
-      __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      SRegister out = locations->Out().AsFpuRegister<SRegister>();
-      __ LoadSFromOffset(out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      DRegister out = FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>());
-      __ LoadDFromOffset(out, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue());
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  // Temporary registers for the write barrier.
-  if (needs_write_barrier) {
-    locations->AddTemp(Location::RequiresRegister());
-    locations->AddTemp(Location::RequiresRegister());
-  }
-}
-
-void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register cls = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->GetFieldType();
-
-  switch (field_type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ StoreToOffset(kStoreByte, value, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ StoreToOffset(kStoreHalfword, value, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ StoreToOffset(kStoreWord, value, cls, offset);
-      if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-        Register card = locations->GetTemp(1).AsRegister<Register>();
-        codegen_->MarkGCCard(temp, card, cls, value);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      Location value = locations->InAt(1);
-      __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      SRegister value = locations->InAt(1).AsFpuRegister<SRegister>();
-      __ StoreSToOffset(value, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      DRegister value = FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>());
-      __ StoreDToOffset(value, cls, offset);
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << field_type;
-      UNREACHABLE();
-  }
-}
-
 void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
@@ -3346,7 +3682,8 @@
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister());
+  // The out register is used as a temporary, so it overlaps with the inputs.
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) {
@@ -3442,8 +3779,7 @@
          || instruction->GetResultType() == Primitive::kPrimLong);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  bool output_overlaps = (instruction->GetResultType() == Primitive::kPrimLong);
-  locations->SetOut(Location::RequiresRegister(), output_overlaps);
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM::VisitAnd(HAnd* instruction) {
@@ -3504,5 +3840,38 @@
   }
 }
 
+void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
+  DCHECK_EQ(temp, kArtMethodRegister);
+
+  // TODO: Implement all kinds of calls:
+  // 1) boot -> boot
+  // 2) app -> boot
+  // 3) app -> app
+  //
+  // Currently we implement the app -> app logic, which looks up in the resolve cache.
+
+  // temp = method;
+  LoadCurrentMethod(temp);
+  if (!invoke->IsRecursive()) {
+    // temp = temp->dex_cache_resolved_methods_;
+    __ LoadFromOffset(
+        kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+    // temp = temp[index_in_cache]
+    __ LoadFromOffset(
+        kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()));
+    // LR = temp[offset_of_quick_compiled_code]
+    __ LoadFromOffset(kLoadWord, LR, temp,
+                      mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+                          kArmWordSize).Int32Value());
+    // LR()
+    __ blx(LR);
+  } else {
+    __ bl(GetFrameEntryLabel());
+  }
+
+  RecordPcInfo(invoke, invoke->GetDexPc());
+  DCHECK(!IsLeafMethod());
+}
+
 }  // namespace arm
 }  // namespace art
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 226e635..47d81ff 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -18,6 +18,8 @@
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_H_
 
 #include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "parallel_move_resolver.h"
 #include "utils/arm/assembler_thumb2.h"
@@ -32,12 +34,19 @@
 static constexpr size_t kArmWordSize = kArmPointerSize;
 
 static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 };
-static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 };
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
 static constexpr SRegister kParameterFpuRegisters[] =
     { S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 };
 static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters);
 
+static constexpr Register kArtMethodRegister = R0;
+
+static constexpr DRegister FromLowSToD(SRegister reg) {
+  return DCHECK_CONSTEXPR(reg % 2 == 0, , D0)
+      static_cast<DRegister>(reg / 2);
+}
+
+
 class InvokeDexCallingConvention : public CallingConvention<Register, SRegister> {
  public:
   InvokeDexCallingConvention()
@@ -46,11 +55,6 @@
                           kParameterFpuRegisters,
                           kParameterFpuRegistersLength) {}
 
-  RegisterPair GetRegisterPairAt(size_t argument_index) {
-    DCHECK_LT(argument_index + 1, GetNumberOfRegisters());
-    return kParameterCorePairRegisters[argument_index];
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
 };
@@ -94,6 +98,20 @@
   DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARM);
 };
 
+class SlowPathCodeARM : public SlowPathCode {
+ public:
+  SlowPathCodeARM() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM);
+};
+
 class LocationsBuilderARM : public HGraphVisitor {
  public:
   LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
@@ -110,6 +128,8 @@
   void HandleInvoke(HInvoke* invoke);
   void HandleBitwiseOperation(HBinaryOperation* operation);
   void HandleShift(HBinaryOperation* operation);
+  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
 
   CodeGeneratorARM* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -138,6 +158,17 @@
   void GenerateClassInitializationCheck(SlowPathCodeARM* slow_path, Register class_reg);
   void HandleBitwiseOperation(HBinaryOperation* operation);
   void HandleShift(HBinaryOperation* operation);
+  void GenerateMemoryBarrier(MemBarrierKind kind);
+  void GenerateWideAtomicStore(Register addr, uint32_t offset,
+                               Register value_lo, Register value_hi,
+                               Register temp1, Register temp2,
+                               HInstruction* instruction);
+  void GenerateWideAtomicLoad(Register addr, uint32_t offset,
+                              Register out_lo, Register out_hi);
+  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+  void GenerateImplicitNullCheck(HNullCheck* instruction);
+  void GenerateExplicitNullCheck(HNullCheck* instruction);
 
   ArmAssembler* const assembler_;
   CodeGeneratorARM* const codegen_;
@@ -147,7 +178,9 @@
 
 class CodeGeneratorARM : public CodeGenerator {
  public:
-  explicit CodeGeneratorARM(HGraph* graph);
+  CodeGeneratorARM(HGraph* graph,
+                   const ArmInstructionSetFeatures& isa_features,
+                   const CompilerOptions& compiler_options);
   virtual ~CodeGeneratorARM() {}
 
   void GenerateFrameEntry() OVERRIDE;
@@ -156,12 +189,17 @@
   void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
 
   size_t GetWordSize() const OVERRIDE {
     return kArmWordSize;
   }
 
-  size_t FrameEntrySpillSize() const OVERRIDE;
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+    // Allocated in S registers, which are word sized.
+    return kArmWordSize;
+  }
 
   HGraphVisitor* GetLocationBuilder() OVERRIDE {
     return &location_builder_;
@@ -179,7 +217,7 @@
     return GetLabelOf(block)->Position();
   }
 
-  void SetupBlockedRegisters() const OVERRIDE;
+  void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
 
   Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
 
@@ -221,13 +259,29 @@
     block_labels_.SetSize(GetGraph()->GetBlocks().Size());
   }
 
+  const ArmInstructionSetFeatures& GetInstructionSetFeatures() const {
+    return isa_features_;
+  }
+
+  bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+    return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
+  }
+
+  void ComputeSpillMask() OVERRIDE;
+
+  Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
+
  private:
   // Labels for each block that will be compiled.
   GrowableArray<Label> block_labels_;
+  Label frame_entry_label_;
   LocationsBuilderARM location_builder_;
   InstructionCodeGeneratorARM instruction_visitor_;
   ParallelMoveResolverARM move_resolver_;
   Thumb2Assembler assembler_;
+  const ArmInstructionSetFeatures& isa_features_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
 };
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index a61ef2d..46f1a9b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -16,11 +16,16 @@
 
 #include "code_generator_arm64.h"
 
+#include "common_arm64.h"
 #include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "gc/accounting/card_table.h"
+#include "intrinsics.h"
+#include "intrinsics_arm64.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_method.h"
 #include "mirror/class.h"
+#include "offsets.h"
 #include "thread.h"
 #include "utils/arm64/assembler_arm64.h"
 #include "utils/assembler.h"
@@ -33,164 +38,34 @@
 #error "ARM64 Codegen VIXL macro-assembler macro already defined."
 #endif
 
-
 namespace art {
 
 namespace arm64 {
 
-static constexpr bool kExplicitStackOverflowCheck = false;
+using helpers::CPURegisterFrom;
+using helpers::DRegisterFrom;
+using helpers::FPRegisterFrom;
+using helpers::HeapOperand;
+using helpers::HeapOperandFrom;
+using helpers::InputCPURegisterAt;
+using helpers::InputFPRegisterAt;
+using helpers::InputRegisterAt;
+using helpers::InputOperandAt;
+using helpers::Int64ConstantFrom;
+using helpers::LocationFrom;
+using helpers::OperandFromMemOperand;
+using helpers::OutputCPURegister;
+using helpers::OutputFPRegister;
+using helpers::OutputRegister;
+using helpers::RegisterFrom;
+using helpers::StackOperandFrom;
+using helpers::VIXLRegCodeFromART;
+using helpers::WRegisterFrom;
+using helpers::XRegisterFrom;
+
 static constexpr size_t kHeapRefSize = sizeof(mirror::HeapReference<mirror::Object>);
 static constexpr int kCurrentMethodStackOffset = 0;
 
-namespace {
-
-bool IsFPType(Primitive::Type type) {
-  return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble;
-}
-
-bool IsIntegralType(Primitive::Type type) {
-  switch (type) {
-    case Primitive::kPrimByte:
-    case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-    case Primitive::kPrimInt:
-    case Primitive::kPrimLong:
-      return true;
-    default:
-      return false;
-  }
-}
-
-bool Is64BitType(Primitive::Type type) {
-  return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
-}
-
-// Convenience helpers to ease conversion to and from VIXL operands.
-static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32),
-              "Unexpected values for register codes.");
-
-int VIXLRegCodeFromART(int code) {
-  if (code == SP) {
-    return vixl::kSPRegInternalCode;
-  }
-  if (code == XZR) {
-    return vixl::kZeroRegCode;
-  }
-  return code;
-}
-
-int ARTRegCodeFromVIXL(int code) {
-  if (code == vixl::kSPRegInternalCode) {
-    return SP;
-  }
-  if (code == vixl::kZeroRegCode) {
-    return XZR;
-  }
-  return code;
-}
-
-Register XRegisterFrom(Location location) {
-  return Register::XRegFromCode(VIXLRegCodeFromART(location.reg()));
-}
-
-Register WRegisterFrom(Location location) {
-  return Register::WRegFromCode(VIXLRegCodeFromART(location.reg()));
-}
-
-Register RegisterFrom(Location location, Primitive::Type type) {
-  DCHECK(type != Primitive::kPrimVoid && !IsFPType(type));
-  return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
-}
-
-Register OutputRegister(HInstruction* instr) {
-  return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
-}
-
-Register InputRegisterAt(HInstruction* instr, int input_index) {
-  return RegisterFrom(instr->GetLocations()->InAt(input_index),
-                      instr->InputAt(input_index)->GetType());
-}
-
-FPRegister DRegisterFrom(Location location) {
-  return FPRegister::DRegFromCode(location.reg());
-}
-
-FPRegister SRegisterFrom(Location location) {
-  return FPRegister::SRegFromCode(location.reg());
-}
-
-FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
-  DCHECK(IsFPType(type));
-  return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
-}
-
-FPRegister OutputFPRegister(HInstruction* instr) {
-  return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
-}
-
-FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
-  return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
-                        instr->InputAt(input_index)->GetType());
-}
-
-CPURegister OutputCPURegister(HInstruction* instr) {
-  return IsFPType(instr->GetType()) ? static_cast<CPURegister>(OutputFPRegister(instr))
-                                    : static_cast<CPURegister>(OutputRegister(instr));
-}
-
-CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
-  return IsFPType(instr->InputAt(index)->GetType())
-      ? static_cast<CPURegister>(InputFPRegisterAt(instr, index))
-      : static_cast<CPURegister>(InputRegisterAt(instr, index));
-}
-
-int64_t Int64ConstantFrom(Location location) {
-  HConstant* instr = location.GetConstant();
-  return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
-                                : instr->AsLongConstant()->GetValue();
-}
-
-Operand OperandFrom(Location location, Primitive::Type type) {
-  if (location.IsRegister()) {
-    return Operand(RegisterFrom(location, type));
-  } else {
-    return Operand(Int64ConstantFrom(location));
-  }
-}
-
-Operand InputOperandAt(HInstruction* instr, int input_index) {
-  return OperandFrom(instr->GetLocations()->InAt(input_index),
-                     instr->InputAt(input_index)->GetType());
-}
-
-MemOperand StackOperandFrom(Location location) {
-  return MemOperand(sp, location.GetStackIndex());
-}
-
-MemOperand HeapOperand(const Register& base, size_t offset = 0) {
-  // A heap reference must be 32bit, so fit in a W register.
-  DCHECK(base.IsW());
-  return MemOperand(base.X(), offset);
-}
-
-MemOperand HeapOperand(const Register& base, Offset offset) {
-  return HeapOperand(base, offset.SizeValue());
-}
-
-MemOperand HeapOperandFrom(Location location, Offset offset) {
-  return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset);
-}
-
-Location LocationFrom(const Register& reg) {
-  return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code()));
-}
-
-Location LocationFrom(const FPRegister& fpreg) {
-  return Location::FpuRegisterLocation(fpreg.code());
-}
-
-}  // namespace
-
 inline Condition ARM64Condition(IfCondition cond) {
   switch (cond) {
     case kCondEQ: return eq;
@@ -224,8 +99,9 @@
 static const Register kRuntimeParameterCoreRegisters[] = { x0, x1, x2, x3, x4, x5, x6, x7 };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
-static const FPRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static const FPRegister kRuntimeParameterFpuRegisters[] = { d0, d1, d2, d3, d4, d5, d6, d7 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+    arraysize(kRuntimeParameterCoreRegisters);
 
 class InvokeRuntimeCallingConvention : public CallingConvention<Register, FPRegister> {
  public:
@@ -250,30 +126,35 @@
 #define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()->
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value()
 
-class SlowPathCodeARM64 : public SlowPathCode {
- public:
-  SlowPathCodeARM64() : entry_label_(), exit_label_() {}
-
-  vixl::Label* GetEntryLabel() { return &entry_label_; }
-  vixl::Label* GetExitLabel() { return &exit_label_; }
-
- private:
-  vixl::Label entry_label_;
-  vixl::Label exit_label_;
-
-  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64);
-};
-
 class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
  public:
-  BoundsCheckSlowPathARM64() {}
+  BoundsCheckSlowPathARM64(HBoundsCheck* instruction,
+                           Location index_location,
+                           Location length_location)
+      : instruction_(instruction),
+        index_location_(index_location),
+        length_location_(length_location) {}
+
 
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
     __ Bind(GetEntryLabel());
-    __ Brk(__LINE__);  // TODO: Unimplemented BoundsCheckSlowPathARM64.
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
+    InvokeRuntimeCallingConvention calling_convention;
+    codegen->EmitParallelMoves(
+        index_location_, LocationFrom(calling_convention.GetRegisterAt(0)),
+        length_location_, LocationFrom(calling_convention.GetRegisterAt(1)));
+    arm64_codegen->InvokeRuntime(
+        QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc());
+    CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
   }
 
  private:
+  HBoundsCheck* const instruction_;
+  const Location index_location_;
+  const Location length_location_;
+
   DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
 };
 
@@ -286,6 +167,7 @@
     __ Bind(GetEntryLabel());
     arm64_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc());
+    CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
   }
 
  private:
@@ -316,13 +198,18 @@
     int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage)
                                             : QUICK_ENTRY_POINT(pInitializeType);
     arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_);
+    if (do_clinit_) {
+      CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t, mirror::ArtMethod*>();
+    } else {
+      CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t, mirror::ArtMethod*>();
+    }
 
     // Move the class to the desired location.
     Location out = locations->Out();
     if (out.IsValid()) {
       DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
       Primitive::Type type = at_->GetType();
-      arm64_codegen->MoveHelper(out, calling_convention.GetReturnLocation(type), type);
+      arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
     }
 
     codegen->RestoreLiveRegisters(locations);
@@ -359,12 +246,13 @@
     codegen->SaveLiveRegisters(locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0).W());
-    __ Mov(calling_convention.GetRegisterAt(1).W(), instruction_->GetStringIndex());
+    arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1).W());
+    __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex());
     arm64_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc());
+    CheckEntrypointTypes<kQuickResolveString, void*, uint32_t, mirror::ArtMethod*>();
     Primitive::Type type = instruction_->GetType();
-    arm64_codegen->MoveHelper(locations->Out(), calling_convention.GetReturnLocation(type), type);
+    arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type);
 
     codegen->RestoreLiveRegisters(locations);
     __ B(GetExitLabel());
@@ -385,6 +273,7 @@
     __ Bind(GetEntryLabel());
     arm64_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc());
+    CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
   }
 
  private:
@@ -393,20 +282,6 @@
   DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64);
 };
 
-class StackOverflowCheckSlowPathARM64 : public SlowPathCodeARM64 {
- public:
-  StackOverflowCheckSlowPathARM64() {}
-
-  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
-    __ Bind(GetEntryLabel());
-    arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowStackOverflow), nullptr, 0);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM64);
-};
-
 class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
  public:
   explicit SuspendCheckSlowPathARM64(HSuspendCheck* instruction,
@@ -419,6 +294,7 @@
     codegen->SaveLiveRegisters(instruction_->GetLocations());
     arm64_codegen->InvokeRuntime(
         QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc());
+    CheckEntrypointTypes<kQuickTestSuspend, void, void>();
     codegen->RestoreLiveRegisters(instruction_->GetLocations());
     if (successor_ == nullptr) {
       __ B(GetReturnLabel());
@@ -445,15 +321,54 @@
 
 class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
  public:
-  TypeCheckSlowPathARM64() {}
+  TypeCheckSlowPathARM64(HInstruction* instruction,
+                         Location class_to_check,
+                         Location object_class,
+                         uint32_t dex_pc)
+      : instruction_(instruction),
+        class_to_check_(class_to_check),
+        object_class_(object_class),
+        dex_pc_(dex_pc) {}
 
   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+    LocationSummary* locations = instruction_->GetLocations();
+    DCHECK(instruction_->IsCheckCast()
+           || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+    CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+
     __ Bind(GetEntryLabel());
-    __ Brk(__LINE__);  // TODO: Unimplemented TypeCheckSlowPathARM64.
+    codegen->SaveLiveRegisters(locations);
+
+    // We're moving two locations to locations that could overlap, so we need a parallel
+    // move resolver.
+    InvokeRuntimeCallingConvention calling_convention;
+    codegen->EmitParallelMoves(
+        class_to_check_, LocationFrom(calling_convention.GetRegisterAt(0)),
+        object_class_, LocationFrom(calling_convention.GetRegisterAt(1)));
+
+    if (instruction_->IsInstanceOf()) {
+      arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial), instruction_, dex_pc_);
+      Primitive::Type ret_type = instruction_->GetType();
+      Location ret_loc = calling_convention.GetReturnLocation(ret_type);
+      arm64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
+      CheckEntrypointTypes<kQuickInstanceofNonTrivial, uint32_t,
+                           const mirror::Class*, const mirror::Class*>();
+    } else {
+      DCHECK(instruction_->IsCheckCast());
+      arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast), instruction_, dex_pc_);
+      CheckEntrypointTypes<kQuickCheckCast, void, const mirror::Class*, const mirror::Class*>();
+    }
+
+    codegen->RestoreLiveRegisters(locations);
     __ B(GetExitLabel());
   }
 
  private:
+  HInstruction* const instruction_;
+  const Location class_to_check_;
+  const Location object_class_;
+  uint32_t dex_pc_;
+
   DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64);
 };
 
@@ -465,29 +380,38 @@
     LOG(FATAL) << "Unreachable type " << type;
   }
 
-  if (IsFPType(type) && (fp_index_ < calling_convention.GetNumberOfFpuRegisters())) {
+  if (Primitive::IsFloatingPointType(type) &&
+      (fp_index_ < calling_convention.GetNumberOfFpuRegisters())) {
     next_location = LocationFrom(calling_convention.GetFpuRegisterAt(fp_index_++));
-  } else if (!IsFPType(type) && (gp_index_ < calling_convention.GetNumberOfRegisters())) {
+  } else if (!Primitive::IsFloatingPointType(type) &&
+             (gp_index_ < calling_convention.GetNumberOfRegisters())) {
     next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_++));
   } else {
     size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
-    next_location = Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
-                                      : Location::StackSlot(stack_offset);
+    next_location = Primitive::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
+                                                 : Location::StackSlot(stack_offset);
   }
 
   // Space on the stack is reserved for all arguments.
-  stack_index_ += Is64BitType(type) ? 2 : 1;
+  stack_index_ += Primitive::Is64BitType(type) ? 2 : 1;
   return next_location;
 }
 
-CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph)
+CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options)
     : CodeGenerator(graph,
                     kNumberOfAllocatableRegisters,
                     kNumberOfAllocatableFPRegisters,
-                    kNumberOfAllocatableRegisterPairs),
+                    kNumberOfAllocatableRegisterPairs,
+                    callee_saved_core_registers.list(),
+                    callee_saved_fp_registers.list(),
+                    compiler_options),
       block_labels_(nullptr),
       location_builder_(graph, this),
-      instruction_visitor_(graph, this) {}
+      instruction_visitor_(graph, this),
+      move_resolver_(graph->GetArena(), this) {
+  // Save the link register (containing the return address) to mimic Quick.
+  AddAllocatedRegister(LocationFrom(lr));
+}
 
 #undef __
 #define __ GetVIXLAssembler()->
@@ -498,45 +422,58 @@
   CodeGenerator::Finalize(allocator);
 }
 
+void ParallelMoveResolverARM64::EmitMove(size_t index) {
+  MoveOperands* move = moves_.Get(index);
+  codegen_->MoveLocation(move->GetDestination(), move->GetSource());
+}
+
+void ParallelMoveResolverARM64::EmitSwap(size_t index) {
+  MoveOperands* move = moves_.Get(index);
+  codegen_->SwapLocations(move->GetDestination(), move->GetSource());
+}
+
+void ParallelMoveResolverARM64::RestoreScratch(int reg) {
+  __ Pop(Register(VIXLRegCodeFromART(reg), kXRegSize));
+}
+
+void ParallelMoveResolverARM64::SpillScratch(int reg) {
+  __ Push(Register(VIXLRegCodeFromART(reg), kXRegSize));
+}
+
 void CodeGeneratorARM64::GenerateFrameEntry() {
+  __ Bind(&frame_entry_label_);
+
   bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kArm64) || !IsLeafMethod();
   if (do_overflow_check) {
     UseScratchRegisterScope temps(GetVIXLAssembler());
     Register temp = temps.AcquireX();
-    if (kExplicitStackOverflowCheck) {
-      SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM64();
-      AddSlowPath(slow_path);
-
-      __ Ldr(temp, MemOperand(tr, Thread::StackEndOffset<kArm64WordSize>().Int32Value()));
-      __ Cmp(sp, temp);
-      __ B(lo, slow_path->GetEntryLabel());
-    } else {
-      __ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
-      __ Ldr(wzr, MemOperand(temp, 0));
-      RecordPcInfo(nullptr, 0);
-    }
+    DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+    __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
+    __ Ldr(wzr, MemOperand(temp, 0));
+    RecordPcInfo(nullptr, 0);
   }
 
-  CPURegList preserved_regs = GetFramePreservedRegisters();
-  int frame_size = GetFrameSize();
-  core_spill_mask_ |= preserved_regs.list();
-
-  __ Str(w0, MemOperand(sp, -frame_size, PreIndex));
-  __ PokeCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes());
-
-  // Stack layout:
-  // sp[frame_size - 8]        : lr.
-  // ...                       : other preserved registers.
-  // sp[frame_size - regs_size]: first preserved register.
-  // ...                       : reserved frame space.
-  // sp[0]                     : current method.
+  if (!HasEmptyFrame()) {
+    int frame_size = GetFrameSize();
+    // Stack layout:
+    //      sp[frame_size - 8]        : lr.
+    //      ...                       : other preserved core registers.
+    //      ...                       : other preserved fp registers.
+    //      ...                       : reserved frame space.
+    //      sp[0]                     : current method.
+    __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex));
+    __ PokeCPURegList(GetFramePreservedCoreRegisters(), frame_size - GetCoreSpillSize());
+    __ PokeCPURegList(GetFramePreservedFPRegisters(), frame_size - FrameEntrySpillSize());
+  }
 }
 
 void CodeGeneratorARM64::GenerateFrameExit() {
-  int frame_size = GetFrameSize();
-  CPURegList preserved_regs = GetFramePreservedRegisters();
-  __ PeekCPURegList(preserved_regs, frame_size - preserved_regs.TotalSizeInBytes());
-  __ Drop(frame_size);
+  if (!HasEmptyFrame()) {
+    int frame_size = GetFrameSize();
+    __ PeekCPURegList(GetFramePreservedFPRegisters(), frame_size - FrameEntrySpillSize());
+    __ PeekCPURegList(GetFramePreservedCoreRegisters(), frame_size - GetCoreSpillSize());
+    __ Drop(frame_size);
+  }
 }
 
 void CodeGeneratorARM64::Bind(HBasicBlock* block) {
@@ -571,25 +508,21 @@
     }
   } else if (instruction->IsTemporary()) {
     Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
-    MoveHelper(location, temp_location, type);
+    MoveLocation(location, temp_location, type);
   } else if (instruction->IsLoadLocal()) {
     uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
-    if (Is64BitType(type)) {
-      MoveHelper(location, Location::DoubleStackSlot(stack_slot), type);
+    if (Primitive::Is64BitType(type)) {
+      MoveLocation(location, Location::DoubleStackSlot(stack_slot), type);
     } else {
-      MoveHelper(location, Location::StackSlot(stack_slot), type);
+      MoveLocation(location, Location::StackSlot(stack_slot), type);
     }
 
   } else {
     DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
-    MoveHelper(location, locations->Out(), type);
+    MoveLocation(location, locations->Out(), type);
   }
 }
 
-size_t CodeGeneratorARM64::FrameEntrySpillSize() const {
-  return GetFramePreservedRegistersSize();
-}
-
 Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
   Primitive::Type type = load->GetType();
 
@@ -627,26 +560,38 @@
   __ Bind(&done);
 }
 
-void CodeGeneratorARM64::SetupBlockedRegisters() const {
-  // Block reserved registers:
-  //   ip0 (VIXL temporary)
-  //   ip1 (VIXL temporary)
-  //   tr
-  //   lr
-  // sp is not part of the allocatable registers, so we don't need to block it.
-  // TODO: Avoid blocking callee-saved registers, and instead preserve them
-  // where necessary.
+void CodeGeneratorARM64::SetupBlockedRegisters(bool is_baseline) const {
+  // Blocked core registers:
+  //      lr        : Runtime reserved.
+  //      tr        : Runtime reserved.
+  //      xSuspend  : Runtime reserved. TODO: Unblock this when the runtime stops using it.
+  //      ip1       : VIXL core temp.
+  //      ip0       : VIXL core temp.
+  //
+  // Blocked fp registers:
+  //      d31       : VIXL fp temp.
   CPURegList reserved_core_registers = vixl_reserved_core_registers;
   reserved_core_registers.Combine(runtime_reserved_core_registers);
-  reserved_core_registers.Combine(quick_callee_saved_registers);
   while (!reserved_core_registers.IsEmpty()) {
     blocked_core_registers_[reserved_core_registers.PopLowestIndex().code()] = true;
   }
+
   CPURegList reserved_fp_registers = vixl_reserved_fp_registers;
-  reserved_fp_registers.Combine(CPURegList::GetCalleeSavedFP());
   while (!reserved_core_registers.IsEmpty()) {
     blocked_fpu_registers_[reserved_fp_registers.PopLowestIndex().code()] = true;
   }
+
+  if (is_baseline) {
+    CPURegList reserved_core_baseline_registers = callee_saved_core_registers;
+    while (!reserved_core_baseline_registers.IsEmpty()) {
+      blocked_core_registers_[reserved_core_baseline_registers.PopLowestIndex().code()] = true;
+    }
+
+    CPURegList reserved_fp_baseline_registers = callee_saved_fp_registers;
+    while (!reserved_fp_baseline_registers.IsEmpty()) {
+      blocked_fpu_registers_[reserved_fp_baseline_registers.PopLowestIndex().code()] = true;
+    }
+  }
 }
 
 Location CodeGeneratorARM64::AllocateFreeRegister(Primitive::Type type) const {
@@ -654,7 +599,7 @@
     LOG(FATAL) << "Unreachable type " << type;
   }
 
-  if (IsFPType(type)) {
+  if (Primitive::IsFloatingPointType(type)) {
     ssize_t reg = FindFreeEntry(blocked_fpu_registers_, kNumberOfAllocatableFPRegisters);
     DCHECK_NE(reg, -1);
     return Location::FpuRegisterLocation(reg);
@@ -665,6 +610,30 @@
   }
 }
 
+size_t CodeGeneratorARM64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
+  Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
+  __ Str(reg, MemOperand(sp, stack_index));
+  return kArm64WordSize;
+}
+
+size_t CodeGeneratorARM64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
+  Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
+  __ Ldr(reg, MemOperand(sp, stack_index));
+  return kArm64WordSize;
+}
+
+size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  FPRegister reg = FPRegister(reg_id, kDRegSize);
+  __ Str(reg, MemOperand(sp, stack_index));
+  return kArm64WordSize;
+}
+
+size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  FPRegister reg = FPRegister(reg_id, kDRegSize);
+  __ Ldr(reg, MemOperand(sp, stack_index));
+  return kArm64WordSize;
+}
+
 void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const {
   stream << Arm64ManagedRegister::FromXRegister(XRegister(reg));
 }
@@ -686,61 +655,165 @@
   }
 }
 
-void CodeGeneratorARM64::MoveHelper(Location destination,
-                                    Location source,
-                                    Primitive::Type type) {
+
+static bool CoherentConstantAndType(Location constant, Primitive::Type type) {
+  DCHECK(constant.IsConstant());
+  HConstant* cst = constant.GetConstant();
+  return (cst->IsIntConstant() && type == Primitive::kPrimInt) ||
+         (cst->IsLongConstant() && type == Primitive::kPrimLong) ||
+         (cst->IsFloatConstant() && type == Primitive::kPrimFloat) ||
+         (cst->IsDoubleConstant() && type == Primitive::kPrimDouble);
+}
+
+void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Primitive::Type type) {
   if (source.Equals(destination)) {
     return;
   }
-  if (destination.IsRegister()) {
-    Register dst = RegisterFrom(destination, type);
-    if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
-      DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
-      __ Ldr(dst, StackOperandFrom(source));
-    } else {
-      __ Mov(dst, OperandFrom(source, type));
-    }
-  } else if (destination.IsFpuRegister()) {
-    FPRegister dst = FPRegisterFrom(destination, type);
-    if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
-      DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
-      __ Ldr(dst, StackOperandFrom(source));
-    } else if (source.IsFpuRegister()) {
-      __ Fmov(dst, FPRegisterFrom(source, type));
-    } else {
-      MoveConstant(dst, source.GetConstant());
-    }
-  } else {
-    DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
-    if (source.IsRegister()) {
-      __ Str(RegisterFrom(source, type), StackOperandFrom(destination));
-    } else if (source.IsFpuRegister()) {
-      __ Str(FPRegisterFrom(source, type), StackOperandFrom(destination));
-    } else if (source.IsConstant()) {
-      UseScratchRegisterScope temps(GetVIXLAssembler());
-      HConstant* cst = source.GetConstant();
-      CPURegister temp;
-      if (cst->IsIntConstant() || cst->IsLongConstant()) {
-        temp = cst->IsIntConstant() ? temps.AcquireW() : temps.AcquireX();
+
+  // A valid move can always be inferred from the destination and source
+  // locations. When moving from and to a register, the argument type can be
+  // used to generate 32bit instead of 64bit moves. In debug mode we also
+  // checks the coherency of the locations and the type.
+  bool unspecified_type = (type == Primitive::kPrimVoid);
+
+  if (destination.IsRegister() || destination.IsFpuRegister()) {
+    if (unspecified_type) {
+      HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
+      if (source.IsStackSlot() ||
+          (src_cst != nullptr && (src_cst->IsIntConstant() || src_cst->IsFloatConstant()))) {
+        // For stack slots and 32bit constants, a 64bit type is appropriate.
+        type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
       } else {
-        DCHECK(cst->IsFloatConstant() || cst->IsDoubleConstant());
-        temp = cst->IsFloatConstant() ? temps.AcquireS() : temps.AcquireD();
+        // If the source is a double stack slot or a 64bit constant, a 64bit
+        // type is appropriate. Else the source is a register, and since the
+        // type has not been specified, we chose a 64bit type to force a 64bit
+        // move.
+        type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
       }
-      MoveConstant(temp, cst);
+    }
+    DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) ||
+           (destination.IsRegister() && !Primitive::IsFloatingPointType(type)));
+    CPURegister dst = CPURegisterFrom(destination, type);
+    if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
+      DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
+      __ Ldr(dst, StackOperandFrom(source));
+    } else if (source.IsConstant()) {
+      DCHECK(CoherentConstantAndType(source, type));
+      MoveConstant(dst, source.GetConstant());
+    } else {
+      if (destination.IsRegister()) {
+        __ Mov(Register(dst), RegisterFrom(source, type));
+      } else {
+        __ Fmov(FPRegister(dst), FPRegisterFrom(source, type));
+      }
+    }
+
+  } else {  // The destination is not a register. It must be a stack slot.
+    DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
+    if (source.IsRegister() || source.IsFpuRegister()) {
+      if (unspecified_type) {
+        if (source.IsRegister()) {
+          type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
+        } else {
+          type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
+        }
+      }
+      DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) &&
+             (source.IsFpuRegister() == Primitive::IsFloatingPointType(type)));
+      __ Str(CPURegisterFrom(source, type), StackOperandFrom(destination));
+    } else if (source.IsConstant()) {
+      DCHECK(unspecified_type || CoherentConstantAndType(source, type));
+      UseScratchRegisterScope temps(GetVIXLAssembler());
+      HConstant* src_cst = source.GetConstant();
+      CPURegister temp;
+      if (src_cst->IsIntConstant()) {
+        temp = temps.AcquireW();
+      } else if (src_cst->IsLongConstant()) {
+        temp = temps.AcquireX();
+      } else if (src_cst->IsFloatConstant()) {
+        temp = temps.AcquireS();
+      } else {
+        DCHECK(src_cst->IsDoubleConstant());
+        temp = temps.AcquireD();
+      }
+      MoveConstant(temp, src_cst);
       __ Str(temp, StackOperandFrom(destination));
     } else {
       DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
+      DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot());
       UseScratchRegisterScope temps(GetVIXLAssembler());
-      Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW();
+      // There is generally less pressure on FP registers.
+      FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS();
       __ Ldr(temp, StackOperandFrom(source));
       __ Str(temp, StackOperandFrom(destination));
     }
   }
 }
 
+void CodeGeneratorARM64::SwapLocations(Location loc1, Location loc2) {
+  DCHECK(!loc1.IsConstant());
+  DCHECK(!loc2.IsConstant());
+
+  if (loc1.Equals(loc2)) {
+    return;
+  }
+
+  UseScratchRegisterScope temps(GetAssembler()->vixl_masm_);
+
+  bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot();
+  bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot();
+  bool is_fp_reg1 = loc1.IsFpuRegister();
+  bool is_fp_reg2 = loc2.IsFpuRegister();
+
+  if (loc2.IsRegister() && loc1.IsRegister()) {
+    Register r1 = XRegisterFrom(loc1);
+    Register r2 = XRegisterFrom(loc2);
+    Register tmp = temps.AcquireSameSizeAs(r1);
+    __ Mov(tmp, r2);
+    __ Mov(r2, r1);
+    __ Mov(r1, tmp);
+  } else if (is_fp_reg2 && is_fp_reg1) {
+    FPRegister r1 = DRegisterFrom(loc1);
+    FPRegister r2 = DRegisterFrom(loc2);
+    FPRegister tmp = temps.AcquireSameSizeAs(r1);
+    __ Fmov(tmp, r2);
+    __ Fmov(r2, r1);
+    __ Fmov(r1, tmp);
+  } else if (is_slot1 != is_slot2) {
+    MemOperand mem = StackOperandFrom(is_slot1 ? loc1 : loc2);
+    Location reg_loc = is_slot1 ? loc2 : loc1;
+    CPURegister reg, tmp;
+    if (reg_loc.IsFpuRegister()) {
+      reg = DRegisterFrom(reg_loc);
+      tmp = temps.AcquireD();
+    } else {
+      reg = XRegisterFrom(reg_loc);
+      tmp = temps.AcquireX();
+    }
+    __ Ldr(tmp, mem);
+    __ Str(reg, mem);
+    if (reg_loc.IsFpuRegister()) {
+      __ Fmov(FPRegister(reg), FPRegister(tmp));
+    } else {
+      __ Mov(Register(reg), Register(tmp));
+    }
+  } else if (is_slot1 && is_slot2) {
+    MemOperand mem1 = StackOperandFrom(loc1);
+    MemOperand mem2 = StackOperandFrom(loc2);
+    Register tmp1 = loc1.IsStackSlot() ? temps.AcquireW() : temps.AcquireX();
+    Register tmp2 = temps.AcquireSameSizeAs(tmp1);
+    __ Ldr(tmp1, mem1);
+    __ Ldr(tmp2, mem2);
+    __ Str(tmp1, mem2);
+    __ Str(tmp2, mem1);
+  } else {
+    LOG(FATAL) << "Unimplemented";
+  }
+}
+
 void CodeGeneratorARM64::Load(Primitive::Type type,
-                              vixl::CPURegister dst,
-                              const vixl::MemOperand& src) {
+                              CPURegister dst,
+                              const MemOperand& src) {
   switch (type) {
     case Primitive::kPrimBoolean:
       __ Ldrb(Register(dst), src);
@@ -759,7 +832,7 @@
     case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      DCHECK(dst.Is64Bits() == Is64BitType(type));
+      DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
       __ Ldr(dst, src);
       break;
     case Primitive::kPrimVoid:
@@ -767,32 +840,131 @@
   }
 }
 
+void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
+                                     CPURegister dst,
+                                     const MemOperand& src) {
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  Register temp_base = temps.AcquireX();
+  Primitive::Type type = instruction->GetType();
+
+  DCHECK(!src.IsPreIndex());
+  DCHECK(!src.IsPostIndex());
+
+  // TODO(vixl): Let the MacroAssembler handle MemOperand.
+  __ Add(temp_base, src.base(), OperandFromMemOperand(src));
+  MemOperand base = MemOperand(temp_base);
+  switch (type) {
+    case Primitive::kPrimBoolean:
+      __ Ldarb(Register(dst), base);
+      MaybeRecordImplicitNullCheck(instruction);
+      break;
+    case Primitive::kPrimByte:
+      __ Ldarb(Register(dst), base);
+      MaybeRecordImplicitNullCheck(instruction);
+      __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
+      break;
+    case Primitive::kPrimChar:
+      __ Ldarh(Register(dst), base);
+      MaybeRecordImplicitNullCheck(instruction);
+      break;
+    case Primitive::kPrimShort:
+      __ Ldarh(Register(dst), base);
+      MaybeRecordImplicitNullCheck(instruction);
+      __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+    case Primitive::kPrimLong:
+      DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
+      __ Ldar(Register(dst), base);
+      MaybeRecordImplicitNullCheck(instruction);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      DCHECK(dst.IsFPRegister());
+      DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type));
+
+      Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
+      __ Ldar(temp, base);
+      MaybeRecordImplicitNullCheck(instruction);
+      __ Fmov(FPRegister(dst), temp);
+      break;
+    }
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << type;
+  }
+}
+
 void CodeGeneratorARM64::Store(Primitive::Type type,
-                               vixl::CPURegister rt,
-                               const vixl::MemOperand& dst) {
+                               CPURegister src,
+                               const MemOperand& dst) {
   switch (type) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
-      __ Strb(Register(rt), dst);
+      __ Strb(Register(src), dst);
       break;
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
-      __ Strh(Register(rt), dst);
+      __ Strh(Register(src), dst);
       break;
     case Primitive::kPrimInt:
     case Primitive::kPrimNot:
     case Primitive::kPrimLong:
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
-      DCHECK(rt.Is64Bits() == Is64BitType(type));
-      __ Str(rt, dst);
+      DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
+      __ Str(src, dst);
       break;
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << type;
   }
 }
 
+void CodeGeneratorARM64::StoreRelease(Primitive::Type type,
+                                      CPURegister src,
+                                      const MemOperand& dst) {
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  Register temp_base = temps.AcquireX();
+
+  DCHECK(!dst.IsPreIndex());
+  DCHECK(!dst.IsPostIndex());
+
+  // TODO(vixl): Let the MacroAssembler handle this.
+  Operand op = OperandFromMemOperand(dst);
+  __ Add(temp_base, dst.base(), op);
+  MemOperand base = MemOperand(temp_base);
+  switch (type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      __ Stlrb(Register(src), base);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      __ Stlrh(Register(src), base);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+    case Primitive::kPrimLong:
+      DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
+      __ Stlr(Register(src), base);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      DCHECK(src.IsFPRegister());
+      DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type));
+
+      Register temp = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
+      __ Fmov(temp, FPRegister(src));
+      __ Stlr(temp, base);
+      break;
+    }
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << type;
+  }
+}
+
 void CodeGeneratorARM64::LoadCurrentMethod(vixl::Register current_method) {
+  DCHECK(RequiresCurrentMethod());
   DCHECK(current_method.IsW());
   __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
 }
@@ -816,14 +988,47 @@
                                                                      vixl::Register class_reg) {
   UseScratchRegisterScope temps(GetVIXLAssembler());
   Register temp = temps.AcquireW();
-  __ Ldr(temp, HeapOperand(class_reg, mirror::Class::StatusOffset()));
-  __ Cmp(temp, mirror::Class::kStatusInitialized);
-  __ B(lt, slow_path->GetEntryLabel());
+  size_t status_offset = mirror::Class::StatusOffset().SizeValue();
+
   // Even if the initialized flag is set, we need to ensure consistent memory ordering.
-  __ Dmb(InnerShareable, BarrierReads);
+  if (kUseAcquireRelease) {
+    // TODO(vixl): Let the MacroAssembler handle MemOperand.
+    __ Add(temp, class_reg, status_offset);
+    __ Ldar(temp, HeapOperand(temp));
+    __ Cmp(temp, mirror::Class::kStatusInitialized);
+    __ B(lt, slow_path->GetEntryLabel());
+  } else {
+    __ Ldr(temp, HeapOperand(class_reg, status_offset));
+    __ Cmp(temp, mirror::Class::kStatusInitialized);
+    __ B(lt, slow_path->GetEntryLabel());
+    __ Dmb(InnerShareable, BarrierReads);
+  }
   __ Bind(slow_path->GetExitLabel());
 }
 
+void InstructionCodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
+  BarrierType type = BarrierAll;
+
+  switch (kind) {
+    case MemBarrierKind::kAnyAny:
+    case MemBarrierKind::kAnyStore: {
+      type = BarrierAll;
+      break;
+    }
+    case MemBarrierKind::kLoadAny: {
+      type = BarrierReads;
+      break;
+    }
+    case MemBarrierKind::kStoreStore: {
+      type = BarrierWrites;
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected memory barrier " << kind;
+  }
+  __ Dmb(InnerShareable, type);
+}
+
 void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,
                                                          HBasicBlock* successor) {
   SuspendCheckSlowPathARM64* slow_path =
@@ -850,7 +1055,7 @@
         codegen_(codegen) {}
 
 #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M)              \
-  M(ParallelMove)                                          \
+  /* No unimplemented IR. */
 
 #define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode
 
@@ -1044,6 +1249,7 @@
   }
 
   codegen_->Load(type, OutputCPURegister(instruction), source);
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
@@ -1055,6 +1261,7 @@
 void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
   __ Ldr(OutputRegister(instruction),
          HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset()));
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
@@ -1078,7 +1285,7 @@
   Primitive::Type value_type = instruction->GetComponentType();
   if (value_type == Primitive::kPrimNot) {
     codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
-
+    CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
   } else {
     LocationSummary* locations = instruction->GetLocations();
     Register obj = InputRegisterAt(instruction, 0);
@@ -1099,6 +1306,7 @@
     }
 
     codegen_->Store(value_type, value, destination);
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
   }
 }
 
@@ -1113,7 +1321,9 @@
 }
 
 void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
-  BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64();
+  LocationSummary* locations = instruction->GetLocations();
+  BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(
+      instruction, locations->InAt(0), locations->InAt(1));
   codegen_->AddSlowPath(slow_path);
 
   __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
@@ -1125,22 +1335,24 @@
       instruction, LocationSummary::kCallOnSlowPath);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
+  locations->AddTemp(Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
-  UseScratchRegisterScope temps(GetVIXLAssembler());
+  LocationSummary* locations = instruction->GetLocations();
   Register obj = InputRegisterAt(instruction, 0);;
   Register cls = InputRegisterAt(instruction, 1);;
-  Register temp = temps.AcquireW();
+  Register obj_cls = WRegisterFrom(instruction->GetLocations()->GetTemp(0));
 
-  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64();
+  SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(
+      instruction, locations->InAt(1), LocationFrom(obj_cls), instruction->GetDexPc());
   codegen_->AddSlowPath(slow_path);
 
   // TODO: avoid this check if we know obj is not null.
   __ Cbz(obj, slow_path->GetExitLabel());
   // Compare the class of `obj` with `cls`.
-  __ Ldr(temp, HeapOperand(obj, mirror::Object::ClassOffset()));
-  __ Cmp(temp, cls);
+  __ Ldr(obj_cls, HeapOperand(obj, mirror::Object::ClassOffset()));
+  __ Cmp(obj_cls, cls);
   __ B(ne, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
@@ -1316,12 +1528,20 @@
   codegen_->AddSlowPath(slow_path);
   Location value = instruction->GetLocations()->InAt(0);
 
+  Primitive::Type type = instruction->GetType();
+
+  if ((type != Primitive::kPrimInt) && (type != Primitive::kPrimLong)) {
+      LOG(FATAL) << "Unexpected type " << type << "for DivZeroCheck.";
+    return;
+  }
+
   if (value.IsConstant()) {
     int64_t divisor = Int64ConstantFrom(value);
     if (divisor == 0) {
       __ B(slow_path->GetEntryLabel());
     } else {
-      LOG(FATAL) << "Divisions by non-null constants should have been optimized away.";
+      // A division by a non-null constant is valid. We don't need to perform
+      // any check, so simply fall through.
     }
   } else {
     __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
@@ -1438,28 +1658,60 @@
 }
 
 void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
   MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), instruction->GetFieldOffset());
-  codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+
+  if (instruction->IsVolatile()) {
+    if (kUseAcquireRelease) {
+      // NB: LoadAcquire will record the pc info if needed.
+      codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field);
+    } else {
+      codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
+      // For IRIW sequential consistency kLoadAny is not sufficient.
+      GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+    }
+  } else {
+    codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
 }
 
 void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
 }
 
 void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  Primitive::Type field_type = instruction->GetFieldType();
-  CPURegister value = InputCPURegisterAt(instruction, 1);
   Register obj = InputRegisterAt(instruction, 0);
-  codegen_->Store(field_type, value, HeapOperand(obj, instruction->GetFieldOffset()));
-  if (field_type == Primitive::kPrimNot) {
+  CPURegister value = InputCPURegisterAt(instruction, 1);
+  Offset offset = instruction->GetFieldOffset();
+  Primitive::Type field_type = instruction->GetFieldType();
+
+  if (instruction->IsVolatile()) {
+    if (kUseAcquireRelease) {
+      codegen_->StoreRelease(field_type, value, HeapOperand(obj, offset));
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
+    } else {
+      GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+      codegen_->Store(field_type, value, HeapOperand(obj, offset));
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
+      GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+    }
+  } else {
+    codegen_->Store(field_type, value, HeapOperand(obj, offset));
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
+
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
     codegen_->MarkGCCard(obj, Register(value));
   }
 }
@@ -1470,7 +1722,8 @@
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), true);  // The output does overlap inputs.
+  // The output does overlap inputs.
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
 }
 
 void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -1496,7 +1749,8 @@
     // If the classes are not equal, we go into a slow path.
     DCHECK(locations->OnlyCallsOnSlowPath());
     SlowPathCodeARM64* slow_path =
-        new (GetGraph()->GetArena()) TypeCheckSlowPathARM64();
+        new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(
+        instruction, locations->InAt(1), locations->Out(), instruction->GetDexPc());
     codegen_->AddSlowPath(slow_path);
     __ B(ne, slow_path->GetEntryLabel());
     __ Mov(out, 1);
@@ -1559,6 +1813,7 @@
   } else {
     __ Ldr(temp, HeapOperandFrom(receiver, class_offset));
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetImtEntryAt(method_offset);
   __ Ldr(temp, HeapOperand(temp, method_offset));
   // lr = temp->GetEntryPoint();
@@ -1570,19 +1825,37 @@
 }
 
 void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
+  if (intrinsic.TryDispatch(invoke)) {
+    return;
+  }
+
   HandleInvoke(invoke);
 }
 
-void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+  IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena());
+  if (intrinsic.TryDispatch(invoke)) {
+    return;
+  }
+
   HandleInvoke(invoke);
 }
 
-void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
-  Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
-  // Make sure that ArtMethod* is passed in W0 as per the calling convention
-  DCHECK(temp.Is(w0));
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codegen) {
+  if (invoke->GetLocations()->Intrinsified()) {
+    IntrinsicCodeGeneratorARM64 intrinsic(codegen);
+    intrinsic.Dispatch(invoke);
+    return true;
+  }
+  return false;
+}
+
+void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
+  // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
+  DCHECK(temp.Is(kArtMethodRegister));
   size_t index_in_cache = mirror::Array::DataOffset(kHeapRefSize).SizeValue() +
-    invoke->GetIndexInDexCache() * kHeapRefSize;
+      invoke->GetDexMethodIndex() * kHeapRefSize;
 
   // TODO: Implement all kinds of calls:
   // 1) boot -> boot
@@ -1592,22 +1865,39 @@
   // Currently we implement the app -> app logic, which looks up in the resolve cache.
 
   // temp = method;
-  codegen_->LoadCurrentMethod(temp);
-  // temp = temp->dex_cache_resolved_methods_;
-  __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset()));
-  // temp = temp[index_in_cache];
-  __ Ldr(temp, HeapOperand(temp, index_in_cache));
-  // lr = temp->entry_point_from_quick_compiled_code_;
-  __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-                          kArm64WordSize)));
-  // lr();
-  __ Blr(lr);
+  LoadCurrentMethod(temp);
+  if (!invoke->IsRecursive()) {
+    // temp = temp->dex_cache_resolved_methods_;
+    __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset()));
+    // temp = temp[index_in_cache];
+    __ Ldr(temp, HeapOperand(temp, index_in_cache));
+    // lr = temp->entry_point_from_quick_compiled_code_;
+    __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+        kArm64WordSize)));
+    // lr();
+    __ Blr(lr);
+  } else {
+    __ Bl(&frame_entry_label_);
+  }
 
-  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
-  DCHECK(!codegen_->IsLeafMethod());
+  RecordPcInfo(invoke, invoke->GetDexPc());
+  DCHECK(!IsLeafMethod());
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
+
+  Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
+  codegen_->GenerateStaticOrDirectCall(invoke, temp);
 }
 
 void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
+
   LocationSummary* locations = invoke->GetLocations();
   Location receiver = locations->InAt(0);
   Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
@@ -1624,6 +1914,7 @@
     DCHECK(receiver.IsRegister());
     __ Ldr(temp, HeapOperandFrom(receiver, class_offset));
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   __ Ldr(temp, HeapOperand(temp, method_offset));
   // lr = temp->GetEntryPoint();
@@ -1736,6 +2027,7 @@
         ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject),
       instruction,
       instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
 }
 
 void LocationsBuilderARM64::VisitMul(HMul* mul) {
@@ -1821,9 +2113,11 @@
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
+  locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
   locations->SetOut(LocationFrom(x0));
-  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
+  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
+                       void*, uint32_t, int32_t, mirror::ArtMethod*>();
 }
 
 void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
@@ -1832,11 +2126,15 @@
   Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
   DCHECK(type_index.Is(w0));
   Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
-  DCHECK(current_method.Is(w1));
+  DCHECK(current_method.Is(w2));
   codegen_->LoadCurrentMethod(current_method);
   __ Mov(type_index, instruction->GetTypeIndex());
   codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+      GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+      instruction,
+      instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
+                       void*, uint32_t, int32_t, mirror::ArtMethod*>();
 }
 
 void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
@@ -1846,6 +2144,7 @@
   locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
   locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
   locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
+  CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*>();
 }
 
 void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) {
@@ -1857,7 +2156,10 @@
   codegen_->LoadCurrentMethod(current_method);
   __ Mov(type_index, instruction->GetTypeIndex());
   codegen_->InvokeRuntime(
-      QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+      GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+      instruction,
+      instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*>();
 }
 
 void LocationsBuilderARM64::VisitNot(HNot* instruction) {
@@ -1868,10 +2170,6 @@
 
 void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) {
   switch (instruction->InputAt(0)->GetType()) {
-    case Primitive::kPrimBoolean:
-      __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), Operand(1));
-      break;
-
     case Primitive::kPrimInt:
     case Primitive::kPrimLong:
       __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0));
@@ -1891,18 +2189,31 @@
   }
 }
 
-void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+  if (codegen_->CanMoveNullCheckToUser(instruction)) {
+    return;
+  }
+  Location obj = instruction->GetLocations()->InAt(0);
+
+  __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
   SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction);
   codegen_->AddSlowPath(slow_path);
 
   LocationSummary* locations = instruction->GetLocations();
   Location obj = locations->InAt(0);
-  if (obj.IsRegister()) {
-    __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
+
+  __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
+}
+
+void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
+  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+    GenerateImplicitNullCheck(instruction);
   } else {
-    DCHECK(obj.IsConstant()) << obj;
-    DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0);
-    __ B(slow_path->GetEntryLabel());
+    GenerateExplicitNullCheck(instruction);
   }
 }
 
@@ -1914,6 +2225,14 @@
   HandleBinaryOp(instruction);
 }
 
+void LocationsBuilderARM64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
+  LOG(FATAL) << "Unreachable";
+}
+
+void InstructionCodeGeneratorARM64::VisitParallelMove(HParallelMove* instruction) {
+  codegen_->GetMoveResolver()->EmitNativeCode(instruction);
+}
+
 void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
   Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
@@ -1944,9 +2263,12 @@
 }
 
 void LocationsBuilderARM64::VisitRem(HRem* rem) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
-  switch (rem->GetResultType()) {
+  Primitive::Type type = rem->GetResultType();
+  LocationSummary::CallKind call_kind =
+      Primitive::IsFloatingPointType(type) ? LocationSummary::kCall : LocationSummary::kNoCall;
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
+
+  switch (type) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong:
       locations->SetInAt(0, Location::RequiresRegister());
@@ -1954,13 +2276,24 @@
       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
 
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      InvokeRuntimeCallingConvention calling_convention;
+      locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
+      locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
+      locations->SetOut(calling_convention.GetReturnLocation(type));
+
+      break;
+    }
+
     default:
-      LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+      LOG(FATAL) << "Unexpected rem type " << type;
   }
 }
 
 void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) {
   Primitive::Type type = rem->GetResultType();
+
   switch (type) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
@@ -1975,6 +2308,14 @@
       break;
     }
 
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble: {
+      int32_t entry_offset = (type == Primitive::kPrimFloat) ? QUICK_ENTRY_POINT(pFmodf)
+                                                             : QUICK_ENTRY_POINT(pFmod);
+      codegen_->InvokeRuntime(entry_offset, rem, rem->GetDexPc());
+      break;
+    }
+
     default:
       LOG(FATAL) << "Unexpected rem type " << type;
   }
@@ -1989,7 +2330,7 @@
 void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction) {
   UNUSED(instruction);
   codegen_->GenerateFrameExit();
-  __ Br(lr);
+  __ Ret();
 }
 
 void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) {
@@ -1999,7 +2340,7 @@
 void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction) {
   UNUSED(instruction);
   codegen_->GenerateFrameExit();
-  __ Br(lr);
+  __ Ret();
 }
 
 void LocationsBuilderARM64::VisitShl(HShl* shl) {
@@ -2063,7 +2404,19 @@
 
 void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
   MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), instruction->GetFieldOffset());
-  codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+
+  if (instruction->IsVolatile()) {
+    if (kUseAcquireRelease) {
+      // NB: LoadAcquire will record the pc info if needed.
+      codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field);
+    } else {
+      codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+      // For IRIW sequential consistency kLoadAny is not sufficient.
+      GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+    }
+  } else {
+    codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
+  }
 }
 
 void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
@@ -2074,13 +2427,24 @@
 }
 
 void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  CPURegister value = InputCPURegisterAt(instruction, 1);
   Register cls = InputRegisterAt(instruction, 0);
+  CPURegister value = InputCPURegisterAt(instruction, 1);
   Offset offset = instruction->GetFieldOffset();
   Primitive::Type field_type = instruction->GetFieldType();
 
-  codegen_->Store(field_type, value, HeapOperand(cls, offset));
-  if (field_type == Primitive::kPrimNot) {
+  if (instruction->IsVolatile()) {
+    if (kUseAcquireRelease) {
+      codegen_->StoreRelease(field_type, value, HeapOperand(cls, offset));
+    } else {
+      GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+      codegen_->Store(field_type, value, HeapOperand(cls, offset));
+      GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+    }
+  } else {
+    codegen_->Store(field_type, value, HeapOperand(cls, offset));
+  }
+
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
     codegen_->MarkGCCard(cls, Register(value));
   }
 }
@@ -2122,6 +2486,7 @@
 void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) {
   codegen_->InvokeRuntime(
       QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc());
+  CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
 }
 
 void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) {
@@ -2135,13 +2500,13 @@
     LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
   }
 
-  if (IsFPType(input_type)) {
+  if (Primitive::IsFloatingPointType(input_type)) {
     locations->SetInAt(0, Location::RequiresFpuRegister());
   } else {
     locations->SetInAt(0, Location::RequiresRegister());
   }
 
-  if (IsFPType(result_type)) {
+  if (Primitive::IsFloatingPointType(result_type)) {
     locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
   } else {
     locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -2154,25 +2519,27 @@
 
   DCHECK_NE(input_type, result_type);
 
-  if (IsIntegralType(result_type) && IsIntegralType(input_type)) {
+  if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
     int result_size = Primitive::ComponentSize(result_type);
     int input_size = Primitive::ComponentSize(input_type);
-    int min_size = kBitsPerByte * std::min(result_size, input_size);
+    int min_size = std::min(result_size, input_size);
     Register output = OutputRegister(conversion);
     Register source = InputRegisterAt(conversion, 0);
-    if ((result_type == Primitive::kPrimChar) ||
-        ((input_type == Primitive::kPrimChar) && (result_size > input_size))) {
-      __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size);
+    if ((result_type == Primitive::kPrimChar) && (input_size < result_size)) {
+      __ Ubfx(output, source, 0, result_size * kBitsPerByte);
+    } else if ((result_type == Primitive::kPrimChar) ||
+               ((input_type == Primitive::kPrimChar) && (result_size > input_size))) {
+      __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
     } else {
-      __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size);
+      __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
     }
-  } else if (IsFPType(result_type) && IsIntegralType(input_type)) {
-    CHECK(input_type == Primitive::kPrimInt || input_type == Primitive::kPrimLong);
+  } else if (Primitive::IsFloatingPointType(result_type) && Primitive::IsIntegralType(input_type)) {
     __ Scvtf(OutputFPRegister(conversion), InputRegisterAt(conversion, 0));
-  } else if (IsIntegralType(result_type) && IsFPType(input_type)) {
+  } else if (Primitive::IsIntegralType(result_type) && Primitive::IsFloatingPointType(input_type)) {
     CHECK(result_type == Primitive::kPrimInt || result_type == Primitive::kPrimLong);
     __ Fcvtzs(OutputRegister(conversion), InputFPRegisterAt(conversion, 0));
-  } else if (IsFPType(result_type) && IsFPType(input_type)) {
+  } else if (Primitive::IsFloatingPointType(result_type) &&
+             Primitive::IsFloatingPointType(input_type)) {
     __ Fcvt(OutputFPRegister(conversion), InputFPRegisterAt(conversion, 0));
   } else {
     LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 0e3d25f..2e937e2 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -18,6 +18,8 @@
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
 
 #include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "parallel_move_resolver.h"
 #include "utils/arm64/assembler_arm64.h"
@@ -29,7 +31,10 @@
 namespace arm64 {
 
 class CodeGeneratorARM64;
-class SlowPathCodeARM64;
+
+// TODO: Tune the use of Load-Acquire, Store-Release vs Data Memory Barriers.
+// For now we prefer the use of load-acquire, store-release over explicit memory barriers.
+static constexpr bool kUseAcquireRelease = true;
 
 // Use a local definition to prevent copying mistakes.
 static constexpr size_t kArm64WordSize = kArm64PointerSize;
@@ -43,17 +48,42 @@
 };
 static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters);
 
-const vixl::Register tr = vixl::x18;        // Thread Register
+const vixl::Register tr = vixl::x18;                        // Thread Register
+static const vixl::Register kArtMethodRegister = vixl::w0;  // Method register on invoke.
+const vixl::Register kQuickSuspendRegister = vixl::x19;
 
 const vixl::CPURegList vixl_reserved_core_registers(vixl::ip0, vixl::ip1);
 const vixl::CPURegList vixl_reserved_fp_registers(vixl::d31);
-const vixl::CPURegList runtime_reserved_core_registers(tr, vixl::lr);
-const vixl::CPURegList quick_callee_saved_registers(vixl::CPURegister::kRegister,
-                                                    vixl::kXRegSize,
-                                                    kArm64CalleeSaveRefSpills);
 
+// TODO: When the runtime does not use kQuickSuspendRegister as a suspend
+// counter remove it from the reserved registers list.
+const vixl::CPURegList runtime_reserved_core_registers(tr, kQuickSuspendRegister, vixl::lr);
+
+// Callee-saved registers defined by AAPCS64.
+const vixl::CPURegList callee_saved_core_registers(vixl::CPURegister::kRegister,
+                                                   vixl::kXRegSize,
+                                                   vixl::x19.code(),
+                                                   vixl::x30.code());
+const vixl::CPURegList callee_saved_fp_registers(vixl::CPURegister::kFPRegister,
+                                                 vixl::kDRegSize,
+                                                 vixl::d8.code(),
+                                                 vixl::d15.code());
 Location ARM64ReturnLocation(Primitive::Type return_type);
 
+class SlowPathCodeARM64 : public SlowPathCode {
+ public:
+  SlowPathCodeARM64() : entry_label_(), exit_label_() {}
+
+  vixl::Label* GetEntryLabel() { return &entry_label_; }
+  vixl::Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  vixl::Label entry_label_;
+  vixl::Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64);
+};
+
 class InvokeDexCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> {
  public:
   InvokeDexCallingConvention()
@@ -108,9 +138,12 @@
 
  private:
   void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::Register class_reg);
+  void GenerateMemoryBarrier(MemBarrierKind kind);
   void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
   void HandleBinaryOp(HBinaryOperation* instr);
   void HandleShift(HBinaryOperation* instr);
+  void GenerateImplicitNullCheck(HNullCheck* instruction);
+  void GenerateExplicitNullCheck(HNullCheck* instruction);
 
   Arm64Assembler* const assembler_;
   CodeGeneratorARM64* const codegen_;
@@ -139,21 +172,43 @@
   DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARM64);
 };
 
+class ParallelMoveResolverARM64 : public ParallelMoveResolver {
+ public:
+  ParallelMoveResolverARM64(ArenaAllocator* allocator, CodeGeneratorARM64* codegen)
+      : ParallelMoveResolver(allocator), codegen_(codegen) {}
+
+  void EmitMove(size_t index) OVERRIDE;
+  void EmitSwap(size_t index) OVERRIDE;
+  void RestoreScratch(int reg) OVERRIDE;
+  void SpillScratch(int reg) OVERRIDE;
+
+ private:
+  Arm64Assembler* GetAssembler() const;
+  vixl::MacroAssembler* GetVIXLAssembler() const {
+    return GetAssembler()->vixl_masm_;
+  }
+
+  CodeGeneratorARM64* const codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARM64);
+};
+
 class CodeGeneratorARM64 : public CodeGenerator {
  public:
-  explicit CodeGeneratorARM64(HGraph* graph);
+  CodeGeneratorARM64(HGraph* graph, const CompilerOptions& compiler_options);
   virtual ~CodeGeneratorARM64() {}
 
   void GenerateFrameEntry() OVERRIDE;
   void GenerateFrameExit() OVERRIDE;
 
-  static const vixl::CPURegList& GetFramePreservedRegisters() {
-    static const vixl::CPURegList frame_preserved_regs =
-        vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize, vixl::lr.Bit());
-    return frame_preserved_regs;
+  vixl::CPURegList GetFramePreservedCoreRegisters() const {
+    return vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize,
+                            core_spill_mask_);
   }
-  static int GetFramePreservedRegistersSize() {
-    return GetFramePreservedRegisters().TotalSizeInBytes();
+
+  vixl::CPURegList GetFramePreservedFPRegisters() const {
+    return vixl::CPURegList(vixl::CPURegister::kFPRegister, vixl::kDRegSize,
+                            fpu_spill_mask_);
   }
 
   void Bind(HBasicBlock* block) OVERRIDE;
@@ -168,14 +223,17 @@
     return kArm64WordSize;
   }
 
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+    // Allocated in D registers, which are word sized.
+    return kArm64WordSize;
+  }
+
   uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
     vixl::Label* block_entry_label = GetLabelOf(block);
     DCHECK(block_entry_label->IsBound());
     return block_entry_label->location();
   }
 
-  size_t FrameEntrySpillSize() const OVERRIDE;
-
   HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
   HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
   Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; }
@@ -186,26 +244,17 @@
 
   // Register allocation.
 
-  void SetupBlockedRegisters() const OVERRIDE;
+  void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
   // AllocateFreeRegister() is only used when allocating registers locally
   // during CompileBaseline().
   Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
 
   Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
 
-  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
-    UNUSED(stack_index);
-    UNUSED(reg_id);
-    LOG(INFO) << "CodeGeneratorARM64::SaveCoreRegister()";
-    return kArm64WordSize;
-  }
-
-  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
-    UNUSED(stack_index);
-    UNUSED(reg_id);
-    LOG(INFO) << "CodeGeneratorARM64::RestoreCoreRegister()";
-    return kArm64WordSize;
-  }
+  size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
+  size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
+  size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
+  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id);
 
   // The number of registers that can be allocated. The register allocator may
   // decide to reserve and not use a few of them.
@@ -237,30 +286,45 @@
 
   // Code generation helpers.
   void MoveConstant(vixl::CPURegister destination, HConstant* constant);
-  void MoveHelper(Location destination, Location source, Primitive::Type type);
+  // The type is optional. When specified it must be coherent with the
+  // locations, and is used for optimisation and debugging.
+  void MoveLocation(Location destination, Location source,
+                    Primitive::Type type = Primitive::kPrimVoid);
+  void SwapLocations(Location loc_1, Location loc_2);
   void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src);
   void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
   void LoadCurrentMethod(vixl::Register current_method);
+  void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src);
+  void StoreRelease(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
 
   // Generate code to invoke a runtime entry point.
   void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc);
 
-  ParallelMoveResolver* GetMoveResolver() OVERRIDE {
-    UNIMPLEMENTED(INFO) << "TODO: MoveResolver";
-    return nullptr;
+  ParallelMoveResolverARM64* GetMoveResolver() { return &move_resolver_; }
+
+  bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return false;
   }
 
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, vixl::Register temp);
+
  private:
   // Labels for each block that will be compiled.
   vixl::Label* block_labels_;
+  vixl::Label frame_entry_label_;
 
   LocationsBuilderARM64 location_builder_;
   InstructionCodeGeneratorARM64 instruction_visitor_;
+  ParallelMoveResolverARM64 move_resolver_;
   Arm64Assembler assembler_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64);
 };
 
+inline Arm64Assembler* ParallelMoveResolverARM64::GetAssembler() const {
+  return codegen_->GetAssembler();
+}
+
 }  // namespace arm64
 }  // namespace art
 
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 6f83d9f..1a95f41 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -17,6 +17,7 @@
 #include "code_generator_x86.h"
 
 #include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "gc/accounting/card_table.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_method.h"
@@ -31,19 +32,20 @@
 
 namespace x86 {
 
-static constexpr bool kExplicitStackOverflowCheck = false;
-
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
 static constexpr int kCurrentMethodStackOffset = 0;
 
 static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX, EBX };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
-static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+    arraysize(kRuntimeParameterFpuRegisters);
+
+static constexpr int kC2ConditionMask = 0x400;
 
 // Marker for places that can be updated once we don't follow the quick ABI.
 static constexpr bool kFollowsQuickABI = true;
+static constexpr int kFakeReturnRegister = Register(8);
 
 class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> {
  public:
@@ -123,21 +125,6 @@
   DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86);
 };
 
-class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 {
- public:
-  StackOverflowCheckSlowPathX86() {}
-
-  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    __ Bind(GetEntryLabel());
-    __ addl(ESP,
-            Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
-    __ fs()->jmp(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowStackOverflow)));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
-};
-
 class BoundsCheckSlowPathX86 : public SlowPathCodeX86 {
  public:
   BoundsCheckSlowPathX86(HBoundsCheck* instruction,
@@ -215,8 +202,8 @@
     codegen->SaveLiveRegisters(locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0));
-    __ movl(calling_convention.GetRegisterAt(1), Immediate(instruction_->GetStringIndex()));
+    x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+    __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction_->GetStringIndex()));
     __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pResolveString)));
     codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
     x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX));
@@ -373,15 +360,25 @@
   return kX86WordSize;
 }
 
-CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
-    : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters, kNumberOfRegisterPairs),
+size_t CodeGeneratorX86::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  __ movsd(Address(ESP, stack_index), XmmRegister(reg_id));
+  return GetFloatingPointSpillSlotSize();
+}
+
+size_t CodeGeneratorX86::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
+  __ movsd(XmmRegister(reg_id), Address(ESP, stack_index));
+  return GetFloatingPointSpillSlotSize();
+}
+
+CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options)
+    : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfXmmRegisters,
+                    kNumberOfRegisterPairs, (1 << kFakeReturnRegister), 0, compiler_options),
       block_labels_(graph->GetArena(), 0),
       location_builder_(graph, this),
       instruction_visitor_(graph, this),
-      move_resolver_(graph->GetArena(), this) {}
-
-size_t CodeGeneratorX86::FrameEntrySpillSize() const {
-  return kNumberOfPushedRegistersAtEntry * kX86WordSize;
+      move_resolver_(graph->GetArena(), this) {
+  // Use a fake return address register to mimic Quick.
+  AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
 }
 
 Location CodeGeneratorX86::AllocateFreeRegister(Primitive::Type type) const {
@@ -430,7 +427,7 @@
   return Location();
 }
 
-void CodeGeneratorX86::SetupBlockedRegisters() const {
+void CodeGeneratorX86::SetupBlockedRegisters(bool is_baseline ATTRIBUTE_UNUSED) const {
   // Don't allocate the dalvik style register pair passing.
   blocked_register_pairs_[ECX_EDX] = true;
 
@@ -463,33 +460,26 @@
         codegen_(codegen) {}
 
 void CodeGeneratorX86::GenerateFrameEntry() {
-  // Create a fake register to mimic Quick.
-  static const int kFakeReturnRegister = 8;
-  core_spill_mask_ |= (1 << kFakeReturnRegister);
-
+  __ Bind(&frame_entry_label_);
   bool skip_overflow_check =
       IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
-  if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+  DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+
+  if (!skip_overflow_check) {
     __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
     RecordPcInfo(nullptr, 0);
   }
 
-  // The return PC has already been pushed on the stack.
-  __ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
-
-  if (!skip_overflow_check && kExplicitStackOverflowCheck) {
-    SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
-    AddSlowPath(slow_path);
-
-    __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>()));
-    __ j(kLess, slow_path->GetEntryLabel());
+  if (!HasEmptyFrame()) {
+    __ subl(ESP, Immediate(GetFrameSize() - FrameEntrySpillSize()));
+    __ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
   }
-
-  __ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
 }
 
 void CodeGeneratorX86::GenerateFrameExit() {
-  __ addl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
+  if (!HasEmptyFrame()) {
+    __ addl(ESP, Immediate(GetFrameSize() - FrameEntrySpillSize()));
+  }
 }
 
 void CodeGeneratorX86::Bind(HBasicBlock* block) {
@@ -497,6 +487,7 @@
 }
 
 void CodeGeneratorX86::LoadCurrentMethod(Register reg) {
+  DCHECK(RequiresCurrentMethod());
   __ movl(reg, Address(ESP, kCurrentMethodStackOffset));
 }
 
@@ -531,30 +522,46 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
-    case Primitive::kPrimFloat:
     case Primitive::kPrimNot: {
       uint32_t index = gp_index_++;
+      stack_index_++;
       if (index < calling_convention.GetNumberOfRegisters()) {
         return Location::RegisterLocation(calling_convention.GetRegisterAt(index));
       } else {
-        return Location::StackSlot(calling_convention.GetStackOffsetOf(index));
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 1));
       }
     }
 
-    case Primitive::kPrimLong:
-    case Primitive::kPrimDouble: {
+    case Primitive::kPrimLong: {
       uint32_t index = gp_index_;
       gp_index_ += 2;
+      stack_index_ += 2;
       if (index + 1 < calling_convention.GetNumberOfRegisters()) {
         X86ManagedRegister pair = X86ManagedRegister::FromRegisterPair(
             calling_convention.GetRegisterPairAt(index));
         return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh());
-      } else if (index + 1 == calling_convention.GetNumberOfRegisters()) {
-        // On X86, the register index and stack index of a quick parameter is the same, since
-        // we are passing floating pointer values in core registers.
-        return Location::QuickParameter(index, index);
       } else {
-        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(index));
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 2));
+      }
+    }
+
+    case Primitive::kPrimFloat: {
+      uint32_t index = fp_index_++;
+      stack_index_++;
+      if (index < calling_convention.GetNumberOfFpuRegisters()) {
+        return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(index));
+      } else {
+        return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 1));
+      }
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t index = fp_index_++;
+      stack_index_ += 2;
+      if (index < calling_convention.GetNumberOfFpuRegisters()) {
+        return Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(index));
+      } else {
+        return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index_ - 2));
       }
     }
 
@@ -593,6 +600,16 @@
       __ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>());
     } else if (source.IsFpuRegister()) {
       __ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
+    } else if (source.IsConstant()) {
+      HConstant* constant = source.GetConstant();
+      int32_t value;
+      if (constant->IsIntConstant()) {
+        value = constant->AsIntConstant()->GetValue();
+      } else {
+        DCHECK(constant->IsFloatConstant());
+        value = bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue());
+      }
+      __ movl(Address(ESP, destination.GetStackIndex()), Immediate(value));
     } else {
       DCHECK(source.IsStackSlot());
       __ pushl(Address(ESP, source.GetStackIndex()));
@@ -614,16 +631,6 @@
           Location::RegisterLocation(destination.AsRegisterPairLow<Register>()));
     } else if (source.IsFpuRegister()) {
       LOG(FATAL) << "Unimplemented";
-    } else if (source.IsQuickParameter()) {
-      uint16_t register_index = source.GetQuickParameterRegisterIndex();
-      uint16_t stack_index = source.GetQuickParameterStackIndex();
-      InvokeDexCallingConvention calling_convention;
-      EmitParallelMoves(
-          Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
-          Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
-          Location::StackSlot(
-              calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize()),
-          Location::RegisterLocation(destination.AsRegisterPairHigh<Register>()));
     } else {
       // No conflict possible, so just do the moves.
       DCHECK(source.IsDoubleStackSlot());
@@ -631,24 +638,10 @@
       __ movl(destination.AsRegisterPairHigh<Register>(),
               Address(ESP, source.GetHighStackIndex(kX86WordSize)));
     }
-  } else if (destination.IsQuickParameter()) {
-    InvokeDexCallingConvention calling_convention;
-    uint16_t register_index = destination.GetQuickParameterRegisterIndex();
-    uint16_t stack_index = destination.GetQuickParameterStackIndex();
-    if (source.IsRegisterPair()) {
-      LOG(FATAL) << "Unimplemented";
-    } else if (source.IsFpuRegister()) {
-      LOG(FATAL) << "Unimplemented";
-    } else {
-      DCHECK(source.IsDoubleStackSlot());
-      EmitParallelMoves(
-          Location::StackSlot(source.GetStackIndex()),
-          Location::RegisterLocation(calling_convention.GetRegisterAt(register_index)),
-          Location::StackSlot(source.GetHighStackIndex(kX86WordSize)),
-          Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index + 1)));
-    }
   } else if (destination.IsFpuRegister()) {
-    if (source.IsDoubleStackSlot()) {
+    if (source.IsFpuRegister()) {
+      __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+    } else if (source.IsDoubleStackSlot()) {
       __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
     } else {
       LOG(FATAL) << "Unimplemented";
@@ -660,18 +653,6 @@
       __ movl(Address(ESP, destination.GetStackIndex()), source.AsRegisterPairLow<Register>());
       __ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
               source.AsRegisterPairHigh<Register>());
-    } else if (source.IsQuickParameter()) {
-      // No conflict possible, so just do the move.
-      InvokeDexCallingConvention calling_convention;
-      uint16_t register_index = source.GetQuickParameterRegisterIndex();
-      uint16_t stack_index = source.GetQuickParameterStackIndex();
-      // Just move the low part. The only time a source is a quick parameter is
-      // when moving the parameter to its stack locations. And the (Java) caller
-      // of this method has already done that.
-      __ movl(Address(ESP, destination.GetStackIndex()),
-              calling_convention.GetRegisterAt(register_index));
-      DCHECK_EQ(calling_convention.GetStackOffsetOf(stack_index + 1) + GetFrameSize(),
-                static_cast<size_t>(destination.GetHighStackIndex(kX86WordSize)));
     } else if (source.IsFpuRegister()) {
       __ movsd(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
     } else {
@@ -1115,11 +1096,11 @@
   __ ret();
 }
 
-void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   HandleInvoke(invoke);
 }
 
-void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
 
   // TODO: Implement all kinds of calls:
@@ -1131,13 +1112,17 @@
 
   // temp = method;
   codegen_->LoadCurrentMethod(temp);
-  // temp = temp->dex_cache_resolved_methods_;
-  __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
-  // temp = temp[index_in_cache]
-  __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
-  // (temp + offset_of_quick_compiled_code)()
-  __ call(Address(
-      temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+  if (!invoke->IsRecursive()) {
+    // temp = temp->dex_cache_resolved_methods_;
+    __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+    // temp = temp[index_in_cache]
+    __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())));
+    // (temp + offset_of_quick_compiled_code)()
+    __ call(Address(
+        temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+  } else {
+    __ call(codegen_->GetFrameEntryLabel());
+  }
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1198,6 +1183,7 @@
   } else {
     __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   __ movl(temp, Address(temp, method_offset));
   // call temp->GetEntryPoint();
@@ -1211,7 +1197,7 @@
 void LocationsBuilderX86::VisitInvokeInterface(HInvokeInterface* invoke) {
   HandleInvoke(invoke);
   // Add the hidden argument.
-  invoke->GetLocations()->AddTemp(Location::FpuRegisterLocation(XMM0));
+  invoke->GetLocations()->AddTemp(Location::FpuRegisterLocation(XMM7));
 }
 
 void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) {
@@ -1234,6 +1220,7 @@
   } else {
     __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
   }
+    codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetImtEntryAt(method_offset);
   __ movl(temp, Address(temp, method_offset));
   // call temp->GetEntryPoint();
@@ -1326,11 +1313,20 @@
 }
 
 void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
   Primitive::Type result_type = conversion->GetResultType();
   Primitive::Type input_type = conversion->GetInputType();
   DCHECK_NE(result_type, input_type);
+
+  // The float-to-long and double-to-long type conversions rely on a
+  // call to the runtime.
+  LocationSummary::CallKind call_kind =
+      ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+       && result_type == Primitive::kPrimLong)
+      ? LocationSummary::kCall
+      : LocationSummary::kNoCall;
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
+
   switch (result_type) {
     case Primitive::kPrimByte:
       switch (input_type) {
@@ -1380,8 +1376,10 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-int' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
           break;
 
         default:
@@ -1402,10 +1400,16 @@
           break;
 
         case Primitive::kPrimFloat:
-        case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type << " to "
-                     << result_type << " not yet implemented";
-          break;
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `float-to-long' or 'double-to-long' instruction.
+          InvokeRuntimeCallingConvention calling_convention;
+          XmmRegister parameter = calling_convention.GetFpuRegisterAt(0);
+          locations->SetInAt(0, Location::FpuRegisterLocation(parameter));
+
+          // The runtime helper puts the result in EAX, EDX.
+          locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
+        }
+        break;
 
         default:
           LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1449,8 +1453,9 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-float' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
           break;
 
         default:
@@ -1479,8 +1484,9 @@
           break;
 
         case Primitive::kPrimFloat:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `float-to-double' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
           break;
 
         default:
@@ -1590,10 +1596,30 @@
           break;
         }
 
-        case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-int' instruction.
+          XmmRegister input = in.AsFpuRegister<XmmRegister>();
+          Register output = out.AsRegister<Register>();
+          XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          Label done, nan;
+
+          __ movl(output, Immediate(kPrimIntMax));
+          // temp = int-to-double(output)
+          __ cvtsi2sd(temp, output);
+          // if input >= temp goto done
+          __ comisd(input, temp);
+          __ j(kAboveEqual, &done);
+          // if input == NaN goto nan
+          __ j(kUnordered, &nan);
+          // output = double-to-int-truncate(input)
+          __ cvttsd2si(output, input);
+          __ jmp(&done);
+          __ Bind(&nan);
+          //  output = 0
+          __ xorl(output, output);
+          __ Bind(&done);
           break;
+        }
 
         default:
           LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1615,9 +1641,15 @@
           break;
 
         case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-long' instruction.
+          __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pF2l)));
+          codegen_->RecordPcInfo(conversion, conversion->GetDexPc());
+          break;
+
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type << " to "
-                     << result_type << " not yet implemented";
+          // Processing a Dex `double-to-long' instruction.
+          __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pD2l)));
+          codegen_->RecordPcInfo(conversion, conversion->GetDexPc());
           break;
 
         default:
@@ -1694,8 +1726,8 @@
         }
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-float' instruction.
+          __ cvtsd2ss(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
           break;
 
         default:
@@ -1741,8 +1773,8 @@
         }
 
         case Primitive::kPrimFloat:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `float-to-double' instruction.
+          __ cvtss2sd(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
           break;
 
         default:
@@ -2015,6 +2047,81 @@
   }
 }
 
+void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, uint32_t temp_offset,
+                                                  uint32_t stack_adjustment, bool is_float) {
+  if (source.IsStackSlot()) {
+    DCHECK(is_float);
+    __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment));
+  } else if (source.IsDoubleStackSlot()) {
+    DCHECK(!is_float);
+    __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment));
+  } else {
+    // Write the value to the temporary location on the stack and load to FP stack.
+    if (is_float) {
+      Location stack_temp = Location::StackSlot(temp_offset);
+      codegen_->Move32(stack_temp, source);
+      __ flds(Address(ESP, temp_offset));
+    } else {
+      Location stack_temp = Location::DoubleStackSlot(temp_offset);
+      codegen_->Move64(stack_temp, source);
+      __ fldl(Address(ESP, temp_offset));
+    }
+  }
+}
+
+void InstructionCodeGeneratorX86::GenerateRemFP(HRem *rem) {
+  Primitive::Type type = rem->GetResultType();
+  bool is_float = type == Primitive::kPrimFloat;
+  size_t elem_size = Primitive::ComponentSize(type);
+  LocationSummary* locations = rem->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  Location out = locations->Out();
+
+  // Create stack space for 2 elements.
+  // TODO: enhance register allocator to ask for stack temporaries.
+  __ subl(ESP, Immediate(2 * elem_size));
+
+  // Load the values to the FP stack in reverse order, using temporaries if needed.
+  PushOntoFPStack(second, elem_size, 2 * elem_size, is_float);
+  PushOntoFPStack(first, 0, 2 * elem_size, is_float);
+
+  // Loop doing FPREM until we stabilize.
+  Label retry;
+  __ Bind(&retry);
+  __ fprem();
+
+  // Move FP status to AX.
+  __ fstsw();
+
+  // And see if the argument reduction is complete. This is signaled by the
+  // C2 FPU flag bit set to 0.
+  __ andl(EAX, Immediate(kC2ConditionMask));
+  __ j(kNotEqual, &retry);
+
+  // We have settled on the final value. Retrieve it into an XMM register.
+  // Store FP top of stack to real stack.
+  if (is_float) {
+    __ fsts(Address(ESP, 0));
+  } else {
+    __ fstl(Address(ESP, 0));
+  }
+
+  // Pop the 2 items from the FP stack.
+  __ fucompp();
+
+  // Load the value from the stack into an XMM register.
+  DCHECK(out.IsFpuRegister()) << out;
+  if (is_float) {
+    __ movss(out.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+  } else {
+    __ movsd(out.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+  }
+
+  // And remove the temporary stack space we allocated.
+  __ addl(ESP, Immediate(2 * elem_size));
+}
+
 void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) {
   DCHECK(instruction->IsDiv() || instruction->IsRem());
 
@@ -2147,12 +2254,11 @@
 }
 
 void LocationsBuilderX86::VisitRem(HRem* rem) {
-  LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong
-      ? LocationSummary::kCall
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
+  Primitive::Type type = rem->GetResultType();
+  LocationSummary* locations =
+    new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
 
-  switch (rem->GetResultType()) {
+  switch (type) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RegisterLocation(EAX));
       locations->SetInAt(1, Location::RequiresRegister());
@@ -2169,14 +2275,17 @@
       locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
       break;
     }
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble: {
-      LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimFloat: {
+      locations->SetInAt(0, Location::Any());
+      locations->SetInAt(1, Location::Any());
+      locations->SetOut(Location::RequiresFpuRegister());
+      locations->AddTemp(Location::RegisterLocation(EAX));
       break;
     }
 
     default:
-      LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+      LOG(FATAL) << "Unexpected rem type " << type;
   }
 }
 
@@ -2190,7 +2299,7 @@
     }
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
-      LOG(FATAL) << "Unimplemented rem type " << type;
+      GenerateRemFP(rem);
       break;
     }
     default:
@@ -2311,7 +2420,7 @@
           __ shrl(first_reg, second_reg);
         }
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
         if (op->IsShl()) {
           __ shll(first_reg, imm);
         } else if (op->IsShr()) {
@@ -2410,8 +2519,7 @@
   codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
   __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
 
-  __ fs()->call(
-      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
+  __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
 
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
@@ -2423,17 +2531,16 @@
   locations->SetOut(Location::RegisterLocation(EAX));
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+  codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
   __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
 
-  __ fs()->call(
-      Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck)));
+  __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
 
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
   DCHECK(!codegen_->IsLeafMethod());
@@ -2468,10 +2575,6 @@
   Location out = locations->Out();
   DCHECK(in.Equals(out));
   switch (not_->InputAt(0)->GetType()) {
-    case Primitive::kPrimBoolean:
-      __ xorl(out.AsRegister<Register>(), Immediate(1));
-      break;
-
     case Primitive::kPrimInt:
       __ notl(out.AsRegister<Register>());
       break;
@@ -2576,90 +2679,28 @@
   LOG(FATAL) << "Unreachable";
 }
 
-void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  Primitive::Type field_type = instruction->GetFieldType();
-  bool needs_write_barrier =
-    CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
-
-  bool is_byte_type = (field_type == Primitive::kPrimBoolean)
-      || (field_type == Primitive::kPrimByte);
-  // The register allocator does not support multiple
-  // inputs that die at entry with one in a specific register.
-  if (is_byte_type) {
-    // Ensure the value is in a byte register.
-    locations->SetInAt(1, Location::RegisterLocation(EAX));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
-  // Temporary registers for the write barrier.
-  if (needs_write_barrier) {
-    locations->AddTemp(Location::RequiresRegister());
-    // Ensure the card is in a byte register.
-    locations->AddTemp(Location::RegisterLocation(ECX));
+void InstructionCodeGeneratorX86::GenerateMemoryBarrier(MemBarrierKind kind) {
+  /*
+   * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
+   * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model.
+   * For those cases, all we need to ensure is that there is a scheduling barrier in place.
+   */
+  switch (kind) {
+    case MemBarrierKind::kAnyAny: {
+      __ mfence();
+      break;
+    }
+    case MemBarrierKind::kAnyStore:
+    case MemBarrierKind::kLoadAny:
+    case MemBarrierKind::kStoreStore: {
+      // nop
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected memory barrier " << kind;
   }
 }
 
-void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->GetFieldType();
-
-  switch (field_type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte: {
-      ByteRegister value = locations->InAt(1).AsRegister<ByteRegister>();
-      __ movb(Address(obj, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ movw(Address(obj, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ movl(Address(obj, offset), value);
-
-      if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-        Register card = locations->GetTemp(1).AsRegister<Register>();
-        codegen_->MarkGCCard(temp, card, obj, value);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      Location value = locations->InAt(1);
-      __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
-      __ movl(Address(obj, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movss(Address(obj, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movsd(Address(obj, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << field_type;
-      UNREACHABLE();
-  }
-}
 
 void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) {
   Label is_null;
@@ -2673,85 +2714,273 @@
   __ Bind(&is_null);
 }
 
-void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+  if (field_info.IsVolatile() && (field_info.GetFieldType() == Primitive::kPrimLong)) {
+    // Long values can be loaded atomically into an XMM using movsd.
+    // So we use an XMM register as a temp to achieve atomicity (first load the temp into the XMM
+    // and then copy the XMM into the output 32bits at a time).
+    locations->AddTemp(Location::RequiresFpuRegister());
+  }
 }
 
-void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register obj = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction,
+                                                 const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
 
-  switch (instruction->GetType()) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register base = locations->InAt(0).AsRegister<Register>();
+  Location out = locations->Out();
+  bool is_volatile = field_info.IsVolatile();
+  Primitive::Type field_type = field_info.GetFieldType();
+  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+  switch (field_type) {
     case Primitive::kPrimBoolean: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movzxb(out, Address(obj, offset));
+      __ movzxb(out.AsRegister<Register>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimByte: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movsxb(out, Address(obj, offset));
+      __ movsxb(out.AsRegister<Register>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimShort: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movsxw(out, Address(obj, offset));
+      __ movsxw(out.AsRegister<Register>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimChar: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movzxw(out, Address(obj, offset));
+      __ movzxw(out.AsRegister<Register>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movl(out, Address(obj, offset));
+      __ movl(out.AsRegister<Register>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimLong: {
-      // TODO: support volatile.
-      __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(obj, offset));
-      __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(obj, kX86WordSize + offset));
+      if (is_volatile) {
+        XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+        __ movsd(temp, Address(base, offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ movd(out.AsRegisterPairLow<Register>(), temp);
+        __ psrlq(temp, Immediate(32));
+        __ movd(out.AsRegisterPairHigh<Register>(), temp);
+      } else {
+        __ movl(out.AsRegisterPairLow<Register>(), Address(base, offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ movl(out.AsRegisterPairHigh<Register>(), Address(base, kX86WordSize + offset));
+      }
       break;
     }
 
     case Primitive::kPrimFloat: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movss(out, Address(obj, offset));
+      __ movss(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimDouble: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movsd(out, Address(obj, offset));
+      __ movsd(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
       break;
     }
 
     case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      LOG(FATAL) << "Unreachable type " << field_type;
       UNREACHABLE();
   }
+
+  // Longs are handled in the switch.
+  if (field_type != Primitive::kPrimLong) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+  }
+}
+
+void LocationsBuilderX86::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  bool is_volatile = field_info.IsVolatile();
+  Primitive::Type field_type = field_info.GetFieldType();
+  bool is_byte_type = (field_type == Primitive::kPrimBoolean)
+    || (field_type == Primitive::kPrimByte);
+
+  // The register allocator does not support multiple
+  // inputs that die at entry with one in a specific register.
+  if (is_byte_type) {
+    // Ensure the value is in a byte register.
+    locations->SetInAt(1, Location::RegisterLocation(EAX));
+  } else {
+    locations->SetInAt(1, Location::RequiresRegister());
+  }
+  // Temporary registers for the write barrier.
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+    locations->AddTemp(Location::RequiresRegister());
+    // Ensure the card is in a byte register.
+    locations->AddTemp(Location::RegisterLocation(ECX));
+  } else if (is_volatile && (field_type == Primitive::kPrimLong)) {
+    // 64bits value can be atomically written to an address with movsd and an XMM register.
+    // We need two XMM registers because there's no easier way to (bit) copy a register pair
+    // into a single XMM register (we copy each pair part into the XMMs and then interleave them).
+    // NB: We could make the register allocator understand fp_reg <-> core_reg moves but given the
+    // isolated cases when we need this it isn't worth adding the extra complexity.
+    locations->AddTemp(Location::RequiresFpuRegister());
+    locations->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction,
+                                                 const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
+  LocationSummary* locations = instruction->GetLocations();
+  Register base = locations->InAt(0).AsRegister<Register>();
+  Location value = locations->InAt(1);
+  bool is_volatile = field_info.IsVolatile();
+  Primitive::Type field_type = field_info.GetFieldType();
+  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+  }
+
+  switch (field_type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte: {
+      __ movb(Address(base, offset), value.AsRegister<ByteRegister>());
+      break;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimChar: {
+      __ movw(Address(base, offset), value.AsRegister<Register>());
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      __ movl(Address(base, offset), value.AsRegister<Register>());
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      if (is_volatile) {
+        XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+        XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
+        __ movd(temp1, value.AsRegisterPairLow<Register>());
+        __ movd(temp2, value.AsRegisterPairHigh<Register>());
+        __ punpckldq(temp1, temp2);
+        __ movsd(Address(base, offset), temp1);
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+      } else {
+        __ movl(Address(base, offset), value.AsRegisterPairLow<Register>());
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
+        __ movl(Address(base, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
+      }
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ movss(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ movsd(Address(base, offset), value.AsFpuRegister<XmmRegister>());
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
+  }
+
+  // Longs are handled in the switch.
+  if (field_type != Primitive::kPrimLong) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
+
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+    Register temp = locations->GetTemp(0).AsRegister<Register>();
+    Register card = locations->GetTemp(1).AsRegister<Register>();
+    codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>());
+  }
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+  }
+}
+
+void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
 }
 
 void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::Any());
+  Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+      ? Location::RequiresRegister()
+      : Location::Any();
+  locations->SetInAt(0, loc);
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
   }
 }
 
-void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) {
+  if (codegen_->CanMoveNullCheckToUser(instruction)) {
+    return;
+  }
+  LocationSummary* locations = instruction->GetLocations();
+  Location obj = locations->InAt(0);
+
+  __ testl(EAX, Address(obj.AsRegister<Register>(), 0));
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) {
   SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
   codegen_->AddSlowPath(slow_path);
 
@@ -2771,6 +3000,14 @@
   __ j(kEqual, slow_path->GetEntryLabel());
 }
 
+void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
+  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+    GenerateImplicitNullCheck(instruction);
+  } else {
+    GenerateExplicitNullCheck(instruction);
+  }
+}
+
 void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2784,7 +3021,8 @@
   Register obj = locations->InAt(0).AsRegister<Register>();
   Location index = locations->InAt(1);
 
-  switch (instruction->GetType()) {
+  Primitive::Type type = instruction->GetType();
+  switch (type) {
     case Primitive::kPrimBoolean: {
       uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
       Register out = locations->Out().AsRegister<Register>();
@@ -2852,24 +3090,50 @@
       if (index.IsConstant()) {
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         __ movl(out.AsRegisterPairLow<Register>(), Address(obj, offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
         __ movl(out.AsRegisterPairHigh<Register>(), Address(obj, offset + kX86WordSize));
       } else {
         __ movl(out.AsRegisterPairLow<Register>(),
                 Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset));
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
         __ movl(out.AsRegisterPairHigh<Register>(),
                 Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize));
       }
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-      UNREACHABLE();
+    case Primitive::kPrimFloat: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+      if (index.IsConstant()) {
+        __ movss(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset));
+      } else {
+        __ movss(out, Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset));
+      }
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+      if (index.IsConstant()) {
+        __ movsd(out, Address(obj,
+            (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset));
+      } else {
+        __ movsd(out, Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset));
+      }
+      break;
+    }
+
     case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
+      LOG(FATAL) << "Unreachable type " << type;
       UNREACHABLE();
   }
+
+  if (type != Primitive::kPrimLong) {
+    codegen_->MaybeRecordImplicitNullCheck(instruction);
+  }
 }
 
 void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
@@ -2946,6 +3210,7 @@
                   Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
         }
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2969,6 +3234,7 @@
                   Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
         }
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2997,6 +3263,7 @@
                     Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
           }
         }
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
 
         if (needs_write_barrier) {
           Register temp = locations->GetTemp(0).AsRegister<Register>();
@@ -3018,17 +3285,20 @@
         size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
         if (value.IsRegisterPair()) {
           __ movl(Address(obj, offset), value.AsRegisterPairLow<Register>());
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
           __ movl(Address(obj, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
         } else {
           DCHECK(value.IsConstant());
           int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
           __ movl(Address(obj, offset), Immediate(Low32Bits(val)));
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
           __ movl(Address(obj, offset + kX86WordSize), Immediate(High32Bits(val)));
         }
       } else {
         if (value.IsRegisterPair()) {
           __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
                   value.AsRegisterPairLow<Register>());
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
           __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
                   value.AsRegisterPairHigh<Register>());
         } else {
@@ -3036,6 +3306,7 @@
           int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
           __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
                   Immediate(Low32Bits(val)));
+          codegen_->MaybeRecordImplicitNullCheck(instruction);
           __ movl(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
                   Immediate(High32Bits(val)));
         }
@@ -3043,10 +3314,32 @@
       break;
     }
 
-    case Primitive::kPrimFloat:
-    case Primitive::kPrimDouble:
-      LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
-      UNREACHABLE();
+    case Primitive::kPrimFloat: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
+      DCHECK(value.IsFpuRegister());
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+        __ movss(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
+      } else {
+        __ movss(Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset),
+                value.AsFpuRegister<XmmRegister>());
+      }
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
+      DCHECK(value.IsFpuRegister());
+      if (index.IsConstant()) {
+        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+        __ movsd(Address(obj, offset), value.AsFpuRegister<XmmRegister>());
+      } else {
+        __ movsd(Address(obj, index.AsRegister<Register>(), TIMES_8, data_offset),
+                value.AsFpuRegister<XmmRegister>());
+      }
+      break;
+    }
+
     case Primitive::kPrimVoid:
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
       UNREACHABLE();
@@ -3066,6 +3359,7 @@
   Register obj = locations->InAt(0).AsRegister<Register>();
   Register out = locations->Out().AsRegister<Register>();
   __ movl(out, Address(obj, offset));
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -3147,12 +3441,24 @@
   return codegen_->GetAssembler();
 }
 
-void ParallelMoveResolverX86::MoveMemoryToMemory(int dst, int src) {
+void ParallelMoveResolverX86::MoveMemoryToMemory32(int dst, int src) {
   ScratchRegisterScope ensure_scratch(
       this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+  Register temp_reg = static_cast<Register>(ensure_scratch.GetRegister());
   int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
-  __ movl(static_cast<Register>(ensure_scratch.GetRegister()), Address(ESP, src + stack_offset));
-  __ movl(Address(ESP, dst + stack_offset), static_cast<Register>(ensure_scratch.GetRegister()));
+  __ movl(temp_reg, Address(ESP, src + stack_offset));
+  __ movl(Address(ESP, dst + stack_offset), temp_reg);
+}
+
+void ParallelMoveResolverX86::MoveMemoryToMemory64(int dst, int src) {
+  ScratchRegisterScope ensure_scratch(
+      this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+  Register temp_reg = static_cast<Register>(ensure_scratch.GetRegister());
+  int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
+  __ movl(temp_reg, Address(ESP, src + stack_offset));
+  __ movl(Address(ESP, dst + stack_offset), temp_reg);
+  __ movl(temp_reg, Address(ESP, src + stack_offset + kX86WordSize));
+  __ movl(Address(ESP, dst + stack_offset + kX86WordSize), temp_reg);
 }
 
 void ParallelMoveResolverX86::EmitMove(size_t index) {
@@ -3167,24 +3473,58 @@
       DCHECK(destination.IsStackSlot());
       __ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>());
     }
+  } else if (source.IsFpuRegister()) {
+    if (destination.IsFpuRegister()) {
+      __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+    } else if (destination.IsStackSlot()) {
+      __ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
+    } else {
+      DCHECK(destination.IsDoubleStackSlot());
+      __ movsd(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
+    }
   } else if (source.IsStackSlot()) {
     if (destination.IsRegister()) {
       __ movl(destination.AsRegister<Register>(), Address(ESP, source.GetStackIndex()));
+    } else if (destination.IsFpuRegister()) {
+      __ movss(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
     } else {
       DCHECK(destination.IsStackSlot());
-      MoveMemoryToMemory(destination.GetStackIndex(),
-                         source.GetStackIndex());
+      MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex());
+    }
+  } else if (source.IsDoubleStackSlot()) {
+    if (destination.IsFpuRegister()) {
+      __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
+    } else {
+      DCHECK(destination.IsDoubleStackSlot()) << destination;
+      MoveMemoryToMemory64(destination.GetStackIndex(), source.GetStackIndex());
     }
   } else if (source.IsConstant()) {
-    HIntConstant* instruction = source.GetConstant()->AsIntConstant();
-    Immediate imm(instruction->AsIntConstant()->GetValue());
-    if (destination.IsRegister()) {
-      __ movl(destination.AsRegister<Register>(), imm);
+    HConstant* constant = source.GetConstant();
+    if (constant->IsIntConstant()) {
+      Immediate imm(constant->AsIntConstant()->GetValue());
+      if (destination.IsRegister()) {
+        __ movl(destination.AsRegister<Register>(), imm);
+      } else {
+        DCHECK(destination.IsStackSlot()) << destination;
+        __ movl(Address(ESP, destination.GetStackIndex()), imm);
+      }
     } else {
-      __ movl(Address(ESP, destination.GetStackIndex()), imm);
+      DCHECK(constant->IsFloatConstant());
+      float value = constant->AsFloatConstant()->GetValue();
+      Immediate imm(bit_cast<float, int32_t>(value));
+      if (destination.IsFpuRegister()) {
+        ScratchRegisterScope ensure_scratch(
+            this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+        Register temp = static_cast<Register>(ensure_scratch.GetRegister());
+        __ movl(temp, imm);
+        __ movd(destination.AsFpuRegister<XmmRegister>(), temp);
+      } else {
+        DCHECK(destination.IsStackSlot()) << destination;
+        __ movl(Address(ESP, destination.GetStackIndex()), imm);
+      }
     }
   } else {
-    LOG(FATAL) << "Unimplemented";
+    LOG(FATAL) << "Unimplemented move: " << destination << " <- " << source;
   }
 }
 
@@ -3199,6 +3539,17 @@
   __ movl(reg, static_cast<Register>(ensure_scratch.GetRegister()));
 }
 
+void ParallelMoveResolverX86::Exchange32(XmmRegister reg, int mem) {
+  ScratchRegisterScope ensure_scratch(
+      this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
+
+  Register temp_reg = static_cast<Register>(ensure_scratch.GetRegister());
+  int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0;
+  __ movl(temp_reg, Address(ESP, mem + stack_offset));
+  __ movss(Address(ESP, mem + stack_offset), reg);
+  __ movd(reg, temp_reg);
+}
+
 void ParallelMoveResolverX86::Exchange(int mem1, int mem2) {
   ScratchRegisterScope ensure_scratch1(
       this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters());
@@ -3228,8 +3579,18 @@
     Exchange(destination.AsRegister<Register>(), source.GetStackIndex());
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
     Exchange(destination.GetStackIndex(), source.GetStackIndex());
+  } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
+    // Use XOR Swap algorithm to avoid a temporary.
+    DCHECK_NE(source.reg(), destination.reg());
+    __ xorpd(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+    __ xorpd(source.AsFpuRegister<XmmRegister>(), destination.AsFpuRegister<XmmRegister>());
+    __ xorpd(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+  } else if (source.IsFpuRegister() && destination.IsStackSlot()) {
+    Exchange32(source.AsFpuRegister<XmmRegister>(), destination.GetStackIndex());
+  } else if (destination.IsFpuRegister() && source.IsStackSlot()) {
+    Exchange32(destination.AsFpuRegister<XmmRegister>(), source.GetStackIndex());
   } else {
-    LOG(FATAL) << "Unimplemented";
+    LOG(FATAL) << "Unimplemented: source: " << source << ", destination: " << destination;
   }
 }
 
@@ -3303,159 +3664,6 @@
   // No need for memory fence, thanks to the X86 memory model.
 }
 
-void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register cls = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-
-  switch (instruction->GetType()) {
-    case Primitive::kPrimBoolean: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movzxb(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimByte: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movsxb(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimShort: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movsxw(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimChar: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movzxw(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      Register out = locations->Out().AsRegister<Register>();
-      __ movl(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      // TODO: support volatile.
-      __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(cls, offset));
-      __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(cls, kX86WordSize + offset));
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movss(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movsd(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  Primitive::Type field_type = instruction->GetFieldType();
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
-  bool is_byte_type = (field_type == Primitive::kPrimBoolean)
-      || (field_type == Primitive::kPrimByte);
-  // The register allocator does not support multiple
-  // inputs that die at entry with one in a specific register.
-  if (is_byte_type) {
-    // Ensure the value is in a byte register.
-    locations->SetInAt(1, Location::RegisterLocation(EAX));
-  } else {
-    locations->SetInAt(1, Location::RequiresRegister());
-  }
-  // Temporary registers for the write barrier.
-  if (needs_write_barrier) {
-    locations->AddTemp(Location::RequiresRegister());
-    // Ensure the card is in a byte register.
-    locations->AddTemp(Location::RegisterLocation(ECX));
-  }
-}
-
-void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  Register cls = locations->InAt(0).AsRegister<Register>();
-  uint32_t offset = instruction->GetFieldOffset().Uint32Value();
-  Primitive::Type field_type = instruction->GetFieldType();
-
-  switch (field_type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte: {
-      ByteRegister value = locations->InAt(1).AsRegister<ByteRegister>();
-      __ movb(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ movw(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      Register value = locations->InAt(1).AsRegister<Register>();
-      __ movl(Address(cls, offset), value);
-
-      if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
-        Register temp = locations->GetTemp(0).AsRegister<Register>();
-        Register card = locations->GetTemp(1).AsRegister<Register>();
-        codegen_->MarkGCCard(temp, card, cls, value);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      Location value = locations->InAt(1);
-      __ movl(Address(cls, offset), value.AsRegisterPairLow<Register>());
-      __ movl(Address(cls, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movss(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movsd(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << field_type;
-      UNREACHABLE();
-  }
-}
-
 void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index aed06c0..107ddaf 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -18,6 +18,8 @@
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_X86_H_
 
 #include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "parallel_move_resolver.h"
 #include "utils/x86/assembler_x86.h"
@@ -34,8 +36,8 @@
 static constexpr Register kParameterCoreRegisters[] = { ECX, EDX, EBX };
 static constexpr RegisterPair kParameterCorePairRegisters[] = { ECX_EDX, EDX_EBX };
 static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
-static constexpr XmmRegister kParameterFpuRegisters[] = { };
-static constexpr size_t kParameterFpuRegistersLength = 0;
+static constexpr XmmRegister kParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 };
+static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters);
 
 class InvokeDexCallingConvention : public CallingConvention<Register, XmmRegister> {
  public:
@@ -56,13 +58,18 @@
 
 class InvokeDexCallingConventionVisitor {
  public:
-  InvokeDexCallingConventionVisitor() : gp_index_(0) {}
+  InvokeDexCallingConventionVisitor() : gp_index_(0), fp_index_(0), stack_index_(0) {}
 
   Location GetNextLocation(Primitive::Type type);
 
  private:
   InvokeDexCallingConvention calling_convention;
+  // The current index for cpu registers.
   uint32_t gp_index_;
+  // The current index for fpu registers.
+  uint32_t fp_index_;
+  // The current stack index.
+  uint32_t stack_index_;
 
   DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
 };
@@ -82,7 +89,9 @@
  private:
   void Exchange(Register reg, int mem);
   void Exchange(int mem1, int mem2);
-  void MoveMemoryToMemory(int dst, int src);
+  void Exchange32(XmmRegister reg, int mem);
+  void MoveMemoryToMemory32(int dst, int src);
+  void MoveMemoryToMemory64(int dst, int src);
 
   CodeGeneratorX86* const codegen_;
 
@@ -105,6 +114,8 @@
   void HandleBitwiseOperation(HBinaryOperation* instruction);
   void HandleInvoke(HInvoke* invoke);
   void HandleShift(HBinaryOperation* instruction);
+  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
 
   CodeGeneratorX86* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -133,10 +144,19 @@
   void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg);
   void HandleBitwiseOperation(HBinaryOperation* instruction);
   void GenerateDivRemIntegral(HBinaryOperation* instruction);
+  void GenerateRemFP(HRem *rem);
   void HandleShift(HBinaryOperation* instruction);
   void GenerateShlLong(const Location& loc, Register shifter);
   void GenerateShrLong(const Location& loc, Register shifter);
   void GenerateUShrLong(const Location& loc, Register shifter);
+  void GenerateMemoryBarrier(MemBarrierKind kind);
+  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+  void PushOntoFPStack(Location source, uint32_t temp_offset,
+                       uint32_t stack_adjustment, bool is_float);
+
+  void GenerateImplicitNullCheck(HNullCheck* instruction);
+  void GenerateExplicitNullCheck(HNullCheck* instruction);
 
   X86Assembler* const assembler_;
   CodeGeneratorX86* const codegen_;
@@ -146,7 +166,7 @@
 
 class CodeGeneratorX86 : public CodeGenerator {
  public:
-  explicit CodeGeneratorX86(HGraph* graph);
+  CodeGeneratorX86(HGraph* graph, const CompilerOptions& compiler_options);
   virtual ~CodeGeneratorX86() {}
 
   void GenerateFrameEntry() OVERRIDE;
@@ -155,12 +175,17 @@
   void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
   size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
   size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+  size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
 
   size_t GetWordSize() const OVERRIDE {
     return kX86WordSize;
   }
 
-  size_t FrameEntrySpillSize() const OVERRIDE;
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+    // 8 bytes == 2 words for each spill.
+    return 2 * kX86WordSize;
+  }
 
   HGraphVisitor* GetLocationBuilder() OVERRIDE {
     return &location_builder_;
@@ -178,7 +203,7 @@
     return GetLabelOf(block)->Position();
   }
 
-  void SetupBlockedRegisters() const OVERRIDE;
+  void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
 
   Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
 
@@ -216,9 +241,16 @@
     block_labels_.SetSize(GetGraph()->GetBlocks().Size());
   }
 
+  bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+    return type == Primitive::kPrimLong;
+  }
+
+  Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
  private:
   // Labels for each block that will be compiled.
   GrowableArray<Label> block_labels_;
+  Label frame_entry_label_;
   LocationsBuilderX86 location_builder_;
   InstructionCodeGeneratorX86 instruction_visitor_;
   ParallelMoveResolverX86 move_resolver_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 47fd304..88f1753 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -18,6 +18,8 @@
 
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/accounting/card_table.h"
+#include "intrinsics.h"
+#include "intrinsics_x86_64.h"
 #include "mirror/array-inl.h"
 #include "mirror/art_method.h"
 #include "mirror/class.h"
@@ -32,19 +34,21 @@
 
 namespace x86_64 {
 
-static constexpr bool kExplicitStackOverflowCheck = false;
-
 // Some x86_64 instructions require a register to be available as temp.
 static constexpr Register TMP = R11;
 
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
 static constexpr int kCurrentMethodStackOffset = 0;
 
 static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX };
 static constexpr size_t kRuntimeParameterCoreRegistersLength =
     arraysize(kRuntimeParameterCoreRegisters);
-static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { };
-static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1 };
+static constexpr size_t kRuntimeParameterFpuRegistersLength =
+    arraysize(kRuntimeParameterFpuRegisters);
+static constexpr Register kCoreCalleeSaves[] = { RBX, RBP, R12, R13, R14, R15 };
+static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15 };
+
+static constexpr int kC2ConditionMask = 0x400;
 
 class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> {
  public:
@@ -60,20 +64,6 @@
 
 #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
 
-class SlowPathCodeX86_64 : public SlowPathCode {
- public:
-  SlowPathCodeX86_64() : entry_label_(), exit_label_() {}
-
-  Label* GetEntryLabel() { return &entry_label_; }
-  Label* GetExitLabel() { return &exit_label_; }
-
- private:
-  Label entry_label_;
-  Label exit_label_;
-
-  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64);
-};
-
 class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
@@ -138,22 +128,6 @@
   DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86_64);
 };
 
-class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
- public:
-  StackOverflowCheckSlowPathX86_64() {}
-
-  virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
-    __ Bind(GetEntryLabel());
-    __ addq(CpuRegister(RSP),
-            Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
-    __ gs()->jmp(
-        Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowStackOverflow), true));
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
-};
-
 class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
  public:
   explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
@@ -284,8 +258,8 @@
     codegen->SaveLiveRegisters(locations);
 
     InvokeRuntimeCallingConvention calling_convention;
-    x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(0)));
-    __ movl(CpuRegister(calling_convention.GetRegisterAt(1)),
+    x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+    __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
             Immediate(instruction_->GetStringIndex()));
     __ gs()->call(Address::Absolute(
         QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pResolveString), true));
@@ -374,6 +348,35 @@
   return kEqual;
 }
 
+void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
+                                                     CpuRegister temp) {
+  // All registers are assumed to be correctly set up.
+
+  // TODO: Implement all kinds of calls:
+  // 1) boot -> boot
+  // 2) app -> boot
+  // 3) app -> app
+  //
+  // Currently we implement the app -> app logic, which looks up in the resolve cache.
+
+  // temp = method;
+  LoadCurrentMethod(temp);
+  if (!invoke->IsRecursive()) {
+    // temp = temp->dex_cache_resolved_methods_;
+    __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+    // temp = temp[index_in_cache]
+    __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())));
+    // (temp + offset_of_quick_compiled_code)()
+    __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+        kX86_64WordSize).SizeValue()));
+  } else {
+    __ call(&frame_entry_label_);
+  }
+
+  DCHECK(!IsLeafMethod());
+  RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
 void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
   stream << X86_64ManagedRegister::FromCpuRegister(Register(reg));
 }
@@ -402,15 +405,25 @@
   return kX86_64WordSize;
 }
 
-CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
-      : CodeGenerator(graph, kNumberOfCpuRegisters, kNumberOfFloatRegisters, 0),
+static constexpr int kNumberOfCpuRegisterPairs = 0;
+// Use a fake return address register to mimic Quick.
+static constexpr Register kFakeReturnRegister = Register(kLastCpuRegister + 1);
+CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, const CompilerOptions& compiler_options)
+      : CodeGenerator(graph,
+                      kNumberOfCpuRegisters,
+                      kNumberOfFloatRegisters,
+                      kNumberOfCpuRegisterPairs,
+                      ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
+                                          arraysize(kCoreCalleeSaves))
+                          | (1 << kFakeReturnRegister),
+                      ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
+                                          arraysize(kFpuCalleeSaves)),
+                      compiler_options),
         block_labels_(graph->GetArena(), 0),
         location_builder_(graph, this),
         instruction_visitor_(graph, this),
-        move_resolver_(graph->GetArena(), this) {}
-
-size_t CodeGeneratorX86_64::FrameEntrySpillSize() const {
-  return kNumberOfPushedRegistersAtEntry * kX86_64WordSize;
+        move_resolver_(graph->GetArena(), this) {
+  AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
 }
 
 InstructionCodeGeneratorX86_64::InstructionCodeGeneratorX86_64(HGraph* graph,
@@ -445,60 +458,81 @@
   return Location();
 }
 
-void CodeGeneratorX86_64::SetupBlockedRegisters() const {
+void CodeGeneratorX86_64::SetupBlockedRegisters(bool is_baseline) const {
   // Stack register is always reserved.
   blocked_core_registers_[RSP] = true;
 
   // Block the register used as TMP.
   blocked_core_registers_[TMP] = true;
 
-  // TODO: We currently don't use Quick's callee saved registers.
-  blocked_core_registers_[RBX] = true;
-  blocked_core_registers_[RBP] = true;
-  blocked_core_registers_[R12] = true;
-  blocked_core_registers_[R13] = true;
-  blocked_core_registers_[R14] = true;
-  blocked_core_registers_[R15] = true;
-
-  blocked_fpu_registers_[XMM12] = true;
-  blocked_fpu_registers_[XMM13] = true;
-  blocked_fpu_registers_[XMM14] = true;
-  blocked_fpu_registers_[XMM15] = true;
+  if (is_baseline) {
+    for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+      blocked_core_registers_[kCoreCalleeSaves[i]] = true;
+    }
+    for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+      blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
+    }
+  }
 }
 
 void CodeGeneratorX86_64::GenerateFrameEntry() {
-  // Create a fake register to mimic Quick.
-  static const int kFakeReturnRegister = 16;
-  core_spill_mask_ |= (1 << kFakeReturnRegister);
-
+  __ Bind(&frame_entry_label_);
   bool skip_overflow_check = IsLeafMethod()
       && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
+  DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
 
-  if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+  if (!skip_overflow_check) {
     __ testq(CpuRegister(RAX), Address(
         CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
     RecordPcInfo(nullptr, 0);
   }
 
-  // The return PC has already been pushed on the stack.
-  __ subq(CpuRegister(RSP),
-          Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+  if (HasEmptyFrame()) {
+    return;
+  }
 
-  if (!skip_overflow_check && kExplicitStackOverflowCheck) {
-    SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
-    AddSlowPath(slow_path);
+  for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
+    Register reg = kCoreCalleeSaves[i];
+    if (allocated_registers_.ContainsCoreRegister(reg)) {
+      __ pushq(CpuRegister(reg));
+    }
+  }
 
-    __ gs()->cmpq(CpuRegister(RSP),
-                  Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
-    __ j(kLess, slow_path->GetEntryLabel());
+  __ subq(CpuRegister(RSP), Immediate(GetFrameSize() - GetCoreSpillSize()));
+  uint32_t xmm_spill_location = GetFpuSpillStart();
+  size_t xmm_spill_slot_size = GetFloatingPointSpillSlotSize();
+
+  for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) {
+    if (allocated_registers_.ContainsFloatingPointRegister(kFpuCalleeSaves[i])) {
+      __ movsd(Address(CpuRegister(RSP), xmm_spill_location + (xmm_spill_slot_size * i)),
+               XmmRegister(kFpuCalleeSaves[i]));
+    }
   }
 
   __ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
 }
 
 void CodeGeneratorX86_64::GenerateFrameExit() {
-  __ addq(CpuRegister(RSP),
-          Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+  if (HasEmptyFrame()) {
+    return;
+  }
+  uint32_t xmm_spill_location = GetFpuSpillStart();
+  size_t xmm_spill_slot_size = GetFloatingPointSpillSlotSize();
+  for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+    if (allocated_registers_.ContainsFloatingPointRegister(kFpuCalleeSaves[i])) {
+      __ movsd(XmmRegister(kFpuCalleeSaves[i]),
+               Address(CpuRegister(RSP), xmm_spill_location + (xmm_spill_slot_size * i)));
+    }
+  }
+
+  __ addq(CpuRegister(RSP), Immediate(GetFrameSize() - GetCoreSpillSize()));
+
+  for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
+    Register reg = kCoreCalleeSaves[i];
+    if (allocated_registers_.ContainsCoreRegister(reg)) {
+      __ popq(CpuRegister(reg));
+    }
+  }
 }
 
 void CodeGeneratorX86_64::Bind(HBasicBlock* block) {
@@ -506,6 +540,7 @@
 }
 
 void CodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
+  DCHECK(RequiresCurrentMethod());
   __ movl(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
 }
 
@@ -570,8 +605,18 @@
     } else if (source.IsFpuRegister()) {
       __ movss(Address(CpuRegister(RSP), destination.GetStackIndex()),
                source.AsFpuRegister<XmmRegister>());
+    } else if (source.IsConstant()) {
+      HConstant* constant = source.GetConstant();
+      int32_t value;
+      if (constant->IsFloatConstant()) {
+        value = bit_cast<float, int32_t>(constant->AsFloatConstant()->GetValue());
+      } else {
+        DCHECK(constant->IsIntConstant());
+        value = constant->AsIntConstant()->GetValue();
+      }
+      __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), Immediate(value));
     } else {
-      DCHECK(source.IsStackSlot());
+      DCHECK(source.IsStackSlot()) << source;
       __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
       __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
     }
@@ -583,6 +628,17 @@
     } else if (source.IsFpuRegister()) {
       __ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()),
                source.AsFpuRegister<XmmRegister>());
+    } else if (source.IsConstant()) {
+      HConstant* constant = source.GetConstant();
+      int64_t value = constant->AsLongConstant()->GetValue();
+      if (constant->IsDoubleConstant()) {
+        value = bit_cast<double, int64_t>(constant->AsDoubleConstant()->GetValue());
+      } else {
+        DCHECK(constant->IsLongConstant());
+        value = constant->AsLongConstant()->GetValue();
+      }
+      __ movq(CpuRegister(TMP), Immediate(value));
+      __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
     } else {
       DCHECK(source.IsDoubleStackSlot());
       __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
@@ -742,7 +798,7 @@
         // Materialized condition, compare against 0.
         Location lhs = if_instr->GetLocations()->InAt(0);
         if (lhs.IsRegister()) {
-          __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(0));
+          __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
         } else {
           __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()),
                   Immediate(0));
@@ -758,8 +814,12 @@
       if (rhs.IsRegister()) {
         __ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
       } else if (rhs.IsConstant()) {
-        __ cmpl(lhs.AsRegister<CpuRegister>(),
-                Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
+        int32_t constant = rhs.GetConstant()->AsIntConstant()->GetValue();
+        if (constant == 0) {
+          __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
+        } else {
+          __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
+        }
       } else {
         __ cmpl(lhs.AsRegister<CpuRegister>(),
                 Address(CpuRegister(RSP), rhs.GetStackIndex()));
@@ -835,15 +895,19 @@
     CpuRegister reg = locations->Out().AsRegister<CpuRegister>();
     // Clear register: setcc only sets the low byte.
     __ xorq(reg, reg);
-    if (locations->InAt(1).IsRegister()) {
-      __ cmpl(locations->InAt(0).AsRegister<CpuRegister>(),
-              locations->InAt(1).AsRegister<CpuRegister>());
-    } else if (locations->InAt(1).IsConstant()) {
-      __ cmpl(locations->InAt(0).AsRegister<CpuRegister>(),
-              Immediate(locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()));
+    Location lhs = locations->InAt(0);
+    Location rhs = locations->InAt(1);
+    if (rhs.IsRegister()) {
+      __ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
+    } else if (rhs.IsConstant()) {
+      int32_t constant = rhs.GetConstant()->AsIntConstant()->GetValue();
+      if (constant == 0) {
+        __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
+      } else {
+        __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
+      }
     } else {
-      __ cmpl(locations->InAt(0).AsRegister<CpuRegister>(),
-              Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+      __ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
     }
     __ setcc(X86_64Condition(comp->GetCondition()), reg);
   }
@@ -1121,31 +1185,32 @@
   return Location();
 }
 
-void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+  IntrinsicLocationsBuilderX86_64 intrinsic(GetGraph()->GetArena());
+  if (intrinsic.TryDispatch(invoke)) {
+    return;
+  }
+
   HandleInvoke(invoke);
 }
 
-void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
-  CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
-  // TODO: Implement all kinds of calls:
-  // 1) boot -> boot
-  // 2) app -> boot
-  // 3) app -> app
-  //
-  // Currently we implement the app -> app logic, which looks up in the resolve cache.
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86_64* codegen) {
+  if (invoke->GetLocations()->Intrinsified()) {
+    IntrinsicCodeGeneratorX86_64 intrinsic(codegen);
+    intrinsic.Dispatch(invoke);
+    return true;
+  }
+  return false;
+}
 
-  // temp = method;
-  codegen_->LoadCurrentMethod(temp);
-  // temp = temp->dex_cache_resolved_methods_;
-  __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
-  // temp = temp[index_in_cache]
-  __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
-  // (temp + offset_of_quick_compiled_code)()
-  __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
-      kX86_64WordSize).SizeValue()));
+void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
 
-  DCHECK(!codegen_->IsLeafMethod());
-  codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+  codegen_->GenerateStaticOrDirectCall(
+      invoke,
+      invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>());
 }
 
 void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) {
@@ -1181,10 +1246,19 @@
 }
 
 void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  IntrinsicLocationsBuilderX86_64 intrinsic(GetGraph()->GetArena());
+  if (intrinsic.TryDispatch(invoke)) {
+    return;
+  }
+
   HandleInvoke(invoke);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+  if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+    return;
+  }
+
   CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
   size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() +
           invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
@@ -1198,6 +1272,7 @@
   } else {
     __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetMethodAt(method_offset);
   __ movl(temp, Address(temp, method_offset));
   // call temp->GetEntryPoint();
@@ -1234,6 +1309,7 @@
   } else {
     __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
   }
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
   // temp = temp->GetImtEntryAt(method_offset);
   __ movl(temp, Address(temp, method_offset));
   // call temp->GetEntryPoint();
@@ -1370,8 +1446,10 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-int' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
           break;
 
         default:
@@ -1394,9 +1472,17 @@
           break;
 
         case Primitive::kPrimFloat:
+          // Processing a Dex `float-to-long' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
+          break;
+
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type << " to "
-                     << result_type << " not yet implemented";
+          // Processing a Dex `double-to-long' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresRegister());
+          locations->AddTemp(Location::RequiresFpuRegister());
           break;
 
         default:
@@ -1439,8 +1525,9 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-float' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
           break;
 
         default:
@@ -1467,8 +1554,9 @@
           break;
 
         case Primitive::kPrimFloat:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `float-to-double' instruction.
+          locations->SetInAt(0, Location::RequiresFpuRegister());
+          locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
           break;
 
         default:
@@ -1565,14 +1653,14 @@
 
           __ movl(output, Immediate(kPrimIntMax));
           // temp = int-to-float(output)
-          __ cvtsi2ss(temp, output);
+          __ cvtsi2ss(temp, output, false);
           // if input >= temp goto done
           __ comiss(input, temp);
           __ j(kAboveEqual, &done);
           // if input == NaN goto nan
           __ j(kUnordered, &nan);
           // output = float-to-int-truncate(input)
-          __ cvttss2si(output, input);
+          __ cvttss2si(output, input, false);
           __ jmp(&done);
           __ Bind(&nan);
           //  output = 0
@@ -1581,10 +1669,30 @@
           break;
         }
 
-        case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-int' instruction.
+          XmmRegister input = in.AsFpuRegister<XmmRegister>();
+          CpuRegister output = out.AsRegister<CpuRegister>();
+          XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          Label done, nan;
+
+          __ movl(output, Immediate(kPrimIntMax));
+          // temp = int-to-double(output)
+          __ cvtsi2sd(temp, output);
+          // if input >= temp goto done
+          __ comisd(input, temp);
+          __ j(kAboveEqual, &done);
+          // if input == NaN goto nan
+          __ j(kUnordered, &nan);
+          // output = double-to-int-truncate(input)
+          __ cvttsd2si(output, input);
+          __ jmp(&done);
+          __ Bind(&nan);
+          //  output = 0
+          __ xorl(output, output);
+          __ Bind(&done);
           break;
+        }
 
         default:
           LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1604,11 +1712,55 @@
           __ movsxd(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
           break;
 
-        case Primitive::kPrimFloat:
-        case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type << " to "
-                     << result_type << " not yet implemented";
+        case Primitive::kPrimFloat: {
+          // Processing a Dex `float-to-long' instruction.
+          XmmRegister input = in.AsFpuRegister<XmmRegister>();
+          CpuRegister output = out.AsRegister<CpuRegister>();
+          XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          Label done, nan;
+
+          __ movq(output, Immediate(kPrimLongMax));
+          // temp = long-to-float(output)
+          __ cvtsi2ss(temp, output, true);
+          // if input >= temp goto done
+          __ comiss(input, temp);
+          __ j(kAboveEqual, &done);
+          // if input == NaN goto nan
+          __ j(kUnordered, &nan);
+          // output = float-to-long-truncate(input)
+          __ cvttss2si(output, input, true);
+          __ jmp(&done);
+          __ Bind(&nan);
+          //  output = 0
+          __ xorq(output, output);
+          __ Bind(&done);
           break;
+        }
+
+        case Primitive::kPrimDouble: {
+          // Processing a Dex `double-to-long' instruction.
+          XmmRegister input = in.AsFpuRegister<XmmRegister>();
+          CpuRegister output = out.AsRegister<CpuRegister>();
+          XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          Label done, nan;
+
+          __ movq(output, Immediate(kPrimLongMax));
+          // temp = long-to-double(output)
+          __ cvtsi2sd(temp, output, true);
+          // if input >= temp goto done
+          __ comisd(input, temp);
+          __ j(kAboveEqual, &done);
+          // if input == NaN goto nan
+          __ j(kUnordered, &nan);
+          // output = double-to-long-truncate(input)
+          __ cvttsd2si(output, input, true);
+          __ jmp(&done);
+          __ Bind(&nan);
+          //  output = 0
+          __ xorq(output, output);
+          __ Bind(&done);
+          break;
+        }
 
         default:
           LOG(FATAL) << "Unexpected type conversion from " << input_type
@@ -1656,8 +1808,8 @@
           break;
 
         case Primitive::kPrimDouble:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `double-to-float' instruction.
+          __ cvtsd2ss(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
           break;
 
         default:
@@ -1682,8 +1834,8 @@
           break;
 
         case Primitive::kPrimFloat:
-          LOG(FATAL) << "Type conversion from " << input_type
-                     << " to " << result_type << " not yet implemented";
+          // Processing a Dex `float-to-double' instruction.
+          __ cvtss2sd(out.AsFpuRegister<XmmRegister>(), in.AsFpuRegister<XmmRegister>());
           break;
 
         default:
@@ -1704,8 +1856,8 @@
   switch (add->GetResultType()) {
     case Primitive::kPrimInt: {
       locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetInAt(1, Location::Any());
-      locations->SetOut(Location::SameAsFirstInput());
+      locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
+      locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
       break;
     }
 
@@ -1733,16 +1885,27 @@
   LocationSummary* locations = add->GetLocations();
   Location first = locations->InAt(0);
   Location second = locations->InAt(1);
-  DCHECK(first.Equals(locations->Out()));
+  Location out = locations->Out();
 
   switch (add->GetResultType()) {
     case Primitive::kPrimInt: {
       if (second.IsRegister()) {
-        __ addl(first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+        if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
+          __ addl(out.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+        } else {
+          __ leal(out.AsRegister<CpuRegister>(), Address(
+              first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>(), TIMES_1, 0));
+        }
       } else if (second.IsConstant()) {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
-        __ addl(first.AsRegister<CpuRegister>(), imm);
+        if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
+          __ addl(out.AsRegister<CpuRegister>(),
+                  Immediate(second.GetConstant()->AsIntConstant()->GetValue()));
+        } else {
+          __ leal(out.AsRegister<CpuRegister>(), Address(
+              first.AsRegister<CpuRegister>(), second.GetConstant()->AsIntConstant()->GetValue()));
+        }
       } else {
+        DCHECK(first.Equals(locations->Out()));
         __ addl(first.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), second.GetStackIndex()));
       }
       break;
@@ -1901,6 +2064,81 @@
   }
 }
 
+void InstructionCodeGeneratorX86_64::PushOntoFPStack(Location source, uint32_t temp_offset,
+                                                     uint32_t stack_adjustment, bool is_float) {
+  if (source.IsStackSlot()) {
+    DCHECK(is_float);
+    __ flds(Address(CpuRegister(RSP), source.GetStackIndex() + stack_adjustment));
+  } else if (source.IsDoubleStackSlot()) {
+    DCHECK(!is_float);
+    __ fldl(Address(CpuRegister(RSP), source.GetStackIndex() + stack_adjustment));
+  } else {
+    // Write the value to the temporary location on the stack and load to FP stack.
+    if (is_float) {
+      Location stack_temp = Location::StackSlot(temp_offset);
+      codegen_->Move(stack_temp, source);
+      __ flds(Address(CpuRegister(RSP), temp_offset));
+    } else {
+      Location stack_temp = Location::DoubleStackSlot(temp_offset);
+      codegen_->Move(stack_temp, source);
+      __ fldl(Address(CpuRegister(RSP), temp_offset));
+    }
+  }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateRemFP(HRem *rem) {
+  Primitive::Type type = rem->GetResultType();
+  bool is_float = type == Primitive::kPrimFloat;
+  size_t elem_size = Primitive::ComponentSize(type);
+  LocationSummary* locations = rem->GetLocations();
+  Location first = locations->InAt(0);
+  Location second = locations->InAt(1);
+  Location out = locations->Out();
+
+  // Create stack space for 2 elements.
+  // TODO: enhance register allocator to ask for stack temporaries.
+  __ subq(CpuRegister(RSP), Immediate(2 * elem_size));
+
+  // Load the values to the FP stack in reverse order, using temporaries if needed.
+  PushOntoFPStack(second, elem_size, 2 * elem_size, is_float);
+  PushOntoFPStack(first, 0, 2 * elem_size, is_float);
+
+  // Loop doing FPREM until we stabilize.
+  Label retry;
+  __ Bind(&retry);
+  __ fprem();
+
+  // Move FP status to AX.
+  __ fstsw();
+
+  // And see if the argument reduction is complete. This is signaled by the
+  // C2 FPU flag bit set to 0.
+  __ andl(CpuRegister(RAX), Immediate(kC2ConditionMask));
+  __ j(kNotEqual, &retry);
+
+  // We have settled on the final value. Retrieve it into an XMM register.
+  // Store FP top of stack to real stack.
+  if (is_float) {
+    __ fsts(Address(CpuRegister(RSP), 0));
+  } else {
+    __ fstl(Address(CpuRegister(RSP), 0));
+  }
+
+  // Pop the 2 items from the FP stack.
+  __ fucompp();
+
+  // Load the value from the stack into an XMM register.
+  DCHECK(out.IsFpuRegister()) << out;
+  if (is_float) {
+    __ movss(out.AsFpuRegister<XmmRegister>(), Address(CpuRegister(RSP), 0));
+  } else {
+    __ movsd(out.AsFpuRegister<XmmRegister>(), Address(CpuRegister(RSP), 0));
+  }
+
+  // And remove the temporary stack space we allocated.
+  __ addq(CpuRegister(RSP), Immediate(2 * elem_size));
+}
+
 void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
   DCHECK(instruction->IsDiv() || instruction->IsRem());
   Primitive::Type type = instruction->GetResultType();
@@ -1923,16 +2161,16 @@
   // 0x80000000(00000000)/-1 triggers an arithmetic exception!
   // Dividing by -1 is actually negation and -0x800000000(00000000) = 0x80000000(00000000)
   // so it's safe to just use negl instead of more complex comparisons.
-
-  __ cmpl(second_reg, Immediate(-1));
-  __ j(kEqual, slow_path->GetEntryLabel());
-
   if (type == Primitive::kPrimInt) {
+    __ cmpl(second_reg, Immediate(-1));
+    __ j(kEqual, slow_path->GetEntryLabel());
     // edx:eax <- sign-extended of eax
     __ cdq();
     // eax = quotient, edx = remainder
     __ idivl(second_reg);
   } else {
+    __ cmpq(second_reg, Immediate(-1));
+    __ j(kEqual, slow_path->GetEntryLabel());
     // rdx:rax <- sign-extended of rax
     __ cqo();
     // rax = quotient, rdx = remainder
@@ -1999,9 +2237,11 @@
 }
 
 void LocationsBuilderX86_64::VisitRem(HRem* rem) {
+  Primitive::Type type = rem->GetResultType();
   LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
-  switch (rem->GetResultType()) {
+    new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
+
+  switch (type) {
     case Primitive::kPrimInt:
     case Primitive::kPrimLong: {
       locations->SetInAt(0, Location::RegisterLocation(RAX));
@@ -2013,12 +2253,15 @@
 
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
-      LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+      locations->SetInAt(0, Location::Any());
+      locations->SetInAt(1, Location::Any());
+      locations->SetOut(Location::RequiresFpuRegister());
+      locations->AddTemp(Location::RegisterLocation(RAX));
       break;
     }
 
     default:
-      LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+      LOG(FATAL) << "Unexpected rem type " << type;
   }
 }
 
@@ -2030,13 +2273,11 @@
       GenerateDivRemIntegral(rem);
       break;
     }
-
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble: {
-      LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+      GenerateRemFP(rem);
       break;
     }
-
     default:
       LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
   }
@@ -2134,7 +2375,7 @@
           __ shrl(first_reg, second_reg);
         }
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue);
         if (op->IsShl()) {
           __ shll(first_reg, imm);
         } else if (op->IsShr()) {
@@ -2156,7 +2397,7 @@
           __ shrq(first_reg, second_reg);
         }
       } else {
-        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
+        Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue);
         if (op->IsShl()) {
           __ shlq(first_reg, imm);
         } else if (op->IsShr()) {
@@ -2210,8 +2451,8 @@
   codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
   __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
 
-  __ gs()->call(Address::Absolute(
-      QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
+  __ gs()->call(
+      Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2222,18 +2463,18 @@
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
   InvokeRuntimeCallingConvention calling_convention;
   locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
-  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+  locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
   locations->SetOut(Location::RegisterLocation(RAX));
-  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+  locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
 }
 
 void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
   InvokeRuntimeCallingConvention calling_convention;
-  codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+  codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(2)));
   __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
 
-  __ gs()->call(Address::Absolute(
-      QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true));
+  __ gs()->call(
+      Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
 
   DCHECK(!codegen_->IsLeafMethod());
   codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2269,10 +2510,6 @@
             locations->Out().AsRegister<CpuRegister>().AsRegister());
   Location out = locations->Out();
   switch (not_->InputAt(0)->GetType()) {
-    case Primitive::kPrimBoolean:
-      __ xorq(out.AsRegister<CpuRegister>(), Immediate(1));
-      break;
-
     case Primitive::kPrimInt:
       __ notl(out.AsRegister<CpuRegister>());
       break;
@@ -2300,12 +2537,111 @@
   LOG(FATAL) << "Unimplemented";
 }
 
-void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) {
+  /*
+   * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
+   * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model.
+   * For those cases, all we need to ensure is that there is a scheduling barrier in place.
+   */
+  switch (kind) {
+    case MemBarrierKind::kAnyAny: {
+      __ mfence();
+      break;
+    }
+    case MemBarrierKind::kAnyStore:
+    case MemBarrierKind::kLoadAny:
+    case MemBarrierKind::kStoreStore: {
+      // nop
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unexpected memory barier " << kind;
+  }
+}
+
+void LocationsBuilderX86_64::HandleFieldGet(HInstruction* instruction) {
+  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
+
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  Primitive::Type field_type = instruction->GetFieldType();
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
+                                                    const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
+
+  LocationSummary* locations = instruction->GetLocations();
+  CpuRegister base = locations->InAt(0).AsRegister<CpuRegister>();
+  Location out = locations->Out();
+  bool is_volatile = field_info.IsVolatile();
+  Primitive::Type field_type = field_info.GetFieldType();
+  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+  switch (field_type) {
+    case Primitive::kPrimBoolean: {
+      __ movzxb(out.AsRegister<CpuRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimByte: {
+      __ movsxb(out.AsRegister<CpuRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimShort: {
+      __ movsxw(out.AsRegister<CpuRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimChar: {
+      __ movzxw(out.AsRegister<CpuRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      __ movl(out.AsRegister<CpuRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimLong: {
+      __ movq(out.AsRegister<CpuRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimFloat: {
+      __ movss(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimDouble: {
+      __ movsd(out.AsFpuRegister<XmmRegister>(), Address(base, offset));
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unreachable type " << field_type;
+      UNREACHABLE();
+  }
+
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+  }
+}
+
+void LocationsBuilderX86_64::HandleFieldSet(HInstruction* instruction,
+                                            const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
   bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue());
+      CodeGenerator::StoreNeedsWriteBarrier(field_info.GetFieldType(), instruction->InputAt(1));
+
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
   if (needs_write_barrier) {
@@ -2315,54 +2651,52 @@
   }
 }
 
-void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
+                                                    const FieldInfo& field_info) {
+  DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
+
   LocationSummary* locations = instruction->GetLocations();
-  CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
-  size_t offset = instruction->GetFieldOffset().SizeValue();
-  Primitive::Type field_type = instruction->GetFieldType();
+  CpuRegister base = locations->InAt(0).AsRegister<CpuRegister>();
+  Location value = locations->InAt(1);
+  bool is_volatile = field_info.IsVolatile();
+  Primitive::Type field_type = field_info.GetFieldType();
+  uint32_t offset = field_info.GetFieldOffset().Uint32Value();
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+  }
 
   switch (field_type) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movb(Address(obj, offset), value);
+      __ movb(Address(base, offset), value.AsRegister<CpuRegister>());
       break;
     }
 
     case Primitive::kPrimShort:
     case Primitive::kPrimChar: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movw(Address(obj, offset), value);
+      __ movw(Address(base, offset), value.AsRegister<CpuRegister>());
       break;
     }
 
     case Primitive::kPrimInt:
     case Primitive::kPrimNot: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movl(Address(obj, offset), value);
-      if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
-        CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
-        CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
-        codegen_->MarkGCCard(temp, card, obj, value);
-      }
+      __ movl(Address(base, offset), value.AsRegister<CpuRegister>());
       break;
     }
 
     case Primitive::kPrimLong: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movq(Address(obj, offset), value);
+      __ movq(Address(base, offset), value.AsRegister<CpuRegister>());
       break;
     }
 
     case Primitive::kPrimFloat: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movss(Address(obj, offset), value);
+      __ movss(Address(base, offset), value.AsFpuRegister<XmmRegister>());
       break;
     }
 
     case Primitive::kPrimDouble: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movsd(Address(obj, offset), value);
+      __ movsd(Address(base, offset), value.AsFpuRegister<XmmRegister>());
       break;
     }
 
@@ -2370,86 +2704,76 @@
       LOG(FATAL) << "Unreachable type " << field_type;
       UNREACHABLE();
   }
+
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
+
+  if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+    CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+    CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
+    codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>());
+  }
+
+  if (is_volatile) {
+    GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+  }
+}
+
+void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
 }
 
 void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+  HandleFieldGet(instruction);
 }
 
 void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
-  size_t offset = instruction->GetFieldOffset().SizeValue();
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
 
-  switch (instruction->GetType()) {
-    case Primitive::kPrimBoolean: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movzxb(out, Address(obj, offset));
-      break;
-    }
+void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  HandleFieldGet(instruction);
+}
 
-    case Primitive::kPrimByte: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movsxb(out, Address(obj, offset));
-      break;
-    }
+void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+  HandleFieldGet(instruction, instruction->GetFieldInfo());
+}
 
-    case Primitive::kPrimShort: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movsxw(out, Address(obj, offset));
-      break;
-    }
+void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
 
-    case Primitive::kPrimChar: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movzxw(out, Address(obj, offset));
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movl(out, Address(obj, offset));
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movq(out, Address(obj, offset));
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movss(out, Address(obj, offset));
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movsd(out, Address(obj, offset));
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
-      UNREACHABLE();
-  }
+void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+  HandleFieldSet(instruction, instruction->GetFieldInfo());
 }
 
 void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::Any());
+  Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks()
+      ? Location::RequiresRegister()
+      : Location::Any();
+  locations->SetInAt(0, loc);
   if (instruction->HasUses()) {
     locations->SetOut(Location::SameAsFirstInput());
   }
 }
 
-void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
+void InstructionCodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+  if (codegen_->CanMoveNullCheckToUser(instruction)) {
+    return;
+  }
+  LocationSummary* locations = instruction->GetLocations();
+  Location obj = locations->InAt(0);
+
+  __ testl(CpuRegister(RAX), Address(obj.AsRegister<CpuRegister>(), 0));
+  codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+}
+
+void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) {
   SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
   codegen_->AddSlowPath(slow_path);
 
@@ -2457,7 +2781,7 @@
   Location obj = locations->InAt(0);
 
   if (obj.IsRegister()) {
-    __ cmpl(obj.AsRegister<CpuRegister>(), Immediate(0));
+    __ testl(obj.AsRegister<CpuRegister>(), obj.AsRegister<CpuRegister>());
   } else if (obj.IsStackSlot()) {
     __ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0));
   } else {
@@ -2469,6 +2793,14 @@
   __ j(kEqual, slow_path->GetEntryLabel());
 }
 
+void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
+  if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) {
+    GenerateImplicitNullCheck(instruction);
+  } else {
+    GenerateExplicitNullCheck(instruction);
+  }
+}
+
 void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2586,6 +2918,7 @@
       LOG(FATAL) << "Unreachable type " << instruction->GetType();
       UNREACHABLE();
   }
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
@@ -2654,6 +2987,7 @@
                   Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
         }
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2680,6 +3014,7 @@
                   Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
         }
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2708,7 +3043,7 @@
                     Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
           }
         }
-
+        codegen_->MaybeRecordImplicitNullCheck(instruction);
         if (needs_write_barrier) {
           DCHECK_EQ(value_type, Primitive::kPrimNot);
           CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
@@ -2736,6 +3071,7 @@
         __ movq(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
                 value.AsRegister<CpuRegister>());
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2750,6 +3086,7 @@
         __ movss(Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset),
                 value.AsFpuRegister<XmmRegister>());
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2764,6 +3101,7 @@
         __ movsd(Address(obj, index.AsRegister<CpuRegister>(), TIMES_8, data_offset),
                 value.AsFpuRegister<XmmRegister>());
       }
+      codegen_->MaybeRecordImplicitNullCheck(instruction);
       break;
     }
 
@@ -2786,6 +3124,7 @@
   CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
   CpuRegister out = locations->Out().AsRegister<CpuRegister>();
   __ movl(out, Address(obj, offset));
+  codegen_->MaybeRecordImplicitNullCheck(instruction);
 }
 
 void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
@@ -2925,12 +3264,16 @@
   } else if (source.IsConstant()) {
     HConstant* constant = source.GetConstant();
     if (constant->IsIntConstant()) {
-      Immediate imm(constant->AsIntConstant()->GetValue());
+      int32_t value = constant->AsIntConstant()->GetValue();
       if (destination.IsRegister()) {
-        __ movl(destination.AsRegister<CpuRegister>(), imm);
+        if (value == 0) {
+          __ xorl(destination.AsRegister<CpuRegister>(), destination.AsRegister<CpuRegister>());
+        } else {
+          __ movl(destination.AsRegister<CpuRegister>(), Immediate(value));
+        }
       } else {
         DCHECK(destination.IsStackSlot()) << destination;
-        __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);
+        __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), Immediate(value));
       }
     } else if (constant->IsLongConstant()) {
       int64_t value = constant->AsLongConstant()->GetValue();
@@ -3133,146 +3476,6 @@
                                    check->GetLocations()->InAt(0).AsRegister<CpuRegister>());
 }
 
-void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  CpuRegister cls = locations->InAt(0).AsRegister<CpuRegister>();
-  size_t offset = instruction->GetFieldOffset().SizeValue();
-
-  switch (instruction->GetType()) {
-    case Primitive::kPrimBoolean: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movzxb(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimByte: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movsxb(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimShort: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movsxw(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimChar: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movzxw(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movl(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-      __ movq(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movss(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
-      __ movsd(out, Address(cls, offset));
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << instruction->GetType();
-      UNREACHABLE();
-  }
-}
-
-void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  LocationSummary* locations =
-      new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
-  Primitive::Type field_type = instruction->GetFieldType();
-  bool needs_write_barrier =
-      CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue());
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetInAt(1, Location::RequiresRegister());
-  if (needs_write_barrier) {
-    // Temporary registers for the write barrier.
-    locations->AddTemp(Location::RequiresRegister());
-    locations->AddTemp(Location::RequiresRegister());
-  }
-}
-
-void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
-  LocationSummary* locations = instruction->GetLocations();
-  CpuRegister cls = locations->InAt(0).AsRegister<CpuRegister>();
-  size_t offset = instruction->GetFieldOffset().SizeValue();
-  Primitive::Type field_type = instruction->GetFieldType();
-
-  switch (field_type) {
-    case Primitive::kPrimBoolean:
-    case Primitive::kPrimByte: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movb(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimShort:
-    case Primitive::kPrimChar: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movw(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimInt:
-    case Primitive::kPrimNot: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movl(Address(cls, offset), value);
-      if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
-        CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
-        CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
-        codegen_->MarkGCCard(temp, card, cls, value);
-      }
-      break;
-    }
-
-    case Primitive::kPrimLong: {
-      CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
-      __ movq(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimFloat: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movss(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimDouble: {
-      XmmRegister value = locations->InAt(1).AsFpuRegister<XmmRegister>();
-      __ movsd(Address(cls, offset), value);
-      break;
-    }
-
-    case Primitive::kPrimVoid:
-      LOG(FATAL) << "Unreachable type " << field_type;
-      UNREACHABLE();
-  }
-}
-
 void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 794b81f..dbdbf86 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -18,6 +18,8 @@
 #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_X86_64_H_
 
 #include "code_generator.h"
+#include "dex/compiler_enums.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "parallel_move_resolver.h"
 #include "utils/x86_64/assembler_x86_64.h"
@@ -66,7 +68,20 @@
 };
 
 class CodeGeneratorX86_64;
-class SlowPathCodeX86_64;
+
+class SlowPathCodeX86_64 : public SlowPathCode {
+ public:
+  SlowPathCodeX86_64() : entry_label_(), exit_label_() {}
+
+  Label* GetEntryLabel() { return &entry_label_; }
+  Label* GetExitLabel() { return &exit_label_; }
+
+ private:
+  Label entry_label_;
+  Label exit_label_;
+
+  DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86_64);
+};
 
 class ParallelMoveResolverX86_64 : public ParallelMoveResolver {
  public:
@@ -109,6 +124,8 @@
   void HandleInvoke(HInvoke* invoke);
   void HandleBitwiseOperation(HBinaryOperation* operation);
   void HandleShift(HBinaryOperation* operation);
+  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+  void HandleFieldGet(HInstruction* instruction);
 
   CodeGeneratorX86_64* const codegen_;
   InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -136,8 +153,16 @@
   void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
   void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg);
   void HandleBitwiseOperation(HBinaryOperation* operation);
+  void GenerateRemFP(HRem *rem);
   void GenerateDivRemIntegral(HBinaryOperation* instruction);
   void HandleShift(HBinaryOperation* operation);
+  void GenerateMemoryBarrier(MemBarrierKind kind);
+  void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+  void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+  void GenerateImplicitNullCheck(HNullCheck* instruction);
+  void GenerateExplicitNullCheck(HNullCheck* instruction);
+  void PushOntoFPStack(Location source, uint32_t temp_offset,
+                       uint32_t stack_adjustment, bool is_float);
 
   X86_64Assembler* const assembler_;
   CodeGeneratorX86_64* const codegen_;
@@ -147,7 +172,7 @@
 
 class CodeGeneratorX86_64 : public CodeGenerator {
  public:
-  explicit CodeGeneratorX86_64(HGraph* graph);
+  CodeGeneratorX86_64(HGraph* graph, const CompilerOptions& compiler_options);
   virtual ~CodeGeneratorX86_64() {}
 
   void GenerateFrameEntry() OVERRIDE;
@@ -163,7 +188,9 @@
     return kX86_64WordSize;
   }
 
-  size_t FrameEntrySpillSize() const OVERRIDE;
+  size_t GetFloatingPointSpillSlotSize() const OVERRIDE {
+    return kX86_64WordSize;
+  }
 
   HGraphVisitor* GetLocationBuilder() OVERRIDE {
     return &location_builder_;
@@ -187,7 +214,7 @@
 
   Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
 
-  void SetupBlockedRegisters() const OVERRIDE;
+  void SetupBlockedRegisters(bool is_baseline) const OVERRIDE;
   Location AllocateFreeRegister(Primitive::Type type) const OVERRIDE;
   void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
   void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
@@ -212,9 +239,16 @@
     block_labels_.SetSize(GetGraph()->GetBlocks().Size());
   }
 
+  bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+    return false;
+  }
+
+  void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, CpuRegister temp);
+
  private:
   // Labels for each block that will be compiled.
   GrowableArray<Label> block_labels_;
+  Label frame_entry_label_;
   LocationsBuilderX86_64 location_builder_;
   InstructionCodeGeneratorX86_64 instruction_visitor_;
   ParallelMoveResolverX86_64 move_resolver_;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index fee3ea6..e0e0b4c 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -17,6 +17,7 @@
 #include <functional>
 
 #include "arch/instruction_set.h"
+#include "arch/arm/instruction_set_features_arm.h"
 #include "base/macros.h"
 #include "builder.h"
 #include "code_generator_arm.h"
@@ -26,6 +27,7 @@
 #include "common_compiler_test.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 #include "prepare_for_register_allocation.h"
@@ -37,6 +39,31 @@
 
 namespace art {
 
+// Provide our own codegen, that ensures the C calling conventions
+// are preserved. Currently, ART and C do not match as R4 is caller-save
+// in ART, and callee-save in C. Alternatively, we could use or write
+// the stub that saves and restores all registers, but it is easier
+// to just overwrite the code generator.
+class TestCodeGeneratorARM : public arm::CodeGeneratorARM {
+ public:
+  TestCodeGeneratorARM(HGraph* graph,
+                       const ArmInstructionSetFeatures& isa_features,
+                       const CompilerOptions& compiler_options)
+      : arm::CodeGeneratorARM(graph, isa_features, compiler_options) {
+    AddAllocatedRegister(Location::RegisterLocation(6));
+    AddAllocatedRegister(Location::RegisterLocation(7));
+  }
+
+  void SetupBlockedRegisters(bool is_baseline) const OVERRIDE {
+    arm::CodeGeneratorARM::SetupBlockedRegisters(is_baseline);
+    blocked_core_registers_[4] = true;
+    blocked_core_registers_[6] = false;
+    blocked_core_registers_[7] = false;
+    // Makes pair R6-R7 available.
+    blocked_register_pairs_[6 >> 1] = false;
+  }
+};
+
 class InternalCodeAllocator : public CodeAllocator {
  public:
   InternalCodeAllocator() : size_(0) { }
@@ -79,7 +106,8 @@
 static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) {
   InternalCodeAllocator allocator;
 
-  x86::CodeGeneratorX86 codegenX86(graph);
+  CompilerOptions compiler_options;
+  x86::CodeGeneratorX86 codegenX86(graph, compiler_options);
   // We avoid doing a stack overflow check that requires the runtime being setup,
   // by making sure the compiler knows the methods we are running are leaf methods.
   codegenX86.CompileBaseline(&allocator, true);
@@ -87,19 +115,21 @@
     Run(allocator, codegenX86, has_result, expected);
   }
 
-  arm::CodeGeneratorARM codegenARM(graph);
+  std::unique_ptr<const ArmInstructionSetFeatures> features(
+      ArmInstructionSetFeatures::FromCppDefines());
+  TestCodeGeneratorARM codegenARM(graph, *features.get(), compiler_options);
   codegenARM.CompileBaseline(&allocator, true);
   if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
     Run(allocator, codegenARM, has_result, expected);
   }
 
-  x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+  x86_64::CodeGeneratorX86_64 codegenX86_64(graph, compiler_options);
   codegenX86_64.CompileBaseline(&allocator, true);
   if (kRuntimeISA == kX86_64) {
     Run(allocator, codegenX86_64, has_result, expected);
   }
 
-  arm64::CodeGeneratorARM64 codegenARM64(graph);
+  arm64::CodeGeneratorARM64 codegenARM64(graph, compiler_options);
   codegenARM64.CompileBaseline(&allocator, true);
   if (kRuntimeISA == kArm64) {
     Run(allocator, codegenARM64, has_result, expected);
@@ -129,14 +159,20 @@
                              std::function<void(HGraph*)> hook_before_codegen,
                              bool has_result,
                              Expected expected) {
-  if (kRuntimeISA == kX86) {
-    x86::CodeGeneratorX86 codegenX86(graph);
-    RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
-  } else if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
-    arm::CodeGeneratorARM codegenARM(graph);
+  CompilerOptions compiler_options;
+  if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+    TestCodeGeneratorARM codegenARM(graph,
+                                    *ArmInstructionSetFeatures::FromCppDefines(),
+                                    compiler_options);
     RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected);
+  } else if (kRuntimeISA == kArm64) {
+    arm64::CodeGeneratorARM64 codegenARM64(graph, compiler_options);
+    RunCodeOptimized(&codegenARM64, graph, hook_before_codegen, has_result, expected);
+  } else if (kRuntimeISA == kX86) {
+    x86::CodeGeneratorX86 codegenX86(graph, compiler_options);
+    RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected);
   } else if (kRuntimeISA == kX86_64) {
-    x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+    x86_64::CodeGeneratorX86_64 codegenX86_64(graph, compiler_options);
     RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected);
   }
 }
@@ -144,10 +180,11 @@
 static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  HGraphBuilder builder(&arena);
+  HGraph* graph = new (&arena) HGraph(&arena);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
   // Remove suspend checks, they cannot be executed in this context.
   RemoveSuspendChecks(graph);
   RunCodeBaseline(graph, has_result, expected);
@@ -156,10 +193,11 @@
 static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) {
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  HGraphBuilder builder(&arena, Primitive::kPrimLong);
+  HGraph* graph = new (&arena) HGraph(&arena);
+  HGraphBuilder builder(graph, Primitive::kPrimLong);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
   // Remove suspend checks, they cannot be executed in this context.
   RemoveSuspendChecks(graph);
   RunCodeBaseline(graph, has_result, expected);
@@ -362,11 +400,7 @@
 
 #undef NOT_LONG_TEST
 
-#if defined(__aarch64__)
-TEST(CodegenTest, DISABLED_IntToLongOfLongToInt) {
-#else
 TEST(CodegenTest, IntToLongOfLongToInt) {
-#endif
   const int64_t input = INT64_C(4294967296);             // 2^32
   const uint16_t word0 = Low16Bits(Low32Bits(input));    // LSW.
   const uint16_t word1 = High16Bits(Low32Bits(input));
@@ -493,10 +527,8 @@
     TestCode(data, true, 12);                         \
   }
 
-#if !defined(__aarch64__)
 MUL_TEST(INT, MulInt);
 MUL_TEST(LONG, MulLong);
-#endif
 
 TEST(CodegenTest, ReturnMulIntLit8) {
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
@@ -633,11 +665,7 @@
   }
 }
 
-#if defined(__aarch64__)
-TEST(CodegenTest, DISABLED_ReturnDivIntLit8) {
-#else
 TEST(CodegenTest, ReturnDivIntLit8) {
-#endif
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 4 << 12 | 0 << 8,
     Instruction::DIV_INT_LIT8, 3 << 8 | 0,
@@ -646,11 +674,7 @@
   TestCode(data, true, 1);
 }
 
-#if defined(__aarch64__)
-TEST(CodegenTest, DISABLED_ReturnDivInt2Addr) {
-#else
 TEST(CodegenTest, ReturnDivInt2Addr) {
-#endif
   const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
     Instruction::CONST_4 | 4 << 12 | 0,
     Instruction::CONST_4 | 2 << 12 | 1 << 8,
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
new file mode 100644
index 0000000..007324e
--- /dev/null
+++ b/compiler/optimizing/common_arm64.h
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
+#define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
+
+#include "locations.h"
+#include "nodes.h"
+#include "utils/arm64/assembler_arm64.h"
+#include "a64/disasm-a64.h"
+#include "a64/macro-assembler-a64.h"
+
+namespace art {
+namespace arm64 {
+namespace helpers {
+
+// Convenience helpers to ease conversion to and from VIXL operands.
+static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32),
+              "Unexpected values for register codes.");
+
+static inline int VIXLRegCodeFromART(int code) {
+  if (code == SP) {
+    return vixl::kSPRegInternalCode;
+  }
+  if (code == XZR) {
+    return vixl::kZeroRegCode;
+  }
+  return code;
+}
+
+static inline int ARTRegCodeFromVIXL(int code) {
+  if (code == vixl::kSPRegInternalCode) {
+    return SP;
+  }
+  if (code == vixl::kZeroRegCode) {
+    return XZR;
+  }
+  return code;
+}
+
+static inline vixl::Register XRegisterFrom(Location location) {
+  DCHECK(location.IsRegister());
+  return vixl::Register::XRegFromCode(VIXLRegCodeFromART(location.reg()));
+}
+
+static inline vixl::Register WRegisterFrom(Location location) {
+  DCHECK(location.IsRegister());
+  return vixl::Register::WRegFromCode(VIXLRegCodeFromART(location.reg()));
+}
+
+static inline vixl::Register RegisterFrom(Location location, Primitive::Type type) {
+  DCHECK(type != Primitive::kPrimVoid && !Primitive::IsFloatingPointType(type));
+  return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
+}
+
+static inline vixl::Register OutputRegister(HInstruction* instr) {
+  return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+static inline vixl::Register InputRegisterAt(HInstruction* instr, int input_index) {
+  return RegisterFrom(instr->GetLocations()->InAt(input_index),
+                      instr->InputAt(input_index)->GetType());
+}
+
+static inline vixl::FPRegister DRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister());
+  return vixl::FPRegister::DRegFromCode(location.reg());
+}
+
+static inline vixl::FPRegister SRegisterFrom(Location location) {
+  DCHECK(location.IsFpuRegister());
+  return vixl::FPRegister::SRegFromCode(location.reg());
+}
+
+static inline vixl::FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
+  DCHECK(Primitive::IsFloatingPointType(type));
+  return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
+}
+
+static inline vixl::FPRegister OutputFPRegister(HInstruction* instr) {
+  return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
+}
+
+static inline vixl::FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
+  return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
+                        instr->InputAt(input_index)->GetType());
+}
+
+static inline vixl::CPURegister CPURegisterFrom(Location location, Primitive::Type type) {
+  return Primitive::IsFloatingPointType(type) ? vixl::CPURegister(FPRegisterFrom(location, type))
+                                              : vixl::CPURegister(RegisterFrom(location, type));
+}
+
+static inline vixl::CPURegister OutputCPURegister(HInstruction* instr) {
+  return Primitive::IsFloatingPointType(instr->GetType())
+      ? static_cast<vixl::CPURegister>(OutputFPRegister(instr))
+      : static_cast<vixl::CPURegister>(OutputRegister(instr));
+}
+
+static inline vixl::CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
+  return Primitive::IsFloatingPointType(instr->InputAt(index)->GetType())
+      ? static_cast<vixl::CPURegister>(InputFPRegisterAt(instr, index))
+      : static_cast<vixl::CPURegister>(InputRegisterAt(instr, index));
+}
+
+static inline int64_t Int64ConstantFrom(Location location) {
+  HConstant* instr = location.GetConstant();
+  return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
+                                : instr->AsLongConstant()->GetValue();
+}
+
+static inline vixl::Operand OperandFrom(Location location, Primitive::Type type) {
+  if (location.IsRegister()) {
+    return vixl::Operand(RegisterFrom(location, type));
+  } else {
+    return vixl::Operand(Int64ConstantFrom(location));
+  }
+}
+
+static inline vixl::Operand InputOperandAt(HInstruction* instr, int input_index) {
+  return OperandFrom(instr->GetLocations()->InAt(input_index),
+                     instr->InputAt(input_index)->GetType());
+}
+
+static inline vixl::MemOperand StackOperandFrom(Location location) {
+  return vixl::MemOperand(vixl::sp, location.GetStackIndex());
+}
+
+static inline vixl::MemOperand HeapOperand(const vixl::Register& base, size_t offset = 0) {
+  // A heap reference must be 32bit, so fit in a W register.
+  DCHECK(base.IsW());
+  return vixl::MemOperand(base.X(), offset);
+}
+
+static inline vixl::MemOperand HeapOperand(const vixl::Register& base, Offset offset) {
+  return HeapOperand(base, offset.SizeValue());
+}
+
+static inline vixl::MemOperand HeapOperandFrom(Location location, Offset offset) {
+  return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset);
+}
+
+static inline Location LocationFrom(const vixl::Register& reg) {
+  return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.code()));
+}
+
+static inline Location LocationFrom(const vixl::FPRegister& fpreg) {
+  return Location::FpuRegisterLocation(fpreg.code());
+}
+
+static inline vixl::Operand OperandFromMemOperand(const vixl::MemOperand& mem_op) {
+  if (mem_op.IsImmediateOffset()) {
+    return vixl::Operand(mem_op.offset());
+  } else {
+    DCHECK(mem_op.IsRegisterOffset());
+    if (mem_op.extend() != vixl::NO_EXTEND) {
+      return vixl::Operand(mem_op.regoffset(), mem_op.extend(), mem_op.shift_amount());
+    } else if (mem_op.shift() != vixl::NO_SHIFT) {
+      return vixl::Operand(mem_op.regoffset(), mem_op.shift(), mem_op.shift_amount());
+    } else {
+      LOG(FATAL) << "Should not reach here";
+      UNREACHABLE();
+    }
+  }
+}
+
+}  // namespace helpers
+}  // namespace arm64
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index a56b9d9..6ceccfb 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -19,6 +19,7 @@
 #include "code_generator_x86.h"
 #include "constant_folding.h"
 #include "dead_code_elimination.h"
+#include "driver/compiler_options.h"
 #include "graph_checker.h"
 #include "optimizing_unit_test.h"
 #include "pretty_printer.h"
@@ -38,19 +39,18 @@
   HGraph* graph = CreateCFG(&allocator, data, return_type);
   ASSERT_NE(graph, nullptr);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TryBuildingSsa();
 
   StringPrettyPrinter printer_before(graph);
   printer_before.VisitInsertionOrder();
   std::string actual_before = printer_before.str();
   ASSERT_EQ(expected_before, actual_before);
 
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   HConstantFolding(graph).Run();
-  SSAChecker ssa_checker(&allocator, graph);
-  ssa_checker.Run();
-  ASSERT_TRUE(ssa_checker.IsValid());
+  SSAChecker ssa_checker_cf(&allocator, graph);
+  ssa_checker_cf.Run();
+  ASSERT_TRUE(ssa_checker_cf.IsValid());
 
   StringPrettyPrinter printer_after_cf(graph);
   printer_after_cf.VisitInsertionOrder();
@@ -60,8 +60,9 @@
   check_after_cf(graph);
 
   HDeadCodeElimination(graph).Run();
-  ssa_checker.Run();
-  ASSERT_TRUE(ssa_checker.IsValid());
+  SSAChecker ssa_checker_dce(&allocator, graph);
+  ssa_checker_dce.Run();
+  ASSERT_TRUE(ssa_checker_dce.IsValid());
 
   StringPrettyPrinter printer_after_dce(graph);
   printer_after_dce.VisitInsertionOrder();
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 5d4b9cb..a6447196 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -16,6 +16,7 @@
 
 #include "code_generator_x86.h"
 #include "dead_code_elimination.h"
+#include "driver/compiler_options.h"
 #include "graph_checker.h"
 #include "optimizing_unit_test.h"
 #include "pretty_printer.h"
@@ -32,15 +33,14 @@
   HGraph* graph = CreateCFG(&allocator, data);
   ASSERT_NE(graph, nullptr);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TryBuildingSsa();
 
   StringPrettyPrinter printer_before(graph);
   printer_before.VisitInsertionOrder();
   std::string actual_before = printer_before.str();
   ASSERT_EQ(actual_before, expected_before);
 
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   HDeadCodeElimination(graph).Run();
   SSAChecker ssa_checker(&allocator, graph);
   ssa_checker.Run();
diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc
index 3062e37..b246c6f 100644
--- a/compiler/optimizing/dominator_test.cc
+++ b/compiler/optimizing/dominator_test.cc
@@ -27,10 +27,11 @@
 static void TestCode(const uint16_t* data, const int* blocks, size_t blocks_length) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
   graph->BuildDominatorTree();
   ASSERT_EQ(graph->GetBlocks().Size(), blocks_length);
   for (size_t i = 0, e = blocks_length; i < e; ++i) {
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index 82fe03c..e05d9b3 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -28,9 +28,10 @@
 namespace art {
 
 static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) {
-  HGraphBuilder builder(allocator);
+  HGraph* graph = new (allocator) HGraph(allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
+  builder.BuildGraph(*item);
   graph->BuildDominatorTree();
   graph->AnalyzeNaturalLoops();
   return graph;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 5d712fe..4ebb136 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -16,11 +16,11 @@
 
 #include "graph_checker.h"
 
-#include <string>
 #include <map>
-#include <sstream>
+#include <string>
 
 #include "base/bit_vector-inl.h"
+#include "base/stringprintf.h"
 
 namespace art {
 
@@ -45,15 +45,11 @@
       }
     }
     if (p_count_in_block_predecessors != block_count_in_p_successors) {
-      std::stringstream error;
-      error << "Block " << block->GetBlockId()
-            << " lists " << p_count_in_block_predecessors
-            << " occurrences of block " << p->GetBlockId()
-            << " in its predecessors, whereas block " << p->GetBlockId()
-            << " lists " << block_count_in_p_successors
-            << " occurrences of block " << block->GetBlockId()
-            << " in its successors.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Block %d lists %zu occurrences of block %d in its predecessors, whereas "
+          "block %d lists %zu occurrences of block %d in its successors.",
+          block->GetBlockId(), p_count_in_block_predecessors, p->GetBlockId(),
+          p->GetBlockId(), block_count_in_p_successors, block->GetBlockId()));
     }
   }
 
@@ -75,35 +71,27 @@
       }
     }
     if (s_count_in_block_successors != block_count_in_s_predecessors) {
-      std::stringstream error;
-      error << "Block " << block->GetBlockId()
-            << " lists " << s_count_in_block_successors
-            << " occurrences of block " << s->GetBlockId()
-            << " in its successors, whereas block " << s->GetBlockId()
-            << " lists " << block_count_in_s_predecessors
-            << " occurrences of block " << block->GetBlockId()
-            << " in its predecessors.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Block %d lists %zu occurrences of block %d in its successors, whereas "
+          "block %d lists %zu occurrences of block %d in its predecessors.",
+          block->GetBlockId(), s_count_in_block_successors, s->GetBlockId(),
+          s->GetBlockId(), block_count_in_s_predecessors, block->GetBlockId()));
     }
   }
 
   // Ensure `block` ends with a branch instruction.
   HInstruction* last_inst = block->GetLastInstruction();
   if (last_inst == nullptr || !last_inst->IsControlFlow()) {
-    std::stringstream error;
-    error  << "Block " << block->GetBlockId()
-           << " does not end with a branch instruction.";
-    errors_.push_back(error.str());
+    AddError(StringPrintf("Block %d does not end with a branch instruction.",
+                          block->GetBlockId()));
   }
 
   // Visit this block's list of phis.
   for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
     // Ensure this block's list of phis contains only phis.
     if (!it.Current()->IsPhi()) {
-      std::stringstream error;
-      error << "Block " << current_block_->GetBlockId()
-            << " has a non-phi in its phi list.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf("Block %d has a non-phi in its phi list.",
+                            current_block_->GetBlockId()));
     }
     it.Current()->Accept(this);
   }
@@ -113,33 +101,33 @@
        it.Advance()) {
     // Ensure this block's list of instructions does not contains phis.
     if (it.Current()->IsPhi()) {
-      std::stringstream error;
-      error << "Block " << current_block_->GetBlockId()
-            << " has a phi in its non-phi list.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf("Block %d has a phi in its non-phi list.",
+                            current_block_->GetBlockId()));
     }
     it.Current()->Accept(this);
   }
 }
 
 void GraphChecker::VisitInstruction(HInstruction* instruction) {
+  if (seen_ids_.IsBitSet(instruction->GetId())) {
+    AddError(StringPrintf("Instruction id %d is duplicate in graph.",
+                          instruction->GetId()));
+  } else {
+    seen_ids_.SetBit(instruction->GetId());
+  }
+
   // Ensure `instruction` is associated with `current_block_`.
-  if (instruction->GetBlock() != current_block_) {
-    std::stringstream error;
-    if (instruction->IsPhi()) {
-      error << "Phi ";
-    } else {
-      error << "Instruction ";
-    }
-    error << instruction->GetId() << " in block "
-          << current_block_->GetBlockId();
-    if (instruction->GetBlock() != nullptr) {
-      error << " associated with block "
-            << instruction->GetBlock()->GetBlockId() << ".";
-    } else {
-      error << " not associated with any block.";
-    }
-    errors_.push_back(error.str());
+  if (instruction->GetBlock() == nullptr) {
+    AddError(StringPrintf("%s %d in block %d not associated with any block.",
+                          instruction->IsPhi() ? "Phi" : "Instruction",
+                          instruction->GetId(),
+                          current_block_->GetBlockId()));
+  } else if (instruction->GetBlock() != current_block_) {
+    AddError(StringPrintf("%s %d in block %d associated with block %d.",
+                          instruction->IsPhi() ? "Phi" : "Instruction",
+                          instruction->GetId(),
+                          current_block_->GetBlockId(),
+                          instruction->GetBlock()->GetBlockId()));
   }
 
   // Ensure the inputs of `instruction` are defined in a block of the graph.
@@ -150,27 +138,26 @@
         ? input->GetBlock()->GetPhis()
         : input->GetBlock()->GetInstructions();
     if (!list.Contains(input)) {
-      std::stringstream error;
-      error << "Input " << input->GetId()
-            << " of instruction " << instruction->GetId()
-            << " is not defined in a basic block of the control-flow graph.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf("Input %d of instruction %d is not defined "
+                            "in a basic block of the control-flow graph.",
+                            input->GetId(),
+                            instruction->GetId()));
     }
   }
 
   // Ensure the uses of `instruction` are defined in a block of the graph.
-  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+  for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
        !use_it.Done(); use_it.Advance()) {
     HInstruction* use = use_it.Current()->GetUser();
     const HInstructionList& list = use->IsPhi()
         ? use->GetBlock()->GetPhis()
         : use->GetBlock()->GetInstructions();
     if (!list.Contains(use)) {
-      std::stringstream error;
-      error << "User " << use->GetId()
-            << " of instruction " << instruction->GetId()
-            << " is not defined in a basic block of the control-flow graph.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf("User %s:%d of instruction %d is not defined "
+                            "in a basic block of the control-flow graph.",
+                            use->DebugName(),
+                            use->GetId(),
+                            instruction->GetId()));
     }
   }
 }
@@ -185,10 +172,9 @@
     for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
       HBasicBlock* successor = block->GetSuccessors().Get(j);
       if (successor->GetPredecessors().Size() > 1) {
-        std::stringstream error;
-        error << "Critical edge between blocks " << block->GetBlockId()
-              << " and "  << successor->GetBlockId() << ".";
-        errors_.push_back(error.str());
+        AddError(StringPrintf("Critical edge between blocks %d and %d.",
+                              block->GetBlockId(),
+                              successor->GetBlockId()));
       }
     }
   }
@@ -204,47 +190,52 @@
   // Ensure the pre-header block is first in the list of
   // predecessors of a loop header.
   if (!loop_header->IsLoopPreHeaderFirstPredecessor()) {
-    std::stringstream error;
-    error << "Loop pre-header is not the first predecessor of the loop header "
-          << id << ".";
-    errors_.push_back(error.str());
+    AddError(StringPrintf(
+        "Loop pre-header is not the first predecessor of the loop header %d.",
+        id));
   }
 
   // Ensure the loop header has only two predecessors and that only the
   // second one is a back edge.
-  if (loop_header->GetPredecessors().Size() < 2) {
-    std::stringstream error;
-    error << "Loop header " << id << " has less than two predecessors.";
-    errors_.push_back(error.str());
-  } else if (loop_header->GetPredecessors().Size() > 2) {
-    std::stringstream error;
-    error << "Loop header " << id << " has more than two predecessors.";
-    errors_.push_back(error.str());
+  size_t num_preds = loop_header->GetPredecessors().Size();
+  if (num_preds < 2) {
+    AddError(StringPrintf(
+        "Loop header %d has less than two predecessors: %zu.",
+        id,
+        num_preds));
+  } else if (num_preds > 2) {
+    AddError(StringPrintf(
+        "Loop header %d has more than two predecessors: %zu.",
+        id,
+        num_preds));
   } else {
     HLoopInformation* loop_information = loop_header->GetLoopInformation();
     HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
     if (loop_information->IsBackEdge(first_predecessor)) {
-      std::stringstream error;
-      error << "First predecessor of loop header " << id << " is a back edge.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "First predecessor of loop header %d is a back edge.",
+          id));
     }
     HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1);
     if (!loop_information->IsBackEdge(second_predecessor)) {
-      std::stringstream error;
-      error << "Second predecessor of loop header " << id
-            << " is not a back edge.";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Second predecessor of loop header %d is not a back edge.",
+          id));
     }
   }
 
   // Ensure there is only one back edge per loop.
   size_t num_back_edges =
     loop_header->GetLoopInformation()->GetBackEdges().Size();
-  if (num_back_edges != 1) {
-      std::stringstream error;
-      error << "Loop defined by header " << id << " has "
-            << num_back_edges << " back edge(s).";
-      errors_.push_back(error.str());
+  if (num_back_edges == 0) {
+    AddError(StringPrintf(
+        "Loop defined by header %d has no back edge.",
+        id));
+  } else if (num_back_edges > 1) {
+    AddError(StringPrintf(
+        "Loop defined by header %d has several back edges: %zu.",
+        id,
+        num_back_edges));
   }
 
   // Ensure all blocks in the loop are dominated by the loop header.
@@ -253,10 +244,9 @@
   for (uint32_t i : loop_blocks.Indexes()) {
     HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i);
     if (!loop_header->Dominates(loop_block)) {
-      std::stringstream error;
-      error << "Loop block " << loop_block->GetBlockId()
-            << " not dominated by loop header " << id;
-      errors_.push_back(error.str());
+      AddError(StringPrintf("Loop block %d not dominated by loop header %d.",
+                            loop_block->GetBlockId(),
+                            id));
     }
   }
 }
@@ -265,16 +255,14 @@
   super_type::VisitInstruction(instruction);
 
   // Ensure an instruction dominates all its uses.
-  for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+  for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
        !use_it.Done(); use_it.Advance()) {
     HInstruction* use = use_it.Current()->GetUser();
     if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
-      std::stringstream error;
-      error << "Instruction " << instruction->GetId()
-            << " in block " << current_block_->GetBlockId()
-            << " does not dominate use " << use->GetId()
-            << " in block " << use->GetBlock()->GetBlockId() << ".";
-      errors_.push_back(error.str());
+      AddError(StringPrintf("Instruction %d in block %d does not dominate "
+                            "use %d in block %d.",
+                            instruction->GetId(), current_block_->GetBlockId(),
+                            use->GetId(), use->GetBlock()->GetBlockId()));
     }
   }
 
@@ -286,13 +274,12 @@
       HInstruction* env_instruction = environment->GetInstructionAt(i);
       if (env_instruction != nullptr
           && !env_instruction->StrictlyDominates(instruction)) {
-        std::stringstream error;
-        error << "Instruction " << env_instruction->GetId()
-              << " in environment of instruction " << instruction->GetId()
-              << " from block " << current_block_->GetBlockId()
-              << " does not dominate instruction " << instruction->GetId()
-              << ".";
-        errors_.push_back(error.str());
+        AddError(StringPrintf("Instruction %d in environment of instruction %d "
+                              "from block %d does not dominate instruction %d.",
+                              env_instruction->GetId(),
+                              instruction->GetId(),
+                              current_block_->GetBlockId(),
+                              instruction->GetId()));
       }
     }
   }
@@ -303,25 +290,21 @@
 
   // Ensure the first input of a phi is not itself.
   if (phi->InputAt(0) == phi) {
-      std::stringstream error;
-      error << "Loop phi " << phi->GetId()
-            << " in block " << phi->GetBlock()->GetBlockId()
-            << " is its own first input.";
-      errors_.push_back(error.str());
+    AddError(StringPrintf("Loop phi %d in block %d is its own first input.",
+                          phi->GetId(),
+                          phi->GetBlock()->GetBlockId()));
   }
 
-  // Ensure the number of phi inputs is the same as the number of
+  // Ensure the number of inputs of a phi is the same as the number of
   // its predecessors.
   const GrowableArray<HBasicBlock*>& predecessors =
     phi->GetBlock()->GetPredecessors();
   if (phi->InputCount() != predecessors.Size()) {
-    std::stringstream error;
-    error << "Phi " << phi->GetId()
-          << " in block " << phi->GetBlock()->GetBlockId()
-          << " has " << phi->InputCount() << " inputs, but block "
-          << phi->GetBlock()->GetBlockId() << " has "
-          << predecessors.Size() << " predecessors.";
-    errors_.push_back(error.str());
+    AddError(StringPrintf(
+        "Phi %d in block %d has %zu inputs, "
+        "but block %d has %zu predecessors.",
+        phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(),
+        phi->GetBlock()->GetBlockId(), predecessors.Size()));
   } else {
     // Ensure phi input at index I either comes from the Ith
     // predecessor or from a block that dominates this predecessor.
@@ -330,13 +313,11 @@
       HBasicBlock* predecessor = predecessors.Get(i);
       if (!(input->GetBlock() == predecessor
             || input->GetBlock()->Dominates(predecessor))) {
-        std::stringstream error;
-        error << "Input " << input->GetId() << " at index " << i
-              << " of phi " << phi->GetId()
-              << " from block " << phi->GetBlock()->GetBlockId()
-              << " is not defined in predecessor number " << i
-              << " nor in a block dominating it.";
-        errors_.push_back(error.str());
+        AddError(StringPrintf(
+            "Input %d at index %zu of phi %d from block %d is not defined in "
+            "predecessor number %zu nor in a block dominating it.",
+            input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
+            i));
       }
     }
   }
@@ -355,15 +336,67 @@
   }
 }
 
+void SSAChecker::VisitIf(HIf* instruction) {
+  VisitInstruction(instruction);
+  HInstruction* input = instruction->InputAt(0);
+  if (input->IsIntConstant()) {
+    int value = input->AsIntConstant()->GetValue();
+    if (value != 0 && value != 1) {
+      AddError(StringPrintf(
+          "If instruction %d has a non-Boolean constant input "
+          "whose value is: %d.",
+          instruction->GetId(),
+          value));
+    }
+  } else if (instruction->InputAt(0)->GetType() != Primitive::kPrimBoolean) {
+    AddError(StringPrintf(
+        "If instruction %d has a non-Boolean input type: %s.",
+        instruction->GetId(),
+        Primitive::PrettyDescriptor(instruction->InputAt(0)->GetType())));
+  }
+}
+
 void SSAChecker::VisitCondition(HCondition* op) {
   VisitInstruction(op);
-  // TODO: check inputs types, and special case the `null` check.
   if (op->GetType() != Primitive::kPrimBoolean) {
-    std::stringstream error;
-    error << "Condition " << op->DebugName() << " " << op->GetId()
-          << " has a non-boolean result type: "
-          << op->GetType() << ".";
-    errors_.push_back(error.str());
+    AddError(StringPrintf(
+        "Condition %s %d has a non-Boolean result type: %s.",
+        op->DebugName(), op->GetId(),
+        Primitive::PrettyDescriptor(op->GetType())));
+  }
+  HInstruction* lhs = op->InputAt(0);
+  HInstruction* rhs = op->InputAt(1);
+  if (lhs->GetType() == Primitive::kPrimNot) {
+    if (!op->IsEqual() && !op->IsNotEqual()) {
+      AddError(StringPrintf(
+          "Condition %s %d uses an object as left-hand side input.",
+          op->DebugName(), op->GetId()));
+    }
+    if (rhs->IsIntConstant() && rhs->AsIntConstant()->GetValue() != 0) {
+      AddError(StringPrintf(
+          "Condition %s %d compares an object with a non-zero integer: %d.",
+          op->DebugName(), op->GetId(),
+          rhs->AsIntConstant()->GetValue()));
+    }
+  } else if (rhs->GetType() == Primitive::kPrimNot) {
+    if (!op->IsEqual() && !op->IsNotEqual()) {
+      AddError(StringPrintf(
+          "Condition %s %d uses an object as right-hand side input.",
+          op->DebugName(), op->GetId()));
+    }
+    if (lhs->IsIntConstant() && lhs->AsIntConstant()->GetValue() != 0) {
+      AddError(StringPrintf(
+          "Condition %s %d compares a non-zero integer with an object: %d.",
+          op->DebugName(), op->GetId(),
+          lhs->AsIntConstant()->GetValue()));
+    }
+  } else if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) {
+      AddError(StringPrintf(
+          "Condition %s %d has inputs of different types: "
+          "%s, and %s.",
+          op->DebugName(), op->GetId(),
+          Primitive::PrettyDescriptor(lhs->GetType()),
+          Primitive::PrettyDescriptor(rhs->GetType())));
   }
 }
 
@@ -371,41 +404,40 @@
   VisitInstruction(op);
   if (op->IsUShr() || op->IsShr() || op->IsShl()) {
     if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) {
-      std::stringstream error;
-      error << "Shift operation " << op->DebugName() << " " << op->GetId()
-            << " has a non-int kind second input: "
-            << op->InputAt(1)->DebugName() << " of type " << op->InputAt(1)->GetType()
-            << ".";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Shift operation %s %d has a non-int kind second input: "
+          "%s of type %s.",
+          op->DebugName(), op->GetId(),
+          op->InputAt(1)->DebugName(),
+          Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
     }
   } else {
     if (PrimitiveKind(op->InputAt(1)->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
-      std::stringstream error;
-      error << "Binary operation " << op->DebugName() << " " << op->GetId()
-            << " has inputs of different type: "
-            << op->InputAt(0)->GetType() << ", and " << op->InputAt(1)->GetType()
-            << ".";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Binary operation %s %d has inputs of different types: "
+          "%s, and %s.",
+          op->DebugName(), op->GetId(),
+          Primitive::PrettyDescriptor(op->InputAt(0)->GetType()),
+          Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
     }
   }
 
   if (op->IsCompare()) {
     if (op->GetType() != Primitive::kPrimInt) {
-      std::stringstream error;
-      error << "Compare operation " << op->GetId()
-            << " has a non-int result type: "
-            << op->GetType() << ".";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Compare operation %d has a non-int result type: %s.",
+          op->GetId(),
+          Primitive::PrettyDescriptor(op->GetType())));
     }
   } else {
     // Use the first input, so that we can also make this check for shift operations.
     if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
-      std::stringstream error;
-      error << "Binary operation " << op->DebugName() << " " << op->GetId()
-            << " has a result type different than its input type: "
-            << op->GetType() << ", and " << op->InputAt(1)->GetType()
-            << ".";
-      errors_.push_back(error.str());
+      AddError(StringPrintf(
+          "Binary operation %s %d has a result type different "
+          "from its input type: %s vs %s.",
+          op->DebugName(), op->GetId(),
+          Primitive::PrettyDescriptor(op->GetType()),
+          Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
     }
   }
 }
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index b6c9f17..5ec3003 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -30,7 +30,8 @@
                const char* dump_prefix = "art::GraphChecker: ")
     : HGraphDelegateVisitor(graph),
       allocator_(allocator),
-      dump_prefix_(dump_prefix) {}
+      dump_prefix_(dump_prefix),
+      seen_ids_(allocator, graph->GetCurrentInstructionId(), false) {}
 
   // Check the whole graph (in insertion order).
   virtual void Run() { VisitInsertionOrder(); }
@@ -59,6 +60,11 @@
   }
 
  protected:
+  // Report a new error.
+  void AddError(const std::string& error) {
+    errors_.push_back(error);
+  }
+
   ArenaAllocator* const allocator_;
   // The block currently visited.
   HBasicBlock* current_block_ = nullptr;
@@ -68,6 +74,7 @@
  private:
   // String displayed before dumped errors.
   const char* const dump_prefix_;
+  ArenaBitVector seen_ids_;
 
   DISALLOW_COPY_AND_ASSIGN(GraphChecker);
 };
@@ -99,6 +106,7 @@
   void VisitPhi(HPhi* phi) OVERRIDE;
   void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
   void VisitCondition(HCondition* op) OVERRIDE;
+  void VisitIf(HIf* instruction) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SSAChecker);
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index 39def82..923468f 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -62,7 +62,7 @@
   ASSERT_NE(graph, nullptr);
 
   graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TransformToSsa();
 
   SSAChecker ssa_checker(&allocator, graph);
   ssa_checker.Run();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4ed2156..835bca6 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -17,8 +17,8 @@
 #include "graph_visualizer.h"
 
 #include "code_generator.h"
-#include "driver/dex_compilation_unit.h"
 #include "nodes.h"
+#include "optimization.h"
 #include "ssa_liveness_analysis.h"
 
 namespace art {
@@ -31,10 +31,12 @@
   HGraphVisualizerPrinter(HGraph* graph,
                           std::ostream& output,
                           const char* pass_name,
+                          bool is_after_pass,
                           const CodeGenerator& codegen)
       : HGraphVisitor(graph),
         output_(output),
         pass_name_(pass_name),
+        is_after_pass_(is_after_pass),
         codegen_(codegen),
         indent_(0) {}
 
@@ -67,7 +69,7 @@
 
   void PrintTime(const char* name) {
     AddIndent();
-    output_ << name << " " << time(NULL) << std::endl;
+    output_ << name << " " << time(nullptr) << std::endl;
   }
 
   void PrintInt(const char* name, int value) {
@@ -137,14 +139,21 @@
       output_ << "invalid";
     } else if (location.IsStackSlot()) {
       output_ << location.GetStackIndex() << "(sp)";
+    } else if (location.IsFpuRegisterPair()) {
+      codegen_.DumpFloatingPointRegister(output_, location.low());
+      output_ << " and ";
+      codegen_.DumpFloatingPointRegister(output_, location.high());
+    } else if (location.IsRegisterPair()) {
+      codegen_.DumpCoreRegister(output_, location.low());
+      output_ << " and ";
+      codegen_.DumpCoreRegister(output_, location.high());
     } else {
       DCHECK(location.IsDoubleStackSlot());
       output_ << "2x" << location.GetStackIndex() << "(sp)";
     }
   }
 
-  void VisitParallelMove(HParallelMove* instruction) {
-    output_ << instruction->DebugName();
+  void VisitParallelMove(HParallelMove* instruction) OVERRIDE {
     output_ << " (";
     for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
       MoveOperands* move = instruction->MoveOperandsAt(i);
@@ -159,8 +168,25 @@
     output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
   }
 
-  void VisitInstruction(HInstruction* instruction) {
+  void VisitIntConstant(HIntConstant* instruction) OVERRIDE {
+    output_ << " " << instruction->GetValue();
+  }
+
+  void VisitLongConstant(HLongConstant* instruction) OVERRIDE {
+    output_ << " " << instruction->GetValue();
+  }
+
+  void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE {
+    output_ << " " << instruction->GetValue();
+  }
+
+  void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE {
+    output_ << " " << instruction->GetValue();
+  }
+
+  void PrintInstruction(HInstruction* instruction) {
     output_ << instruction->DebugName();
+    instruction->Accept(this);
     if (instruction->InputCount() > 0) {
       output_ << " [ ";
       for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
@@ -168,7 +194,22 @@
       }
       output_ << "]";
     }
-    if (pass_name_ == kLivenessPassName && instruction->GetLifetimePosition() != kNoLifetime) {
+    if (instruction->HasEnvironment()) {
+      HEnvironment* env = instruction->GetEnvironment();
+      output_ << " (env: [ ";
+      for (size_t i = 0, e = env->Size(); i < e; ++i) {
+        HInstruction* insn = env->GetInstructionAt(i);
+        if (insn != nullptr) {
+          output_ << GetTypeId(insn->GetType()) << insn->GetId() << " ";
+        } else {
+          output_ << " _ ";
+        }
+      }
+      output_ << "])";
+    }
+    if (pass_name_ == kLivenessPassName
+        && is_after_pass_
+        && instruction->GetLifetimePosition() != kNoLifetime) {
       output_ << " (liveness: " << instruction->GetLifetimePosition();
       if (instruction->HasLiveInterval()) {
         output_ << " ";
@@ -176,7 +217,7 @@
         interval.Dump(output_);
       }
       output_ << ")";
-    } else if (pass_name_ == kRegisterAllocatorPassName) {
+    } else if (pass_name_ == kRegisterAllocatorPassName && is_after_pass_) {
       LocationSummary* locations = instruction->GetLocations();
       if (locations != nullptr) {
         output_ << " ( ";
@@ -191,6 +232,14 @@
         }
       }
       output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
+    } else if (pass_name_ == kLoopInvariantCodeMotionPassName) {
+      output_ << " ( loop_header:";
+      HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+      if (info == nullptr) {
+        output_ << "null )";
+      } else {
+        output_ << "B" << info->GetHeader()->GetBlockId() << " )";
+      }
     }
   }
 
@@ -198,23 +247,30 @@
     const char* kEndInstructionMarker = "<|@";
     for (HInstructionIterator it(list); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
-      AddIndent();
       int bci = 0;
-      output_ << bci << " " << instruction->NumberOfUses()
-              << " " << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
-      instruction->Accept(this);
+      size_t num_uses = 0;
+      for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
+           !use_it.Done();
+           use_it.Advance()) {
+        ++num_uses;
+      }
+      AddIndent();
+      output_ << bci << " " << num_uses << " "
+              << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
+      PrintInstruction(instruction);
       output_ << kEndInstructionMarker << std::endl;
     }
   }
 
   void Run() {
     StartTag("cfg");
-    PrintProperty("name", pass_name_);
+    std::string pass_desc = std::string(pass_name_) + (is_after_pass_ ? " (after)" : " (before)");
+    PrintProperty("name", pass_desc.c_str());
     VisitInsertionOrder();
     EndTag("cfg");
   }
 
-  void VisitBasicBlock(HBasicBlock* block) {
+  void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
     StartTag("block");
     PrintProperty("name", "B", block->GetBlockId());
     if (block->GetLifetimeStart() != kNoLifetime) {
@@ -260,6 +316,7 @@
  private:
   std::ostream& output_;
   const char* pass_name_;
+  const bool is_after_pass_;
   const CodeGenerator& codegen_;
   size_t indent_;
 
@@ -268,51 +325,27 @@
 
 HGraphVisualizer::HGraphVisualizer(std::ostream* output,
                                    HGraph* graph,
-                                   const char* string_filter,
                                    const CodeGenerator& codegen,
-                                   const DexCompilationUnit& cu)
-    : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
-  if (output == nullptr) {
-    return;
-  }
-  std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile());
-  if (pretty_name.find(string_filter) == std::string::npos) {
-    return;
-  }
-
-  is_enabled_ = true;
-  HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
-  printer.StartTag("compilation");
-  printer.PrintProperty("name", pretty_name.c_str());
-  printer.PrintProperty("method", pretty_name.c_str());
-  printer.PrintTime("date");
-  printer.EndTag("compilation");
-}
-
-HGraphVisualizer::HGraphVisualizer(std::ostream* output,
-                                   HGraph* graph,
-                                   const CodeGenerator& codegen,
-                                   const char* name)
-    : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
+                                   const char* method_name)
+  : output_(output), graph_(graph), codegen_(codegen) {
   if (output == nullptr) {
     return;
   }
 
-  is_enabled_ = true;
-  HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
+  HGraphVisualizerPrinter printer(graph_, *output_, "", true, codegen_);
   printer.StartTag("compilation");
-  printer.PrintProperty("name", name);
-  printer.PrintProperty("method", name);
+  printer.PrintProperty("name", method_name);
+  printer.PrintProperty("method", method_name);
   printer.PrintTime("date");
   printer.EndTag("compilation");
 }
 
-void HGraphVisualizer::DumpGraph(const char* pass_name) const {
-  if (!is_enabled_) {
-    return;
+void HGraphVisualizer::DumpGraph(const char* pass_name, bool is_after_pass) const {
+  DCHECK(output_ != nullptr);
+  if (!graph_->GetBlocks().IsEmpty()) {
+    HGraphVisualizerPrinter printer(graph_, *output_, pass_name, is_after_pass, codegen_);
+    printer.Run();
   }
-  HGraphVisualizerPrinter printer(graph_, *output_, pass_name, codegen_);
-  printer.Run();
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 60d996b..bc553ae 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -27,52 +27,24 @@
 class DexCompilationUnit;
 class HGraph;
 
-// TODO: Create an analysis/optimization abstraction.
-static const char* kLivenessPassName = "liveness";
-static const char* kRegisterAllocatorPassName = "register";
-
 /**
- * If enabled, emits compilation information suitable for the c1visualizer tool
- * and IRHydra.
- * Currently only works if the compiler is single threaded.
+ * This class outputs the HGraph in the C1visualizer format.
+ * Note: Currently only works if the compiler is single threaded.
  */
 class HGraphVisualizer : public ValueObject {
  public:
-  /**
-   * If output is not null, and the method name of the dex compilation
-   * unit contains `string_filter`, the compilation information will be
-   * emitted.
-   */
-  HGraphVisualizer(std::ostream* output,
-                   HGraph* graph,
-                   const char* string_filter,
-                   const CodeGenerator& codegen,
-                   const DexCompilationUnit& cu);
-
-  /**
-   * Version of `HGraphVisualizer` for unit testing, that is when a
-   * `DexCompilationUnit` is not available.
-   */
   HGraphVisualizer(std::ostream* output,
                    HGraph* graph,
                    const CodeGenerator& codegen,
-                   const char* name);
+                   const char* method_name);
 
-  /**
-   * If this visualizer is enabled, emit the compilation information
-   * in `output_`.
-   */
-  void DumpGraph(const char* pass_name) const;
+  void DumpGraph(const char* pass_name, bool is_after_pass = true) const;
 
  private:
   std::ostream* const output_;
   HGraph* const graph_;
   const CodeGenerator& codegen_;
 
-  // Is true when `output_` is not null, and the compiled method's name
-  // contains the string_filter given in the constructor.
-  bool is_enabled_;
-
   DISALLOW_COPY_AND_ASSIGN(HGraphVisualizer);
 };
 
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 6e5f1bd..89bba2d 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -15,82 +15,251 @@
  */
 
 #include "gvn.h"
+#include "side_effects_analysis.h"
 
 namespace art {
 
-void GlobalValueNumberer::Run() {
-  ComputeSideEffects();
+/**
+ * A node in the collision list of a ValueSet. Encodes the instruction,
+ * the hash code, and the next node in the collision list.
+ */
+class ValueSetNode : public ArenaObject<kArenaAllocMisc> {
+ public:
+  ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
+      : instruction_(instruction), hash_code_(hash_code), next_(next) {}
 
+  size_t GetHashCode() const { return hash_code_; }
+  HInstruction* GetInstruction() const { return instruction_; }
+  ValueSetNode* GetNext() const { return next_; }
+  void SetNext(ValueSetNode* node) { next_ = node; }
+
+ private:
+  HInstruction* const instruction_;
+  const size_t hash_code_;
+  ValueSetNode* next_;
+
+  DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
+};
+
+/**
+ * A ValueSet holds instructions that can replace other instructions. It is updated
+ * through the `Add` method, and the `Kill` method. The `Kill` method removes
+ * instructions that are affected by the given side effect.
+ *
+ * The `Lookup` method returns an equivalent instruction to the given instruction
+ * if there is one in the set. In GVN, we would say those instructions have the
+ * same "number".
+ */
+class ValueSet : public ArenaObject<kArenaAllocMisc> {
+ public:
+  explicit ValueSet(ArenaAllocator* allocator)
+      : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      table_[i] = nullptr;
+    }
+  }
+
+  // Adds an instruction in the set.
+  void Add(HInstruction* instruction) {
+    DCHECK(Lookup(instruction) == nullptr);
+    size_t hash_code = instruction->ComputeHashCode();
+    size_t index = hash_code % kDefaultNumberOfEntries;
+    if (table_[index] == nullptr) {
+      table_[index] = instruction;
+    } else {
+      collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
+    }
+    ++number_of_entries_;
+  }
+
+  // If in the set, returns an equivalent instruction to the given instruction. Returns
+  // null otherwise.
+  HInstruction* Lookup(HInstruction* instruction) const {
+    size_t hash_code = instruction->ComputeHashCode();
+    size_t index = hash_code % kDefaultNumberOfEntries;
+    HInstruction* existing = table_[index];
+    if (existing != nullptr && existing->Equals(instruction)) {
+      return existing;
+    }
+
+    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+      if (node->GetHashCode() == hash_code) {
+        existing = node->GetInstruction();
+        if (existing->Equals(instruction)) {
+          return existing;
+        }
+      }
+    }
+    return nullptr;
+  }
+
+  // Returns whether `instruction` is in the set.
+  HInstruction* IdentityLookup(HInstruction* instruction) const {
+    size_t hash_code = instruction->ComputeHashCode();
+    size_t index = hash_code % kDefaultNumberOfEntries;
+    HInstruction* existing = table_[index];
+    if (existing != nullptr && existing == instruction) {
+      return existing;
+    }
+
+    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+      if (node->GetHashCode() == hash_code) {
+        existing = node->GetInstruction();
+        if (existing == instruction) {
+          return existing;
+        }
+      }
+    }
+    return nullptr;
+  }
+
+  // Removes all instructions in the set that are affected by the given side effects.
+  void Kill(SideEffects side_effects) {
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      HInstruction* instruction = table_[i];
+      if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
+        table_[i] = nullptr;
+        --number_of_entries_;
+      }
+    }
+
+    for (ValueSetNode* current = collisions_, *previous = nullptr;
+         current != nullptr;
+         current = current->GetNext()) {
+      HInstruction* instruction = current->GetInstruction();
+      if (instruction->GetSideEffects().DependsOn(side_effects)) {
+        if (previous == nullptr) {
+          collisions_ = current->GetNext();
+        } else {
+          previous->SetNext(current->GetNext());
+        }
+        --number_of_entries_;
+      } else {
+        previous = current;
+      }
+    }
+  }
+
+  // Returns a copy of this set.
+  ValueSet* Copy() const {
+    ValueSet* copy = new (allocator_) ValueSet(allocator_);
+
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      copy->table_[i] = table_[i];
+    }
+
+    // Note that the order will be inverted in the copy. This is fine, as the order is not
+    // relevant for a ValueSet.
+    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+      copy->collisions_ = new (allocator_) ValueSetNode(
+          node->GetInstruction(), node->GetHashCode(), copy->collisions_);
+    }
+
+    copy->number_of_entries_ = number_of_entries_;
+    return copy;
+  }
+
+  void Clear() {
+    number_of_entries_ = 0;
+    collisions_ = nullptr;
+    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+      table_[i] = nullptr;
+    }
+  }
+
+  // Update this `ValueSet` by intersecting with instructions in `other`.
+  void IntersectionWith(ValueSet* other) {
+    if (IsEmpty()) {
+      return;
+    } else if (other->IsEmpty()) {
+      Clear();
+    } else {
+      for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+        if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
+          --number_of_entries_;
+          table_[i] = nullptr;
+        }
+      }
+      for (ValueSetNode* current = collisions_, *previous = nullptr;
+           current != nullptr;
+           current = current->GetNext()) {
+        if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
+          if (previous == nullptr) {
+            collisions_ = current->GetNext();
+          } else {
+            previous->SetNext(current->GetNext());
+          }
+          --number_of_entries_;
+        } else {
+          previous = current;
+        }
+      }
+    }
+  }
+
+  bool IsEmpty() const { return number_of_entries_ == 0; }
+  size_t GetNumberOfEntries() const { return number_of_entries_; }
+
+ private:
+  static constexpr size_t kDefaultNumberOfEntries = 8;
+
+  ArenaAllocator* const allocator_;
+
+  // The number of entries in the set.
+  size_t number_of_entries_;
+
+  // The internal implementation of the set. It uses a combination of a hash code based
+  // fixed-size list, and a linked list to handle hash code collisions.
+  // TODO: Tune the fixed size list original size, and support growing it.
+  ValueSetNode* collisions_;
+  HInstruction* table_[kDefaultNumberOfEntries];
+
+  DISALLOW_COPY_AND_ASSIGN(ValueSet);
+};
+
+/**
+ * Optimization phase that removes redundant instruction.
+ */
+class GlobalValueNumberer : public ValueObject {
+ public:
+  GlobalValueNumberer(ArenaAllocator* allocator,
+                      HGraph* graph,
+                      const SideEffectsAnalysis& side_effects)
+      : graph_(graph),
+        allocator_(allocator),
+        side_effects_(side_effects),
+        sets_(allocator, graph->GetBlocks().Size(), nullptr) {}
+
+  void Run();
+
+ private:
+  // Per-block GVN. Will also update the ValueSet of the dominated and
+  // successor blocks.
+  void VisitBasicBlock(HBasicBlock* block);
+
+  HGraph* graph_;
+  ArenaAllocator* const allocator_;
+  const SideEffectsAnalysis& side_effects_;
+
+  // ValueSet for blocks. Initially null, but for an individual block they
+  // are allocated and populated by the dominator, and updated by all blocks
+  // in the path from the dominator to the block.
+  GrowableArray<ValueSet*> sets_;
+
+  DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
+};
+
+void GlobalValueNumberer::Run() {
+  DCHECK(side_effects_.HasRun());
   sets_.Put(graph_->GetEntryBlock()->GetBlockId(), new (allocator_) ValueSet(allocator_));
 
-  // Do reverse post order to ensure the non back-edge predecessors of a block are
+  // Use the reverse post order to ensure the non back-edge predecessors of a block are
   // visited before the block itself.
   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     VisitBasicBlock(it.Current());
   }
 }
 
-void GlobalValueNumberer::UpdateLoopEffects(HLoopInformation* info, SideEffects effects) {
-  int id = info->GetHeader()->GetBlockId();
-  loop_effects_.Put(id, loop_effects_.Get(id).Union(effects));
-}
-
-void GlobalValueNumberer::ComputeSideEffects() {
-  if (kIsDebugBuild) {
-    for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
-      HBasicBlock* block = it.Current();
-      SideEffects effects = GetBlockEffects(block);
-      DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
-      if (block->IsLoopHeader()) {
-        effects = GetLoopEffects(block);
-        DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
-      }
-    }
-  }
-
-  // Do a post order visit to ensure we visit a loop header after its loop body.
-  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
-    HBasicBlock* block = it.Current();
-
-    SideEffects effects = SideEffects::None();
-    // Update `effects` with the side effects of all instructions in this block.
-    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
-         inst_it.Advance()) {
-      HInstruction* instruction = inst_it.Current();
-      effects = effects.Union(instruction->GetSideEffects());
-      if (effects.HasAllSideEffects()) {
-        break;
-      }
-    }
-
-    block_effects_.Put(block->GetBlockId(), effects);
-
-    if (block->IsLoopHeader()) {
-      // The side effects of the loop header are part of the loop.
-      UpdateLoopEffects(block->GetLoopInformation(), effects);
-      HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
-      if (pre_header->IsInLoop()) {
-        // Update the side effects of the outer loop with the side effects of the inner loop.
-        // Note that this works because we know all the blocks of the inner loop are visited
-        // before the loop header of the outer loop.
-        UpdateLoopEffects(pre_header->GetLoopInformation(), GetLoopEffects(block));
-      }
-    } else if (block->IsInLoop()) {
-      // Update the side effects of the loop with the side effects of this block.
-      UpdateLoopEffects(block->GetLoopInformation(), effects);
-    }
-  }
-}
-
-SideEffects GlobalValueNumberer::GetLoopEffects(HBasicBlock* block) const {
-  DCHECK(block->IsLoopHeader());
-  return loop_effects_.Get(block->GetBlockId());
-}
-
-SideEffects GlobalValueNumberer::GetBlockEffects(HBasicBlock* block) const {
-  return block_effects_.Get(block->GetBlockId());
-}
-
 void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
   ValueSet* set = nullptr;
   const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
@@ -110,7 +279,7 @@
     if (!set->IsEmpty()) {
       if (block->IsLoopHeader()) {
         DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader());
-        set->Kill(GetLoopEffects(block));
+        set->Kill(side_effects_.GetLoopEffects(block));
       } else if (predecessors.Size() > 1) {
         for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
           set->IntersectionWith(sets_.Get(predecessors.Get(i)->GetBlockId()));
@@ -142,4 +311,9 @@
   }
 }
 
+void GVNOptimization::Run() {
+  GlobalValueNumberer gvn(graph_->GetArena(), graph_, side_effects_);
+  gvn.Run();
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index 81f2c3f..57e0487 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -22,272 +22,18 @@
 
 namespace art {
 
-/**
- * A node in the collision list of a ValueSet. Encodes the instruction,
- * the hash code, and the next node in the collision list.
- */
-class ValueSetNode : public ArenaObject<kArenaAllocMisc> {
- public:
-  ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
-      : instruction_(instruction), hash_code_(hash_code), next_(next) {}
-
-  size_t GetHashCode() const { return hash_code_; }
-  HInstruction* GetInstruction() const { return instruction_; }
-  ValueSetNode* GetNext() const { return next_; }
-  void SetNext(ValueSetNode* node) { next_ = node; }
-
- private:
-  HInstruction* const instruction_;
-  const size_t hash_code_;
-  ValueSetNode* next_;
-
-  DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
-};
-
-/**
- * A ValueSet holds instructions that can replace other instructions. It is updated
- * through the `Add` method, and the `Kill` method. The `Kill` method removes
- * instructions that are affected by the given side effect.
- *
- * The `Lookup` method returns an equivalent instruction to the given instruction
- * if there is one in the set. In GVN, we would say those instructions have the
- * same "number".
- */
-class ValueSet : public ArenaObject<kArenaAllocMisc> {
- public:
-  explicit ValueSet(ArenaAllocator* allocator)
-      : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
-    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
-      table_[i] = nullptr;
-    }
-  }
-
-  // Adds an instruction in the set.
-  void Add(HInstruction* instruction) {
-    DCHECK(Lookup(instruction) == nullptr);
-    size_t hash_code = instruction->ComputeHashCode();
-    size_t index = hash_code % kDefaultNumberOfEntries;
-    if (table_[index] == nullptr) {
-      table_[index] = instruction;
-    } else {
-      collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
-    }
-    ++number_of_entries_;
-  }
-
-  // If in the set, returns an equivalent instruction to the given instruction. Returns
-  // null otherwise.
-  HInstruction* Lookup(HInstruction* instruction) const {
-    size_t hash_code = instruction->ComputeHashCode();
-    size_t index = hash_code % kDefaultNumberOfEntries;
-    HInstruction* existing = table_[index];
-    if (existing != nullptr && existing->Equals(instruction)) {
-      return existing;
-    }
-
-    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
-      if (node->GetHashCode() == hash_code) {
-        existing = node->GetInstruction();
-        if (existing->Equals(instruction)) {
-          return existing;
-        }
-      }
-    }
-    return nullptr;
-  }
-
-  // Returns whether `instruction` is in the set.
-  HInstruction* IdentityLookup(HInstruction* instruction) const {
-    size_t hash_code = instruction->ComputeHashCode();
-    size_t index = hash_code % kDefaultNumberOfEntries;
-    HInstruction* existing = table_[index];
-    if (existing != nullptr && existing == instruction) {
-      return existing;
-    }
-
-    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
-      if (node->GetHashCode() == hash_code) {
-        existing = node->GetInstruction();
-        if (existing == instruction) {
-          return existing;
-        }
-      }
-    }
-    return nullptr;
-  }
-
-  // Removes all instructions in the set that are affected by the given side effects.
-  void Kill(SideEffects side_effects) {
-    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
-      HInstruction* instruction = table_[i];
-      if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
-        table_[i] = nullptr;
-        --number_of_entries_;
-      }
-    }
-
-    for (ValueSetNode* current = collisions_, *previous = nullptr;
-         current != nullptr;
-         current = current->GetNext()) {
-      HInstruction* instruction = current->GetInstruction();
-      if (instruction->GetSideEffects().DependsOn(side_effects)) {
-        if (previous == nullptr) {
-          collisions_ = current->GetNext();
-        } else {
-          previous->SetNext(current->GetNext());
-        }
-        --number_of_entries_;
-      } else {
-        previous = current;
-      }
-    }
-  }
-
-  // Returns a copy of this set.
-  ValueSet* Copy() const {
-    ValueSet* copy = new (allocator_) ValueSet(allocator_);
-
-    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
-      copy->table_[i] = table_[i];
-    }
-
-    // Note that the order will be inverted in the copy. This is fine, as the order is not
-    // relevant for a ValueSet.
-    for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
-      copy->collisions_ = new (allocator_) ValueSetNode(
-          node->GetInstruction(), node->GetHashCode(), copy->collisions_);
-    }
-
-    copy->number_of_entries_ = number_of_entries_;
-    return copy;
-  }
-
-  void Clear() {
-    number_of_entries_ = 0;
-    collisions_ = nullptr;
-    for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
-      table_[i] = nullptr;
-    }
-  }
-
-  // Update this `ValueSet` by intersecting with instructions in `other`.
-  void IntersectionWith(ValueSet* other) {
-    if (IsEmpty()) {
-      return;
-    } else if (other->IsEmpty()) {
-      Clear();
-    } else {
-      for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
-        if (table_[i] != nullptr && other->IdentityLookup(table_[i]) == nullptr) {
-          --number_of_entries_;
-          table_[i] = nullptr;
-        }
-      }
-      for (ValueSetNode* current = collisions_, *previous = nullptr;
-           current != nullptr;
-           current = current->GetNext()) {
-        if (other->IdentityLookup(current->GetInstruction()) == nullptr) {
-          if (previous == nullptr) {
-            collisions_ = current->GetNext();
-          } else {
-            previous->SetNext(current->GetNext());
-          }
-          --number_of_entries_;
-        } else {
-          previous = current;
-        }
-      }
-    }
-  }
-
-  bool IsEmpty() const { return number_of_entries_ == 0; }
-  size_t GetNumberOfEntries() const { return number_of_entries_; }
-
- private:
-  static constexpr size_t kDefaultNumberOfEntries = 8;
-
-  ArenaAllocator* const allocator_;
-
-  // The number of entries in the set.
-  size_t number_of_entries_;
-
-  // The internal implementation of the set. It uses a combination of a hash code based
-  // fixed-size list, and a linked list to handle hash code collisions.
-  // TODO: Tune the fixed size list original size, and support growing it.
-  ValueSetNode* collisions_;
-  HInstruction* table_[kDefaultNumberOfEntries];
-
-  DISALLOW_COPY_AND_ASSIGN(ValueSet);
-};
-
-/**
- * Optimization phase that removes redundant instruction.
- */
-class GlobalValueNumberer : public ValueObject {
- public:
-  GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph)
-      : graph_(graph),
-        allocator_(allocator),
-        block_effects_(allocator, graph->GetBlocks().Size()),
-        loop_effects_(allocator, graph->GetBlocks().Size()),
-        sets_(allocator, graph->GetBlocks().Size()) {
-    size_t number_of_blocks = graph->GetBlocks().Size();
-    block_effects_.SetSize(number_of_blocks);
-    loop_effects_.SetSize(number_of_blocks);
-    sets_.SetSize(number_of_blocks);
-
-    for (size_t i = 0; i < number_of_blocks; ++i) {
-      block_effects_.Put(i, SideEffects::None());
-      loop_effects_.Put(i, SideEffects::None());
-    }
-  }
-
-  void Run();
-
- private:
-  // Per-block GVN. Will also update the ValueSet of the dominated and
-  // successor blocks.
-  void VisitBasicBlock(HBasicBlock* block);
-
-  // Compute side effects of individual blocks and loops. The GVN algorithm
-  // will use these side effects to update the ValueSet of individual blocks.
-  void ComputeSideEffects();
-
-  void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
-  SideEffects GetLoopEffects(HBasicBlock* block) const;
-  SideEffects GetBlockEffects(HBasicBlock* block) const;
-
-  HGraph* graph_;
-
-  ArenaAllocator* const allocator_;
-
-  // Side effects of individual blocks, that is the union of the side effects
-  // of the instructions in the block.
-  GrowableArray<SideEffects> block_effects_;
-
-  // Side effects of loops, that is the union of the side effects of the
-  // blocks contained in that loop.
-  GrowableArray<SideEffects> loop_effects_;
-
-  // ValueSet for blocks. Initially null, but for an individual block they
-  // are allocated and populated by the dominator, and updated by all blocks
-  // in the path from the dominator to the block.
-  GrowableArray<ValueSet*> sets_;
-
-  ART_FRIEND_TEST(GVNTest, LoopSideEffects);
-  DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
-};
+class SideEffectsAnalysis;
 
 class GVNOptimization : public HOptimization {
  public:
-  explicit GVNOptimization(HGraph* graph) : HOptimization(graph, true, "GVN") {}
+  GVNOptimization(HGraph* graph, const SideEffectsAnalysis& side_effects)
+      : HOptimization(graph, true, "GVN"), side_effects_(side_effects) {}
 
-  void Run() OVERRIDE {
-    GlobalValueNumberer gvn(graph_->GetArena(), graph_);
-    gvn.Run();
-  }
+  void Run() OVERRIDE;
 
  private:
+  const SideEffectsAnalysis& side_effects_;
+
   DISALLOW_COPY_AND_ASSIGN(GVNOptimization);
 };
 
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index a6a68ca..4a48fee 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -18,6 +18,7 @@
 #include "gvn.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
+#include "side_effects_analysis.h"
 #include "utils/arena_allocator.h"
 
 #include "gtest/gtest.h"
@@ -40,18 +41,22 @@
   entry->AddSuccessor(block);
 
   block->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+          MemberOffset(42), false));
   block->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+          MemberOffset(42), false));
   HInstruction* to_remove = block->GetLastInstruction();
   block->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(43)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+          MemberOffset(43), false));
   HInstruction* different_offset = block->GetLastInstruction();
   // Kill the value.
   block->AddInstruction(new (&allocator) HInstanceFieldSet(
-      parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+      parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
   block->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
+          MemberOffset(42), false));
   HInstruction* use_after_kill = block->GetLastInstruction();
   block->AddInstruction(new (&allocator) HExit());
 
@@ -59,9 +64,10 @@
   ASSERT_EQ(different_offset->GetBlock(), block);
   ASSERT_EQ(use_after_kill->GetBlock(), block);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  GlobalValueNumberer(&allocator, graph).Run();
+  graph->TryBuildingSsa();
+  SideEffectsAnalysis side_effects(graph);
+  side_effects.Run();
+  GVNOptimization(graph, side_effects).Run();
 
   ASSERT_TRUE(to_remove->GetBlock() == nullptr);
   ASSERT_EQ(different_offset->GetBlock(), block);
@@ -83,7 +89,8 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   block->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
 
   block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
   HBasicBlock* then = new (&allocator) HBasicBlock(graph);
@@ -99,18 +106,22 @@
   else_->AddSuccessor(join);
 
   then->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   then->AddInstruction(new (&allocator) HGoto());
   else_->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   else_->AddInstruction(new (&allocator) HGoto());
   join->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   join->AddInstruction(new (&allocator) HExit());
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  GlobalValueNumberer(&allocator, graph).Run();
+  graph->TryBuildingSsa();
+  SideEffectsAnalysis side_effects(graph);
+  side_effects.Run();
+  GVNOptimization(graph, side_effects).Run();
 
   // Check that all field get instructions have been GVN'ed.
   ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
@@ -134,7 +145,8 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
   block->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   block->AddInstruction(new (&allocator) HGoto());
 
   HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
@@ -150,22 +162,25 @@
   loop_body->AddSuccessor(loop_header);
 
   loop_header->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
   loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
 
   // Kill inside the loop body to prevent field gets inside the loop header
   // and the body to be GVN'ed.
   loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
-      parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+      parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
   HInstruction* field_set = loop_body->GetLastInstruction();
   loop_body->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
   loop_body->AddInstruction(new (&allocator) HGoto());
 
   exit->AddInstruction(
-      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+      new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
+          MemberOffset(42), false));
   HInstruction* field_get_in_exit = exit->GetLastInstruction();
   exit->AddInstruction(new (&allocator) HExit());
 
@@ -173,10 +188,12 @@
   ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
   ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
-  GlobalValueNumberer(&allocator, graph).Run();
+  graph->TryBuildingSsa();
+  {
+    SideEffectsAnalysis side_effects(graph);
+    side_effects.Run();
+    GVNOptimization(graph, side_effects).Run();
+  }
 
   // Check that all field get instructions are still there.
   ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
@@ -187,7 +204,11 @@
 
   // Now remove the field set, and check that all field get instructions have been GVN'ed.
   loop_body->RemoveInstruction(field_set);
-  GlobalValueNumberer(&allocator, graph).Run();
+  {
+    SideEffectsAnalysis side_effects(graph);
+    side_effects.Run();
+    GVNOptimization(graph, side_effects).Run();
+  }
 
   ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
   ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
@@ -237,9 +258,7 @@
   inner_loop_exit->AddInstruction(new (&allocator) HGoto());
   outer_loop_exit->AddInstruction(new (&allocator) HExit());
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
 
   ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
       *outer_loop_header->GetLoopInformation()));
@@ -248,30 +267,30 @@
   {
     // Make one block with a side effect.
     entry->AddInstruction(new (&allocator) HInstanceFieldSet(
-        parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+        parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
 
-    GlobalValueNumberer gvn(&allocator, graph);
-    gvn.Run();
+    SideEffectsAnalysis side_effects(graph);
+    side_effects.Run();
 
-    ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
-    ASSERT_FALSE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
-    ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
+    ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
+    ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
   }
 
   // Check that the side effects of the outer loop does not affect the inner loop.
   {
     outer_loop_body->InsertInstructionBefore(
         new (&allocator) HInstanceFieldSet(
-            parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+            parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
         outer_loop_body->GetLastInstruction());
 
-    GlobalValueNumberer gvn(&allocator, graph);
-    gvn.Run();
+    SideEffectsAnalysis side_effects(graph);
+    side_effects.Run();
 
-    ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
-    ASSERT_TRUE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
-    ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
-    ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
+    ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
   }
 
   // Check that the side effects of the inner loop affects the outer loop.
@@ -279,16 +298,16 @@
     outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
     inner_loop_body->InsertInstructionBefore(
         new (&allocator) HInstanceFieldSet(
-            parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+            parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
         inner_loop_body->GetLastInstruction());
 
-    GlobalValueNumberer gvn(&allocator, graph);
-    gvn.Run();
+    SideEffectsAnalysis side_effects(graph);
+    side_effects.Run();
 
-    ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
-    ASSERT_FALSE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
-    ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
-    ASSERT_TRUE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
+    ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
+    ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
   }
 }
 }  // namespace art
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
new file mode 100644
index 0000000..32f6972
--- /dev/null
+++ b/compiler/optimizing/inliner.cc
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+#include "inliner.h"
+
+#include "builder.h"
+#include "class_linker.h"
+#include "constant_folding.h"
+#include "dead_code_elimination.h"
+#include "driver/compiler_driver-inl.h"
+#include "driver/dex_compilation_unit.h"
+#include "instruction_simplifier.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache.h"
+#include "nodes.h"
+#include "register_allocator.h"
+#include "ssa_phi_elimination.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+static constexpr int kMaxInlineCodeUnits = 100;
+static constexpr int kDepthLimit = 5;
+
+void HInliner::Run() {
+  const GrowableArray<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+  for (size_t i = 0; i < blocks.Size(); ++i) {
+    HBasicBlock* block = blocks.Get(i);
+    for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
+      HInstruction* next = instruction->GetNext();
+      HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
+      if (call != nullptr) {
+        if (!TryInline(call, call->GetDexMethodIndex(), call->GetInvokeType())) {
+          if (kIsDebugBuild) {
+            std::string callee_name =
+                PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
+            bool should_inline = callee_name.find("$inline$") != std::string::npos;
+            CHECK(!should_inline) << "Could not inline " << callee_name;
+          }
+        }
+      }
+      instruction = next;
+    }
+  }
+}
+
+bool HInliner::TryInline(HInvoke* invoke_instruction,
+                         uint32_t method_index,
+                         InvokeType invoke_type) const {
+  ScopedObjectAccess soa(Thread::Current());
+  const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
+  VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, outer_dex_file);
+
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::DexCache> dex_cache(
+      hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file)));
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader())));
+  Handle<mirror::ArtMethod> resolved_method(hs.NewHandle(
+      compiler_driver_->ResolveMethod(
+          soa, dex_cache, class_loader, &outer_compilation_unit_, method_index, invoke_type)));
+
+  if (resolved_method.Get() == nullptr) {
+    VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file);
+    return false;
+  }
+
+  if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
+    VLOG(compiler) << "Did not inline "
+                   << PrettyMethod(method_index, outer_dex_file)
+                   << " because it is in a different dex file";
+    return false;
+  }
+
+  const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
+
+  if (code_item == nullptr) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " is not inlined because it is native";
+    return false;
+  }
+
+  if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " is too big to inline";
+    return false;
+  }
+
+  if (code_item->tries_size_ != 0) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " is not inlined because of try block";
+    return false;
+  }
+
+  if (!resolved_method->GetDeclaringClass()->IsVerified()) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " is not inlined because its class could not be verified";
+    return false;
+  }
+
+  DexCompilationUnit dex_compilation_unit(
+    nullptr,
+    outer_compilation_unit_.GetClassLoader(),
+    outer_compilation_unit_.GetClassLinker(),
+    outer_dex_file,
+    code_item,
+    resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
+    method_index,
+    resolved_method->GetAccessFlags(),
+    nullptr);
+
+  HGraph* callee_graph =
+      new (graph_->GetArena()) HGraph(graph_->GetArena(), graph_->GetCurrentInstructionId());
+
+  OptimizingCompilerStats inline_stats;
+  HGraphBuilder builder(callee_graph,
+                        &dex_compilation_unit,
+                        &outer_compilation_unit_,
+                        &outer_dex_file,
+                        compiler_driver_,
+                        &inline_stats);
+
+  if (!builder.BuildGraph(*code_item)) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " could not be built, so cannot be inlined";
+    return false;
+  }
+
+  if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
+                                                  compiler_driver_->GetInstructionSet())) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " cannot be inlined because of the register allocator";
+    return false;
+  }
+
+  if (!callee_graph->TryBuildingSsa()) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " could not be transformed to SSA";
+    return false;
+  }
+
+  // Run simple optimizations on the graph.
+  SsaRedundantPhiElimination redundant_phi(callee_graph);
+  SsaDeadPhiElimination dead_phi(callee_graph);
+  HDeadCodeElimination dce(callee_graph);
+  HConstantFolding fold(callee_graph);
+  InstructionSimplifier simplify(callee_graph);
+
+  HOptimization* optimizations[] = {
+    &redundant_phi,
+    &dead_phi,
+    &dce,
+    &fold,
+    &simplify,
+  };
+
+  for (size_t i = 0; i < arraysize(optimizations); ++i) {
+    HOptimization* optimization = optimizations[i];
+    optimization->Run();
+  }
+
+  if (depth_ + 1 < kDepthLimit) {
+    HInliner inliner(
+        callee_graph, outer_compilation_unit_, compiler_driver_, outer_stats_, depth_ + 1);
+    inliner.Run();
+  }
+
+  HReversePostOrderIterator it(*callee_graph);
+  it.Advance();  // Past the entry block, it does not contain instructions that prevent inlining.
+  for (; !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    if (block->IsLoopHeader()) {
+      VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                     << " could not be inlined because it contains a loop";
+      return false;
+    }
+
+    for (HInstructionIterator instr_it(block->GetInstructions());
+         !instr_it.Done();
+         instr_it.Advance()) {
+      HInstruction* current = instr_it.Current();
+      if (current->IsSuspendCheck()) {
+        continue;
+      }
+
+      if (current->CanThrow()) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                       << " could not be inlined because " << current->DebugName()
+                       << " can throw";
+        return false;
+      }
+
+      if (current->NeedsEnvironment()) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                       << " could not be inlined because " << current->DebugName()
+                       << " needs an environment";
+        return false;
+      }
+    }
+  }
+
+  callee_graph->InlineInto(graph_, invoke_instruction);
+
+  // Now that we have inlined the callee, we need to update the next
+  // instruction id of the caller, so that new instructions added
+  // after optimizations get a unique id.
+  graph_->SetCurrentInstructionId(callee_graph->GetNextInstructionId());
+  VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file);
+  outer_stats_->RecordStat(kInlinedInvoke);
+  return true;
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
new file mode 100644
index 0000000..07d893e
--- /dev/null
+++ b/compiler/optimizing/inliner.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
+#define ART_COMPILER_OPTIMIZING_INLINER_H_
+
+#include "invoke_type.h"
+#include "optimization.h"
+
+namespace art {
+
+class CompilerDriver;
+class DexCompilationUnit;
+class HGraph;
+class HInvoke;
+class OptimizingCompilerStats;
+
+class HInliner : public HOptimization {
+ public:
+  HInliner(HGraph* outer_graph,
+           const DexCompilationUnit& outer_compilation_unit,
+           CompilerDriver* compiler_driver,
+           OptimizingCompilerStats* stats,
+           size_t depth = 0)
+      : HOptimization(outer_graph, true, "inliner"),
+        outer_compilation_unit_(outer_compilation_unit),
+        compiler_driver_(compiler_driver),
+        outer_stats_(stats),
+        depth_(depth) {}
+
+  void Run() OVERRIDE;
+
+ private:
+  bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
+
+  const DexCompilationUnit& outer_compilation_unit_;
+  CompilerDriver* const compiler_driver_;
+  OptimizingCompilerStats* const outer_stats_;
+  const size_t depth_;
+
+  DISALLOW_COPY_AND_ASSIGN(HInliner);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INLINER_H_
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 49ca443..44dbb9d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -27,6 +27,8 @@
   void VisitEqual(HEqual* equal) OVERRIDE;
   void VisitArraySet(HArraySet* equal) OVERRIDE;
   void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
+  void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
+  void VisitArrayLength(HArrayLength* instruction) OVERRIDE;
 };
 
 void InstructionSimplifier::Run() {
@@ -34,6 +36,14 @@
   visitor.VisitInsertionOrder();
 }
 
+void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) {
+  HInstruction* obj = null_check->InputAt(0);
+  if (!obj->CanBeNull()) {
+    null_check->ReplaceWith(obj);
+    null_check->GetBlock()->RemoveInstruction(null_check);
+  }
+}
+
 void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
   HBasicBlock* block = check->GetBlock();
   // Currently always keep the suspend check at entry.
@@ -59,10 +69,21 @@
       equal->ReplaceWith(equal->InputAt(0));
       equal->GetBlock()->RemoveInstruction(equal);
     } else {
-      // Replace (bool_value == 0) with !bool_value
+      // We should replace (bool_value == 0) with !bool_value, but we unfortunately
+      // do not have such instruction.
       DCHECK_EQ(input2->AsIntConstant()->GetValue(), 0);
-      equal->GetBlock()->ReplaceAndRemoveInstructionWith(
-          equal, new (GetGraph()->GetArena()) HNot(Primitive::kPrimBoolean, input1));
+    }
+  }
+}
+
+void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) {
+  HInstruction* input = instruction->InputAt(0);
+  // If the array is a NewArray with constant size, replace the array length
+  // with the constant instruction. This helps the bounds check elimination phase.
+  if (input->IsNewArray()) {
+    input = input->InputAt(0);
+    if (input->IsIntConstant()) {
+      instruction->ReplaceWith(input);
     }
   }
 }
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index 7068c7f..bca6697 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -27,8 +27,8 @@
  */
 class InstructionSimplifier : public HOptimization {
  public:
-  explicit InstructionSimplifier(HGraph* graph)
-    : HOptimization(graph, true, "instruction_simplifier") {}
+  explicit InstructionSimplifier(HGraph* graph, const char* name = "instruction_simplifier")
+    : HOptimization(graph, true, name) {}
 
   void Run() OVERRIDE;
 };
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
new file mode 100644
index 0000000..36cf856
--- /dev/null
+++ b/compiler/optimizing/intrinsics.cc
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "intrinsics.h"
+
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_driver.h"
+#include "invoke_type.h"
+#include "nodes.h"
+#include "quick/inline_method_analyser.h"
+
+namespace art {
+
+// Function that returns whether an intrinsic is static/direct or virtual.
+static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
+  switch (i) {
+    case Intrinsics::kNone:
+      return kInterface;  // Non-sensical for intrinsic.
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+    case Intrinsics::k ## Name:               \
+      return IsStatic;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+  }
+  return kInterface;
+}
+
+
+
+static Primitive::Type GetType(uint64_t data, bool is_op_size) {
+  if (is_op_size) {
+    switch (static_cast<OpSize>(data)) {
+      case kSignedByte:
+        return Primitive::kPrimByte;
+      case kSignedHalf:
+        return Primitive::kPrimShort;
+      case k32:
+        return Primitive::kPrimInt;
+      case k64:
+        return Primitive::kPrimLong;
+      default:
+        LOG(FATAL) << "Unknown/unsupported op size " << data;
+        UNREACHABLE();
+    }
+  } else {
+    if ((data & kIntrinsicFlagIsLong) != 0) {
+      return Primitive::kPrimLong;
+    }
+    if ((data & kIntrinsicFlagIsObject) != 0) {
+      return Primitive::kPrimNot;
+    }
+    return Primitive::kPrimInt;
+  }
+}
+
+static Intrinsics GetIntrinsic(InlineMethod method) {
+  switch (method.opcode) {
+    // Floating-point conversions.
+    case kIntrinsicDoubleCvt:
+      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
+          Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
+    case kIntrinsicFloatCvt:
+      return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
+          Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
+
+    // Bit manipulations.
+    case kIntrinsicReverseBits:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimInt:
+          return Intrinsics::kIntegerReverse;
+        case Primitive::kPrimLong:
+          return Intrinsics::kLongReverse;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+    case kIntrinsicReverseBytes:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimShort:
+          return Intrinsics::kShortReverseBytes;
+        case Primitive::kPrimInt:
+          return Intrinsics::kIntegerReverseBytes;
+        case Primitive::kPrimLong:
+          return Intrinsics::kLongReverseBytes;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+
+    // Abs.
+    case kIntrinsicAbsDouble:
+      return Intrinsics::kMathAbsDouble;
+    case kIntrinsicAbsFloat:
+      return Intrinsics::kMathAbsFloat;
+    case kIntrinsicAbsInt:
+      return Intrinsics::kMathAbsInt;
+    case kIntrinsicAbsLong:
+      return Intrinsics::kMathAbsLong;
+
+    // Min/max.
+    case kIntrinsicMinMaxDouble:
+      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+          Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
+    case kIntrinsicMinMaxFloat:
+      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+          Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
+    case kIntrinsicMinMaxInt:
+      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+          Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
+    case kIntrinsicMinMaxLong:
+      return ((method.d.data & kIntrinsicFlagMin) == 0) ?
+          Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
+
+    // Misc math.
+    case kIntrinsicSqrt:
+      return Intrinsics::kMathSqrt;
+    case kIntrinsicCeil:
+      return Intrinsics::kMathCeil;
+    case kIntrinsicFloor:
+      return Intrinsics::kMathFloor;
+    case kIntrinsicRint:
+      return Intrinsics::kMathRint;
+    case kIntrinsicRoundDouble:
+      return Intrinsics::kMathRoundDouble;
+    case kIntrinsicRoundFloat:
+      return Intrinsics::kMathRoundFloat;
+
+    // System.arraycopy.
+    case kIntrinsicSystemArrayCopyCharArray:
+      return Intrinsics::kSystemArrayCopyChar;
+
+    // Thread.currentThread.
+    case kIntrinsicCurrentThread:
+      return  Intrinsics::kThreadCurrentThread;
+
+    // Memory.peek.
+    case kIntrinsicPeek:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimByte:
+          return Intrinsics::kMemoryPeekByte;
+        case Primitive::kPrimShort:
+          return Intrinsics::kMemoryPeekShortNative;
+        case Primitive::kPrimInt:
+          return Intrinsics::kMemoryPeekIntNative;
+        case Primitive::kPrimLong:
+          return Intrinsics::kMemoryPeekLongNative;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+
+    // Memory.poke.
+    case kIntrinsicPoke:
+      switch (GetType(method.d.data, true)) {
+        case Primitive::kPrimByte:
+          return Intrinsics::kMemoryPokeByte;
+        case Primitive::kPrimShort:
+          return Intrinsics::kMemoryPokeShortNative;
+        case Primitive::kPrimInt:
+          return Intrinsics::kMemoryPokeIntNative;
+        case Primitive::kPrimLong:
+          return Intrinsics::kMemoryPokeLongNative;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+
+    // String.
+    case kIntrinsicCharAt:
+      return Intrinsics::kStringCharAt;
+    case kIntrinsicCompareTo:
+      return Intrinsics::kStringCompareTo;
+    case kIntrinsicIsEmptyOrLength:
+      return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ?
+          Intrinsics::kStringLength : Intrinsics::kStringIsEmpty;
+    case kIntrinsicIndexOf:
+      return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
+          Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
+
+    case kIntrinsicCas:
+      switch (GetType(method.d.data, false)) {
+        case Primitive::kPrimNot:
+          return Intrinsics::kUnsafeCASObject;
+        case Primitive::kPrimInt:
+          return Intrinsics::kUnsafeCASInt;
+        case Primitive::kPrimLong:
+          return Intrinsics::kUnsafeCASLong;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+    case kIntrinsicUnsafeGet: {
+      const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
+      switch (GetType(method.d.data, false)) {
+        case Primitive::kPrimInt:
+          return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
+        case Primitive::kPrimLong:
+          return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
+        case Primitive::kPrimNot:
+          return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+    }
+    case kIntrinsicUnsafePut: {
+      enum Sync { kNoSync, kVolatile, kOrdered };
+      const Sync sync =
+          ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
+          ((method.d.data & kIntrinsicFlagIsOrdered) != 0)  ? kOrdered :
+                                                              kNoSync;
+      switch (GetType(method.d.data, false)) {
+        case Primitive::kPrimInt:
+          switch (sync) {
+            case kNoSync:
+              return Intrinsics::kUnsafePut;
+            case kVolatile:
+              return Intrinsics::kUnsafePutVolatile;
+            case kOrdered:
+              return Intrinsics::kUnsafePutOrdered;
+          }
+          break;
+        case Primitive::kPrimLong:
+          switch (sync) {
+            case kNoSync:
+              return Intrinsics::kUnsafePutLong;
+            case kVolatile:
+              return Intrinsics::kUnsafePutLongVolatile;
+            case kOrdered:
+              return Intrinsics::kUnsafePutLongOrdered;
+          }
+          break;
+        case Primitive::kPrimNot:
+          switch (sync) {
+            case kNoSync:
+              return Intrinsics::kUnsafePutObject;
+            case kVolatile:
+              return Intrinsics::kUnsafePutObjectVolatile;
+            case kOrdered:
+              return Intrinsics::kUnsafePutObjectOrdered;
+          }
+          break;
+        default:
+          LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+          UNREACHABLE();
+      }
+      break;
+    }
+
+    // Virtual cases.
+
+    case kIntrinsicReferenceGetReferent:
+      return Intrinsics::kReferenceGetReferent;
+
+    // Quick inliner cases. Remove after refactoring. They are here so that we can use the
+    // compiler to warn on missing cases.
+
+    case kInlineOpNop:
+    case kInlineOpReturnArg:
+    case kInlineOpNonWideConst:
+    case kInlineOpIGet:
+    case kInlineOpIPut:
+      return Intrinsics::kNone;
+
+    // No default case to make the compiler warn on missing cases.
+  }
+  return Intrinsics::kNone;
+}
+
+static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
+  // The DexFileMethodInliner should have checked whether the methods are agreeing with
+  // what we expect, i.e., static methods are called as such. Add another check here for
+  // our expectations:
+  // Whenever the intrinsic is marked as static-or-direct, report an error if we find an
+  // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual
+  // functions that will perform a check inline. If the precise type is known, however,
+  // the instruction will be sharpened to an InvokeStaticOrDirect.
+  InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
+  InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
+      invoke->AsInvokeStaticOrDirect()->GetInvokeType() :
+      invoke->IsInvokeVirtual() ? kVirtual : kSuper;
+  switch (intrinsic_type) {
+    case kStatic:
+      return (invoke_type == kStatic);
+    case kDirect:
+      return (invoke_type == kDirect);
+    case kVirtual:
+      // Call might be devirtualized.
+      return (invoke_type == kVirtual || invoke_type == kDirect);
+
+    default:
+      return false;
+  }
+}
+
+// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
+void IntrinsicsRecognizer::Run() {
+  DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(dex_file_);
+  DCHECK(inliner != nullptr);
+
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+         inst_it.Advance()) {
+      HInstruction* inst = inst_it.Current();
+      if (inst->IsInvoke()) {
+        HInvoke* invoke = inst->AsInvoke();
+        InlineMethod method;
+        if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
+          Intrinsics intrinsic = GetIntrinsic(method);
+
+          if (intrinsic != Intrinsics::kNone) {
+            if (!CheckInvokeType(intrinsic, invoke)) {
+              LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
+                           << intrinsic << " for "
+                           << PrettyMethod(invoke->GetDexMethodIndex(), *dex_file_);
+            } else {
+              invoke->SetIntrinsic(intrinsic);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
+  switch (intrinsic) {
+    case Intrinsics::kNone:
+      os << "No intrinsic.";
+      break;
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+    case Intrinsics::k ## Name: \
+      os << # Name; \
+      break;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef STATIC_INTRINSICS_LIST
+#undef VIRTUAL_INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+  }
+  return os;
+}
+
+}  // namespace art
+
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
new file mode 100644
index 0000000..29cc8ef
--- /dev/null
+++ b/compiler/optimizing/intrinsics.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CompilerDriver;
+class DexFile;
+
+// Recognize intrinsics from HInvoke nodes.
+class IntrinsicsRecognizer : public HOptimization {
+ public:
+  IntrinsicsRecognizer(HGraph* graph, const DexFile* dex_file, CompilerDriver* driver)
+      : HOptimization(graph, true, "intrinsics_recognition"),
+        dex_file_(dex_file), driver_(driver) {}
+
+  void Run() OVERRIDE;
+
+ private:
+  const DexFile* dex_file_;
+  CompilerDriver* driver_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicsRecognizer);
+};
+
+class IntrinsicVisitor : public ValueObject {
+ public:
+  virtual ~IntrinsicVisitor() {}
+
+  // Dispatch logic.
+
+  void Dispatch(HInvoke* invoke) {
+    switch (invoke->GetIntrinsic()) {
+      case Intrinsics::kNone:
+        return;
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
+      case Intrinsics::k ## Name:             \
+        Visit ## Name(invoke);                \
+        return;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+      // Do not put a default case. That way the compiler will complain if we missed a case.
+    }
+  }
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)                    \
+  virtual void Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+  }
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ protected:
+  IntrinsicVisitor() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_H_
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
new file mode 100644
index 0000000..a82d80a
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -0,0 +1,883 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "intrinsics_arm.h"
+
+#include "arch/arm/instruction_set_features_arm.h"
+#include "code_generator_arm.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/arm/assembler_arm.h"
+
+namespace art {
+
+namespace arm {
+
+ArmAssembler* IntrinsicCodeGeneratorARM::GetAssembler() {
+  return codegen_->GetAssembler();
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorARM::GetAllocator() {
+  return codegen_->GetGraph()->GetArena();
+}
+
+#define __ codegen->GetAssembler()->
+
+static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGeneratorARM* codegen) {
+  if (!trg.IsValid()) {
+    DCHECK(type == Primitive::kPrimVoid);
+    return;
+  }
+
+  DCHECK_NE(type, Primitive::kPrimVoid);
+
+  if (Primitive::IsIntegralType(type)) {
+    if (type == Primitive::kPrimLong) {
+      Register trg_reg_lo = trg.AsRegisterPairLow<Register>();
+      Register trg_reg_hi = trg.AsRegisterPairHigh<Register>();
+      Register res_reg_lo = R0;
+      Register res_reg_hi = R1;
+      if (trg_reg_lo != res_reg_hi) {
+        if (trg_reg_lo != res_reg_lo) {
+          __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
+          __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
+        } else {
+          DCHECK_EQ(trg_reg_lo + 1, trg_reg_hi);
+        }
+      } else {
+        __ mov(trg_reg_hi, ShifterOperand(res_reg_hi));
+        __ mov(trg_reg_lo, ShifterOperand(res_reg_lo));
+      }
+    } else {
+      Register trg_reg = trg.AsRegister<Register>();
+      Register res_reg = R0;
+      if (trg_reg != res_reg) {
+        __ mov(trg_reg, ShifterOperand(res_reg));
+      }
+    }
+  } else {
+    UNIMPLEMENTED(FATAL) << "Floating-point return.";
+  }
+}
+
+static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) {
+  if (invoke->InputCount() == 0) {
+    return;
+  }
+
+  LocationSummary* locations = invoke->GetLocations();
+  InvokeDexCallingConventionVisitor calling_convention_visitor;
+
+  // We're moving potentially two or more locations to locations that could overlap, so we need
+  // a parallel move resolver.
+  HParallelMove parallel_move(arena);
+
+  for (size_t i = 0; i < invoke->InputCount(); i++) {
+    HInstruction* input = invoke->InputAt(i);
+    Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
+    Location actual_loc = locations->InAt(i);
+
+    parallel_move.AddMove(actual_loc, cc_loc, nullptr);
+  }
+
+  codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+}
+
+// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
+// call. This will copy the arguments into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations given by the invoke's location
+//       summary. If an intrinsic modifies those locations before a slowpath call, they must be
+//       restored!
+class IntrinsicSlowPathARM : public SlowPathCodeARM {
+ public:
+  explicit IntrinsicSlowPathARM(HInvoke* invoke) : invoke_(invoke) { }
+
+  void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+    CodeGeneratorARM* codegen = down_cast<CodeGeneratorARM*>(codegen_in);
+    __ Bind(GetEntryLabel());
+
+    codegen->SaveLiveRegisters(invoke_->GetLocations());
+
+    MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
+
+    if (invoke_->IsInvokeStaticOrDirect()) {
+      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+    } else {
+      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+      UNREACHABLE();
+    }
+
+    // Copy the result back to the expected output.
+    Location out = invoke_->GetLocations()->Out();
+    if (out.IsValid()) {
+      DCHECK(out.IsRegister());  // TODO: Replace this when we support output in memory.
+      DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+      MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+    }
+
+    codegen->RestoreLiveRegisters(invoke_->GetLocations());
+    __ b(GetExitLabel());
+  }
+
+ private:
+  // The instruction where this slow path is happening.
+  HInvoke* const invoke_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM);
+};
+
+#undef __
+
+bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) {
+  Dispatch(invoke);
+  LocationSummary* res = invoke->GetLocations();
+  return res != nullptr && res->Intrinsified();
+}
+
+#define __ assembler->
+
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
+  Location input = locations->InAt(0);
+  Location output = locations->Out();
+  if (is64bit) {
+    __ vmovrrd(output.AsRegisterPairLow<Register>(),
+               output.AsRegisterPairHigh<Register>(),
+               FromLowSToD(input.AsFpuRegisterPairLow<SRegister>()));
+  } else {
+    __ vmovrs(output.AsRegister<Register>(), input.AsFpuRegister<SRegister>());
+  }
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
+  Location input = locations->InAt(0);
+  Location output = locations->Out();
+  if (is64bit) {
+    __ vmovdrr(FromLowSToD(output.AsFpuRegisterPairLow<SRegister>()),
+               input.AsRegisterPairLow<Register>(),
+               input.AsRegisterPairHigh<Register>());
+  } else {
+    __ vmovsr(output.AsFpuRegister<SRegister>(), input.AsRegister<Register>());
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
+}
+void IntrinsicCodeGeneratorARM::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
+}
+void IntrinsicCodeGeneratorARM::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+
+  if (is64bit) {
+    __ vabsd(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+             FromLowSToD(in.AsFpuRegisterPairLow<SRegister>()));
+  } else {
+    __ vabss(out.AsFpuRegister<SRegister>(), in.AsFpuRegister<SRegister>());
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsDouble(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsDouble(HInvoke* invoke) {
+  MathAbsFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsFloat(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsFloat(HInvoke* invoke) {
+  MathAbsFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+  locations->AddTemp(Location::RequiresRegister());
+}
+
+static void GenAbsInteger(LocationSummary* locations,
+                          bool is64bit,
+                          ArmAssembler* assembler) {
+  Location in = locations->InAt(0);
+  Location output = locations->Out();
+
+  Register mask = locations->GetTemp(0).AsRegister<Register>();
+
+  if (is64bit) {
+    Register in_reg_lo = in.AsRegisterPairLow<Register>();
+    Register in_reg_hi = in.AsRegisterPairHigh<Register>();
+    Register out_reg_lo = output.AsRegisterPairLow<Register>();
+    Register out_reg_hi = output.AsRegisterPairHigh<Register>();
+
+    DCHECK_NE(out_reg_lo, in_reg_hi) << "Diagonal overlap unexpected.";
+
+    __ Asr(mask, in_reg_hi, 31);
+    __ adds(out_reg_lo, in_reg_lo, ShifterOperand(mask));
+    __ adc(out_reg_hi, in_reg_hi, ShifterOperand(mask));
+    __ eor(out_reg_lo, mask, ShifterOperand(out_reg_lo));
+    __ eor(out_reg_hi, mask, ShifterOperand(out_reg_hi));
+  } else {
+    Register in_reg = in.AsRegister<Register>();
+    Register out_reg = output.AsRegister<Register>();
+
+    __ Asr(mask, in_reg, 31);
+    __ add(out_reg, in_reg, ShifterOperand(mask));
+    __ eor(out_reg, mask, ShifterOperand(out_reg));
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsInt(HInvoke* invoke) {
+  CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsInt(HInvoke* invoke) {
+  GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
+}
+
+
+void IntrinsicLocationsBuilderARM::VisitMathAbsLong(HInvoke* invoke) {
+  CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAbsLong(HInvoke* invoke) {
+  GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
+}
+
+static void GenMinMax(LocationSummary* locations,
+                      bool is_min,
+                      ArmAssembler* assembler) {
+  Register op1 = locations->InAt(0).AsRegister<Register>();
+  Register op2 = locations->InAt(1).AsRegister<Register>();
+  Register out = locations->Out().AsRegister<Register>();
+
+  __ cmp(op1, ShifterOperand(op2));
+
+  __ it((is_min) ? Condition::LT : Condition::GT, kItElse);
+  __ mov(out, ShifterOperand(op1), is_min ? Condition::LT : Condition::GT);
+  __ mov(out, ShifterOperand(op2), is_min ? Condition::GE : Condition::LE);
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathMinIntInt(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathMinIntInt(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathMaxIntInt(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathMaxIntInt(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathSqrt(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathSqrt(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  ArmAssembler* assembler = GetAssembler();
+  __ vsqrtd(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
+            FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekByte(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekByte(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  // Ignore upper 4B of long address.
+  __ ldrsb(invoke->GetLocations()->Out().AsRegister<Register>(),
+           Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekIntNative(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  // Ignore upper 4B of long address.
+  __ ldr(invoke->GetLocations()->Out().AsRegister<Register>(),
+         Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekLongNative(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  // Ignore upper 4B of long address.
+  Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+  // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
+  // exception. So we can't use ldrd as addr may be unaligned.
+  Register lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
+  Register hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
+  if (addr == lo) {
+    __ ldr(hi, Address(addr, 4));
+    __ ldr(lo, Address(addr, 0));
+  } else {
+    __ ldr(lo, Address(addr, 0));
+    __ ldr(hi, Address(addr, 4));
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPeekShortNative(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  // Ignore upper 4B of long address.
+  __ ldrsh(invoke->GetLocations()->Out().AsRegister<Register>(),
+           Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeByte(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeByte(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  __ strb(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
+          Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeIntNative(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  __ str(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
+         Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeLongNative(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  // Ignore upper 4B of long address.
+  Register addr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+  // Worst case: Control register bit SCTLR.A = 0. Then unaligned accesses throw a processor
+  // exception. So we can't use ldrd as addr may be unaligned.
+  __ str(invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>(), Address(addr, 0));
+  __ str(invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>(), Address(addr, 4));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMemoryPokeShortNative(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  __ strh(invoke->GetLocations()->InAt(1).AsRegister<Register>(),
+          Address(invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>()));
+}
+
+void IntrinsicLocationsBuilderARM::VisitThreadCurrentThread(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitThreadCurrentThread(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  __ LoadFromOffset(kLoadWord,
+                    invoke->GetLocations()->Out().AsRegister<Register>(),
+                    TR,
+                    Thread::PeerOffset<kArmPointerSize>().Int32Value());
+}
+
+static void GenUnsafeGet(HInvoke* invoke,
+                         Primitive::Type type,
+                         bool is_volatile,
+                         CodeGeneratorARM* codegen) {
+  LocationSummary* locations = invoke->GetLocations();
+  DCHECK((type == Primitive::kPrimInt) ||
+         (type == Primitive::kPrimLong) ||
+         (type == Primitive::kPrimNot));
+  ArmAssembler* assembler = codegen->GetAssembler();
+  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
+  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Long offset, lo part only.
+
+  if (type == Primitive::kPrimLong) {
+    Register trg_lo = locations->Out().AsRegisterPairLow<Register>();
+    __ add(IP, base, ShifterOperand(offset));
+    if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
+      Register trg_hi = locations->Out().AsRegisterPairHigh<Register>();
+      __ ldrexd(trg_lo, trg_hi, IP);
+    } else {
+      __ ldrd(trg_lo, Address(IP));
+    }
+  } else {
+    Register trg = locations->Out().AsRegister<Register>();
+    __ ldr(trg, Address(base, offset));
+  }
+
+  if (is_volatile) {
+    __ dmb(ISH);
+  }
+}
+
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetLong(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetObject(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
+}
+
+static void CreateIntIntIntIntToVoid(ArenaAllocator* arena,
+                                     const ArmInstructionSetFeatures& features,
+                                     Primitive::Type type,
+                                     bool is_volatile,
+                                     HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+
+  if (type == Primitive::kPrimLong) {
+    // Potentially need temps for ldrexd-strexd loop.
+    if (is_volatile && !features.HasAtomicLdrdAndStrd()) {
+      locations->AddTemp(Location::RequiresRegister());  // Temp_lo.
+      locations->AddTemp(Location::RequiresRegister());  // Temp_hi.
+    }
+  } else if (type == Primitive::kPrimNot) {
+    // Temps for card-marking.
+    locations->AddTemp(Location::RequiresRegister());  // Temp.
+    locations->AddTemp(Location::RequiresRegister());  // Card.
+  }
+}
+
+void IntrinsicLocationsBuilderARM::VisitUnsafePut(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimInt, true, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutObject(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimNot, true, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutLong(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, false, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, features_, Primitive::kPrimLong, true, invoke);
+}
+
+static void GenUnsafePut(LocationSummary* locations,
+                         Primitive::Type type,
+                         bool is_volatile,
+                         bool is_ordered,
+                         CodeGeneratorARM* codegen) {
+  ArmAssembler* assembler = codegen->GetAssembler();
+
+  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
+  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Long offset, lo part only.
+  Register value;
+
+  if (is_volatile || is_ordered) {
+    __ dmb(ISH);
+  }
+
+  if (type == Primitive::kPrimLong) {
+    Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
+    value = value_lo;
+    if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
+      Register temp_lo = locations->GetTemp(0).AsRegister<Register>();
+      Register temp_hi = locations->GetTemp(1).AsRegister<Register>();
+      Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
+
+      __ add(IP, base, ShifterOperand(offset));
+      Label loop_head;
+      __ Bind(&loop_head);
+      __ ldrexd(temp_lo, temp_hi, IP);
+      __ strexd(temp_lo, value_lo, value_hi, IP);
+      __ cmp(temp_lo, ShifterOperand(0));
+      __ b(&loop_head, NE);
+    } else {
+      __ add(IP, base, ShifterOperand(offset));
+      __ strd(value_lo, Address(IP));
+    }
+  } else {
+    value =  locations->InAt(3).AsRegister<Register>();
+    __ str(value, Address(base, offset));
+  }
+
+  if (is_volatile) {
+    __ dmb(ISH);
+  }
+
+  if (type == Primitive::kPrimNot) {
+    Register temp = locations->GetTemp(0).AsRegister<Register>();
+    Register card = locations->GetTemp(1).AsRegister<Register>();
+    codegen->MarkGCCard(temp, card, base, value);
+  }
+}
+
+void IntrinsicCodeGeneratorARM::VisitUnsafePut(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutObject(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutLong(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
+}
+
+static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
+                                                HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+  locations->SetInAt(4, Location::RequiresRegister());
+
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+
+  locations->AddTemp(Location::RequiresRegister());  // Pointer.
+  locations->AddTemp(Location::RequiresRegister());  // Temp 1.
+  locations->AddTemp(Location::RequiresRegister());  // Temp 2.
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
+  DCHECK_NE(type, Primitive::kPrimLong);
+
+  ArmAssembler* assembler = codegen->GetAssembler();
+
+  Register out = locations->Out().AsRegister<Register>();              // Boolean result.
+
+  Register base = locations->InAt(1).AsRegister<Register>();           // Object pointer.
+  Register offset = locations->InAt(2).AsRegisterPairLow<Register>();  // Offset (discard high 4B).
+  Register expected_lo = locations->InAt(3).AsRegister<Register>();    // Expected.
+  Register value_lo = locations->InAt(4).AsRegister<Register>();       // Value.
+
+  Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>();     // Pointer to actual memory.
+  Register tmp_lo = locations->GetTemp(1).AsRegister<Register>();      // Value in memory.
+
+  if (type == Primitive::kPrimNot) {
+    // Mark card for object assuming new value is stored. Worst case we will mark an unchanged
+    // object and scan the receiver at the next GC for nothing.
+    codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo);
+  }
+
+  // Prevent reordering with prior memory operations.
+  __ dmb(ISH);
+
+  __ add(tmp_ptr, base, ShifterOperand(offset));
+
+  // do {
+  //   tmp = [r_ptr] - expected;
+  // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
+  // result = tmp != 0;
+
+  Label loop_head;
+  __ Bind(&loop_head);
+
+  __ ldrex(tmp_lo, tmp_ptr);
+
+  __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
+
+  __ it(EQ, ItState::kItT);
+  __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
+  __ cmp(tmp_lo, ShifterOperand(1), EQ);
+
+  __ b(&loop_head, EQ);
+
+  __ dmb(ISH);
+
+  __ rsbs(out, tmp_lo, ShifterOperand(1));
+  __ it(CC);
+  __ mov(out, ShifterOperand(0), CC);
+}
+
+void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke ATTRIBUTE_UNUSED) {
+  CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke ATTRIBUTE_UNUSED) {
+  CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
+void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kCallOnSlowPath,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+
+  locations->AddTemp(Location::RequiresRegister());
+  locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
+  ArmAssembler* assembler = GetAssembler();
+  LocationSummary* locations = invoke->GetLocations();
+
+  // Location of reference to data array
+  const MemberOffset value_offset = mirror::String::ValueOffset();
+  // Location of count
+  const MemberOffset count_offset = mirror::String::CountOffset();
+  // Starting offset within data array
+  const MemberOffset offset_offset = mirror::String::OffsetOffset();
+  // Start of char data with array_
+  const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
+
+  Register obj = locations->InAt(0).AsRegister<Register>();  // String object pointer.
+  Register idx = locations->InAt(1).AsRegister<Register>();  // Index of character.
+  Register out = locations->Out().AsRegister<Register>();    // Result character.
+
+  Register temp = locations->GetTemp(0).AsRegister<Register>();
+  Register array_temp = locations->GetTemp(1).AsRegister<Register>();
+
+  // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
+  //       the cost.
+  // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
+  //       we will not optimize the code for constants (which would save a register).
+
+  SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
+  codegen_->AddSlowPath(slow_path);
+
+  __ ldr(temp, Address(obj, count_offset.Int32Value()));          // temp = str.length.
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
+  __ cmp(idx, ShifterOperand(temp));
+  __ b(slow_path->GetEntryLabel(), CS);
+
+  // Index computation.
+  __ ldr(temp, Address(obj, offset_offset.Int32Value()));         // temp := str.offset.
+  __ ldr(array_temp, Address(obj, value_offset.Int32Value()));    // array_temp := str.offset.
+  __ add(temp, temp, ShifterOperand(idx));
+  DCHECK_EQ(data_offset.Int32Value() % 2, 0);                     // We'll compensate by shifting.
+  __ add(temp, temp, ShifterOperand(data_offset.Int32Value() / 2));
+
+  // Load the value.
+  __ ldrh(out, Address(array_temp, temp, LSL, 1));                // out := array_temp[temp].
+
+  __ Bind(slow_path->GetExitLabel());
+}
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name)                                                  \
+void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}                                                                                      \
+void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) {    \
+}
+
+UNIMPLEMENTED_INTRINSIC(IntegerReverse)
+UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
+UNIMPLEMENTED_INTRINSIC(LongReverse)
+UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
+UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
+UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
+UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
+UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
+UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
+UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
+UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
+UNIMPLEMENTED_INTRINSIC(MathCeil)          // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(MathFloor)         // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(MathRint)
+UNIMPLEMENTED_INTRINSIC(MathRoundDouble)   // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(MathRoundFloat)    // Could be done by changing rounding mode, maybe?
+UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)     // High register pressure.
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringIsEmpty)  // Might not want to do these two anyways, inlining should
+UNIMPLEMENTED_INTRINSIC(StringLength)   // be good enough here.
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+
+}  // namespace arm
+}  // namespace art
diff --git a/compiler/optimizing/intrinsics_arm.h b/compiler/optimizing/intrinsics_arm.h
new file mode 100644
index 0000000..8bfb7d4
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
+
+#include "intrinsics.h"
+
+namespace art {
+
+class ArenaAllocator;
+class ArmInstructionSetFeatures;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace arm {
+
+class ArmAssembler;
+class CodeGeneratorARM;
+
+class IntrinsicLocationsBuilderARM FINAL : public IntrinsicVisitor {
+ public:
+  explicit IntrinsicLocationsBuilderARM(ArenaAllocator* arena,
+                                        const ArmInstructionSetFeatures& features)
+      : arena_(arena), features_(features) {}
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)   \
+  void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+  // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+  // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+  // the invoke.
+  bool TryDispatch(HInvoke* invoke);
+
+ private:
+  ArenaAllocator* arena_;
+
+  const ArmInstructionSetFeatures& features_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderARM);
+};
+
+class IntrinsicCodeGeneratorARM FINAL : public IntrinsicVisitor {
+ public:
+  explicit IntrinsicCodeGeneratorARM(CodeGeneratorARM* codegen) : codegen_(codegen) {}
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)   \
+  void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+  ArmAssembler* GetAssembler();
+
+  ArenaAllocator* GetAllocator();
+
+  CodeGeneratorARM* codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorARM);
+};
+
+}  // namespace arm
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_H_
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
new file mode 100644
index 0000000..8874edc
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "intrinsics_arm64.h"
+
+#include "code_generator_arm64.h"
+#include "common_arm64.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/arm64/assembler_arm64.h"
+#include "utils/arm64/constants_arm64.h"
+
+#include "a64/disasm-a64.h"
+#include "a64/macro-assembler-a64.h"
+
+using namespace vixl;   // NOLINT(build/namespaces)
+
+namespace art {
+
+namespace arm64 {
+
+using helpers::DRegisterFrom;
+using helpers::FPRegisterFrom;
+using helpers::HeapOperand;
+using helpers::RegisterFrom;
+using helpers::SRegisterFrom;
+using helpers::WRegisterFrom;
+using helpers::XRegisterFrom;
+
+
+namespace {
+
+ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
+  return MemOperand(XRegisterFrom(location), offset);
+}
+
+}  // namespace
+
+vixl::MacroAssembler* IntrinsicCodeGeneratorARM64::GetVIXLAssembler() {
+  return codegen_->GetAssembler()->vixl_masm_;
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorARM64::GetAllocator() {
+  return codegen_->GetGraph()->GetArena();
+}
+
+#define __ codegen->GetAssembler()->vixl_masm_->
+
+static void MoveFromReturnRegister(Location trg,
+                                   Primitive::Type type,
+                                   CodeGeneratorARM64* codegen) {
+  if (!trg.IsValid()) {
+    DCHECK(type == Primitive::kPrimVoid);
+    return;
+  }
+
+  DCHECK_NE(type, Primitive::kPrimVoid);
+
+  if (Primitive::IsIntegralType(type)) {
+    Register trg_reg = RegisterFrom(trg, type);
+    Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
+    __ Mov(trg_reg, res_reg, kDiscardForSameWReg);
+  } else {
+    FPRegister trg_reg = FPRegisterFrom(trg, type);
+    FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
+    __ Fmov(trg_reg, res_reg);
+  }
+}
+
+static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM64* codegen) {
+  if (invoke->InputCount() == 0) {
+    return;
+  }
+
+  LocationSummary* locations = invoke->GetLocations();
+  InvokeDexCallingConventionVisitor calling_convention_visitor;
+
+  // We're moving potentially two or more locations to locations that could overlap, so we need
+  // a parallel move resolver.
+  HParallelMove parallel_move(arena);
+
+  for (size_t i = 0; i < invoke->InputCount(); i++) {
+    HInstruction* input = invoke->InputAt(i);
+    Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
+    Location actual_loc = locations->InAt(i);
+
+    parallel_move.AddMove(actual_loc, cc_loc, nullptr);
+  }
+
+  codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+}
+
+// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
+// call. This will copy the arguments into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations given by the invoke's location
+//       summary. If an intrinsic modifies those locations before a slowpath call, they must be
+//       restored!
+class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+  explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { }
+
+  void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+    CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
+    __ Bind(GetEntryLabel());
+
+    codegen->SaveLiveRegisters(invoke_->GetLocations());
+
+    MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
+
+    if (invoke_->IsInvokeStaticOrDirect()) {
+      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+    } else {
+      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+      UNREACHABLE();
+    }
+
+    // Copy the result back to the expected output.
+    Location out = invoke_->GetLocations()->Out();
+    if (out.IsValid()) {
+      DCHECK(out.IsRegister());  // TODO: Replace this when we support output in memory.
+      DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+      MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+    }
+
+    codegen->RestoreLiveRegisters(invoke_->GetLocations());
+    __ B(GetExitLabel());
+  }
+
+ private:
+  // The instruction where this slow path is happening.
+  HInvoke* const invoke_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathARM64);
+};
+
+#undef __
+
+bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) {
+  Dispatch(invoke);
+  LocationSummary* res = invoke->GetLocations();
+  return res != nullptr && res->Intrinsified();
+}
+
+#define __ masm->
+
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
+  Location input = locations->InAt(0);
+  Location output = locations->Out();
+  __ Fmov(is64bit ? XRegisterFrom(output) : WRegisterFrom(output),
+          is64bit ? DRegisterFrom(input) : SRegisterFrom(input));
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
+  Location input = locations->InAt(0);
+  Location output = locations->Out();
+  __ Fmov(is64bit ? DRegisterFrom(output) : SRegisterFrom(output),
+          is64bit ? XRegisterFrom(input) : WRegisterFrom(input));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+void IntrinsicCodeGeneratorARM64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+void IntrinsicCodeGeneratorARM64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenReverseBytes(LocationSummary* locations,
+                            Primitive::Type type,
+                            vixl::MacroAssembler* masm) {
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+
+  switch (type) {
+    case Primitive::kPrimShort:
+      __ Rev16(WRegisterFrom(out), WRegisterFrom(in));
+      __ Sxth(WRegisterFrom(out), WRegisterFrom(out));
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      __ Rev(RegisterFrom(out, type), RegisterFrom(in, type));
+      break;
+    default:
+      LOG(FATAL) << "Unexpected size for reverse-bytes: " << type;
+      UNREACHABLE();
+  }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitShortReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
+}
+
+static void GenReverse(LocationSummary* locations,
+                       Primitive::Type type,
+                       vixl::MacroAssembler* masm) {
+  DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+
+  __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerReverse(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerReverse(HInvoke* invoke) {
+  GenReverse(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongReverse(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
+  GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, vixl::MacroAssembler* masm) {
+  Location in = locations->InAt(0);
+  Location out = locations->Out();
+
+  FPRegister in_reg = is64bit ? DRegisterFrom(in) : SRegisterFrom(in);
+  FPRegister out_reg = is64bit ? DRegisterFrom(out) : SRegisterFrom(out);
+
+  __ Fabs(out_reg, in_reg);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsDouble(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsDouble(HInvoke* invoke) {
+  MathAbsFP(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsFloat(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsFloat(HInvoke* invoke) {
+  MathAbsFP(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenAbsInteger(LocationSummary* locations,
+                          bool is64bit,
+                          vixl::MacroAssembler* masm) {
+  Location in = locations->InAt(0);
+  Location output = locations->Out();
+
+  Register in_reg = is64bit ? XRegisterFrom(in) : WRegisterFrom(in);
+  Register out_reg = is64bit ? XRegisterFrom(output) : WRegisterFrom(output);
+
+  __ Cmp(in_reg, Operand(0));
+  __ Cneg(out_reg, in_reg, lt);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
+  CreateIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
+  GenAbsInteger(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
+  CreateIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
+  GenAbsInteger(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+static void GenMinMaxFP(LocationSummary* locations,
+                        bool is_min,
+                        bool is_double,
+                        vixl::MacroAssembler* masm) {
+  Location op1 = locations->InAt(0);
+  Location op2 = locations->InAt(1);
+  Location out = locations->Out();
+
+  FPRegister op1_reg = is_double ? DRegisterFrom(op1) : SRegisterFrom(op1);
+  FPRegister op2_reg = is_double ? DRegisterFrom(op2) : SRegisterFrom(op2);
+  FPRegister out_reg = is_double ? DRegisterFrom(out) : SRegisterFrom(out);
+  if (is_min) {
+    __ Fmin(out_reg, op1_reg, op2_reg);
+  } else {
+    __ Fmax(out_reg, op1_reg, op2_reg);
+  }
+}
+
+static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetInAt(1, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+  CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), true, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
+  CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinFloatFloat(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), true, false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+  CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), false, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+  CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), false, false, GetVIXLAssembler());
+}
+
+static void GenMinMax(LocationSummary* locations,
+                      bool is_min,
+                      bool is_long,
+                      vixl::MacroAssembler* masm) {
+  Location op1 = locations->InAt(0);
+  Location op2 = locations->InAt(1);
+  Location out = locations->Out();
+
+  Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
+  Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
+  Register out_reg = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
+
+  __ Cmp(op1_reg, op2_reg);
+  __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinIntInt(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), true, false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMinLongLong(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMinLongLong(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), true, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxIntInt(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxIntInt(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), false, false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathMaxLongLong(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathMaxLongLong(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), false, true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathSqrt(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathSqrt(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Fsqrt(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathCeil(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathCeil(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Frintp(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathFloor(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathFloor(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Frintm(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathRint(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathRint(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Frintn(DRegisterFrom(locations->Out()), DRegisterFrom(locations->InAt(0)));
+}
+
+static void CreateFPToIntPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+static void GenMathRound(LocationSummary* locations,
+                         bool is_double,
+                         vixl::MacroAssembler* masm) {
+  FPRegister in_reg = is_double ?
+      DRegisterFrom(locations->InAt(0)) : SRegisterFrom(locations->InAt(0));
+  Register out_reg = is_double ?
+      XRegisterFrom(locations->Out()) : WRegisterFrom(locations->Out());
+  UseScratchRegisterScope temps(masm);
+  FPRegister temp1_reg = temps.AcquireSameSizeAs(in_reg);
+
+  // 0.5 can be encoded as an immediate, so use fmov.
+  if (is_double) {
+    __ Fmov(temp1_reg, static_cast<double>(0.5));
+  } else {
+    __ Fmov(temp1_reg, static_cast<float>(0.5));
+  }
+  __ Fadd(temp1_reg, in_reg, temp1_reg);
+  __ Fcvtms(out_reg, temp1_reg);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathRoundDouble(HInvoke* invoke) {
+  CreateFPToIntPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathRoundDouble(HInvoke* invoke) {
+  GenMathRound(invoke->GetLocations(), true, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathRoundFloat(HInvoke* invoke) {
+  CreateFPToIntPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathRoundFloat(HInvoke* invoke) {
+  GenMathRound(invoke->GetLocations(), false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekByte(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekByte(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Ldrsb(WRegisterFrom(invoke->GetLocations()->Out()),
+          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Ldr(WRegisterFrom(invoke->GetLocations()->Out()),
+         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Ldr(XRegisterFrom(invoke->GetLocations()->Out()),
+         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Ldrsh(WRegisterFrom(invoke->GetLocations()->Out()),
+           AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeByte(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeByte(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Strb(WRegisterFrom(invoke->GetLocations()->InAt(1)),
+          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Str(WRegisterFrom(invoke->GetLocations()->InAt(1)),
+         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Str(XRegisterFrom(invoke->GetLocations()->InAt(1)),
+         AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  __ Strh(WRegisterFrom(invoke->GetLocations()->InAt(1)),
+          AbsoluteHeapOperandFrom(invoke->GetLocations()->InAt(0), 0));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitThreadCurrentThread(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitThreadCurrentThread(HInvoke* invoke) {
+  codegen_->Load(Primitive::kPrimNot, WRegisterFrom(invoke->GetLocations()->Out()),
+                 MemOperand(tr, Thread::PeerOffset<8>().Int32Value()));
+}
+
+static void GenUnsafeGet(HInvoke* invoke,
+                         Primitive::Type type,
+                         bool is_volatile,
+                         CodeGeneratorARM64* codegen) {
+  LocationSummary* locations = invoke->GetLocations();
+  DCHECK((type == Primitive::kPrimInt) ||
+         (type == Primitive::kPrimLong) ||
+         (type == Primitive::kPrimNot));
+  vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
+  Register base = WRegisterFrom(locations->InAt(1));    // Object pointer.
+  Register offset = XRegisterFrom(locations->InAt(2));  // Long offset.
+  Register trg = RegisterFrom(locations->Out(), type);
+
+  MemOperand mem_op(base.X(), offset);
+  if (is_volatile) {
+    if (kUseAcquireRelease) {
+      codegen->LoadAcquire(invoke, trg, mem_op);
+    } else {
+      codegen->Load(type, trg, mem_op);
+      __ Dmb(InnerShareable, BarrierReads);
+    }
+  } else {
+    codegen->Load(type, trg, mem_op);
+  }
+}
+
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLong(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObject(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGet(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimInt, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLong(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimLong, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObject(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke, Primitive::kPrimNot, true, codegen_);
+}
+
+static void CreateIntIntIntIntToVoid(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitUnsafePut(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutObject(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutLong(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoid(arena_, invoke);
+}
+
+static void GenUnsafePut(LocationSummary* locations,
+                         Primitive::Type type,
+                         bool is_volatile,
+                         bool is_ordered,
+                         CodeGeneratorARM64* codegen) {
+  vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
+
+  Register base = WRegisterFrom(locations->InAt(1));    // Object pointer.
+  Register offset = XRegisterFrom(locations->InAt(2));  // Long offset.
+  Register value = RegisterFrom(locations->InAt(3), type);
+
+  MemOperand mem_op(base.X(), offset);
+
+  if (is_volatile || is_ordered) {
+    if (kUseAcquireRelease) {
+      codegen->StoreRelease(type, value, mem_op);
+    } else {
+      __ Dmb(InnerShareable, BarrierAll);
+      codegen->Store(type, value, mem_op);
+      if (is_volatile) {
+        __ Dmb(InnerShareable, BarrierReads);
+      }
+    }
+  } else {
+    codegen->Store(type, value, mem_op);
+  }
+
+  if (type == Primitive::kPrimNot) {
+    codegen->MarkGCCard(base, value);
+  }
+}
+
+void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, false, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, true, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
+}
+
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+  locations->SetInAt(4, Location::RequiresRegister());
+
+  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
+  // TODO: Currently we use acquire-release load-stores in the CAS loop. One could reasonably write
+  //       a version relying on simple exclusive load-stores and barriers instead.
+  static_assert(kUseAcquireRelease, "Non-acquire-release inlined CAS not implemented, yet.");
+
+  vixl::MacroAssembler* masm = codegen->GetAssembler()->vixl_masm_;
+
+  Register out = WRegisterFrom(locations->Out());                  // Boolean result.
+
+  Register base = WRegisterFrom(locations->InAt(1));               // Object pointer.
+  Register offset = XRegisterFrom(locations->InAt(2));             // Long offset.
+  Register expected = RegisterFrom(locations->InAt(3), type);      // Expected.
+  Register value = RegisterFrom(locations->InAt(4), type);         // Value.
+
+  // This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
+  if (type == Primitive::kPrimNot) {
+    // Mark card for object assuming new value is stored.
+    codegen->MarkGCCard(base, value);
+  }
+
+  UseScratchRegisterScope temps(masm);
+  Register tmp_ptr = temps.AcquireX();                             // Pointer to actual memory.
+  Register tmp_value = temps.AcquireSameSizeAs(value);             // Value in memory.
+
+  Register tmp_32 = tmp_value.W();
+
+  __ Add(tmp_ptr, base.X(), Operand(offset));
+
+  // do {
+  //   tmp_value = [tmp_ptr] - expected;
+  // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
+  // result = tmp_value != 0;
+
+  vixl::Label loop_head, exit_loop;
+  __ Bind(&loop_head);
+
+  __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
+  __ Cmp(tmp_value, expected);
+  __ B(&exit_loop, ne);
+
+  __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
+  __ Cbnz(tmp_32, &loop_head);
+
+  __ Bind(&exit_loop);
+  __ Cset(out, eq);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
+  CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
+  CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
+  CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
+}
+void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kCallOnSlowPath,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  // In case we need to go in the slow path, we can't have the output be the same
+  // as the input: the current liveness analysis considers the input to be live
+  // at the point of the call.
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
+  vixl::MacroAssembler* masm = GetVIXLAssembler();
+  LocationSummary* locations = invoke->GetLocations();
+
+  // Location of reference to data array
+  const MemberOffset value_offset = mirror::String::ValueOffset();
+  // Location of count
+  const MemberOffset count_offset = mirror::String::CountOffset();
+  // Starting offset within data array
+  const MemberOffset offset_offset = mirror::String::OffsetOffset();
+  // Start of char data with array_
+  const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t));
+
+  Register obj = WRegisterFrom(locations->InAt(0));  // String object pointer.
+  Register idx = WRegisterFrom(locations->InAt(1));  // Index of character.
+  Register out = WRegisterFrom(locations->Out());    // Result character.
+
+  UseScratchRegisterScope temps(masm);
+  Register temp = temps.AcquireW();
+  Register array_temp = temps.AcquireW();            // We can trade this for worse scheduling.
+
+  // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
+  //       the cost.
+  // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
+  //       we will not optimize the code for constants (which would save a register).
+
+  SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
+  codegen_->AddSlowPath(slow_path);
+
+  __ Ldr(temp, HeapOperand(obj, count_offset));          // temp = str.length.
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
+  __ Cmp(idx, temp);
+  __ B(hs, slow_path->GetEntryLabel());
+
+  // Index computation.
+  __ Ldr(temp, HeapOperand(obj, offset_offset));         // temp := str.offset.
+  __ Ldr(array_temp, HeapOperand(obj, value_offset));    // array_temp := str.offset.
+  __ Add(temp, temp, idx);
+  DCHECK_EQ(data_offset.Int32Value() % 2, 0);            // We'll compensate by shifting.
+  __ Add(temp, temp, Operand(data_offset.Int32Value() / 2));
+
+  // Load the value.
+  __ Ldrh(out, MemOperand(array_temp.X(), temp, UXTW, 1));  // out := array_temp[temp].
+
+  __ Bind(slow_path->GetExitLabel());
+}
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name)                                                  \
+void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}                                                                                      \
+void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) {    \
+}
+
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringIsEmpty)  // Might not want to do these two anyways, inlining should
+UNIMPLEMENTED_INTRINSIC(StringLength)   // be good enough here.
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+
+}  // namespace arm64
+}  // namespace art
diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h
new file mode 100644
index 0000000..ba21889
--- /dev/null
+++ b/compiler/optimizing/intrinsics_arm64.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
+
+#include "intrinsics.h"
+
+namespace vixl {
+
+class MacroAssembler;
+
+}  // namespace vixl
+
+namespace art {
+
+class ArenaAllocator;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace arm64 {
+
+class CodeGeneratorARM64;
+
+class IntrinsicLocationsBuilderARM64 FINAL : public IntrinsicVisitor {
+ public:
+  explicit IntrinsicLocationsBuilderARM64(ArenaAllocator* arena) : arena_(arena) {}
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)   \
+  void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+  // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+  // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+  // the invoke.
+  bool TryDispatch(HInvoke* invoke);
+
+ private:
+  ArenaAllocator* arena_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderARM64);
+};
+
+class IntrinsicCodeGeneratorARM64 FINAL : public IntrinsicVisitor {
+ public:
+  explicit IntrinsicCodeGeneratorARM64(CodeGeneratorARM64* codegen) : codegen_(codegen) {}
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)   \
+  void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+  vixl::MacroAssembler* GetVIXLAssembler();
+
+  ArenaAllocator* GetAllocator();
+
+  CodeGeneratorARM64* codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorARM64);
+};
+
+}  // namespace arm64
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
new file mode 100644
index 0000000..9cc77c6
--- /dev/null
+++ b/compiler/optimizing/intrinsics_list.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
+
+// All intrinsics supported by the optimizing compiler. Format is name, then whether it is expected
+// to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual).
+
+#define INTRINSICS_LIST(V) \
+  V(DoubleDoubleToRawLongBits, kStatic) \
+  V(DoubleLongBitsToDouble, kStatic) \
+  V(FloatFloatToRawIntBits, kStatic) \
+  V(FloatIntBitsToFloat, kStatic) \
+  V(IntegerReverse, kStatic) \
+  V(IntegerReverseBytes, kStatic) \
+  V(LongReverse, kStatic) \
+  V(LongReverseBytes, kStatic) \
+  V(ShortReverseBytes, kStatic) \
+  V(MathAbsDouble, kStatic) \
+  V(MathAbsFloat, kStatic) \
+  V(MathAbsLong, kStatic) \
+  V(MathAbsInt, kStatic) \
+  V(MathMinDoubleDouble, kStatic) \
+  V(MathMinFloatFloat, kStatic) \
+  V(MathMinLongLong, kStatic) \
+  V(MathMinIntInt, kStatic) \
+  V(MathMaxDoubleDouble, kStatic) \
+  V(MathMaxFloatFloat, kStatic) \
+  V(MathMaxLongLong, kStatic) \
+  V(MathMaxIntInt, kStatic) \
+  V(MathSqrt, kStatic) \
+  V(MathCeil, kStatic) \
+  V(MathFloor, kStatic) \
+  V(MathRint, kStatic) \
+  V(MathRoundDouble, kStatic) \
+  V(MathRoundFloat, kStatic) \
+  V(SystemArrayCopyChar, kStatic) \
+  V(ThreadCurrentThread, kStatic) \
+  V(MemoryPeekByte, kStatic) \
+  V(MemoryPeekIntNative, kStatic) \
+  V(MemoryPeekLongNative, kStatic) \
+  V(MemoryPeekShortNative, kStatic) \
+  V(MemoryPokeByte, kStatic) \
+  V(MemoryPokeIntNative, kStatic) \
+  V(MemoryPokeLongNative, kStatic) \
+  V(MemoryPokeShortNative, kStatic) \
+  V(StringCharAt, kDirect) \
+  V(StringCompareTo, kDirect) \
+  V(StringIsEmpty, kDirect) \
+  V(StringIndexOf, kDirect) \
+  V(StringIndexOfAfter, kDirect) \
+  V(StringLength, kDirect) \
+  V(UnsafeCASInt, kDirect) \
+  V(UnsafeCASLong, kDirect) \
+  V(UnsafeCASObject, kDirect) \
+  V(UnsafeGet, kDirect) \
+  V(UnsafeGetVolatile, kDirect) \
+  V(UnsafeGetObject, kDirect) \
+  V(UnsafeGetObjectVolatile, kDirect) \
+  V(UnsafeGetLong, kDirect) \
+  V(UnsafeGetLongVolatile, kDirect) \
+  V(UnsafePut, kDirect) \
+  V(UnsafePutOrdered, kDirect) \
+  V(UnsafePutVolatile, kDirect) \
+  V(UnsafePutObject, kDirect) \
+  V(UnsafePutObjectOrdered, kDirect) \
+  V(UnsafePutObjectVolatile, kDirect) \
+  V(UnsafePutLong, kDirect) \
+  V(UnsafePutLongOrdered, kDirect) \
+  V(UnsafePutLongVolatile, kDirect) \
+  V(ReferenceGetReferent, kDirect)
+
+#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_
+#undef ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_   // #define is only for lint.
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
new file mode 100644
index 0000000..c73f092
--- /dev/null
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "intrinsics_x86_64.h"
+
+#include "code_generator_x86_64.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/x86_64/assembler_x86_64.h"
+#include "utils/x86_64/constants_x86_64.h"
+
+namespace art {
+
+namespace x86_64 {
+
+X86_64Assembler* IntrinsicCodeGeneratorX86_64::GetAssembler() {
+  return reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorX86_64::GetAllocator() {
+  return codegen_->GetGraph()->GetArena();
+}
+
+bool IntrinsicLocationsBuilderX86_64::TryDispatch(HInvoke* invoke) {
+  Dispatch(invoke);
+  const LocationSummary* res = invoke->GetLocations();
+  return res != nullptr && res->Intrinsified();
+}
+
+#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
+
+// TODO: trg as memory.
+static void MoveFromReturnRegister(Location trg,
+                                   Primitive::Type type,
+                                   CodeGeneratorX86_64* codegen) {
+  if (!trg.IsValid()) {
+    DCHECK(type == Primitive::kPrimVoid);
+    return;
+  }
+
+  switch (type) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot: {
+      CpuRegister trg_reg = trg.AsRegister<CpuRegister>();
+      if (trg_reg.AsRegister() != RAX) {
+        __ movl(trg_reg, CpuRegister(RAX));
+      }
+      break;
+    }
+    case Primitive::kPrimLong: {
+      CpuRegister trg_reg = trg.AsRegister<CpuRegister>();
+      if (trg_reg.AsRegister() != RAX) {
+        __ movq(trg_reg, CpuRegister(RAX));
+      }
+      break;
+    }
+
+    case Primitive::kPrimVoid:
+      LOG(FATAL) << "Unexpected void type for valid location " << trg;
+      UNREACHABLE();
+
+    case Primitive::kPrimDouble: {
+      XmmRegister trg_reg = trg.AsFpuRegister<XmmRegister>();
+      if (trg_reg.AsFloatRegister() != XMM0) {
+        __ movsd(trg_reg, XmmRegister(XMM0));
+      }
+      break;
+    }
+    case Primitive::kPrimFloat: {
+      XmmRegister trg_reg = trg.AsFpuRegister<XmmRegister>();
+      if (trg_reg.AsFloatRegister() != XMM0) {
+        __ movss(trg_reg, XmmRegister(XMM0));
+      }
+      break;
+    }
+  }
+}
+
+static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86_64* codegen) {
+  if (invoke->InputCount() == 0) {
+    return;
+  }
+
+  LocationSummary* locations = invoke->GetLocations();
+  InvokeDexCallingConventionVisitor calling_convention_visitor;
+
+  // We're moving potentially two or more locations to locations that could overlap, so we need
+  // a parallel move resolver.
+  HParallelMove parallel_move(arena);
+
+  for (size_t i = 0; i < invoke->InputCount(); i++) {
+    HInstruction* input = invoke->InputAt(i);
+    Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
+    Location actual_loc = locations->InAt(i);
+
+    parallel_move.AddMove(actual_loc, cc_loc, nullptr);
+  }
+
+  codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
+}
+
+// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified
+// call. This will copy the arguments into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations given by the invoke's location
+//       summary. If an intrinsic modifies those locations before a slowpath call, they must be
+//       restored!
+class IntrinsicSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+  explicit IntrinsicSlowPathX86_64(HInvoke* invoke) : invoke_(invoke) { }
+
+  void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+    CodeGeneratorX86_64* codegen = down_cast<CodeGeneratorX86_64*>(codegen_in);
+    __ Bind(GetEntryLabel());
+
+    codegen->SaveLiveRegisters(invoke_->GetLocations());
+
+    MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen);
+
+    if (invoke_->IsInvokeStaticOrDirect()) {
+      codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), CpuRegister(RDI));
+    } else {
+      UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+      UNREACHABLE();
+    }
+
+    // Copy the result back to the expected output.
+    Location out = invoke_->GetLocations()->Out();
+    if (out.IsValid()) {
+      DCHECK(out.IsRegister());  // TODO: Replace this when we support output in memory.
+      DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+      MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+    }
+
+    codegen->RestoreLiveRegisters(invoke_->GetLocations());
+    __ jmp(GetExitLabel());
+  }
+
+ private:
+  // The instruction where this slow path is happening.
+  HInvoke* const invoke_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathX86_64);
+};
+
+#undef __
+#define __ assembler->
+
+static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresFpuRegister());
+}
+
+static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+  Location input = locations->InAt(0);
+  Location output = locations->Out();
+  __ movd(output.AsRegister<CpuRegister>(), input.AsFpuRegister<XmmRegister>(), is64bit);
+}
+
+static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+  Location input = locations->InAt(0);
+  Location output = locations->Out();
+  __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<CpuRegister>(), is64bit);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), true, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitDoubleLongBitsToDouble(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  CreateFPToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  CreateIntToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitFloatFloatToRawIntBits(HInvoke* invoke) {
+  MoveFPToInt(invoke->GetLocations(), false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitFloatIntBitsToFloat(HInvoke* invoke) {
+  MoveIntToFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::SameAsFirstInput());
+}
+
+static void GenReverseBytes(LocationSummary* locations,
+                            Primitive::Type size,
+                            X86_64Assembler* assembler) {
+  CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+  switch (size) {
+    case Primitive::kPrimShort:
+      // TODO: Can be done with an xchg of 8b registers. This is straight from Quick.
+      __ bswapl(out);
+      __ sarl(out, Immediate(16));
+      break;
+    case Primitive::kPrimInt:
+      __ bswapl(out);
+      break;
+    case Primitive::kPrimLong:
+      __ bswapq(out);
+      break;
+    default:
+      LOG(FATAL) << "Unexpected size for reverse-bytes: " << size;
+      UNREACHABLE();
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitIntegerReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitLongReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitLongReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitShortReverseBytes(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitShortReverseBytes(HInvoke* invoke) {
+  GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
+}
+
+
+// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we
+//       need is 64b.
+
+static void CreateFloatToFloatPlusTemps(ArenaAllocator* arena, HInvoke* invoke) {
+  // TODO: Enable memory operations when the assembler supports them.
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  // TODO: Allow x86 to work with memory. This requires assembler support, see below.
+  // locations->SetInAt(0, Location::Any());               // X86 can work on memory directly.
+  locations->SetOut(Location::SameAsFirstInput());
+  locations->AddTemp(Location::RequiresRegister());     // Immediate constant.
+  locations->AddTemp(Location::RequiresFpuRegister());  // FP version of above.
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+  Location output = locations->Out();
+  CpuRegister cpu_temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+  if (output.IsFpuRegister()) {
+    // In-register
+    XmmRegister xmm_temp = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
+
+    if (is64bit) {
+      __ movq(cpu_temp, Immediate(INT64_C(0x7FFFFFFFFFFFFFFF)));
+      __ movd(xmm_temp, cpu_temp);
+      __ andpd(output.AsFpuRegister<XmmRegister>(), xmm_temp);
+    } else {
+      __ movl(cpu_temp, Immediate(INT64_C(0x7FFFFFFF)));
+      __ movd(xmm_temp, cpu_temp);
+      __ andps(output.AsFpuRegister<XmmRegister>(), xmm_temp);
+    }
+  } else {
+    // TODO: update when assember support is available.
+    UNIMPLEMENTED(FATAL) << "Needs assembler support.";
+//  Once assembler support is available, in-memory operations look like this:
+//    if (is64bit) {
+//      DCHECK(output.IsDoubleStackSlot());
+//      // No 64b and with literal.
+//      __ movq(cpu_temp, Immediate(INT64_C(0x7FFFFFFFFFFFFFFF)));
+//      __ andq(Address(CpuRegister(RSP), output.GetStackIndex()), cpu_temp);
+//    } else {
+//      DCHECK(output.IsStackSlot());
+//      // Can use and with a literal directly.
+//      __ andl(Address(CpuRegister(RSP), output.GetStackIndex()), Immediate(INT64_C(0x7FFFFFFF)));
+//    }
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsDouble(HInvoke* invoke) {
+  CreateFloatToFloatPlusTemps(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsDouble(HInvoke* invoke) {
+  MathAbsFP(invoke->GetLocations(), true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsFloat(HInvoke* invoke) {
+  CreateFloatToFloatPlusTemps(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsFloat(HInvoke* invoke) {
+  MathAbsFP(invoke->GetLocations(), false, GetAssembler());
+}
+
+static void CreateIntToIntPlusTemp(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::SameAsFirstInput());
+  locations->AddTemp(Location::RequiresRegister());
+}
+
+static void GenAbsInteger(LocationSummary* locations, bool is64bit, X86_64Assembler* assembler) {
+  Location output = locations->Out();
+  CpuRegister out = output.AsRegister<CpuRegister>();
+  CpuRegister mask = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+  if (is64bit) {
+    // Create mask.
+    __ movq(mask, out);
+    __ sarq(mask, Immediate(63));
+    // Add mask.
+    __ addq(out, mask);
+    __ xorq(out, mask);
+  } else {
+    // Create mask.
+    __ movl(mask, out);
+    __ sarl(mask, Immediate(31));
+    // Add mask.
+    __ addl(out, mask);
+    __ xorl(out, mask);
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsInt(HInvoke* invoke) {
+  CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsInt(HInvoke* invoke) {
+  GenAbsInteger(invoke->GetLocations(), false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAbsLong(HInvoke* invoke) {
+  CreateIntToIntPlusTemp(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAbsLong(HInvoke* invoke) {
+  GenAbsInteger(invoke->GetLocations(), true, GetAssembler());
+}
+
+static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double,
+                        X86_64Assembler* assembler) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+  Location out_loc = locations->Out();
+  XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
+
+  // Shortcut for same input locations.
+  if (op1_loc.Equals(op2_loc)) {
+    DCHECK(out_loc.Equals(op1_loc));
+    return;
+  }
+
+  //  (out := op1)
+  //  out <=? op2
+  //  if Nan jmp Nan_label
+  //  if out is min jmp done
+  //  if op2 is min jmp op2_label
+  //  handle -0/+0
+  //  jmp done
+  // Nan_label:
+  //  out := NaN
+  // op2_label:
+  //  out := op2
+  // done:
+  //
+  // This removes one jmp, but needs to copy one input (op1) to out.
+  //
+  // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath?
+
+  XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>();
+
+  Label nan, done, op2_label;
+  if (is_double) {
+    __ ucomisd(out, op2);
+  } else {
+    __ ucomiss(out, op2);
+  }
+
+  __ j(Condition::kParityEven, &nan);
+
+  __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label);
+  __ j(is_min ? Condition::kBelow : Condition::kAbove, &done);
+
+  // Handle 0.0/-0.0.
+  if (is_min) {
+    if (is_double) {
+      __ orpd(out, op2);
+    } else {
+      __ orps(out, op2);
+    }
+  } else {
+    if (is_double) {
+      __ andpd(out, op2);
+    } else {
+      __ andps(out, op2);
+    }
+  }
+  __ jmp(&done);
+
+  // NaN handling.
+  __ Bind(&nan);
+  CpuRegister cpu_temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+  // TODO: Literal pool. Trades 64b immediate in CPU reg for direct memory access.
+  if (is_double) {
+    __ movq(cpu_temp, Immediate(INT64_C(0x7FF8000000000000)));
+  } else {
+    __ movl(cpu_temp, Immediate(INT64_C(0x7FC00000)));
+  }
+  __ movd(out, cpu_temp, is_double);
+  __ jmp(&done);
+
+  // out := op2;
+  __ Bind(&op2_label);
+  if (is_double) {
+    __ movsd(out, op2);
+  } else {
+    __ movss(out, op2);
+  }
+
+  // Done.
+  __ Bind(&done);
+}
+
+static void CreateFPFPToFPPlusTempLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetInAt(1, Location::RequiresFpuRegister());
+  // The following is sub-optimal, but all we can do for now. It would be fine to also accept
+  // the second input to be the output (we can simply swap inputs).
+  locations->SetOut(Location::SameAsFirstInput());
+  locations->AddTemp(Location::RequiresRegister());     // Immediate constant.
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+  CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinDoubleDouble(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), true, true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
+  CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinFloatFloat(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), true, false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+  CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), false, true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+  CreateFPFPToFPPlusTempLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxFloatFloat(HInvoke* invoke) {
+  GenMinMaxFP(invoke->GetLocations(), false, false, GetAssembler());
+}
+
+static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
+                      X86_64Assembler* assembler) {
+  Location op1_loc = locations->InAt(0);
+  Location op2_loc = locations->InAt(1);
+
+  // Shortcut for same input locations.
+  if (op1_loc.Equals(op2_loc)) {
+    // Can return immediately, as op1_loc == out_loc.
+    // Note: if we ever support separate registers, e.g., output into memory, we need to check for
+    //       a copy here.
+    DCHECK(locations->Out().Equals(op1_loc));
+    return;
+  }
+
+  CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+  CpuRegister op2 = op2_loc.AsRegister<CpuRegister>();
+
+  //  (out := op1)
+  //  out <=? op2
+  //  if out is min jmp done
+  //  out := op2
+  // done:
+
+  if (is_long) {
+    __ cmpq(out, op2);
+  } else {
+    __ cmpl(out, op2);
+  }
+
+  __ cmov(is_min ? Condition::kGreater : Condition::kLess, out, op2, is_long);
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::SameAsFirstInput());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinIntInt(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinIntInt(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), true, false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMinLongLong(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMinLongLong(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), true, true, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxIntInt(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), false, false, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
+  CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathMaxLongLong(HInvoke* invoke) {
+  GenMinMax(invoke->GetLocations(), false, true, GetAssembler());
+}
+
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresFpuRegister());
+  locations->SetOut(Location::RequiresFpuRegister());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathSqrt(HInvoke* invoke) {
+  CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathSqrt(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>();
+
+  GetAssembler()->sqrtsd(out, in);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
+  // The inputs plus one temp.
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kCallOnSlowPath,
+                                                            kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetOut(Location::SameAsFirstInput());
+  locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+
+  // Location of reference to data array
+  const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+  // Location of count
+  const int32_t count_offset = mirror::String::CountOffset().Int32Value();
+  // Starting offset within data array
+  const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value();
+  // Start of char data with array_
+  const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
+
+  CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
+  CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>();
+  CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+  Location temp_loc = locations->GetTemp(0);
+  CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
+
+  // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
+  //       the cost.
+  // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
+  //       we will not optimize the code for constants (which would save a register).
+
+  SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
+  codegen_->AddSlowPath(slow_path);
+
+  X86_64Assembler* assembler = GetAssembler();
+
+  __ cmpl(idx, Address(obj, count_offset));
+  codegen_->MaybeRecordImplicitNullCheck(invoke);
+  __ j(kAboveEqual, slow_path->GetEntryLabel());
+
+  // Get the actual element.
+  __ movl(temp, idx);                          // temp := idx.
+  __ addl(temp, Address(obj, offset_offset));  // temp := offset + idx.
+  __ movl(out, Address(obj, value_offset));    // obj := obj.array.
+  // out = out[2*temp].
+  __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset));
+
+  __ Bind(slow_path->GetExitLabel());
+}
+
+static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
+  CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>();
+  CpuRegister out = locations->Out().AsRegister<CpuRegister>();  // == address, here for clarity.
+  // x86 allows unaligned access. We do not have to check the input or use specific instructions
+  // to avoid a SIGBUS.
+  switch (size) {
+    case Primitive::kPrimByte:
+      __ movsxb(out, Address(address, 0));
+      break;
+    case Primitive::kPrimShort:
+      __ movsxw(out, Address(address, 0));
+      break;
+    case Primitive::kPrimInt:
+      __ movl(out, Address(address, 0));
+      break;
+    case Primitive::kPrimLong:
+      __ movq(out, Address(address, 0));
+      break;
+    default:
+      LOG(FATAL) << "Type not recognized for peek: " << size;
+      UNREACHABLE();
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekByte(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekByte(HInvoke* invoke) {
+  GenPeek(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekIntNative(HInvoke* invoke) {
+  GenPeek(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekLongNative(HInvoke* invoke) {
+  GenPeek(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPeekShortNative(HInvoke* invoke) {
+  GenPeek(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetInAt(1, Location::RequiresRegister());
+}
+
+static void GenPoke(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) {
+  CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>();
+  CpuRegister value = locations->InAt(1).AsRegister<CpuRegister>();
+  // x86 allows unaligned access. We do not have to check the input or use specific instructions
+  // to avoid a SIGBUS.
+  switch (size) {
+    case Primitive::kPrimByte:
+      __ movb(Address(address, 0), value);
+      break;
+    case Primitive::kPrimShort:
+      __ movw(Address(address, 0), value);
+      break;
+    case Primitive::kPrimInt:
+      __ movl(Address(address, 0), value);
+      break;
+    case Primitive::kPrimLong:
+      __ movq(Address(address, 0), value);
+      break;
+    default:
+      LOG(FATAL) << "Type not recognized for poke: " << size;
+      UNREACHABLE();
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeByte(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeByte(HInvoke* invoke) {
+  GenPoke(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeIntNative(HInvoke* invoke) {
+  GenPoke(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeLongNative(HInvoke* invoke) {
+  GenPoke(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+  CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMemoryPokeShortNative(HInvoke* invoke) {
+  GenPoke(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitThreadCurrentThread(HInvoke* invoke) {
+  LocationSummary* locations = new (arena_) LocationSummary(invoke,
+                                                            LocationSummary::kNoCall,
+                                                            kIntrinsified);
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitThreadCurrentThread(HInvoke* invoke) {
+  CpuRegister out = invoke->GetLocations()->Out().AsRegister<CpuRegister>();
+  GetAssembler()->gs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86_64WordSize>(), true));
+}
+
+static void GenUnsafeGet(LocationSummary* locations, Primitive::Type type,
+                         bool is_volatile ATTRIBUTE_UNUSED, X86_64Assembler* assembler) {
+  CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
+  CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
+  CpuRegister trg = locations->Out().AsRegister<CpuRegister>();
+
+  switch (type) {
+    case Primitive::kPrimInt:
+    case Primitive::kPrimNot:
+      __ movl(trg, Address(base, offset, ScaleFactor::TIMES_1, 0));
+      break;
+
+    case Primitive::kPrimLong:
+      __ movq(trg, Address(base, offset, ScaleFactor::TIMES_1, 0));
+      break;
+
+    default:
+      LOG(FATAL) << "Unsupported op size " << type;
+      UNREACHABLE();
+  }
+}
+
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGet(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+  CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGet(HInvoke* invoke) {
+  GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, true, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
+  GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, true, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
+  GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, false, GetAssembler());
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+  GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, true, GetAssembler());
+}
+
+
+static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* arena,
+                                                       Primitive::Type type,
+                                                       HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+  if (type == Primitive::kPrimNot) {
+    // Need temp registers for card-marking.
+    locations->AddTemp(Location::RequiresRegister());
+    locations->AddTemp(Location::RequiresRegister());
+  }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePut(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObject(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLong(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
+}
+void IntrinsicLocationsBuilderX86_64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+  CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke);
+}
+
+// We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86
+// memory model.
+static void GenUnsafePut(LocationSummary* locations, Primitive::Type type, bool is_volatile,
+                         CodeGeneratorX86_64* codegen) {
+  X86_64Assembler* assembler = reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler());
+  CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
+  CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
+  CpuRegister value = locations->InAt(3).AsRegister<CpuRegister>();
+
+  if (type == Primitive::kPrimLong) {
+    __ movq(Address(base, offset, ScaleFactor::TIMES_1, 0), value);
+  } else {
+    __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value);
+  }
+
+  if (is_volatile) {
+    __ mfence();
+  }
+
+  if (type == Primitive::kPrimNot) {
+    codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
+                        locations->GetTemp(1).AsRegister<CpuRegister>(),
+                        base,
+                        value);
+  }
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePut(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObject(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLong(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_);
+}
+void IntrinsicCodeGeneratorX86_64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+  GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_);
+}
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name)                                                   \
+void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}                                                                                       \
+void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) {    \
+}
+
+UNIMPLEMENTED_INTRINSIC(IntegerReverse)
+UNIMPLEMENTED_INTRINSIC(LongReverse)
+UNIMPLEMENTED_INTRINSIC(MathFloor)
+UNIMPLEMENTED_INTRINSIC(MathCeil)
+UNIMPLEMENTED_INTRINSIC(MathRint)
+UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
+UNIMPLEMENTED_INTRINSIC(MathRoundFloat)
+UNIMPLEMENTED_INTRINSIC(StringIsEmpty)  // Might not want to do these two anyways, inlining should
+UNIMPLEMENTED_INTRINSIC(StringLength)   // be good enough here.
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+
+}  // namespace x86_64
+}  // namespace art
diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h
new file mode 100644
index 0000000..dfae7fa
--- /dev/null
+++ b/compiler/optimizing/intrinsics_x86_64.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
+
+#include "intrinsics.h"
+
+namespace art {
+
+class ArenaAllocator;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace x86_64 {
+
+class CodeGeneratorX86_64;
+class X86_64Assembler;
+
+class IntrinsicLocationsBuilderX86_64 FINAL : public IntrinsicVisitor {
+ public:
+  explicit IntrinsicLocationsBuilderX86_64(ArenaAllocator* arena) : arena_(arena) {}
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)   \
+  void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+  // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+  // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+  // the invoke.
+  bool TryDispatch(HInvoke* invoke);
+
+ private:
+  ArenaAllocator* arena_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderX86_64);
+};
+
+class IntrinsicCodeGeneratorX86_64 FINAL : public IntrinsicVisitor {
+ public:
+  explicit IntrinsicCodeGeneratorX86_64(CodeGeneratorX86_64* codegen) : codegen_(codegen) {}
+
+  // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic)   \
+  void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+  X86_64Assembler* GetAssembler();
+
+  ArenaAllocator* GetAllocator();
+
+  CodeGeneratorX86_64* codegen_;
+
+  DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorX86_64);
+};
+
+}  // namespace x86_64
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
new file mode 100644
index 0000000..10f24d8
--- /dev/null
+++ b/compiler/optimizing/licm.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "licm.h"
+#include "side_effects_analysis.h"
+
+namespace art {
+
+static bool IsPhiOf(HInstruction* instruction, HBasicBlock* block) {
+  return instruction->IsPhi() && instruction->GetBlock() == block;
+}
+
+/**
+ * Returns whether `instruction` has all its inputs and environment defined
+ * before the loop it is in.
+ */
+static bool InputsAreDefinedBeforeLoop(HInstruction* instruction) {
+  DCHECK(instruction->IsInLoop());
+  HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+  for (HInputIterator it(instruction); !it.Done(); it.Advance()) {
+    HLoopInformation* input_loop = it.Current()->GetBlock()->GetLoopInformation();
+    // We only need to check whether the input is defined in the loop. If it is not
+    // it is defined before the loop.
+    if (input_loop != nullptr && input_loop->IsIn(*info)) {
+      return false;
+    }
+  }
+
+  if (instruction->HasEnvironment()) {
+    HEnvironment* environment = instruction->GetEnvironment();
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* input = environment->GetInstructionAt(i);
+      if (input != nullptr) {
+        HLoopInformation* input_loop = input->GetBlock()->GetLoopInformation();
+        if (input_loop != nullptr && input_loop->IsIn(*info)) {
+          // We can move an instruction that takes a loop header phi in the environment:
+          // we will just replace that phi with its first input later in `UpdateLoopPhisIn`.
+          bool is_loop_header_phi = IsPhiOf(input, info->GetHeader());
+          if (!is_loop_header_phi) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
+/**
+ * If `environment` has a loop header phi, we replace it with its first input.
+ */
+static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) {
+  for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+    HInstruction* input = environment->GetInstructionAt(i);
+    if (input != nullptr && IsPhiOf(input, info->GetHeader())) {
+      HUseListNode<HEnvironment*>* env_use = environment->GetInstructionEnvUseAt(i);
+      input->RemoveEnvironmentUser(env_use);
+      HInstruction* incoming = input->InputAt(0);
+      environment->SetRawEnvAt(i, incoming);
+      incoming->AddEnvUseAt(environment, i);
+    }
+  }
+}
+
+void LICM::Run() {
+  DCHECK(side_effects_.HasRun());
+  // Only used during debug.
+  ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().Size(), false);
+
+  // Post order visit to visit inner loops before outer loops.
+  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    if (!block->IsLoopHeader()) {
+      // Only visit the loop when we reach the header.
+      continue;
+    }
+
+    HLoopInformation* loop_info = block->GetLoopInformation();
+    SideEffects loop_effects = side_effects_.GetLoopEffects(block);
+    HBasicBlock* pre_header = loop_info->GetPreHeader();
+
+    for (HBlocksInLoopIterator it_loop(*loop_info); !it_loop.Done(); it_loop.Advance()) {
+      HBasicBlock* inner = it_loop.Current();
+      DCHECK(inner->IsInLoop());
+      if (inner->GetLoopInformation() != loop_info) {
+        // Thanks to post order visit, inner loops were already visited.
+        DCHECK(visited.IsBitSet(inner->GetBlockId()));
+        continue;
+      }
+      visited.SetBit(inner->GetBlockId());
+
+      // We can move an instruction that can throw only if it is the first
+      // throwing instruction in the loop. Note that the first potentially
+      // throwing instruction encountered that is not hoisted stops this
+      // optimization. Non-throwing instruction can still be hoisted.
+      bool found_first_non_hoisted_throwing_instruction_in_loop = !inner->IsLoopHeader();
+      for (HInstructionIterator inst_it(inner->GetInstructions());
+           !inst_it.Done();
+           inst_it.Advance()) {
+        HInstruction* instruction = inst_it.Current();
+        if (instruction->CanBeMoved()
+            && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop)
+            && !instruction->GetSideEffects().DependsOn(loop_effects)
+            && InputsAreDefinedBeforeLoop(instruction)) {
+          // We need to update the environment if the instruction has a loop header
+          // phi in it.
+          if (instruction->NeedsEnvironment()) {
+            UpdateLoopPhisIn(instruction->GetEnvironment(), loop_info);
+          }
+          instruction->MoveBefore(pre_header->GetLastInstruction());
+        } else if (instruction->CanThrow()) {
+          // If `instruction` can throw, we cannot move further instructions
+          // that can throw as well.
+          found_first_non_hoisted_throwing_instruction_in_loop = true;
+        }
+      }
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
new file mode 100644
index 0000000..4812394
--- /dev/null
+++ b/compiler/optimizing/licm.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_LICM_H_
+#define ART_COMPILER_OPTIMIZING_LICM_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class SideEffectsAnalysis;
+
+class LICM : public HOptimization {
+ public:
+  LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
+      : HOptimization(graph, true, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+
+  void Run() OVERRIDE;
+
+ private:
+  const SideEffectsAnalysis& side_effects_;
+
+  DISALLOW_COPY_AND_ASSIGN(LICM);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_LICM_H_
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 28ca5e8..eb27965 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -22,6 +22,7 @@
 #include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
+#include "driver/compiler_options.h"
 #include "graph_visualizer.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
@@ -37,16 +38,15 @@
 static void TestCode(const uint16_t* data, const int* expected_order, size_t number_of_blocks) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
 
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
diff --git a/compiler/optimizing/live_interval_test.cc b/compiler/optimizing/live_interval_test.cc
index 3e4b83b..ac8759c 100644
--- a/compiler/optimizing/live_interval_test.cc
+++ b/compiler/optimizing/live_interval_test.cc
@@ -278,4 +278,55 @@
   }
 }
 
+TEST(LiveInterval, AddLoopRange) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  {
+    // Test when only used in a loop.
+    static constexpr size_t ranges[][2] = {{0, 4}};
+    LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+    interval->AddLoopRange(0, 8);
+    LiveRange* range = interval->GetFirstRange();
+    ASSERT_TRUE(range->GetNext() == nullptr);
+    ASSERT_EQ(range->GetStart(), 0u);
+    ASSERT_EQ(range->GetEnd(), 8u);
+  }
+
+  {
+    // Test when only used in a loop.
+    static constexpr size_t ranges[][2] = {{2, 4}};
+    LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+    interval->AddLoopRange(0, 8);
+    LiveRange* range = interval->GetFirstRange();
+    ASSERT_TRUE(range->GetNext() == nullptr);
+    ASSERT_EQ(range->GetStart(), 0u);
+    ASSERT_EQ(range->GetEnd(), 8u);
+  }
+
+  {
+    // Test when used just after the loop.
+    static constexpr size_t ranges[][2] = {{2, 4}, {8, 10}};
+    LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+    interval->AddLoopRange(0, 8);
+    LiveRange* range = interval->GetFirstRange();
+    ASSERT_TRUE(range->GetNext() == nullptr);
+    ASSERT_EQ(range->GetStart(), 0u);
+    ASSERT_EQ(range->GetEnd(), 10u);
+  }
+
+  {
+    // Test when use after the loop is after a lifetime hole.
+    static constexpr size_t ranges[][2] = {{2, 4}, {10, 12}};
+    LiveInterval* interval = BuildInterval(ranges, arraysize(ranges), &allocator);
+    interval->AddLoopRange(0, 8);
+    LiveRange* range = interval->GetFirstRange();
+    ASSERT_EQ(range->GetStart(), 0u);
+    ASSERT_EQ(range->GetEnd(), 8u);
+    range = range->GetNext();
+    ASSERT_EQ(range->GetStart(), 10u);
+    ASSERT_EQ(range->GetEnd(), 12u);
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 5c7e6f0..0558b85 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -19,6 +19,7 @@
 #include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 #include "prepare_for_register_allocation.h"
@@ -30,15 +31,14 @@
 namespace art {
 
 static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) {
-  HGraphBuilder builder(allocator);
+  HGraph* graph = new (allocator) HGraph(allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
+  builder.BuildGraph(*item);
   // Suspend checks implementation may change in the future, and this test relies
   // on how instructions are ordered.
   RemoveSuspendChecks(graph);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
   // `Inline` conditions into ifs.
   PrepareForRegisterAllocation(graph).Run();
   return graph;
@@ -65,7 +65,7 @@
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
 
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -111,7 +111,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -160,7 +160,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -237,7 +237,7 @@
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
   RemoveSuspendChecks(graph);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -315,7 +315,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -391,7 +391,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildGraph(data, &allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -433,7 +433,7 @@
   ASSERT_TRUE(range->GetNext() == nullptr);
 
   HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi();
-  ASSERT_EQ(phi->NumberOfUses(), 1u);
+  ASSERT_TRUE(phi->GetUses().HasOnlyOneUse());
   interval = phi->GetLiveInterval();
   range = interval->GetFirstRange();
   ASSERT_EQ(26u, range->GetStart());
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 4b69e57..c9be570 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -19,6 +19,7 @@
 #include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 #include "prepare_for_register_allocation.h"
@@ -44,16 +45,15 @@
 static void TestCode(const uint16_t* data, const char* expected) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
+  graph->TryBuildingSsa();
   // `Inline` conditions into ifs.
   PrepareForRegisterAllocation(graph).Run();
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index ed5e260..4ac1fe8 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,16 +20,19 @@
 
 namespace art {
 
-LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind)
+LocationSummary::LocationSummary(HInstruction* instruction,
+                                 CallKind call_kind,
+                                 bool intrinsified)
     : inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
       temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0),
       environment_(instruction->GetBlock()->GetGraph()->GetArena(),
                    instruction->EnvironmentSize()),
-      output_overlaps_(true),
+      output_overlaps_(Location::kOutputOverlap),
       call_kind_(call_kind),
       stack_mask_(nullptr),
       register_mask_(0),
-      live_registers_() {
+      live_registers_(),
+      intrinsified_(intrinsified) {
   inputs_.SetSize(instruction->InputCount());
   for (size_t i = 0; i < instruction->InputCount(); ++i) {
     inputs_.Put(i, Location());
@@ -61,6 +64,13 @@
 
 std::ostream& operator<<(std::ostream& os, const Location& location) {
   os << location.DebugString();
+  if (location.IsRegister() || location.IsFpuRegister()) {
+    os << location.reg();
+  } else if (location.IsPair()) {
+    os << location.low() << ":" << location.high();
+  } else if (location.IsStackSlot() || location.IsDoubleStackSlot()) {
+    os << location.GetStackIndex();
+  }
   return os;
 }
 
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 1ff26d9..9ce8d35 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -37,7 +37,10 @@
  */
 class Location : public ValueObject {
  public:
-  static constexpr bool kNoOutputOverlap = false;
+  enum OutputOverlap {
+    kOutputOverlap,
+    kNoOutputOverlap
+  };
 
   enum Kind {
     kInvalid = 0,
@@ -59,17 +62,11 @@
     // We do not use the value 9 because it conflicts with kLocationConstantMask.
     kDoNotUse9 = 9,
 
-    // On 32bits architectures, quick can pass a long where the
-    // low bits are in the last parameter register, and the high
-    // bits are in a stack slot. The kQuickParameter kind is for
-    // handling this special case.
-    kQuickParameter = 10,
-
     // Unallocated location represents a location that is not fixed and can be
     // allocated by a register allocator.  Each unallocated location has
     // a policy that specifies what kind of location is suitable. Payload
     // contains register allocation policy.
-    kUnallocated = 11,
+    kUnallocated = 10,
   };
 
   Location() : value_(kInvalid) {
@@ -79,7 +76,6 @@
     static_assert((kStackSlot & kLocationConstantMask) != kConstant, "TagError");
     static_assert((kDoubleStackSlot & kLocationConstantMask) != kConstant, "TagError");
     static_assert((kRegister & kLocationConstantMask) != kConstant, "TagError");
-    static_assert((kQuickParameter & kLocationConstantMask) != kConstant, "TagError");
     static_assert((kFpuRegister & kLocationConstantMask) != kConstant, "TagError");
     static_assert((kRegisterPair & kLocationConstantMask) != kConstant, "TagError");
     static_assert((kFpuRegisterPair & kLocationConstantMask) != kConstant, "TagError");
@@ -160,6 +156,16 @@
     return GetPayload();
   }
 
+  int low() const {
+    DCHECK(IsPair());
+    return GetPayload() >> 16;
+  }
+
+  int high() const {
+    DCHECK(IsPair());
+    return GetPayload() & 0xFFFF;
+  }
+
   template <typename T>
   T AsRegister() const {
     DCHECK(IsRegister());
@@ -175,25 +181,41 @@
   template <typename T>
   T AsRegisterPairLow() const {
     DCHECK(IsRegisterPair());
-    return static_cast<T>(GetPayload() >> 16);
+    return static_cast<T>(low());
   }
 
   template <typename T>
   T AsRegisterPairHigh() const {
     DCHECK(IsRegisterPair());
-    return static_cast<T>(GetPayload() & 0xFFFF);
+    return static_cast<T>(high());
   }
 
   template <typename T>
   T AsFpuRegisterPairLow() const {
     DCHECK(IsFpuRegisterPair());
-    return static_cast<T>(GetPayload() >> 16);
+    return static_cast<T>(low());
   }
 
   template <typename T>
   T AsFpuRegisterPairHigh() const {
     DCHECK(IsFpuRegisterPair());
-    return static_cast<T>(GetPayload() & 0xFFFF);
+    return static_cast<T>(high());
+  }
+
+  bool IsPair() const {
+    return IsRegisterPair() || IsFpuRegisterPair();
+  }
+
+  Location ToLow() const {
+    return IsRegisterPair()
+        ? Location::RegisterLocation(low())
+        : Location::FpuRegisterLocation(low());
+  }
+
+  Location ToHigh() const {
+    return IsRegisterPair()
+        ? Location::RegisterLocation(high())
+        : Location::FpuRegisterLocation(high());
   }
 
   static uintptr_t EncodeStackIndex(intptr_t stack_index) {
@@ -238,24 +260,6 @@
     return GetPayload() - kStackIndexBias + word_size;
   }
 
-  static Location QuickParameter(uint16_t register_index, uint16_t stack_index) {
-    return Location(kQuickParameter, register_index << 16 | stack_index);
-  }
-
-  uint32_t GetQuickParameterRegisterIndex() const {
-    DCHECK(IsQuickParameter());
-    return GetPayload() >> 16;
-  }
-
-  uint32_t GetQuickParameterStackIndex() const {
-    DCHECK(IsQuickParameter());
-    return GetPayload() & 0xFFFF;
-  }
-
-  bool IsQuickParameter() const {
-    return GetKind() == kQuickParameter;
-  }
-
   Kind GetKind() const {
     return IsConstant() ? kConstant : KindField::Decode(value_);
   }
@@ -264,13 +268,26 @@
     return value_ == other.value_;
   }
 
+  bool Contains(Location other) const {
+    if (Equals(other)) {
+      return true;
+    } else if (IsFpuRegisterPair() && other.IsFpuRegister()) {
+      return other.reg() == low() || other.reg() == high();
+    } else if (IsRegisterPair() && other.IsRegister()) {
+      return other.reg() == low() || other.reg() == high();
+    } else if (IsDoubleStackSlot() && other.IsStackSlot()) {
+      return (GetStackIndex() == other.GetStackIndex())
+          || (GetStackIndex() + 4 == other.GetStackIndex());
+    }
+    return false;
+  }
+
   const char* DebugString() const {
     switch (GetKind()) {
       case kInvalid: return "I";
       case kRegister: return "R";
       case kStackSlot: return "S";
       case kDoubleStackSlot: return "DS";
-      case kQuickParameter: return "Q";
       case kUnallocated: return "U";
       case kConstant: return "C";
       case kFpuRegister: return "F";
@@ -402,6 +419,14 @@
     return __builtin_popcount(core_registers_) + __builtin_popcount(floating_point_registers_);
   }
 
+  uint32_t GetCoreRegisters() const {
+    return core_registers_;
+  }
+
+  uint32_t GetFloatingPointRegisters() const {
+    return floating_point_registers_;
+  }
+
  private:
   uint32_t core_registers_;
   uint32_t floating_point_registers_;
@@ -409,6 +434,8 @@
   DISALLOW_COPY_AND_ASSIGN(RegisterSet);
 };
 
+static constexpr bool kIntrinsified = true;
+
 /**
  * The code generator computes LocationSummary for each instruction so that
  * the instruction itself knows what code to generate: where to find the inputs
@@ -425,7 +452,9 @@
     kCall
   };
 
-  LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
+  LocationSummary(HInstruction* instruction,
+                  CallKind call_kind = kNoCall,
+                  bool intrinsified = false);
 
   void SetInAt(uint32_t at, Location location) {
     DCHECK(inputs_.Get(at).IsUnallocated() || inputs_.Get(at).IsInvalid());
@@ -440,17 +469,18 @@
     return inputs_.Size();
   }
 
-  void SetOut(Location location, bool overlaps = true) {
-    DCHECK(output_.IsUnallocated() || output_.IsInvalid());
+  void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) {
+    DCHECK(output_.IsInvalid());
     output_overlaps_ = overlaps;
     output_ = location;
   }
 
   void UpdateOut(Location location) {
-    // The only reason for updating an output is for parameters where
-    // we only know the exact stack slot after doing full register
-    // allocation.
-    DCHECK(output_.IsStackSlot() || output_.IsDoubleStackSlot());
+    // There are two reasons for updating an output:
+    // 1) Parameters, where we only know the exact stack slot after
+    //    doing full register allocation.
+    // 2) Unallocated location.
+    DCHECK(output_.IsStackSlot() || output_.IsDoubleStackSlot() || output_.IsUnallocated());
     output_ = location;
   }
 
@@ -498,6 +528,10 @@
     register_mask_ |= (1 << reg_id);
   }
 
+  uint32_t GetRegisterMask() const {
+    return register_mask_;
+  }
+
   bool RegisterContainsObject(uint32_t reg_id) {
     return RegisterSet::Contains(register_mask_, reg_id);
   }
@@ -518,21 +552,27 @@
     return live_registers_.GetNumberOfRegisters();
   }
 
-  bool InputOverlapsWithOutputOrTemp(uint32_t input_index, bool is_environment) const {
-    if (is_environment) return true;
-    if ((input_index == 0)
+  bool OutputUsesSameAs(uint32_t input_index) const {
+    return (input_index == 0)
         && output_.IsUnallocated()
-        && (output_.GetPolicy() == Location::kSameAsFirstInput)) {
-      return false;
-    }
-    if (inputs_.Get(input_index).IsRegister() || inputs_.Get(input_index).IsFpuRegister()) {
-      return false;
-    }
-    return true;
+        && (output_.GetPolicy() == Location::kSameAsFirstInput);
   }
 
-  bool OutputOverlapsWithInputs() const {
-    return output_overlaps_;
+  bool IsFixedInput(uint32_t input_index) const {
+    Location input = inputs_.Get(input_index);
+    return input.IsRegister()
+        || input.IsFpuRegister()
+        || input.IsPair()
+        || input.IsStackSlot()
+        || input.IsDoubleStackSlot();
+  }
+
+  bool OutputCanOverlapWithInputs() const {
+    return output_overlaps_ == Location::kOutputOverlap;
+  }
+
+  bool Intrinsified() const {
+    return intrinsified_;
   }
 
  private:
@@ -541,7 +581,7 @@
   GrowableArray<Location> environment_;
   // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot
   // share the same register as the inputs.
-  bool output_overlaps_;
+  Location::OutputOverlap output_overlaps_;
   Location output_;
   const CallKind call_kind_;
 
@@ -554,6 +594,9 @@
   // Registers that are in use at this position.
   RegisterSet live_registers_;
 
+  // Whether these are locations for an intrinsified call.
+  const bool intrinsified_;
+
   ART_FRIEND_TEST(RegisterAllocatorTest, ExpectedInRegisterHint);
   ART_FRIEND_TEST(RegisterAllocatorTest, SameAsFirstInputHint);
   DISALLOW_COPY_AND_ASSIGN(LocationSummary);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ba4dccf..cd36598 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -15,6 +15,7 @@
  */
 
 #include "nodes.h"
+
 #include "ssa_builder.h"
 #include "utils/growable_array.h"
 
@@ -38,9 +39,11 @@
   HEnvironment* environment = instruction->GetEnvironment();
   if (environment != nullptr) {
     for (size_t i = 0, e = environment->Size(); i < e; ++i) {
-      HInstruction* vreg = environment->GetInstructionAt(i);
-      if (vreg != nullptr) {
-        vreg->RemoveEnvironmentUser(environment, i);
+      HUseListNode<HEnvironment*>* vreg_env_use = environment->GetInstructionEnvUseAt(i);
+      if (vreg_env_use != nullptr) {
+        HInstruction* vreg = environment->GetInstructionAt(i);
+        DCHECK(vreg != nullptr);
+        vreg->RemoveEnvironmentUser(vreg_env_use);
       }
     }
   }
@@ -60,19 +63,22 @@
   }
 }
 
+void HGraph::RemoveBlock(HBasicBlock* block) const {
+  for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
+    block->GetSuccessors().Get(j)->RemovePredecessor(block);
+  }
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    block->RemovePhi(it.Current()->AsPhi());
+  }
+  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    block->RemoveInstruction(it.Current());
+  }
+}
+
 void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const {
   for (size_t i = 0; i < blocks_.Size(); ++i) {
     if (!visited.IsBitSet(i)) {
-      HBasicBlock* block = blocks_.Get(i);
-      for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
-        block->GetSuccessors().Get(j)->RemovePredecessor(block);
-      }
-      for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
-        block->RemovePhi(it.Current()->AsPhi());
-      }
-      for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-        block->RemoveInstruction(it.Current());
-      }
+      RemoveBlock(blocks_.Get(i));
     }
   }
 }
@@ -167,7 +173,7 @@
   }
 }
 
-void HGraph::TransformToSSA() {
+void HGraph::TransformToSsa() {
   DCHECK(!reverse_post_order_.IsEmpty());
   SsaBuilder ssa_builder(this);
   ssa_builder.BuildSsa();
@@ -286,6 +292,10 @@
   return true;
 }
 
+void HLoopInformation::Add(HBasicBlock* block) {
+  blocks_.SetBit(block->GetBlockId());
+}
+
 void HLoopInformation::PopulateRecursive(HBasicBlock* block) {
   if (blocks_.IsBitSet(block->GetBlockId())) {
     return;
@@ -421,8 +431,8 @@
                    HBasicBlock* block,
                    HInstruction* instruction) {
   DCHECK_EQ(block, instruction->GetBlock());
-  DCHECK(instruction->GetUses() == nullptr);
-  DCHECK(instruction->GetEnvUses() == nullptr);
+  DCHECK(instruction->GetUses().IsEmpty());
+  DCHECK(instruction->GetEnvUses().IsEmpty());
   instruction->SetBlock(nullptr);
   instruction_list->RemoveInstruction(instruction);
 
@@ -437,31 +447,49 @@
   Remove(&phis_, this, phi);
 }
 
-template <typename T>
-static void RemoveFromUseList(T* user,
-                              size_t input_index,
-                              HUseListNode<T>** list) {
-  HUseListNode<T>* previous = nullptr;
-  HUseListNode<T>* current = *list;
-  while (current != nullptr) {
-    if (current->GetUser() == user && current->GetIndex() == input_index) {
-      if (previous == NULL) {
-        *list = current->GetTail();
-      } else {
-        previous->SetTail(current->GetTail());
-      }
+void HEnvironment::CopyFrom(HEnvironment* env) {
+  for (size_t i = 0; i < env->Size(); i++) {
+    HInstruction* instruction = env->GetInstructionAt(i);
+    SetRawEnvAt(i, instruction);
+    if (instruction != nullptr) {
+      instruction->AddEnvUseAt(this, i);
     }
-    previous = current;
-    current = current->GetTail();
   }
 }
 
+template <typename T>
+static void RemoveFromUseList(T user, size_t input_index, HUseList<T>* list) {
+  HUseListNode<T>* current;
+  for (HUseIterator<HInstruction*> use_it(*list); !use_it.Done(); use_it.Advance()) {
+    current = use_it.Current();
+    if (current->GetUser() == user && current->GetIndex() == input_index) {
+      list->Remove(current);
+    }
+  }
+}
+
+HInstruction* HInstruction::GetNextDisregardingMoves() const {
+  HInstruction* next = GetNext();
+  while (next != nullptr && next->IsParallelMove()) {
+    next = next->GetNext();
+  }
+  return next;
+}
+
+HInstruction* HInstruction::GetPreviousDisregardingMoves() const {
+  HInstruction* previous = GetPrevious();
+  while (previous != nullptr && previous->IsParallelMove()) {
+    previous = previous->GetPrevious();
+  }
+  return previous;
+}
+
 void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
   RemoveFromUseList(user, input_index, &uses_);
 }
 
-void HInstruction::RemoveEnvironmentUser(HEnvironment* user, size_t input_index) {
-  RemoveFromUseList(user, input_index, &env_uses_);
+void HInstruction::RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use) {
+  env_uses_.Remove(use);
 }
 
 void HInstructionList::AddInstruction(HInstruction* instruction) {
@@ -553,24 +581,24 @@
 
 void HInstruction::ReplaceWith(HInstruction* other) {
   DCHECK(other != nullptr);
-  for (HUseIterator<HInstruction> it(GetUses()); !it.Done(); it.Advance()) {
-    HUseListNode<HInstruction>* current = it.Current();
+  for (HUseIterator<HInstruction*> it(GetUses()); !it.Done(); it.Advance()) {
+    HUseListNode<HInstruction*>* current = it.Current();
     HInstruction* user = current->GetUser();
     size_t input_index = current->GetIndex();
     user->SetRawInputAt(input_index, other);
     other->AddUseAt(user, input_index);
   }
 
-  for (HUseIterator<HEnvironment> it(GetEnvUses()); !it.Done(); it.Advance()) {
-    HUseListNode<HEnvironment>* current = it.Current();
+  for (HUseIterator<HEnvironment*> it(GetEnvUses()); !it.Done(); it.Advance()) {
+    HUseListNode<HEnvironment*>* current = it.Current();
     HEnvironment* user = current->GetUser();
     size_t input_index = current->GetIndex();
     user->SetRawEnvAt(input_index, other);
     other->AddEnvUseAt(user, input_index);
   }
 
-  uses_ = nullptr;
-  env_uses_ = nullptr;
+  uses_.Clear();
+  env_uses_.Clear();
 }
 
 void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
@@ -643,17 +671,18 @@
   } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
     int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
                              GetRight()->AsLongConstant()->GetValue());
-    return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+    if (GetResultType() == Primitive::kPrimLong) {
+      return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+    } else {
+      DCHECK_EQ(GetResultType(), Primitive::kPrimInt);
+      return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+    }
   }
   return nullptr;
 }
 
 bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const {
-  HInstruction* previous = if_->GetPrevious();
-  while (previous != nullptr && previous->IsParallelMove()) {
-    previous = previous->GetPrevious();
-  }
-  return previous == this;
+  return this == if_->GetPreviousDisregardingMoves();
 }
 
 bool HInstruction::Equals(HInstruction* other) const {
@@ -682,4 +711,283 @@
   return os;
 }
 
+void HInstruction::MoveBefore(HInstruction* cursor) {
+  next_->previous_ = previous_;
+  if (previous_ != nullptr) {
+    previous_->next_ = next_;
+  }
+  if (block_->instructions_.first_instruction_ == this) {
+    block_->instructions_.first_instruction_ = next_;
+  }
+  DCHECK_NE(block_->instructions_.last_instruction_, this);
+
+  previous_ = cursor->previous_;
+  if (previous_ != nullptr) {
+    previous_->next_ = this;
+  }
+  next_ = cursor;
+  cursor->previous_ = this;
+  block_ = cursor->block_;
+
+  if (block_->instructions_.first_instruction_ == cursor) {
+    block_->instructions_.first_instruction_ = this;
+  }
+}
+
+HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+  DCHECK(!cursor->IsControlFlow());
+  DCHECK_NE(instructions_.last_instruction_, cursor);
+  DCHECK_EQ(cursor->GetBlock(), this);
+
+  HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
+  new_block->instructions_.first_instruction_ = cursor->GetNext();
+  new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+  cursor->next_->previous_ = nullptr;
+  cursor->next_ = nullptr;
+  instructions_.last_instruction_ = cursor;
+
+  new_block->instructions_.SetBlockOfInstructions(new_block);
+  for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) {
+    HBasicBlock* successor = GetSuccessors().Get(i);
+    new_block->successors_.Add(successor);
+    successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block);
+  }
+  successors_.Reset();
+
+  for (size_t i = 0, e = GetDominatedBlocks().Size(); i < e; ++i) {
+    HBasicBlock* dominated = GetDominatedBlocks().Get(i);
+    dominated->dominator_ = new_block;
+    new_block->dominated_blocks_.Add(dominated);
+  }
+  dominated_blocks_.Reset();
+  return new_block;
+}
+
+void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
+  for (HInstruction* current = first_instruction_;
+       current != nullptr;
+       current = current->GetNext()) {
+    current->SetBlock(block);
+  }
+}
+
+void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& instruction_list) {
+  DCHECK(Contains(cursor));
+  if (!instruction_list.IsEmpty()) {
+    if (cursor == last_instruction_) {
+      last_instruction_ = instruction_list.last_instruction_;
+    } else {
+      cursor->next_->previous_ = instruction_list.last_instruction_;
+    }
+    instruction_list.last_instruction_->next_ = cursor->next_;
+    cursor->next_ = instruction_list.first_instruction_;
+    instruction_list.first_instruction_->previous_ = cursor;
+  }
+}
+
+void HInstructionList::Add(const HInstructionList& instruction_list) {
+  DCHECK(!IsEmpty());
+  AddAfter(last_instruction_, instruction_list);
+}
+
+void HBasicBlock::MergeWith(HBasicBlock* other) {
+  DCHECK(successors_.IsEmpty()) << "Unimplemented block merge scenario";
+  DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented block merge scenario";
+  DCHECK(other->GetDominator()->IsEntryBlock() && other->GetGraph() != graph_)
+      << "Unimplemented block merge scenario";
+  DCHECK(other->GetPhis().IsEmpty());
+
+  successors_.Reset();
+  dominated_blocks_.Reset();
+  instructions_.Add(other->GetInstructions());
+  other->GetInstructions().SetBlockOfInstructions(this);
+
+  while (!other->GetSuccessors().IsEmpty()) {
+    HBasicBlock* successor = other->GetSuccessors().Get(0);
+    successor->ReplacePredecessor(other, this);
+  }
+
+  for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) {
+    HBasicBlock* dominated = other->GetDominatedBlocks().Get(i);
+    dominated_blocks_.Add(dominated);
+    dominated->SetDominator(this);
+  }
+  other->dominated_blocks_.Reset();
+  other->dominator_ = nullptr;
+  other->graph_ = nullptr;
+}
+
+void HBasicBlock::ReplaceWith(HBasicBlock* other) {
+  while (!GetPredecessors().IsEmpty()) {
+    HBasicBlock* predecessor = GetPredecessors().Get(0);
+    predecessor->ReplaceSuccessor(this, other);
+  }
+  while (!GetSuccessors().IsEmpty()) {
+    HBasicBlock* successor = GetSuccessors().Get(0);
+    successor->ReplacePredecessor(this, other);
+  }
+  for (size_t i = 0; i < dominated_blocks_.Size(); ++i) {
+    other->AddDominatedBlock(dominated_blocks_.Get(i));
+  }
+  GetDominator()->ReplaceDominatedBlock(this, other);
+  other->SetDominator(GetDominator());
+  dominator_ = nullptr;
+  graph_ = nullptr;
+}
+
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+static void MakeRoomFor(GrowableArray<HBasicBlock*>* blocks,
+                        size_t number_of_new_blocks,
+                        size_t at) {
+  size_t old_size = blocks->Size();
+  size_t new_size = old_size + number_of_new_blocks;
+  blocks->SetSize(new_size);
+  for (size_t i = old_size - 1, j = new_size - 1; i > at; --i, --j) {
+    blocks->Put(j, blocks->Get(i));
+  }
+}
+
+void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+  // Walk over the entry block and:
+  // - Move constants from the entry block to the outer_graph's entry block,
+  // - Replace HParameterValue instructions with their real value.
+  // - Remove suspend checks, that hold an environment.
+  int parameter_index = 0;
+  for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
+    HInstruction* current = it.Current();
+    if (current->IsConstant()) {
+      current->MoveBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
+    } else if (current->IsParameterValue()) {
+      current->ReplaceWith(invoke->InputAt(parameter_index++));
+    } else {
+      DCHECK(current->IsGoto() || current->IsSuspendCheck());
+      entry_block_->RemoveInstruction(current);
+    }
+  }
+
+  if (GetBlocks().Size() == 3) {
+    // Simple case of an entry block, a body block, and an exit block.
+    // Put the body block's instruction into `invoke`'s block.
+    HBasicBlock* body = GetBlocks().Get(1);
+    DCHECK(GetBlocks().Get(0)->IsEntryBlock());
+    DCHECK(GetBlocks().Get(2)->IsExitBlock());
+    DCHECK(!body->IsExitBlock());
+    HInstruction* last = body->GetLastInstruction();
+
+    invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+    body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
+
+    // Replace the invoke with the return value of the inlined graph.
+    if (last->IsReturn()) {
+      invoke->ReplaceWith(last->InputAt(0));
+    } else {
+      DCHECK(last->IsReturnVoid());
+    }
+
+    invoke->GetBlock()->RemoveInstruction(last);
+  } else {
+    // Need to inline multiple blocks. We split `invoke`'s block
+    // into two blocks, merge the first block of the inlined graph into
+    // the first half, and replace the exit block of the inlined graph
+    // with the second half.
+    ArenaAllocator* allocator = outer_graph->GetArena();
+    HBasicBlock* at = invoke->GetBlock();
+    HBasicBlock* to = at->SplitAfter(invoke);
+
+    HBasicBlock* first = entry_block_->GetSuccessors().Get(0);
+    DCHECK(!first->IsInLoop());
+    at->MergeWith(first);
+    exit_block_->ReplaceWith(to);
+
+    // Update all predecessors of the exit block (now the `to` block)
+    // to not `HReturn` but `HGoto` instead. Also collect the return
+    // values if any, and potentially make it a phi if there are multiple
+    // predecessors.
+    HInstruction* return_value = nullptr;
+    for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) {
+      HBasicBlock* predecessor = to->GetPredecessors().Get(i);
+      HInstruction* last = predecessor->GetLastInstruction();
+      if (!last->IsReturnVoid()) {
+        if (return_value != nullptr) {
+          if (!return_value->IsPhi()) {
+            HPhi* phi = new (allocator) HPhi(allocator, kNoRegNumber, 0, invoke->GetType());
+            to->AddPhi(phi);
+            phi->AddInput(return_value);
+            return_value = phi;
+          }
+          return_value->AsPhi()->AddInput(last->InputAt(0));
+        } else {
+          return_value = last->InputAt(0);
+        }
+      }
+      predecessor->AddInstruction(new (allocator) HGoto());
+      predecessor->RemoveInstruction(last);
+    }
+
+    if (return_value != nullptr) {
+      invoke->ReplaceWith(return_value);
+    }
+
+    // Update the meta information surrounding blocks:
+    // (1) the graph they are now in,
+    // (2) the reverse post order of that graph,
+    // (3) the potential loop information they are now in.
+
+    // We don't add the entry block, the exit block, and the first block, which
+    // has been merged with `at`.
+    static constexpr int kNumberOfSkippedBlocksInCallee = 3;
+
+    // We add the `to` block.
+    static constexpr int kNumberOfNewBlocksInCaller = 1;
+    size_t blocks_added = (reverse_post_order_.Size() - kNumberOfSkippedBlocksInCallee)
+        + kNumberOfNewBlocksInCaller;
+
+    // Find the location of `at` in the outer graph's reverse post order. The new
+    // blocks will be added after it.
+    size_t index_of_at = 0;
+    while (outer_graph->reverse_post_order_.Get(index_of_at) != at) {
+      index_of_at++;
+    }
+    MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
+
+    // Do a reverse post order of the blocks in the callee and do (1), (2),
+    // and (3) to the blocks that apply.
+    HLoopInformation* info = at->GetLoopInformation();
+    for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+      HBasicBlock* current = it.Current();
+      if (current != exit_block_ && current != entry_block_ && current != first) {
+        DCHECK(!current->IsInLoop());
+        DCHECK(current->GetGraph() == this);
+        current->SetGraph(outer_graph);
+        outer_graph->AddBlock(current);
+        outer_graph->reverse_post_order_.Put(++index_of_at, current);
+        if (info != nullptr) {
+          info->Add(current);
+          current->SetLoopInformation(info);
+        }
+      }
+    }
+
+    // Do (1), (2), and (3) to `to`.
+    to->SetGraph(outer_graph);
+    outer_graph->AddBlock(to);
+    outer_graph->reverse_post_order_.Put(++index_of_at, to);
+    if (info != nullptr) {
+      info->Add(to);
+      to->SetLoopInformation(info);
+      if (info->IsBackEdge(at)) {
+        // Only `at` can become a back edge, as the inlined blocks
+        // are predecessors of `at`.
+        DCHECK_EQ(1u, info->NumberOfBackEdges());
+        info->ClearBackEdges();
+        info->AddBackEdge(to);
+      }
+    }
+  }
+
+  // Finally remove the invoke from the caller.
+  invoke->GetBlock()->RemoveInstruction(invoke);
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3908a61..fd88e42 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,8 @@
 #ifndef ART_COMPILER_OPTIMIZING_NODES_H_
 #define ART_COMPILER_OPTIMIZING_NODES_H_
 
+#include "entrypoints/quick/quick_entrypoints_enum.h"
+#include "invoke_type.h"
 #include "locations.h"
 #include "offsets.h"
 #include "primitive.h"
@@ -30,6 +32,7 @@
 class HEnvironment;
 class HInstruction;
 class HIntConstant;
+class HInvoke;
 class HGraphVisitor;
 class HPhi;
 class HSuspendCheck;
@@ -70,11 +73,22 @@
   bool FoundBefore(const HInstruction* instruction1,
                    const HInstruction* instruction2) const;
 
+  bool IsEmpty() const { return first_instruction_ == nullptr; }
+  void Clear() { first_instruction_ = last_instruction_ = nullptr; }
+
+  // Update the block of all instructions to be `block`.
+  void SetBlockOfInstructions(HBasicBlock* block) const;
+
+  void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+  void Add(const HInstructionList& instruction_list);
+
  private:
   HInstruction* first_instruction_;
   HInstruction* last_instruction_;
 
   friend class HBasicBlock;
+  friend class HGraph;
+  friend class HInstruction;
   friend class HInstructionIterator;
   friend class HBackwardInstructionIterator;
 
@@ -84,7 +98,7 @@
 // Control-flow graph of a method. Contains a list of basic blocks.
 class HGraph : public ArenaObject<kArenaAllocMisc> {
  public:
-  explicit HGraph(ArenaAllocator* arena)
+  HGraph(ArenaAllocator* arena, int start_instruction_id = 0)
       : arena_(arena),
         blocks_(arena, kDefaultNumberOfBlocks),
         reverse_post_order_(arena, kDefaultNumberOfBlocks),
@@ -94,7 +108,7 @@
         number_of_vregs_(0),
         number_of_in_vregs_(0),
         temporaries_vreg_slots_(0),
-        current_instruction_id_(0) {}
+        current_instruction_id_(start_instruction_id) {}
 
   ArenaAllocator* GetArena() const { return arena_; }
   const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
@@ -108,8 +122,16 @@
 
   void AddBlock(HBasicBlock* block);
 
+  // Try building the SSA form of this graph, with dominance computation and loop
+  // recognition. Returns whether it was successful in doing all these steps.
+  bool TryBuildingSsa() {
+    BuildDominatorTree();
+    TransformToSsa();
+    return AnalyzeNaturalLoops();
+  }
+
   void BuildDominatorTree();
-  void TransformToSSA();
+  void TransformToSsa();
   void SimplifyCFG();
 
   // Analyze all natural loops in this graph. Returns false if one
@@ -117,19 +139,31 @@
   // back edge.
   bool AnalyzeNaturalLoops() const;
 
+  // Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
+  void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+
   void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
   void SimplifyLoop(HBasicBlock* header);
 
-  int GetNextInstructionId() {
+  int32_t GetNextInstructionId() {
+    DCHECK_NE(current_instruction_id_, INT32_MAX);
     return current_instruction_id_++;
   }
 
+  int32_t GetCurrentInstructionId() const {
+    return current_instruction_id_;
+  }
+
+  void SetCurrentInstructionId(int32_t id) {
+    current_instruction_id_ = id;
+  }
+
   uint16_t GetMaximumNumberOfOutVRegs() const {
     return maximum_number_of_out_vregs_;
   }
 
-  void UpdateMaximumNumberOfOutVRegs(uint16_t new_value) {
-    maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_);
+  void SetMaximumNumberOfOutVRegs(uint16_t new_value) {
+    maximum_number_of_out_vregs_ = new_value;
   }
 
   void UpdateTemporariesVRegSlots(size_t slots) {
@@ -152,10 +186,6 @@
     number_of_in_vregs_ = value;
   }
 
-  uint16_t GetNumberOfInVRegs() const {
-    return number_of_in_vregs_;
-  }
-
   uint16_t GetNumberOfLocalVRegs() const {
     return number_of_vregs_ - number_of_in_vregs_;
   }
@@ -175,6 +205,7 @@
                               ArenaBitVector* visiting);
   void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
   void RemoveDeadBlocks(const ArenaBitVector& visited) const;
+  void RemoveBlock(HBasicBlock* block) const;
 
   ArenaAllocator* const arena_;
 
@@ -200,8 +231,9 @@
   size_t temporaries_vreg_slots_;
 
   // The current id to assign to a newly added instruction. See HInstruction.id_.
-  int current_instruction_id_;
+  int32_t current_instruction_id_;
 
+  ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
   DISALLOW_COPY_AND_ASSIGN(HGraph);
 };
 
@@ -218,6 +250,10 @@
     return header_;
   }
 
+  void SetHeader(HBasicBlock* block) {
+    header_ = block;
+  }
+
   HSuspendCheck* GetSuspendCheck() const { return suspend_check_; }
   void SetSuspendCheck(HSuspendCheck* check) { suspend_check_ = check; }
   bool HasSuspendCheck() const { return suspend_check_ != nullptr; }
@@ -265,6 +301,8 @@
 
   const ArenaBitVector& GetBlocks() const { return blocks_; }
 
+  void Add(HBasicBlock* block);
+
  private:
   // Internal recursive implementation of `Populate`.
   void PopulateRecursive(HBasicBlock* block);
@@ -328,6 +366,7 @@
   }
 
   HGraph* GetGraph() const { return graph_; }
+  void SetGraph(HGraph* graph) { graph_ = graph; }
 
   int GetBlockId() const { return block_id_; }
   void SetBlockId(int id) { block_id_ = id; }
@@ -335,6 +374,16 @@
   HBasicBlock* GetDominator() const { return dominator_; }
   void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
   void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
+  void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) {
+    for (size_t i = 0, e = dominated_blocks_.Size(); i < e; ++i) {
+      if (dominated_blocks_.Get(i) == existing) {
+        dominated_blocks_.Put(i, new_block);
+        return;
+      }
+    }
+    LOG(FATAL) << "Unreachable";
+    UNREACHABLE();
+  }
 
   int NumberOfBackEdges() const {
     return loop_information_ == nullptr
@@ -361,10 +410,22 @@
     successors_.Put(successor_index, new_block);
   }
 
+  void ReplacePredecessor(HBasicBlock* existing, HBasicBlock* new_block) {
+    size_t predecessor_index = GetPredecessorIndexOf(existing);
+    DCHECK_NE(predecessor_index, static_cast<size_t>(-1));
+    existing->RemoveSuccessor(this);
+    new_block->successors_.Add(this);
+    predecessors_.Put(predecessor_index, new_block);
+  }
+
   void RemovePredecessor(HBasicBlock* block) {
     predecessors_.Delete(block);
   }
 
+  void RemoveSuccessor(HBasicBlock* block) {
+    successors_.Delete(block);
+  }
+
   void ClearAllPredecessors() {
     predecessors_.Reset();
   }
@@ -399,6 +460,26 @@
     return -1;
   }
 
+  // Split the block into two blocks just after `cursor`. Returns the newly
+  // created block. Note that this method just updates raw block information,
+  // like predecessors, successors, dominators, and instruction list. It does not
+  // update the graph, reverse post order, loop information, nor make sure the
+  // blocks are consistent (for example ending with a control flow instruction).
+  HBasicBlock* SplitAfter(HInstruction* cursor);
+
+  // Merge `other` at the end of `this`. Successors and dominated blocks of
+  // `other` are changed to be successors and dominated blocks of `this`. Note
+  // that this method does not update the graph, reverse post order, loop
+  // information, nor make sure the blocks are consistent (for example ending
+  // with a control flow instruction).
+  void MergeWith(HBasicBlock* other);
+
+  // Replace `this` with `other`. Predecessors, successors, and dominated blocks
+  // of `this` are moved to `other`.
+  // Note that this method does not update the graph, reverse post order, loop
+  // information, nor make sure the blocks are consistent (for example ending
+  void ReplaceWith(HBasicBlock* other);
+
   void AddInstruction(HInstruction* instruction);
   void RemoveInstruction(HInstruction* instruction);
   void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
@@ -423,8 +504,9 @@
     return loop_information_;
   }
 
-  // Set the loop_information_ on this block. This method overrides the current
+  // Set the loop_information_ on this block. Overrides the current
   // loop_information if it is an outer loop of the passed loop information.
+  // Note that this method is called while creating the loop information.
   void SetInLoop(HLoopInformation* info) {
     if (IsLoopHeader()) {
       // Nothing to do. This just means `info` is an outer loop.
@@ -442,6 +524,11 @@
     }
   }
 
+  // Raw update of the loop information.
+  void SetLoopInformation(HLoopInformation* info) {
+    loop_information_ = info;
+  }
+
   bool IsInLoop() const { return loop_information_ != nullptr; }
 
   // Returns wheter this block dominates the blocked passed as parameter.
@@ -459,7 +546,7 @@
   void SetIsCatchBlock() { is_catch_block_ = true; }
 
  private:
-  HGraph* const graph_;
+  HGraph* graph_;
   GrowableArray<HBasicBlock*> predecessors_;
   GrowableArray<HBasicBlock*> successors_;
   HInstructionList instructions_;
@@ -474,6 +561,9 @@
   size_t lifetime_end_;
   bool is_catch_block_;
 
+  friend class HGraph;
+  friend class HInstruction;
+
   DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
 };
 
@@ -503,7 +593,7 @@
   M(InstanceOf, Instruction)                                            \
   M(IntConstant, Constant)                                              \
   M(InvokeInterface, Invoke)                                            \
-  M(InvokeStatic, Invoke)                                               \
+  M(InvokeStaticOrDirect, Invoke)                                       \
   M(InvokeVirtual, Invoke)                                              \
   M(LessThan, Condition)                                                \
   M(LessThanOrEqual, Condition)                                         \
@@ -562,26 +652,104 @@
   }                                                                     \
   virtual void Accept(HGraphVisitor* visitor)
 
+template <typename T> class HUseList;
+
 template <typename T>
 class HUseListNode : public ArenaObject<kArenaAllocMisc> {
  public:
-  HUseListNode(T* user, size_t index, HUseListNode* tail)
-      : user_(user), index_(index), tail_(tail) {}
-
-  HUseListNode* GetTail() const { return tail_; }
-  T* GetUser() const { return user_; }
+  HUseListNode* GetPrevious() const { return prev_; }
+  HUseListNode* GetNext() const { return next_; }
+  T GetUser() const { return user_; }
   size_t GetIndex() const { return index_; }
 
-  void SetTail(HUseListNode<T>* node) { tail_ = node; }
-
  private:
-  T* const user_;
+  HUseListNode(T user, size_t index)
+      : user_(user), index_(index), prev_(nullptr), next_(nullptr) {}
+
+  T const user_;
   const size_t index_;
-  HUseListNode<T>* tail_;
+  HUseListNode<T>* prev_;
+  HUseListNode<T>* next_;
+
+  friend class HUseList<T>;
 
   DISALLOW_COPY_AND_ASSIGN(HUseListNode);
 };
 
+template <typename T>
+class HUseList : public ValueObject {
+ public:
+  HUseList() : first_(nullptr) {}
+
+  void Clear() {
+    first_ = nullptr;
+  }
+
+  // Adds a new entry at the beginning of the use list and returns
+  // the newly created node.
+  HUseListNode<T>* AddUse(T user, size_t index, ArenaAllocator* arena) {
+    HUseListNode<T>* new_node = new (arena) HUseListNode<T>(user, index);
+    if (IsEmpty()) {
+      first_ = new_node;
+    } else {
+      first_->prev_ = new_node;
+      new_node->next_ = first_;
+      first_ = new_node;
+    }
+    return new_node;
+  }
+
+  HUseListNode<T>* GetFirst() const {
+    return first_;
+  }
+
+  void Remove(HUseListNode<T>* node) {
+    if (node->prev_ != nullptr) {
+      node->prev_->next_ = node->next_;
+    }
+    if (node->next_ != nullptr) {
+      node->next_->prev_ = node->prev_;
+    }
+    if (node == first_) {
+      first_ = node->next_;
+    }
+  }
+
+  bool IsEmpty() const {
+    return first_ == nullptr;
+  }
+
+  bool HasOnlyOneUse() const {
+    return first_ != nullptr && first_->next_ == nullptr;
+  }
+
+ private:
+  HUseListNode<T>* first_;
+};
+
+template<typename T>
+class HUseIterator : public ValueObject {
+ public:
+  explicit HUseIterator(const HUseList<T>& uses) : current_(uses.GetFirst()) {}
+
+  bool Done() const { return current_ == nullptr; }
+
+  void Advance() {
+    DCHECK(!Done());
+    current_ = current_->GetNext();
+  }
+
+  HUseListNode<T>* Current() const {
+    DCHECK(!Done());
+    return current_;
+  }
+
+ private:
+  HUseListNode<T>* current_;
+
+  friend class HValue;
+};
+
 // Represents the side effects an instruction may have.
 class SideEffects : public ValueObject {
  public:
@@ -645,6 +813,57 @@
   size_t flags_;
 };
 
+// A HEnvironment object contains the values of virtual registers at a given location.
+class HEnvironment : public ArenaObject<kArenaAllocMisc> {
+ public:
+  HEnvironment(ArenaAllocator* arena, size_t number_of_vregs)
+     : vregs_(arena, number_of_vregs) {
+    vregs_.SetSize(number_of_vregs);
+    for (size_t i = 0; i < number_of_vregs; i++) {
+      vregs_.Put(i, VRegInfo(nullptr, nullptr));
+    }
+  }
+
+  void CopyFrom(HEnvironment* env);
+
+  void SetRawEnvAt(size_t index, HInstruction* instruction) {
+    vregs_.Put(index, VRegInfo(instruction, nullptr));
+  }
+
+  // Record instructions' use entries of this environment for constant-time removal.
+  void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) {
+    DCHECK(env_use->GetUser() == this);
+    size_t index = env_use->GetIndex();
+    VRegInfo info = vregs_.Get(index);
+    DCHECK(info.vreg_ != nullptr);
+    DCHECK(info.node_ == nullptr);
+    vregs_.Put(index, VRegInfo(info.vreg_, env_use));
+  }
+
+  HInstruction* GetInstructionAt(size_t index) const {
+    return vregs_.Get(index).vreg_;
+  }
+
+  HUseListNode<HEnvironment*>* GetInstructionEnvUseAt(size_t index) const {
+    return vregs_.Get(index).node_;
+  }
+
+  size_t Size() const { return vregs_.Size(); }
+
+ private:
+  struct VRegInfo {
+    HInstruction* vreg_;
+    HUseListNode<HEnvironment*>* node_;
+
+    VRegInfo(HInstruction* instruction, HUseListNode<HEnvironment*>* env_use)
+        : vreg_(instruction), node_(env_use) {}
+  };
+
+  GrowableArray<VRegInfo> vregs_;
+
+  DISALLOW_COPY_AND_ASSIGN(HEnvironment);
+};
+
 class HInstruction : public ArenaObject<kArenaAllocMisc> {
  public:
   explicit HInstruction(SideEffects side_effects)
@@ -653,8 +872,6 @@
         block_(nullptr),
         id_(-1),
         ssa_index_(-1),
-        uses_(nullptr),
-        env_uses_(nullptr),
         environment_(nullptr),
         locations_(nullptr),
         live_interval_(nullptr),
@@ -672,6 +889,9 @@
   HInstruction* GetNext() const { return next_; }
   HInstruction* GetPrevious() const { return previous_; }
 
+  HInstruction* GetNextDisregardingMoves() const;
+  HInstruction* GetPreviousDisregardingMoves() const;
+
   HBasicBlock* GetBlock() const { return block_; }
   void SetBlock(HBasicBlock* block) { block_ = block; }
   bool IsInBlock() const { return block_ != nullptr; }
@@ -692,35 +912,31 @@
   virtual bool CanThrow() const { return false; }
   bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
 
+  // Does not apply for all instructions, but having this at top level greatly
+  // simplifies the null check elimination.
+  virtual bool CanBeNull() const { return true; }
+
+  virtual bool CanDoImplicitNullCheck() const { return false; }
+
   void AddUseAt(HInstruction* user, size_t index) {
-    uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_);
+    uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
   }
 
   void AddEnvUseAt(HEnvironment* user, size_t index) {
     DCHECK(user != nullptr);
-    env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HEnvironment>(
-        user, index, env_uses_);
+    HUseListNode<HEnvironment*>* env_use =
+        env_uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
+    user->RecordEnvUse(env_use);
   }
 
   void RemoveUser(HInstruction* user, size_t index);
-  void RemoveEnvironmentUser(HEnvironment* user, size_t index);
+  void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use);
 
-  HUseListNode<HInstruction>* GetUses() const { return uses_; }
-  HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; }
+  const HUseList<HInstruction*>& GetUses() { return uses_; }
+  const HUseList<HEnvironment*>& GetEnvUses() { return env_uses_; }
 
-  bool HasUses() const { return uses_ != nullptr || env_uses_ != nullptr; }
-  bool HasEnvironmentUses() const { return env_uses_ != nullptr; }
-
-  size_t NumberOfUses() const {
-    // TODO: Optimize this method if it is used outside of the HGraphVisualizer.
-    size_t result = 0;
-    HUseListNode<HInstruction>* current = uses_;
-    while (current != nullptr) {
-      current = current->GetTail();
-      ++result;
-    }
-    return result;
-  }
+  bool HasUses() const { return !uses_.IsEmpty() || !env_uses_.IsEmpty(); }
+  bool HasEnvironmentUses() const { return !env_uses_.IsEmpty(); }
 
   // Does this instruction strictly dominate `other_instruction`?
   // Returns false if this instruction and `other_instruction` are the same.
@@ -748,9 +964,8 @@
   void ReplaceWith(HInstruction* instruction);
   void ReplaceInput(HInstruction* replacement, size_t index);
 
-  bool HasOnlyOneUse() const {
-    return uses_ != nullptr && uses_->GetTail() == nullptr;
-  }
+  // Move `this` instruction before `cursor`.
+  void MoveBefore(HInstruction* cursor);
 
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
   bool Is##type() const { return (As##type() != nullptr); }                    \
@@ -800,6 +1015,18 @@
   void SetLiveInterval(LiveInterval* interval) { live_interval_ = interval; }
   bool HasLiveInterval() const { return live_interval_ != nullptr; }
 
+  bool IsSuspendCheckEntry() const { return IsSuspendCheck() && GetBlock()->IsEntryBlock(); }
+
+  // Returns whether the code generation of the instruction will require to have access
+  // to the current method. Such instructions are:
+  // (1): Instructions that require an environment, as calling the runtime requires
+  //      to walk the stack and have the current method stored at a specific stack address.
+  // (2): Object literals like classes and strings, that are loaded from the dex cache
+  //      fields of the current method.
+  bool NeedsCurrentMethod() const {
+    return NeedsEnvironment() || IsLoadClass() || IsLoadString();
+  }
+
  private:
   HInstruction* previous_;
   HInstruction* next_;
@@ -814,10 +1041,10 @@
   int ssa_index_;
 
   // List of instructions that have this instruction as input.
-  HUseListNode<HInstruction>* uses_;
+  HUseList<HInstruction*> uses_;
 
   // List of environments that contain this instruction.
-  HUseListNode<HEnvironment>* env_uses_;
+  HUseList<HEnvironment*> env_uses_;
 
   // The environment associated with this instruction. Not null if the instruction
   // might jump out of the method.
@@ -836,75 +1063,13 @@
   const SideEffects side_effects_;
 
   friend class HBasicBlock;
+  friend class HGraph;
   friend class HInstructionList;
 
   DISALLOW_COPY_AND_ASSIGN(HInstruction);
 };
 std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
 
-template<typename T>
-class HUseIterator : public ValueObject {
- public:
-  explicit HUseIterator(HUseListNode<T>* uses) : current_(uses) {}
-
-  bool Done() const { return current_ == nullptr; }
-
-  void Advance() {
-    DCHECK(!Done());
-    current_ = current_->GetTail();
-  }
-
-  HUseListNode<T>* Current() const {
-    DCHECK(!Done());
-    return current_;
-  }
-
- private:
-  HUseListNode<T>* current_;
-
-  friend class HValue;
-};
-
-// A HEnvironment object contains the values of virtual registers at a given location.
-class HEnvironment : public ArenaObject<kArenaAllocMisc> {
- public:
-  HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) : vregs_(arena, number_of_vregs) {
-    vregs_.SetSize(number_of_vregs);
-    for (size_t i = 0; i < number_of_vregs; i++) {
-      vregs_.Put(i, nullptr);
-    }
-  }
-
-  void Populate(const GrowableArray<HInstruction*>& env) {
-    for (size_t i = 0; i < env.Size(); i++) {
-      HInstruction* instruction = env.Get(i);
-      vregs_.Put(i, instruction);
-      if (instruction != nullptr) {
-        instruction->AddEnvUseAt(this, i);
-      }
-    }
-  }
-
-  void SetRawEnvAt(size_t index, HInstruction* instruction) {
-    vregs_.Put(index, instruction);
-  }
-
-  HInstruction* GetInstructionAt(size_t index) const {
-    return vregs_.Get(index);
-  }
-
-  GrowableArray<HInstruction*>* GetVRegs() {
-    return &vregs_;
-  }
-
-  size_t Size() const { return vregs_.Size(); }
-
- private:
-  GrowableArray<HInstruction*> vregs_;
-
-  DISALLOW_COPY_AND_ASSIGN(HEnvironment);
-};
-
 class HInputIterator : public ValueObject {
  public:
   explicit HInputIterator(HInstruction* instruction) : instruction_(instruction), index_(0) {}
@@ -1552,25 +1717,24 @@
   DISALLOW_COPY_AND_ASSIGN(HLongConstant);
 };
 
+enum class Intrinsics {
+#define OPTIMIZING_INTRINSICS(Name, IsStatic) k ## Name,
+#include "intrinsics_list.h"
+  kNone,
+  INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+};
+std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic);
+
 class HInvoke : public HInstruction {
  public:
-  HInvoke(ArenaAllocator* arena,
-          uint32_t number_of_arguments,
-          Primitive::Type return_type,
-          uint32_t dex_pc)
-    : HInstruction(SideEffects::All()),
-      inputs_(arena, number_of_arguments),
-      return_type_(return_type),
-      dex_pc_(dex_pc) {
-    inputs_.SetSize(number_of_arguments);
-  }
-
   virtual size_t InputCount() const { return inputs_.Size(); }
   virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
 
   // Runtime needs to walk the stack, so Dex -> Dex calls need to
   // know their environment.
-  virtual bool NeedsEnvironment() const { return true; }
+  bool NeedsEnvironment() const OVERRIDE { return true; }
 
   void SetArgumentAt(size_t index, HInstruction* argument) {
     SetRawInputAt(index, argument);
@@ -1584,35 +1748,72 @@
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
+  uint32_t GetDexMethodIndex() const { return dex_method_index_; }
+
+  Intrinsics GetIntrinsic() {
+    return intrinsic_;
+  }
+
+  void SetIntrinsic(Intrinsics intrinsic) {
+    intrinsic_ = intrinsic;
+  }
+
   DECLARE_INSTRUCTION(Invoke);
 
  protected:
+  HInvoke(ArenaAllocator* arena,
+          uint32_t number_of_arguments,
+          Primitive::Type return_type,
+          uint32_t dex_pc,
+          uint32_t dex_method_index)
+    : HInstruction(SideEffects::All()),
+      inputs_(arena, number_of_arguments),
+      return_type_(return_type),
+      dex_pc_(dex_pc),
+      dex_method_index_(dex_method_index),
+      intrinsic_(Intrinsics::kNone) {
+    inputs_.SetSize(number_of_arguments);
+  }
+
   GrowableArray<HInstruction*> inputs_;
   const Primitive::Type return_type_;
   const uint32_t dex_pc_;
+  const uint32_t dex_method_index_;
+  Intrinsics intrinsic_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(HInvoke);
 };
 
-class HInvokeStatic : public HInvoke {
+class HInvokeStaticOrDirect : public HInvoke {
  public:
-  HInvokeStatic(ArenaAllocator* arena,
-                uint32_t number_of_arguments,
-                Primitive::Type return_type,
-                uint32_t dex_pc,
-                uint32_t index_in_dex_cache)
-      : HInvoke(arena, number_of_arguments, return_type, dex_pc),
-        index_in_dex_cache_(index_in_dex_cache) {}
+  HInvokeStaticOrDirect(ArenaAllocator* arena,
+                        uint32_t number_of_arguments,
+                        Primitive::Type return_type,
+                        uint32_t dex_pc,
+                        uint32_t dex_method_index,
+                        bool is_recursive,
+                        InvokeType invoke_type)
+      : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
+        invoke_type_(invoke_type),
+        is_recursive_(is_recursive) {}
 
-  uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; }
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    // We access the method via the dex cache so we can't do an implicit null check.
+    // TODO: for intrinsics we can generate implicit null checks.
+    return false;
+  }
 
-  DECLARE_INSTRUCTION(InvokeStatic);
+  InvokeType GetInvokeType() const { return invoke_type_; }
+  bool IsRecursive() const { return is_recursive_; }
+
+  DECLARE_INSTRUCTION(InvokeStaticOrDirect);
 
  private:
-  const uint32_t index_in_dex_cache_;
+  const InvokeType invoke_type_;
+  const bool is_recursive_;
 
-  DISALLOW_COPY_AND_ASSIGN(HInvokeStatic);
+  DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect);
 };
 
 class HInvokeVirtual : public HInvoke {
@@ -1621,10 +1822,16 @@
                  uint32_t number_of_arguments,
                  Primitive::Type return_type,
                  uint32_t dex_pc,
+                 uint32_t dex_method_index,
                  uint32_t vtable_index)
-      : HInvoke(arena, number_of_arguments, return_type, dex_pc),
+      : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
         vtable_index_(vtable_index) {}
 
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    // TODO: Add implicit null checks in intrinsics.
+    return !GetLocations()->Intrinsified();
+  }
+
   uint32_t GetVTableIndex() const { return vtable_index_; }
 
   DECLARE_INSTRUCTION(InvokeVirtual);
@@ -1643,17 +1850,20 @@
                    uint32_t dex_pc,
                    uint32_t dex_method_index,
                    uint32_t imt_index)
-      : HInvoke(arena, number_of_arguments, return_type, dex_pc),
-        dex_method_index_(dex_method_index),
+      : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
         imt_index_(imt_index) {}
 
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    // TODO: Add implicit null checks in intrinsics.
+    return !GetLocations()->Intrinsified();
+  }
+
   uint32_t GetImtIndex() const { return imt_index_; }
   uint32_t GetDexMethodIndex() const { return dex_method_index_; }
 
   DECLARE_INSTRUCTION(InvokeInterface);
 
  private:
-  const uint32_t dex_method_index_;
   const uint32_t imt_index_;
 
   DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
@@ -1661,10 +1871,11 @@
 
 class HNewInstance : public HExpression<0> {
  public:
-  HNewInstance(uint32_t dex_pc, uint16_t type_index)
+  HNewInstance(uint32_t dex_pc, uint16_t type_index, QuickEntrypointEnum entrypoint)
       : HExpression(Primitive::kPrimNot, SideEffects::None()),
         dex_pc_(dex_pc),
-        type_index_(type_index) {}
+        type_index_(type_index),
+        entrypoint_(entrypoint) {}
 
   uint32_t GetDexPc() const { return dex_pc_; }
   uint16_t GetTypeIndex() const { return type_index_; }
@@ -1677,11 +1888,16 @@
   // TODO: optimize when possible.
   bool CanThrow() const OVERRIDE { return true; }
 
+  bool CanBeNull() const OVERRIDE { return false; }
+
+  QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+
   DECLARE_INSTRUCTION(NewInstance);
 
  private:
   const uint32_t dex_pc_;
   const uint16_t type_index_;
+  const QuickEntrypointEnum entrypoint_;
 
   DISALLOW_COPY_AND_ASSIGN(HNewInstance);
 };
@@ -1702,10 +1918,14 @@
 
 class HNewArray : public HExpression<1> {
  public:
-  HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index)
+  HNewArray(HInstruction* length,
+            uint32_t dex_pc,
+            uint16_t type_index,
+            QuickEntrypointEnum entrypoint)
       : HExpression(Primitive::kPrimNot, SideEffects::None()),
         dex_pc_(dex_pc),
-        type_index_(type_index) {
+        type_index_(type_index),
+        entrypoint_(entrypoint) {
     SetRawInputAt(0, length);
   }
 
@@ -1713,13 +1933,18 @@
   uint16_t GetTypeIndex() const { return type_index_; }
 
   // Calls runtime so needs an environment.
-  virtual bool NeedsEnvironment() const { return true; }
+  bool NeedsEnvironment() const OVERRIDE { return true; }
+
+  bool CanBeNull() const OVERRIDE { return false; }
+
+  QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
 
   DECLARE_INSTRUCTION(NewArray);
 
  private:
   const uint32_t dex_pc_;
   const uint16_t type_index_;
+  const QuickEntrypointEnum entrypoint_;
 
   DISALLOW_COPY_AND_ASSIGN(HNewArray);
 };
@@ -1963,18 +2188,23 @@
 // the calling convention.
 class HParameterValue : public HExpression<0> {
  public:
-  HParameterValue(uint8_t index, Primitive::Type parameter_type)
-      : HExpression(parameter_type, SideEffects::None()), index_(index) {}
+  HParameterValue(uint8_t index, Primitive::Type parameter_type, bool is_this = false)
+      : HExpression(parameter_type, SideEffects::None()), index_(index), is_this_(is_this) {}
 
   uint8_t GetIndex() const { return index_; }
 
+  bool CanBeNull() const OVERRIDE { return !is_this_; }
+
   DECLARE_INSTRUCTION(ParameterValue);
 
  private:
   // The index of this parameter in the parameters list. Must be less
-  // than HGraph::number_of_in_vregs_;
+  // than HGraph::number_of_in_vregs_.
   const uint8_t index_;
 
+  // Whether or not the parameter value corresponds to 'this' argument.
+  const bool is_this_;
+
   DISALLOW_COPY_AND_ASSIGN(HParameterValue);
 };
 
@@ -2001,8 +2231,8 @@
 class HTypeConversion : public HExpression<1> {
  public:
   // Instantiate a type conversion of `input` to `result_type`.
-  HTypeConversion(Primitive::Type result_type, HInstruction* input)
-      : HExpression(result_type, SideEffects::None()) {
+  HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc)
+      : HExpression(result_type, SideEffects::None()), dex_pc_(dex_pc) {
     SetRawInputAt(0, input);
     DCHECK_NE(input->GetType(), result_type);
   }
@@ -2011,15 +2241,23 @@
   Primitive::Type GetInputType() const { return GetInput()->GetType(); }
   Primitive::Type GetResultType() const { return GetType(); }
 
+  // Required by the x86 and ARM code generators when producing calls
+  // to the runtime.
+  uint32_t GetDexPc() const { return dex_pc_; }
+
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
 
   DECLARE_INSTRUCTION(TypeConversion);
 
  private:
+  const uint32_t dex_pc_;
+
   DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
 };
 
+static constexpr uint32_t kNoRegNumber = -1;
+
 class HPhi : public HInstruction {
  public:
   HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
@@ -2027,22 +2265,26 @@
         inputs_(arena, number_of_inputs),
         reg_number_(reg_number),
         type_(type),
-        is_live_(false) {
+        is_live_(false),
+        can_be_null_(true) {
     inputs_.SetSize(number_of_inputs);
   }
 
-  virtual size_t InputCount() const { return inputs_.Size(); }
-  virtual HInstruction* InputAt(size_t i) const { return inputs_.Get(i); }
+  size_t InputCount() const OVERRIDE { return inputs_.Size(); }
+  HInstruction* InputAt(size_t i) const OVERRIDE { return inputs_.Get(i); }
 
-  virtual void SetRawInputAt(size_t index, HInstruction* input) {
+  void SetRawInputAt(size_t index, HInstruction* input) OVERRIDE {
     inputs_.Put(index, input);
   }
 
   void AddInput(HInstruction* input);
 
-  virtual Primitive::Type GetType() const { return type_; }
+  Primitive::Type GetType() const OVERRIDE { return type_; }
   void SetType(Primitive::Type type) { type_ = type; }
 
+  bool CanBeNull() const OVERRIDE { return can_be_null_; }
+  void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+
   uint32_t GetRegNumber() const { return reg_number_; }
 
   void SetDead() { is_live_ = false; }
@@ -2057,6 +2299,7 @@
   const uint32_t reg_number_;
   Primitive::Type type_;
   bool is_live_;
+  bool can_be_null_;
 
   DISALLOW_COPY_AND_ASSIGN(HPhi);
 };
@@ -2068,15 +2311,17 @@
     SetRawInputAt(0, value);
   }
 
-  virtual bool CanBeMoved() const { return true; }
-  virtual bool InstructionDataEquals(HInstruction* other) const {
+  bool CanBeMoved() const OVERRIDE { return true; }
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
     UNUSED(other);
     return true;
   }
 
-  virtual bool NeedsEnvironment() const { return true; }
+  bool NeedsEnvironment() const OVERRIDE { return true; }
 
-  virtual bool CanThrow() const { return true; }
+  bool CanThrow() const OVERRIDE { return true; }
+
+  bool CanBeNull() const OVERRIDE { return false; }
 
   uint32_t GetDexPc() const { return dex_pc_; }
 
@@ -2090,39 +2335,49 @@
 
 class FieldInfo : public ValueObject {
  public:
-  FieldInfo(MemberOffset field_offset, Primitive::Type field_type)
-      : field_offset_(field_offset), field_type_(field_type) {}
+  FieldInfo(MemberOffset field_offset, Primitive::Type field_type, bool is_volatile)
+      : field_offset_(field_offset), field_type_(field_type), is_volatile_(is_volatile) {}
 
   MemberOffset GetFieldOffset() const { return field_offset_; }
   Primitive::Type GetFieldType() const { return field_type_; }
+  bool IsVolatile() const { return is_volatile_; }
 
  private:
   const MemberOffset field_offset_;
   const Primitive::Type field_type_;
+  const bool is_volatile_;
 };
 
 class HInstanceFieldGet : public HExpression<1> {
  public:
   HInstanceFieldGet(HInstruction* value,
                     Primitive::Type field_type,
-                    MemberOffset field_offset)
+                    MemberOffset field_offset,
+                    bool is_volatile)
       : HExpression(field_type, SideEffects::DependsOnSomething()),
-        field_info_(field_offset, field_type) {
+        field_info_(field_offset, field_type, is_volatile) {
     SetRawInputAt(0, value);
   }
 
-  virtual bool CanBeMoved() const { return true; }
-  virtual bool InstructionDataEquals(HInstruction* other) const {
-    size_t other_offset = other->AsInstanceFieldGet()->GetFieldOffset().SizeValue();
-    return other_offset == GetFieldOffset().SizeValue();
+  bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
+
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+    HInstanceFieldGet* other_get = other->AsInstanceFieldGet();
+    return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue();
   }
 
-  virtual size_t ComputeHashCode() const {
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    return GetFieldOffset().Uint32Value() < kPageSize;
+  }
+
+  size_t ComputeHashCode() const OVERRIDE {
     return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
   }
 
+  const FieldInfo& GetFieldInfo() const { return field_info_; }
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
   Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+  bool IsVolatile() const { return field_info_.IsVolatile(); }
 
   DECLARE_INSTRUCTION(InstanceFieldGet);
 
@@ -2137,16 +2392,22 @@
   HInstanceFieldSet(HInstruction* object,
                     HInstruction* value,
                     Primitive::Type field_type,
-                    MemberOffset field_offset)
+                    MemberOffset field_offset,
+                    bool is_volatile)
       : HTemplateInstruction(SideEffects::ChangesSomething()),
-        field_info_(field_offset, field_type) {
+        field_info_(field_offset, field_type, is_volatile) {
     SetRawInputAt(0, object);
     SetRawInputAt(1, value);
   }
 
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    return GetFieldOffset().Uint32Value() < kPageSize;
+  }
+
+  const FieldInfo& GetFieldInfo() const { return field_info_; }
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
   Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
-
+  bool IsVolatile() const { return field_info_.IsVolatile(); }
   HInstruction* GetValue() const { return InputAt(1); }
 
   DECLARE_INSTRUCTION(InstanceFieldSet);
@@ -2170,6 +2431,15 @@
     UNUSED(other);
     return true;
   }
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    // TODO: We can be smarter here.
+    // Currently, the array access is always preceded by an ArrayLength or a NullCheck
+    // which generates the implicit null check. There are cases when these can be removed
+    // to produce better code. If we ever add optimizations to do so we should allow an
+    // implicit check here (as long as the address falls in the first page).
+    return false;
+  }
+
   void SetType(Primitive::Type type) { type_ = type; }
 
   HInstruction* GetArray() const { return InputAt(0); }
@@ -2197,12 +2467,17 @@
     SetRawInputAt(2, value);
   }
 
-  bool NeedsEnvironment() const {
+  bool NeedsEnvironment() const OVERRIDE {
     // We currently always call a runtime method to catch array store
     // exceptions.
     return needs_type_check_;
   }
 
+  bool CanDoImplicitNullCheck() const OVERRIDE {
+    // TODO: Same as for ArrayGet.
+    return false;
+  }
+
   void ClearNeedsTypeCheck() {
     needs_type_check_ = false;
   }
@@ -2245,11 +2520,12 @@
     SetRawInputAt(0, array);
   }
 
-  virtual bool CanBeMoved() const { return true; }
-  virtual bool InstructionDataEquals(HInstruction* other) const {
+  bool CanBeMoved() const OVERRIDE { return true; }
+  bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
     UNUSED(other);
     return true;
   }
+  bool CanDoImplicitNullCheck() const OVERRIDE { return true; }
 
   DECLARE_INSTRUCTION(ArrayLength);
 
@@ -2376,6 +2652,12 @@
     return MustGenerateClinitCheck() || !is_referrers_class_;
   }
 
+  bool CanThrow() const OVERRIDE {
+    // May call runtime and and therefore can throw.
+    // TODO: finer grain decision.
+    return !is_referrers_class_;
+  }
+
   DECLARE_INSTRUCTION(LoadClass);
 
  private:
@@ -2419,7 +2701,7 @@
   DISALLOW_COPY_AND_ASSIGN(HLoadString);
 };
 
-// TODO: Pass this check to HInvokeStatic nodes.
+// TODO: Pass this check to HInvokeStaticOrDirect nodes.
 /**
  * Performs an initialization check on its Class object input.
  */
@@ -2458,24 +2740,29 @@
  public:
   HStaticFieldGet(HInstruction* cls,
                   Primitive::Type field_type,
-                  MemberOffset field_offset)
+                  MemberOffset field_offset,
+                  bool is_volatile)
       : HExpression(field_type, SideEffects::DependsOnSomething()),
-        field_info_(field_offset, field_type) {
+        field_info_(field_offset, field_type, is_volatile) {
     SetRawInputAt(0, cls);
   }
 
-  bool CanBeMoved() const OVERRIDE { return true; }
+
+  bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
+
   bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
-    size_t other_offset = other->AsStaticFieldGet()->GetFieldOffset().SizeValue();
-    return other_offset == GetFieldOffset().SizeValue();
+    HStaticFieldGet* other_get = other->AsStaticFieldGet();
+    return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue();
   }
 
   size_t ComputeHashCode() const OVERRIDE {
     return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
   }
 
+  const FieldInfo& GetFieldInfo() const { return field_info_; }
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
   Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+  bool IsVolatile() const { return field_info_.IsVolatile(); }
 
   DECLARE_INSTRUCTION(StaticFieldGet);
 
@@ -2490,15 +2777,18 @@
   HStaticFieldSet(HInstruction* cls,
                   HInstruction* value,
                   Primitive::Type field_type,
-                  MemberOffset field_offset)
+                  MemberOffset field_offset,
+                  bool is_volatile)
       : HTemplateInstruction(SideEffects::ChangesSomething()),
-        field_info_(field_offset, field_type) {
+        field_info_(field_offset, field_type, is_volatile) {
     SetRawInputAt(0, cls);
     SetRawInputAt(1, value);
   }
 
+  const FieldInfo& GetFieldInfo() const { return field_info_; }
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
   Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+  bool IsVolatile() const { return field_info_.IsVolatile(); }
 
   HInstruction* GetValue() const { return InputAt(1); }
 
@@ -2532,12 +2822,14 @@
 
   bool NeedsEnvironment() const OVERRIDE { return true; }
 
+  bool CanThrow() const OVERRIDE { return true; }
+
   uint32_t GetDexPc() const { return dex_pc_; }
 
   DECLARE_INSTRUCTION(Throw);
 
  private:
-  uint32_t dex_pc_;
+  const uint32_t dex_pc_;
 
   DISALLOW_COPY_AND_ASSIGN(HThrow);
 };
@@ -2639,7 +2931,7 @@
 
   DECLARE_INSTRUCTION(MonitorOperation);
 
- protected:
+ private:
   const OperationKind kind_;
   const uint32_t dex_pc_;
 
@@ -2647,7 +2939,6 @@
   DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
 };
 
-
 class MoveOperands : public ArenaObject<kArenaAllocMisc> {
  public:
   MoveOperands(Location source, Location destination, HInstruction* instruction)
@@ -2680,7 +2971,7 @@
 
   // True if this blocks a move from the given location.
   bool Blocks(Location loc) const {
-    return !IsEliminated() && source_.Equals(loc);
+    return !IsEliminated() && (source_.Contains(loc) || loc.Contains(source_));
   }
 
   // A move is redundant if it's been eliminated, if its source and
@@ -2709,8 +3000,6 @@
   // This is only used in debug mode, to ensure we do not connect interval siblings
   // in the same parallel move.
   HInstruction* instruction_;
-
-  DISALLOW_COPY_AND_ASSIGN(MoveOperands);
 };
 
 static constexpr size_t kDefaultNumberOfMoves = 4;
@@ -2720,18 +3009,26 @@
   explicit HParallelMove(ArenaAllocator* arena)
       : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
 
-  void AddMove(MoveOperands* move) {
-    if (kIsDebugBuild && move->GetInstruction() != nullptr) {
+  void AddMove(Location source, Location destination, HInstruction* instruction) {
+    DCHECK(source.IsValid());
+    DCHECK(destination.IsValid());
+    if (kIsDebugBuild) {
+      if (instruction != nullptr) {
+        for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+          DCHECK_NE(moves_.Get(i).GetInstruction(), instruction)
+            << "Doing parallel moves for the same instruction.";
+        }
+      }
       for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
-        DCHECK_NE(moves_.Get(i)->GetInstruction(), move->GetInstruction())
-          << "Doing parallel moves for the same instruction.";
+        DCHECK(!destination.Equals(moves_.Get(i).GetDestination()))
+            << "Same destination for two moves in a parallel move.";
       }
     }
-    moves_.Add(move);
+    moves_.Add(MoveOperands(source, destination, instruction));
   }
 
   MoveOperands* MoveOperandsAt(size_t index) const {
-    return moves_.Get(index);
+    return moves_.GetRawStorage() + index;
   }
 
   size_t NumMoves() const { return moves_.Size(); }
@@ -2739,7 +3036,7 @@
   DECLARE_INSTRUCTION(ParallelMove);
 
  private:
-  GrowableArray<MoveOperands*> moves_;
+  GrowableArray<MoveOperands> moves_;
 
   DISALLOW_COPY_AND_ASSIGN(HParallelMove);
 };
@@ -2837,6 +3134,39 @@
   DISALLOW_COPY_AND_ASSIGN(HPostOrderIterator);
 };
 
+// Iterator over the blocks that art part of the loop. Includes blocks part
+// of an inner loop. The order in which the blocks are iterated is on their
+// block id.
+class HBlocksInLoopIterator : public ValueObject {
+ public:
+  explicit HBlocksInLoopIterator(const HLoopInformation& info)
+      : blocks_in_loop_(info.GetBlocks()),
+        blocks_(info.GetHeader()->GetGraph()->GetBlocks()),
+        index_(0) {
+    if (!blocks_in_loop_.IsBitSet(index_)) {
+      Advance();
+    }
+  }
+
+  bool Done() const { return index_ == blocks_.Size(); }
+  HBasicBlock* Current() const { return blocks_.Get(index_); }
+  void Advance() {
+    ++index_;
+    for (size_t e = blocks_.Size(); index_ < e; ++index_) {
+      if (blocks_in_loop_.IsBitSet(index_)) {
+        break;
+      }
+    }
+  }
+
+ private:
+  const BitVector& blocks_in_loop_;
+  const GrowableArray<HBasicBlock*>& blocks_;
+  size_t index_;
+
+  DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopIterator);
+};
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 70dd8d7..5dbdc74 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -81,13 +81,12 @@
   entry->AddInstruction(new (&allocator) HExit());
 
   ASSERT_FALSE(parameter1->HasUses());
-  ASSERT_EQ(parameter1->NumberOfUses(), 0u);
 
   HInstruction* to_insert = new (&allocator) HNullCheck(parameter1, 0);
   entry->InsertInstructionBefore(to_insert, parameter2);
 
   ASSERT_TRUE(parameter1->HasUses());
-  ASSERT_EQ(parameter1->NumberOfUses(), 1u);
+  ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
 }
 
 /**
@@ -105,13 +104,12 @@
   entry->AddInstruction(parameter);
 
   ASSERT_FALSE(parameter->HasUses());
-  ASSERT_EQ(parameter->NumberOfUses(), 0u);
 
   HInstruction* to_add = new (&allocator) HNullCheck(parameter, 0);
   entry->AddInstruction(to_add);
 
   ASSERT_TRUE(parameter->HasUses());
-  ASSERT_EQ(parameter->NumberOfUses(), 1u);
+  ASSERT_TRUE(parameter->GetUses().HasOnlyOneUse());
 }
 
 }  // namespace art
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index e36ef19..d9e082a 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -21,6 +21,12 @@
 
 namespace art {
 
+static const char* kBuilderPassName = "builder";
+static const char* kSsaBuilderPassName = "ssa_builder";
+static const char* kLivenessPassName = "liveness";
+static const char* kRegisterAllocatorPassName = "register";
+static const char* kLoopInvariantCodeMotionPassName = "licm";
+
 /**
  * Abstraction to implement an optimization pass.
  */
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 100a6bc..c518f33 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -19,25 +19,34 @@
 #include <fstream>
 #include <stdint.h>
 
+#include "base/dumpable.h"
+#include "base/timing_logger.h"
+#include "bounds_check_elimination.h"
 #include "builder.h"
 #include "code_generator.h"
 #include "compiler.h"
 #include "constant_folding.h"
 #include "dead_code_elimination.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
 #include "elf_writer_quick.h"
 #include "graph_visualizer.h"
 #include "gvn.h"
+#include "inliner.h"
 #include "instruction_simplifier.h"
+#include "intrinsics.h"
+#include "licm.h"
 #include "jni/quick/jni_compiler.h"
 #include "mirror/art_method-inl.h"
 #include "nodes.h"
 #include "prepare_for_register_allocation.h"
 #include "register_allocator.h"
+#include "side_effects_analysis.h"
 #include "ssa_builder.h"
 #include "ssa_phi_elimination.h"
 #include "ssa_liveness_analysis.h"
+#include "reference_type_propagation.h"
 #include "utils/arena_allocator.h"
 
 namespace art {
@@ -66,16 +75,88 @@
 };
 
 /**
- * If set to true, generates a file suitable for the c1visualizer tool and IRHydra.
- */
-static bool kIsVisualizerEnabled = false;
-
-/**
  * Filter to apply to the visualizer. Methods whose name contain that filter will
- * be in the file.
+ * be dumped.
  */
 static const char* kStringFilter = "";
 
+class PassInfo;
+
+class PassInfoPrinter : public ValueObject {
+ public:
+  PassInfoPrinter(HGraph* graph,
+                  const char* method_name,
+                  const CodeGenerator& codegen,
+                  std::ostream* visualizer_output,
+                  CompilerDriver* compiler_driver)
+      : method_name_(method_name),
+        timing_logger_enabled_(compiler_driver->GetDumpPasses()),
+        timing_logger_(method_name, true, true),
+        visualizer_enabled_(!compiler_driver->GetDumpCfgFileName().empty()),
+        visualizer_(visualizer_output, graph, codegen, method_name_) {
+    if (strstr(method_name, kStringFilter) == nullptr) {
+      timing_logger_enabled_ = visualizer_enabled_ = false;
+    }
+  }
+
+  ~PassInfoPrinter() {
+    if (timing_logger_enabled_) {
+      LOG(INFO) << "TIMINGS " << method_name_;
+      LOG(INFO) << Dumpable<TimingLogger>(timing_logger_);
+    }
+  }
+
+ private:
+  void StartPass(const char* pass_name) {
+    // Dump graph first, then start timer.
+    if (visualizer_enabled_) {
+      visualizer_.DumpGraph(pass_name, /* is_after_pass */ false);
+    }
+    if (timing_logger_enabled_) {
+      timing_logger_.StartTiming(pass_name);
+    }
+  }
+
+  void EndPass(const char* pass_name) {
+    // Pause timer first, then dump graph.
+    if (timing_logger_enabled_) {
+      timing_logger_.EndTiming();
+    }
+    if (visualizer_enabled_) {
+      visualizer_.DumpGraph(pass_name, /* is_after_pass */ true);
+    }
+  }
+
+  const char* method_name_;
+
+  bool timing_logger_enabled_;
+  TimingLogger timing_logger_;
+
+  bool visualizer_enabled_;
+  HGraphVisualizer visualizer_;
+
+  friend PassInfo;
+
+  DISALLOW_COPY_AND_ASSIGN(PassInfoPrinter);
+};
+
+class PassInfo : public ValueObject {
+ public:
+  PassInfo(const char *pass_name, PassInfoPrinter* pass_info_printer)
+      : pass_name_(pass_name),
+        pass_info_printer_(pass_info_printer) {
+    pass_info_printer_->StartPass(pass_name_);
+  }
+
+  ~PassInfo() {
+    pass_info_printer_->EndPass(pass_name_);
+  }
+
+ private:
+  const char* const pass_name_;
+  PassInfoPrinter* const pass_info_printer_;
+};
+
 class OptimizingCompiler FINAL : public Compiler {
  public:
   explicit OptimizingCompiler(CompilerDriver* driver);
@@ -105,14 +186,9 @@
                 const std::string& android_root,
                 bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  Backend* GetCodeGenerator(CompilationUnit* cu ATTRIBUTE_UNUSED,
-                            void* compilation_unit ATTRIBUTE_UNUSED) const OVERRIDE {
-    return nullptr;
-  }
-
   void InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const OVERRIDE {}
 
-  void Init() const OVERRIDE {}
+  void Init() OVERRIDE;
 
   void UnInit() const OVERRIDE {}
 
@@ -120,9 +196,20 @@
   // Whether we should run any optimization or register allocation. If false, will
   // just run the code generation after the graph was built.
   const bool run_optimizations_;
-  mutable AtomicInteger total_compiled_methods_;
-  mutable AtomicInteger unoptimized_compiled_methods_;
-  mutable AtomicInteger optimized_compiled_methods_;
+
+  // Optimize and compile `graph`.
+  CompiledMethod* CompileOptimized(HGraph* graph,
+                                   CodeGenerator* codegen,
+                                   CompilerDriver* driver,
+                                   const DexCompilationUnit& dex_compilation_unit,
+                                   PassInfoPrinter* pass_info) const;
+
+  // Just compile without doing optimizations.
+  CompiledMethod* CompileBaseline(CodeGenerator* codegen,
+                                  CompilerDriver* driver,
+                                  const DexCompilationUnit& dex_compilation_unit) const;
+
+  mutable OptimizingCompilerStats compilation_stats_;
 
   std::unique_ptr<std::ostream> visualizer_output_;
 
@@ -135,24 +222,23 @@
     : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
       run_optimizations_(
           driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
-      total_compiled_methods_(0),
-      unoptimized_compiled_methods_(0),
-      optimized_compiled_methods_(0) {
-  if (kIsVisualizerEnabled) {
-    visualizer_output_.reset(new std::ofstream("art.cfg"));
+      compilation_stats_() {}
+
+void OptimizingCompiler::Init() {
+  // Enable C1visualizer output. Must be done in Init() because the compiler
+  // driver is not fully initialized when passed to the compiler's constructor.
+  CompilerDriver* driver = GetCompilerDriver();
+  const std::string cfg_file_name = driver->GetDumpCfgFileName();
+  if (!cfg_file_name.empty()) {
+    CHECK_EQ(driver->GetThreadCount(), 1U)
+      << "Graph visualizer requires the compiler to run single-threaded. "
+      << "Invoke the compiler with '-j1'.";
+    visualizer_output_.reset(new std::ofstream(cfg_file_name));
   }
 }
 
 OptimizingCompiler::~OptimizingCompiler() {
-  if (total_compiled_methods_ == 0) {
-    LOG(INFO) << "Did not compile any method.";
-  } else {
-    size_t unoptimized_percent = (unoptimized_compiled_methods_ * 100 / total_compiled_methods_);
-    size_t optimized_percent = (optimized_compiled_methods_ * 100 / total_compiled_methods_);
-    LOG(INFO) << "Compiled " << total_compiled_methods_ << " methods: "
-              << unoptimized_percent << "% (" << unoptimized_compiled_methods_ << ") unoptimized, "
-              << optimized_percent << "% (" << optimized_compiled_methods_ << ") optimized.";
-  }
+  compilation_stats_.Log();
 }
 
 bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
@@ -191,48 +277,140 @@
   return code_item.tries_size_ == 0;
 }
 
-static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) {
-  HDeadCodeElimination opt1(graph);
-  HConstantFolding opt2(graph);
-  SsaRedundantPhiElimination opt3(graph);
-  SsaDeadPhiElimination opt4(graph);
-  InstructionSimplifier opt5(graph);
-  GVNOptimization opt6(graph);
-  InstructionSimplifier opt7(graph);
-
-  HOptimization* optimizations[] = {
-    &opt1,
-    &opt2,
-    &opt3,
-    &opt4,
-    &opt5,
-    &opt6,
-    &opt7
-  };
-
-  for (size_t i = 0; i < arraysize(optimizations); ++i) {
+static void RunOptimizations(HOptimization* optimizations[],
+                             size_t length,
+                             PassInfoPrinter* pass_info_printer) {
+  for (size_t i = 0; i < length; ++i) {
     HOptimization* optimization = optimizations[i];
-    optimization->Run();
-    visualizer.DumpGraph(optimization->GetPassName());
+    {
+      PassInfo pass_info(optimization->GetPassName(), pass_info_printer);
+      optimization->Run();
+    }
     optimization->Check();
   }
 }
 
-static bool TryBuildingSsa(HGraph* graph,
-                           const DexCompilationUnit& dex_compilation_unit,
-                           const HGraphVisualizer& visualizer) {
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+static void RunOptimizations(HGraph* graph,
+                             CompilerDriver* driver,
+                             OptimizingCompilerStats* stats,
+                             const DexCompilationUnit& dex_compilation_unit,
+                             PassInfoPrinter* pass_info_printer) {
+  SsaRedundantPhiElimination redundant_phi(graph);
+  SsaDeadPhiElimination dead_phi(graph);
+  HDeadCodeElimination dce(graph);
+  HConstantFolding fold1(graph);
+  InstructionSimplifier simplify1(graph);
 
-  if (!graph->AnalyzeNaturalLoops()) {
-    LOG(INFO) << "Skipping compilation of "
-              << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
-                              *dex_compilation_unit.GetDexFile())
-              << ": it contains a non natural loop";
-    return false;
+  HInliner inliner(graph, dex_compilation_unit, driver, stats);
+
+  HConstantFolding fold2(graph);
+  SideEffectsAnalysis side_effects(graph);
+  GVNOptimization gvn(graph, side_effects);
+  LICM licm(graph, side_effects);
+  BoundsCheckElimination bce(graph);
+  ReferenceTypePropagation type_propagation(graph);
+  InstructionSimplifier simplify2(graph, "instruction_simplifier_after_types");
+
+  IntrinsicsRecognizer intrinsics(graph, dex_compilation_unit.GetDexFile(), driver);
+
+  HOptimization* optimizations[] = {
+    &redundant_phi,
+    &dead_phi,
+    &intrinsics,
+    &dce,
+    &fold1,
+    &simplify1,
+    &inliner,
+    &fold2,
+    &side_effects,
+    &gvn,
+    &licm,
+    &bce,
+    &type_propagation,
+    &simplify2
+  };
+
+  RunOptimizations(optimizations, arraysize(optimizations), pass_info_printer);
+}
+
+// The stack map we generate must be 4-byte aligned on ARM. Since existing
+// maps are generated alongside these stack maps, we must also align them.
+static ArrayRef<const uint8_t> AlignVectorSize(std::vector<uint8_t>& vector) {
+  size_t size = vector.size();
+  size_t aligned_size = RoundUp(size, 4);
+  for (; size < aligned_size; ++size) {
+    vector.push_back(0);
   }
-  visualizer.DumpGraph("ssa transform");
-  return true;
+  return ArrayRef<const uint8_t>(vector);
+}
+
+
+CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
+                                                     CodeGenerator* codegen,
+                                                     CompilerDriver* compiler_driver,
+                                                     const DexCompilationUnit& dex_compilation_unit,
+                                                     PassInfoPrinter* pass_info_printer) const {
+  RunOptimizations(
+      graph, compiler_driver, &compilation_stats_, dex_compilation_unit, pass_info_printer);
+
+  PrepareForRegisterAllocation(graph).Run();
+  SsaLivenessAnalysis liveness(*graph, codegen);
+  {
+    PassInfo pass_info(kLivenessPassName, pass_info_printer);
+    liveness.Analyze();
+  }
+  {
+    PassInfo pass_info(kRegisterAllocatorPassName, pass_info_printer);
+    RegisterAllocator(graph->GetArena(), codegen, liveness).AllocateRegisters();
+  }
+
+  CodeVectorAllocator allocator;
+  codegen->CompileOptimized(&allocator);
+
+  std::vector<uint8_t> stack_map;
+  codegen->BuildStackMaps(&stack_map);
+
+  compilation_stats_.RecordStat(MethodCompilationStat::kCompiledOptimized);
+
+  return CompiledMethod::SwapAllocCompiledMethodStackMap(
+      compiler_driver,
+      codegen->GetInstructionSet(),
+      ArrayRef<const uint8_t>(allocator.GetMemory()),
+      codegen->GetFrameSize(),
+      codegen->GetCoreSpillMask(),
+      codegen->GetFpuSpillMask(),
+      ArrayRef<const uint8_t>(stack_map));
+}
+
+
+CompiledMethod* OptimizingCompiler::CompileBaseline(
+    CodeGenerator* codegen,
+    CompilerDriver* compiler_driver,
+    const DexCompilationUnit& dex_compilation_unit) const {
+  CodeVectorAllocator allocator;
+  codegen->CompileBaseline(&allocator);
+
+  std::vector<uint8_t> mapping_table;
+  DefaultSrcMap src_mapping_table;
+  bool include_debug_symbol = compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+  codegen->BuildMappingTable(&mapping_table, include_debug_symbol ? &src_mapping_table : nullptr);
+  std::vector<uint8_t> vmap_table;
+  codegen->BuildVMapTable(&vmap_table);
+  std::vector<uint8_t> gc_map;
+  codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+
+  compilation_stats_.RecordStat(MethodCompilationStat::kCompiledBaseline);
+  return CompiledMethod::SwapAllocCompiledMethod(compiler_driver,
+                                                 codegen->GetInstructionSet(),
+                                                 ArrayRef<const uint8_t>(allocator.GetMemory()),
+                                                 codegen->GetFrameSize(),
+                                                 codegen->GetCoreSpillMask(),
+                                                 codegen->GetFpuSpillMask(),
+                                                 &src_mapping_table,
+                                                 AlignVectorSize(mapping_table),
+                                                 AlignVectorSize(vmap_table),
+                                                 AlignVectorSize(gc_map),
+                                                 ArrayRef<const uint8_t>());
 }
 
 CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
@@ -243,8 +421,10 @@
                                             jobject class_loader,
                                             const DexFile& dex_file) const {
   UNUSED(invoke_type);
-  total_compiled_methods_++;
-  InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
+  std::string method_name = PrettyMethod(method_idx, dex_file);
+  compilation_stats_.RecordStat(MethodCompilationStat::kAttemptCompilation);
+  CompilerDriver* compiler_driver = GetCompilerDriver();
+  InstructionSet instruction_set = compiler_driver->GetInstructionSet();
   // Always use the thumb2 assembler: some runtime functionality (like implicit stack
   // overflow checks) assume thumb2.
   if (instruction_set == kArm) {
@@ -253,114 +433,98 @@
 
   // Do not attempt to compile on architectures we do not support.
   if (!IsInstructionSetSupported(instruction_set)) {
+    compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledUnsupportedIsa);
     return nullptr;
   }
 
   if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
+    compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledPathological);
     return nullptr;
   }
 
   DexCompilationUnit dex_compilation_unit(
     nullptr, class_loader, art::Runtime::Current()->GetClassLinker(), dex_file, code_item,
     class_def_idx, method_idx, access_flags,
-    GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx));
-
-  // For testing purposes, we put a special marker on method names that should be compiled
-  // with this compiler. This makes sure we're not regressing.
-  bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos;
-  bool shouldOptimize =
-      dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos;
+    compiler_driver->GetVerifiedMethod(&dex_file, method_idx));
 
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  HGraphBuilder builder(&arena, &dex_compilation_unit, &dex_file, GetCompilerDriver());
+  HGraph* graph = new (&arena) HGraph(&arena);
 
-  HGraph* graph = builder.BuildGraph(*code_item);
-  if (graph == nullptr) {
-    CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
-    return nullptr;
-  }
+  // For testing purposes, we put a special marker on method names that should be compiled
+  // with this compiler. This makes sure we're not regressing.
+  bool shouldCompile = method_name.find("$opt$") != std::string::npos;
+  bool shouldOptimize = method_name.find("$opt$reg$") != std::string::npos;
 
-  CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, instruction_set);
-  if (codegen == nullptr) {
+  std::unique_ptr<CodeGenerator> codegen(
+      CodeGenerator::Create(graph,
+                            instruction_set,
+                            *compiler_driver->GetInstructionSetFeatures(),
+                            compiler_driver->GetCompilerOptions()));
+  if (codegen.get() == nullptr) {
     CHECK(!shouldCompile) << "Could not find code generator for optimizing compiler";
+    compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledNoCodegen);
     return nullptr;
   }
 
-  HGraphVisualizer visualizer(
-      visualizer_output_.get(), graph, kStringFilter, *codegen, dex_compilation_unit);
-  visualizer.DumpGraph("builder");
+  PassInfoPrinter pass_info_printer(graph,
+                                    method_name.c_str(),
+                                    *codegen.get(),
+                                    visualizer_output_.get(),
+                                    compiler_driver);
 
-  CodeVectorAllocator allocator;
+  HGraphBuilder builder(graph,
+                        &dex_compilation_unit,
+                        &dex_compilation_unit,
+                        &dex_file,
+                        compiler_driver,
+                        &compilation_stats_);
 
-  if (run_optimizations_
-      && CanOptimize(*code_item)
-      && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
-    VLOG(compiler) << "Optimizing " << PrettyMethod(method_idx, dex_file);
-    optimized_compiled_methods_++;
-    if (!TryBuildingSsa(graph, dex_compilation_unit, visualizer)) {
-      // We could not transform the graph to SSA, bailout.
+  VLOG(compiler) << "Building " << method_name;
+
+  {
+    PassInfo pass_info(kBuilderPassName, &pass_info_printer);
+    if (!builder.BuildGraph(*code_item)) {
+      CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
       return nullptr;
     }
-    RunOptimizations(graph, visualizer);
+  }
 
-    PrepareForRegisterAllocation(graph).Run();
-    SsaLivenessAnalysis liveness(*graph, codegen);
-    liveness.Analyze();
-    visualizer.DumpGraph(kLivenessPassName);
+  bool can_optimize = CanOptimize(*code_item);
+  bool can_allocate_registers = RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set);
+  if (run_optimizations_ && can_optimize && can_allocate_registers) {
+    VLOG(compiler) << "Optimizing " << method_name;
 
-    RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness);
-    register_allocator.AllocateRegisters();
+    {
+      PassInfo pass_info(kSsaBuilderPassName, &pass_info_printer);
+      if (!graph->TryBuildingSsa()) {
+        // We could not transform the graph to SSA, bailout.
+        LOG(INFO) << "Skipping compilation of " << method_name << ": it contains a non natural loop";
+        compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA);
+        return nullptr;
+      }
+    }
 
-    visualizer.DumpGraph(kRegisterAllocatorPassName);
-    codegen->CompileOptimized(&allocator);
-
-    std::vector<uint8_t> mapping_table;
-    SrcMap src_mapping_table;
-    codegen->BuildMappingTable(&mapping_table,
-            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
-                 &src_mapping_table : nullptr);
-
-    std::vector<uint8_t> stack_map;
-    codegen->BuildStackMaps(&stack_map);
-
-    return new CompiledMethod(GetCompilerDriver(),
-                              instruction_set,
-                              allocator.GetMemory(),
-                              codegen->GetFrameSize(),
-                              codegen->GetCoreSpillMask(),
-                              0, /* FPR spill mask, unused */
-                              mapping_table,
-                              stack_map);
+    return CompileOptimized(graph,
+                            codegen.get(),
+                            compiler_driver,
+                            dex_compilation_unit,
+                            &pass_info_printer);
   } else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
     LOG(FATAL) << "Could not allocate registers in optimizing compiler";
     UNREACHABLE();
   } else {
-    VLOG(compiler) << "Compile baseline " << PrettyMethod(method_idx, dex_file);
-    unoptimized_compiled_methods_++;
-    codegen->CompileBaseline(&allocator);
+    VLOG(compiler) << "Compile baseline " << method_name;
 
-    std::vector<uint8_t> mapping_table;
-    SrcMap src_mapping_table;
-    codegen->BuildMappingTable(&mapping_table,
-            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
-                 &src_mapping_table : nullptr);
-    std::vector<uint8_t> vmap_table;
-    codegen->BuildVMapTable(&vmap_table);
-    std::vector<uint8_t> gc_map;
-    codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+    if (!run_optimizations_) {
+      compilation_stats_.RecordStat(MethodCompilationStat::kNotOptimizedDisabled);
+    } else if (!can_optimize) {
+      compilation_stats_.RecordStat(MethodCompilationStat::kNotOptimizedTryCatch);
+    } else if (!can_allocate_registers) {
+      compilation_stats_.RecordStat(MethodCompilationStat::kNotOptimizedRegisterAllocator);
+    }
 
-    return new CompiledMethod(GetCompilerDriver(),
-                              instruction_set,
-                              allocator.GetMemory(),
-                              codegen->GetFrameSize(),
-                              codegen->GetCoreSpillMask(),
-                              0, /* FPR spill mask, unused */
-                              &src_mapping_table,
-                              mapping_table,
-                              vmap_table,
-                              gc_map,
-                              nullptr);
+    return CompileBaseline(codegen.get(), compiler_driver, dex_compilation_unit);
   }
 }
 
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index a415eca..d076fb5 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -24,6 +24,6 @@
 
 Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
 
-}
+}  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
new file mode 100644
index 0000000..cc2723d
--- /dev/null
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_STATS_H_
+#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_STATS_H_
+
+#include <sstream>
+#include <string>
+
+#include "atomic.h"
+
+namespace art {
+
+enum MethodCompilationStat {
+  kAttemptCompilation = 0,
+  kCompiledBaseline,
+  kCompiledOptimized,
+  kInlinedInvoke,
+  kNotCompiledUnsupportedIsa,
+  kNotCompiledPathological,
+  kNotCompiledHugeMethod,
+  kNotCompiledLargeMethodNoBranches,
+  kNotCompiledCannotBuildSSA,
+  kNotCompiledNoCodegen,
+  kNotCompiledUnresolvedMethod,
+  kNotCompiledUnresolvedField,
+  kNotCompiledNonSequentialRegPair,
+  kNotOptimizedTryCatch,
+  kNotOptimizedDisabled,
+  kNotCompiledCantAccesType,
+  kNotOptimizedRegisterAllocator,
+  kNotCompiledUnhandledInstruction,
+  kLastStat
+};
+
+class OptimizingCompilerStats {
+ public:
+  OptimizingCompilerStats() {}
+
+  void RecordStat(MethodCompilationStat stat) {
+    compile_stats_[stat]++;
+  }
+
+  void Log() const {
+    if (compile_stats_[kAttemptCompilation] == 0) {
+      LOG(INFO) << "Did not compile any method.";
+    } else {
+      size_t unoptimized_percent =
+          compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation];
+      size_t optimized_percent =
+          compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation];
+      std::ostringstream oss;
+      oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "
+          << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "
+          << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.";
+      for (int i = 0; i < kLastStat; i++) {
+        if (compile_stats_[i] != 0) {
+          oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
+        }
+      }
+      LOG(INFO) << oss.str();
+    }
+  }
+
+ private:
+  std::string PrintMethodCompilationStat(int stat) const {
+    switch (stat) {
+      case kAttemptCompilation : return "kAttemptCompilation";
+      case kCompiledBaseline : return "kCompiledBaseline";
+      case kCompiledOptimized : return "kCompiledOptimized";
+      case kInlinedInvoke : return "kInlinedInvoke";
+      case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
+      case kNotCompiledPathological : return "kNotCompiledPathological";
+      case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";
+      case kNotCompiledLargeMethodNoBranches : return "kNotCompiledLargeMethodNoBranches";
+      case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA";
+      case kNotCompiledNoCodegen : return "kNotCompiledNoCodegen";
+      case kNotCompiledUnresolvedMethod : return "kNotCompiledUnresolvedMethod";
+      case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField";
+      case kNotCompiledNonSequentialRegPair : return "kNotCompiledNonSequentialRegPair";
+      case kNotOptimizedDisabled : return "kNotOptimizedDisabled";
+      case kNotOptimizedTryCatch : return "kNotOptimizedTryCatch";
+      case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType";
+      case kNotOptimizedRegisterAllocator : return "kNotOptimizedRegisterAllocator";
+      case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
+      default: LOG(FATAL) << "invalid stat";
+    }
+    return "";
+  }
+
+  AtomicInteger compile_stats_[kLastStat];
+
+  DISALLOW_COPY_AND_ASSIGN(OptimizingCompilerStats);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_STATS_H_
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index c4106b7..6b23692 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -19,6 +19,7 @@
 
 #include "nodes.h"
 #include "builder.h"
+#include "compiler/dex/pass_manager.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
 #include "ssa_liveness_analysis.h"
@@ -45,8 +46,12 @@
 LiveInterval* BuildInterval(const size_t ranges[][2],
                             size_t number_of_ranges,
                             ArenaAllocator* allocator,
-                            int reg = -1) {
-  LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt);
+                            int reg = -1,
+                            HInstruction* defined_by = nullptr) {
+  LiveInterval* interval = LiveInterval::MakeInterval(allocator, Primitive::kPrimInt, defined_by);
+  if (defined_by != nullptr) {
+    defined_by->SetLiveInterval(interval);
+  }
   for (size_t i = number_of_ranges; i > 0; --i) {
     interval->AddRange(ranges[i - 1][0], ranges[i - 1][1]);
   }
@@ -71,11 +76,12 @@
 inline HGraph* CreateCFG(ArenaAllocator* allocator,
                          const uint16_t* data,
                          Primitive::Type return_type = Primitive::kPrimInt) {
-  HGraphBuilder builder(allocator, return_type);
+  HGraph* graph = new (allocator) HGraph(allocator);
+  HGraphBuilder builder(graph, return_type);
   const DexFile::CodeItem* item =
     reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  return graph;
+  bool graph_built = builder.BuildGraph(*item);
+  return graph_built ? graph : nullptr;
 }
 
 // Naive string diff data type.
@@ -96,6 +102,11 @@
   return result;
 }
 
+// Returns if the instruction is removed from the graph.
+inline bool IsRemoved(HInstruction* instruction) {
+  return instruction->GetBlock() == nullptr;
+}
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 1e93ece..7d0641e 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -37,10 +37,12 @@
 
   // Perform the moves with constant sources.
   for (size_t i = 0; i < moves_.Size(); ++i) {
-    const MoveOperands& move = *moves_.Get(i);
-    if (!move.IsEliminated()) {
-      DCHECK(move.GetSource().IsConstant());
+    MoveOperands* move = moves_.Get(i);
+    if (!move->IsEliminated()) {
+      DCHECK(move->GetSource().IsConstant());
       EmitMove(i);
+      // Eliminate the move, in case following moves need a scratch register.
+      move->Eliminate();
     }
   }
 
@@ -61,8 +63,43 @@
   }
 }
 
+// Update the source of `move`, knowing that `updated_location` has been swapped
+// with `new_source`. Note that `updated_location` can be a pair, therefore if
+// `move` is non-pair, we need to extract which register to use.
+static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) {
+  Location source = move->GetSource();
+  if (new_source.GetKind() == source.GetKind()) {
+    DCHECK(updated_location.Equals(source));
+    move->SetSource(new_source);
+  } else if (new_source.IsStackSlot()
+             || new_source.IsDoubleStackSlot()
+             || source.IsStackSlot()
+             || source.IsDoubleStackSlot()) {
+    // Stack slots never take part of a pair/non-pair swap.
+    DCHECK(updated_location.Equals(source));
+    move->SetSource(new_source);
+  } else if (source.IsRegister()) {
+    DCHECK(new_source.IsRegisterPair()) << new_source;
+    DCHECK(updated_location.IsRegisterPair()) << updated_location;
+    if (updated_location.low() == source.reg()) {
+      move->SetSource(Location::RegisterLocation(new_source.low()));
+    } else {
+      DCHECK_EQ(updated_location.high(), source.reg());
+      move->SetSource(Location::RegisterLocation(new_source.high()));
+    }
+  } else if (source.IsFpuRegister()) {
+    DCHECK(new_source.IsFpuRegisterPair()) << new_source;
+    DCHECK(updated_location.IsFpuRegisterPair()) << updated_location;
+    if (updated_location.low() == source.reg()) {
+      move->SetSource(Location::FpuRegisterLocation(new_source.low()));
+    } else {
+      DCHECK_EQ(updated_location.high(), source.reg());
+      move->SetSource(Location::FpuRegisterLocation(new_source.high()));
+    }
+  }
+}
 
-void ParallelMoveResolver::PerformMove(size_t index) {
+MoveOperands* ParallelMoveResolver::PerformMove(size_t index) {
   // Each call to this function performs a move and deletes it from the move
   // graph.  We first recursively perform any move blocking this one.  We
   // mark a move as "pending" on entry to PerformMove in order to detect
@@ -70,35 +107,59 @@
   // which means that a call to PerformMove could change any source operand
   // in the move graph.
 
-  DCHECK(!moves_.Get(index)->IsPending());
-  DCHECK(!moves_.Get(index)->IsRedundant());
+  MoveOperands* move = moves_.Get(index);
+  DCHECK(!move->IsPending());
+  if (move->IsRedundant()) {
+    // Because we swap register pairs first, following, un-pending
+    // moves may become redundant.
+    move->Eliminate();
+    return nullptr;
+  }
 
   // Clear this move's destination to indicate a pending move.  The actual
   // destination is saved in a stack-allocated local.  Recursion may allow
   // multiple moves to be pending.
-  DCHECK(!moves_.Get(index)->GetSource().IsInvalid());
-  Location destination = moves_.Get(index)->MarkPending();
+  DCHECK(!move->GetSource().IsInvalid());
+  Location destination = move->MarkPending();
 
   // Perform a depth-first traversal of the move graph to resolve
   // dependencies.  Any unperformed, unpending move with a source the same
   // as this one's destination blocks this one so recursively perform all
   // such moves.
+  MoveOperands* required_swap = nullptr;
   for (size_t i = 0; i < moves_.Size(); ++i) {
     const MoveOperands& other_move = *moves_.Get(i);
     if (other_move.Blocks(destination) && !other_move.IsPending()) {
       // Though PerformMove can change any source operand in the move graph,
-      // this call cannot create a blocking move via a swap (this loop does
-      // not miss any).  Assume there is a non-blocking move with source A
+      // calling `PerformMove` cannot create a blocking move via a swap
+      // (this loop does not miss any).
+      // For example, assume there is a non-blocking move with source A
       // and this move is blocked on source B and there is a swap of A and
       // B.  Then A and B must be involved in the same cycle (or they would
       // not be swapped).  Since this move's destination is B and there is
       // only a single incoming edge to an operand, this move must also be
       // involved in the same cycle.  In that case, the blocking move will
       // be created but will be "pending" when we return from PerformMove.
-      PerformMove(i);
+      required_swap = PerformMove(i);
+
+      if (required_swap == move) {
+        // If this move is required to swap, we do so without looking
+        // at the next moves. Swapping is not blocked by anything, it just
+        // updates other moves's source.
+        break;
+      } else if (required_swap == moves_.Get(i)) {
+        // If `other_move` was swapped, we iterate again to find a new
+        // potential cycle.
+        required_swap = nullptr;
+        i = 0;
+      } else if (required_swap != nullptr) {
+        // A move is required to swap. We walk back the cycle to find the
+        // move by just returning from this `PerforrmMove`.
+        moves_.Get(index)->ClearPending(destination);
+        return required_swap;
+      }
     }
   }
-  MoveOperands* move = moves_.Get(index);
 
   // We are about to resolve this move and don't need it marked as
   // pending, so restore its destination.
@@ -108,19 +169,30 @@
   // so it may now be the last move in the cycle.  If so remove it.
   if (move->GetSource().Equals(destination)) {
     move->Eliminate();
-    return;
+    DCHECK(required_swap == nullptr);
+    return nullptr;
   }
 
   // The move may be blocked on a (at most one) pending move, in which case
   // we have a cycle.  Search for such a blocking move and perform a swap to
   // resolve it.
   bool do_swap = false;
-  for (size_t i = 0; i < moves_.Size(); ++i) {
-    const MoveOperands& other_move = *moves_.Get(i);
-    if (other_move.Blocks(destination)) {
-      DCHECK(other_move.IsPending());
-      do_swap = true;
-      break;
+  if (required_swap != nullptr) {
+    DCHECK_EQ(required_swap, move);
+    do_swap = true;
+  } else {
+    for (size_t i = 0; i < moves_.Size(); ++i) {
+      const MoveOperands& other_move = *moves_.Get(i);
+      if (other_move.Blocks(destination)) {
+        DCHECK(other_move.IsPending());
+        if (!destination.IsPair() && other_move.GetSource().IsPair()) {
+          // We swap pairs before swapping non-pairs. Go back from the
+          // cycle by returning the pair that must be swapped.
+          return moves_.Get(i);
+        }
+        do_swap = true;
+        break;
+      }
     }
   }
 
@@ -135,15 +207,21 @@
     for (size_t i = 0; i < moves_.Size(); ++i) {
       const MoveOperands& other_move = *moves_.Get(i);
       if (other_move.Blocks(source)) {
-        moves_.Get(i)->SetSource(swap_destination);
+        UpdateSourceOf(moves_.Get(i), source, swap_destination);
       } else if (other_move.Blocks(swap_destination)) {
-        moves_.Get(i)->SetSource(source);
+        UpdateSourceOf(moves_.Get(i), swap_destination, source);
       }
     }
+    // If the swap was required because of a pair in the middle of a cycle,
+    // we return the swapped move, so that the caller knows it needs to re-iterate
+    // its dependency loop.
+    return required_swap;
   } else {
     // This move is not blocked.
     EmitMove(index);
     move->Eliminate();
+    DCHECK(required_swap == nullptr);
+    return nullptr;
   }
 }
 
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index 309425e..3fa1b37 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -58,6 +58,9 @@
   };
 
   bool IsScratchLocation(Location loc);
+
+  // Allocate a scratch register for performing a move. The method will try to use
+  // a register that is the destination of a move, but that move has not been emitted yet.
   int AllocateScratchRegister(int blocked, int if_scratch, int register_count, bool* spilled);
 
   // Emit a move.
@@ -80,7 +83,15 @@
 
   // Perform the move at the moves_ index in question (possibly requiring
   // other moves to satisfy dependencies).
-  void PerformMove(size_t index);
+  //
+  // Return whether another move in the dependency cycle needs to swap. This
+  // is to handle pair swaps, where we want the pair to swap first to avoid
+  // building pairs that are unexpected by the code generator. For example, if
+  // we were to swap R1 with R2, we would need to update all locations using
+  // R2 to R1. So a (R2,R3) pair register could become (R1,R3). We could make
+  // the code generator understand such pairs, but it's easier and cleaner to
+  // just not create such pairs and exchange pairs in priority.
+  MoveOperands* PerformMove(size_t index);
 
   DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolver);
 };
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 62629bc..bb7541d 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -26,16 +26,26 @@
  public:
   explicit TestParallelMoveResolver(ArenaAllocator* allocator) : ParallelMoveResolver(allocator) {}
 
+  void Dump(Location location) {
+    if (location.IsConstant()) {
+      message_ << "C";
+    } else if (location.IsPair()) {
+      message_ << location.low() << "," << location.high();
+    } else {
+      message_ << location.reg();
+    }
+  }
+
   virtual void EmitMove(size_t index) {
     MoveOperands* move = moves_.Get(index);
     if (!message_.str().empty()) {
       message_ << " ";
     }
-    message_ << "("
-             << move->GetSource().reg()
-             << " -> "
-             << move->GetDestination().reg()
-             << ")";
+    message_ << "(";
+    Dump(move->GetSource());
+    message_ << " -> ";
+    Dump(move->GetDestination());
+    message_ << ")";
   }
 
   virtual void EmitSwap(size_t index) {
@@ -43,11 +53,11 @@
     if (!message_.str().empty()) {
       message_ << " ";
     }
-    message_ << "("
-             << move->GetSource().reg()
-             << " <-> "
-             << move->GetDestination().reg()
-             << ")";
+    message_ << "(";
+    Dump(move->GetSource());
+    message_ << " <-> ";
+    Dump(move->GetDestination());
+    message_ << ")";
   }
 
   virtual void SpillScratch(int reg ATTRIBUTE_UNUSED) {}
@@ -69,10 +79,10 @@
                                         size_t number_of_moves) {
   HParallelMove* moves = new (allocator) HParallelMove(allocator);
   for (size_t i = 0; i < number_of_moves; ++i) {
-    moves->AddMove(new (allocator) MoveOperands(
+    moves->AddMove(
         Location::RegisterLocation(operands[i][0]),
         Location::RegisterLocation(operands[i][1]),
-        nullptr));
+        nullptr);
   }
   return moves;
 }
@@ -116,16 +126,158 @@
 
   {
     TestParallelMoveResolver resolver(&allocator);
-    static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 1}};
+    static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}};
     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
-    ASSERT_STREQ("(4 <-> 1) (3 <-> 4) (2 <-> 3) (0 -> 1)", resolver.GetMessage().c_str());
+    ASSERT_STREQ("(4 <-> 0) (3 <-> 4) (2 <-> 3) (1 <-> 2)", resolver.GetMessage().c_str());
+  }
+}
+
+TEST(ParallelMoveTest, ConstantLast) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+  TestParallelMoveResolver resolver(&allocator);
+  HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+  moves->AddMove(
+      Location::ConstantLocation(new (&allocator) HIntConstant(0)),
+      Location::RegisterLocation(0),
+      nullptr);
+  moves->AddMove(
+      Location::RegisterLocation(1),
+      Location::RegisterLocation(2),
+      nullptr);
+  resolver.EmitNativeCode(moves);
+  ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
+}
+
+TEST(ParallelMoveTest, Pairs) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(4),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2 -> 4) (0,1 -> 2,3)", resolver.GetMessage().c_str());
   }
 
   {
     TestParallelMoveResolver resolver(&allocator);
-    static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 1}, {5, 4}};
-    resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
-    ASSERT_STREQ("(4 <-> 1) (3 <-> 4) (2 <-> 3) (0 -> 1) (5 -> 4)", resolver.GetMessage().c_str());
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(4),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2 -> 4) (0,1 -> 2,3)", resolver.GetMessage().c_str());
+  }
+
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(0),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
+  }
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(7),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(7),
+        Location::RegisterLocation(1),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
+  }
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(7),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(7),
+        Location::RegisterLocation(1),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
+  }
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(7),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(7),
+        Location::RegisterLocation(1),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
+  }
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(2, 3),
+        Location::RegisterPairLocation(0, 1),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2,3 <-> 0,1)", resolver.GetMessage().c_str());
+  }
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(2, 3),
+        Location::RegisterPairLocation(0, 1),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
   }
 }
 
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 7186dbe..12acd08 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -55,11 +55,10 @@
 
 void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
   bool needs_materialization = false;
-  if (!condition->HasOnlyOneUse()) {
+  if (!condition->GetUses().HasOnlyOneUse()) {
     needs_materialization = true;
   } else {
-    HUseListNode<HInstruction>* uses = condition->GetUses();
-    HInstruction* user = uses->GetUser();
+    HInstruction* user = condition->GetUses().GetFirst()->GetUser();
     if (!user->IsIf()) {
       needs_materialization = true;
     } else {
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 2c8166e..d2a21c8 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -55,7 +55,7 @@
     if (instruction->HasUses()) {
       PrintString(" [");
       bool first = true;
-      for (HUseIterator<HInstruction> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+      for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
         if (first) {
           first = false;
         } else {
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index da6b294..9cf8235 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -30,10 +30,11 @@
 static void TestCode(const uint16_t* data, const char* expected) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
   StringPrettyPrinter printer(graph);
   printer.VisitInsertionOrder();
   ASSERT_STREQ(expected, printer.str().c_str());
@@ -100,17 +101,16 @@
 TEST(PrettyPrinterTest, CFG3) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  5: SuspendCheck\n"
-    "  6: Goto 1\n"
+    "  4: SuspendCheck\n"
+    "  5: Goto 1\n"
     "BasicBlock 1, pred: 0, succ: 3\n"
     "  0: Goto 3\n"
     "BasicBlock 2, pred: 3, succ: 4\n"
     "  1: ReturnVoid\n"
     "BasicBlock 3, pred: 1, succ: 2\n"
-    "  2: SuspendCheck\n"
-    "  3: Goto 2\n"
+    "  2: Goto 2\n"
     "BasicBlock 4, pred: 2\n"
-    "  4: Exit\n";
+    "  3: Exit\n";
 
   const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::GOTO | 0x200,
@@ -160,15 +160,14 @@
 TEST(PrettyPrinterTest, CFG5) {
   const char* expected =
     "BasicBlock 0, succ: 1\n"
-    "  4: SuspendCheck\n"
-    "  5: Goto 1\n"
+    "  3: SuspendCheck\n"
+    "  4: Goto 1\n"
     "BasicBlock 1, pred: 0, 2, succ: 3\n"
     "  0: ReturnVoid\n"
     "BasicBlock 2, succ: 1\n"
-    "  1: SuspendCheck\n"
-    "  2: Goto 1\n"
+    "  1: Goto 1\n"
     "BasicBlock 3, pred: 1\n"
-    "  3: Exit\n";
+    "  2: Exit\n";
 
   const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
     Instruction::RETURN_VOID,
diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc
similarity index 88%
rename from compiler/optimizing/ssa_type_propagation.cc
rename to compiler/optimizing/primitive_type_propagation.cc
index cb5ce20..7e274f6 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/primitive_type_propagation.cc
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#include "ssa_builder.h"
-#include "ssa_type_propagation.h"
+#include "primitive_type_propagation.h"
 
 #include "nodes.h"
+#include "ssa_builder.h"
 
 namespace art {
 
@@ -39,7 +39,7 @@
 
 // Re-compute and update the type of the instruction. Returns
 // whether or not the type was changed.
-bool SsaTypePropagation::UpdateType(HPhi* phi) {
+bool PrimitiveTypePropagation::UpdateType(HPhi* phi) {
   Primitive::Type existing = phi->GetType();
 
   Primitive::Type new_type = existing;
@@ -67,14 +67,14 @@
   return existing != new_type;
 }
 
-void SsaTypePropagation::Run() {
+void PrimitiveTypePropagation::Run() {
   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     VisitBasicBlock(it.Current());
   }
   ProcessWorklist();
 }
 
-void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+void PrimitiveTypePropagation::VisitBasicBlock(HBasicBlock* block) {
   if (block->IsLoopHeader()) {
     for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
       HPhi* phi = it.Current()->AsPhi();
@@ -100,7 +100,7 @@
   }
 }
 
-void SsaTypePropagation::ProcessWorklist() {
+void PrimitiveTypePropagation::ProcessWorklist() {
   while (!worklist_.IsEmpty()) {
     HPhi* instruction = worklist_.Pop();
     if (UpdateType(instruction)) {
@@ -109,12 +109,12 @@
   }
 }
 
-void SsaTypePropagation::AddToWorklist(HPhi* instruction) {
+void PrimitiveTypePropagation::AddToWorklist(HPhi* instruction) {
   worklist_.Add(instruction);
 }
 
-void SsaTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
-  for (HUseIterator<HInstruction> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+void PrimitiveTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
+  for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
     HPhi* phi = it.Current()->GetUser()->AsPhi();
     if (phi != nullptr) {
       AddToWorklist(phi);
diff --git a/compiler/optimizing/ssa_type_propagation.h b/compiler/optimizing/primitive_type_propagation.h
similarity index 72%
rename from compiler/optimizing/ssa_type_propagation.h
rename to compiler/optimizing/primitive_type_propagation.h
index f4d3d63..1374cbb 100644
--- a/compiler/optimizing/ssa_type_propagation.h
+++ b/compiler/optimizing/primitive_type_propagation.h
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
-#define ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
+#ifndef ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
 
 #include "nodes.h"
 
 namespace art {
 
-// Compute and propagate types of phis in the graph.
-class SsaTypePropagation : public ValueObject {
+// Compute and propagate primitive types of phis in the graph.
+class PrimitiveTypePropagation : public ValueObject {
  public:
-  explicit SsaTypePropagation(HGraph* graph)
+  explicit PrimitiveTypePropagation(HGraph* graph)
       : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
 
   void Run();
@@ -41,9 +41,9 @@
 
   static constexpr size_t kDefaultWorklistSize = 8;
 
-  DISALLOW_COPY_AND_ASSIGN(SsaTypePropagation);
+  DISALLOW_COPY_AND_ASSIGN(PrimitiveTypePropagation);
 };
 
 }  // namespace art
 
-#endif  // ART_COMPILER_OPTIMIZING_SSA_TYPE_PROPAGATION_H_
+#endif  // ART_COMPILER_OPTIMIZING_PRIMITIVE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
new file mode 100644
index 0000000..24e6837
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reference_type_propagation.h"
+
+namespace art {
+
+// TODO: Only do the analysis on reference types. We currently have to handle
+// the `null` constant, that is represented as a `HIntConstant` and therefore
+// has the Primitive::kPrimInt type.
+
+void ReferenceTypePropagation::Run() {
+  // Compute null status for instructions.
+
+  // To properly propagate not-null info we need to visit in the dominator-based order.
+  // Reverse post order guarantees a node's dominators are visited first.
+  // We take advantage of this order in `VisitBasicBlock`.
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    VisitBasicBlock(it.Current());
+  }
+  ProcessWorklist();
+}
+
+// Re-computes and updates the nullability of the instruction. Returns whether or
+// not the nullability was changed.
+bool ReferenceTypePropagation::UpdateNullability(HPhi* phi) {
+  bool existing_can_be_null = phi->CanBeNull();
+  bool new_can_be_null = false;
+  for (size_t i = 0; i < phi->InputCount(); i++) {
+    new_can_be_null |= phi->InputAt(i)->CanBeNull();
+  }
+  phi->SetCanBeNull(new_can_be_null);
+
+  return existing_can_be_null != new_can_be_null;
+}
+
+
+void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
+  if (block->IsLoopHeader()) {
+    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+      // Set the initial type for the phi. Use the non back edge input for reaching
+      // a fixed point faster.
+      HPhi* phi = it.Current()->AsPhi();
+      AddToWorklist(phi);
+      phi->SetCanBeNull(phi->InputAt(0)->CanBeNull());
+    }
+  } else {
+    for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+      // Eagerly compute the type of the phi, for quicker convergence. Note
+      // that we don't need to add users to the worklist because we are
+      // doing a reverse post-order visit, therefore either the phi users are
+      // non-loop phi and will be visited later in the visit, or are loop-phis,
+      // and they are already in the work list.
+      UpdateNullability(it.Current()->AsPhi());
+    }
+  }
+}
+
+void ReferenceTypePropagation::ProcessWorklist() {
+  while (!worklist_.IsEmpty()) {
+    HPhi* instruction = worklist_.Pop();
+    if (UpdateNullability(instruction)) {
+      AddDependentInstructionsToWorklist(instruction);
+    }
+  }
+}
+
+void ReferenceTypePropagation::AddToWorklist(HPhi* instruction) {
+  worklist_.Add(instruction);
+}
+
+void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HPhi* instruction) {
+  for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+    HPhi* phi = it.Current()->GetUser()->AsPhi();
+    if (phi != nullptr) {
+      AddToWorklist(phi);
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
new file mode 100644
index 0000000..a74319d
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Propagates reference types to instructions.
+ * TODO: Currently only nullability is computed.
+ */
+class ReferenceTypePropagation : public HOptimization {
+ public:
+  explicit ReferenceTypePropagation(HGraph* graph)
+    : HOptimization(graph, true, "reference_type_propagation"),
+      worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+
+  void Run() OVERRIDE;
+
+ private:
+  void VisitBasicBlock(HBasicBlock* block);
+  void ProcessWorklist();
+  void AddToWorklist(HPhi* phi);
+  void AddDependentInstructionsToWorklist(HPhi* phi);
+  bool UpdateNullability(HPhi* phi);
+
+  GrowableArray<HPhi*> worklist_;
+
+  static constexpr size_t kDefaultWorklistSize = 8;
+
+  DISALLOW_COPY_AND_ASSIGN(ReferenceTypePropagation);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index a6c0635..bfbe63f 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -27,6 +27,12 @@
 static constexpr size_t kMaxLifetimePosition = -1;
 static constexpr size_t kDefaultNumberOfSpillSlots = 4;
 
+// For simplicity, we implement register pairs as (reg, reg + 1).
+// Note that this is a requirement for double registers on ARM, since we
+// allocate SRegister.
+static int GetHighForLowRegister(int reg) { return reg + 1; }
+static bool IsLowRegister(int reg) { return (reg & 1) == 0; }
+
 RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator,
                                      CodeGenerator* codegen,
                                      const SsaLivenessAnalysis& liveness)
@@ -50,8 +56,10 @@
         blocked_core_registers_(codegen->GetBlockedCoreRegisters()),
         blocked_fp_registers_(codegen->GetBlockedFloatingPointRegisters()),
         reserved_out_slots_(0),
-        maximum_number_of_live_registers_(0) {
-  codegen->SetupBlockedRegisters();
+        maximum_number_of_live_core_registers_(0),
+        maximum_number_of_live_fp_registers_(0) {
+  static constexpr bool kIsBaseline = false;
+  codegen->SetupBlockedRegisters(kIsBaseline);
   physical_core_register_intervals_.SetSize(codegen->GetNumberOfCoreRegisters());
   physical_fp_register_intervals_.SetSize(codegen->GetNumberOfFloatingPointRegisters());
   // Always reserve for the current method and the graph's max out registers.
@@ -64,15 +72,18 @@
   if (!Supports(instruction_set)) {
     return false;
   }
+  if (instruction_set == kArm64
+      || instruction_set == kX86_64
+      || instruction_set == kArm
+      || instruction_set == kThumb2) {
+    return true;
+  }
   for (size_t i = 0, e = graph.GetBlocks().Size(); i < e; ++i) {
     for (HInstructionIterator it(graph.GetBlocks().Get(i)->GetInstructions());
          !it.Done();
          it.Advance()) {
       HInstruction* current = it.Current();
-      if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false;
-      if ((current->GetType() == Primitive::kPrimFloat
-           || current->GetType() == Primitive::kPrimDouble)
-          && instruction_set != kX86_64) {
+      if (instruction_set == kX86 && current->GetType() == Primitive::kPrimLong) {
         return false;
       }
     }
@@ -128,7 +139,7 @@
       : physical_fp_register_intervals_.Get(reg);
   Primitive::Type type = location.IsRegister()
       ? Primitive::kPrimInt
-      : Primitive::kPrimDouble;
+      : Primitive::kPrimFloat;
   if (interval == nullptr) {
     interval = LiveInterval::MakeFixedInterval(allocator_, reg, type);
     if (location.IsRegister()) {
@@ -171,9 +182,6 @@
   }
   LinearScan();
 
-  size_t saved_maximum_number_of_live_registers = maximum_number_of_live_registers_;
-  maximum_number_of_live_registers_ = 0;
-
   inactive_.Reset();
   active_.Reset();
   handled_.Reset();
@@ -193,7 +201,6 @@
     }
   }
   LinearScan();
-  maximum_number_of_live_registers_ += saved_maximum_number_of_live_registers;
 }
 
 void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
@@ -224,6 +231,12 @@
               LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimDouble);
           temp_intervals_.Add(interval);
           interval->AddRange(position, position + 1);
+          if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
+            interval->AddHighInterval(true);
+            LiveInterval* high = interval->GetHighInterval();
+            temp_intervals_.Add(high);
+            unhandled_fp_intervals_.Add(high);
+          }
           unhandled_fp_intervals_.Add(interval);
           break;
         }
@@ -239,8 +252,13 @@
       && (instruction->GetType() != Primitive::kPrimFloat);
 
   if (locations->CanCall()) {
-    if (!instruction->IsSuspendCheck()) {
-      codegen_->MarkNotLeaf();
+    if (codegen_->IsLeafMethod()) {
+      // TODO: We do this here because we do not want the suspend check to artificially
+      // create live registers. We should find another place, but this is currently the
+      // simplest.
+      DCHECK(instruction->IsSuspendCheckEntry());
+      instruction->GetBlock()->RemoveInstruction(instruction);
+      return;
     }
     safepoints_.Add(instruction);
     if (locations->OnlyCallsOnSlowPath()) {
@@ -262,14 +280,18 @@
   if (locations->WillCall()) {
     // Block all registers.
     for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
-      BlockRegister(Location::RegisterLocation(i),
-                    position,
-                    position + 1);
+      if (!codegen_->IsCoreCalleeSaveRegister(i)) {
+        BlockRegister(Location::RegisterLocation(i),
+                      position,
+                      position + 1);
+      }
     }
     for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) {
-      BlockRegister(Location::FpuRegisterLocation(i),
-                    position,
-                    position + 1);
+      if (!codegen_->IsFloatingPointCalleeSaveRegister(i)) {
+        BlockRegister(Location::FpuRegisterLocation(i),
+                      position,
+                      position + 1);
+      }
     }
   }
 
@@ -277,6 +299,9 @@
     Location input = locations->InAt(i);
     if (input.IsRegister() || input.IsFpuRegister()) {
       BlockRegister(input, position, position + 1);
+    } else if (input.IsPair()) {
+      BlockRegister(input.ToLow(), position, position + 1);
+      BlockRegister(input.ToHigh(), position, position + 1);
     }
   }
 
@@ -289,6 +314,10 @@
 
   DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
 
+  if (codegen_->NeedsTwoRegisters(current->GetType())) {
+    current->AddHighInterval();
+  }
+
   // Some instructions define their output in fixed register/stack slot. We need
   // to ensure we know these locations before doing register allocation. For a
   // given register, we create an interval that covers these locations. The register
@@ -302,14 +331,30 @@
     if (first.IsRegister() || first.IsFpuRegister()) {
       current->SetFrom(position + 1);
       current->SetRegister(first.reg());
+    } else if (first.IsPair()) {
+      current->SetFrom(position + 1);
+      current->SetRegister(first.low());
+      LiveInterval* high = current->GetHighInterval();
+      high->SetRegister(first.high());
+      high->SetFrom(position + 1);
     }
   } else if (output.IsRegister() || output.IsFpuRegister()) {
     // Shift the interval's start by one to account for the blocked register.
     current->SetFrom(position + 1);
     current->SetRegister(output.reg());
     BlockRegister(output, position, position + 1);
+  } else if (output.IsPair()) {
+    current->SetFrom(position + 1);
+    current->SetRegister(output.low());
+    LiveInterval* high = current->GetHighInterval();
+    high->SetRegister(output.high());
+    high->SetFrom(position + 1);
+    BlockRegister(output.ToLow(), position, position + 1);
+    BlockRegister(output.ToHigh(), position, position + 1);
   } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
     current->SetSpillSlot(output.GetStackIndex());
+  } else {
+    DCHECK(output.IsUnallocated() || output.IsConstant());
   }
 
   // If needed, add interval to the list of unhandled intervals.
@@ -445,6 +490,9 @@
         BitVector* liveness_of_register = liveness_of_values.Get(current->GetRegister());
         for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
           if (liveness_of_register->IsBitSet(j)) {
+            if (current->IsUsingInputRegister() && current->CanUseInputRegister()) {
+              continue;
+            }
             if (log_fatal_on_failure) {
               std::ostringstream message;
               message << "Register conflict at " << j << " ";
@@ -514,6 +562,7 @@
     LiveInterval* current = unhandled_->Pop();
     DCHECK(!current->IsFixed() && !current->HasSpillSlot());
     DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart());
+    DCHECK(!current->IsLowInterval() || unhandled_->Peek()->IsHighInterval());
 
     size_t position = current->GetStart();
 
@@ -558,12 +607,24 @@
     if (current->IsSlowPathSafepoint()) {
       // Synthesized interval to record the maximum number of live registers
       // at safepoints. No need to allocate a register for it.
-      maximum_number_of_live_registers_ =
-          std::max(maximum_number_of_live_registers_, active_.Size());
+      if (processing_core_registers_) {
+        maximum_number_of_live_core_registers_ =
+          std::max(maximum_number_of_live_core_registers_, active_.Size());
+      } else {
+        maximum_number_of_live_fp_registers_ =
+          std::max(maximum_number_of_live_fp_registers_, active_.Size());
+      }
       DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() > current->GetStart());
       continue;
     }
 
+    if (current->IsHighInterval() && !current->GetLowInterval()->HasRegister()) {
+      DCHECK(!current->HasRegister());
+      // Allocating the low part was unsucessful. The splitted interval for the high part
+      // will be handled next (it is in the `unhandled_` list).
+      continue;
+    }
+
     // (4) Try to find an available register.
     bool success = TryAllocateFreeReg(current);
 
@@ -575,7 +636,36 @@
     // (6) If the interval had a register allocated, add it to the list of active
     //     intervals.
     if (success) {
+      codegen_->AddAllocatedRegister(processing_core_registers_
+          ? Location::RegisterLocation(current->GetRegister())
+          : Location::FpuRegisterLocation(current->GetRegister()));
       active_.Add(current);
+      if (current->HasHighInterval() && !current->GetHighInterval()->HasRegister()) {
+        current->GetHighInterval()->SetRegister(GetHighForLowRegister(current->GetRegister()));
+      }
+    }
+  }
+}
+
+static void FreeIfNotCoverAt(LiveInterval* interval, size_t position, size_t* free_until) {
+  DCHECK(!interval->IsHighInterval());
+  // Note that the same instruction may occur multiple times in the input list,
+  // so `free_until` may have changed already.
+  if (interval->IsDeadAt(position)) {
+    // Set the register to be free. Note that inactive intervals might later
+    // update this.
+    free_until[interval->GetRegister()] = kMaxLifetimePosition;
+    if (interval->HasHighInterval()) {
+      DCHECK(interval->GetHighInterval()->IsDeadAt(position));
+      free_until[interval->GetHighInterval()->GetRegister()] = kMaxLifetimePosition;
+    }
+  } else if (!interval->Covers(position)) {
+    // The interval becomes inactive at `defined_by`. We make its register
+    // available only until the next use strictly after `defined_by`.
+    free_until[interval->GetRegister()] = interval->FirstUseAfter(position);
+    if (interval->HasHighInterval()) {
+      DCHECK(!interval->GetHighInterval()->Covers(position));
+      free_until[interval->GetHighInterval()->GetRegister()] = free_until[interval->GetRegister()];
     }
   }
 }
@@ -597,6 +687,32 @@
     free_until[interval->GetRegister()] = 0;
   }
 
+  // An interval that starts an instruction (that is, it is not split), may
+  // re-use the registers used by the inputs of that instruciton, based on the
+  // location summary.
+  HInstruction* defined_by = current->GetDefinedBy();
+  if (defined_by != nullptr && !current->IsSplit()) {
+    LocationSummary* locations = defined_by->GetLocations();
+    if (!locations->OutputCanOverlapWithInputs() && locations->Out().IsUnallocated()) {
+      for (HInputIterator it(defined_by); !it.Done(); it.Advance()) {
+        // Take the last interval of the input. It is the location of that interval
+        // that will be used at `defined_by`.
+        LiveInterval* interval = it.Current()->GetLiveInterval()->GetLastSibling();
+        // Note that interval may have not been processed yet.
+        // TODO: Handle non-split intervals last in the work list.
+        if (interval->HasRegister() && interval->SameRegisterKind(*current)) {
+          // The input must be live until the end of `defined_by`, to comply to
+          // the linear scan algorithm. So we use `defined_by`'s end lifetime
+          // position to check whether the input is dead or is inactive after
+          // `defined_by`.
+          DCHECK(interval->Covers(defined_by->GetLifetimePosition()));
+          size_t position = defined_by->GetLifetimePosition() + 1;
+          FreeIfNotCoverAt(interval, position, free_until);
+        }
+      }
+    }
+  }
+
   // For each inactive interval, set its register to be free until
   // the next intersection with `current`.
   for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
@@ -624,30 +740,35 @@
     }
   }
 
-  int reg = -1;
+  int reg = kNoRegister;
   if (current->HasRegister()) {
     // Some instructions have a fixed register output.
     reg = current->GetRegister();
-    DCHECK_NE(free_until[reg], 0u);
+    if (free_until[reg] == 0) {
+      DCHECK(current->IsHighInterval());
+      // AllocateBlockedReg will spill the holder of the register.
+      return false;
+    }
   } else {
+    DCHECK(!current->IsHighInterval());
     int hint = current->FindFirstRegisterHint(free_until);
     if (hint != kNoRegister) {
       DCHECK(!IsBlocked(hint));
       reg = hint;
+    } else if (current->IsLowInterval()) {
+      reg = FindAvailableRegisterPair(free_until, current->GetStart());
     } else {
-      // Pick the register that is free the longest.
-      for (size_t i = 0; i < number_of_registers_; ++i) {
-        if (IsBlocked(i)) continue;
-        if (reg == -1 || free_until[i] > free_until[reg]) {
-          reg = i;
-          if (free_until[i] == kMaxLifetimePosition) break;
-        }
-      }
+      reg = FindAvailableRegister(free_until);
     }
   }
 
+  DCHECK_NE(reg, kNoRegister);
   // If we could not find a register, we need to spill.
-  if (reg == -1 || free_until[reg] == 0) {
+  if (free_until[reg] == 0) {
+    return false;
+  }
+
+  if (current->IsLowInterval() && free_until[GetHighForLowRegister(reg)] == 0) {
     return false;
   }
 
@@ -669,6 +790,66 @@
       : blocked_fp_registers_[reg];
 }
 
+int RegisterAllocator::FindAvailableRegisterPair(size_t* next_use, size_t starting_at) const {
+  int reg = kNoRegister;
+  // Pick the register pair that is used the last.
+  for (size_t i = 0; i < number_of_registers_; ++i) {
+    if (IsBlocked(i)) continue;
+    if (!IsLowRegister(i)) continue;
+    int high_register = GetHighForLowRegister(i);
+    if (IsBlocked(high_register)) continue;
+    int existing_high_register = GetHighForLowRegister(reg);
+    if ((reg == kNoRegister) || (next_use[i] >= next_use[reg]
+                        && next_use[high_register] >= next_use[existing_high_register])) {
+      reg = i;
+      if (next_use[i] == kMaxLifetimePosition
+          && next_use[high_register] == kMaxLifetimePosition) {
+        break;
+      }
+    } else if (next_use[reg] <= starting_at || next_use[existing_high_register] <= starting_at) {
+      // If one of the current register is known to be unavailable, just unconditionally
+      // try a new one.
+      reg = i;
+    }
+  }
+  return reg;
+}
+
+int RegisterAllocator::FindAvailableRegister(size_t* next_use) const {
+  int reg = kNoRegister;
+  // Pick the register that is used the last.
+  for (size_t i = 0; i < number_of_registers_; ++i) {
+    if (IsBlocked(i)) continue;
+    if (reg == kNoRegister || next_use[i] > next_use[reg]) {
+      reg = i;
+      if (next_use[i] == kMaxLifetimePosition) break;
+    }
+  }
+  return reg;
+}
+
+bool RegisterAllocator::TrySplitNonPairIntervalAt(size_t position,
+                                                  size_t first_register_use,
+                                                  size_t* next_use) {
+  for (size_t i = 0, e = active_.Size(); i < e; ++i) {
+    LiveInterval* active = active_.Get(i);
+    DCHECK(active->HasRegister());
+    // Split the first interval found.
+    if (first_register_use <= next_use[active->GetRegister()]
+        && !active->IsLowInterval()
+        && !active->IsHighInterval()) {
+      LiveInterval* split = Split(active, position);
+      active_.DeleteAt(i);
+      if (split != active) {
+        handled_.Add(active);
+      }
+      AddSorted(unhandled_, split);
+      return true;
+    }
+  }
+  return false;
+}
+
 // Find the register that is used the last, and spill the interval
 // that holds it. If the first use of `current` is after that register
 // we spill `current` instead.
@@ -729,24 +910,50 @@
     }
   }
 
-  // Pick the register that is used the last.
-  int reg = -1;
-  for (size_t i = 0; i < number_of_registers_; ++i) {
-    if (IsBlocked(i)) continue;
-    if (reg == -1 || next_use[i] > next_use[reg]) {
-      reg = i;
-      if (next_use[i] == kMaxLifetimePosition) break;
-    }
+  int reg = kNoRegister;
+  bool should_spill = false;
+  if (current->HasRegister()) {
+    DCHECK(current->IsHighInterval());
+    reg = current->GetRegister();
+    // When allocating the low part, we made sure the high register was available.
+    DCHECK_LT(first_register_use, next_use[reg]);
+  } else if (current->IsLowInterval()) {
+    reg = FindAvailableRegisterPair(next_use, current->GetStart());
+    // We should spill if both registers are not available.
+    should_spill = (first_register_use >= next_use[reg])
+      || (first_register_use >= next_use[GetHighForLowRegister(reg)]);
+  } else {
+    DCHECK(!current->IsHighInterval());
+    reg = FindAvailableRegister(next_use);
+    should_spill = (first_register_use >= next_use[reg]);
   }
 
-  if (first_register_use >= next_use[reg]) {
-    // If the first use of that instruction is after the last use of the found
-    // register, we split this interval just before its first register use.
-    AllocateSpillSlotFor(current);
-    LiveInterval* split = Split(current, first_register_use - 1);
-    DCHECK_NE(current, split) << "There is not enough registers available for "
-      << split->GetParent()->GetDefinedBy()->DebugName();
-    AddSorted(unhandled_, split);
+  DCHECK_NE(reg, kNoRegister);
+  if (should_spill) {
+    DCHECK(!current->IsHighInterval());
+    bool is_allocation_at_use_site = (current->GetStart() == (first_register_use - 1));
+    if (current->IsLowInterval()
+        && is_allocation_at_use_site
+        && TrySplitNonPairIntervalAt(current->GetStart(), first_register_use, next_use)) {
+      // If we're allocating a register for `current` because the instruction at
+      // that position requires it, but we think we should spill, then there are
+      // non-pair intervals blocking the allocation. We split the first
+      // interval found, and put ourselves first in the `unhandled_` list.
+      LiveInterval* existing = unhandled_->Peek();
+      DCHECK(existing->IsHighInterval());
+      DCHECK_EQ(existing->GetLowInterval(), current);
+      unhandled_->Add(current);
+    } else {
+      // If the first use of that instruction is after the last use of the found
+      // register, we split this interval just before its first register use.
+      AllocateSpillSlotFor(current);
+      LiveInterval* split = Split(current, first_register_use - 1);
+      DCHECK_NE(current, split) << "There is not enough registers available for "
+        << split->GetParent()->GetDefinedBy()->DebugName() << " "
+        << split->GetParent()->GetDefinedBy()->GetId()
+        << " at " << first_register_use - 1;
+      AddSorted(unhandled_, split);
+    }
     return false;
   } else {
     // Use this register and spill the active and inactives interval that
@@ -759,8 +966,27 @@
         DCHECK(!active->IsFixed());
         LiveInterval* split = Split(active, current->GetStart());
         active_.DeleteAt(i);
-        handled_.Add(active);
+        if (split != active) {
+          handled_.Add(active);
+        }
         AddSorted(unhandled_, split);
+
+        if (active->IsLowInterval() || active->IsHighInterval()) {
+          LiveInterval* other_half = active->IsLowInterval()
+              ? active->GetHighInterval()
+              : active->GetLowInterval();
+          // We also need to remove the other half from the list of actives.
+          bool found = false;
+          for (size_t j = 0; j < active_.Size(); ++j) {
+            if (active_.Get(j) == other_half) {
+              found = true;
+              active_.DeleteAt(j);
+              handled_.Add(other_half);
+              break;
+            }
+          }
+          DCHECK(found);
+        }
         break;
       }
     }
@@ -780,14 +1006,38 @@
         if (next_intersection != kNoLifetime) {
           if (inactive->IsFixed()) {
             LiveInterval* split = Split(current, next_intersection);
+            DCHECK_NE(split, current);
             AddSorted(unhandled_, split);
           } else {
-            LiveInterval* split = Split(inactive, next_intersection);
+            // Split at the start of `current`, which will lead to splitting
+            // at the end of the lifetime hole of `inactive`.
+            LiveInterval* split = Split(inactive, current->GetStart());
+            // If it's inactive, it must start before the current interval.
+            DCHECK_NE(split, inactive);
             inactive_.DeleteAt(i);
             --i;
             --e;
             handled_.Add(inactive);
             AddSorted(unhandled_, split);
+
+            if (inactive->IsLowInterval() || inactive->IsHighInterval()) {
+              LiveInterval* other_half = inactive->IsLowInterval()
+                  ? inactive->GetHighInterval()
+                  : inactive->GetLowInterval();
+
+              // We also need to remove the other half from the list of inactives.
+              bool found = false;
+              for (size_t j = 0; j < inactive_.Size(); ++j) {
+                if (inactive_.Get(j) == other_half) {
+                  found = true;
+                  inactive_.DeleteAt(j);
+                  --e;
+                  handled_.Add(other_half);
+                  break;
+                }
+              }
+              DCHECK(found);
+            }
           }
         }
       }
@@ -802,7 +1052,8 @@
   size_t insert_at = 0;
   for (size_t i = array->Size(); i > 0; --i) {
     LiveInterval* current = array->Get(i - 1);
-    if (current->StartsAfter(interval)) {
+    // High intervals must be processed right after their low equivalent.
+    if (current->StartsAfter(interval) && !current->IsHighInterval()) {
       insert_at = i;
       break;
     } else if ((current->GetStart() == interval->GetStart()) && current->IsSlowPathSafepoint()) {
@@ -813,23 +1064,49 @@
       break;
     }
   }
+
   array->InsertAt(insert_at, interval);
+  // Insert the high interval before the low, to ensure the low is processed before.
+  if (interval->HasHighInterval()) {
+    array->InsertAt(insert_at, interval->GetHighInterval());
+  } else if (interval->HasLowInterval()) {
+    array->InsertAt(insert_at + 1, interval->GetLowInterval());
+  }
 }
 
 LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
-  DCHECK(position >= interval->GetStart());
+  DCHECK_GE(position, interval->GetStart());
   DCHECK(!interval->IsDeadAt(position));
   if (position == interval->GetStart()) {
     // Spill slot will be allocated when handling `interval` again.
     interval->ClearRegister();
+    if (interval->HasHighInterval()) {
+      interval->GetHighInterval()->ClearRegister();
+    } else if (interval->HasLowInterval()) {
+      interval->GetLowInterval()->ClearRegister();
+    }
     return interval;
   } else {
     LiveInterval* new_interval = interval->SplitAt(position);
+    if (interval->HasHighInterval()) {
+      LiveInterval* high = interval->GetHighInterval()->SplitAt(position);
+      new_interval->SetHighInterval(high);
+      high->SetLowInterval(new_interval);
+    } else if (interval->HasLowInterval()) {
+      LiveInterval* low = interval->GetLowInterval()->SplitAt(position);
+      new_interval->SetLowInterval(low);
+      low->SetHighInterval(new_interval);
+    }
     return new_interval;
   }
 }
 
 void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) {
+  if (interval->IsHighInterval()) {
+    // The low interval will contain the spill slot.
+    return;
+  }
+
   LiveInterval* parent = interval->GetParent();
 
   // An instruction gets a spill slot for its entire lifetime. If the parent
@@ -895,7 +1172,9 @@
 
 static bool IsValidDestination(Location destination) {
   return destination.IsRegister()
+      || destination.IsRegisterPair()
       || destination.IsFpuRegister()
+      || destination.IsFpuRegisterPair()
       || destination.IsStackSlot()
       || destination.IsDoubleStackSlot();
 }
@@ -903,7 +1182,6 @@
 void RegisterAllocator::AddInputMoveFor(HInstruction* user,
                                         Location source,
                                         Location destination) const {
-  DCHECK(IsValidDestination(destination));
   if (source.Equals(destination)) return;
 
   DCHECK(!user->IsPhi());
@@ -920,7 +1198,7 @@
     move = previous->AsParallelMove();
   }
   DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition());
-  move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr));
+  move->AddMove(source, destination, nullptr);
 }
 
 static bool IsInstructionStart(size_t position) {
@@ -935,7 +1213,7 @@
                                              HInstruction* instruction,
                                              Location source,
                                              Location destination) const {
-  DCHECK(IsValidDestination(destination));
+  DCHECK(IsValidDestination(destination)) << destination;
   if (source.Equals(destination)) return;
 
   HInstruction* at = liveness_.GetInstructionFromPosition(position / 2);
@@ -992,14 +1270,14 @@
     }
   }
   DCHECK_EQ(move->GetLifetimePosition(), position);
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
                                                    HInstruction* instruction,
                                                    Location source,
                                                    Location destination) const {
-  DCHECK(IsValidDestination(destination));
+  DCHECK(IsValidDestination(destination)) << destination;
   if (source.Equals(destination)) return;
 
   DCHECK_EQ(block->GetSuccessors().Size(), 1u);
@@ -1022,14 +1300,14 @@
   } else {
     move = previous->AsParallelMove();
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
                                                     HInstruction* instruction,
                                                     Location source,
                                                     Location destination) const {
-  DCHECK(IsValidDestination(destination));
+  DCHECK(IsValidDestination(destination)) << destination;
   if (source.Equals(destination)) return;
 
   HInstruction* first = block->GetFirstInstruction();
@@ -1041,13 +1319,13 @@
     move->SetLifetimePosition(block->GetLifetimeStart());
     block->InsertInstructionBefore(move, first);
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
                                         Location source,
                                         Location destination) const {
-  DCHECK(IsValidDestination(destination));
+  DCHECK(IsValidDestination(destination)) << destination;
   if (source.Equals(destination)) return;
 
   if (instruction->IsPhi()) {
@@ -1065,7 +1343,7 @@
     move->SetLifetimePosition(position);
     instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
@@ -1073,9 +1351,7 @@
   if (current->HasSpillSlot() && current->HasRegister()) {
     // We spill eagerly, so move must be at definition.
     InsertMoveAfter(interval->GetDefinedBy(),
-                    interval->IsFloatingPoint()
-                        ? Location::FpuRegisterLocation(interval->GetRegister())
-                        : Location::RegisterLocation(interval->GetRegister()),
+                    interval->ToLocation(),
                     interval->NeedsTwoSpillSlots()
                         ? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot())
                         : Location::StackSlot(interval->GetParent()->GetSpillSlot()));
@@ -1095,10 +1371,17 @@
         locations->SetEnvironmentAt(use->GetInputIndex(), source);
       } else {
         Location expected_location = locations->InAt(use->GetInputIndex());
-        if (expected_location.IsUnallocated()) {
-          locations->SetInAt(use->GetInputIndex(), source);
-        } else if (!expected_location.IsConstant()) {
-          AddInputMoveFor(use->GetUser(), source, expected_location);
+        // The expected (actual) location may be invalid in case the input is unused. Currently
+        // this only happens for intrinsics.
+        if (expected_location.IsValid()) {
+          if (expected_location.IsUnallocated()) {
+            locations->SetInAt(use->GetInputIndex(), source);
+          } else if (!expected_location.IsConstant()) {
+            AddInputMoveFor(use->GetUser(), source, expected_location);
+          }
+        } else {
+          DCHECK(use->GetUser()->IsInvoke());
+          DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone);
         }
       }
       use = use->GetNext();
@@ -1135,8 +1418,11 @@
       switch (source.GetKind()) {
         case Location::kRegister: {
           locations->AddLiveRegister(source);
-          DCHECK_LE(locations->GetNumberOfLiveRegisters(), maximum_number_of_live_registers_);
-
+          if (kIsDebugBuild && locations->OnlyCallsOnSlowPath()) {
+            DCHECK_LE(locations->GetNumberOfLiveRegisters(),
+                      maximum_number_of_live_core_registers_ +
+                      maximum_number_of_live_fp_registers_);
+          }
           if (current->GetType() == Primitive::kPrimNot) {
             locations->SetRegisterBit(source.reg());
           }
@@ -1146,6 +1432,13 @@
           locations->AddLiveRegister(source);
           break;
         }
+
+        case Location::kRegisterPair:
+        case Location::kFpuRegisterPair: {
+          locations->AddLiveRegister(source.ToLow());
+          locations->AddLiveRegister(source.ToHigh());
+          break;
+        }
         case Location::kStackSlot:  // Fall-through
         case Location::kDoubleStackSlot:  // Fall-through
         case Location::kConstant: {
@@ -1223,8 +1516,11 @@
 }
 
 void RegisterAllocator::Resolve() {
-  codegen_->ComputeFrameSize(
-      spill_slots_.Size(), maximum_number_of_live_registers_, reserved_out_slots_);
+  codegen_->InitializeCodeGeneration(spill_slots_.Size(),
+                                     maximum_number_of_live_core_registers_,
+                                     maximum_number_of_live_fp_registers_,
+                                     reserved_out_slots_,
+                                     liveness_.GetLinearOrder());
 
   // Adjust the Out Location of instructions.
   // TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
@@ -1258,7 +1554,7 @@
           DCHECK(locations->InAt(0).Equals(source));
         }
       }
-      locations->SetOut(source);
+      locations->UpdateOut(source);
     } else {
       DCHECK(source.Equals(location));
     }
@@ -1305,6 +1601,10 @@
   size_t temp_index = 0;
   for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
     LiveInterval* temp = temp_intervals_.Get(i);
+    if (temp->IsHighInterval()) {
+      // High intervals can be skipped, they are already handled by the low interval.
+      continue;
+    }
     HInstruction* at = liveness_.GetTempUser(temp);
     if (at != current) {
       temp_index = 0;
@@ -1318,14 +1618,14 @@
         break;
 
       case Primitive::kPrimDouble:
-        // TODO: Support the case of ARM, where a double value
-        // requires an FPU register pair (note that the ARM back end
-        // does not yet use this register allocator when a method uses
-        // floats or doubles).
-        DCHECK(codegen_->GetInstructionSet() != kArm
-               && codegen_->GetInstructionSet() != kThumb2);
-        locations->SetTempAt(
-            temp_index++, Location::FpuRegisterLocation(temp->GetRegister()));
+        if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
+          Location location = Location::FpuRegisterPairLocation(
+              temp->GetRegister(), temp->GetHighInterval()->GetRegister());
+          locations->SetTempAt(temp_index++, location);
+        } else {
+          locations->SetTempAt(
+              temp_index++, Location::FpuRegisterLocation(temp->GetRegister()));
+        }
         break;
 
       default:
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 976ee39..b8f70bd 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -67,10 +67,11 @@
 
   static bool CanAllocateRegistersFor(const HGraph& graph, InstructionSet instruction_set);
   static bool Supports(InstructionSet instruction_set) {
-    return instruction_set == kX86
-        || instruction_set == kArm
-        || instruction_set == kX86_64
-        || instruction_set == kThumb2;
+    return instruction_set == kArm
+        || instruction_set == kArm64
+        || instruction_set == kThumb2
+        || instruction_set == kX86
+        || instruction_set == kX86_64;
   }
 
   size_t GetNumberOfSpillSlots() const {
@@ -127,6 +128,12 @@
   bool ValidateInternal(bool log_fatal_on_failure) const;
   void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
   void DumpAllIntervals(std::ostream& stream) const;
+  int FindAvailableRegisterPair(size_t* next_use, size_t starting_at) const;
+  int FindAvailableRegister(size_t* next_use) const;
+
+  // Try splitting an active non-pair interval at the given `position`.
+  // Returns whether it was successful at finding such an interval.
+  bool TrySplitNonPairIntervalAt(size_t position, size_t first_register_use, size_t* next_use);
 
   ArenaAllocator* const allocator_;
   CodeGenerator* const codegen_;
@@ -187,10 +194,14 @@
   // Slots reserved for out arguments.
   size_t reserved_out_slots_;
 
-  // The maximum live registers at safepoints.
-  size_t maximum_number_of_live_registers_;
+  // The maximum live core registers at safepoints.
+  size_t maximum_number_of_live_core_registers_;
+
+  // The maximum live FP registers at safepoints.
+  size_t maximum_number_of_live_fp_registers_;
 
   ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
+  ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
 
   DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
 };
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 8d75db9..0cc00c0 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -19,6 +19,7 @@
 #include "code_generator_x86.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
+#include "driver/compiler_options.h"
 #include "nodes.h"
 #include "optimizing_unit_test.h"
 #include "register_allocator.h"
@@ -36,13 +37,12 @@
 static bool Check(const uint16_t* data) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
-  x86::CodeGeneratorX86 codegen(graph);
+  builder.BuildGraph(*item);
+  graph->TryBuildingSsa();
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
   RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -58,7 +58,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = new (&allocator) HGraph(&allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   GrowableArray<LiveInterval*> intervals(&allocator, 0);
 
   // Test with two intervals of the same range.
@@ -250,12 +250,11 @@
 }
 
 static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
-  HGraphBuilder builder(allocator);
+  HGraph* graph = new (allocator) HGraph(allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  builder.BuildGraph(*item);
+  graph->TryBuildingSsa();
   return graph;
 }
 
@@ -299,7 +298,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildSSAGraph(data, &allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
   RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -331,7 +330,7 @@
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildSSAGraph(data, &allocator);
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
 
@@ -384,7 +383,7 @@
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildSSAGraph(data, &allocator);
   SsaDeadPhiElimination(graph).Run();
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
   RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -406,7 +405,7 @@
   ArenaAllocator allocator(&pool);
   HGraph* graph = BuildSSAGraph(data, &allocator);
   SsaDeadPhiElimination(graph).Run();
-  x86::CodeGeneratorX86 codegen(graph);
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
   RegisterAllocator register_allocator(&allocator, &codegen, liveness);
@@ -466,7 +465,7 @@
   entry->AddSuccessor(block);
 
   HInstruction* test = new (allocator) HInstanceFieldGet(
-      parameter, Primitive::kPrimBoolean, MemberOffset(22));
+      parameter, Primitive::kPrimBoolean, MemberOffset(22), false);
   block->AddInstruction(test);
   block->AddInstruction(new (allocator) HIf(test));
   HBasicBlock* then = new (allocator) HBasicBlock(graph);
@@ -485,8 +484,10 @@
 
   *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
   join->AddPhi(*phi);
-  *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
-  *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+  *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
+                                              MemberOffset(42), false);
+  *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
+                                              MemberOffset(42), false);
   then->AddInstruction(*input1);
   else_->AddInstruction(*input2);
   join->AddInstruction(new (allocator) HExit());
@@ -506,7 +507,7 @@
 
   {
     HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
@@ -521,13 +522,13 @@
 
   {
     HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
     // Set the phi to a specific register, and check that the inputs get allocated
     // the same register.
-    phi->GetLocations()->SetOut(Location::RegisterLocation(2));
+    phi->GetLocations()->UpdateOut(Location::RegisterLocation(2));
     RegisterAllocator register_allocator(&allocator, &codegen, liveness);
     register_allocator.AllocateRegisters();
 
@@ -538,13 +539,13 @@
 
   {
     HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
     // Set input1 to a specific register, and check that the phi and other input get allocated
     // the same register.
-    input1->GetLocations()->SetOut(Location::RegisterLocation(2));
+    input1->GetLocations()->UpdateOut(Location::RegisterLocation(2));
     RegisterAllocator register_allocator(&allocator, &codegen, liveness);
     register_allocator.AllocateRegisters();
 
@@ -555,13 +556,13 @@
 
   {
     HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
     // Set input2 to a specific register, and check that the phi and other input get allocated
     // the same register.
-    input2->GetLocations()->SetOut(Location::RegisterLocation(2));
+    input2->GetLocations()->UpdateOut(Location::RegisterLocation(2));
     RegisterAllocator register_allocator(&allocator, &codegen, liveness);
     register_allocator.AllocateRegisters();
 
@@ -585,7 +586,8 @@
   graph->AddBlock(block);
   entry->AddSuccessor(block);
 
-  *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt, MemberOffset(42));
+  *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
+                                             MemberOffset(42), false);
   block->AddInstruction(*field);
   *ret = new (allocator) HReturn(*field);
   block->AddInstruction(*ret);
@@ -604,7 +606,7 @@
 
   {
     HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
@@ -617,7 +619,7 @@
 
   {
     HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
@@ -666,7 +668,7 @@
 
   {
     HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
@@ -680,12 +682,12 @@
 
   {
     HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
     // check that both adds get the same register.
-    // Don't use SetOutput because output is already allocated.
+    // Don't use UpdateOutput because output is already allocated.
     first_add->InputAt(0)->GetLocations()->output_ = Location::RegisterLocation(2);
     ASSERT_EQ(first_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
     ASSERT_EQ(second_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
@@ -727,7 +729,7 @@
 
   {
     HGraph* graph = BuildDiv(&allocator, &div);
-    x86::CodeGeneratorX86 codegen(graph);
+    x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
     SsaLivenessAnalysis liveness(*graph, &codegen);
     liveness.Analyze();
 
@@ -739,4 +741,106 @@
   }
 }
 
+// Test a bug in the register allocator, where allocating a blocked
+// register would lead to spilling an inactive interval at the wrong
+// position.
+TEST(RegisterAllocatorTest, SpillInactive) {
+  ArenaPool pool;
+
+  // Create a synthesized graph to please the register_allocator and
+  // ssa_liveness_analysis code.
+  ArenaAllocator allocator(&pool);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(entry);
+  graph->SetEntryBlock(entry);
+  HInstruction* one = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+  HInstruction* two = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+  HInstruction* three = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+  HInstruction* four = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
+  entry->AddInstruction(one);
+  entry->AddInstruction(two);
+  entry->AddInstruction(three);
+  entry->AddInstruction(four);
+
+  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+  graph->AddBlock(block);
+  entry->AddSuccessor(block);
+  block->AddInstruction(new (&allocator) HExit());
+
+  // We create a synthesized user requesting a register, to avoid just spilling the
+  // intervals.
+  HPhi* user = new (&allocator) HPhi(&allocator, 0, 1, Primitive::kPrimInt);
+  user->AddInput(one);
+  user->SetBlock(block);
+  LocationSummary* locations = new (&allocator) LocationSummary(user, LocationSummary::kNoCall);
+  locations->SetInAt(0, Location::RequiresRegister());
+  static constexpr size_t phi_ranges[][2] = {{20, 30}};
+  BuildInterval(phi_ranges, arraysize(phi_ranges), &allocator, -1, user);
+
+  // Create an interval with lifetime holes.
+  static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}};
+  LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), &allocator, -1, one);
+  first->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, first->first_use_);
+  first->first_use_ = new(&allocator) UsePosition(user, 0, false, 7, first->first_use_);
+  first->first_use_ = new(&allocator) UsePosition(user, 0, false, 6, first->first_use_);
+
+  locations = new (&allocator) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+  first = first->SplitAt(1);
+
+  // Create an interval that conflicts with the next interval, to force the next
+  // interval to call `AllocateBlockedReg`.
+  static constexpr size_t ranges2[][2] = {{2, 4}};
+  LiveInterval* second = BuildInterval(ranges2, arraysize(ranges2), &allocator, -1, two);
+  locations = new (&allocator) LocationSummary(second->GetDefinedBy(), LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+
+  // Create an interval that will lead to splitting the first interval. The bug occured
+  // by splitting at a wrong position, in this case at the next intersection between
+  // this interval and the first interval. We would have then put the interval with ranges
+  // "[0, 2(, [4, 6(" in the list of handled intervals, even though we haven't processed intervals
+  // before lifetime position 6 yet.
+  static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}};
+  LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), &allocator, -1, three);
+  third->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, third->first_use_);
+  third->first_use_ = new(&allocator) UsePosition(user, 0, false, 4, third->first_use_);
+  third->first_use_ = new(&allocator) UsePosition(user, 0, false, 3, third->first_use_);
+  locations = new (&allocator) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+  third = third->SplitAt(3);
+
+  // Because the first part of the split interval was considered handled, this interval
+  // was free to allocate the same register, even though it conflicts with it.
+  static constexpr size_t ranges4[][2] = {{4, 6}};
+  LiveInterval* fourth = BuildInterval(ranges4, arraysize(ranges4), &allocator, -1, four);
+  locations = new (&allocator) LocationSummary(fourth->GetDefinedBy(), LocationSummary::kNoCall);
+  locations->SetOut(Location::RequiresRegister());
+
+  x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
+  SsaLivenessAnalysis liveness(*graph, &codegen);
+
+  RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+  register_allocator.unhandled_core_intervals_.Add(fourth);
+  register_allocator.unhandled_core_intervals_.Add(third);
+  register_allocator.unhandled_core_intervals_.Add(second);
+  register_allocator.unhandled_core_intervals_.Add(first);
+
+  // Set just one register available to make all intervals compete for the same.
+  register_allocator.number_of_registers_ = 1;
+  register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
+  register_allocator.processing_core_registers_ = true;
+  register_allocator.unhandled_ = &register_allocator.unhandled_core_intervals_;
+  register_allocator.LinearScan();
+
+  // Test that there is no conflicts between intervals.
+  GrowableArray<LiveInterval*> intervals(&allocator, 0);
+  intervals.Add(first);
+  intervals.Add(second);
+  intervals.Add(third);
+  intervals.Add(fourth);
+  ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
+      intervals, 0, 0, codegen, &allocator, true, false));
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
new file mode 100644
index 0000000..ea1ca5a
--- /dev/null
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include "side_effects_analysis.h"
+
+namespace art {
+
+void SideEffectsAnalysis::Run() {
+  // Inlining might have created more blocks, so we need to increase the size
+  // if needed.
+  block_effects_.SetSize(graph_->GetBlocks().Size());
+  loop_effects_.SetSize(graph_->GetBlocks().Size());
+
+  if (kIsDebugBuild) {
+    for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+      HBasicBlock* block = it.Current();
+      SideEffects effects = GetBlockEffects(block);
+      DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+      if (block->IsLoopHeader()) {
+        effects = GetLoopEffects(block);
+        DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+      }
+    }
+  }
+
+  // Do a post order visit to ensure we visit a loop header after its loop body.
+  for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+
+    SideEffects effects = SideEffects::None();
+    // Update `effects` with the side effects of all instructions in this block.
+    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+         inst_it.Advance()) {
+      HInstruction* instruction = inst_it.Current();
+      effects = effects.Union(instruction->GetSideEffects());
+      if (effects.HasAllSideEffects()) {
+        break;
+      }
+    }
+
+    block_effects_.Put(block->GetBlockId(), effects);
+
+    if (block->IsLoopHeader()) {
+      // The side effects of the loop header are part of the loop.
+      UpdateLoopEffects(block->GetLoopInformation(), effects);
+      HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+      if (pre_header->IsInLoop()) {
+        // Update the side effects of the outer loop with the side effects of the inner loop.
+        // Note that this works because we know all the blocks of the inner loop are visited
+        // before the loop header of the outer loop.
+        UpdateLoopEffects(pre_header->GetLoopInformation(), GetLoopEffects(block));
+      }
+    } else if (block->IsInLoop()) {
+      // Update the side effects of the loop with the side effects of this block.
+      UpdateLoopEffects(block->GetLoopInformation(), effects);
+    }
+  }
+  has_run_ = true;
+}
+
+SideEffects SideEffectsAnalysis::GetLoopEffects(HBasicBlock* block) const {
+  DCHECK(block->IsLoopHeader());
+  return loop_effects_.Get(block->GetBlockId());
+}
+
+SideEffects SideEffectsAnalysis::GetBlockEffects(HBasicBlock* block) const {
+  return block_effects_.Get(block->GetBlockId());
+}
+
+void SideEffectsAnalysis::UpdateLoopEffects(HLoopInformation* info, SideEffects effects) {
+  int id = info->GetHeader()->GetBlockId();
+  loop_effects_.Put(id, loop_effects_.Get(id).Union(effects));
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h
new file mode 100644
index 0000000..f1c98ac
--- /dev/null
+++ b/compiler/optimizing/side_effects_analysis.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
+#define ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class SideEffectsAnalysis : public HOptimization {
+ public:
+  explicit SideEffectsAnalysis(HGraph* graph)
+      : HOptimization(graph, true, "SideEffects"),
+        graph_(graph),
+        block_effects_(graph->GetArena(), graph->GetBlocks().Size(), SideEffects::None()),
+        loop_effects_(graph->GetArena(), graph->GetBlocks().Size(), SideEffects::None()) {}
+
+  SideEffects GetLoopEffects(HBasicBlock* block) const;
+  SideEffects GetBlockEffects(HBasicBlock* block) const;
+
+  // Compute side effects of individual blocks and loops.
+  void Run();
+
+  bool HasRun() const { return has_run_; }
+
+ private:
+  void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
+
+  HGraph* graph_;
+
+  // Checked in debug build, to ensure the pass has been run prior to
+  // running a pass that depends on it.
+  bool has_run_ = false;
+
+  // Side effects of individual blocks, that is the union of the side effects
+  // of the instructions in the block.
+  GrowableArray<SideEffects> block_effects_;
+
+  // Side effects of loops, that is the union of the side effects of the
+  // blocks contained in that loop.
+  GrowableArray<SideEffects> loop_effects_;
+
+  ART_FRIEND_TEST(GVNTest, LoopSideEffects);
+  DISALLOW_COPY_AND_ASSIGN(SideEffectsAnalysis);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index edfafcd..c9a21aa 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -17,7 +17,7 @@
 #include "ssa_builder.h"
 
 #include "nodes.h"
-#include "ssa_type_propagation.h"
+#include "primitive_type_propagation.h"
 #include "ssa_phi_elimination.h"
 
 namespace art {
@@ -52,7 +52,7 @@
   // 4) Propagate types of phis. At this point, phis are typed void in the general
   // case, or float or double when we created a floating-point equivalent. So we
   // need to propagate the types across phis to give them a correct type.
-  SsaTypePropagation type_propagation(GetGraph());
+  PrimitiveTypePropagation type_propagation(GetGraph());
   type_propagation.Run();
 
   // 5) Clear locals.
@@ -68,7 +68,7 @@
 }
 
 HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) {
-  return GetLocalsFor(block)->Get(local);
+  return GetLocalsFor(block)->GetInstructionAt(local);
 }
 
 void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {
@@ -85,7 +85,7 @@
         HPhi* phi = new (GetGraph()->GetArena()) HPhi(
             GetGraph()->GetArena(), local, 0, Primitive::kPrimVoid);
         block->AddPhi(phi);
-        current_locals_->Put(local, phi);
+        current_locals_->SetRawEnvAt(local, phi);
       }
     }
     // Save the loop header so that the last phase of the analysis knows which
@@ -125,7 +125,7 @@
         block->AddPhi(phi);
         value = phi;
       }
-      current_locals_->Put(local, value);
+      current_locals_->SetRawEnvAt(local, value);
     }
   }
 
@@ -235,7 +235,7 @@
 }
 
 void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
-  HInstruction* value = current_locals_->Get(load->GetLocal()->GetRegNumber());
+  HInstruction* value = current_locals_->GetInstructionAt(load->GetLocal()->GetRegNumber());
   if (load->GetType() != value->GetType()
       && (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble)) {
     // If the operation requests a specific type, we make sure its input is of that type.
@@ -246,7 +246,7 @@
 }
 
 void SsaBuilder::VisitStoreLocal(HStoreLocal* store) {
-  current_locals_->Put(store->GetLocal()->GetRegNumber(), store->InputAt(1));
+  current_locals_->SetRawEnvAt(store->GetLocal()->GetRegNumber(), store->InputAt(1));
   store->GetBlock()->RemoveInstruction(store);
 }
 
@@ -256,7 +256,7 @@
   }
   HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment(
       GetGraph()->GetArena(), current_locals_->Size());
-  environment->Populate(*current_locals_);
+  environment->CopyFrom(current_locals_);
   instruction->SetEnvironment(environment);
 }
 
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 2cbd51a..2eec87b 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -36,14 +36,14 @@
 
   void BuildSsa();
 
-  GrowableArray<HInstruction*>* GetLocalsFor(HBasicBlock* block) {
+  HEnvironment* GetLocalsFor(HBasicBlock* block) {
     HEnvironment* env = locals_for_.Get(block->GetBlockId());
     if (env == nullptr) {
       env = new (GetGraph()->GetArena()) HEnvironment(
           GetGraph()->GetArena(), GetGraph()->GetNumberOfVRegs());
       locals_for_.Put(block->GetBlockId(), env);
     }
-    return env->GetVRegs();
+    return env;
   }
 
   HInstruction* ValueOfLocal(HBasicBlock* block, size_t local);
@@ -60,7 +60,7 @@
 
  private:
   // Locals for the current block being visited.
-  GrowableArray<HInstruction*>* current_locals_;
+  HEnvironment* current_locals_;
 
   // Keep track of loop headers found. The last phase of the analysis iterates
   // over these blocks to set the inputs of their phis.
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 660a5c5..2a84735 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -115,14 +115,13 @@
   // to differentiate between the start and end of an instruction. Adding 2 to
   // the lifetime position for each instruction ensures the start of an
   // instruction is different than the end of the previous instruction.
-  HGraphVisitor* location_builder = codegen_->GetLocationBuilder();
   for (HLinearOrderIterator it(*this); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     block->SetLifetimeStart(lifetime_position);
 
     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
       HInstruction* current = inst_it.Current();
-      current->Accept(location_builder);
+      codegen_->AllocateLocations(current);
       LocationSummary* locations = current->GetLocations();
       if (locations != nullptr && locations->Out().IsValid()) {
         instructions_from_ssa_index_.Add(current);
@@ -140,7 +139,7 @@
     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
          inst_it.Advance()) {
       HInstruction* current = inst_it.Current();
-      current->Accept(codegen_->GetLocationBuilder());
+      codegen_->AllocateLocations(current);
       LocationSummary* locations = current->GetLocations();
       if (locations != nullptr && locations->Out().IsValid()) {
         instructions_from_ssa_index_.Add(current);
@@ -232,9 +231,9 @@
 
       if (current->HasEnvironment()) {
         // All instructions in the environment must be live.
-        GrowableArray<HInstruction*>* environment = current->GetEnvironment()->GetVRegs();
+        HEnvironment* environment = current->GetEnvironment();
         for (size_t i = 0, e = environment->Size(); i < e; ++i) {
-          HInstruction* instruction = environment->Get(i);
+          HInstruction* instruction = environment->GetInstructionAt(i);
           if (instruction != nullptr) {
             DCHECK(instruction->HasSsaIndex());
             live_in->SetBit(instruction->GetSsaIndex());
@@ -419,10 +418,21 @@
 }
 
 Location LiveInterval::ToLocation() const {
+  DCHECK(!IsHighInterval());
   if (HasRegister()) {
-    return IsFloatingPoint()
-        ? Location::FpuRegisterLocation(GetRegister())
-        : Location::RegisterLocation(GetRegister());
+    if (IsFloatingPoint()) {
+      if (HasHighInterval()) {
+        return Location::FpuRegisterPairLocation(GetRegister(), GetHighInterval()->GetRegister());
+      } else {
+        return Location::FpuRegisterLocation(GetRegister());
+      }
+    } else {
+      if (HasHighInterval()) {
+        return Location::RegisterPairLocation(GetRegister(), GetHighInterval()->GetRegister());
+      } else {
+        return Location::RegisterLocation(GetRegister());
+      }
+    }
   } else {
     HInstruction* defined_by = GetParent()->GetDefinedBy();
     if (defined_by->IsConstant()) {
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 2312389..0e68a61 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_
 
 #include "nodes.h"
+#include <iostream>
 
 namespace art {
 
@@ -77,6 +78,15 @@
     stream << "[" << start_ << ", " << end_ << ")";
   }
 
+  LiveRange* Dup(ArenaAllocator* allocator) const {
+    return new (allocator) LiveRange(
+        start_, end_, next_ == nullptr ? nullptr : next_->Dup(allocator));
+  }
+
+  LiveRange* GetLastRange() {
+    return next_ == nullptr ? this : next_->GetLastRange();
+  }
+
  private:
   size_t start_;
   size_t end_;
@@ -123,6 +133,12 @@
     stream << position_;
   }
 
+  UsePosition* Dup(ArenaAllocator* allocator) const {
+    return new (allocator) UsePosition(
+        user_, input_index_, is_environment_, position_,
+        next_ == nullptr ? nullptr : next_->Dup(allocator));
+  }
+
  private:
   HInstruction* const user_;
   const size_t input_index_;
@@ -166,12 +182,21 @@
 
   void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
     // Set the use within the instruction.
-    size_t position = instruction->GetLifetimePosition();
-    if (instruction->GetLocations()->InputOverlapsWithOutputOrTemp(input_index, is_environment)) {
-      // If it overlaps, we need to make sure the user will not try to allocate a temp
-      // or its output to the same register.
-      ++position;
+    size_t position = instruction->GetLifetimePosition() + 1;
+    LocationSummary* locations = instruction->GetLocations();
+    if (!is_environment) {
+      if (locations->IsFixedInput(input_index) || locations->OutputUsesSameAs(input_index)) {
+        // For fixed inputs and output same as input, the register allocator
+        // requires to have inputs die at the instruction, so that input moves use the
+        // location of the input just before that instruction (and not potential moves due
+        // to splitting).
+        position = instruction->GetLifetimePosition();
+      }
     }
+
+    DCHECK(position == instruction->GetLifetimePosition()
+           || position == instruction->GetLifetimePosition() + 1);
+
     if ((first_use_ != nullptr)
         && (first_use_->GetUser() == instruction)
         && (first_use_->GetPosition() < position)) {
@@ -239,16 +264,28 @@
 
   void AddLoopRange(size_t start, size_t end) {
     DCHECK(first_range_ != nullptr);
-    while (first_range_ != nullptr && first_range_->GetEnd() < end) {
-      DCHECK_LE(start, first_range_->GetStart());
-      first_range_ = first_range_->GetNext();
+    DCHECK_LE(start, first_range_->GetStart());
+    // Find the range that covers the positions after the loop.
+    LiveRange* after_loop = first_range_;
+    LiveRange* last_in_loop = nullptr;
+    while (after_loop != nullptr && after_loop->GetEnd() < end) {
+      DCHECK_LE(start, after_loop->GetStart());
+      last_in_loop = after_loop;
+      after_loop = after_loop->GetNext();
     }
-    if (first_range_ == nullptr) {
+    if (after_loop == nullptr) {
       // Uses are only in the loop.
       first_range_ = last_range_ = new (allocator_) LiveRange(start, end, nullptr);
-    } else {
+    } else if (after_loop->GetStart() <= end) {
+      first_range_ = after_loop;
       // There are uses after the loop.
       first_range_->start_ = start;
+    } else {
+      // The use after the loop is after a lifetime hole.
+      DCHECK(last_in_loop != nullptr);
+      first_range_ = last_in_loop;
+      first_range_->start_ = start;
+      first_range_->end_ = end;
     }
   }
 
@@ -274,6 +311,7 @@
   LiveInterval* GetParent() const { return parent_; }
 
   LiveRange* GetFirstRange() const { return first_range_; }
+  LiveRange* GetLastRange() const { return last_range_; }
 
   int GetRegister() const { return register_; }
   void SetRegister(int reg) { register_ = reg; }
@@ -376,6 +414,23 @@
     return FirstRegisterUseAfter(GetStart());
   }
 
+  size_t FirstUseAfter(size_t position) const {
+    if (is_temp_) {
+      return position == GetStart() ? position : kNoLifetime;
+    }
+
+    UsePosition* use = first_use_;
+    size_t end = GetEnd();
+    while (use != nullptr && use->GetPosition() <= end) {
+      size_t use_position = use->GetPosition();
+      if (use_position > position) {
+        return use_position;
+      }
+      use = use->GetNext();
+    }
+    return kNoLifetime;
+  }
+
   UsePosition* GetFirstUse() const {
     return first_use_;
   }
@@ -414,7 +469,7 @@
     LiveRange* current = first_range_;
     LiveRange* previous = nullptr;
     // Iterate over the ranges, and either find a range that covers this position, or
-    // a two ranges in between this position (that is, the position is in a lifetime hole).
+    // two ranges in between this position (that is, the position is in a lifetime hole).
     do {
       if (position >= current->GetEnd()) {
         // Move to next range.
@@ -464,10 +519,11 @@
   void Dump(std::ostream& stream) const {
     stream << "ranges: { ";
     LiveRange* current = first_range_;
-    do {
+    while (current != nullptr) {
       current->Dump(stream);
       stream << " ";
-    } while ((current = current->GetNext()) != nullptr);
+      current = current->GetNext();
+    }
     stream << "}, uses: { ";
     UsePosition* use = first_use_;
     if (use != nullptr) {
@@ -478,9 +534,18 @@
     }
     stream << "}";
     stream << " is_fixed: " << is_fixed_ << ", is_split: " << IsSplit();
+    stream << " is_high: " << IsHighInterval();
+    stream << " is_low: " << IsLowInterval();
   }
 
   LiveInterval* GetNextSibling() const { return next_sibling_; }
+  LiveInterval* GetLastSibling() {
+    LiveInterval* result = this;
+    while (result->next_sibling_ != nullptr) {
+      result = result->next_sibling_;
+    }
+    return result;
+  }
 
   // Returns the first register hint that is at least free before
   // the value contained in `free_until`. If none is found, returns
@@ -511,6 +576,115 @@
 
   // Returns whether `other` and `this` share the same kind of register.
   bool SameRegisterKind(Location other) const;
+  bool SameRegisterKind(const LiveInterval& other) const {
+    return IsFloatingPoint() == other.IsFloatingPoint();
+  }
+
+  bool HasHighInterval() const {
+    return IsLowInterval();
+  }
+
+  bool HasLowInterval() const {
+    return IsHighInterval();
+  }
+
+  LiveInterval* GetLowInterval() const {
+    DCHECK(HasLowInterval());
+    return high_or_low_interval_;
+  }
+
+  LiveInterval* GetHighInterval() const {
+    DCHECK(HasHighInterval());
+    return high_or_low_interval_;
+  }
+
+  bool IsHighInterval() const {
+    return GetParent()->is_high_interval_;
+  }
+
+  bool IsLowInterval() const {
+    return !IsHighInterval() && (GetParent()->high_or_low_interval_ != nullptr);
+  }
+
+  void SetLowInterval(LiveInterval* low) {
+    DCHECK(IsHighInterval());
+    high_or_low_interval_ = low;
+  }
+
+  void SetHighInterval(LiveInterval* high) {
+    DCHECK(IsLowInterval());
+    high_or_low_interval_ = high;
+  }
+
+  void AddHighInterval(bool is_temp = false) {
+    DCHECK_EQ(GetParent(), this);
+    DCHECK(!HasHighInterval());
+    DCHECK(!HasLowInterval());
+    high_or_low_interval_ = new (allocator_) LiveInterval(
+        allocator_, type_, defined_by_, false, kNoRegister, is_temp, false, true);
+    high_or_low_interval_->high_or_low_interval_ = this;
+    if (first_range_ != nullptr) {
+      high_or_low_interval_->first_range_ = first_range_->Dup(allocator_);
+      high_or_low_interval_->last_range_ = first_range_->GetLastRange();
+    }
+    if (first_use_ != nullptr) {
+      high_or_low_interval_->first_use_ = first_use_->Dup(allocator_);
+    }
+  }
+
+  // Returns whether an interval, when it is non-split, is using
+  // the same register of one of its input.
+  bool IsUsingInputRegister() const {
+    if (defined_by_ != nullptr && !IsSplit()) {
+      for (HInputIterator it(defined_by_); !it.Done(); it.Advance()) {
+        LiveInterval* interval = it.Current()->GetLiveInterval();
+
+        // Find the interval that covers `defined_by`_.
+        while (interval != nullptr && !interval->Covers(defined_by_->GetLifetimePosition())) {
+          interval = interval->GetNextSibling();
+        }
+
+        // Check if both intervals have the same register of the same kind.
+        if (interval != nullptr
+            && interval->SameRegisterKind(*this)
+            && interval->GetRegister() == GetRegister()) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  // Returns whether an interval, when it is non-split, can safely use
+  // the same register of one of its input. Note that this method requires
+  // IsUsingInputRegister() to be true.
+  bool CanUseInputRegister() const {
+    DCHECK(IsUsingInputRegister());
+    if (defined_by_ != nullptr && !IsSplit()) {
+      LocationSummary* locations = defined_by_->GetLocations();
+      if (locations->OutputCanOverlapWithInputs()) {
+        return false;
+      }
+      for (HInputIterator it(defined_by_); !it.Done(); it.Advance()) {
+        LiveInterval* interval = it.Current()->GetLiveInterval();
+
+        // Find the interval that covers `defined_by`_.
+        while (interval != nullptr && !interval->Covers(defined_by_->GetLifetimePosition())) {
+          interval = interval->GetNextSibling();
+        }
+
+        if (interval != nullptr
+            && interval->SameRegisterKind(*this)
+            && interval->GetRegister() == GetRegister()) {
+          // We found the input that has the same register. Check if it is live after
+          // `defined_by`_.
+          return !interval->Covers(defined_by_->GetLifetimePosition() + 1);
+        }
+      }
+    }
+    LOG(FATAL) << "Unreachable";
+    UNREACHABLE();
+  }
 
  private:
   LiveInterval(ArenaAllocator* allocator,
@@ -519,7 +693,8 @@
                bool is_fixed = false,
                int reg = kNoRegister,
                bool is_temp = false,
-               bool is_slow_path_safepoint = false)
+               bool is_slow_path_safepoint = false,
+               bool is_high_interval = false)
       : allocator_(allocator),
         first_range_(nullptr),
         last_range_(nullptr),
@@ -532,6 +707,8 @@
         is_fixed_(is_fixed),
         is_temp_(is_temp),
         is_slow_path_safepoint_(is_slow_path_safepoint),
+        is_high_interval_(is_high_interval),
+        high_or_low_interval_(nullptr),
         defined_by_(defined_by) {}
 
   ArenaAllocator* const allocator_;
@@ -568,12 +745,21 @@
   // Whether the interval is for a safepoint that calls on slow path.
   const bool is_slow_path_safepoint_;
 
+  // Whether this interval is a synthesized interval for register pair.
+  const bool is_high_interval_;
+
+  // If this interval needs a register pair, the high or low equivalent.
+  // `is_high_interval_` tells whether this holds the low or the high.
+  LiveInterval* high_or_low_interval_;
+
   // The instruction represented by this interval.
   HInstruction* const defined_by_;
 
   static constexpr int kNoRegister = -1;
   static constexpr int kNoSpillSlot = -1;
 
+  ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
+
   DISALLOW_COPY_AND_ASSIGN(LiveInterval);
 };
 
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 58cea77..fd30c1b 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -26,8 +26,8 @@
       HPhi* phi = inst_it.Current()->AsPhi();
       // Set dead ahead of running through uses. The phi may have no use.
       phi->SetDead();
-      for (HUseIterator<HInstruction> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
-        HUseListNode<HInstruction>* current = use_it.Current();
+      for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
+        HUseListNode<HInstruction*>* current = use_it.Current();
         HInstruction* user = current->GetUser();
         if (!user->IsPhi()) {
           worklist_.Add(phi);
@@ -61,9 +61,9 @@
       next = current->GetNext();
       if (current->AsPhi()->IsDead()) {
         if (current->HasUses()) {
-          for (HUseIterator<HInstruction> use_it(current->GetUses()); !use_it.Done();
+          for (HUseIterator<HInstruction*> use_it(current->GetUses()); !use_it.Done();
                use_it.Advance()) {
-            HUseListNode<HInstruction>* user_node = use_it.Current();
+            HUseListNode<HInstruction*>* user_node = use_it.Current();
             HInstruction* user = user_node->GetUser();
             DCHECK(user->IsLoopHeaderPhi()) << user->GetId();
             DCHECK(user->AsPhi()->IsDead()) << user->GetId();
@@ -73,12 +73,12 @@
           }
         }
         if (current->HasEnvironmentUses()) {
-          for (HUseIterator<HEnvironment> use_it(current->GetEnvUses()); !use_it.Done();
+          for (HUseIterator<HEnvironment*> use_it(current->GetEnvUses()); !use_it.Done();
                use_it.Advance()) {
-            HUseListNode<HEnvironment>* user_node = use_it.Current();
+            HUseListNode<HEnvironment*>* user_node = use_it.Current();
             HEnvironment* user = user_node->GetUser();
             user->SetRawEnvAt(user_node->GetIndex(), nullptr);
-            current->RemoveEnvironmentUser(user, user_node->GetIndex());
+            current->RemoveEnvironmentUser(user_node);
           }
         }
         block->RemovePhi(current->AsPhi());
@@ -132,8 +132,8 @@
       // Because we're updating the users of this phi, we may have new
       // phis candidate for elimination if this phi is in a loop. Add phis that
       // used this phi to the worklist.
-      for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
-        HUseListNode<HInstruction>* current = it.Current();
+      for (HUseIterator<HInstruction*> it(phi->GetUses()); !it.Done(); it.Advance()) {
+        HUseListNode<HInstruction*>* current = it.Current();
         HInstruction* user = current->GetUser();
         if (user->IsPhi()) {
           worklist_.Add(user->AsPhi());
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 6174dd4..7e90b37 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -78,16 +78,17 @@
 static void TestCode(const uint16_t* data, const char* expected) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
 
   graph->BuildDominatorTree();
   // Suspend checks implementation may change in the future, and this test relies
   // on how instructions are ordered.
   RemoveSuspendChecks(graph);
-  graph->TransformToSSA();
+  graph->TransformToSsa();
   ReNumberInstructions(graph);
 
   // Test that phis had their type set.
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 9cfa71c..3974e53 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -111,7 +111,7 @@
   }
 
   size_t ComputeStackMapSize() const {
-    return stack_maps_.Size() * (StackMap::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
+    return stack_maps_.Size() * StackMap::ComputeAlignedStackMapSize(stack_mask_max_);
   }
 
   size_t ComputeDexRegisterMapSize() const {
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
index 2e48ee8..a5a0eb2 100644
--- a/compiler/optimizing/suspend_check_test.cc
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -30,10 +30,11 @@
 static void TestCode(const uint16_t* data) {
   ArenaPool pool;
   ArenaAllocator allocator(&pool);
-  HGraphBuilder builder(&allocator);
+  HGraph* graph = new (&allocator) HGraph(&allocator);
+  HGraphBuilder builder(graph);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
-  HGraph* graph = builder.BuildGraph(*item);
-  ASSERT_NE(graph, nullptr);
+  bool graph_built = builder.BuildGraph(*item);
+  ASSERT_TRUE(graph_built);
 
   HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0);
   HInstruction* first_instruction = first_block->GetFirstInstruction();
diff --git a/compiler/sea_ir/code_gen/code_gen.cc b/compiler/sea_ir/code_gen/code_gen.cc
deleted file mode 100644
index 8d79c41..0000000
--- a/compiler/sea_ir/code_gen/code_gen.cc
+++ /dev/null
@@ -1,291 +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.
- */
-
-#include <llvm/Support/raw_ostream.h>
-
-#include "base/logging.h"
-#include "utils.h"
-
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/code_gen/code_gen.h"
-#include "sea_ir/types/type_inference.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-void CodeGenPrepassVisitor::Visit(PhiInstructionNode* phi) {
-  Region* r = phi->GetRegion();
-  const std::vector<Region*>* predecessors = r->GetPredecessors();
-  DCHECK(NULL != predecessors);
-  DCHECK_GT(predecessors->size(), 0u);
-  llvm::PHINode *llvm_phi  = llvm_data_->builder_.CreatePHI(
-      llvm::Type::getInt32Ty(*llvm_data_->context_), predecessors->size(), phi->StringId());
-  llvm_data_->AddValue(phi, llvm_phi);
-}
-
-void CodeGenPassVisitor::Initialize(SeaGraph* graph) {
-  Region* root_region;
-  ordered_regions_.clear();
-  for (std::vector<Region*>::const_iterator cit = graph->GetRegions()->begin();
-        cit != graph->GetRegions()->end(); cit++ ) {
-    if ((*cit)->GetIDominator() == (*cit)) {
-      root_region = *cit;
-    }
-  }
-  ordered_regions_.push_back(root_region);
-  for (unsigned int id = 0; id < ordered_regions_.size(); id++) {
-    Region* current_region = ordered_regions_.at(id);
-    const std::set<Region*>* dominated_regions = current_region->GetIDominatedSet();
-    for (std::set<Region*>::const_iterator cit = dominated_regions->begin();
-            cit != dominated_regions->end(); cit++ ) {
-      ordered_regions_.push_back(*cit);
-    }
-  }
-}
-
-void CodeGenPostpassVisitor::Visit(SeaGraph* graph) { }
-void CodeGenVisitor::Visit(SeaGraph* graph) { }
-void CodeGenPrepassVisitor::Visit(SeaGraph* graph) {
-  std::vector<SignatureNode*>* parameters = graph->GetParameterNodes();
-  // TODO: It may be better to extract correct types from dex
-  //       instead than from type inference.
-  DCHECK(parameters != NULL);
-  std::vector<llvm::Type*> parameter_types;
-  for (std::vector<SignatureNode*>::const_iterator param_iterator = parameters->begin();
-      param_iterator!= parameters->end(); param_iterator++) {
-    const Type* param_type = graph->ti_->type_data_.FindTypeOf((*param_iterator)->Id());
-    DCHECK(param_type->Equals(graph->ti_->type_cache_->Integer()))
-      << "Code generation for types other than integer not implemented.";
-    parameter_types.push_back(llvm::Type::getInt32Ty(*llvm_data_->context_));
-  }
-
-  // TODO: Get correct function return type.
-  const Type* return_type = graph->ti_->type_data_.FindTypeOf(-1);
-  DCHECK(return_type->Equals(graph->ti_->type_cache_->Integer()))
-    << "Code generation for types other than integer not implemented.";
-  llvm::FunctionType *function_type = llvm::FunctionType::get(
-      llvm::Type::getInt32Ty(*llvm_data_->context_),
-      parameter_types, false);
-
-  llvm_data_->function_ = llvm::Function::Create(function_type,
-      llvm::Function::ExternalLinkage, function_name_, &llvm_data_->module_);
-  unsigned param_id = 0;
-  for (llvm::Function::arg_iterator arg_it = llvm_data_->function_->arg_begin();
-      param_id != llvm_data_->function_->arg_size(); ++arg_it, ++param_id) {
-    // TODO: The "+1" is because of the Method parameter on position 0.
-    DCHECK(parameters->size() > param_id) << "Insufficient parameters for function signature";
-    // Build parameter register name for LLVM IR clarity.
-    std::string arg_name = art::StringPrintf("r%d", parameters->at(param_id)->GetResultRegister());
-    arg_it->setName(arg_name);
-    SignatureNode* parameter = parameters->at(param_id);
-    llvm_data_->AddValue(parameter, arg_it);
-  }
-
-  std::vector<Region*>* regions = &ordered_regions_;
-  DCHECK_GT(regions->size(), 0u);
-  // Then create all other basic blocks.
-  for (std::vector<Region*>::const_iterator cit = regions->begin(); cit != regions->end(); cit++) {
-    llvm::BasicBlock* new_basic_block = llvm::BasicBlock::Create(*llvm_data_->context_,
-        (*cit)->StringId(), llvm_data_->function_);
-    llvm_data_->AddBlock((*cit), new_basic_block);
-  }
-}
-
-void CodeGenPrepassVisitor::Visit(Region* region) {
-  llvm_data_->builder_.SetInsertPoint(llvm_data_->GetBlock(region));
-}
-void CodeGenPostpassVisitor::Visit(Region* region) {
-  llvm_data_->builder_.SetInsertPoint(llvm_data_->GetBlock(region));
-}
-void CodeGenVisitor::Visit(Region* region) {
-  llvm_data_->builder_.SetInsertPoint(llvm_data_->GetBlock(region));
-}
-
-
-void CodeGenVisitor::Visit(InstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  DCHECK(0);  // This whole function is useful only during development.
-}
-
-void CodeGenVisitor::Visit(UnnamedConstInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "1.Instruction: " << instr << std::endl;
-  llvm_data_->AddValue(instruction,
-      llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, instruction->GetConstValue())));
-}
-
-void CodeGenVisitor::Visit(ConstInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "1.Instruction: " << instr << std::endl;
-  llvm_data_->AddValue(instruction,
-      llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, instruction->GetConstValue())));
-}
-void CodeGenVisitor::Visit(ReturnInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "2.Instruction: " << instr << std::endl;
-  DCHECK_GT(instruction->GetSSAProducers().size(), 0u);
-  llvm::Value* return_value = llvm_data_->GetValue(instruction->GetSSAProducers().at(0));
-  llvm_data_->builder_.CreateRet(return_value);
-}
-void CodeGenVisitor::Visit(IfNeInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "3.Instruction: " << instr << std::endl;
-  std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
-  DCHECK_GT(ssa_uses.size(), 1u);
-  InstructionNode* use_l = ssa_uses.at(0);
-  llvm::Value* left = llvm_data_->GetValue(use_l);
-
-  InstructionNode* use_r = ssa_uses.at(1);
-  llvm::Value* right = llvm_data_->GetValue(use_r);
-  llvm::Value* ifne = llvm_data_->builder_.CreateICmpNE(left, right, instruction->StringId());
-  DCHECK(instruction->GetRegion() != NULL);
-  std::vector<Region*>* successors = instruction->GetRegion()->GetSuccessors();
-  DCHECK_GT(successors->size(), 0u);
-  llvm::BasicBlock* then_block = llvm_data_->GetBlock(successors->at(0));
-  llvm::BasicBlock* else_block = llvm_data_->GetBlock(successors->at(1));
-
-  llvm_data_->builder_.CreateCondBr(ifne, then_block, else_block);
-}
-
-/*
-void CodeGenVisitor::Visit(AddIntLitInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "4.Instruction: " << instr << std::endl;
-  std::vector<InstructionNode*> ssa_uses = instruction->GetSSAUses();
-  InstructionNode* use_l = ssa_uses.at(0);
-  llvm::Value* left = llvm_data->GetValue(use_l);
-  llvm::Value* right = llvm::ConstantInt::get(*llvm_data->context_,
-      llvm::APInt(32, instruction->GetConstValue()));
-  llvm::Value* result = llvm_data->builder_.CreateAdd(left, right);
-  llvm_data->AddValue(instruction, result);
-}
-*/
-void CodeGenVisitor::Visit(MoveResultInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "5.Instruction: " << instr << std::endl;
-  // TODO: Currently, this "mov" instruction is simulated by "res = return_register + 0".
-  // This is inefficient, but should be optimized out by the coalescing phase of the reg alloc.
-  // The TODO is to either ensure that this happens, or to
-  // remove the move-result instructions completely from the IR
-  // by merging them with the invoke-* instructions,
-  // since their purpose of minimizing the number of opcodes in dex is
-  // not relevant for the IR. (Will need to have different
-  // instruction subclasses for functions and procedures.)
-  std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
-  InstructionNode* use_l = ssa_uses.at(0);
-  llvm::Value* left = llvm_data_->GetValue(use_l);
-  llvm::Value* right = llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, 0));
-  llvm::Value* result = llvm_data_->builder_.CreateAdd(left, right);
-  llvm_data_->AddValue(instruction, result);
-}
-void CodeGenVisitor::Visit(InvokeStaticInstructionNode* invoke) {
-  std::string instr = invoke->GetInstruction()->DumpString(NULL);
-  std::cout << "6.Instruction: " << instr << std::endl;
-  // TODO: Build callee LLVM function name.
-  std::string symbol = "dex_";
-  symbol += art::MangleForJni(PrettyMethod(invoke->GetCalledMethodIndex(), dex_file_));
-  std::string function_name = "dex_int_00020Main_fibonacci_00028int_00029";
-  llvm::Function *callee = llvm_data_->module_.getFunction(function_name);
-  // TODO: Add proper checking of the matching between formal and actual signature.
-  DCHECK(NULL != callee);
-  std::vector<llvm::Value*> parameter_values;
-  std::vector<InstructionNode*> parameter_sources = invoke->GetSSAProducers();
-  // TODO: Replace first parameter with Method argument instead of 0.
-  parameter_values.push_back(llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, 0)));
-  for (std::vector<InstructionNode*>::const_iterator cit = parameter_sources.begin();
-      cit != parameter_sources.end(); ++cit) {
-    llvm::Value* parameter_value = llvm_data_->GetValue((*cit));
-    DCHECK(NULL != parameter_value);
-    parameter_values.push_back(parameter_value);
-  }
-  llvm::Value* return_value = llvm_data_->builder_.CreateCall(callee,
-      parameter_values, invoke->StringId());
-  llvm_data_->AddValue(invoke, return_value);
-}
-void CodeGenVisitor::Visit(AddIntInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "7.Instruction: " << instr << std::endl;
-  std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
-  DCHECK_GT(ssa_uses.size(), 1u);
-  InstructionNode* use_l = ssa_uses.at(0);
-  InstructionNode* use_r = ssa_uses.at(1);
-  llvm::Value* left = llvm_data_->GetValue(use_l);
-  llvm::Value* right = llvm_data_->GetValue(use_r);
-  llvm::Value* result = llvm_data_->builder_.CreateAdd(left, right);
-  llvm_data_->AddValue(instruction, result);
-}
-void CodeGenVisitor::Visit(GotoInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "8.Instruction: " << instr << std::endl;
-  std::vector<sea_ir::Region*>* targets = instruction->GetRegion()->GetSuccessors();
-  DCHECK_EQ(targets->size(), 1u);
-  llvm::BasicBlock* target_block = llvm_data_->GetBlock(targets->at(0));
-  llvm_data_->builder_.CreateBr(target_block);
-}
-void CodeGenVisitor::Visit(IfEqzInstructionNode* instruction) {
-  std::string instr = instruction->GetInstruction()->DumpString(NULL);
-  std::cout << "9. Instruction: " << instr << "; Id: " <<instruction << std::endl;
-  std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
-  DCHECK_GT(ssa_uses.size(), 0u);
-  InstructionNode* use_l = ssa_uses.at(0);
-  llvm::Value* left = llvm_data_->GetValue(use_l);
-  llvm::Value* ifeqz = llvm_data_->builder_.CreateICmpEQ(left,
-      llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt::getNullValue(32)),
-      instruction->StringId());
-  DCHECK(instruction->GetRegion() != NULL);
-  std::vector<Region*>* successors = instruction->GetRegion()->GetSuccessors();
-  DCHECK_GT(successors->size(), 0u);
-  llvm::BasicBlock* then_block = llvm_data_->GetBlock(successors->at(0));
-  llvm::BasicBlock* else_block = llvm_data_->GetBlock(successors->at(1));
-  llvm_data_->builder_.CreateCondBr(ifeqz, then_block, else_block);
-}
-
-void CodeGenPostpassVisitor::Visit(PhiInstructionNode* phi) {
-  std::cout << "10. Instruction: Phi(" << phi->GetRegisterNumber() << ")" << std::endl;
-  Region* r = phi->GetRegion();
-  const std::vector<Region*>* predecessors = r->GetPredecessors();
-  DCHECK(NULL != predecessors);
-  DCHECK_GT(predecessors->size(), 0u);
-  // Prepass (CodeGenPrepassVisitor) should create the phi function value.
-  llvm::PHINode* llvm_phi = (llvm::PHINode*) llvm_data_->GetValue(phi);
-  int predecessor_pos = 0;
-  for (std::vector<Region*>::const_iterator cit = predecessors->begin();
-      cit != predecessors->end(); ++cit) {
-    std::vector<InstructionNode*>* defining_instructions = phi->GetSSAUses(predecessor_pos++);
-    DCHECK_EQ(defining_instructions->size(), 1u);
-    InstructionNode* defining_instruction = defining_instructions->at(0);
-    DCHECK(NULL != defining_instruction);
-    Region* incoming_region = *cit;
-    llvm::BasicBlock* incoming_basic_block = llvm_data_->GetBlock(incoming_region);
-    llvm::Value* incoming_value = llvm_data_->GetValue(defining_instruction);
-    llvm_phi->addIncoming(incoming_value, incoming_basic_block);
-  }
-}
-
-void CodeGenVisitor::Visit(SignatureNode* signature) {
-  DCHECK_EQ(signature->GetDefinitions().size(), 1u) <<
-      "Signature nodes must correspond to a single parameter register.";
-}
-void CodeGenPrepassVisitor::Visit(SignatureNode* signature) {
-  DCHECK_EQ(signature->GetDefinitions().size(), 1u) <<
-      "Signature nodes must correspond to a single parameter register.";
-}
-void CodeGenPostpassVisitor::Visit(SignatureNode* signature) {
-  DCHECK_EQ(signature->GetDefinitions().size(), 1u) <<
-      "Signature nodes must correspond to a single parameter register.";
-}
-
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/code_gen/code_gen.h b/compiler/sea_ir/code_gen/code_gen.h
deleted file mode 100644
index 544e9f0..0000000
--- a/compiler/sea_ir/code_gen/code_gen.h
+++ /dev/null
@@ -1,171 +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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_
-#define ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_
-
-#include "instruction_set.h"
-#include "llvm/Analysis/Verifier.h"
-#include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Analysis/Verifier.h"
-#include "sea_ir/ir/visitor.h"
-
-namespace sea_ir {
-// Abstracts away the containers we use to map SEA IR objects to LLVM IR objects.
-class CodeGenData {
- public:
-  explicit CodeGenData(): context_(&llvm::getGlobalContext()), module_("sea_ir", *context_),
-      builder_(*context_), function_(), blocks_(), values_() { }
-  // Returns the llvm::BasicBlock* corresponding to the sea_ir::Region with id @region_id.
-  llvm::BasicBlock* GetBlock(int region_id) {
-    std::map<int, llvm::BasicBlock*>::iterator block_it = blocks_.find(region_id);
-    DCHECK(block_it != blocks_.end());
-    return block_it->second;
-  }
-  // Returns the llvm::BasicBlock* corresponding top the sea_ir::Region @region.
-  llvm::BasicBlock* GetBlock(Region* region) {
-    return GetBlock(region->Id());
-  }
-  // Records @block as corresponding to the sea_ir::Region with id @region_id.
-  void AddBlock(int region_id, llvm::BasicBlock* block) {
-    blocks_.insert(std::pair<int, llvm::BasicBlock*>(region_id, block));
-  }
-  // Records @block as corresponding to the sea_ir::Region with @region.
-  void AddBlock(Region* region, llvm::BasicBlock* block) {
-    AddBlock(region->Id(), block);
-  }
-
-  llvm::Value* GetValue(int instruction_id) {
-    std::map<int, llvm::Value*>::iterator value_it = values_.find(instruction_id);
-    DCHECK(value_it != values_.end());
-    return value_it->second;
-  }
-  // Returns the llvm::Value* corresponding to the output of @instruction.
-  llvm::Value* GetValue(InstructionNode* instruction) {
-    return GetValue(instruction->Id());
-  }
-  // Records @value as corresponding to the sea_ir::InstructionNode with id @instruction_id.
-  void AddValue(int instruction_id, llvm::Value* value) {
-    values_.insert(std::pair<int, llvm::Value*>(instruction_id, value));
-  }
-  // Records @value as corresponding to the sea_ir::InstructionNode  @instruction.
-  void AddValue(InstructionNode* instruction, llvm::Value* value) {
-      AddValue(instruction->Id(), value);
-  }
-  // Generates and returns in @elf the executable code corresponding to the llvm module
-  //
-  std::string GetElf(art::InstructionSet instruction_set);
-
-  llvm::LLVMContext* const context_;
-  llvm::Module module_;
-  llvm::IRBuilder<> builder_;
-  llvm::Function* function_;
-
- private:
-  std::map<int, llvm::BasicBlock*> blocks_;
-  std::map<int, llvm::Value*> values_;
-};
-
-class CodeGenPassVisitor: public IRVisitor {
- public:
-  explicit CodeGenPassVisitor(CodeGenData* cgd): llvm_data_(cgd) { }
-  CodeGenPassVisitor(): llvm_data_(new CodeGenData()) { }
-  // Initialize any data structure needed before the start of visiting.
-  virtual void Initialize(SeaGraph* graph);
-  CodeGenData* GetData() {
-    return llvm_data_;
-  }
-  void Write(std::string file) {
-      llvm_data_->module_.dump();
-      llvm::verifyFunction(*llvm_data_->function_);
-    }
-
- protected:
-  CodeGenData* const llvm_data_;
-};
-
-class CodeGenPrepassVisitor: public CodeGenPassVisitor {
- public:
-  explicit CodeGenPrepassVisitor(const std::string& function_name):
-    function_name_(function_name) { }
-  void Visit(SeaGraph* graph);
-  void Visit(SignatureNode* region);
-  void Visit(Region* region);
-  void Visit(InstructionNode* instruction) { }
-
-  void Visit(UnnamedConstInstructionNode* instruction) { }
-  void Visit(ConstInstructionNode* instruction) { }
-  void Visit(ReturnInstructionNode* instruction) { }
-  void Visit(IfNeInstructionNode* instruction) { }
-  // void Visit(AddIntLitInstructionNode* instruction) { }
-  void Visit(MoveResultInstructionNode* instruction) { }
-  void Visit(InvokeStaticInstructionNode* instruction) { }
-  void Visit(AddIntInstructionNode* instruction) { }
-  void Visit(GotoInstructionNode* instruction) { }
-  void Visit(IfEqzInstructionNode* instruction) { }
-  void Visit(PhiInstructionNode* region);
-
- private:
-  std::string function_name_;
-};
-
-class CodeGenPostpassVisitor: public CodeGenPassVisitor {
- public:
-  explicit CodeGenPostpassVisitor(CodeGenData* code_gen_data): CodeGenPassVisitor(code_gen_data) { }
-  void Visit(SeaGraph* graph);
-  void Visit(SignatureNode* region);
-  void Visit(Region* region);
-  void Visit(InstructionNode* region) { }
-  void Visit(UnnamedConstInstructionNode* instruction) { }
-  void Visit(ConstInstructionNode* instruction) { }
-  void Visit(ReturnInstructionNode* instruction) { }
-  void Visit(IfNeInstructionNode* instruction) { }
-  // void Visit(AddIntLitInstructionNode* instruction) { }
-  void Visit(MoveResultInstructionNode* instruction) { }
-  void Visit(InvokeStaticInstructionNode* instruction) { }
-  void Visit(AddIntInstructionNode* instruction) { }
-  void Visit(GotoInstructionNode* instruction) { }
-  void Visit(IfEqzInstructionNode* instruction) { }
-  void Visit(PhiInstructionNode* region);
-};
-
-class CodeGenVisitor: public CodeGenPassVisitor {
- public:
-  explicit CodeGenVisitor(CodeGenData* code_gen_data,
-      const art::DexFile& dex_file): CodeGenPassVisitor(code_gen_data), dex_file_(dex_file) { }
-  void Visit(SeaGraph* graph);
-  void Visit(SignatureNode* region);
-  void Visit(Region* region);
-  void Visit(InstructionNode* region);
-  void Visit(UnnamedConstInstructionNode* instruction);
-  void Visit(ConstInstructionNode* instruction);
-  void Visit(ReturnInstructionNode* instruction);
-  void Visit(IfNeInstructionNode* instruction);
-  void Visit(MoveResultInstructionNode* instruction);
-  void Visit(InvokeStaticInstructionNode* instruction);
-  void Visit(AddIntInstructionNode* instruction);
-  void Visit(GotoInstructionNode* instruction);
-  void Visit(IfEqzInstructionNode* instruction);
-  void Visit(PhiInstructionNode* region) { }
-
- private:
-  std::string function_name_;
-  const art::DexFile& dex_file_;
-};
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_
diff --git a/compiler/sea_ir/code_gen/code_gen_data.cc b/compiler/sea_ir/code_gen/code_gen_data.cc
deleted file mode 100644
index 17f64db..0000000
--- a/compiler/sea_ir/code_gen/code_gen_data.cc
+++ /dev/null
@@ -1,104 +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.
- */
-
-#include <string>
-#include <llvm/PassManager.h>
-#include <llvm/Support/TargetRegistry.h>
-#include <llvm/Support/FormattedStream.h>
-#include <llvm/Target/TargetMachine.h>
-#include <llvm/Transforms/IPO.h>
-#include <llvm/Transforms/IPO/PassManagerBuilder.h>
-
-#include "base/logging.h"
-#include "driver/compiler_driver.h"
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/code_gen/code_gen.h"
-
-
-namespace sea_ir {
-std::string CodeGenData::GetElf(art::InstructionSet instruction_set) {
-  std::string elf;
-  ::llvm::raw_string_ostream out_stream(elf);
-  // Lookup the LLVM target
-  std::string target_triple;
-  std::string target_cpu;
-  std::string target_attr;
-  art::CompilerDriver::InstructionSetToLLVMTarget(instruction_set,
-      target_triple, target_cpu, target_attr);
-
-  std::string errmsg;
-  const ::llvm::Target* target =
-    ::llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
-
-  CHECK(target != NULL) << errmsg;
-
-  // Target options
-  ::llvm::TargetOptions target_options;
-  target_options.FloatABIType = ::llvm::FloatABI::Soft;
-  target_options.NoFramePointerElim = true;
-  target_options.NoFramePointerElimNonLeaf = true;
-  target_options.UseSoftFloat = false;
-  target_options.EnableFastISel = false;
-
-  // Create the ::llvm::TargetMachine
-  ::llvm::OwningPtr< ::llvm::TargetMachine> target_machine(
-    target->createTargetMachine(target_triple, target_cpu, target_attr, target_options,
-                                ::llvm::Reloc::Static, ::llvm::CodeModel::Small,
-                                ::llvm::CodeGenOpt::Aggressive));
-
-  CHECK(target_machine.get() != NULL) << "Failed to create target machine";
-
-  // Add target data
-  const ::llvm::DataLayout* data_layout = target_machine->getDataLayout();
-
-  // PassManager for code generation passes
-  ::llvm::PassManager pm;
-  pm.add(new ::llvm::DataLayout(*data_layout));
-
-  // FunctionPassManager for optimization pass
-  ::llvm::FunctionPassManager fpm(&module_);
-  fpm.add(new ::llvm::DataLayout(*data_layout));
-
-  // Add optimization pass
-  ::llvm::PassManagerBuilder pm_builder;
-  // TODO: Use inliner after we can do IPO.
-  pm_builder.Inliner = NULL;
-  // pm_builder.Inliner = ::llvm::createFunctionInliningPass();
-  // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass();
-  // pm_builder.Inliner = ::llvm::createPartialInliningPass();
-  pm_builder.OptLevel = 3;
-  pm_builder.DisableSimplifyLibCalls = 1;
-  pm_builder.DisableUnitAtATime = 1;
-  pm_builder.populateFunctionPassManager(fpm);
-  pm_builder.populateModulePassManager(pm);
-  pm.add(::llvm::createStripDeadPrototypesPass());
-  // Add passes to emit ELF image
-  {
-    ::llvm::formatted_raw_ostream formatted_os(out_stream, false);
-    // Ask the target to add backend passes as necessary.
-    if (target_machine->addPassesToEmitFile(pm,
-                                            formatted_os,
-                                            ::llvm::TargetMachine::CGFT_ObjectFile,
-                                            true)) {
-      LOG(FATAL) << "Unable to generate ELF for this target";
-    }
-
-    // Run the code generation passes
-    pm.run(module_);
-  }
-  return elf;
-}
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/debug/dot_gen.cc b/compiler/sea_ir/debug/dot_gen.cc
deleted file mode 100644
index 9442684..0000000
--- a/compiler/sea_ir/debug/dot_gen.cc
+++ /dev/null
@@ -1,173 +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.
- */
-
-
-#include "scoped_thread_state_change.h"
-#include "sea_ir/debug/dot_gen.h"
-
-namespace sea_ir {
-
-void DotGenerationVisitor::Initialize(SeaGraph* graph) {
-  graph_ = graph;
-  Region* root_region;
-  ordered_regions_.clear();
-  for (std::vector<Region*>::const_iterator cit = graph->GetRegions()->begin();
-      cit != graph->GetRegions()->end(); cit++ ) {
-    if ((*cit)->GetIDominator() == (*cit)) {
-      root_region = *cit;
-    }
-  }
-  ordered_regions_.push_back(root_region);
-  for (unsigned int id = 0; id < ordered_regions_.size(); id++) {
-    Region* current_region = ordered_regions_.at(id);
-    const std::set<Region*>* dominated_regions = current_region->GetIDominatedSet();
-    for (std::set<Region*>::const_iterator cit = dominated_regions->begin();
-        cit != dominated_regions->end(); cit++ ) {
-      ordered_regions_.push_back(*cit);
-    }
-  }
-}
-
-void DotGenerationVisitor::ToDotSSAEdges(InstructionNode* instruction) {
-  std::map<int, InstructionNode*>* definition_edges = instruction->GetSSAProducersMap();
-  // SSA definitions:
-  for (std::map<int, InstructionNode*>::const_iterator
-      def_it = definition_edges->begin();
-      def_it != definition_edges->end(); def_it++) {
-    if (NULL != def_it->second) {
-      dot_text_ += def_it->second->StringId() + " -> ";
-      dot_text_ += instruction->StringId() + "[color=gray,label=\"";
-      dot_text_ += art::StringPrintf("vR = %d", def_it->first);
-      art::SafeMap<int, const Type*>::const_iterator type_it = types_->find(def_it->second->Id());
-      if (type_it != types_->end()) {
-        art::ScopedObjectAccess soa(art::Thread::Current());
-        dot_text_ += "(" + type_it->second->Dump() + ")";
-      } else {
-        dot_text_ += "()";
-      }
-      dot_text_ += "\"] ; // SSA edge\n";
-    }
-  }
-
-  // SSA used-by:
-  if (options_->WillSaveUseEdges()) {
-    std::vector<InstructionNode*>* used_in = instruction->GetSSAConsumers();
-    for (std::vector<InstructionNode*>::const_iterator cit = used_in->begin();
-        cit != used_in->end(); cit++) {
-      dot_text_ += (*cit)->StringId() + " -> " + instruction->StringId() + "[color=gray,label=\"";
-      dot_text_ += "\"] ; // SSA used-by edge\n";
-    }
-  }
-}
-
-void DotGenerationVisitor::ToDotSSAEdges(PhiInstructionNode* instruction) {
-  std::vector<InstructionNode*> definition_edges = instruction->GetSSAProducers();
-  // SSA definitions:
-  for (std::vector<InstructionNode*>::const_iterator
-      def_it = definition_edges.begin();
-      def_it != definition_edges.end(); def_it++) {
-    if (NULL != *def_it) {
-      dot_text_ += (*def_it)->StringId() + " -> ";
-      dot_text_ += instruction->StringId() + "[color=gray,label=\"";
-      dot_text_ += art::StringPrintf("vR = %d", instruction->GetRegisterNumber());
-      art::SafeMap<int, const Type*>::const_iterator type_it = types_->find((*def_it)->Id());
-      if (type_it != types_->end()) {
-        art::ScopedObjectAccess soa(art::Thread::Current());
-        dot_text_ += "(" + type_it->second->Dump() + ")";
-      } else {
-        dot_text_ += "()";
-      }
-      dot_text_ += "\"] ; // SSA edge\n";
-    }
-  }
-
-  // SSA used-by:
-  if (options_->WillSaveUseEdges()) {
-    std::vector<InstructionNode*>* used_in = instruction->GetSSAConsumers();
-    for (std::vector<InstructionNode*>::const_iterator cit = used_in->begin();
-        cit != used_in->end(); cit++) {
-      dot_text_ += (*cit)->StringId() + " -> " + instruction->StringId() + "[color=gray,label=\"";
-      dot_text_ += "\"] ; // SSA used-by edge\n";
-    }
-  }
-}
-
-void DotGenerationVisitor::Visit(SignatureNode* parameter) {
-  dot_text_ += parameter->StringId() +" [label=\"[" + parameter->StringId() + "] signature:";
-  dot_text_ += art::StringPrintf("r%d", parameter->GetResultRegister());
-  dot_text_ += "\"] // signature node\n";
-  ToDotSSAEdges(parameter);
-}
-
-// Appends to @result a dot language formatted string representing the node and
-//    (by convention) outgoing edges, so that the composition of theToDot() of all nodes
-//    builds a complete dot graph (without prolog and epilog though).
-void DotGenerationVisitor::Visit(Region* region) {
-  dot_text_ += "\n// Region: \nsubgraph " + region->StringId();
-  dot_text_ += " { label=\"region " + region->StringId() + "(rpo=";
-  dot_text_ += art::StringPrintf("%d", region->GetRPO());
-  if (NULL != region->GetIDominator()) {
-    dot_text_ += " dom=" + region->GetIDominator()->StringId();
-  }
-  dot_text_ += ")\";\n";
-
-  std::vector<PhiInstructionNode*>* phi_instructions = region->GetPhiNodes();
-  for (std::vector<PhiInstructionNode*>::const_iterator cit = phi_instructions->begin();
-        cit != phi_instructions->end(); cit++) {
-    dot_text_ += (*cit)->StringId() +";\n";
-  }
-  std::vector<InstructionNode*>* instructions = region->GetInstructions();
-  for (std::vector<InstructionNode*>::const_iterator cit = instructions->begin();
-        cit != instructions->end(); cit++) {
-      dot_text_ += (*cit)->StringId() +";\n";
-    }
-
-  dot_text_ += "} // End Region.\n";
-  std::vector<Region*>* successors =  region->GetSuccessors();
-  for (std::vector<Region*>::const_iterator cit = successors->begin(); cit != successors->end();
-      cit++) {
-    DCHECK(NULL != *cit) << "Null successor found for SeaNode" <<
-        region->GetLastChild()->StringId() << ".";
-    dot_text_ += region->GetLastChild()->StringId() + " -> " +
-        (*cit)->GetLastChild()->StringId() +
-        "[lhead=" + (*cit)->StringId() + ", " + "ltail=" + region->StringId() + "];\n\n";
-  }
-}
-void DotGenerationVisitor::Visit(InstructionNode* instruction) {
-  dot_text_ += "// Instruction ("+instruction->StringId()+"): \n" + instruction->StringId() +
-      " [label=\"[" + instruction->StringId() + "] " +
-      instruction->GetInstruction()->DumpString(graph_->GetDexFile()) + "\"";
-  dot_text_ += "];\n";
-  ToDotSSAEdges(instruction);
-}
-
-void DotGenerationVisitor::Visit(UnnamedConstInstructionNode* instruction) {
-  dot_text_ += "// Instruction ("+instruction->StringId()+"): \n" + instruction->StringId() +
-        " [label=\"[" + instruction->StringId() + "] const/x v-3, #" +
-        art::StringPrintf("%d", instruction->GetConstValue()) + "\"";
-  dot_text_ += "];\n";
-  ToDotSSAEdges(instruction);
-}
-
-void DotGenerationVisitor::Visit(PhiInstructionNode* phi) {
-  dot_text_ += "// PhiInstruction: \n" + phi->StringId() +
-      " [label=\"[" + phi->StringId() + "] PHI(";
-  dot_text_ += art::StringPrintf("%d", phi->GetRegisterNumber());
-  dot_text_ += ")\"";
-  dot_text_ += "];\n";
-  ToDotSSAEdges(phi);
-}
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/debug/dot_gen.h b/compiler/sea_ir/debug/dot_gen.h
deleted file mode 100644
index a5d6819..0000000
--- a/compiler/sea_ir/debug/dot_gen.h
+++ /dev/null
@@ -1,121 +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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_DEBUG_DOT_GEN_H_
-#define ART_COMPILER_SEA_IR_DEBUG_DOT_GEN_H_
-
-#include "safe_map.h"
-#include "base/stringprintf.h"
-#include "file_output_stream.h"
-#include "os.h"
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/types/type_inference.h"
-
-namespace sea_ir {
-
-class DotConversionOptions {
- public:
-  DotConversionOptions(): save_use_edges_(false) { }
-  bool WillSaveUseEdges() const {
-    return save_use_edges_;
-  }
- private:
-  bool save_use_edges_;
-};
-
-class DotGenerationVisitor: public IRVisitor {
- public:
-  explicit DotGenerationVisitor(const DotConversionOptions* const options,
-      art::SafeMap<int, const Type*>* types): graph_(), types_(types), options_(options) { }
-
-  virtual void Initialize(SeaGraph* graph);
-  // Saves the ssa def->use edges corresponding to @instruction.
-  void ToDotSSAEdges(InstructionNode* instruction);
-  void ToDotSSAEdges(PhiInstructionNode* instruction);
-  void Visit(SeaGraph* graph) {
-    dot_text_ += "digraph seaOfNodes {\ncompound=true\n";
-  }
-  void Visit(SignatureNode* parameter);
-
-  // Appends to @result a dot language formatted string representing the node and
-  //    (by convention) outgoing edges, so that the composition of theToDot() of all nodes
-  //    builds a complete dot graph (without prolog and epilog though).
-  void Visit(Region* region);
-  void Visit(InstructionNode* instruction);
-  void Visit(PhiInstructionNode* phi);
-  void Visit(UnnamedConstInstructionNode* instruction);
-
-  void Visit(ConstInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(ReturnInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(IfNeInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(MoveResultInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(InvokeStaticInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(AddIntInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(GotoInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-  void Visit(IfEqzInstructionNode* instruction) {
-    Visit(reinterpret_cast<InstructionNode*>(instruction));
-  }
-
-  std::string GetResult() const {
-    return dot_text_;
-  }
-
- private:
-  std::string dot_text_;
-  SeaGraph* graph_;
-  art::SafeMap<int, const Type*>* types_;
-  const DotConversionOptions* const options_;
-};
-
-// Stores options for turning a SEA IR graph to a .dot file.
-class DotConversion {
- public:
-  DotConversion(): options_() { }
-  // Saves to @filename the .dot representation of @graph with the options @options.
-  void DumpSea(SeaGraph* graph, std::string filename,
-      art::SafeMap<int, const Type*>* types) const {
-    LOG(INFO) << "Starting to write SEA string to file " << filename << std::endl;
-    DotGenerationVisitor dgv = DotGenerationVisitor(&options_, types);
-    graph->Accept(&dgv);
-    // TODO: std::unique_ptr to close file properly. Switch to BufferedOutputStream.
-    art::File* file = art::OS::CreateEmptyFile(filename.c_str());
-    art::FileOutputStream fos(file);
-    std::string graph_as_string = dgv.GetResult();
-    graph_as_string += "}";
-    fos.WriteFully(graph_as_string.c_str(), graph_as_string.size());
-    LOG(INFO) << "Written SEA string to file.";
-  }
-
- private:
-  DotConversionOptions options_;
-};
-
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_DEBUG_DOT_GEN_H_
diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc
deleted file mode 100644
index b57007b..0000000
--- a/compiler/sea_ir/frontend.cc
+++ /dev/null
@@ -1,93 +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.
- */
-
-#ifdef ART_SEA_IR_MODE
-#include <llvm/Support/Threading.h>
-#include <llvm/Support/raw_ostream.h>
-#include <llvm/Bitcode/ReaderWriter.h>
-
-#include "base/logging.h"
-#include "llvm/llvm_compilation_unit.h"
-#include "dex/portable/mir_to_gbc.h"
-#include "driver/compiler_driver.h"
-#include "verifier/method_verifier.h"
-#include "mirror/object.h"
-#include "utils.h"
-
-#include "runtime.h"
-#include "safe_map.h"
-
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/debug/dot_gen.h"
-#include "sea_ir/types/types.h"
-#include "sea_ir/code_gen/code_gen.h"
-
-namespace art {
-
-static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler,
-                                     CompilerBackend* compiler_backend,
-                                     const DexFile::CodeItem* code_item,
-                                     uint32_t method_access_flags, InvokeType invoke_type,
-                                     uint16_t class_def_idx, uint32_t method_idx,
-                                     jobject class_loader, const DexFile& dex_file,
-                                     void* llvm_compilation_unit) {
-  LOG(INFO) << "Compiling " << PrettyMethod(method_idx, dex_file) << ".";
-  sea_ir::SeaGraph* ir_graph = sea_ir::SeaGraph::GetGraph(dex_file);
-  std::string symbol = "dex_" + MangleForJni(PrettyMethod(method_idx, dex_file));
-  sea_ir::CodeGenData* llvm_data = ir_graph->CompileMethod(symbol,
-          code_item, class_def_idx, method_idx, method_access_flags, dex_file);
-  sea_ir::DotConversion dc;
-  SafeMap<int, const sea_ir::Type*>*  types = ir_graph->ti_->GetTypeMap();
-  dc.DumpSea(ir_graph, "/tmp/temp.dot", types);
-  MethodReference mref(&dex_file, method_idx);
-  std::string llvm_code = llvm_data->GetElf(compiler.GetInstructionSet());
-  CompiledMethod* compiled_method =
-      new CompiledMethod(compiler, compiler.GetInstructionSet(), llvm_code,
-                         *compiler.GetVerifiedMethodsData()->GetDexGcMap(mref), symbol);
-  LOG(INFO) << "Compiled SEA IR method " << PrettyMethod(method_idx, dex_file) << ".";
-  return compiled_method;
-}
-
-CompiledMethod* SeaIrCompileOneMethod(CompilerDriver& compiler,
-                                 CompilerBackend* backend,
-                                 const DexFile::CodeItem* code_item,
-                                 uint32_t method_access_flags,
-                                 InvokeType invoke_type,
-                                 uint16_t class_def_idx,
-                                 uint32_t method_idx,
-                                 jobject class_loader,
-                                 const DexFile& dex_file,
-                                 void* llvm_compilation_unit) {
-  return CompileMethodWithSeaIr(compiler, backend, code_item, method_access_flags, invoke_type,
-      class_def_idx, method_idx, class_loader, dex_file, llvm_compilation_unit);
-}
-
-extern "C" art::CompiledMethod*
-    SeaIrCompileMethod(art::CompilerDriver& compiler,
-                          const art::DexFile::CodeItem* code_item,
-                          uint32_t method_access_flags, art::InvokeType invoke_type,
-                          uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
-                          const art::DexFile& dex_file) {
-  // TODO: Check method fingerprint here to determine appropriate backend type.
-  //       Until then, use build default
-  art::CompilerBackend* backend = compiler.GetCompilerBackend();
-  return art::SeaIrCompileOneMethod(compiler, backend, code_item, method_access_flags, invoke_type,
-                               class_def_idx, method_idx, class_loader, dex_file,
-                               NULL /* use thread llvm_info */);
-}
-#endif
-
-}  // namespace art
diff --git a/compiler/sea_ir/ir/instruction_nodes.h b/compiler/sea_ir/ir/instruction_nodes.h
deleted file mode 100644
index 63e89e7..0000000
--- a/compiler/sea_ir/ir/instruction_nodes.h
+++ /dev/null
@@ -1,248 +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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_IR_INSTRUCTION_NODES_H_
-#define ART_COMPILER_SEA_IR_IR_INSTRUCTION_NODES_H_
-#include "dex_instruction-inl.h"
-#include "sea_ir/ir/sea_node.h"
-#include "sea_ir/ir/visitor.h"
-
-
-namespace sea_ir {
-
-enum SpecialRegisters {
-  NO_REGISTER = -1,             // Usually signifies that there is no register
-                                // that respects the condition you asked for.
-  RETURN_REGISTER = -2,         // Written by the invoke* instructions, read by move-results.
-  UNNAMED_CONST_REGISTER = -3   // Written by UnnamedConst* instructions, read by *Lit* instruction.
-};
-
-class IRVisitor;
-
-// This class represents an instruction in SEA IR.
-// As we add support for specific classes of instructions,
-// the number of InstructionNode objects should dwindle, while the
-// number of subclasses and instances of subclasses will go up.
-class InstructionNode: public SeaNode {
- public:
-  static std::vector<sea_ir::InstructionNode*> Create(const art::Instruction* in);
-  // Returns the Dalvik instruction around which this InstructionNode is wrapped.
-  const art::Instruction* GetInstruction() const {
-    DCHECK(NULL != instruction_) << "Tried to access NULL instruction in an InstructionNode.";
-    return instruction_;
-  }
-  // Returns the register that is defined by the current instruction, or NO_REGISTER otherwise.
-  virtual int GetResultRegister() const;
-  // Returns the set of registers defined by the current instruction.
-  virtual std::vector<int> GetDefinitions() const;
-  // Returns the set of register numbers that are used by the instruction.
-  virtual std::vector<int> GetUses() const;
-  // Mark the current instruction as a downward exposed definition.
-  void MarkAsDEDef();
-  // Rename the use of @reg_no to refer to the instruction @definition,
-  // essentially creating SSA form.
-  void RenameToSSA(int reg_no, InstructionNode* definition) {
-    definition_edges_.insert(std::pair<int, InstructionNode*>(reg_no, definition));
-    DCHECK(NULL != definition) << "SSA definition for register " << reg_no
-        << " used in instruction " << Id() << " not found.";
-    definition->AddSSAUse(this);
-  }
-  // Returns the ordered set of Instructions that define the input operands of this instruction.
-  // Precondition: SeaGraph.ConvertToSSA().
-  virtual std::vector<InstructionNode*> GetSSAProducers() {
-    std::vector<int> uses = GetUses();
-    std::vector<InstructionNode*> ssa_uses;
-    for (std::vector<int>::const_iterator cit = uses.begin(); cit != uses.end(); cit++) {
-      ssa_uses.push_back((*definition_edges_.find(*cit)).second);
-    }
-    return ssa_uses;
-  }
-  std::map<int, InstructionNode* >* GetSSAProducersMap() {
-    return &definition_edges_;
-  }
-  std::vector<InstructionNode*>* GetSSAConsumers() {
-    return &used_in_;
-  }
-  virtual void AddSSAUse(InstructionNode* use) {
-    used_in_.push_back(use);
-  }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-  // Set the region to which this instruction belongs.
-  Region* GetRegion() {
-    DCHECK(NULL != region_);
-    return region_;
-  }
-  // Get the region to which this instruction belongs.
-  void SetRegion(Region* region) {
-    region_ = region;
-  }
-
- protected:
-  explicit InstructionNode(const art::Instruction* in):
-      SeaNode(), instruction_(in), used_in_(), de_def_(false), region_(NULL) { }
-
- protected:
-  const art::Instruction* const instruction_;
-  std::map<int, InstructionNode* > definition_edges_;  // Maps used registers to their definitions.
-  // Stores pointers to instructions that use the result of the current instruction.
-  std::vector<InstructionNode*> used_in_;
-  bool de_def_;
-  Region* region_;
-};
-
-class ConstInstructionNode: public InstructionNode {
- public:
-  explicit ConstInstructionNode(const art::Instruction* inst):
-      InstructionNode(inst) { }
-
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-
-  virtual int32_t GetConstValue() const {
-    return GetInstruction()->VRegB_11n();
-  }
-};
-
-class UnnamedConstInstructionNode: public ConstInstructionNode {
- public:
-  explicit UnnamedConstInstructionNode(const art::Instruction* inst, int32_t value):
-      ConstInstructionNode(inst), value_(value) { }
-
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-
-  int GetResultRegister() const {
-    return UNNAMED_CONST_REGISTER;
-  }
-
-  int32_t GetConstValue() const {
-    return value_;
-  }
-
- private:
-  const int32_t value_;
-};
-
-class ReturnInstructionNode: public InstructionNode {
- public:
-  explicit ReturnInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-
-class IfNeInstructionNode: public InstructionNode {
- public:
-  explicit IfNeInstructionNode(const art::Instruction* inst): InstructionNode(inst) {
-    DCHECK(InstructionTools::IsDefinition(inst) == false);
-  }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-
-
-
-class MoveResultInstructionNode: public InstructionNode {
- public:
-  explicit MoveResultInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
-  std::vector<int> GetUses() const {
-    std::vector<int> uses;  // Using vector<> instead of set<> because order matters.
-    uses.push_back(RETURN_REGISTER);
-    return uses;
-  }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-
-class InvokeStaticInstructionNode: public InstructionNode {
- public:
-  explicit InvokeStaticInstructionNode(const art::Instruction* inst): InstructionNode(inst),
-    method_index_(inst->VRegB_35c()) { }
-  int GetResultRegister() const {
-    return RETURN_REGISTER;
-  }
-
-  int GetCalledMethodIndex() const {
-    return method_index_;
-  }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-
- private:
-  const uint32_t method_index_;
-};
-
-class AddIntInstructionNode: public InstructionNode {
- public:
-  explicit AddIntInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-
-class AddIntLitInstructionNode: public AddIntInstructionNode {
- public:
-  explicit AddIntLitInstructionNode(const art::Instruction* inst):
-      AddIntInstructionNode(inst) { }
-
-  std::vector<int> GetUses() const {
-    std::vector<int> uses =  AddIntInstructionNode::GetUses();
-    uses.push_back(UNNAMED_CONST_REGISTER);
-    return uses;
-    }
-
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-
-class GotoInstructionNode: public InstructionNode {
- public:
-  explicit GotoInstructionNode(const art::Instruction* inst): InstructionNode(inst) { }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-
-class IfEqzInstructionNode: public InstructionNode {
- public:
-  explicit IfEqzInstructionNode(const art::Instruction* inst): InstructionNode(inst) {
-    DCHECK(InstructionTools::IsDefinition(inst) == false);
-  }
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-};
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_IR_INSTRUCTION_NODES_H_
diff --git a/compiler/sea_ir/ir/instruction_tools.cc b/compiler/sea_ir/ir/instruction_tools.cc
deleted file mode 100644
index 143209d..0000000
--- a/compiler/sea_ir/ir/instruction_tools.cc
+++ /dev/null
@@ -1,797 +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.
- */
-
-#include "sea_ir/ir/instruction_tools.h"
-
-namespace sea_ir {
-
-bool InstructionTools::IsDefinition(const art::Instruction* const instruction) {
-  if (0 != (InstructionTools::instruction_attributes_[instruction->Opcode()] & (1 << kDA))) {
-    return true;
-  }
-  return false;
-}
-
-const int InstructionTools::instruction_attributes_[] = {
-  // 00 NOP
-  DF_NOP,
-
-  // 01 MOVE vA, vB
-  DF_DA | DF_UB | DF_IS_MOVE,
-
-  // 02 MOVE_FROM16 vAA, vBBBB
-  DF_DA | DF_UB | DF_IS_MOVE,
-
-  // 03 MOVE_16 vAAAA, vBBBB
-  DF_DA | DF_UB | DF_IS_MOVE,
-
-  // 04 MOVE_WIDE vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
-
-  // 05 MOVE_WIDE_FROM16 vAA, vBBBB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
-
-  // 06 MOVE_WIDE_16 vAAAA, vBBBB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
-
-  // 07 MOVE_OBJECT vA, vB
-  DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
-
-  // 08 MOVE_OBJECT_FROM16 vAA, vBBBB
-  DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
-
-  // 09 MOVE_OBJECT_16 vAAAA, vBBBB
-  DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
-
-  // 0A MOVE_RESULT vAA
-  DF_DA,
-
-  // 0B MOVE_RESULT_WIDE vAA
-  DF_DA | DF_A_WIDE,
-
-  // 0C MOVE_RESULT_OBJECT vAA
-  DF_DA | DF_REF_A,
-
-  // 0D MOVE_EXCEPTION vAA
-  DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
-  // 0E RETURN_VOID
-  DF_NOP,
-
-  // 0F RETURN vAA
-  DF_UA,
-
-  // 10 RETURN_WIDE vAA
-  DF_UA | DF_A_WIDE,
-
-  // 11 RETURN_OBJECT vAA
-  DF_UA | DF_REF_A,
-
-  // 12 CONST_4 vA, #+B
-  DF_DA | DF_SETS_CONST,
-
-  // 13 CONST_16 vAA, #+BBBB
-  DF_DA | DF_SETS_CONST,
-
-  // 14 CONST vAA, #+BBBBBBBB
-  DF_DA | DF_SETS_CONST,
-
-  // 15 CONST_HIGH16 VAA, #+BBBB0000
-  DF_DA | DF_SETS_CONST,
-
-  // 16 CONST_WIDE_16 vAA, #+BBBB
-  DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
-  // 17 CONST_WIDE_32 vAA, #+BBBBBBBB
-  DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
-  // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
-  DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
-  // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
-  DF_DA | DF_A_WIDE | DF_SETS_CONST,
-
-  // 1A CONST_STRING vAA, string@BBBB
-  DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
-  // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB
-  DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
-  // 1C CONST_CLASS vAA, type@BBBB
-  DF_DA | DF_REF_A | DF_NON_NULL_DST,
-
-  // 1D MONITOR_ENTER vAA
-  DF_UA | DF_NULL_CHK_0 | DF_REF_A,
-
-  // 1E MONITOR_EXIT vAA
-  DF_UA | DF_NULL_CHK_0 | DF_REF_A,
-
-  // 1F CHK_CAST vAA, type@BBBB
-  DF_UA | DF_REF_A | DF_UMS,
-
-  // 20 INSTANCE_OF vA, vB, type@CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS,
-
-  // 21 ARRAY_LENGTH vA, vB
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_CORE_A | DF_REF_B,
-
-  // 22 NEW_INSTANCE vAA, type@BBBB
-  DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS,
-
-  // 23 NEW_ARRAY vA, vB, type@CCCC
-  DF_DA | DF_UB | DF_NON_NULL_DST | DF_REF_A | DF_CORE_B | DF_UMS,
-
-  // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_NON_NULL_RET | DF_UMS,
-
-  // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
-  DF_FORMAT_3RC | DF_NON_NULL_RET | DF_UMS,
-
-  // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB
-  DF_UA | DF_REF_A | DF_UMS,
-
-  // 27 THROW vAA
-  DF_UA | DF_REF_A | DF_UMS,
-
-  // 28 GOTO
-  DF_NOP,
-
-  // 29 GOTO_16
-  DF_NOP,
-
-  // 2A GOTO_32
-  DF_NOP,
-
-  // 2B PACKED_SWITCH vAA, +BBBBBBBB
-  DF_UA,
-
-  // 2C SPARSE_SWITCH vAA, +BBBBBBBB
-  DF_UA,
-
-  // 2D CMPL_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A,
-
-  // 2E CMPG_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A,
-
-  // 2F CMPL_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A,
-
-  // 30 CMPG_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A,
-
-  // 31 CMP_LONG vAA, vBB, vCC
-  DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 32 IF_EQ vA, vB, +CCCC
-  DF_UA | DF_UB,
-
-  // 33 IF_NE vA, vB, +CCCC
-  DF_UA | DF_UB,
-
-  // 34 IF_LT vA, vB, +CCCC
-  DF_UA | DF_UB,
-
-  // 35 IF_GE vA, vB, +CCCC
-  DF_UA | DF_UB,
-
-  // 36 IF_GT vA, vB, +CCCC
-  DF_UA | DF_UB,
-
-  // 37 IF_LE vA, vB, +CCCC
-  DF_UA | DF_UB,
-
-  // 38 IF_EQZ vAA, +BBBB
-  DF_UA,
-
-  // 39 IF_NEZ vAA, +BBBB
-  DF_UA,
-
-  // 3A IF_LTZ vAA, +BBBB
-  DF_UA,
-
-  // 3B IF_GEZ vAA, +BBBB
-  DF_UA,
-
-  // 3C IF_GTZ vAA, +BBBB
-  DF_UA,
-
-  // 3D IF_LEZ vAA, +BBBB
-  DF_UA,
-
-  // 3E UNUSED_3E
-  DF_NOP,
-
-  // 3F UNUSED_3F
-  DF_NOP,
-
-  // 40 UNUSED_40
-  DF_NOP,
-
-  // 41 UNUSED_41
-  DF_NOP,
-
-  // 42 UNUSED_42
-  DF_NOP,
-
-  // 43 UNUSED_43
-  DF_NOP,
-
-  // 44 AGET vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
-  // 45 AGET_WIDE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
-  // 46 AGET_OBJECT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C,
-
-  // 47 AGET_BOOLEAN vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
-  // 48 AGET_BYTE vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
-  // 49 AGET_CHAR vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
-  // 4A AGET_SHORT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
-
-  // 4B APUT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
-  // 4C APUT_WIDE vAA, vBB, vCC
-  DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C,
-
-  // 4D APUT_OBJECT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C,
-
-  // 4E APUT_BOOLEAN vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
-  // 4F APUT_BYTE vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
-  // 50 APUT_CHAR vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
-  // 51 APUT_SHORT vAA, vBB, vCC
-  DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
-
-  // 52 IGET vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // 53 IGET_WIDE vA, vB, field@CCCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // 54 IGET_OBJECT vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B,
-
-  // 55 IGET_BOOLEAN vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // 56 IGET_BYTE vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // 57 IGET_CHAR vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // 58 IGET_SHORT vA, vB, field@CCCC
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // 59 IPUT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
-  // 5A IPUT_WIDE vA, vB, field@CCCC
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B,
-
-  // 5B IPUT_OBJECT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B,
-
-  // 5C IPUT_BOOLEAN vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
-  // 5D IPUT_BYTE vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
-  // 5E IPUT_CHAR vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
-  // 5F IPUT_SHORT vA, vB, field@CCCC
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
-  // 60 SGET vAA, field@BBBB
-  DF_DA | DF_UMS,
-
-  // 61 SGET_WIDE vAA, field@BBBB
-  DF_DA | DF_A_WIDE | DF_UMS,
-
-  // 62 SGET_OBJECT vAA, field@BBBB
-  DF_DA | DF_REF_A | DF_UMS,
-
-  // 63 SGET_BOOLEAN vAA, field@BBBB
-  DF_DA | DF_UMS,
-
-  // 64 SGET_BYTE vAA, field@BBBB
-  DF_DA | DF_UMS,
-
-  // 65 SGET_CHAR vAA, field@BBBB
-  DF_DA | DF_UMS,
-
-  // 66 SGET_SHORT vAA, field@BBBB
-  DF_DA | DF_UMS,
-
-  // 67 SPUT vAA, field@BBBB
-  DF_UA | DF_UMS,
-
-  // 68 SPUT_WIDE vAA, field@BBBB
-  DF_UA | DF_A_WIDE | DF_UMS,
-
-  // 69 SPUT_OBJECT vAA, field@BBBB
-  DF_UA | DF_REF_A | DF_UMS,
-
-  // 6A SPUT_BOOLEAN vAA, field@BBBB
-  DF_UA | DF_UMS,
-
-  // 6B SPUT_BYTE vAA, field@BBBB
-  DF_UA | DF_UMS,
-
-  // 6C SPUT_CHAR vAA, field@BBBB
-  DF_UA | DF_UMS,
-
-  // 6D SPUT_SHORT vAA, field@BBBB
-  DF_UA | DF_UMS,
-
-  // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 6F INVOKE_SUPER {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 71 INVOKE_STATIC {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_UMS,
-
-  // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 73 UNUSED_73
-  DF_NOP,
-
-  // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_UMS,
-
-  // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // 79 UNUSED_79
-  DF_NOP,
-
-  // 7A UNUSED_7A
-  DF_NOP,
-
-  // 7B NEG_INT vA, vB
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // 7C NOT_INT vA, vB
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // 7D NEG_LONG vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // 7E NOT_LONG vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // 7F NEG_FLOAT vA, vB
-  DF_DA | DF_UB | DF_FP_A | DF_FP_B,
-
-  // 80 NEG_DOUBLE vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // 81 INT_TO_LONG vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // 82 INT_TO_FLOAT vA, vB
-  DF_DA | DF_UB | DF_FP_A | DF_CORE_B,
-
-  // 83 INT_TO_DOUBLE vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_CORE_B,
-
-  // 84 LONG_TO_INT vA, vB
-  DF_DA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // 85 LONG_TO_FLOAT vA, vB
-  DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B,
-
-  // 86 LONG_TO_DOUBLE vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B,
-
-  // 87 FLOAT_TO_INT vA, vB
-  DF_DA | DF_UB | DF_FP_B | DF_CORE_A,
-
-  // 88 FLOAT_TO_LONG vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_FP_B | DF_CORE_A,
-
-  // 89 FLOAT_TO_DOUBLE vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_FP_B,
-
-  // 8A DOUBLE_TO_INT vA, vB
-  DF_DA | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A,
-
-  // 8B DOUBLE_TO_LONG vA, vB
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A,
-
-  // 8C DOUBLE_TO_FLOAT vA, vB
-  DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // 8D INT_TO_BYTE vA, vB
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // 8E INT_TO_CHAR vA, vB
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // 8F INT_TO_SHORT vA, vB
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // 90 ADD_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 91 SUB_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 92 MUL_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 93 DIV_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 94 REM_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 95 AND_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 96 OR_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 97 XOR_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 98 SHL_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 99 SHR_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 9A USHR_INT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 9B ADD_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 9C SUB_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 9D MUL_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 9E DIV_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // 9F REM_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A0 AND_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A1 OR_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A2 XOR_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A3 SHL_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A4 SHR_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A5 USHR_LONG vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
-
-  // A6 ADD_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // A7 SUB_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // A8 MUL_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // A9 DIV_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // AA REM_FLOAT vAA, vBB, vCC
-  DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // AB ADD_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // AC SUB_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // AD MUL_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // AE DIV_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // AF REM_DOUBLE vAA, vBB, vCC
-  DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
-
-  // B0 ADD_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B1 SUB_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B2 MUL_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B3 DIV_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B4 REM_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B5 AND_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B6 OR_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B7 XOR_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B8 SHL_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // B9 SHR_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // BA USHR_INT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // BB ADD_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // BC SUB_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // BD MUL_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // BE DIV_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // BF REM_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // C0 AND_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // C1 OR_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // C2 XOR_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
-
-  // C3 SHL_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // C4 SHR_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // C5 USHR_LONG_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // C6 ADD_FLOAT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
-  // C7 SUB_FLOAT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
-  // C8 MUL_FLOAT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
-  // C9 DIV_FLOAT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
-  // CA REM_FLOAT_2ADDR vA, vB
-  DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
-
-  // CB ADD_DOUBLE_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // CC SUB_DOUBLE_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // CD MUL_DOUBLE_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // CE DIV_DOUBLE_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // CF REM_DOUBLE_2ADDR vA, vB
-  DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
-
-  // D0 ADD_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D1 RSUB_INT vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D2 MUL_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D3 DIV_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D4 REM_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D5 AND_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D6 OR_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D7 XOR_INT_LIT16 vA, vB, #+CCCC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D8 ADD_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // D9 RSUB_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // DA MUL_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // DB DIV_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // DC REM_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // DD AND_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // DE OR_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // DF XOR_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // E0 SHL_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // E1 SHR_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // E2 USHR_INT_LIT8 vAA, vBB, #+CC
-  DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
-
-  // E3 IGET_VOLATILE
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // E4 IPUT_VOLATILE
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
-
-  // E5 SGET_VOLATILE
-  DF_DA | DF_UMS,
-
-  // E6 SPUT_VOLATILE
-  DF_UA | DF_UMS,
-
-  // E7 IGET_OBJECT_VOLATILE
-  DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B,
-
-  // E8 IGET_WIDE_VOLATILE
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
-
-  // E9 IPUT_WIDE_VOLATILE
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B,
-
-  // EA SGET_WIDE_VOLATILE
-  DF_DA | DF_A_WIDE | DF_UMS,
-
-  // EB SPUT_WIDE_VOLATILE
-  DF_UA | DF_A_WIDE | DF_UMS,
-
-  // EC BREAKPOINT
-  DF_NOP,
-
-  // ED THROW_VERIFICATION_ERROR
-  DF_NOP | DF_UMS,
-
-  // EE EXECUTE_INLINE
-  DF_FORMAT_35C,
-
-  // EF EXECUTE_INLINE_RANGE
-  DF_FORMAT_3RC,
-
-  // F0 INVOKE_OBJECT_INIT_RANGE
-  DF_NOP | DF_NULL_CHK_0,
-
-  // F1 RETURN_VOID_BARRIER
-  DF_NOP,
-
-  // F2 IGET_QUICK
-  DF_DA | DF_UB | DF_NULL_CHK_0,
-
-  // F3 IGET_WIDE_QUICK
-  DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0,
-
-  // F4 IGET_OBJECT_QUICK
-  DF_DA | DF_UB | DF_NULL_CHK_0,
-
-  // F5 IPUT_QUICK
-  DF_UA | DF_UB | DF_NULL_CHK_1,
-
-  // F6 IPUT_WIDE_QUICK
-  DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2,
-
-  // F7 IPUT_OBJECT_QUICK
-  DF_UA | DF_UB | DF_NULL_CHK_1,
-
-  // F8 INVOKE_VIRTUAL_QUICK
-  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // F9 INVOKE_VIRTUAL_QUICK_RANGE
-  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // FA INVOKE_SUPER_QUICK
-  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // FB INVOKE_SUPER_QUICK_RANGE
-  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
-
-  // FC IPUT_OBJECT_VOLATILE
-  DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B,
-
-  // FD SGET_OBJECT_VOLATILE
-  DF_DA | DF_REF_A | DF_UMS,
-
-  // FE SPUT_OBJECT_VOLATILE
-  DF_UA | DF_REF_A | DF_UMS,
-
-  // FF UNUSED_FF
-  DF_NOP
-};
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/ir/instruction_tools.h b/compiler/sea_ir/ir/instruction_tools.h
deleted file mode 100644
index 895e017..0000000
--- a/compiler/sea_ir/ir/instruction_tools.h
+++ /dev/null
@@ -1,125 +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.
- */
-
-#include "sea.h"
-#include "dex_instruction.h"
-
-#ifndef ART_COMPILER_SEA_IR_IR_INSTRUCTION_TOOLS_H_
-#define ART_COMPILER_SEA_IR_IR_INSTRUCTION_TOOLS_H_
-
-
-// Note: This file has content cannibalized for SEA_IR from the MIR implementation,
-//       to avoid having a dependence on MIR.
-namespace sea_ir {
-
-#define DF_NOP                  0
-#define DF_UA                   (1 << kUA)
-#define DF_UB                   (1 << kUB)
-#define DF_UC                   (1 << kUC)
-#define DF_A_WIDE               (1 << kAWide)
-#define DF_B_WIDE               (1 << kBWide)
-#define DF_C_WIDE               (1 << kCWide)
-#define DF_DA                   (1 << kDA)
-#define DF_IS_MOVE              (1 << kIsMove)
-#define DF_SETS_CONST           (1 << kSetsConst)
-#define DF_FORMAT_35C           (1 << kFormat35c)
-#define DF_FORMAT_3RC           (1 << kFormat3rc)
-#define DF_NULL_CHK_0           (1 << kNullCheckSrc0)
-#define DF_NULL_CHK_1           (1 << kNullCheckSrc1)
-#define DF_NULL_CHK_2           (1 << kNullCheckSrc2)
-#define DF_NULL_CHK_OUT0        (1 << kNullCheckOut0)
-#define DF_NON_NULL_DST         (1 << kDstNonNull)
-#define DF_NON_NULL_RET         (1 << kRetNonNull)
-#define DF_NULL_TRANSFER_0      (1 << kNullTransferSrc0)
-#define DF_NULL_TRANSFER_N      (1 << kNullTransferSrcN)
-#define DF_RANGE_CHK_1          (1 << kRangeCheckSrc1)
-#define DF_RANGE_CHK_2          (1 << kRangeCheckSrc2)
-#define DF_RANGE_CHK_3          (1 << kRangeCheckSrc3)
-#define DF_FP_A                 (1 << kFPA)
-#define DF_FP_B                 (1 << kFPB)
-#define DF_FP_C                 (1 << kFPC)
-#define DF_CORE_A               (1 << kCoreA)
-#define DF_CORE_B               (1 << kCoreB)
-#define DF_CORE_C               (1 << kCoreC)
-#define DF_REF_A                (1 << kRefA)
-#define DF_REF_B                (1 << kRefB)
-#define DF_REF_C                (1 << kRefC)
-#define DF_UMS                  (1 << kUsesMethodStar)
-
-#define DF_HAS_USES             (DF_UA | DF_UB | DF_UC)
-
-#define DF_HAS_DEFS             (DF_DA)
-
-#define DF_HAS_NULL_CHKS        (DF_NULL_CHK_0 | \
-                                 DF_NULL_CHK_1 | \
-                                 DF_NULL_CHK_2 | \
-                                 DF_NULL_CHK_OUT0)
-
-#define DF_HAS_RANGE_CHKS       (DF_RANGE_CHK_1 | \
-                                 DF_RANGE_CHK_2 | \
-                                 DF_RANGE_CHK_3)
-
-#define DF_HAS_NR_CHKS          (DF_HAS_NULL_CHKS | \
-                                 DF_HAS_RANGE_CHKS)
-
-#define DF_A_IS_REG             (DF_UA | DF_DA)
-#define DF_B_IS_REG             (DF_UB)
-#define DF_C_IS_REG             (DF_UC)
-#define DF_IS_GETTER_OR_SETTER  (DF_IS_GETTER | DF_IS_SETTER)
-#define DF_USES_FP              (DF_FP_A | DF_FP_B | DF_FP_C)
-
-enum DataFlowAttributePos {
-  kUA = 0,
-  kUB,
-  kUC,
-  kAWide,
-  kBWide,
-  kCWide,
-  kDA,
-  kIsMove,
-  kSetsConst,
-  kFormat35c,
-  kFormat3rc,
-  kNullCheckSrc0,        // Null check of uses[0].
-  kNullCheckSrc1,        // Null check of uses[1].
-  kNullCheckSrc2,        // Null check of uses[2].
-  kNullCheckOut0,        // Null check out outgoing arg0.
-  kDstNonNull,           // May assume dst is non-null.
-  kRetNonNull,           // May assume retval is non-null.
-  kNullTransferSrc0,     // Object copy src[0] -> dst.
-  kNullTransferSrcN,     // Phi null check state transfer.
-  kRangeCheckSrc1,       // Range check of uses[1].
-  kRangeCheckSrc2,       // Range check of uses[2].
-  kRangeCheckSrc3,       // Range check of uses[3].
-  kFPA,
-  kFPB,
-  kFPC,
-  kCoreA,
-  kCoreB,
-  kCoreC,
-  kRefA,
-  kRefB,
-  kRefC,
-  kUsesMethodStar,       // Implicit use of Method*.
-};
-
-class InstructionTools {
- public:
-  static bool IsDefinition(const art::Instruction* instruction);
-  static const int instruction_attributes_[];
-};
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_IR_INSTRUCTION_TOOLS_H_
diff --git a/compiler/sea_ir/ir/regions_test.cc b/compiler/sea_ir/ir/regions_test.cc
deleted file mode 100644
index 95bd310..0000000
--- a/compiler/sea_ir/ir/regions_test.cc
+++ /dev/null
@@ -1,58 +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.
- */
-
-#include "common_compiler_test.h"
-#include "sea_ir/ir/sea.h"
-
-using utils::ScopedHashtable;
-
-namespace sea_ir {
-
-class RegionsTest : public art::CommonCompilerTest {};
-
-TEST_F(RegionsTest, Basics) {
-  sea_ir::SeaGraph sg(*java_lang_dex_file_);
-  sea_ir::Region* root = sg.GetNewRegion();
-  sea_ir::Region* then_region = sg.GetNewRegion();
-  sea_ir::Region* else_region = sg.GetNewRegion();
-  std::vector<sea_ir::Region*>* regions = sg.GetRegions();
-  // Test that regions have been registered correctly as children of the graph.
-  EXPECT_TRUE(std::find(regions->begin(), regions->end(), root) != regions->end());
-  EXPECT_TRUE(std::find(regions->begin(), regions->end(), then_region) != regions->end());
-  EXPECT_TRUE(std::find(regions->begin(), regions->end(), else_region) != regions->end());
-  // Check that an edge recorded correctly in both the head and the tail.
-  sg.AddEdge(root, then_region);
-  std::vector<sea_ir::Region*>* succs = root->GetSuccessors();
-  EXPECT_EQ(1U, succs->size());
-  EXPECT_EQ(then_region, succs->at(0));
-  std::vector<sea_ir::Region*>* preds = then_region->GetPredecessors();
-  EXPECT_EQ(1U, preds->size());
-  EXPECT_EQ(root, preds->at(0));
-  // Check that two edges are recorded properly for both head and tail.
-  sg.AddEdge(root, else_region);
-  succs = root->GetSuccessors();
-  EXPECT_EQ(2U, succs->size());
-  EXPECT_TRUE(std::find(succs->begin(), succs->end(), then_region) != succs->end());
-  EXPECT_TRUE(std::find(succs->begin(), succs->end(), else_region) != succs->end());
-  preds = then_region->GetPredecessors();
-  EXPECT_EQ(1U, preds->size());
-  EXPECT_EQ(root, preds->at(0));
-  preds = else_region->GetPredecessors();
-  EXPECT_EQ(1U, preds->size());
-  EXPECT_EQ(root, preds->at(0));
-}
-
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/ir/sea.cc b/compiler/sea_ir/ir/sea.cc
deleted file mode 100644
index 2b25f56..0000000
--- a/compiler/sea_ir/ir/sea.cc
+++ /dev/null
@@ -1,681 +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.
- */
-#include "base/stringprintf.h"
-#include "sea_ir/ir/instruction_tools.h"
-#include "sea_ir/ir/sea.h"
-#include "sea_ir/code_gen/code_gen.h"
-#include "sea_ir/types/type_inference.h"
-
-#define MAX_REACHING_DEF_ITERERATIONS (10)
-// TODO: When development is done, this define should not
-// be needed, it is currently used as a cutoff
-// for cases where the iterative fixed point algorithm
-// does not reach a fixed point because of a bug.
-
-namespace sea_ir {
-
-int SeaNode::current_max_node_id_ = 0;
-
-void IRVisitor::Traverse(Region* region) {
-  std::vector<PhiInstructionNode*>* phis = region->GetPhiNodes();
-  for (std::vector<PhiInstructionNode*>::const_iterator cit = phis->begin();
-      cit != phis->end(); cit++) {
-    (*cit)->Accept(this);
-  }
-  std::vector<InstructionNode*>* instructions = region->GetInstructions();
-  for (std::vector<InstructionNode*>::const_iterator cit = instructions->begin();
-      cit != instructions->end(); cit++) {
-    (*cit)->Accept(this);
-  }
-}
-
-void IRVisitor::Traverse(SeaGraph* graph) {
-  for (std::vector<Region*>::const_iterator cit = ordered_regions_.begin();
-          cit != ordered_regions_.end(); cit++ ) {
-    (*cit)->Accept(this);
-  }
-}
-
-SeaGraph* SeaGraph::GetGraph(const art::DexFile& dex_file) {
-  return new SeaGraph(dex_file);
-}
-
-void SeaGraph::AddEdge(Region* src, Region* dst) const {
-  src->AddSuccessor(dst);
-  dst->AddPredecessor(src);
-}
-
-void SeaGraph::ComputeRPO(Region* current_region, int& current_rpo) {
-  current_region->SetRPO(VISITING);
-  std::vector<sea_ir::Region*>* succs = current_region->GetSuccessors();
-  for (std::vector<sea_ir::Region*>::iterator succ_it = succs->begin();
-      succ_it != succs->end(); ++succ_it) {
-    if (NOT_VISITED == (*succ_it)->GetRPO()) {
-      SeaGraph::ComputeRPO(*succ_it, current_rpo);
-    }
-  }
-  current_region->SetRPO(current_rpo--);
-}
-
-void SeaGraph::ComputeIDominators() {
-  bool changed = true;
-  while (changed) {
-    changed = false;
-    // Entry node has itself as IDOM.
-    std::vector<Region*>::iterator crt_it;
-    std::set<Region*> processedNodes;
-    // Find and mark the entry node(s).
-    for (crt_it = regions_.begin(); crt_it != regions_.end(); ++crt_it) {
-      if ((*crt_it)->GetPredecessors()->size() == 0) {
-        processedNodes.insert(*crt_it);
-        (*crt_it)->SetIDominator(*crt_it);
-      }
-    }
-    for (crt_it = regions_.begin(); crt_it != regions_.end(); ++crt_it) {
-      if ((*crt_it)->GetPredecessors()->size() == 0) {
-        continue;
-      }
-      // NewIDom = first (processed) predecessor of b.
-      Region* new_dom = NULL;
-      std::vector<Region*>* preds = (*crt_it)->GetPredecessors();
-      DCHECK(NULL != preds);
-      Region* root_pred = NULL;
-      for (std::vector<Region*>::iterator pred_it = preds->begin();
-          pred_it != preds->end(); ++pred_it) {
-        if (processedNodes.end() != processedNodes.find((*pred_it))) {
-          root_pred = *pred_it;
-          new_dom = root_pred;
-          break;
-        }
-      }
-      // For all other predecessors p of b, if idom is not set,
-      // then NewIdom = Intersect(p, NewIdom)
-      for (std::vector<Region*>::const_iterator pred_it = preds->begin();
-          pred_it != preds->end(); ++pred_it) {
-        DCHECK(NULL != *pred_it);
-        // if IDOMS[p] != UNDEFINED
-        if ((*pred_it != root_pred) && (*pred_it)->GetIDominator() != NULL) {
-          DCHECK(NULL != new_dom);
-          new_dom = SeaGraph::Intersect(*pred_it, new_dom);
-        }
-      }
-      DCHECK(NULL != *crt_it);
-      if ((*crt_it)->GetIDominator() != new_dom) {
-        (*crt_it)->SetIDominator(new_dom);
-        changed = true;
-      }
-      processedNodes.insert(*crt_it);
-    }
-  }
-
-  // For easily ordering of regions we need edges dominator->dominated.
-  for (std::vector<Region*>::iterator region_it = regions_.begin();
-      region_it != regions_.end(); region_it++) {
-    Region* idom = (*region_it)->GetIDominator();
-    if (idom != *region_it) {
-      idom->AddToIDominatedSet(*region_it);
-    }
-  }
-}
-
-Region* SeaGraph::Intersect(Region* i, Region* j) {
-  Region* finger1 = i;
-  Region* finger2 = j;
-  while (finger1 != finger2) {
-    while (finger1->GetRPO() > finger2->GetRPO()) {
-      DCHECK(NULL != finger1);
-      finger1 = finger1->GetIDominator();  // should have: finger1 != NULL
-      DCHECK(NULL != finger1);
-    }
-    while (finger1->GetRPO() < finger2->GetRPO()) {
-      DCHECK(NULL != finger2);
-      finger2 = finger2->GetIDominator();  // should have: finger1 != NULL
-      DCHECK(NULL != finger2);
-    }
-  }
-  return finger1;  // finger1 should be equal to finger2 at this point.
-}
-
-void SeaGraph::ComputeDownExposedDefs() {
-  for (std::vector<Region*>::iterator region_it = regions_.begin();
-        region_it != regions_.end(); region_it++) {
-      (*region_it)->ComputeDownExposedDefs();
-    }
-}
-
-void SeaGraph::ComputeReachingDefs() {
-  // Iterate until the reaching definitions set doesn't change anymore.
-  // (See Cooper & Torczon, "Engineering a Compiler", second edition, page 487)
-  bool changed = true;
-  int iteration = 0;
-  while (changed && (iteration < MAX_REACHING_DEF_ITERERATIONS)) {
-    iteration++;
-    changed = false;
-    // TODO: optimize the ordering if this becomes performance bottleneck.
-    for (std::vector<Region*>::iterator regions_it = regions_.begin();
-        regions_it != regions_.end();
-        regions_it++) {
-      changed |= (*regions_it)->UpdateReachingDefs();
-    }
-  }
-  DCHECK(!changed) << "Reaching definitions computation did not reach a fixed point.";
-}
-
-void SeaGraph::InsertSignatureNodes(const art::DexFile::CodeItem* code_item, Region* r) {
-  // Insert a fake SignatureNode for the first parameter.
-  // TODO: Provide a register enum value for the fake parameter.
-  SignatureNode* parameter_def_node = new sea_ir::SignatureNode(0, 0);
-  AddParameterNode(parameter_def_node);
-  r->AddChild(parameter_def_node);
-  // Insert SignatureNodes for each Dalvik register parameter.
-  for (unsigned int crt_offset = 0; crt_offset < code_item->ins_size_; crt_offset++) {
-    int register_no = code_item->registers_size_ - crt_offset - 1;
-    int position = crt_offset + 1;
-    SignatureNode* parameter_def_node = new sea_ir::SignatureNode(register_no, position);
-    AddParameterNode(parameter_def_node);
-    r->AddChild(parameter_def_node);
-  }
-}
-
-void SeaGraph::BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
-    const art::DexFile& dex_file, uint16_t class_def_idx,
-    uint32_t method_idx, uint32_t method_access_flags) {
-  code_item_ = code_item;
-  class_def_idx_ = class_def_idx;
-  method_idx_ = method_idx;
-  method_access_flags_ = method_access_flags;
-  const uint16_t* code = code_item->insns_;
-  const size_t size_in_code_units = code_item->insns_size_in_code_units_;
-  // This maps target instruction pointers to their corresponding region objects.
-  std::map<const uint16_t*, Region*> target_regions;
-  size_t i = 0;
-  // Pass: Find the start instruction of basic blocks
-  //         by locating targets and flow-though instructions of branches.
-  while (i < size_in_code_units) {
-    const art::Instruction* inst = art::Instruction::At(&code[i]);
-    if (inst->IsBranch() || inst->IsUnconditional()) {
-      int32_t offset = inst->GetTargetOffset();
-      if (target_regions.end() == target_regions.find(&code[i + offset])) {
-        Region* region = GetNewRegion();
-        target_regions.insert(std::pair<const uint16_t*, Region*>(&code[i + offset], region));
-      }
-      if (inst->CanFlowThrough()
-          && (target_regions.end() == target_regions.find(&code[i + inst->SizeInCodeUnits()]))) {
-        Region* region = GetNewRegion();
-        target_regions.insert(
-            std::pair<const uint16_t*, Region*>(&code[i + inst->SizeInCodeUnits()], region));
-      }
-    }
-    i += inst->SizeInCodeUnits();
-  }
-
-
-  Region* r = GetNewRegion();
-
-  InsertSignatureNodes(code_item, r);
-  // Pass: Assign instructions to region nodes and
-  //         assign branches their control flow successors.
-  i = 0;
-  sea_ir::InstructionNode* last_node = NULL;
-  sea_ir::InstructionNode* node = NULL;
-  while (i < size_in_code_units) {
-    const art::Instruction* inst = art::Instruction::At(&code[i]);
-    std::vector<InstructionNode*> sea_instructions_for_dalvik =
-        sea_ir::InstructionNode::Create(inst);
-    for (std::vector<InstructionNode*>::const_iterator cit = sea_instructions_for_dalvik.begin();
-        sea_instructions_for_dalvik.end() != cit; ++cit) {
-      last_node = node;
-      node = *cit;
-
-      if (inst->IsBranch() || inst->IsUnconditional()) {
-        int32_t offset = inst->GetTargetOffset();
-        std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i + offset]);
-        DCHECK(it != target_regions.end());
-        AddEdge(r, it->second);  // Add edge to branch target.
-      }
-      std::map<const uint16_t*, Region*>::iterator it = target_regions.find(&code[i]);
-      if (target_regions.end() != it) {
-        // Get the already created region because this is a branch target.
-        Region* nextRegion = it->second;
-        if (last_node->GetInstruction()->IsBranch()
-            && last_node->GetInstruction()->CanFlowThrough()) {
-          AddEdge(r, it->second);  // Add flow-through edge.
-        }
-        r = nextRegion;
-      }
-      r->AddChild(node);
-    }
-    i += inst->SizeInCodeUnits();
-  }
-}
-
-void SeaGraph::ComputeRPO() {
-  int rpo_id = regions_.size() - 1;
-  for (std::vector<Region*>::const_iterator crt_it = regions_.begin(); crt_it != regions_.end();
-      ++crt_it) {
-    if ((*crt_it)->GetPredecessors()->size() == 0) {
-      ComputeRPO(*crt_it, rpo_id);
-    }
-  }
-}
-
-// Performs the renaming phase in traditional SSA transformations.
-// See: Cooper & Torczon, "Engineering a Compiler", second edition, page 505.)
-void SeaGraph::RenameAsSSA() {
-  utils::ScopedHashtable<int, InstructionNode*> scoped_table;
-  scoped_table.OpenScope();
-  for (std::vector<Region*>::iterator region_it = regions_.begin(); region_it != regions_.end();
-      region_it++) {
-    if ((*region_it)->GetIDominator() == *region_it) {
-      RenameAsSSA(*region_it, &scoped_table);
-    }
-  }
-  scoped_table.CloseScope();
-}
-
-void SeaGraph::ConvertToSSA() {
-  // Pass: find global names.
-  // The map @block maps registers to the blocks in which they are defined.
-  std::map<int, std::set<Region*>> blocks;
-  // The set @globals records registers whose use
-  // is in a different block than the corresponding definition.
-  std::set<int> globals;
-  for (std::vector<Region*>::iterator region_it = regions_.begin(); region_it != regions_.end();
-      region_it++) {
-    std::set<int> var_kill;
-    std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
-    for (std::vector<InstructionNode*>::iterator inst_it = instructions->begin();
-        inst_it != instructions->end(); inst_it++) {
-      std::vector<int> used_regs = (*inst_it)->GetUses();
-      for (std::size_t i = 0; i < used_regs.size(); i++) {
-        int used_reg = used_regs[i];
-        if (var_kill.find(used_reg) == var_kill.end()) {
-          globals.insert(used_reg);
-        }
-      }
-      const int reg_def = (*inst_it)->GetResultRegister();
-      if (reg_def != NO_REGISTER) {
-        var_kill.insert(reg_def);
-      }
-
-      blocks.insert(std::pair<int, std::set<Region*>>(reg_def, std::set<Region*>()));
-      std::set<Region*>* reg_def_blocks = &(blocks.find(reg_def)->second);
-      reg_def_blocks->insert(*region_it);
-    }
-  }
-
-  // Pass: Actually add phi-nodes to regions.
-  for (std::set<int>::const_iterator globals_it = globals.begin();
-      globals_it != globals.end(); globals_it++) {
-    int global = *globals_it;
-    // Copy the set, because we will modify the worklist as we go.
-    std::set<Region*> worklist((*(blocks.find(global))).second);
-    for (std::set<Region*>::const_iterator b_it = worklist.begin();
-        b_it != worklist.end(); b_it++) {
-      std::set<Region*>* df = (*b_it)->GetDominanceFrontier();
-      for (std::set<Region*>::const_iterator df_it = df->begin(); df_it != df->end(); df_it++) {
-        if ((*df_it)->InsertPhiFor(global)) {
-          // Check that the dominance frontier element is in the worklist already
-          // because we only want to break if the element is actually not there yet.
-          if (worklist.find(*df_it) == worklist.end()) {
-            worklist.insert(*df_it);
-            b_it = worklist.begin();
-            break;
-          }
-        }
-      }
-    }
-  }
-  // Pass: Build edges to the definition corresponding to each use.
-  // (This corresponds to the renaming phase in traditional SSA transformations.
-  // See: Cooper & Torczon, "Engineering a Compiler", second edition, page 505.)
-  RenameAsSSA();
-}
-
-void SeaGraph::RenameAsSSA(Region* crt_region,
-    utils::ScopedHashtable<int, InstructionNode*>* scoped_table) {
-  scoped_table->OpenScope();
-  // Rename phi nodes defined in the current region.
-  std::vector<PhiInstructionNode*>* phis = crt_region->GetPhiNodes();
-  for (std::vector<PhiInstructionNode*>::iterator phi_it = phis->begin();
-      phi_it != phis->end(); phi_it++) {
-    int reg_no = (*phi_it)->GetRegisterNumber();
-    scoped_table->Add(reg_no, (*phi_it));
-  }
-  // Rename operands of instructions from the current region.
-  std::vector<InstructionNode*>* instructions = crt_region->GetInstructions();
-  for (std::vector<InstructionNode*>::const_iterator instructions_it = instructions->begin();
-      instructions_it != instructions->end(); instructions_it++) {
-    InstructionNode* current_instruction = (*instructions_it);
-    // Rename uses.
-    std::vector<int> used_regs = current_instruction->GetUses();
-    for (std::vector<int>::const_iterator reg_it = used_regs.begin();
-        reg_it != used_regs.end(); reg_it++) {
-      int current_used_reg = (*reg_it);
-      InstructionNode* definition = scoped_table->Lookup(current_used_reg);
-      current_instruction->RenameToSSA(current_used_reg, definition);
-    }
-    // Update scope table with latest definitions.
-    std::vector<int> def_regs = current_instruction->GetDefinitions();
-    for (std::vector<int>::const_iterator reg_it = def_regs.begin();
-            reg_it != def_regs.end(); reg_it++) {
-      int current_defined_reg = (*reg_it);
-      scoped_table->Add(current_defined_reg, current_instruction);
-    }
-  }
-  // Fill in uses of phi functions in CFG successor regions.
-  const std::vector<Region*>* successors = crt_region->GetSuccessors();
-  for (std::vector<Region*>::const_iterator successors_it = successors->begin();
-      successors_it != successors->end(); successors_it++) {
-    Region* successor = (*successors_it);
-    successor->SetPhiDefinitionsForUses(scoped_table, crt_region);
-  }
-
-  // Rename all successors in the dominators tree.
-  const std::set<Region*>* dominated_nodes = crt_region->GetIDominatedSet();
-  for (std::set<Region*>::const_iterator dominated_nodes_it = dominated_nodes->begin();
-      dominated_nodes_it != dominated_nodes->end(); dominated_nodes_it++) {
-    Region* dominated_node = (*dominated_nodes_it);
-    RenameAsSSA(dominated_node, scoped_table);
-  }
-  scoped_table->CloseScope();
-}
-
-CodeGenData* SeaGraph::GenerateLLVM(const std::string& function_name,
-    const art::DexFile& dex_file) {
-  // Pass: Generate LLVM IR.
-  CodeGenPrepassVisitor code_gen_prepass_visitor(function_name);
-  std::cout << "Generating code..." << std::endl;
-  Accept(&code_gen_prepass_visitor);
-  CodeGenVisitor code_gen_visitor(code_gen_prepass_visitor.GetData(),  dex_file);
-  Accept(&code_gen_visitor);
-  CodeGenPostpassVisitor code_gen_postpass_visitor(code_gen_visitor.GetData());
-  Accept(&code_gen_postpass_visitor);
-  return code_gen_postpass_visitor.GetData();
-}
-
-CodeGenData* SeaGraph::CompileMethod(
-    const std::string& function_name,
-    const art::DexFile::CodeItem* code_item, uint16_t class_def_idx,
-    uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file) {
-  // Two passes: Builds the intermediate structure (non-SSA) of the sea-ir for the function.
-  BuildMethodSeaGraph(code_item, dex_file, class_def_idx, method_idx, method_access_flags);
-  // Pass: Compute reverse post-order of regions.
-  ComputeRPO();
-  // Multiple passes: compute immediate dominators.
-  ComputeIDominators();
-  // Pass: compute downward-exposed definitions.
-  ComputeDownExposedDefs();
-  // Multiple Passes (iterative fixed-point algorithm): Compute reaching definitions
-  ComputeReachingDefs();
-  // Pass (O(nlogN)): Compute the dominance frontier for region nodes.
-  ComputeDominanceFrontier();
-  // Two Passes: Phi node insertion.
-  ConvertToSSA();
-  // Pass: type inference
-  ti_->ComputeTypes(this);
-  // Pass: Generate LLVM IR.
-  CodeGenData* cgd = GenerateLLVM(function_name, dex_file);
-  return cgd;
-}
-
-void SeaGraph::ComputeDominanceFrontier() {
-  for (std::vector<Region*>::iterator region_it = regions_.begin();
-      region_it != regions_.end(); region_it++) {
-    std::vector<Region*>* preds = (*region_it)->GetPredecessors();
-    if (preds->size() > 1) {
-      for (std::vector<Region*>::iterator pred_it = preds->begin();
-          pred_it != preds->end(); pred_it++) {
-        Region* runner = *pred_it;
-        while (runner != (*region_it)->GetIDominator()) {
-          runner->AddToDominanceFrontier(*region_it);
-          runner = runner->GetIDominator();
-        }
-      }
-    }
-  }
-}
-
-Region* SeaGraph::GetNewRegion() {
-  Region* new_region = new Region();
-  AddRegion(new_region);
-  return new_region;
-}
-
-void SeaGraph::AddRegion(Region* r) {
-  DCHECK(r) << "Tried to add NULL region to SEA graph.";
-  regions_.push_back(r);
-}
-
-SeaGraph::SeaGraph(const art::DexFile& df)
-    :ti_(new TypeInference()), class_def_idx_(0), method_idx_(0),  method_access_flags_(),
-     regions_(), parameters_(), dex_file_(df), code_item_(NULL) { }
-
-void Region::AddChild(sea_ir::InstructionNode* instruction) {
-  DCHECK(instruction) << "Tried to add NULL instruction to region node.";
-  instructions_.push_back(instruction);
-  instruction->SetRegion(this);
-}
-
-SeaNode* Region::GetLastChild() const {
-  if (instructions_.size() > 0) {
-    return instructions_.back();
-  }
-  return NULL;
-}
-
-void Region::ComputeDownExposedDefs() {
-  for (std::vector<InstructionNode*>::const_iterator inst_it = instructions_.begin();
-      inst_it != instructions_.end(); inst_it++) {
-    int reg_no = (*inst_it)->GetResultRegister();
-    std::map<int, InstructionNode*>::iterator res = de_defs_.find(reg_no);
-    if ((reg_no != NO_REGISTER) && (res == de_defs_.end())) {
-      de_defs_.insert(std::pair<int, InstructionNode*>(reg_no, *inst_it));
-    } else {
-      res->second = *inst_it;
-    }
-  }
-  for (std::map<int, sea_ir::InstructionNode*>::const_iterator cit = de_defs_.begin();
-      cit != de_defs_.end(); cit++) {
-    (*cit).second->MarkAsDEDef();
-  }
-}
-
-const std::map<int, sea_ir::InstructionNode*>* Region::GetDownExposedDefs() const {
-  return &de_defs_;
-}
-
-std::map<int, std::set<sea_ir::InstructionNode*>* >* Region::GetReachingDefs() {
-  return &reaching_defs_;
-}
-
-bool Region::UpdateReachingDefs() {
-  std::map<int, std::set<sea_ir::InstructionNode*>* > new_reaching;
-  for (std::vector<Region*>::const_iterator pred_it = predecessors_.begin();
-      pred_it != predecessors_.end(); pred_it++) {
-    // The reaching_defs variable will contain reaching defs __for current predecessor only__
-    std::map<int, std::set<sea_ir::InstructionNode*>* > reaching_defs;
-    std::map<int, std::set<sea_ir::InstructionNode*>* >* pred_reaching =
-        (*pred_it)->GetReachingDefs();
-    const std::map<int, InstructionNode*>* de_defs = (*pred_it)->GetDownExposedDefs();
-
-    // The definitions from the reaching set of the predecessor
-    // may be shadowed by downward exposed definitions from the predecessor,
-    // otherwise the defs from the reaching set are still good.
-    for (std::map<int, InstructionNode*>::const_iterator de_def = de_defs->begin();
-        de_def != de_defs->end(); de_def++) {
-      std::set<InstructionNode*>* solo_def;
-      solo_def = new std::set<InstructionNode*>();
-      solo_def->insert(de_def->second);
-      reaching_defs.insert(
-          std::pair<int const, std::set<InstructionNode*>*>(de_def->first, solo_def));
-    }
-    reaching_defs.insert(pred_reaching->begin(), pred_reaching->end());
-
-    // Now we combine the reaching map coming from the current predecessor (reaching_defs)
-    // with the accumulated set from all predecessors so far (from new_reaching).
-    std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it =
-        reaching_defs.begin();
-    for (; reaching_it != reaching_defs.end(); reaching_it++) {
-      std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator crt_entry =
-          new_reaching.find(reaching_it->first);
-      if (new_reaching.end() != crt_entry) {
-        crt_entry->second->insert(reaching_it->second->begin(), reaching_it->second->end());
-      } else {
-        new_reaching.insert(
-            std::pair<int, std::set<sea_ir::InstructionNode*>*>(
-                reaching_it->first,
-                reaching_it->second) );
-      }
-    }
-  }
-  bool changed = false;
-  // Because the sets are monotonically increasing,
-  // we can compare sizes instead of using set comparison.
-  // TODO: Find formal proof.
-  int old_size = 0;
-  if (-1 == reaching_defs_size_) {
-    std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it =
-        reaching_defs_.begin();
-    for (; reaching_it != reaching_defs_.end(); reaching_it++) {
-      old_size += (*reaching_it).second->size();
-    }
-  } else {
-    old_size = reaching_defs_size_;
-  }
-  int new_size = 0;
-  std::map<int, std::set<sea_ir::InstructionNode*>*>::iterator reaching_it = new_reaching.begin();
-  for (; reaching_it != new_reaching.end(); reaching_it++) {
-    new_size += (*reaching_it).second->size();
-  }
-  if (old_size != new_size) {
-    changed = true;
-  }
-  if (changed) {
-    reaching_defs_ = new_reaching;
-    reaching_defs_size_ = new_size;
-  }
-  return changed;
-}
-
-bool Region::InsertPhiFor(int reg_no) {
-  if (!ContainsPhiFor(reg_no)) {
-    phi_set_.insert(reg_no);
-    PhiInstructionNode* new_phi = new PhiInstructionNode(reg_no);
-    new_phi->SetRegion(this);
-    phi_instructions_.push_back(new_phi);
-    return true;
-  }
-  return false;
-}
-
-void Region::SetPhiDefinitionsForUses(
-    const utils::ScopedHashtable<int, InstructionNode*>* scoped_table, Region* predecessor) {
-  int predecessor_id = -1;
-  for (unsigned int crt_pred_id = 0; crt_pred_id < predecessors_.size(); crt_pred_id++) {
-    if (predecessors_.at(crt_pred_id) == predecessor) {
-      predecessor_id = crt_pred_id;
-    }
-  }
-  DCHECK_NE(-1, predecessor_id);
-  for (std::vector<PhiInstructionNode*>::iterator phi_it = phi_instructions_.begin();
-      phi_it != phi_instructions_.end(); phi_it++) {
-    PhiInstructionNode* phi = (*phi_it);
-    int reg_no = phi->GetRegisterNumber();
-    InstructionNode* definition = scoped_table->Lookup(reg_no);
-    phi->RenameToSSA(reg_no, definition, predecessor_id);
-  }
-}
-
-std::vector<InstructionNode*> InstructionNode::Create(const art::Instruction* in) {
-  std::vector<InstructionNode*> sea_instructions;
-  switch (in->Opcode()) {
-    case art::Instruction::CONST_4:
-      sea_instructions.push_back(new ConstInstructionNode(in));
-      break;
-    case art::Instruction::RETURN:
-      sea_instructions.push_back(new ReturnInstructionNode(in));
-      break;
-    case art::Instruction::IF_NE:
-      sea_instructions.push_back(new IfNeInstructionNode(in));
-      break;
-    case art::Instruction::ADD_INT_LIT8:
-      sea_instructions.push_back(new UnnamedConstInstructionNode(in, in->VRegC_22b()));
-      sea_instructions.push_back(new AddIntLitInstructionNode(in));
-      break;
-    case art::Instruction::MOVE_RESULT:
-      sea_instructions.push_back(new MoveResultInstructionNode(in));
-      break;
-    case art::Instruction::INVOKE_STATIC:
-      sea_instructions.push_back(new InvokeStaticInstructionNode(in));
-      break;
-    case art::Instruction::ADD_INT:
-      sea_instructions.push_back(new AddIntInstructionNode(in));
-      break;
-    case art::Instruction::GOTO:
-      sea_instructions.push_back(new GotoInstructionNode(in));
-      break;
-    case art::Instruction::IF_EQZ:
-      sea_instructions.push_back(new IfEqzInstructionNode(in));
-      break;
-    default:
-      // Default, generic IR instruction node; default case should never be reached
-      // when support for all instructions ahs been added.
-      sea_instructions.push_back(new InstructionNode(in));
-  }
-  return sea_instructions;
-}
-
-void InstructionNode::MarkAsDEDef() {
-  de_def_ = true;
-}
-
-int InstructionNode::GetResultRegister() const {
-  if (instruction_->HasVRegA() && InstructionTools::IsDefinition(instruction_)) {
-    return instruction_->VRegA();
-  }
-  return NO_REGISTER;
-}
-
-std::vector<int> InstructionNode::GetDefinitions() const {
-  // TODO: Extend this to handle instructions defining more than one register (if any)
-  // The return value should be changed to pointer to field then; for now it is an object
-  // so that we avoid possible memory leaks from allocating objects dynamically.
-  std::vector<int> definitions;
-  int result = GetResultRegister();
-  if (NO_REGISTER != result) {
-    definitions.push_back(result);
-  }
-  return definitions;
-}
-
-std::vector<int> InstructionNode::GetUses() const {
-  std::vector<int> uses;  // Using vector<> instead of set<> because order matters.
-  if (!InstructionTools::IsDefinition(instruction_) && (instruction_->HasVRegA())) {
-    int vA = instruction_->VRegA();
-    uses.push_back(vA);
-  }
-  if (instruction_->HasVRegB()) {
-    int vB = instruction_->VRegB();
-    uses.push_back(vB);
-  }
-  if (instruction_->HasVRegC()) {
-    int vC = instruction_->VRegC();
-    uses.push_back(vC);
-  }
-  return uses;
-}
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/ir/sea.h b/compiler/sea_ir/ir/sea.h
deleted file mode 100644
index 26b16be..0000000
--- a/compiler/sea_ir/ir/sea.h
+++ /dev/null
@@ -1,353 +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.
- */
-
-
-#ifndef ART_COMPILER_SEA_IR_IR_SEA_H_
-#define ART_COMPILER_SEA_IR_IR_SEA_H_
-
-#include <set>
-#include <map>
-
-#include "utils/scoped_hashtable.h"
-#include "gtest/gtest_prod.h"
-#include "dex_file.h"
-#include "dex_instruction.h"
-#include "sea_ir/ir/instruction_tools.h"
-#include "sea_ir/ir/instruction_nodes.h"
-
-namespace sea_ir {
-
-// Reverse post-order numbering constants
-enum RegionNumbering {
-  NOT_VISITED = -1,
-  VISITING = -2
-};
-
-class TypeInference;
-class CodeGenData;
-
-class Region;
-class InstructionNode;
-class PhiInstructionNode;
-class SignatureNode;
-
-// A SignatureNode is a declaration of one parameter in the function signature.
-// This class is used to provide place-holder definitions to which instructions
-// can return from the GetSSAUses() calls, instead of having missing SSA edges.
-class SignatureNode: public InstructionNode {
- public:
-  // Creates a new signature node representing the initial definition of the
-  // register @register_no which is the @signature_position-th argument to the method.
-  explicit SignatureNode(unsigned int register_no, unsigned int signature_position):
-    InstructionNode(NULL), register_no_(register_no), position_(signature_position) { }
-
-  int GetResultRegister() const {
-    return register_no_;
-  }
-
-  unsigned int GetPositionInSignature() const {
-    return position_;
-  }
-
-  std::vector<int> GetUses() const {
-    return std::vector<int>();
-  }
-
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-
- private:
-  const unsigned int register_no_;
-  const unsigned int position_;     // The position of this parameter node is
-                                    // in the function parameter list.
-};
-
-class PhiInstructionNode: public InstructionNode {
- public:
-  explicit PhiInstructionNode(int register_no):
-    InstructionNode(NULL), register_no_(register_no), definition_edges_() {}
-  // Returns the register on which this phi-function is used.
-  int GetRegisterNumber() const {
-    return register_no_;
-  }
-
-  // Renames the use of @reg_no to refer to the instruction @definition.
-  // Phi-functions are different than normal instructions in that they
-  // have multiple predecessor regions; this is why RenameToSSA has
-  // the additional parameter specifying that @parameter_id is the incoming
-  // edge for @definition, essentially creating SSA form.
-  void RenameToSSA(int reg_no, InstructionNode* definition, unsigned int predecessor_id) {
-    DCHECK(NULL != definition) << "Tried to rename to SSA using a NULL definition for "
-        << StringId() << " register " << reg_no;
-    if (definition_edges_.size() < predecessor_id+1) {
-      definition_edges_.resize(predecessor_id+1, NULL);
-    }
-    if (NULL == definition_edges_.at(predecessor_id)) {
-      definition_edges_[predecessor_id] = new std::vector<InstructionNode*>();
-    }
-    definition_edges_[predecessor_id]->push_back(definition);
-    definition->AddSSAUse(this);
-  }
-
-  // Returns the ordered set of Instructions that define the input operands of this instruction.
-  // Precondition: SeaGraph.ConvertToSSA().
-  std::vector<InstructionNode*> GetSSAProducers() {
-    std::vector<InstructionNode*> producers;
-    for (std::vector<std::vector<InstructionNode*>*>::const_iterator
-        cit = definition_edges_.begin(); cit != definition_edges_.end(); cit++) {
-      producers.insert(producers.end(), (*cit)->begin(), (*cit)->end());
-    }
-    return producers;
-  }
-
-  // Returns the instruction that defines the phi register from predecessor
-  // on position @predecessor_pos. Note that the return value is vector<> just
-  // for consistency with the return value of GetSSAUses() on regular instructions,
-  // The returned vector should always have a single element because the IR is SSA.
-  std::vector<InstructionNode*>* GetSSAUses(int predecessor_pos) {
-    return definition_edges_.at(predecessor_pos);
-  }
-
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-
- private:
-  int register_no_;
-  // This vector has one entry for each predecessors, each with a single
-  // element, storing the id of the instruction that defines the register
-  // corresponding to this phi function.
-  std::vector<std::vector<InstructionNode*>*> definition_edges_;
-};
-
-// This class corresponds to a basic block in traditional compiler IRs.
-// The dataflow analysis relies on this class both during execution and
-// for storing its results.
-class Region : public SeaNode {
- public:
-  explicit Region():
-    SeaNode(), successors_(), predecessors_(), reaching_defs_size_(0),
-    rpo_number_(NOT_VISITED), idom_(NULL), idominated_set_(), df_(), phi_set_() {
-    string_id_ = "cluster_" + string_id_;
-  }
-  // Adds @instruction as an instruction node child in the current region.
-  void AddChild(sea_ir::InstructionNode* instruction);
-  // Returns the last instruction node child of the current region.
-  // This child has the CFG successors pointing to the new regions.
-  SeaNode* GetLastChild() const;
-  // Returns all the child instructions of this region, in program order.
-  std::vector<InstructionNode*>* GetInstructions() {
-    return &instructions_;
-  }
-
-  // Computes Downward Exposed Definitions for the current node.
-  void ComputeDownExposedDefs();
-  const std::map<int, sea_ir::InstructionNode*>* GetDownExposedDefs() const;
-  // Performs one iteration of the reaching definitions algorithm
-  // and returns true if the reaching definitions set changed.
-  bool UpdateReachingDefs();
-  // Returns the set of reaching definitions for the current region.
-  std::map<int, std::set<sea_ir::InstructionNode*>* >* GetReachingDefs();
-
-  void SetRPO(int rpo) {
-    rpo_number_ = rpo;
-  }
-
-  int GetRPO() {
-    return rpo_number_;
-  }
-
-  void SetIDominator(Region* dom) {
-    idom_ = dom;
-  }
-
-  Region* GetIDominator() const {
-    return idom_;
-  }
-
-  void AddToIDominatedSet(Region* dominated) {
-    idominated_set_.insert(dominated);
-  }
-
-  const std::set<Region*>* GetIDominatedSet() {
-    return &idominated_set_;
-  }
-  // Adds @df_reg to the dominance frontier of the current region.
-  void AddToDominanceFrontier(Region* df_reg) {
-    df_.insert(df_reg);
-  }
-  // Returns the dominance frontier of the current region.
-  // Preconditions: SeaGraph.ComputeDominanceFrontier()
-  std::set<Region*>* GetDominanceFrontier() {
-    return &df_;
-  }
-  // Returns true if the region contains a phi function for @reg_no.
-  bool ContainsPhiFor(int reg_no) {
-    return (phi_set_.end() != phi_set_.find(reg_no));
-  }
-  // Returns the phi-functions from the region.
-  std::vector<PhiInstructionNode*>* GetPhiNodes() {
-    return &phi_instructions_;
-  }
-  // Adds a phi-function for @reg_no to this region.
-  // Note: The insertion order does not matter, as phi-functions
-  //       are conceptually executed at the same time.
-  bool InsertPhiFor(int reg_no);
-  // Sets the phi-function uses to be as defined in @scoped_table for predecessor @@predecessor.
-  void SetPhiDefinitionsForUses(const utils::ScopedHashtable<int, InstructionNode*>* scoped_table,
-      Region* predecessor);
-
-  void Accept(IRVisitor* v) {
-    v->Visit(this);
-    v->Traverse(this);
-  }
-
-  void AddSuccessor(Region* successor) {
-    DCHECK(successor) << "Tried to add NULL successor to SEA node.";
-    successors_.push_back(successor);
-    return;
-  }
-  void AddPredecessor(Region* predecessor) {
-    DCHECK(predecessor) << "Tried to add NULL predecessor to SEA node.";
-    predecessors_.push_back(predecessor);
-  }
-
-  std::vector<sea_ir::Region*>* GetSuccessors() {
-    return &successors_;
-  }
-  std::vector<sea_ir::Region*>* GetPredecessors() {
-    return &predecessors_;
-  }
-
- private:
-  std::vector<sea_ir::Region*> successors_;    // CFG successor nodes (regions)
-  std::vector<sea_ir::Region*> predecessors_;  // CFG predecessor nodes (instructions/regions)
-  std::vector<sea_ir::InstructionNode*> instructions_;
-  std::map<int, sea_ir::InstructionNode*> de_defs_;
-  std::map<int, std::set<sea_ir::InstructionNode*>* > reaching_defs_;
-  int reaching_defs_size_;
-  int rpo_number_;                              // reverse postorder number of the region
-  // Immediate dominator node.
-  Region* idom_;
-  // The set of nodes immediately dominated by the region.
-  std::set<Region*> idominated_set_;
-  // Records the dominance frontier.
-  std::set<Region*> df_;
-  // Records the set of register numbers that have phi nodes in this region.
-  std::set<int> phi_set_;
-  std::vector<PhiInstructionNode*> phi_instructions_;
-};
-
-// A SeaGraph instance corresponds to a source code function.
-// Its main point is to encapsulate the SEA IR representation of it
-// and acts as starting point for visitors (ex: during code generation).
-class SeaGraph: IVisitable {
- public:
-  static SeaGraph* GetGraph(const art::DexFile&);
-
-  CodeGenData* CompileMethod(const std::string& function_name,
-      const art::DexFile::CodeItem* code_item, uint16_t class_def_idx,
-      uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file);
-  // Returns all regions corresponding to this SeaGraph.
-  std::vector<Region*>* GetRegions() {
-    return &regions_;
-  }
-  // Recursively computes the reverse postorder value for @crt_bb and successors.
-  static void ComputeRPO(Region* crt_bb, int& crt_rpo);
-  // Returns the "lowest common ancestor" of @i and @j in the dominator tree.
-  static Region* Intersect(Region* i, Region* j);
-  // Returns the vector of parameters of the function.
-  std::vector<SignatureNode*>* GetParameterNodes() {
-    return &parameters_;
-  }
-
-  const art::DexFile* GetDexFile() const {
-    return &dex_file_;
-  }
-
-  virtual void Accept(IRVisitor* visitor) {
-    visitor->Initialize(this);
-    visitor->Visit(this);
-    visitor->Traverse(this);
-  }
-
-  TypeInference* ti_;
-  uint16_t class_def_idx_;
-  uint32_t method_idx_;
-  uint32_t method_access_flags_;
-
- protected:
-  explicit SeaGraph(const art::DexFile& df);
-  virtual ~SeaGraph() { }
-
- private:
-  FRIEND_TEST(RegionsTest, Basics);
-  // Registers @childReg as a region belonging to the SeaGraph instance.
-  void AddRegion(Region* childReg);
-  // Returns new region and registers it with the  SeaGraph instance.
-  Region* GetNewRegion();
-  // Adds a (formal) parameter node to the vector of parameters of the function.
-  void AddParameterNode(SignatureNode* parameterNode) {
-    parameters_.push_back(parameterNode);
-  }
-  // Adds a CFG edge from @src node to @dst node.
-  void AddEdge(Region* src, Region* dst) const;
-  // Builds the non-SSA sea-ir representation of the function @code_item from @dex_file
-  // with class id @class_def_idx and method id @method_idx.
-  void BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
-      const art::DexFile& dex_file, uint16_t class_def_idx,
-      uint32_t method_idx, uint32_t method_access_flags);
-  // Computes immediate dominators for each region.
-  // Precondition: ComputeMethodSeaGraph()
-  void ComputeIDominators();
-  // Computes Downward Exposed Definitions for all regions in the graph.
-  void ComputeDownExposedDefs();
-  // Computes the reaching definitions set following the equations from
-  // Cooper & Torczon, "Engineering a Compiler", second edition, page 491.
-  // Precondition: ComputeDEDefs()
-  void ComputeReachingDefs();
-  // Computes the reverse-postorder numbering for the region nodes.
-  // Precondition: ComputeDEDefs()
-  void ComputeRPO();
-  // Computes the dominance frontier for all regions in the graph,
-  // following the algorithm from
-  // Cooper & Torczon, "Engineering a Compiler", second edition, page 499.
-  // Precondition: ComputeIDominators()
-  void ComputeDominanceFrontier();
-  // Converts the IR to semi-pruned SSA form.
-  void ConvertToSSA();
-  // Performs the renaming phase of the SSA transformation during ConvertToSSA() execution.
-  void RenameAsSSA();
-  // Identifies the definitions corresponding to uses for region @node
-  // by using the scoped hashtable of names @ scoped_table.
-  void RenameAsSSA(Region* node, utils::ScopedHashtable<int, InstructionNode*>* scoped_table);
-  // Generate LLVM IR for the method.
-  // Precondition: ConvertToSSA().
-  CodeGenData* GenerateLLVM(const std::string& function_name, const art::DexFile& dex_file);
-  // Inserts one SignatureNode for each argument of the function in
-  void InsertSignatureNodes(const art::DexFile::CodeItem* code_item, Region* r);
-
-  static SeaGraph graph_;
-  std::vector<Region*> regions_;
-  std::vector<SignatureNode*> parameters_;
-  const art::DexFile& dex_file_;
-  const art::DexFile::CodeItem* code_item_;
-};
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_IR_SEA_H_
diff --git a/compiler/sea_ir/ir/sea_node.h b/compiler/sea_ir/ir/sea_node.h
deleted file mode 100644
index 4dab5cb..0000000
--- a/compiler/sea_ir/ir/sea_node.h
+++ /dev/null
@@ -1,77 +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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_IR_SEA_NODE_H_
-#define ART_COMPILER_SEA_IR_IR_SEA_NODE_H_
-
-#include "base/stringprintf.h"
-
-namespace sea_ir {
-class Region;
-class IRVisitor;
-
-class IVisitable {
- public:
-  virtual void Accept(IRVisitor* visitor) = 0;
-  virtual ~IVisitable() {}
-};
-
-// This abstract class provides the essential services that
-// we want each SEA IR element to have.
-// At the moment, these are:
-// - an id and corresponding string representation.
-// - a .dot graph language representation for .dot output.
-//
-// Note that SEA IR nodes could also be Regions, Projects
-// which are not instructions.
-class SeaNode: public IVisitable {
- public:
-  explicit SeaNode():id_(GetNewId()), string_id_() {
-    string_id_ = art::StringPrintf("%d", id_);
-  }
-
-  // Adds CFG predecessors and successors to each block.
-  void AddSuccessor(Region* successor);
-  void AddPredecessor(Region* predecesor);
-
-  // Returns the id of the current block as string
-  const std::string& StringId() const {
-    return string_id_;
-  }
-  // Returns the id of this node as int. The id is supposed to be unique among
-  // all instances of all subclasses of this class.
-  int Id() const {
-    return id_;
-  }
-
-  virtual ~SeaNode() { }
-
- protected:
-  static int GetNewId() {
-    return current_max_node_id_++;
-  }
-
-  const int id_;
-  std::string string_id_;
-
- private:
-  static int current_max_node_id_;
-  // Creating new instances of sea node objects should not be done through copy or assignment
-  // operators because that would lead to duplication of their unique ids.
-  DISALLOW_COPY_AND_ASSIGN(SeaNode);
-};
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_IR_SEA_NODE_H_
diff --git a/compiler/sea_ir/ir/visitor.h b/compiler/sea_ir/ir/visitor.h
deleted file mode 100644
index cc7b5d1..0000000
--- a/compiler/sea_ir/ir/visitor.h
+++ /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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_IR_VISITOR_H_
-#define ART_COMPILER_SEA_IR_IR_VISITOR_H_
-
-namespace sea_ir {
-
-class SeaGraph;
-class Region;
-class InstructionNode;
-class PhiInstructionNode;
-class SignatureNode;
-class UnnamedConstInstructionNode;
-class ConstInstructionNode;
-class ReturnInstructionNode;
-class IfNeInstructionNode;
-class AddIntLit8InstructionNode;
-class MoveResultInstructionNode;
-class InvokeStaticInstructionNode;
-class AddIntInstructionNode;
-class AddIntLitInstructionNode;
-class GotoInstructionNode;
-class IfEqzInstructionNode;
-
-
-
-
-class IRVisitor {
- public:
-  explicit IRVisitor(): ordered_regions_() { }
-  virtual void Initialize(SeaGraph* graph) = 0;
-  virtual void Visit(SeaGraph* graph) = 0;
-  virtual void Visit(Region* region) = 0;
-  virtual void Visit(PhiInstructionNode* region) = 0;
-  virtual void Visit(SignatureNode* region) = 0;
-
-  virtual void Visit(InstructionNode* region) = 0;
-  virtual void Visit(ConstInstructionNode* instruction) = 0;
-  virtual void Visit(UnnamedConstInstructionNode* instruction) = 0;
-  virtual void Visit(ReturnInstructionNode* instruction) = 0;
-  virtual void Visit(IfNeInstructionNode* instruction) = 0;
-  virtual void Visit(MoveResultInstructionNode* instruction) = 0;
-  virtual void Visit(InvokeStaticInstructionNode* instruction) = 0;
-  virtual void Visit(AddIntInstructionNode* instruction) = 0;
-  virtual void Visit(GotoInstructionNode* instruction) = 0;
-  virtual void Visit(IfEqzInstructionNode* instruction) = 0;
-
-  // Note: This flavor of visitor separates the traversal functions from the actual visiting part
-  //       so that the Visitor subclasses don't duplicate code and can't get the traversal wrong.
-  //       The disadvantage is the increased number of functions (and calls).
-  virtual void Traverse(SeaGraph* graph);
-  virtual void Traverse(Region* region);
-  // The following functions are meant to be empty and not pure virtual,
-  // because the parameter classes have no children to traverse.
-  virtual void Traverse(InstructionNode* region) { }
-  virtual void Traverse(ConstInstructionNode* instruction) { }
-  virtual void Traverse(ReturnInstructionNode* instruction) { }
-  virtual void Traverse(IfNeInstructionNode* instruction) { }
-  virtual void Traverse(AddIntLit8InstructionNode* instruction) { }
-  virtual void Traverse(MoveResultInstructionNode* instruction) { }
-  virtual void Traverse(InvokeStaticInstructionNode* instruction) { }
-  virtual void Traverse(AddIntInstructionNode* instruction) { }
-  virtual void Traverse(GotoInstructionNode* instruction) { }
-  virtual void Traverse(IfEqzInstructionNode* instruction) { }
-  virtual void Traverse(PhiInstructionNode* phi) { }
-  virtual void Traverse(SignatureNode* sig) { }
-  virtual ~IRVisitor() { }
-
- protected:
-  std::vector<Region*> ordered_regions_;
-};
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_IR_VISITOR_H_
diff --git a/compiler/sea_ir/types/type_data_test.cc b/compiler/sea_ir/types/type_data_test.cc
deleted file mode 100644
index 42c6973..0000000
--- a/compiler/sea_ir/types/type_data_test.cc
+++ /dev/null
@@ -1,40 +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.
- */
-
-#include "common_compiler_test.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-class TypeDataTest : public art::CommonCompilerTest {};
-
-TEST_F(TypeDataTest, Basics) {
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  int first_instruction_id = 1;
-  int second_instruction_id = 3;
-  EXPECT_TRUE(NULL == td.FindTypeOf(first_instruction_id));
-  const Type* int_type = &type_cache.Integer();
-  const Type* byte_type = &type_cache.Byte();
-  td.SetTypeOf(first_instruction_id, int_type);
-  EXPECT_TRUE(int_type == td.FindTypeOf(first_instruction_id));
-  EXPECT_TRUE(NULL == td.FindTypeOf(second_instruction_id));
-  td.SetTypeOf(second_instruction_id, byte_type);
-  EXPECT_TRUE(int_type == td.FindTypeOf(first_instruction_id));
-  EXPECT_TRUE(byte_type == td.FindTypeOf(second_instruction_id));
-}
-
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference.cc b/compiler/sea_ir/types/type_inference.cc
deleted file mode 100644
index 1731987..0000000
--- a/compiler/sea_ir/types/type_inference.cc
+++ /dev/null
@@ -1,187 +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.
- */
-#include "scoped_thread_state_change.h"
-#include "sea_ir/types/type_inference.h"
-#include "sea_ir/types/type_inference_visitor.h"
-#include "sea_ir/ir/sea.h"
-
-namespace sea_ir {
-
-bool TypeInference::IsPrimitiveDescriptor(char descriptor) {
-  switch (descriptor) {
-  case 'I':
-  case 'C':
-  case 'S':
-  case 'B':
-  case 'Z':
-  case 'F':
-  case 'D':
-  case 'J':
-    return true;
-  default:
-    return false;
-  }
-}
-
-FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types)
-    : dex_file_(graph->GetDexFile()), dex_method_idx_(graph->method_idx_), type_cache_(types),
-    method_access_flags_(graph->method_access_flags_) {
-  const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
-  const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
-  declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
-}
-
-FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, InstructionNode* inst,
-    art::verifier::RegTypeCache* types): dex_file_(graph->GetDexFile()),
-        dex_method_idx_(inst->GetInstruction()->VRegB_35c()), type_cache_(types),
-        method_access_flags_(0) {
-  // TODO: Test that GetDeclaredArgumentTypes() works correctly when using this constructor.
-  const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
-  const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
-  declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
-}
-
-const Type* FunctionTypeInfo::GetReturnValueType() {
-  const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
-  uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
-  const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
-  art::ScopedObjectAccess soa(art::Thread::Current());
-  const Type& return_type = type_cache_->FromDescriptor(NULL, descriptor, false);
-  return &return_type;
-}
-
-
-
-std::vector<const Type*> FunctionTypeInfo::GetDeclaredArgumentTypes() {
-  art::ScopedObjectAccess soa(art::Thread::Current());
-  std::vector<const Type*> argument_types;
-  // TODO: The additional (fake) Method parameter is added on the first position,
-  //       but is represented as integer because we don't support  pointers yet.
-  argument_types.push_back(&(type_cache_->Integer()));
-  // Include the "this" pointer.
-  size_t cur_arg = 0;
-  if (!IsStatic()) {
-    // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
-    // argument as uninitialized. This restricts field access until the superclass constructor is
-    // called.
-    const art::verifier::RegType& declaring_class = GetDeclaringClass();
-    if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
-      argument_types.push_back(&(type_cache_->UninitializedThisArgument(declaring_class)));
-    } else {
-      argument_types.push_back(&declaring_class);
-    }
-    cur_arg++;
-  }
-  // Include the types of the parameters in the Java method signature.
-  const art::DexFile::ProtoId& proto_id =
-      dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
-  art::DexFileParameterIterator iterator(*dex_file_, proto_id);
-
-  for (; iterator.HasNext(); iterator.Next()) {
-    const char* descriptor = iterator.GetDescriptor();
-    if (descriptor == NULL) {
-      LOG(FATAL) << "Error: Encountered null type descriptor for function argument.";
-    }
-    switch (descriptor[0]) {
-      case 'L':
-      case '[':
-        // We assume that reference arguments are initialized. The only way it could be otherwise
-        // (assuming the caller was verified) is if the current method is <init>, but in that case
-        // it's effectively considered initialized the instant we reach here (in the sense that we
-        // can return without doing anything or call virtual methods).
-        {
-          const Type& reg_type = type_cache_->FromDescriptor(NULL, descriptor, false);
-          argument_types.push_back(&reg_type);
-        }
-        break;
-      case 'Z':
-        argument_types.push_back(&type_cache_->Boolean());
-        break;
-      case 'C':
-        argument_types.push_back(&type_cache_->Char());
-        break;
-      case 'B':
-        argument_types.push_back(&type_cache_->Byte());
-        break;
-      case 'I':
-        argument_types.push_back(&type_cache_->Integer());
-        break;
-      case 'S':
-        argument_types.push_back(&type_cache_->Short());
-        break;
-      case 'F':
-        argument_types.push_back(&type_cache_->Float());
-        break;
-      case 'J':
-      case 'D': {
-        // TODO: Figure out strategy for two-register operands (double, long)
-        LOG(FATAL) << "Error: Type inference for 64-bit variables has not been implemented.";
-        break;
-      }
-      default:
-        LOG(FATAL) << "Error: Unexpected signature encountered during type inference.";
-    }
-    cur_arg++;
-  }
-  return argument_types;
-}
-
-// TODO: Lock is only used for dumping types (during development). Remove this for performance.
-void TypeInference::ComputeTypes(SeaGraph* graph) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  std::vector<Region*>* regions = graph->GetRegions();
-  std::list<InstructionNode*> worklist;
-  // Fill the work-list with all instructions.
-  for (std::vector<Region*>::const_iterator region_it = regions->begin();
-      region_it != regions->end(); region_it++) {
-    std::vector<PhiInstructionNode*>* phi_instructions = (*region_it)->GetPhiNodes();
-    std::copy(phi_instructions->begin(), phi_instructions->end(), std::back_inserter(worklist));
-    std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
-    std::copy(instructions->begin(), instructions->end(), std::back_inserter(worklist));
-  }
-  TypeInferenceVisitor tiv(graph, &type_data_, type_cache_);
-  // Record return type of the function.
-  graph->Accept(&tiv);
-  const Type* new_type = tiv.GetType();
-  type_data_.SetTypeOf(-1, new_type);   // TODO: Record this info in a way that
-                                        //      does not need magic constants.
-                                        //      Make SeaGraph a SeaNode?
-
-  // Sparse (SSA) fixed-point algorithm that processes each instruction in the work-list,
-  // adding consumers of instructions whose result changed type back into the work-list.
-  // Note: According to [1] list iterators should not be invalidated on insertion,
-  //       which simplifies the implementation; not 100% sure other STL implementations
-  //       maintain this invariant, but they should.
-  //       [1] http://www.sgi.com/tech/stl/List.html
-  // TODO: Making this conditional (as in sparse conditional constant propagation) would be good.
-  // TODO: Remove elements as I go.
-  for (std::list<InstructionNode*>::const_iterator instruction_it = worklist.begin();
-        instruction_it != worklist.end(); instruction_it++) {
-    (*instruction_it)->Accept(&tiv);
-    const Type* old_type = type_data_.FindTypeOf((*instruction_it)->Id());
-    const Type* new_type = tiv.GetType();
-    bool type_changed = (old_type != new_type);
-    if (type_changed) {
-      type_data_.SetTypeOf((*instruction_it)->Id(), new_type);
-      // Add SSA consumers of the current instruction to the work-list.
-      std::vector<InstructionNode*>* consumers = (*instruction_it)->GetSSAConsumers();
-      for (std::vector<InstructionNode*>::iterator consumer = consumers->begin();
-          consumer != consumers->end(); consumer++) {
-        worklist.push_back(*consumer);
-      }
-    }
-  }
-}
-}   // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference.h b/compiler/sea_ir/types/type_inference.h
deleted file mode 100644
index 7a178b2..0000000
--- a/compiler/sea_ir/types/type_inference.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
-#define ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
-
-#include "safe_map.h"
-#include "dex_file-inl.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-class SeaGraph;
-class InstructionNode;
-
-// The type inference in SEA IR is different from the verifier in that it is concerned
-// with a rich type hierarchy (TODO) usable in optimization and does not perform
-// precise verification (which is the job of the verifier).
-class TypeInference {
- public:
-  TypeInference() : type_cache_(new art::verifier::RegTypeCache(false)) {
-  }
-
-  // Computes the types for the method with SEA IR representation provided by @graph.
-  void ComputeTypes(SeaGraph* graph);
-
-  art::SafeMap<int, const Type*>* GetTypeMap() {
-    return type_data_.GetTypeMap();
-  }
-  // Returns true if @descriptor corresponds to a primitive type.
-  static bool IsPrimitiveDescriptor(char descriptor);
-  TypeData type_data_;    // TODO: Make private, add accessor and not publish a SafeMap above.
-  art::verifier::RegTypeCache* const type_cache_;    // TODO: Make private.
-};
-
-// Stores information about the exact type of  a function.
-class FunctionTypeInfo {
- public:
-  // Finds method information about the method encoded by a SEA IR graph.
-  // @graph provides the input method SEA IR representation.
-  // @types provides the input cache of types from which the
-  //        parameter types of the function are found.
-  FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types);
-  // Finds method information about the method encoded by
-  // an invocation instruction in a SEA IR graph.
-  // @graph provides the input method SEA IR representation.
-  // @inst  is an invocation instruction for the desired method.
-  // @types provides the input cache of types from which the
-  //        parameter types of the function are found.
-  FunctionTypeInfo(const SeaGraph* graph, InstructionNode* inst,
-      art::verifier::RegTypeCache* types);
-  // Returns the ordered vector of types corresponding to the function arguments.
-  std::vector<const Type*> GetDeclaredArgumentTypes();
-  // Returns the declared return value type.
-  const Type* GetReturnValueType();
-  // Returns the type corresponding to the class that declared the method.
-  const Type& GetDeclaringClass() {
-    return *declaring_class_;
-  }
-
-  bool IsConstructor() const {
-    return (method_access_flags_ & kAccConstructor) != 0;
-  }
-
-  bool IsStatic() const {
-    return (method_access_flags_ & kAccStatic) != 0;
-  }
-
- protected:
-  const Type* declaring_class_;
-  const art::DexFile* dex_file_;
-  const uint32_t dex_method_idx_;
-  art::verifier::RegTypeCache* type_cache_;
-  const uint32_t method_access_flags_;  // Method's access flags.
-};
-}  // namespace sea_ir
-
-#endif  // ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
diff --git a/compiler/sea_ir/types/type_inference_visitor.cc b/compiler/sea_ir/types/type_inference_visitor.cc
deleted file mode 100644
index 27bb5d8..0000000
--- a/compiler/sea_ir/types/type_inference_visitor.cc
+++ /dev/null
@@ -1,109 +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.
- */
-
-#include "scoped_thread_state_change.h"
-#include "sea_ir/types/type_inference_visitor.h"
-#include "sea_ir/types/type_inference.h"
-#include "sea_ir/ir/sea.h"
-
-namespace sea_ir {
-
-void TypeInferenceVisitor::Visit(SeaGraph* graph) {
-  FunctionTypeInfo fti(graph_, type_cache_);
-  const Type* return_type = fti.GetReturnValueType();
-  crt_type_.push_back(return_type);
-}
-
-void TypeInferenceVisitor::Visit(SignatureNode* parameter) {
-  FunctionTypeInfo fti(graph_, type_cache_);
-  std::vector<const Type*> arguments = fti.GetDeclaredArgumentTypes();
-  DCHECK_LT(parameter->GetPositionInSignature(), arguments.size())
-    << "Signature node position not present in signature.";
-  crt_type_.push_back(arguments.at(parameter->GetPositionInSignature()));
-}
-
-void TypeInferenceVisitor::Visit(UnnamedConstInstructionNode* instruction) {
-  crt_type_.push_back(&type_cache_->Integer());
-}
-
-void TypeInferenceVisitor::Visit(PhiInstructionNode* instruction) {
-  std::vector<const Type*> types_to_merge = GetOperandTypes(instruction);
-  const Type* result_type = MergeTypes(types_to_merge);
-  crt_type_.push_back(result_type);
-}
-
-void TypeInferenceVisitor::Visit(AddIntInstructionNode* instruction) {
-  std::vector<const Type*> operand_types = GetOperandTypes(instruction);
-  for (std::vector<const Type*>::const_iterator cit = operand_types.begin();
-      cit != operand_types.end(); cit++) {
-    if (*cit != NULL) {
-      DCHECK((*cit)->IsInteger());
-    }
-  }
-  crt_type_.push_back(&type_cache_->Integer());
-}
-
-void TypeInferenceVisitor::Visit(MoveResultInstructionNode* instruction) {
-  std::vector<const Type*> operand_types = GetOperandTypes(instruction);
-  const Type* operand_type = operand_types.at(0);
-  crt_type_.push_back(operand_type);
-}
-
-void TypeInferenceVisitor::Visit(InvokeStaticInstructionNode* instruction) {
-  FunctionTypeInfo fti(graph_, instruction, type_cache_);
-  const Type* result_type = fti.GetReturnValueType();
-  crt_type_.push_back(result_type);
-}
-
-std::vector<const Type*> TypeInferenceVisitor::GetOperandTypes(
-    InstructionNode* instruction) const {
-  std::vector<InstructionNode*> sources = instruction->GetSSAProducers();
-  std::vector<const Type*> types_to_merge;
-  for (std::vector<InstructionNode*>::const_iterator cit = sources.begin(); cit != sources.end();
-      cit++) {
-    const Type* source_type = type_data_->FindTypeOf((*cit)->Id());
-    if (source_type != NULL) {
-      types_to_merge.push_back(source_type);
-    }
-  }
-  return types_to_merge;
-}
-
-const Type* TypeInferenceVisitor::MergeTypes(std::vector<const Type*>& types) const {
-  const Type* type = NULL;
-  if (types.size() > 0) {
-    type = *(types.begin());
-    if (types.size() > 1) {
-      for (std::vector<const Type*>::const_iterator cit = types.begin();
-          cit != types.end(); cit++) {
-        if (!type->Equals(**cit)) {
-          type = MergeTypes(type, *cit);
-        }
-      }
-    }
-  }
-  return type;
-}
-
-const Type* TypeInferenceVisitor::MergeTypes(const Type* t1, const Type* t2) const {
-  DCHECK(t2 != NULL);
-  DCHECK(t1 != NULL);
-  art::ScopedObjectAccess soa(art::Thread::Current());
-  const Type* result = &(t1->Merge(*t2, type_cache_));
-  return result;
-}
-
-}   // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference_visitor.h b/compiler/sea_ir/types/type_inference_visitor.h
deleted file mode 100644
index d715151..0000000
--- a/compiler/sea_ir/types/type_inference_visitor.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_VISITOR_H_
-#define ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_VISITOR_H_
-
-
-#include "dex_file-inl.h"
-#include "sea_ir/ir/visitor.h"
-#include "sea_ir/types/types.h"
-
-namespace sea_ir {
-
-// The TypeInferenceVisitor visits each instruction and computes its type taking into account
-//   the current type of the operands. The type is stored in the visitor.
-// We may be better off by using a separate visitor type hierarchy that has return values
-//   or that passes data as parameters, than to use fields to store information that should
-//   in fact be returned after visiting each element. Ideally, I would prefer to use templates
-//   to specify the returned value type, but I am not aware of a possible implementation
-//   that does not horribly duplicate the visitor infrastructure code (version 1: no return value,
-//   version 2: with template return value).
-class TypeInferenceVisitor: public IRVisitor {
- public:
-  TypeInferenceVisitor(SeaGraph* graph, TypeData* type_data,
-      art::verifier::RegTypeCache* types):
-    graph_(graph), type_data_(type_data), type_cache_(types), crt_type_() {
-  }
-  // There are no type related actions to be performed on these classes.
-  void Initialize(SeaGraph* graph) { }
-  void Visit(SeaGraph* graph);
-  void Visit(Region* region) { }
-
-  void Visit(PhiInstructionNode* instruction);
-  void Visit(SignatureNode* parameter);
-  void Visit(InstructionNode* instruction) { }
-  void Visit(UnnamedConstInstructionNode* instruction);
-  void Visit(ConstInstructionNode* instruction) { }
-  void Visit(ReturnInstructionNode* instruction) { }
-  void Visit(IfNeInstructionNode* instruction) { }
-  void Visit(MoveResultInstructionNode* instruction);
-  void Visit(InvokeStaticInstructionNode* instruction);
-  void Visit(AddIntInstructionNode* instruction);
-  void Visit(GotoInstructionNode* instruction) { }
-  void Visit(IfEqzInstructionNode* instruction) { }
-
-  const Type* MergeTypes(std::vector<const Type*>& types) const;
-  const Type* MergeTypes(const Type* t1, const Type* t2) const;
-  std::vector<const Type*> GetOperandTypes(InstructionNode* instruction) const;
-  const Type* GetType() {
-    // TODO: Currently multiple defined types are not supported.
-    if (!crt_type_.empty()) {
-      const Type* single_type = crt_type_.at(0);
-      crt_type_.clear();
-      return single_type;
-    }
-    return NULL;
-  }
-
- protected:
-  const SeaGraph* const graph_;
-  TypeData* type_data_;
-  art::verifier::RegTypeCache* type_cache_;
-  std::vector<const Type*> crt_type_;             // Stored temporarily between two calls to Visit.
-};
-
-}  // namespace sea_ir
-
-#endif  // ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_VISITOR_H_
diff --git a/compiler/sea_ir/types/type_inference_visitor_test.cc b/compiler/sea_ir/types/type_inference_visitor_test.cc
deleted file mode 100644
index ccb6991..0000000
--- a/compiler/sea_ir/types/type_inference_visitor_test.cc
+++ /dev/null
@@ -1,132 +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.
- */
-
-#include "common_compiler_test.h"
-#include "sea_ir/types/type_inference_visitor.h"
-#include "sea_ir/ir/sea.h"
-
-namespace sea_ir {
-
-class TestInstructionNode:public InstructionNode {
- public:
-  explicit TestInstructionNode(std::vector<InstructionNode*> prods): InstructionNode(NULL),
-      producers_(prods) { }
-  std::vector<InstructionNode*> GetSSAProducers() {
-    return producers_;
-  }
- protected:
-  std::vector<InstructionNode*> producers_;
-};
-
-class TypeInferenceVisitorTest : public art::CommonCompilerTest {};
-
-TEST_F(TypeInferenceVisitorTest, MergeIntWithByte) {
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  TypeInferenceVisitor tiv(NULL, &td, &type_cache);
-  const Type* int_type = &type_cache.Integer();
-  const Type* byte_type = &type_cache.Byte();
-  const Type* ib_type = tiv.MergeTypes(int_type, byte_type);
-  const Type* bi_type = tiv.MergeTypes(byte_type, int_type);
-  EXPECT_TRUE(ib_type == int_type);
-  EXPECT_TRUE(bi_type == int_type);
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeIntWithShort) {
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  TypeInferenceVisitor tiv(NULL, &td, &type_cache);
-  const Type* int_type = &type_cache.Integer();
-  const Type* short_type = &type_cache.Short();
-  const Type* is_type = tiv.MergeTypes(int_type, short_type);
-  const Type* si_type = tiv.MergeTypes(short_type, int_type);
-  EXPECT_TRUE(is_type == int_type);
-  EXPECT_TRUE(si_type == int_type);
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeMultipleInts) {
-  int N = 10;  // Number of types to merge.
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  TypeInferenceVisitor tiv(NULL, &td, &type_cache);
-  std::vector<const Type*> types;
-  for (int i = 0; i < N; i++) {
-    const Type* new_type = &type_cache.Integer();
-    types.push_back(new_type);
-  }
-  const Type* merged_type = tiv.MergeTypes(types);
-  EXPECT_TRUE(merged_type == &type_cache.Integer());
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeMultipleShorts) {
-  int N = 10;  // Number of types to merge.
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  TypeInferenceVisitor tiv(NULL, &td, &type_cache);
-  std::vector<const Type*> types;
-  for (int i = 0; i < N; i++) {
-    const Type* new_type = &type_cache.Short();
-    types.push_back(new_type);
-  }
-  const Type* merged_type = tiv.MergeTypes(types);
-  EXPECT_TRUE(merged_type == &type_cache.Short());
-}
-
-TEST_F(TypeInferenceVisitorTest, MergeMultipleIntsWithShorts) {
-  int N = 10;  // Number of types to merge.
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  TypeInferenceVisitor tiv(NULL, &td, &type_cache);
-  std::vector<const Type*> types;
-  for (int i = 0; i < N; i++) {
-    const Type* short_type = &type_cache.Short();
-    const Type* int_type = &type_cache.Integer();
-    types.push_back(short_type);
-    types.push_back(int_type);
-  }
-  const Type* merged_type = tiv.MergeTypes(types);
-  EXPECT_TRUE(merged_type == &type_cache.Integer());
-}
-
-TEST_F(TypeInferenceVisitorTest, GetOperandTypes) {
-  int N = 10;  // Number of types to merge.
-  TypeData td;
-  art::verifier::RegTypeCache type_cache(false);
-  TypeInferenceVisitor tiv(NULL, &td, &type_cache);
-  std::vector<const Type*> types;
-  std::vector<InstructionNode*> preds;
-  for (int i = 0; i < N; i++) {
-    const Type* short_type = &type_cache.Short();
-    const Type* int_type = &type_cache.Integer();
-    TestInstructionNode* short_inst =
-        new TestInstructionNode(std::vector<InstructionNode*>());
-    TestInstructionNode* int_inst =
-        new TestInstructionNode(std::vector<InstructionNode*>());
-    preds.push_back(short_inst);
-    preds.push_back(int_inst);
-    td.SetTypeOf(short_inst->Id(), short_type);
-    td.SetTypeOf(int_inst->Id(), int_type);
-    types.push_back(short_type);
-    types.push_back(int_type);
-  }
-  TestInstructionNode* inst_to_test = new TestInstructionNode(preds);
-  std::vector<const Type*> result = tiv.GetOperandTypes(inst_to_test);
-  EXPECT_TRUE(result.size() == types.size());
-  EXPECT_TRUE(true == std::equal(types.begin(), types.begin() + 2, result.begin()));
-}
-
-
-}  // namespace sea_ir
diff --git a/compiler/sea_ir/types/types.h b/compiler/sea_ir/types/types.h
deleted file mode 100644
index 64f2524..0000000
--- a/compiler/sea_ir/types/types.h
+++ /dev/null
@@ -1,58 +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.
- */
-
-#ifndef ART_COMPILER_SEA_IR_TYPES_TYPES_H_
-#define ART_COMPILER_SEA_IR_TYPES_TYPES_H_
-
-#include "safe_map.h"
-#include "verifier/reg_type.h"
-#include "verifier/reg_type_cache.h"
-
-namespace sea_ir {
-
-// TODO: Replace typedef with an actual class implementation when we have more types.
-typedef art::verifier::RegType Type;
-
-// Stores information about the result type of each instruction.
-// Note: Main purpose is to encapsulate the map<instruction id, type*>,
-//       so that we can replace the underlying storage at any time.
-class TypeData {
- public:
-  art::SafeMap<int, const Type*>* GetTypeMap() {
-    return &type_map_;
-  }
-  // Returns the type associated with instruction with @instruction_id.
-  const Type* FindTypeOf(int instruction_id) {
-    art::SafeMap<int, const Type*>::const_iterator result_it = type_map_.find(instruction_id);
-    if (type_map_.end() != result_it) {
-      return result_it->second;
-    }
-    return NULL;
-  }
-
-  // Saves the fact that instruction @instruction_id produces a value of type @type.
-  void SetTypeOf(int instruction_id, const Type* type) {
-    type_map_.Overwrite(instruction_id, type);
-  }
-
- private:
-  art::SafeMap<int, const Type*> type_map_;
-};
-
-
-
-}  // namespace sea_ir
-#endif  // ART_COMPILER_SEA_IR_TYPES_TYPES_H_
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index cb07ffa..cb51ed8 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -20,6 +20,7 @@
 #include "utils/arm/assembler_arm.h"
 #include "utils/arm64/assembler_arm64.h"
 #include "utils/mips/assembler_mips.h"
+#include "utils/mips64/assembler_mips64.h"
 #include "utils/x86/assembler_x86.h"
 #include "utils/x86_64/assembler_x86_64.h"
 
@@ -40,8 +41,7 @@
       __ LoadFromOffset(kLoadWord, IP, R0, JNIEnvExt::SelfOffset().Int32Value());
       __ LoadFromOffset(kLoadWord, PC, IP, offset.Int32Value());
       break;
-    case kPortableAbi:  // R9 holds Thread*.
-    case kQuickAbi:  // Fall-through.
+    case kQuickAbi:  // R9 holds Thread*.
       __ LoadFromOffset(kLoadWord, PC, R9, offset.Int32Value());
   }
   __ bkpt(0);
@@ -75,8 +75,7 @@
                 Arm64ManagedRegister::FromXRegister(IP0));
 
       break;
-    case kPortableAbi:  // X18 holds Thread*.
-    case kQuickAbi:  // Fall-through.
+    case kQuickAbi:  // X18 holds Thread*.
       __ JumpTo(Arm64ManagedRegister::FromXRegister(TR), Offset(offset.Int32Value()),
                 Arm64ManagedRegister::FromXRegister(IP0));
 
@@ -106,8 +105,7 @@
       __ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
       __ LoadFromOffset(kLoadWord, T9, T9, offset.Int32Value());
       break;
-    case kPortableAbi:  // S1 holds Thread*.
-    case kQuickAbi:  // Fall-through.
+    case kQuickAbi:  // S1 holds Thread*.
       __ LoadFromOffset(kLoadWord, T9, S1, offset.Int32Value());
   }
   __ Jr(T9);
@@ -123,6 +121,35 @@
 }
 }  // namespace mips
 
+namespace mips64 {
+static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention abi,
+                                                    ThreadOffset<8> offset) {
+  std::unique_ptr<Mips64Assembler> assembler(static_cast<Mips64Assembler*>(Assembler::Create(kMips64)));
+
+  switch (abi) {
+    case kInterpreterAbi:  // Thread* is first argument (A0) in interpreter ABI.
+      __ LoadFromOffset(kLoadDoubleword, T9, A0, offset.Int32Value());
+      break;
+    case kJniAbi:  // Load via Thread* held in JNIEnv* in first argument (A0).
+      __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
+      __ LoadFromOffset(kLoadDoubleword, T9, T9, offset.Int32Value());
+      break;
+    case kQuickAbi:  // Fall-through.
+      __ LoadFromOffset(kLoadDoubleword, T9, S1, offset.Int32Value());
+  }
+  __ Jr(T9);
+  __ Nop();
+  __ Break();
+
+  size_t cs = assembler->CodeSize();
+  std::unique_ptr<std::vector<uint8_t>> entry_stub(new std::vector<uint8_t>(cs));
+  MemoryRegion code(&(*entry_stub)[0], entry_stub->size());
+  assembler->FinalizeInstructions(code);
+
+  return entry_stub.release();
+}
+}  // namespace mips64
+
 namespace x86 {
 static const std::vector<uint8_t>* CreateTrampoline(ThreadOffset<4> offset) {
   std::unique_ptr<X86Assembler> assembler(static_cast<X86Assembler*>(Assembler::Create(kX86)));
@@ -163,6 +190,8 @@
   switch (isa) {
     case kArm64:
       return arm64::CreateTrampoline(abi, offset);
+    case kMips64:
+      return mips64::CreateTrampoline(abi, offset);
     case kX86_64:
       return x86_64::CreateTrampoline(offset);
     default:
diff --git a/compiler/utils/arena_bit_vector.h b/compiler/utils/arena_bit_vector.h
index 34f1ca9..e5e1b70 100644
--- a/compiler/utils/arena_bit_vector.h
+++ b/compiler/utils/arena_bit_vector.h
@@ -35,14 +35,10 @@
   kBitMapDominators,
   kBitMapIDominated,
   kBitMapDomFrontier,
-  kBitMapPhi,
-  kBitMapTmpBlocks,
-  kBitMapInputBlocks,
   kBitMapRegisterV,
   kBitMapTempSSARegisterV,
   kBitMapNullCheck,
   kBitMapClInitCheck,
-  kBitMapTmpBlockV,
   kBitMapPredecessors,
   kNumBitMapKinds
 };
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 0528773..1f44f19b23 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -245,6 +245,7 @@
 
 // This is very like the ARM encoding except the offset is 10 bits.
 uint32_t Address::encodingThumbLdrdStrd() const {
+  DCHECK(IsImmediate());
   uint32_t encoding;
   uint32_t am = am_;
   // If P is 0 then W must be 1 (Different from ARM).
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index c86ec4b..0d84ba7 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -429,6 +429,8 @@
 
   virtual void ldrex(Register rd, Register rn, Condition cond = AL) = 0;
   virtual void strex(Register rd, Register rt, Register rn, Condition cond = AL) = 0;
+  virtual void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) = 0;
+  virtual void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) = 0;
 
   // Miscellaneous instructions.
   virtual void clrex(Condition cond = AL) = 0;
@@ -532,6 +534,23 @@
 
   // Load and Store. May clobber IP.
   virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
+  void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {
+    if (!vmovs(sd, value, cond)) {
+      LoadImmediate(IP, bit_cast<int32_t, float>(value), cond);
+      vmovsr(sd, IP, cond);
+    }
+  }
+
+  void LoadDImmediate(DRegister sd, double value, Condition cond = AL) {
+    if (!vmovd(sd, value, cond)) {
+      uint64_t int_value = bit_cast<uint64_t, double>(value);
+      LoadSImmediate(
+          static_cast<SRegister>(sd << 1), bit_cast<float, uint32_t>(Low32Bits(int_value)));
+      LoadSImmediate(
+          static_cast<SRegister>((sd << 1) + 1), bit_cast<float, uint32_t>(High32Bits(int_value)));
+    }
+  }
+
   virtual void MarkExceptionHandler(Label* label) = 0;
   virtual void LoadFromOffset(LoadOperandType type,
                               Register reg,
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 8f6d45a..8d1fb60 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -778,6 +778,7 @@
   Emit(encoding);
 }
 
+
 void Arm32Assembler::ldrex(Register rt, Register rn, Condition cond) {
   CHECK_NE(rn, kNoRegister);
   CHECK_NE(rt, kNoRegister);
@@ -793,6 +794,25 @@
 }
 
 
+void Arm32Assembler::ldrexd(Register rt, Register rt2, Register rn, Condition cond) {
+  CHECK_NE(rn, kNoRegister);
+  CHECK_NE(rt, kNoRegister);
+  CHECK_NE(rt2, kNoRegister);
+  CHECK_NE(rt, R14);
+  CHECK_EQ(0u, static_cast<uint32_t>(rt) % 2);
+  CHECK_EQ(static_cast<uint32_t>(rt) + 1, static_cast<uint32_t>(rt2));
+  CHECK_NE(cond, kNoCondition);
+
+  int32_t encoding =
+      (static_cast<uint32_t>(cond) << kConditionShift) |
+      B24 | B23 | B21 | B20 |
+      static_cast<uint32_t>(rn) << 16 |
+      static_cast<uint32_t>(rt) << 12 |
+      B11 | B10 | B9 | B8 | B7 | B4 | B3 | B2 | B1 | B0;
+  Emit(encoding);
+}
+
+
 void Arm32Assembler::strex(Register rd,
                            Register rt,
                            Register rn,
@@ -811,6 +831,28 @@
   Emit(encoding);
 }
 
+void Arm32Assembler::strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond) {
+  CHECK_NE(rd, kNoRegister);
+  CHECK_NE(rn, kNoRegister);
+  CHECK_NE(rt, kNoRegister);
+  CHECK_NE(rt2, kNoRegister);
+  CHECK_NE(rt, R14);
+  CHECK_NE(rd, rt);
+  CHECK_NE(rd, rt2);
+  CHECK_EQ(0u, static_cast<uint32_t>(rt) % 2);
+  CHECK_EQ(static_cast<uint32_t>(rt) + 1, static_cast<uint32_t>(rt2));
+  CHECK_NE(cond, kNoCondition);
+
+  int32_t encoding =
+      (static_cast<uint32_t>(cond) << kConditionShift) |
+      B24 | B23 | B21 |
+      static_cast<uint32_t>(rn) << 16 |
+      static_cast<uint32_t>(rd) << 12 |
+      B11 | B10 | B9 | B8 | B7 | B4 |
+      static_cast<uint32_t>(rt);
+  Emit(encoding);
+}
+
 
 void Arm32Assembler::clrex(Condition cond) {
   CHECK_EQ(cond, AL);   // This cannot be conditional on ARM.
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 6c8d415..b922d66 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -123,6 +123,8 @@
 
   void ldrex(Register rd, Register rn, Condition cond = AL) OVERRIDE;
   void strex(Register rd, Register rt, Register rn, Condition cond = AL) OVERRIDE;
+  void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
+  void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
 
   // Miscellaneous instructions.
   void clrex(Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 951792d..4a0ae0b 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -697,4 +697,28 @@
   DriverStr(expected, "vmrs");
 }
 
+TEST_F(AssemblerArm32Test, ldrexd) {
+  GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0);
+  GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1);
+  GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2);
+
+  const char* expected =
+      "ldrexd r0, r1, [r0]\n"
+      "ldrexd r0, r1, [r1]\n"
+      "ldrexd r0, r1, [r2]\n";
+  DriverStr(expected, "ldrexd");
+}
+
+TEST_F(AssemblerArm32Test, strexd) {
+  GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0);
+  GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1);
+  GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2);
+
+  const char* expected =
+      "strexd r9, r0, r1, [r0]\n"
+      "strexd r9, r0, r1, [r1]\n"
+      "strexd r9, r0, r1, [r2]\n";
+  DriverStr(expected, "strexd");
+}
+
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 479186c..5383c28 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -25,8 +25,8 @@
 namespace art {
 namespace arm {
 
-bool Thumb2Assembler::ShifterOperandCanHold(Register rd,
-                                            Register rn,
+bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
+                                            Register rn ATTRIBUTE_UNUSED,
                                             Opcode opcode,
                                             uint32_t immediate,
                                             ShifterOperand* shifter_op) {
@@ -37,13 +37,6 @@
   switch (opcode) {
     case ADD:
     case SUB:
-      if (rn == SP) {
-        if (rd == SP) {
-          return immediate < (1 << 9);    // 9 bits allowed.
-        } else {
-          return immediate < (1 << 12);   // 12 bits.
-        }
-      }
       if (immediate < (1 << 12)) {    // Less than (or equal to) 12 bits can always be done.
         return true;
       }
@@ -698,48 +691,37 @@
     return true;
   }
 
-  bool can_contain_high_register = (opcode == MOV)
-      || ((opcode == ADD || opcode == SUB) && (rn == rd));
-
-  if (IsHighRegister(rd) || IsHighRegister(rn)) {
-    if (can_contain_high_register) {
-      // There are high register instructions available for this opcode.
-      // However, there is no RRX available.
-      if (so.IsShift() && so.GetShift() == RRX) {
-        return true;
+  // Check special case for SP relative ADD and SUB immediate.
+  if ((opcode == ADD || opcode == SUB) && rn == SP && so.IsImmediate()) {
+    // If the immediate is in range, use 16 bit.
+    if (rd == SP) {
+      if (so.GetImmediate() < (1 << 9)) {    // 9 bit immediate.
+        return false;
       }
-
-      // Check special case for SP relative ADD and SUB immediate.
-      if ((opcode == ADD || opcode == SUB) && so.IsImmediate()) {
-        // If rn is SP and rd is a high register we need to use a 32 bit encoding.
-         if (rn == SP && rd != SP && IsHighRegister(rd)) {
-           return true;
-         }
-
-         uint32_t imm = so.GetImmediate();
-         // If the immediates are out of range use 32 bit.
-         if (rd == SP && rn == SP) {
-           if (imm > (1 << 9)) {    // 9 bit immediate.
-             return true;
-           }
-         } else if (opcode == ADD && rd != SP && rn == SP) {   // 10 bit immediate.
-           if (imm > (1 << 10)) {
-             return true;
-           }
-         } else if (opcode == SUB && rd != SP && rn == SP) {
-           // SUB rd, SP, #imm is always 32 bit.
-           return true;
-         }
+    } else if (!IsHighRegister(rd) && opcode == ADD) {
+      if (so.GetImmediate() < (1 << 10)) {    // 10 bit immediate.
+        return false;
       }
     }
+  }
 
-    // The ADD,SUB and MOV instructions that work with high registers don't have
-    // immediate variants.
-    if (so.IsImmediate()) {
+  bool can_contain_high_register = (opcode == MOV)
+      || ((opcode == ADD) && (rn == rd) && !set_cc);
+
+  if (IsHighRegister(rd) || IsHighRegister(rn)) {
+    if (!can_contain_high_register) {
       return true;
     }
 
-    if (!can_contain_high_register) {
+    // There are high register instructions available for this opcode.
+    // However, there is no actual shift available, neither for ADD nor for MOV (ASR/LSR/LSL/ROR).
+    if (so.IsShift() && (so.GetShift() == RRX || so.GetImmediate() != 0u)) {
+      return true;
+    }
+
+    // The ADD and MOV instructions that work with high registers don't have 16-bit
+    // immediate variants.
+    if (so.IsImmediate()) {
       return true;
     }
   }
@@ -938,41 +920,71 @@
     if (so.IsImmediate()) {
       use_immediate = true;
       immediate = so.GetImmediate();
+    } else {
+      // Adjust rn and rd: only two registers will be emitted.
+      switch (opcode) {
+        case AND:
+        case ORR:
+        case EOR:
+        case RSB:
+        case ADC:
+        case SBC:
+        case BIC: {
+          if (rn == rd) {
+            rn = so.GetRegister();
+          } else {
+            CHECK_EQ(rd, so.GetRegister());
+          }
+          break;
+        }
+        case CMP:
+        case CMN: {
+          CHECK_EQ(rd, 0);
+          rd = rn;
+          rn = so.GetRegister();
+          break;
+        }
+        case TST:
+        case TEQ:
+        case MVN: {
+          CHECK_EQ(rn, 0);
+          rn = so.GetRegister();
+          break;
+        }
+        default:
+          break;
+      }
     }
 
     switch (opcode) {
       case AND: thumb_opcode = 0U /* 0b0000 */; break;
+      case ORR: thumb_opcode = 12U /* 0b1100 */; break;
       case EOR: thumb_opcode = 1U /* 0b0001 */; break;
-      case SUB: break;
       case RSB: thumb_opcode = 9U /* 0b1001 */; break;
-      case ADD: break;
       case ADC: thumb_opcode = 5U /* 0b0101 */; break;
       case SBC: thumb_opcode = 6U /* 0b0110 */; break;
-      case RSC: break;
-      case TST: thumb_opcode = 8U /* 0b1000 */; rn = so.GetRegister(); break;
-      case TEQ: break;
-      case CMP:
+      case BIC: thumb_opcode = 14U /* 0b1110 */; break;
+      case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break;
+      case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break;
+      case CMP: {
         if (use_immediate) {
           // T2 encoding.
-           dp_opcode = 0;
-           opcode_shift = 11;
-           thumb_opcode = 5U /* 0b101 */;
-           rd_shift = 8;
-           rn_shift = 8;
+          dp_opcode = 0;
+          opcode_shift = 11;
+          thumb_opcode = 5U /* 0b101 */;
+          rd_shift = 8;
+          rn_shift = 8;
         } else {
           thumb_opcode = 10U /* 0b1010 */;
-          rd = rn;
-          rn = so.GetRegister();
         }
 
         break;
+      }
       case CMN: {
+        CHECK(!use_immediate);
         thumb_opcode = 11U /* 0b1011 */;
-        rd = rn;
-        rn = so.GetRegister();
         break;
       }
-      case ORR: thumb_opcode = 12U /* 0b1100 */; break;
       case MOV:
         dp_opcode = 0;
         if (use_immediate) {
@@ -995,9 +1007,11 @@
           }
         }
         break;
-      case BIC: thumb_opcode = 14U /* 0b1110 */; break;
-      case MVN: thumb_opcode = 15U /* 0b1111 */; rn = so.GetRegister(); break;
+
+      case TEQ:
+      case RSC:
       default:
+        LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
         break;
     }
   }
@@ -1020,7 +1034,7 @@
 // ADD and SUB are complex enough to warrant their own emitter.
 void Thumb2Assembler::Emit16BitAddSub(Condition cond ATTRIBUTE_UNUSED,
                                       Opcode opcode,
-                                      bool set_cc ATTRIBUTE_UNUSED,
+                                      bool set_cc,
                                       Register rn,
                                       Register rd,
                                       const ShifterOperand& so) {
@@ -1030,7 +1044,7 @@
   uint8_t rn_shift = 3;
   uint8_t immediate_shift = 0;
   bool use_immediate = false;
-  uint8_t immediate = 0;
+  uint32_t immediate = 0;  // Should be at most 9 bits but keep the full immediate for CHECKs.
   uint8_t thumb_opcode;;
 
   if (so.IsImmediate()) {
@@ -1042,7 +1056,7 @@
     case ADD:
       if (so.IsRegister()) {
         Register rm = so.GetRegister();
-        if (rn == rd) {
+        if (rn == rd && !set_cc) {
           // Can use T2 encoding (allows 4 bit registers)
           dp_opcode = 1U /* 0b01 */;
           opcode_shift = 10;
@@ -1066,8 +1080,8 @@
           dp_opcode = 2U /* 0b10 */;
           thumb_opcode = 3U /* 0b11 */;
           opcode_shift = 12;
-          CHECK_LT(immediate, (1 << 9));
-          CHECK_EQ((immediate & 3 /* 0b11 */), 0);
+          CHECK_LT(immediate, (1u << 9));
+          CHECK_EQ((immediate & 3u /* 0b11 */), 0u);
 
           // Remove rd and rn from instruction by orring it with immed and clearing bits.
           rn = R0;
@@ -1080,8 +1094,8 @@
           dp_opcode = 2U /* 0b10 */;
           thumb_opcode = 5U /* 0b101 */;
           opcode_shift = 11;
-          CHECK_LT(immediate, (1 << 10));
-          CHECK_EQ((immediate & 3 /* 0b11 */), 0);
+          CHECK_LT(immediate, (1u << 10));
+          CHECK_EQ((immediate & 3u /* 0b11 */), 0u);
 
           // Remove rn from instruction.
           rn = R0;
@@ -1117,8 +1131,8 @@
            dp_opcode = 2U /* 0b10 */;
            thumb_opcode = 0x61 /* 0b1100001 */;
            opcode_shift = 7;
-           CHECK_LT(immediate, (1 << 9));
-           CHECK_EQ((immediate & 3 /* 0b11 */), 0);
+           CHECK_LT(immediate, (1u << 9));
+           CHECK_EQ((immediate & 3u /* 0b11 */), 0u);
 
            // Remove rd and rn from instruction by orring it with immed and clearing bits.
            rn = R0;
@@ -1673,9 +1687,6 @@
   CHECK_NE(rn, kNoRegister);
   CHECK_NE(rt, kNoRegister);
   CheckCondition(cond);
-  CHECK_NE(rn, kNoRegister);
-  CHECK_NE(rt, kNoRegister);
-  CheckCondition(cond);
   CHECK_LT(imm, (1u << 10));
 
   int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
@@ -1712,6 +1723,22 @@
 }
 
 
+void Thumb2Assembler::ldrexd(Register rt, Register rt2, Register rn, Condition cond) {
+  CHECK_NE(rn, kNoRegister);
+  CHECK_NE(rt, kNoRegister);
+  CHECK_NE(rt2, kNoRegister);
+  CHECK_NE(rt, rt2);
+  CheckCondition(cond);
+
+  int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 | B20 |
+      static_cast<uint32_t>(rn) << 16 |
+      static_cast<uint32_t>(rt) << 12 |
+      static_cast<uint32_t>(rt2) << 8 |
+      B6 | B5 | B4 | B3 | B2 | B1 | B0;
+  Emit32(encoding);
+}
+
+
 void Thumb2Assembler::strex(Register rd,
                             Register rt,
                             Register rn,
@@ -1720,6 +1747,26 @@
 }
 
 
+void Thumb2Assembler::strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond) {
+  CHECK_NE(rd, kNoRegister);
+  CHECK_NE(rn, kNoRegister);
+  CHECK_NE(rt, kNoRegister);
+  CHECK_NE(rt2, kNoRegister);
+  CHECK_NE(rt, rt2);
+  CHECK_NE(rd, rt);
+  CHECK_NE(rd, rt2);
+  CheckCondition(cond);
+
+  int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 |
+      static_cast<uint32_t>(rn) << 16 |
+      static_cast<uint32_t>(rt) << 12 |
+      static_cast<uint32_t>(rt2) << 8 |
+      B6 | B5 | B4 |
+      static_cast<uint32_t>(rd);
+  Emit32(encoding);
+}
+
+
 void Thumb2Assembler::clrex(Condition cond) {
   CheckCondition(cond);
   int32_t encoding = B31 | B30 | B29 | B27 | B28 | B25 | B24 | B23 |
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 48a3a7e..81dd138 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -149,6 +149,8 @@
   void ldrex(Register rd, Register rn, uint16_t imm, Condition cond = AL);
   void strex(Register rd, Register rt, Register rn, uint16_t imm, Condition cond = AL);
 
+  void ldrexd(Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
+  void strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond = AL) OVERRIDE;
 
   // Miscellaneous instructions.
   void clrex(Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 6ae95a4..ebea9d4 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -30,11 +30,15 @@
   }
 
   std::string GetAssemblerParameters() OVERRIDE {
-    return " -mthumb -mfpu=neon";
+    return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
+  }
+
+  const char* GetAssemblyHeader() OVERRIDE {
+    return kThumb2AssemblyHeader;
   }
 
   std::string GetDisassembleParameters() OVERRIDE {
-    return " -D -bbinary -marm --no-show-raw-insn";
+    return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
   }
 
   void SetUpHelpers() OVERRIDE {
@@ -76,6 +80,8 @@
 
  private:
   std::vector<arm::Register*> registers_;
+
+  static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
 };
 
 
@@ -164,4 +170,61 @@
   DriverStr(expected, "vmrs");
 }
 
+TEST_F(AssemblerThumb2Test, ldrexd) {
+  GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0);
+  GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1);
+  GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2);
+  GetAssembler()->ldrexd(arm::R5, arm::R3, arm::R7);
+
+  const char* expected =
+      "ldrexd r0, r1, [r0]\n"
+      "ldrexd r0, r1, [r1]\n"
+      "ldrexd r0, r1, [r2]\n"
+      "ldrexd r5, r3, [r7]\n";
+  DriverStr(expected, "ldrexd");
+}
+
+TEST_F(AssemblerThumb2Test, strexd) {
+  GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0);
+  GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1);
+  GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2);
+  GetAssembler()->strexd(arm::R9, arm::R5, arm::R3, arm::R7);
+
+  const char* expected =
+      "strexd r9, r0, r1, [r0]\n"
+      "strexd r9, r0, r1, [r1]\n"
+      "strexd r9, r0, r1, [r2]\n"
+      "strexd r9, r5, r3, [r7]\n";
+  DriverStr(expected, "strexd");
+}
+
+TEST_F(AssemblerThumb2Test, LdrdStrd) {
+  GetAssembler()->ldrd(arm::R0, arm::Address(arm::R2, 8));
+  GetAssembler()->ldrd(arm::R0, arm::Address(arm::R12));
+  GetAssembler()->strd(arm::R0, arm::Address(arm::R2, 8));
+
+  const char* expected =
+      "ldrd r0, r1, [r2, #8]\n"
+      "ldrd r0, r1, [r12]\n"
+      "strd r0, r1, [r2, #8]\n";
+  DriverStr(expected, "ldrdstrd");
+}
+
+TEST_F(AssemblerThumb2Test, eor) {
+#define __ GetAssembler()->
+  __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
+  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
+  __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
+  __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
+  __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
+
+  const char* expected =
+      "eors r1, r0\n"
+      "eor r1, r0, r1\n"
+      "eor r1, r8, r0\n"
+      "eor r8, r1, r0\n"
+      "eor r1, r0, r8\n";
+  DriverStr(expected, "abs");
+}
+
 }  // namespace art
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 1a7f2e8..b1b0ee5 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -84,7 +84,7 @@
 
   template <typename U, typename Alloc>
   ArrayRef(const std::vector<U, Alloc>& v,
-           typename std::enable_if<std::is_same<T, const U>::value, tag>::tag
+           typename std::enable_if<std::is_same<T, const U>::value, tag>::type
                t ATTRIBUTE_UNUSED = tag())
       : array_(v.data()), size_(v.size()) {
   }
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 6834512..5340dd3 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -23,6 +23,7 @@
 #include "arm/assembler_thumb2.h"
 #include "arm64/assembler_arm64.h"
 #include "mips/assembler_mips.h"
+#include "mips64/assembler_mips64.h"
 #include "x86/assembler_x86.h"
 #include "x86_64/assembler_x86_64.h"
 #include "globals.h"
@@ -115,6 +116,8 @@
       return new arm64::Arm64Assembler();
     case kMips:
       return new mips::MipsAssembler();
+    case kMips64:
+      return new mips64::Mips64Assembler();
     case kX86:
       return new x86::X86Assembler();
     case kX86_64:
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 67711e3..923ecdb 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -47,6 +47,9 @@
 namespace mips {
   class MipsAssembler;
 }
+namespace mips64 {
+  class Mips64Assembler;
+}
 namespace x86 {
   class X86Assembler;
 }
@@ -120,6 +123,7 @@
   friend class arm::Thumb2Assembler;
   friend class arm64::Arm64Assembler;
   friend class mips::MipsAssembler;
+  friend class mips64::Mips64Assembler;
   friend class x86::X86Assembler;
   friend class x86_64::X86_64Assembler;
 
@@ -502,6 +506,8 @@
 
   virtual void InitializeFrameDescriptionEntry() {}
   virtual void FinalizeFrameDescriptionEntry() {}
+  // Give a vector containing FDE data, or null if not used. Note: the assembler must take care
+  // of handling the lifecycle.
   virtual std::vector<uint8_t>* GetFrameDescriptionEntry() { return nullptr; }
 
   virtual ~Assembler() {}
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 2b55120..6f8b301 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -29,6 +29,10 @@
 
 namespace art {
 
+// If you want to take a look at the differences between the ART assembler and GCC, set this flag
+// to true. The disassembled files will then remain in the tmp directory.
+static constexpr bool kKeepDisassembledFiles = false;
+
 // Helper for a constexpr string length.
 constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
   return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
@@ -685,12 +689,12 @@
 
     bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
 
-    // If you want to take a look at the differences between the ART assembler and GCC, comment
-    // out the removal code.
-//    std::remove(data_name.c_str());
-//    std::remove(as_name.c_str());
-//    std::remove((data_name + ".dis").c_str());
-//    std::remove((as_name + ".dis").c_str());
+    if (!kKeepDisassembledFiles) {
+      std::remove(data_name.c_str());
+      std::remove(as_name.c_str());
+      std::remove((data_name + ".dis").c_str());
+      std::remove((as_name + ".dis").c_str());
+    }
 
     return result;
   }
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index e3a9580..a171e59 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -309,13 +309,13 @@
   // 16 bit variants.
   __ add(R0, R1, ShifterOperand());
   __ sub(R0, R1, ShifterOperand());
-  __ and_(R0, R1, ShifterOperand());
-  __ orr(R0, R1, ShifterOperand());
-  __ eor(R0, R1, ShifterOperand());
-  __ bic(R0, R1, ShifterOperand());
-  __ adc(R0, R1, ShifterOperand());
-  __ sbc(R0, R1, ShifterOperand());
-  __ rsb(R0, R1, ShifterOperand());
+  __ and_(R0, R0, ShifterOperand(R1));
+  __ orr(R0, R0, ShifterOperand(R1));
+  __ eor(R0, R0, ShifterOperand(R1));
+  __ bic(R0, R0, ShifterOperand(R1));
+  __ adc(R0, R0, ShifterOperand(R1));
+  __ sbc(R0, R0, ShifterOperand(R1));
+  __ rsb(R0, R0, ShifterOperand(R1));
 
   __ tst(R0, ShifterOperand(R1));
   __ teq(R0, ShifterOperand(R1));
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 3f2641c..3d03234 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -102,11 +102,11 @@
   "   4:	11a3      	asrs	r3, r4, #6\n",
   "   6:	ea4f 13f4 	mov.w	r3, r4, ror #7\n",
   "   a:	41e3      	rors	r3, r4\n",
-  "   c:	0128      	lsls	r0, r5, #4\n",
-  "   e:	0968      	lsrs	r0, r5, #5\n",
-  "  10:	11a8      	asrs	r0, r5, #6\n",
-  "  12:	ea4f 18f4 	mov.w	r8, r4, ror #7\n",
-  "  16:	ea4f 0834 	mov.w	r8, r4, rrx\n",
+  "   c:	ea4f 1804 	mov.w	r8, r4, lsl #4\n",
+  "  10:	ea4f 1854 	mov.w	r8, r4, lsr #5\n",
+  "  14:	ea4f 18a4 	mov.w	r8, r4, asr #6\n",
+  "  18:	ea4f 18f4 	mov.w	r8, r4, ror #7\n",
+  "  1c:	ea4f 0834 	mov.w	r8, r4, rrx\n",
   nullptr
 };
 const char* BasicLoadResults[] = {
@@ -340,15 +340,15 @@
   nullptr
 };
 const char* SpecialAddSubResults[] = {
-  "   0:	f20d 0250 	addw	r2, sp, #80	; 0x50\n",
-  "   4:	f20d 0d50 	addw	sp, sp, #80	; 0x50\n",
-  "   8:	f20d 0850 	addw	r8, sp, #80	; 0x50\n",
-  "   c:	f60d 7200 	addw	r2, sp, #3840	; 0xf00\n",
-  "  10:	f60d 7d00 	addw	sp, sp, #3840	; 0xf00\n",
-  "  14:	f2ad 0d50 	subw	sp, sp, #80	; 0x50\n",
-  "  18:	f2ad 0050 	subw	r0, sp, #80	; 0x50\n",
-  "  1c:	f2ad 0850 	subw	r8, sp, #80	; 0x50\n",
-  "  20:	f6ad 7d00 	subw	sp, sp, #3840	; 0xf00\n",
+  "   0:	aa14      	add	r2, sp, #80	; 0x50\n",
+  "   2:	b014      	add	sp, #80		; 0x50\n",
+  "   4:	f20d 0850 	addw	r8, sp, #80	; 0x50\n",
+  "   8:	f60d 7200 	addw	r2, sp, #3840	; 0xf00\n",
+  "   c:	f60d 7d00 	addw	sp, sp, #3840	; 0xf00\n",
+  "  10:	b094      	sub	sp, #80		; 0x50\n",
+  "  12:	f2ad 0050 	subw	r0, sp, #80	; 0x50\n",
+  "  16:	f2ad 0850 	subw	r8, sp, #80	; 0x50\n",
+  "  1a:	f6ad 7d00 	subw	sp, sp, #3840	; 0xf00\n",
   nullptr
 };
 const char* StoreToOffsetResults[] = {
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index 4c52174..b062a2a 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -17,50 +17,89 @@
 #ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_
 #define ART_COMPILER_UTILS_DEDUPE_SET_H_
 
+#include <algorithm>
+#include <inttypes.h>
+#include <memory>
 #include <set>
 #include <string>
 
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
+#include "utils/swap_space.h"
 
 namespace art {
 
 // A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
 // Add method. The data-structure is thread-safe through the use of internal locks, it also
 // supports the lock being sharded.
-template <typename Key, typename HashType, typename HashFunc, HashType kShard = 1>
+template <typename InKey, typename StoreKey, typename HashType, typename HashFunc,
+          HashType kShard = 1>
 class DedupeSet {
-  typedef std::pair<HashType, Key*> HashedKey;
+  typedef std::pair<HashType, const InKey*> HashedInKey;
+  struct HashedKey {
+    StoreKey* store_ptr;
+    union {
+      HashType store_hash;        // Valid if store_ptr != nullptr.
+      const HashedInKey* in_key;  // Valid if store_ptr == nullptr.
+    };
+  };
 
   class Comparator {
    public:
     bool operator()(const HashedKey& a, const HashedKey& b) const {
-      if (a.first != b.first) {
-        return a.first < b.first;
+      HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first;
+      HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first;
+      if (a_hash != b_hash) {
+        return a_hash < b_hash;
+      }
+      if (a.store_ptr != nullptr && b.store_ptr != nullptr) {
+        return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+                                            b.store_ptr->begin(), b.store_ptr->end());
+      } else if (a.store_ptr != nullptr && b.store_ptr == nullptr) {
+        return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+                                            b.in_key->second->begin(), b.in_key->second->end());
+      } else if (a.store_ptr == nullptr && b.store_ptr != nullptr) {
+        return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+                                            b.store_ptr->begin(), b.store_ptr->end());
       } else {
-        return *a.second < *b.second;
+        return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+                                            b.in_key->second->begin(), b.in_key->second->end());
       }
     }
   };
 
  public:
-  Key* Add(Thread* self, const Key& key) {
+  StoreKey* Add(Thread* self, const InKey& key) {
+    uint64_t hash_start;
+    if (kIsDebugBuild) {
+      hash_start = NanoTime();
+    }
     HashType raw_hash = HashFunc()(key);
+    if (kIsDebugBuild) {
+      uint64_t hash_end = NanoTime();
+      hash_time_ += hash_end - hash_start;
+    }
     HashType shard_hash = raw_hash / kShard;
     HashType shard_bin = raw_hash % kShard;
-    HashedKey hashed_key(shard_hash, const_cast<Key*>(&key));
+    HashedInKey hashed_in_key(shard_hash, &key);
+    HashedKey hashed_key;
+    hashed_key.store_ptr = nullptr;
+    hashed_key.in_key = &hashed_in_key;
     MutexLock lock(self, *lock_[shard_bin]);
     auto it = keys_[shard_bin].find(hashed_key);
     if (it != keys_[shard_bin].end()) {
-      return it->second;
+      DCHECK(it->store_ptr != nullptr);
+      return it->store_ptr;
     }
-    hashed_key.second = new Key(key);
+    hashed_key.store_ptr = CreateStoreKey(key);
+    hashed_key.store_hash = shard_hash;
     keys_[shard_bin].insert(hashed_key);
-    return hashed_key.second;
+    return hashed_key.store_ptr;
   }
 
-  explicit DedupeSet(const char* set_name) {
+  explicit DedupeSet(const char* set_name, SwapAllocator<void>& alloc)
+      : allocator_(alloc), hash_time_(0) {
     for (HashType i = 0; i < kShard; ++i) {
       std::ostringstream oss;
       oss << set_name << " lock " << i;
@@ -70,15 +109,59 @@
   }
 
   ~DedupeSet() {
-    for (HashType i = 0; i < kShard; ++i) {
-      STLDeleteValues(&keys_[i]);
+    // Have to manually free all pointers.
+    for (auto& shard : keys_) {
+      for (const auto& hashed_key : shard) {
+        DCHECK(hashed_key.store_ptr != nullptr);
+        DeleteStoreKey(hashed_key.store_ptr);
+      }
     }
   }
 
+  std::string DumpStats() const {
+    size_t collision_sum = 0;
+    size_t collision_max = 0;
+    for (HashType shard = 0; shard < kShard; ++shard) {
+      HashType last_hash = 0;
+      size_t collision_cur_max = 0;
+      for (const HashedKey& key : keys_[shard]) {
+        DCHECK(key.store_ptr != nullptr);
+        if (key.store_hash == last_hash) {
+          collision_cur_max++;
+          if (collision_cur_max > 1) {
+            collision_sum++;
+            if (collision_cur_max > collision_max) {
+              collision_max = collision_cur_max;
+            }
+          }
+        } else {
+          collision_cur_max = 1;
+          last_hash = key.store_hash;
+        }
+      }
+    }
+    return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time",
+                        collision_sum, collision_max, hash_time_);
+  }
+
  private:
+  StoreKey* CreateStoreKey(const InKey& key) {
+    StoreKey* ret = allocator_.allocate(1);
+    allocator_.construct(ret, key.begin(), key.end(), allocator_);
+    return ret;
+  }
+
+  void DeleteStoreKey(StoreKey* key) {
+    SwapAllocator<StoreKey> alloc(allocator_);
+    alloc.destroy(key);
+    alloc.deallocate(key, 1);
+  }
+
   std::string lock_name_[kShard];
   std::unique_ptr<Mutex> lock_[kShard];
   std::set<HashedKey, Comparator> keys_[kShard];
+  SwapAllocator<StoreKey> allocator_;
+  uint64_t hash_time_;
 
   DISALLOW_COPY_AND_ASSIGN(DedupeSet);
 };
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 8abe6de..637964e 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -15,6 +15,10 @@
  */
 
 #include "dedupe_set.h"
+
+#include <algorithm>
+#include <cstdio>
+
 #include "gtest/gtest.h"
 #include "thread-inl.h"
 
@@ -35,19 +39,22 @@
 TEST(DedupeSetTest, Test) {
   Thread* self = Thread::Current();
   typedef std::vector<uint8_t> ByteArray;
-  DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator("test");
-  ByteArray* array1;
+  SwapAllocator<void> swap(nullptr);
+  DedupeSet<ByteArray, SwapVector<uint8_t>, size_t, DedupeHashFunc> deduplicator("test", swap);
+  SwapVector<uint8_t>* array1;
   {
     ByteArray test1;
     test1.push_back(10);
     test1.push_back(20);
     test1.push_back(30);
     test1.push_back(45);
+
     array1 = deduplicator.Add(self, test1);
-    ASSERT_EQ(test1, *array1);
+    ASSERT_NE(array1, nullptr);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array1->begin()));
   }
 
-  ByteArray* array2;
+  SwapVector<uint8_t>* array2;
   {
     ByteArray test1;
     test1.push_back(10);
@@ -56,10 +63,10 @@
     test1.push_back(45);
     array2 = deduplicator.Add(self, test1);
     ASSERT_EQ(array2, array1);
-    ASSERT_EQ(test1, *array2);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array2->begin()));
   }
 
-  ByteArray* array3;
+  SwapVector<uint8_t>* array3;
   {
     ByteArray test1;
     test1.push_back(10);
@@ -67,8 +74,8 @@
     test1.push_back(30);
     test1.push_back(47);
     array3 = deduplicator.Add(self, test1);
-    ASSERT_NE(array3, &test1);
-    ASSERT_EQ(test1, *array3);
+    ASSERT_NE(array3, nullptr);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array3->begin()));
   }
 }
 
diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h
index fde65e7..6af4853 100644
--- a/compiler/utils/growable_array.h
+++ b/compiler/utils/growable_array.h
@@ -37,6 +37,17 @@
                                                  kArenaAllocGrowableArray));
     }
 
+    GrowableArray(ArenaAllocator* arena, size_t init_length, T initial_data)
+      : arena_(arena),
+        num_allocated_(init_length),
+        num_used_(init_length) {
+      elem_list_ = static_cast<T*>(arena_->Alloc(sizeof(T) * init_length,
+                                                 kArenaAllocGrowableArray));
+      for (size_t i = 0; i < init_length; ++i) {
+        elem_list_[i] = initial_data;
+      }
+    }
+
 
     // Expand the list size to at least new length.
     void Resize(size_t new_length) {
diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h
index bfb2829..bb62bca 100644
--- a/compiler/utils/managed_register.h
+++ b/compiler/utils/managed_register.h
@@ -30,6 +30,9 @@
 namespace mips {
 class MipsManagedRegister;
 }
+namespace mips64 {
+class Mips64ManagedRegister;
+}
 
 namespace x86 {
 class X86ManagedRegister;
@@ -54,6 +57,7 @@
   arm::ArmManagedRegister AsArm() const;
   arm64::Arm64ManagedRegister AsArm64() const;
   mips::MipsManagedRegister AsMips() const;
+  mips64::Mips64ManagedRegister AsMips64() const;
   x86::X86ManagedRegister AsX86() const;
   x86_64::X86_64ManagedRegister AsX86_64() const;
 
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 8001dcd..b5437b0 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -332,7 +332,7 @@
 }
 
 void MipsAssembler::Jr(Register rs) {
-  EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x08);
+  EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x09);  // Jalr zero, rs
   Nop();
 }
 
@@ -420,7 +420,7 @@
 }
 
 void MipsAssembler::Move(Register rt, Register rs) {
-  EmitI(0x8, rs, rt, 0);
+  EmitI(0x9, rs, rt, 0);    // Addiu
 }
 
 void MipsAssembler::Clear(Register rt) {
@@ -447,11 +447,11 @@
 }
 
 void MipsAssembler::AddConstant(Register rt, Register rs, int32_t value) {
-  Addi(rt, rs, value);
+  Addiu(rt, rs, value);
 }
 
 void MipsAssembler::LoadImmediate(Register rt, int32_t value) {
-  Addi(rt, ZERO, value);
+  Addiu(rt, ZERO, value);
 }
 
 void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
new file mode 100644
index 0000000..233ae7d
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -0,0 +1,1036 @@
+/*
+ * 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.
+ */
+
+#include "assembler_mips64.h"
+
+#include "base/casts.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "memory_region.h"
+#include "thread.h"
+
+namespace art {
+namespace mips64 {
+
+void Mips64Assembler::Emit(int32_t value) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  buffer_.Emit<int32_t>(value);
+}
+
+void Mips64Assembler::EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegister rd,
+                            int shamt, int funct) {
+  CHECK_NE(rs, kNoGpuRegister);
+  CHECK_NE(rt, kNoGpuRegister);
+  CHECK_NE(rd, kNoGpuRegister);
+  int32_t encoding = opcode << kOpcodeShift |
+                     static_cast<int32_t>(rs) << kRsShift |
+                     static_cast<int32_t>(rt) << kRtShift |
+                     static_cast<int32_t>(rd) << kRdShift |
+                     shamt << kShamtShift |
+                     funct;
+  Emit(encoding);
+}
+
+void Mips64Assembler::EmitI(int opcode, GpuRegister rs, GpuRegister rt, uint16_t imm) {
+  CHECK_NE(rs, kNoGpuRegister);
+  CHECK_NE(rt, kNoGpuRegister);
+  int32_t encoding = opcode << kOpcodeShift |
+                     static_cast<int32_t>(rs) << kRsShift |
+                     static_cast<int32_t>(rt) << kRtShift |
+                     imm;
+  Emit(encoding);
+}
+
+void Mips64Assembler::EmitJ(int opcode, int address) {
+  int32_t encoding = opcode << kOpcodeShift |
+                     address;
+  Emit(encoding);
+}
+
+void Mips64Assembler::EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd,
+int funct) {
+  CHECK_NE(ft, kNoFpuRegister);
+  CHECK_NE(fs, kNoFpuRegister);
+  CHECK_NE(fd, kNoFpuRegister);
+  int32_t encoding = opcode << kOpcodeShift |
+                     fmt << kFmtShift |
+                     static_cast<int32_t>(ft) << kFtShift |
+                     static_cast<int32_t>(fs) << kFsShift |
+                     static_cast<int32_t>(fd) << kFdShift |
+                     funct;
+  Emit(encoding);
+}
+
+void Mips64Assembler::EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm) {
+  CHECK_NE(rt, kNoFpuRegister);
+  int32_t encoding = opcode << kOpcodeShift |
+                     fmt << kFmtShift |
+                     static_cast<int32_t>(rt) << kRtShift |
+                     imm;
+  Emit(encoding);
+}
+
+void Mips64Assembler::EmitBranch(GpuRegister rt, GpuRegister rs, Label* label, bool equal) {
+  int offset;
+  if (label->IsBound()) {
+    offset = label->Position() - buffer_.Size();
+  } else {
+    // Use the offset field of the branch instruction for linking the sites.
+    offset = label->position_;
+    label->LinkTo(buffer_.Size());
+  }
+  if (equal) {
+    Beq(rt, rs, (offset >> 2) & kBranchOffsetMask);
+  } else {
+    Bne(rt, rs, (offset >> 2) & kBranchOffsetMask);
+  }
+}
+
+void Mips64Assembler::EmitJump(Label* label, bool link) {
+  int offset;
+  if (label->IsBound()) {
+    offset = label->Position() - buffer_.Size();
+  } else {
+    // Use the offset field of the jump instruction for linking the sites.
+    offset = label->position_;
+    label->LinkTo(buffer_.Size());
+  }
+  if (link) {
+    Jal((offset >> 2) & kJumpOffsetMask);
+  } else {
+    J((offset >> 2) & kJumpOffsetMask);
+  }
+}
+
+int32_t Mips64Assembler::EncodeBranchOffset(int offset, int32_t inst, bool is_jump) {
+  CHECK_ALIGNED(offset, 4);
+  CHECK(IsInt(POPCOUNT(kBranchOffsetMask), offset)) << offset;
+
+  // Properly preserve only the bits supported in the instruction.
+  offset >>= 2;
+  if (is_jump) {
+    offset &= kJumpOffsetMask;
+    return (inst & ~kJumpOffsetMask) | offset;
+  } else {
+    offset &= kBranchOffsetMask;
+    return (inst & ~kBranchOffsetMask) | offset;
+  }
+}
+
+int Mips64Assembler::DecodeBranchOffset(int32_t inst, bool is_jump) {
+  // Sign-extend, then left-shift by 2.
+  if (is_jump) {
+    return (((inst & kJumpOffsetMask) << 6) >> 4);
+  } else {
+    return (((inst & kBranchOffsetMask) << 16) >> 14);
+  }
+}
+
+void Mips64Assembler::Bind(Label* label, bool is_jump) {
+  CHECK(!label->IsBound());
+  int bound_pc = buffer_.Size();
+  while (label->IsLinked()) {
+    int32_t position = label->Position();
+    int32_t next = buffer_.Load<int32_t>(position);
+    int32_t offset = is_jump ? bound_pc - position : bound_pc - position - 4;
+    int32_t encoded = Mips64Assembler::EncodeBranchOffset(offset, next, is_jump);
+    buffer_.Store<int32_t>(position, encoded);
+    label->position_ = Mips64Assembler::DecodeBranchOffset(next, is_jump);
+  }
+  label->BindTo(bound_pc);
+}
+
+void Mips64Assembler::Add(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x20);
+}
+
+void Mips64Assembler::Addi(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x8, rs, rt, imm16);
+}
+
+void Mips64Assembler::Addu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x21);
+}
+
+void Mips64Assembler::Addiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x9, rs, rt, imm16);
+}
+
+void Mips64Assembler::Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x19, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sub(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x22);
+}
+
+void Mips64Assembler::Subu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x23);
+}
+
+void Mips64Assembler::Mult(GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x18);
+}
+
+void Mips64Assembler::Multu(GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x19);
+}
+
+void Mips64Assembler::Div(GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x1a);
+}
+
+void Mips64Assembler::Divu(GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, static_cast<GpuRegister>(0), 0, 0x1b);
+}
+
+void Mips64Assembler::And(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x24);
+}
+
+void Mips64Assembler::Andi(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0xc, rs, rt, imm16);
+}
+
+void Mips64Assembler::Or(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x25);
+}
+
+void Mips64Assembler::Ori(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0xd, rs, rt, imm16);
+}
+
+void Mips64Assembler::Xor(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x26);
+}
+
+void Mips64Assembler::Xori(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0xe, rs, rt, imm16);
+}
+
+void Mips64Assembler::Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x27);
+}
+
+void Mips64Assembler::Sll(GpuRegister rd, GpuRegister rs, int shamt) {
+  EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x00);
+}
+
+void Mips64Assembler::Srl(GpuRegister rd, GpuRegister rs, int shamt) {
+  EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x02);
+}
+
+void Mips64Assembler::Sra(GpuRegister rd, GpuRegister rs, int shamt) {
+  EmitR(0, rs, static_cast<GpuRegister>(0), rd, shamt, 0x03);
+}
+
+void Mips64Assembler::Sllv(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x04);
+}
+
+void Mips64Assembler::Srlv(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x06);
+}
+
+void Mips64Assembler::Srav(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x07);
+}
+
+void Mips64Assembler::Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x20, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lh(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x21, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lw(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x23, rs, rt, imm16);
+}
+
+void Mips64Assembler::Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x37, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x24, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x25, rs, rt, imm16);
+}
+
+void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) {
+  EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16);
+}
+
+void Mips64Assembler::Mfhi(GpuRegister rd) {
+  EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rd, 0, 0x10);
+}
+
+void Mips64Assembler::Mflo(GpuRegister rd) {
+  EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rd, 0, 0x12);
+}
+
+void Mips64Assembler::Sb(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x28, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sh(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x29, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sw(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x2b, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sd(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x3f, rs, rt, imm16);
+}
+
+void Mips64Assembler::Slt(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x2a);
+}
+
+void Mips64Assembler::Sltu(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  EmitR(0, rs, rt, rd, 0, 0x2b);
+}
+
+void Mips64Assembler::Slti(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0xa, rs, rt, imm16);
+}
+
+void Mips64Assembler::Sltiu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0xb, rs, rt, imm16);
+}
+
+void Mips64Assembler::Beq(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x4, rs, rt, imm16);
+  Nop();
+}
+
+void Mips64Assembler::Bne(GpuRegister rt, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x5, rs, rt, imm16);
+  Nop();
+}
+
+void Mips64Assembler::J(uint32_t address) {
+  EmitJ(0x2, address);
+  Nop();
+}
+
+void Mips64Assembler::Jal(uint32_t address) {
+  EmitJ(0x2, address);
+  Nop();
+}
+
+void Mips64Assembler::Jr(GpuRegister rs) {
+  EmitR(0, rs, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), 0, 0x09);  // Jalr zero, rs
+  Nop();
+}
+
+void Mips64Assembler::Jalr(GpuRegister rs) {
+  EmitR(0, rs, static_cast<GpuRegister>(0), RA, 0, 0x09);
+  Nop();
+}
+
+void Mips64Assembler::AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x10, ft, fs, fd, 0x0);
+}
+
+void Mips64Assembler::SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x10, ft, fs, fd, 0x1);
+}
+
+void Mips64Assembler::MulS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x10, ft, fs, fd, 0x2);
+}
+
+void Mips64Assembler::DivS(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x10, ft, fs, fd, 0x3);
+}
+
+void Mips64Assembler::AddD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+         static_cast<FpuRegister>(fd), 0x0);
+}
+
+void Mips64Assembler::SubD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+         static_cast<FpuRegister>(fd), 0x1);
+}
+
+void Mips64Assembler::MulD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+         static_cast<FpuRegister>(fd), 0x2);
+}
+
+void Mips64Assembler::DivD(FpuRegister fd, FpuRegister fs, FpuRegister ft) {
+  EmitFR(0x11, 0x11, static_cast<FpuRegister>(ft), static_cast<FpuRegister>(fs),
+         static_cast<FpuRegister>(fd), 0x3);
+}
+
+void Mips64Assembler::MovS(FpuRegister fd, FpuRegister fs) {
+  EmitFR(0x11, 0x10, static_cast<FpuRegister>(0), fs, fd, 0x6);
+}
+
+void Mips64Assembler::MovD(FpuRegister fd, FpuRegister fs) {
+  EmitFR(0x11, 0x11, static_cast<FpuRegister>(0), static_cast<FpuRegister>(fs),
+         static_cast<FpuRegister>(fd), 0x6);
+}
+
+void Mips64Assembler::Mfc1(GpuRegister rt, FpuRegister fs) {
+  EmitFR(0x11, 0x00, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
+}
+
+void Mips64Assembler::Mtc1(FpuRegister ft, GpuRegister rs) {
+  EmitFR(0x11, 0x04, ft, static_cast<FpuRegister>(rs), static_cast<FpuRegister>(0), 0x0);
+}
+
+void Mips64Assembler::Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x31, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Ldc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x35, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Swc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x39, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Sdc1(FpuRegister ft, GpuRegister rs, uint16_t imm16) {
+  EmitI(0x3d, rs, static_cast<GpuRegister>(ft), imm16);
+}
+
+void Mips64Assembler::Break() {
+  EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0),
+        static_cast<GpuRegister>(0), 0, 0xD);
+}
+
+void Mips64Assembler::Nop() {
+  EmitR(0x0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0),
+        static_cast<GpuRegister>(0), 0, 0x0);
+}
+
+void Mips64Assembler::Move(GpuRegister rt, GpuRegister rs) {
+  EmitI(0x19, rs, rt, 0);   // Daddiu
+}
+
+void Mips64Assembler::Clear(GpuRegister rt) {
+  EmitR(0, static_cast<GpuRegister>(0), static_cast<GpuRegister>(0), rt, 0, 0x20);
+}
+
+void Mips64Assembler::Not(GpuRegister rt, GpuRegister rs) {
+  EmitR(0, static_cast<GpuRegister>(0), rs, rt, 0, 0x27);
+}
+
+void Mips64Assembler::Mul(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  Mult(rs, rt);
+  Mflo(rd);
+}
+
+void Mips64Assembler::Div(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  Div(rs, rt);
+  Mflo(rd);
+}
+
+void Mips64Assembler::Rem(GpuRegister rd, GpuRegister rs, GpuRegister rt) {
+  Div(rs, rt);
+  Mfhi(rd);
+}
+
+void Mips64Assembler::AddConstant64(GpuRegister rt, GpuRegister rs, int32_t value) {
+  CHECK((value >= -32768) && (value <= 32766));
+  Daddiu(rt, rs, value);
+}
+
+void Mips64Assembler::LoadImmediate64(GpuRegister rt, int32_t value) {
+  CHECK((value >= -32768) && (value <= 32766));
+  Daddiu(rt, ZERO, value);
+}
+
+void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base,
+                                     int32_t offset) {
+  switch (type) {
+    case kLoadSignedByte:
+      Lb(reg, base, offset);
+      break;
+    case kLoadUnsignedByte:
+      Lbu(reg, base, offset);
+      break;
+    case kLoadSignedHalfword:
+      Lh(reg, base, offset);
+      break;
+    case kLoadUnsignedHalfword:
+      Lhu(reg, base, offset);
+      break;
+    case kLoadWord:
+      Lw(reg, base, offset);
+      break;
+    case kLoadDoubleword:
+      // TODO: alignment issues ???
+      Ld(reg, base, offset);
+      break;
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+  }
+}
+
+void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base,
+                                        int32_t offset) {
+  CHECK((offset >= -32768) && (offset <= 32766));
+  switch (type) {
+    case kLoadWord:
+      Lwc1(reg, base, offset);
+      break;
+    case kLoadDoubleword:
+      // TODO: alignment issues ???
+      Ldc1(reg, base, offset);
+      break;
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+  }
+}
+
+void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset,
+                               size_t size) {
+  Mips64ManagedRegister dst = m_dst.AsMips64();
+  if (dst.IsNoRegister()) {
+    CHECK_EQ(0u, size) << dst;
+  } else if (dst.IsGpuRegister()) {
+    if (size == 4) {
+      CHECK_EQ(4u, size) << dst;
+      LoadFromOffset(kLoadWord, dst.AsGpuRegister(), src_register, src_offset);
+    } else if (size == 8) {
+      CHECK_EQ(8u, size) << dst;
+      LoadFromOffset(kLoadDoubleword, dst.AsGpuRegister(), src_register, src_offset);
+    } else {
+      UNIMPLEMENTED(FATAL) << "We only support Load() of size 4 and 8";
+    }
+  } else if (dst.IsFpuRegister()) {
+    if (size == 4) {
+      CHECK_EQ(4u, size) << dst;
+      LoadFpuFromOffset(kLoadWord, dst.AsFpuRegister(), src_register, src_offset);
+    } else if (size == 8) {
+      CHECK_EQ(8u, size) << dst;
+      LoadFpuFromOffset(kLoadDoubleword, dst.AsFpuRegister(), src_register, src_offset);
+    } else {
+      UNIMPLEMENTED(FATAL) << "We only support Load() of size 4 and 8";
+    }
+  }
+}
+
+void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base,
+                                    int32_t offset) {
+  switch (type) {
+    case kStoreByte:
+      Sb(reg, base, offset);
+      break;
+    case kStoreHalfword:
+      Sh(reg, base, offset);
+      break;
+    case kStoreWord:
+      Sw(reg, base, offset);
+      break;
+    case kStoreDoubleword:
+      // TODO: alignment issues ???
+      Sd(reg, base, offset);
+      break;
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+  }
+}
+
+void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base,
+                                       int32_t offset) {
+  switch (type) {
+    case kStoreWord:
+      Swc1(reg, base, offset);
+      break;
+    case kStoreDoubleword:
+      Sdc1(reg, base, offset);
+      break;
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+  }
+}
+
+constexpr size_t kFramePointerSize = 8;
+
+void Mips64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
+                                 const std::vector<ManagedRegister>& callee_save_regs,
+                                 const ManagedRegisterEntrySpills& entry_spills) {
+  CHECK_ALIGNED(frame_size, kStackAlignment);
+
+  // Increase frame to required size.
+  IncreaseFrameSize(frame_size);
+
+  // Push callee saves and return address
+  int stack_offset = frame_size - kFramePointerSize;
+  StoreToOffset(kStoreDoubleword, RA, SP, stack_offset);
+  for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
+    stack_offset -= kFramePointerSize;
+    GpuRegister reg = callee_save_regs.at(i).AsMips64().AsGpuRegister();
+    StoreToOffset(kStoreDoubleword, reg, SP, stack_offset);
+  }
+
+  // Write out Method*.
+  StoreToOffset(kStoreWord, method_reg.AsMips64().AsGpuRegister(), SP, 0);
+
+  // Write out entry spills.
+  int32_t offset = frame_size + sizeof(StackReference<mirror::ArtMethod>);
+  for (size_t i = 0; i < entry_spills.size(); ++i) {
+    Mips64ManagedRegister reg = entry_spills.at(i).AsMips64();
+    ManagedRegisterSpill spill = entry_spills.at(i);
+    int32_t size = spill.getSize();
+    if (reg.IsNoRegister()) {
+      // only increment stack offset.
+      offset += size;
+    } else if (reg.IsFpuRegister()) {
+      StoreFpuToOffset((size == 4) ? kStoreWord : kStoreDoubleword, reg.AsFpuRegister(), SP, offset);
+      offset += size;
+    } else if (reg.IsGpuRegister()) {
+      StoreToOffset((size == 4) ? kStoreWord : kStoreDoubleword, reg.AsGpuRegister(), SP, offset);
+      offset += size;
+    }
+  }
+}
+
+void Mips64Assembler::RemoveFrame(size_t frame_size,
+                                  const std::vector<ManagedRegister>& callee_save_regs) {
+  CHECK_ALIGNED(frame_size, kStackAlignment);
+
+  // Pop callee saves and return address
+  int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
+  for (size_t i = 0; i < callee_save_regs.size(); ++i) {
+    GpuRegister reg = callee_save_regs.at(i).AsMips64().AsGpuRegister();
+    LoadFromOffset(kLoadDoubleword, reg, SP, stack_offset);
+    stack_offset += kFramePointerSize;
+  }
+  LoadFromOffset(kLoadDoubleword, RA, SP, stack_offset);
+
+  // Decrease frame to required size.
+  DecreaseFrameSize(frame_size);
+
+  // Then jump to the return address.
+  Jr(RA);
+}
+
+void Mips64Assembler::IncreaseFrameSize(size_t adjust) {
+  CHECK_ALIGNED(adjust, kStackAlignment);
+  AddConstant64(SP, SP, -adjust);
+}
+
+void Mips64Assembler::DecreaseFrameSize(size_t adjust) {
+  CHECK_ALIGNED(adjust, kStackAlignment);
+  AddConstant64(SP, SP, adjust);
+}
+
+void Mips64Assembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
+  Mips64ManagedRegister src = msrc.AsMips64();
+  if (src.IsNoRegister()) {
+    CHECK_EQ(0u, size);
+  } else if (src.IsGpuRegister()) {
+    CHECK(size == 4 || size == 8) << size;
+    if (size == 8) {
+      StoreToOffset(kStoreDoubleword, src.AsGpuRegister(), SP, dest.Int32Value());
+    } else if (size == 4) {
+      StoreToOffset(kStoreWord, src.AsGpuRegister(), SP, dest.Int32Value());
+    } else {
+      UNIMPLEMENTED(FATAL) << "We only support Store() of size 4 and 8";
+    }
+  } else if (src.IsFpuRegister()) {
+    CHECK(size == 4 || size == 8) << size;
+    if (size == 8) {
+      StoreFpuToOffset(kStoreDoubleword, src.AsFpuRegister(), SP, dest.Int32Value());
+    } else if (size == 4) {
+      StoreFpuToOffset(kStoreWord, src.AsFpuRegister(), SP, dest.Int32Value());
+    } else {
+      UNIMPLEMENTED(FATAL) << "We only support Store() of size 4 and 8";
+    }
+  }
+}
+
+void Mips64Assembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
+  Mips64ManagedRegister src = msrc.AsMips64();
+  CHECK(src.IsGpuRegister());
+  StoreToOffset(kStoreWord, src.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
+  Mips64ManagedRegister src = msrc.AsMips64();
+  CHECK(src.IsGpuRegister());
+  StoreToOffset(kStoreDoubleword, src.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
+                                            ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  LoadImmediate64(scratch.AsGpuRegister(), imm);
+  StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t imm,
+                                               ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  LoadImmediate64(scratch.AsGpuRegister(), imm);
+  StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, dest.Int32Value());
+}
+
+void Mips64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> thr_offs,
+                                                 FrameOffset fr_offs,
+                                                 ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  AddConstant64(scratch.AsGpuRegister(), SP, fr_offs.Int32Value());
+  StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, thr_offs.Int32Value());
+}
+
+void Mips64Assembler::StoreStackPointerToThread64(ThreadOffset<8> thr_offs) {
+  StoreToOffset(kStoreDoubleword, SP, S1, thr_offs.Int32Value());
+}
+
+void Mips64Assembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
+                                    FrameOffset in_off, ManagedRegister mscratch) {
+  Mips64ManagedRegister src = msrc.AsMips64();
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  StoreToOffset(kStoreDoubleword, src.AsGpuRegister(), SP, dest.Int32Value());
+  LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), SP, in_off.Int32Value());
+  StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value() + 8);
+}
+
+void Mips64Assembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
+  return EmitLoad(mdest, SP, src.Int32Value(), size);
+}
+
+void Mips64Assembler::LoadFromThread64(ManagedRegister mdest, ThreadOffset<8> src, size_t size) {
+  return EmitLoad(mdest, S1, src.Int32Value(), size);
+}
+
+void Mips64Assembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
+  Mips64ManagedRegister dest = mdest.AsMips64();
+  CHECK(dest.IsGpuRegister());
+  LoadFromOffset(kLoadWord, dest.AsGpuRegister(), SP, src.Int32Value());
+}
+
+void Mips64Assembler::LoadRef(ManagedRegister mdest, ManagedRegister base,
+                            MemberOffset offs) {
+  Mips64ManagedRegister dest = mdest.AsMips64();
+  CHECK(dest.IsGpuRegister() && dest.IsGpuRegister());
+  LoadFromOffset(kLoadWord, dest.AsGpuRegister(),
+                 base.AsMips64().AsGpuRegister(), offs.Int32Value());
+  if (kPoisonHeapReferences) {
+    Subu(dest.AsGpuRegister(), ZERO, dest.AsGpuRegister());
+  }
+}
+
+void Mips64Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
+                               Offset offs) {
+  Mips64ManagedRegister dest = mdest.AsMips64();
+  CHECK(dest.IsGpuRegister() && dest.IsGpuRegister()) << dest;
+  LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(),
+                 base.AsMips64().AsGpuRegister(), offs.Int32Value());
+}
+
+void Mips64Assembler::LoadRawPtrFromThread64(ManagedRegister mdest,
+                                         ThreadOffset<8> offs) {
+  Mips64ManagedRegister dest = mdest.AsMips64();
+  CHECK(dest.IsGpuRegister());
+  LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(), S1, offs.Int32Value());
+}
+
+void Mips64Assembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
+  UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
+}
+
+void Mips64Assembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
+  UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
+}
+
+void Mips64Assembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
+  Mips64ManagedRegister dest = mdest.AsMips64();
+  Mips64ManagedRegister src = msrc.AsMips64();
+  if (!dest.Equals(src)) {
+    if (dest.IsGpuRegister()) {
+      CHECK(src.IsGpuRegister()) << src;
+      Move(dest.AsGpuRegister(), src.AsGpuRegister());
+    } else if (dest.IsFpuRegister()) {
+      CHECK(src.IsFpuRegister()) << src;
+      if (size == 4) {
+        MovS(dest.AsFpuRegister(), src.AsFpuRegister());
+      } else if (size == 8) {
+        MovD(dest.AsFpuRegister(), src.AsFpuRegister());
+      } else {
+        UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+      }
+    }
+  }
+}
+
+void Mips64Assembler::CopyRef(FrameOffset dest, FrameOffset src,
+                              ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, src.Int32Value());
+  StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+}
+
+void Mips64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs,
+                                             ThreadOffset<8> thr_offs,
+                                             ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), S1, thr_offs.Int32Value());
+  StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, fr_offs.Int32Value());
+}
+
+void Mips64Assembler::CopyRawPtrToThread64(ThreadOffset<8> thr_offs,
+                                           FrameOffset fr_offs,
+                                           ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+                 SP, fr_offs.Int32Value());
+  StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(),
+                S1, thr_offs.Int32Value());
+}
+
+void Mips64Assembler::Copy(FrameOffset dest, FrameOffset src,
+                           ManagedRegister mscratch, size_t size) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  CHECK(size == 4 || size == 8) << size;
+  if (size == 4) {
+    LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, src.Int32Value());
+    StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+  } else if (size == 8) {
+    LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), SP, src.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value());
+  } else {
+    UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+  }
+}
+
+void Mips64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
+                         ManagedRegister mscratch, size_t size) {
+  GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();
+  CHECK(size == 4 || size == 8) << size;
+  if (size == 4) {
+    LoadFromOffset(kLoadWord, scratch, src_base.AsMips64().AsGpuRegister(),
+                   src_offset.Int32Value());
+    StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
+  } else if (size == 8) {
+    LoadFromOffset(kLoadDoubleword, scratch, src_base.AsMips64().AsGpuRegister(),
+                   src_offset.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch, SP, dest.Int32Value());
+  } else {
+    UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+  }
+}
+
+void Mips64Assembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
+                         ManagedRegister mscratch, size_t size) {
+  GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();
+  CHECK(size == 4 || size == 8) << size;
+  if (size == 4) {
+    LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
+    StoreToOffset(kStoreWord, scratch, dest_base.AsMips64().AsGpuRegister(),
+                  dest_offset.Int32Value());
+  } else if (size == 8) {
+    LoadFromOffset(kLoadDoubleword, scratch, SP, src.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch, dest_base.AsMips64().AsGpuRegister(),
+                  dest_offset.Int32Value());
+  } else {
+    UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+  }
+}
+
+void Mips64Assembler::Copy(FrameOffset /*dest*/, FrameOffset /*src_base*/, Offset /*src_offset*/,
+                         ManagedRegister /*mscratch*/, size_t /*size*/) {
+  UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::Copy(ManagedRegister dest, Offset dest_offset,
+                         ManagedRegister src, Offset src_offset,
+                         ManagedRegister mscratch, size_t size) {
+  GpuRegister scratch = mscratch.AsMips64().AsGpuRegister();
+  CHECK(size == 4 || size == 8) << size;
+  if (size == 4) {
+    LoadFromOffset(kLoadWord, scratch, src.AsMips64().AsGpuRegister(), src_offset.Int32Value());
+    StoreToOffset(kStoreWord, scratch, dest.AsMips64().AsGpuRegister(), dest_offset.Int32Value());
+  } else if (size == 8) {
+    LoadFromOffset(kLoadDoubleword, scratch, src.AsMips64().AsGpuRegister(),
+                   src_offset.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch, dest.AsMips64().AsGpuRegister(),
+                  dest_offset.Int32Value());
+  } else {
+    UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
+  }
+}
+
+void Mips64Assembler::Copy(FrameOffset /*dest*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset
+/*src_offset*/,
+                         ManagedRegister /*mscratch*/, size_t /*size*/) {
+  UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::MemoryBarrier(ManagedRegister) {
+  UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
+                                    FrameOffset handle_scope_offset,
+                                    ManagedRegister min_reg, bool null_allowed) {
+  Mips64ManagedRegister out_reg = mout_reg.AsMips64();
+  Mips64ManagedRegister in_reg = min_reg.AsMips64();
+  CHECK(in_reg.IsNoRegister() || in_reg.IsGpuRegister()) << in_reg;
+  CHECK(out_reg.IsGpuRegister()) << out_reg;
+  if (null_allowed) {
+    Label null_arg;
+    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
+    // the address in the handle scope holding the reference.
+    // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
+    if (in_reg.IsNoRegister()) {
+      LoadFromOffset(kLoadWord, out_reg.AsGpuRegister(),
+                     SP, handle_scope_offset.Int32Value());
+      in_reg = out_reg;
+    }
+    if (!out_reg.Equals(in_reg)) {
+      LoadImmediate64(out_reg.AsGpuRegister(), 0);
+    }
+    EmitBranch(in_reg.AsGpuRegister(), ZERO, &null_arg, true);
+    AddConstant64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+    Bind(&null_arg, false);
+  } else {
+    AddConstant64(out_reg.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+  }
+}
+
+void Mips64Assembler::CreateHandleScopeEntry(FrameOffset out_off,
+                                    FrameOffset handle_scope_offset,
+                                    ManagedRegister mscratch,
+                                    bool null_allowed) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  if (null_allowed) {
+    Label null_arg;
+    LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP,
+                   handle_scope_offset.Int32Value());
+    // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
+    // the address in the handle scope holding the reference.
+    // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
+    EmitBranch(scratch.AsGpuRegister(), ZERO, &null_arg, true);
+    AddConstant64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+    Bind(&null_arg, false);
+  } else {
+    AddConstant64(scratch.AsGpuRegister(), SP, handle_scope_offset.Int32Value());
+  }
+  StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, out_off.Int32Value());
+}
+
+// Given a handle scope entry, load the associated reference.
+void Mips64Assembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
+                                          ManagedRegister min_reg) {
+  Mips64ManagedRegister out_reg = mout_reg.AsMips64();
+  Mips64ManagedRegister in_reg = min_reg.AsMips64();
+  CHECK(out_reg.IsGpuRegister()) << out_reg;
+  CHECK(in_reg.IsGpuRegister()) << in_reg;
+  Label null_arg;
+  if (!out_reg.Equals(in_reg)) {
+    LoadImmediate64(out_reg.AsGpuRegister(), 0);
+  }
+  EmitBranch(in_reg.AsGpuRegister(), ZERO, &null_arg, true);
+  LoadFromOffset(kLoadDoubleword, out_reg.AsGpuRegister(),
+                 in_reg.AsGpuRegister(), 0);
+  Bind(&null_arg, false);
+}
+
+void Mips64Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
+  // TODO: not validating references
+}
+
+void Mips64Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
+  // TODO: not validating references
+}
+
+void Mips64Assembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
+  Mips64ManagedRegister base = mbase.AsMips64();
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(base.IsGpuRegister()) << base;
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+                 base.AsGpuRegister(), offset.Int32Value());
+  Jalr(scratch.AsGpuRegister());
+  // TODO: place reference map on call
+}
+
+void Mips64Assembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  CHECK(scratch.IsGpuRegister()) << scratch;
+  // Call *(*(SP + base) + offset)
+  LoadFromOffset(kLoadWord, scratch.AsGpuRegister(),
+                 SP, base.Int32Value());
+  LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+                 scratch.AsGpuRegister(), offset.Int32Value());
+  Jalr(scratch.AsGpuRegister());
+  // TODO: place reference map on call
+}
+
+void Mips64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*mscratch*/) {
+  UNIMPLEMENTED(FATAL) << "no mips64 implementation";
+}
+
+void Mips64Assembler::GetCurrentThread(ManagedRegister tr) {
+  Move(tr.AsMips64().AsGpuRegister(), S1);
+}
+
+void Mips64Assembler::GetCurrentThread(FrameOffset offset,
+                                     ManagedRegister /*mscratch*/) {
+  StoreToOffset(kStoreDoubleword, S1, SP, offset.Int32Value());
+}
+
+void Mips64Assembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
+  Mips64ManagedRegister scratch = mscratch.AsMips64();
+  Mips64ExceptionSlowPath* slow = new Mips64ExceptionSlowPath(scratch, stack_adjust);
+  buffer_.EnqueueSlowPath(slow);
+  LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),
+                 S1, Thread::ExceptionOffset<8>().Int32Value());
+  EmitBranch(scratch.AsGpuRegister(), ZERO, slow->Entry(), false);
+}
+
+void Mips64ExceptionSlowPath::Emit(Assembler* sasm) {
+  Mips64Assembler* sp_asm = down_cast<Mips64Assembler*>(sasm);
+#define __ sp_asm->
+  __ Bind(&entry_, false);
+  if (stack_adjust_ != 0) {  // Fix up the frame.
+    __ DecreaseFrameSize(stack_adjust_);
+  }
+  // Pass exception object as argument
+  // Don't care about preserving A0 as this call won't return
+  __ Move(A0, scratch_.AsGpuRegister());
+  // Set up call to Thread::Current()->pDeliverException
+  __ LoadFromOffset(kLoadDoubleword, T9, S1,
+                    QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value());
+  __ Jr(T9);
+  // Call never returns
+  __ Break();
+#undef __
+}
+
+}  // namespace mips64
+}  // namespace art
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
new file mode 100644
index 0000000..36e74d7
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
+#define ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "constants_mips64.h"
+#include "globals.h"
+#include "managed_register_mips64.h"
+#include "utils/assembler.h"
+#include "offsets.h"
+#include "utils.h"
+
+namespace art {
+namespace mips64 {
+
+enum LoadOperandType {
+  kLoadSignedByte,
+  kLoadUnsignedByte,
+  kLoadSignedHalfword,
+  kLoadUnsignedHalfword,
+  kLoadWord,
+  kLoadDoubleword
+};
+
+enum StoreOperandType {
+  kStoreByte,
+  kStoreHalfword,
+  kStoreWord,
+  kStoreDoubleword
+};
+
+class Mips64Assembler FINAL : public Assembler {
+ public:
+  Mips64Assembler() {}
+  virtual ~Mips64Assembler() {}
+
+  // Emit Machine Instructions.
+  void Add(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Addi(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Addu(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Addiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Daddiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Sub(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Subu(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Mult(GpuRegister rs, GpuRegister rt);
+  void Multu(GpuRegister rs, GpuRegister rt);
+  void Div(GpuRegister rs, GpuRegister rt);
+  void Divu(GpuRegister rs, GpuRegister rt);
+
+  void And(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Andi(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Or(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Ori(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Xor(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Xori(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+
+  void Sll(GpuRegister rd, GpuRegister rs, int shamt);
+  void Srl(GpuRegister rd, GpuRegister rs, int shamt);
+  void Sra(GpuRegister rd, GpuRegister rs, int shamt);
+  void Sllv(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Srlv(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Srav(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+
+  void Lb(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Lh(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Lw(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Lui(GpuRegister rt, uint16_t imm16);
+  void Mfhi(GpuRegister rd);
+  void Mflo(GpuRegister rd);
+
+  void Sb(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Sh(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Sw(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Sd(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+
+  void Slt(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Sltu(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Slti(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Sltiu(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+
+  void Beq(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void Bne(GpuRegister rt, GpuRegister rs, uint16_t imm16);
+  void J(uint32_t address);
+  void Jal(uint32_t address);
+  void Jr(GpuRegister rs);
+  void Jalr(GpuRegister rs);
+
+  void AddS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void SubS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void MulS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void DivS(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void AddD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void SubD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void MulD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void DivD(FpuRegister fd, FpuRegister fs, FpuRegister ft);
+  void MovS(FpuRegister fd, FpuRegister fs);
+  void MovD(FpuRegister fd, FpuRegister fs);
+
+  void Mfc1(GpuRegister rt, FpuRegister fs);
+  void Mtc1(FpuRegister ft, GpuRegister rs);
+  void Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+  void Ldc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+  void Swc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+  void Sdc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
+
+  void Break();
+  void Nop();
+  void Move(GpuRegister rt, GpuRegister rs);
+  void Clear(GpuRegister rt);
+  void Not(GpuRegister rt, GpuRegister rs);
+  void Mul(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Div(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+  void Rem(GpuRegister rd, GpuRegister rs, GpuRegister rt);
+
+  void AddConstant64(GpuRegister rt, GpuRegister rs, int32_t value);
+  void LoadImmediate64(GpuRegister rt, int32_t value);
+
+  void EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, size_t size);
+  void LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base, int32_t offset);
+  void LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base, int32_t offset);
+  void StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base, int32_t offset);
+  void StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base, int32_t offset);
+
+  // Emit data (e.g. encoded instruction or immediate) to the instruction stream.
+  void Emit(int32_t value);
+  void EmitBranch(GpuRegister rt, GpuRegister rs, Label* label, bool equal);
+  void EmitJump(Label* label, bool link);
+  void Bind(Label* label, bool is_jump);
+
+  //
+  // Overridden common assembler high-level functionality
+  //
+
+  // Emit code that will create an activation on the stack
+  void BuildFrame(size_t frame_size, ManagedRegister method_reg,
+                  const std::vector<ManagedRegister>& callee_save_regs,
+                  const ManagedRegisterEntrySpills& entry_spills) OVERRIDE;
+
+  // Emit code that will remove an activation from the stack
+  void RemoveFrame(size_t frame_size,
+                   const std::vector<ManagedRegister>& callee_save_regs) OVERRIDE;
+
+  void IncreaseFrameSize(size_t adjust) OVERRIDE;
+  void DecreaseFrameSize(size_t adjust) OVERRIDE;
+
+  // Store routines
+  void Store(FrameOffset offs, ManagedRegister msrc, size_t size) OVERRIDE;
+  void StoreRef(FrameOffset dest, ManagedRegister msrc) OVERRIDE;
+  void StoreRawPtr(FrameOffset dest, ManagedRegister msrc) OVERRIDE;
+
+  void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister mscratch) OVERRIDE;
+
+  void StoreImmediateToThread64(ThreadOffset<8> dest, uint32_t imm,
+                                ManagedRegister mscratch) OVERRIDE;
+
+  void StoreStackOffsetToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs,
+                                  ManagedRegister mscratch) OVERRIDE;
+
+  void StoreStackPointerToThread64(ThreadOffset<8> thr_offs) OVERRIDE;
+
+  void StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off,
+                     ManagedRegister mscratch) OVERRIDE;
+
+  // Load routines
+  void Load(ManagedRegister mdest, FrameOffset src, size_t size) OVERRIDE;
+
+  void LoadFromThread64(ManagedRegister mdest, ThreadOffset<8> src, size_t size) OVERRIDE;
+
+  void LoadRef(ManagedRegister dest, FrameOffset  src) OVERRIDE;
+
+  void LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs) OVERRIDE;
+
+  void LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) OVERRIDE;
+
+  void LoadRawPtrFromThread64(ManagedRegister mdest, ThreadOffset<8> offs) OVERRIDE;
+
+  // Copying routines
+  void Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) OVERRIDE;
+
+  void CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<8> thr_offs,
+                              ManagedRegister mscratch) OVERRIDE;
+
+  void CopyRawPtrToThread64(ThreadOffset<8> thr_offs, FrameOffset fr_offs,
+                            ManagedRegister mscratch) OVERRIDE;
+
+  void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) OVERRIDE;
+
+  void Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) OVERRIDE;
+
+  void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, ManagedRegister mscratch,
+            size_t size) OVERRIDE;
+
+  void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
+            ManagedRegister mscratch, size_t size) OVERRIDE;
+
+  void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, ManagedRegister mscratch,
+            size_t size) OVERRIDE;
+
+  void Copy(ManagedRegister dest, Offset dest_offset, ManagedRegister src, Offset src_offset,
+            ManagedRegister mscratch, size_t size) OVERRIDE;
+
+  void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset,
+            ManagedRegister mscratch, size_t size) OVERRIDE;
+
+  void MemoryBarrier(ManagedRegister) OVERRIDE;
+
+  // Sign extension
+  void SignExtend(ManagedRegister mreg, size_t size) OVERRIDE;
+
+  // Zero extension
+  void ZeroExtend(ManagedRegister mreg, size_t size) OVERRIDE;
+
+  // Exploit fast access in managed code to Thread::Current()
+  void GetCurrentThread(ManagedRegister tr) OVERRIDE;
+  void GetCurrentThread(FrameOffset dest_offset, ManagedRegister mscratch) OVERRIDE;
+
+  // Set up out_reg to hold a Object** into the handle scope, or to be NULL if the
+  // value is null and null_allowed. in_reg holds a possibly stale reference
+  // that can be used to avoid loading the handle scope entry to see if the value is
+  // NULL.
+  void CreateHandleScopeEntry(ManagedRegister out_reg, FrameOffset handlescope_offset,
+                              ManagedRegister in_reg, bool null_allowed) OVERRIDE;
+
+  // Set up out_off to hold a Object** into the handle scope, or to be NULL if the
+  // value is null and null_allowed.
+  void CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handlescope_offset, ManagedRegister
+                              mscratch, bool null_allowed) OVERRIDE;
+
+  // src holds a handle scope entry (Object**) load this into dst
+  void LoadReferenceFromHandleScope(ManagedRegister dst, ManagedRegister src) OVERRIDE;
+
+  // Heap::VerifyObject on src. In some cases (such as a reference to this) we
+  // know that src may not be null.
+  void VerifyObject(ManagedRegister src, bool could_be_null) OVERRIDE;
+  void VerifyObject(FrameOffset src, bool could_be_null) OVERRIDE;
+
+  // Call to address held at [base+offset]
+  void Call(ManagedRegister base, Offset offset, ManagedRegister mscratch) OVERRIDE;
+  void Call(FrameOffset base, Offset offset, ManagedRegister mscratch) OVERRIDE;
+  void CallFromThread64(ThreadOffset<8> offset, ManagedRegister mscratch) OVERRIDE;
+
+  // Generate code to check if Thread::Current()->exception_ is non-null
+  // and branch to a ExceptionSlowPath if it is.
+  void ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) OVERRIDE;
+
+ private:
+  void EmitR(int opcode, GpuRegister rs, GpuRegister rt, GpuRegister rd, int shamt, int funct);
+  void EmitI(int opcode, GpuRegister rs, GpuRegister rt, uint16_t imm);
+  void EmitJ(int opcode, int address);
+  void EmitFR(int opcode, int fmt, FpuRegister ft, FpuRegister fs, FpuRegister fd, int funct);
+  void EmitFI(int opcode, int fmt, FpuRegister rt, uint16_t imm);
+
+  int32_t EncodeBranchOffset(int offset, int32_t inst, bool is_jump);
+  int DecodeBranchOffset(int32_t inst, bool is_jump);
+
+  DISALLOW_COPY_AND_ASSIGN(Mips64Assembler);
+};
+
+// Slowpath entered when Thread::Current()->_exception is non-null
+class Mips64ExceptionSlowPath FINAL : public SlowPath {
+ public:
+  explicit Mips64ExceptionSlowPath(Mips64ManagedRegister scratch, size_t stack_adjust)
+      : scratch_(scratch), stack_adjust_(stack_adjust) {}
+  virtual void Emit(Assembler *sp_asm) OVERRIDE;
+ private:
+  const Mips64ManagedRegister scratch_;
+  const size_t stack_adjust_;
+};
+
+}  // namespace mips64
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_
diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h
new file mode 100644
index 0000000..8b7697c
--- /dev/null
+++ b/compiler/utils/mips64/constants_mips64.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_UTILS_MIPS64_CONSTANTS_MIPS64_H_
+#define ART_COMPILER_UTILS_MIPS64_CONSTANTS_MIPS64_H_
+
+#include <iosfwd>
+
+#include "arch/mips64/registers_mips64.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "globals.h"
+
+namespace art {
+namespace mips64 {
+
+// Constants used for the decoding or encoding of the individual fields of instructions.
+enum InstructionFields {
+  kOpcodeShift = 26,
+  kOpcodeBits = 6,
+  kRsShift = 21,
+  kRsBits = 5,
+  kRtShift = 16,
+  kRtBits = 5,
+  kRdShift = 11,
+  kRdBits = 5,
+  kShamtShift = 6,
+  kShamtBits = 5,
+  kFunctShift = 0,
+  kFunctBits = 6,
+
+  kFmtShift = 21,
+  kFmtBits = 5,
+  kFtShift = 16,
+  kFtBits = 5,
+  kFsShift = 11,
+  kFsBits = 5,
+  kFdShift = 6,
+  kFdBits = 5,
+
+  kBranchOffsetMask = 0x0000ffff,
+  kJumpOffsetMask = 0x03ffffff,
+};
+
+enum ScaleFactor {
+  TIMES_1 = 0,
+  TIMES_2 = 1,
+  TIMES_4 = 2,
+  TIMES_8 = 3
+};
+
+class Instr {
+ public:
+  static const uint32_t kBreakPointInstruction = 0x0000000D;
+
+  bool IsBreakPoint() {
+    return ((*reinterpret_cast<const uint32_t*>(this)) & 0xFC0000CF) == kBreakPointInstruction;
+  }
+
+  // Instructions are read out of a code stream. The only way to get a
+  // reference to an instruction is to convert a pointer. There is no way
+  // to allocate or create instances of class Instr.
+  // Use the At(pc) function to create references to Instr.
+  static Instr* At(uintptr_t pc) { return reinterpret_cast<Instr*>(pc); }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
+};
+
+}  // namespace mips64
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_MIPS64_CONSTANTS_MIPS64_H_
diff --git a/compiler/utils/mips64/managed_register_mips64.cc b/compiler/utils/mips64/managed_register_mips64.cc
new file mode 100644
index 0000000..dea396e
--- /dev/null
+++ b/compiler/utils/mips64/managed_register_mips64.cc
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "managed_register_mips64.h"
+
+#include "globals.h"
+
+namespace art {
+namespace mips64 {
+
+bool Mips64ManagedRegister::Overlaps(const Mips64ManagedRegister& other) const {
+  if (IsNoRegister() || other.IsNoRegister()) return false;
+  CHECK(IsValidManagedRegister());
+  CHECK(other.IsValidManagedRegister());
+  if (Equals(other)) return true;
+  return false;
+}
+
+void Mips64ManagedRegister::Print(std::ostream& os) const {
+  if (!IsValidManagedRegister()) {
+    os << "No Register";
+  } else if (IsGpuRegister()) {
+    os << "GPU: " << static_cast<int>(AsGpuRegister());
+  } else if (IsFpuRegister()) {
+     os << "FpuRegister: " << static_cast<int>(AsFpuRegister());
+  } else {
+    os << "??: " << RegId();
+  }
+}
+
+std::ostream& operator<<(std::ostream& os, const Mips64ManagedRegister& reg) {
+  reg.Print(os);
+  return os;
+}
+
+}  // namespace mips64
+}  // namespace art
diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h
new file mode 100644
index 0000000..924a928
--- /dev/null
+++ b/compiler/utils/mips64/managed_register_mips64.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
+#define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
+
+#include "constants_mips64.h"
+#include "utils/managed_register.h"
+
+namespace art {
+namespace mips64 {
+
+const int kNumberOfGpuRegIds = kNumberOfGpuRegisters;
+const int kNumberOfGpuAllocIds = kNumberOfGpuRegisters;
+
+const int kNumberOfFpuRegIds = kNumberOfFpuRegisters;
+const int kNumberOfFpuAllocIds = kNumberOfFpuRegisters;
+
+const int kNumberOfRegIds = kNumberOfGpuRegIds + kNumberOfFpuRegIds;
+const int kNumberOfAllocIds = kNumberOfGpuAllocIds + kNumberOfFpuAllocIds;
+
+// An instance of class 'ManagedRegister' represents a single GPU register (enum
+// Register) or a double precision FP register (enum FpuRegister)
+// 'ManagedRegister::NoRegister()' provides an invalid register.
+// There is a one-to-one mapping between ManagedRegister and register id.
+class Mips64ManagedRegister : public ManagedRegister {
+ public:
+  GpuRegister AsGpuRegister() const {
+    CHECK(IsGpuRegister());
+    return static_cast<GpuRegister>(id_);
+  }
+
+  FpuRegister AsFpuRegister() const {
+    CHECK(IsFpuRegister());
+    return static_cast<FpuRegister>(id_ - kNumberOfGpuRegIds);
+  }
+
+  bool IsGpuRegister() const {
+    CHECK(IsValidManagedRegister());
+    return (0 <= id_) && (id_ < kNumberOfGpuRegIds);
+  }
+
+  bool IsFpuRegister() const {
+    CHECK(IsValidManagedRegister());
+    const int test = id_ - kNumberOfGpuRegIds;
+    return (0 <= test) && (test < kNumberOfFpuRegIds);
+  }
+
+  void Print(std::ostream& os) const;
+
+  // Returns true if the two managed-registers ('this' and 'other') overlap.
+  // Either managed-register may be the NoRegister. If both are the NoRegister
+  // then false is returned.
+  bool Overlaps(const Mips64ManagedRegister& other) const;
+
+  static Mips64ManagedRegister FromGpuRegister(GpuRegister r) {
+    CHECK_NE(r, kNoGpuRegister);
+    return FromRegId(r);
+  }
+
+  static Mips64ManagedRegister FromFpuRegister(FpuRegister r) {
+    CHECK_NE(r, kNoFpuRegister);
+    return FromRegId(r + kNumberOfGpuRegIds);
+  }
+
+ private:
+  bool IsValidManagedRegister() const {
+    return (0 <= id_) && (id_ < kNumberOfRegIds);
+  }
+
+  int RegId() const {
+    CHECK(!IsNoRegister());
+    return id_;
+  }
+
+  int AllocId() const {
+    CHECK(IsValidManagedRegister());
+    CHECK_LT(id_, kNumberOfAllocIds);
+    return id_;
+  }
+
+  int AllocIdLow() const;
+  int AllocIdHigh() const;
+
+  friend class ManagedRegister;
+
+  explicit Mips64ManagedRegister(int reg_id) : ManagedRegister(reg_id) {}
+
+  static Mips64ManagedRegister FromRegId(int reg_id) {
+    Mips64ManagedRegister reg(reg_id);
+    CHECK(reg.IsValidManagedRegister());
+    return reg;
+  }
+};
+
+std::ostream& operator<<(std::ostream& os, const Mips64ManagedRegister& reg);
+
+}  // namespace mips64
+
+inline mips64::Mips64ManagedRegister ManagedRegister::AsMips64() const {
+  mips64::Mips64ManagedRegister reg(id_);
+  CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister());
+  return reg;
+}
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
diff --git a/compiler/utils/scoped_hashtable.h b/compiler/utils/scoped_hashtable.h
deleted file mode 100644
index bf8dd1f..0000000
--- a/compiler/utils/scoped_hashtable.h
+++ /dev/null
@@ -1,71 +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.
- */
-
-#include <stddef.h>
-#include <map>
-#include <list>
-
-#ifndef ART_COMPILER_UTILS_SCOPED_HASHTABLE_H_
-#define ART_COMPILER_UTILS_SCOPED_HASHTABLE_H_
-
-namespace utils {
-template <typename K, typename V>
-class ScopedHashtable {
- public:
-  explicit ScopedHashtable():scopes() {
-  }
-
-  void OpenScope() {
-    scopes.push_front(std::map<K, V>());
-  }
-
-  // Lookups entry K starting from the current (topmost) scope
-  // and returns its value if found or NULL.
-  V Lookup(K k) const {
-    for (typename std::list<std::map<K, V>>::const_iterator scopes_it = scopes.begin();
-        scopes_it != scopes.end(); scopes_it++) {
-      typename std::map<K, V>::const_iterator result_it = (*scopes_it).find(k);
-      if (result_it != (*scopes_it).end()) {
-        return (*result_it).second;
-      }
-    }
-    return NULL;
-  }
-
-  // Adds a new entry in the current (topmost) scope.
-  void Add(K k, V v) {
-    scopes.front().erase(k);
-    scopes.front().insert(std::pair< K, V >(k, v));
-  }
-
-  // Removes the topmost scope.
-  bool CloseScope() {
-    // Added check to uniformly handle undefined behavior
-    // when removing scope and the list of scopes is empty.
-    if (scopes.size() > 0) {
-      scopes.pop_front();
-      return true;
-    }
-    return false;
-  }
-
- private:
-  std::list<std::map<K, V>> scopes;
-};
-}  // namespace utils
-
-#endif  // ART_COMPILER_UTILS_SCOPED_HASHTABLE_H_
diff --git a/compiler/utils/scoped_hashtable_test.cc b/compiler/utils/scoped_hashtable_test.cc
deleted file mode 100644
index 1c843eb..0000000
--- a/compiler/utils/scoped_hashtable_test.cc
+++ /dev/null
@@ -1,68 +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.
- */
-
-#include "scoped_hashtable.h"
-
-#include "common_runtime_test.h"
-
-using utils::ScopedHashtable;
-
-namespace art {
-
-class Value {
- public:
-  explicit Value(int v):value_(v) {}
-  int value_;
-};
-
-class ScopedHashtableTest : public testing::Test {};
-
-TEST_F(ScopedHashtableTest, Basics) {
-  ScopedHashtable<int, Value*> sht;
-  // Check table is empty when no scope is open.
-  EXPECT_TRUE(NULL == sht.Lookup(1));
-
-  // Check table is empty when scope open.
-  sht.OpenScope();
-  EXPECT_TRUE(NULL == sht.Lookup(1));
-  // Check table is empty after closing scope.
-  EXPECT_EQ(sht.CloseScope(), true);
-  // Check closing scope on empty table is no-op.
-  EXPECT_EQ(sht.CloseScope(), false);
-  // Check that find in current scope works.
-  sht.OpenScope();
-  sht.Add(1, new Value(1));
-  EXPECT_EQ(sht.Lookup(1)->value_, 1);
-  // Check that updating values in current scope works.
-  sht.Add(1, new Value(2));
-  EXPECT_EQ(sht.Lookup(1)->value_, 2);
-  // Check that find works in previous scope.
-  sht.OpenScope();
-  EXPECT_EQ(sht.Lookup(1)->value_, 2);
-  // Check that shadowing scopes works.
-  sht.Add(1, new Value(3));
-  EXPECT_EQ(sht.Lookup(1)->value_, 3);
-  // Check that having multiple keys work correctly.
-  sht.Add(2, new Value(4));
-  EXPECT_EQ(sht.Lookup(1)->value_, 3);
-  EXPECT_EQ(sht.Lookup(2)->value_, 4);
-  // Check that scope removal works corectly.
-  sht.CloseScope();
-  EXPECT_EQ(sht.Lookup(1)->value_, 2);
-  EXPECT_TRUE(NULL == sht.Lookup(2));
-}
-
-}  // namespace art
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
new file mode 100644
index 0000000..325ee4f
--- /dev/null
+++ b/compiler/utils/swap_space.cc
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+#include "swap_space.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "thread-inl.h"
+
+namespace art {
+
+// The chunk size by which the swap file is increased and mapped.
+static constexpr size_t kMininumMapSize = 16 * MB;
+
+static constexpr bool kCheckFreeMaps = false;
+
+template <typename FreeBySizeSet>
+static void DumpFreeMap(const FreeBySizeSet& free_by_size) {
+  size_t last_size = static_cast<size_t>(-1);
+  for (const auto& entry : free_by_size) {
+    if (last_size != entry.first) {
+      last_size = entry.first;
+      LOG(INFO) << "Size " << last_size;
+    }
+    LOG(INFO) << "  0x" << std::hex << entry.second->Start()
+        << " size=" << std::dec << entry.second->size;
+  }
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void RemoveChunk(FreeByStartSet* free_by_start,
+                        FreeBySizeSet* free_by_size,
+                        typename FreeBySizeSet::const_iterator free_by_size_pos) {
+  auto free_by_start_pos = free_by_size_pos->second;
+  free_by_size->erase(free_by_size_pos);
+  free_by_start->erase(free_by_start_pos);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void InsertChunk(FreeByStartSet* free_by_start,
+                        FreeBySizeSet* free_by_size,
+                        const SpaceChunk& chunk) {
+  DCHECK_NE(chunk.size, 0u);
+  auto insert_result = free_by_start->insert(chunk);
+  DCHECK(insert_result.second);
+  free_by_size->emplace(chunk.size, insert_result.first);
+}
+
+SwapSpace::SwapSpace(int fd, size_t initial_size)
+    : fd_(fd),
+      size_(0),
+      lock_("SwapSpace lock", static_cast<LockLevel>(LockLevel::kDefaultMutexLevel - 1)) {
+  // Assume that the file is unlinked.
+
+  InsertChunk(&free_by_start_, &free_by_size_, NewFileChunk(initial_size));
+}
+
+SwapSpace::~SwapSpace() {
+  // All arenas are backed by the same file. Just close the descriptor.
+  close(fd_);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static size_t CollectFree(const FreeByStartSet& free_by_start, const FreeBySizeSet& free_by_size) {
+  if (free_by_start.size() != free_by_size.size()) {
+    LOG(FATAL) << "Size: " << free_by_start.size() << " vs " << free_by_size.size();
+  }
+
+  // Calculate over free_by_size.
+  size_t sum1 = 0;
+  for (const auto& entry : free_by_size) {
+    sum1 += entry.second->size;
+  }
+
+  // Calculate over free_by_start.
+  size_t sum2 = 0;
+  for (const auto& entry : free_by_start) {
+    sum2 += entry.size;
+  }
+
+  if (sum1 != sum2) {
+    LOG(FATAL) << "Sum: " << sum1 << " vs " << sum2;
+  }
+  return sum1;
+}
+
+void* SwapSpace::Alloc(size_t size) {
+  MutexLock lock(Thread::Current(), lock_);
+  size = RoundUp(size, 8U);
+
+  // Check the free list for something that fits.
+  // TODO: Smarter implementation. Global biggest chunk, ...
+  SpaceChunk old_chunk;
+  auto it = free_by_start_.empty()
+      ? free_by_size_.end()
+      : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
+  if (it != free_by_size_.end()) {
+    old_chunk = *it->second;
+    RemoveChunk(&free_by_start_, &free_by_size_, it);
+  } else {
+    // Not a big enough free chunk, need to increase file size.
+    old_chunk = NewFileChunk(size);
+  }
+
+  void* ret = old_chunk.ptr;
+
+  if (old_chunk.size != size) {
+    // Insert the remainder.
+    SpaceChunk new_chunk = { old_chunk.ptr + size, old_chunk.size - size };
+    InsertChunk(&free_by_start_, &free_by_size_, new_chunk);
+  }
+
+  return ret;
+}
+
+SpaceChunk SwapSpace::NewFileChunk(size_t min_size) {
+#if !defined(__APPLE__)
+  size_t next_part = std::max(RoundUp(min_size, kPageSize), RoundUp(kMininumMapSize, kPageSize));
+  int result = TEMP_FAILURE_RETRY(ftruncate64(fd_, size_ + next_part));
+  if (result != 0) {
+    PLOG(FATAL) << "Unable to increase swap file.";
+  }
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(
+      mmap(nullptr, next_part, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, size_));
+  if (ptr == MAP_FAILED) {
+    LOG(ERROR) << "Unable to mmap new swap file chunk.";
+    LOG(ERROR) << "Current size: " << size_ << " requested: " << next_part << "/" << min_size;
+    LOG(ERROR) << "Free list:";
+    MutexLock lock(Thread::Current(), lock_);
+    DumpFreeMap(free_by_size_);
+    LOG(ERROR) << "In free list: " << CollectFree(free_by_start_, free_by_size_);
+    LOG(FATAL) << "Aborting...";
+  }
+  size_ += next_part;
+  SpaceChunk new_chunk = {ptr, next_part};
+  maps_.push_back(new_chunk);
+  return new_chunk;
+#else
+  UNUSED(min_size, kMininumMapSize);
+  LOG(FATAL) << "No swap file support on the Mac.";
+  UNREACHABLE();
+#endif
+}
+
+// TODO: Full coalescing.
+void SwapSpace::Free(void* ptrV, size_t size) {
+  MutexLock lock(Thread::Current(), lock_);
+  size = RoundUp(size, 8U);
+
+  size_t free_before = 0;
+  if (kCheckFreeMaps) {
+    free_before = CollectFree(free_by_start_, free_by_size_);
+  }
+
+  SpaceChunk chunk = { reinterpret_cast<uint8_t*>(ptrV), size };
+  auto it = free_by_start_.lower_bound(chunk);
+  if (it != free_by_start_.begin()) {
+    auto prev = it;
+    --prev;
+    CHECK_LE(prev->End(), chunk.Start());
+    if (prev->End() == chunk.Start()) {
+      // Merge *prev with this chunk.
+      chunk.size += prev->size;
+      chunk.ptr -= prev->size;
+      auto erase_pos = free_by_size_.find(FreeBySizeEntry { prev->size, prev });
+      DCHECK(erase_pos != free_by_size_.end());
+      RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+      // "prev" is invalidated but "it" remains valid.
+    }
+  }
+  if (it != free_by_start_.end()) {
+    CHECK_LE(chunk.End(), it->Start());
+    if (chunk.End() == it->Start()) {
+      // Merge *it with this chunk.
+      chunk.size += it->size;
+      auto erase_pos = free_by_size_.find(FreeBySizeEntry { it->size, it });
+      DCHECK(erase_pos != free_by_size_.end());
+      RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+      // "it" is invalidated but we don't need it anymore.
+    }
+  }
+  InsertChunk(&free_by_start_, &free_by_size_, chunk);
+
+  if (kCheckFreeMaps) {
+    size_t free_after = CollectFree(free_by_start_, free_by_size_);
+
+    if (free_after != free_before + size) {
+      DumpFreeMap(free_by_size_);
+      CHECK_EQ(free_after, free_before + size) << "Should be " << size << " difference from " << free_before;
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
new file mode 100644
index 0000000..2d0d77a
--- /dev/null
+++ b/compiler/utils/swap_space.h
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_
+#define ART_COMPILER_UTILS_SWAP_SPACE_H_
+
+#include <cstdlib>
+#include <list>
+#include <set>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "mem_map.h"
+#include "utils.h"
+#include "utils/debug_stack.h"
+
+namespace art {
+
+// Chunk of space.
+struct SpaceChunk {
+  uint8_t* ptr;
+  size_t size;
+
+  uintptr_t Start() const {
+    return reinterpret_cast<uintptr_t>(ptr);
+  }
+  uintptr_t End() const {
+    return reinterpret_cast<uintptr_t>(ptr) + size;
+  }
+};
+
+inline bool operator==(const SpaceChunk& lhs, const SpaceChunk& rhs) {
+  return (lhs.size == rhs.size) && (lhs.ptr == rhs.ptr);
+}
+
+class SortChunkByPtr {
+ public:
+  bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
+    return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
+  }
+};
+
+// An arena pool that creates arenas backed by an mmaped file.
+class SwapSpace {
+ public:
+  SwapSpace(int fd, size_t initial_size);
+  ~SwapSpace();
+  void* Alloc(size_t size) LOCKS_EXCLUDED(lock_);
+  void Free(void* ptr, size_t size) LOCKS_EXCLUDED(lock_);
+
+  size_t GetSize() {
+    return size_;
+  }
+
+ private:
+  SpaceChunk NewFileChunk(size_t min_size);
+
+  int fd_;
+  size_t size_;
+  std::list<SpaceChunk> maps_;
+
+  // NOTE: Boost.Bimap would be useful for the two following members.
+
+  // Map start of a free chunk to its size.
+  typedef std::set<SpaceChunk, SortChunkByPtr> FreeByStartSet;
+  FreeByStartSet free_by_start_ GUARDED_BY(lock_);
+
+  // Map size to an iterator to free_by_start_'s entry.
+  typedef std::pair<size_t, FreeByStartSet::const_iterator> FreeBySizeEntry;
+  struct FreeBySizeComparator {
+    bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) {
+      if (lhs.first != rhs.first) {
+        return lhs.first < rhs.first;
+      } else {
+        return lhs.second->Start() < rhs.second->Start();
+      }
+    }
+  };
+  typedef std::set<FreeBySizeEntry, FreeBySizeComparator> FreeBySizeSet;
+  FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
+
+  mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  DISALLOW_COPY_AND_ASSIGN(SwapSpace);
+};
+
+template <typename T> class SwapAllocator;
+
+template <>
+class SwapAllocator<void> {
+ public:
+  typedef void value_type;
+  typedef void* pointer;
+  typedef const void* const_pointer;
+
+  template <typename U>
+  struct rebind {
+    typedef SwapAllocator<U> other;
+  };
+
+  explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+  template <typename U>
+  SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+  SwapAllocator(const SwapAllocator& other) = default;
+  SwapAllocator& operator=(const SwapAllocator& other) = default;
+  ~SwapAllocator() = default;
+
+ private:
+  SwapSpace* swap_space_;
+
+  template <typename U>
+  friend class SwapAllocator;
+};
+
+template <typename T>
+class SwapAllocator {
+ public:
+  typedef T value_type;
+  typedef T* pointer;
+  typedef T& reference;
+  typedef const T* const_pointer;
+  typedef const T& const_reference;
+  typedef size_t size_type;
+  typedef ptrdiff_t difference_type;
+
+  template <typename U>
+  struct rebind {
+    typedef SwapAllocator<U> other;
+  };
+
+  explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+  template <typename U>
+  SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+  SwapAllocator(const SwapAllocator& other) = default;
+  SwapAllocator& operator=(const SwapAllocator& other) = default;
+  ~SwapAllocator() = default;
+
+  size_type max_size() const {
+    return static_cast<size_type>(-1) / sizeof(T);
+  }
+
+  pointer address(reference x) const { return &x; }
+  const_pointer address(const_reference x) const { return &x; }
+
+  pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
+    DCHECK_LE(n, max_size());
+    if (swap_space_ == nullptr) {
+      return reinterpret_cast<T*>(malloc(n * sizeof(T)));
+    } else {
+      return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
+    }
+  }
+  void deallocate(pointer p, size_type n) {
+    if (swap_space_ == nullptr) {
+      free(p);
+    } else {
+      swap_space_->Free(p, n * sizeof(T));
+    }
+  }
+
+  void construct(pointer p, const_reference val) {
+    new (static_cast<void*>(p)) value_type(val);
+  }
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+  }
+  void destroy(pointer p) {
+    p->~value_type();
+  }
+
+  inline bool operator==(SwapAllocator const& other) {
+    return swap_space_ == other.swap_space_;
+  }
+  inline bool operator!=(SwapAllocator const& other) {
+    return !operator==(other);
+  }
+
+ private:
+  SwapSpace* swap_space_;
+
+  template <typename U>
+  friend class SwapAllocator;
+};
+
+template <typename T>
+using SwapVector = std::vector<T, SwapAllocator<T>>;
+template <typename T, typename Comparator>
+using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_SWAP_SPACE_H_
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
new file mode 100644
index 0000000..bf50ac3
--- /dev/null
+++ b/compiler/utils/swap_space_test.cc
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include "utils/swap_space.h"
+
+#include <cstdio>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "gtest/gtest.h"
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "os.h"
+
+namespace art {
+
+class SwapSpaceTest : public CommonRuntimeTest {
+};
+
+static void SwapTest(bool use_file) {
+  ScratchFile scratch;
+  int fd = scratch.GetFd();
+  unlink(scratch.GetFilename().c_str());
+
+  SwapSpace pool(fd, 1 * MB);
+  SwapAllocator<void> alloc(use_file ? &pool : nullptr);
+
+  SwapVector<int32_t> v(alloc);
+  v.reserve(1000000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v.push_back(i);
+    EXPECT_EQ(i, v[i]);
+  }
+
+  SwapVector<int32_t> v2(alloc);
+  v2.reserve(1000000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v2.push_back(i);
+    EXPECT_EQ(i, v2[i]);
+  }
+
+  SwapVector<int32_t> v3(alloc);
+  v3.reserve(500000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v3.push_back(i);
+    EXPECT_EQ(i, v2[i]);
+  }
+
+  // Verify contents.
+  for (int32_t i = 0; i < 1000000; ++i) {
+    EXPECT_EQ(i, v[i]);
+    EXPECT_EQ(i, v2[i]);
+    EXPECT_EQ(i, v3[i]);
+  }
+
+  scratch.Close();
+}
+
+TEST_F(SwapSpaceTest, Memory) {
+  SwapTest(false);
+}
+
+TEST_F(SwapSpaceTest, Swap) {
+  SwapTest(true);
+}
+
+}  // namespace art
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index f0353f6..03744e4 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -51,7 +51,8 @@
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xE8);
   static const int kSize = 5;
-  EmitLabel(label, kSize);
+  // Offset by one because we already have emitted the opcode.
+  EmitLabel(label, kSize - 1);
 }
 
 
@@ -409,6 +410,13 @@
 }
 
 
+void X86Assembler::fsts(const Address& dst) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xD9);
+  EmitOperand(2, dst);
+}
+
+
 void X86Assembler::fstps(const Address& dst) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xD9);
@@ -443,6 +451,27 @@
 }
 
 
+void X86Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) {
+  DCHECK(shift_count.is_uint8());
+
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x73);
+  EmitXmmRegisterOperand(2, reg);
+  EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::punpckldq(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0x62);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
 void X86Assembler::addsd(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF2);
@@ -698,6 +727,13 @@
 }
 
 
+void X86Assembler::fstl(const Address& dst) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xDD);
+  EmitOperand(2, dst);
+}
+
+
 void X86Assembler::fstpl(const Address& dst) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xDD);
@@ -705,6 +741,14 @@
 }
 
 
+void X86Assembler::fstsw() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x9B);
+  EmitUint8(0xDF);
+  EmitUint8(0xE0);
+}
+
+
 void X86Assembler::fnstcw(const Address& dst) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xD9);
@@ -776,6 +820,20 @@
 }
 
 
+void X86Assembler::fucompp() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xDA);
+  EmitUint8(0xE9);
+}
+
+
+void X86Assembler::fprem() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xD9);
+  EmitUint8(0xF8);
+}
+
+
 void X86Assembler::xchgl(Register dst, Register src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x87);
@@ -1351,38 +1409,6 @@
 }
 
 
-void X86Assembler::FloatNegate(XmmRegister f) {
-  static const struct {
-    uint32_t a;
-    uint32_t b;
-    uint32_t c;
-    uint32_t d;
-  } float_negate_constant __attribute__((aligned(16))) =
-      { 0x80000000, 0x00000000, 0x80000000, 0x00000000 };
-  xorps(f, Address::Absolute(reinterpret_cast<uintptr_t>(&float_negate_constant)));
-}
-
-
-void X86Assembler::DoubleNegate(XmmRegister d) {
-  static const struct {
-    uint64_t a;
-    uint64_t b;
-  } double_negate_constant __attribute__((aligned(16))) =
-      {0x8000000000000000LL, 0x8000000000000000LL};
-  xorpd(d, Address::Absolute(reinterpret_cast<uintptr_t>(&double_negate_constant)));
-}
-
-
-void X86Assembler::DoubleAbs(XmmRegister reg) {
-  static const struct {
-    uint64_t a;
-    uint64_t b;
-  } double_abs_constant __attribute__((aligned(16))) =
-      {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
-  andpd(reg, Address::Absolute(reinterpret_cast<uintptr_t>(&double_abs_constant)));
-}
-
-
 void X86Assembler::Align(int alignment, int offset) {
   CHECK(IsPowerOfTwo(alignment));
   // Emit nop instruction until the real position is aligned.
@@ -1512,8 +1538,12 @@
 
   uint32_t reg_offset = 1;
   CHECK_ALIGNED(frame_size, kStackAlignment);
+  int gpr_count = 0;
   for (int i = spill_regs.size() - 1; i >= 0; --i) {
-    pushl(spill_regs.at(i).AsX86().AsCpuRegister());
+    x86::X86ManagedRegister spill = spill_regs.at(i).AsX86();
+    DCHECK(spill.IsCpuRegister());
+    pushl(spill.AsCpuRegister());
+    gpr_count++;
 
     // DW_CFA_advance_loc
     DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
@@ -1527,7 +1557,7 @@
   }
 
   // return address then method on stack
-  int32_t adjust = frame_size - (spill_regs.size() * kFramePointerSize) -
+  int32_t adjust = frame_size - (gpr_count * kFramePointerSize) -
                    sizeof(StackReference<mirror::ArtMethod>) /*method*/ -
                    kFramePointerSize /*return address*/;
   addl(ESP, Immediate(-adjust));
@@ -1547,9 +1577,18 @@
   DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
 
   for (size_t i = 0; i < entry_spills.size(); ++i) {
-    movl(Address(ESP, frame_size + sizeof(StackReference<mirror::ArtMethod>) +
-                 (i * kFramePointerSize)),
-         entry_spills.at(i).AsX86().AsCpuRegister());
+    ManagedRegisterSpill spill = entry_spills.at(i);
+    if (spill.AsX86().IsCpuRegister()) {
+      movl(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsCpuRegister());
+    } else {
+      DCHECK(spill.AsX86().IsXmmRegister());
+      if (spill.getSize() == 8) {
+        movsd(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsXmmRegister());
+      } else {
+        CHECK_EQ(spill.getSize(), 4);
+        movss(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsXmmRegister());
+      }
+    }
   }
 }
 
@@ -1559,7 +1598,9 @@
   addl(ESP, Immediate(frame_size - (spill_regs.size() * kFramePointerSize) -
                       sizeof(StackReference<mirror::ArtMethod>)));
   for (size_t i = 0; i < spill_regs.size(); ++i) {
-    popl(spill_regs.at(i).AsX86().AsCpuRegister());
+    x86::X86ManagedRegister spill = spill_regs.at(i).AsX86();
+    DCHECK(spill.IsCpuRegister());
+    popl(spill.AsCpuRegister());
   }
   ret();
 }
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 9fecf1e..3a44ace 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -274,6 +274,9 @@
   void movsd(const Address& dst, XmmRegister src);
   void movsd(XmmRegister dst, XmmRegister src);
 
+  void psrlq(XmmRegister reg, const Immediate& shift_count);
+  void punpckldq(XmmRegister dst, XmmRegister src);
+
   void addsd(XmmRegister dst, XmmRegister src);
   void addsd(XmmRegister dst, const Address& src);
   void subsd(XmmRegister dst, XmmRegister src);
@@ -314,9 +317,15 @@
 
   void flds(const Address& src);
   void fstps(const Address& dst);
+  void fsts(const Address& dst);
 
   void fldl(const Address& src);
   void fstpl(const Address& dst);
+  void fstl(const Address& dst);
+
+  void fstsw();
+
+  void fucompp();
 
   void fnstcw(const Address& dst);
   void fldcw(const Address& src);
@@ -331,6 +340,7 @@
   void fsin();
   void fcos();
   void fptan();
+  void fprem();
 
   void xchgl(Register dst, Register src);
   void xchgl(Register reg, const Address& address);
@@ -444,11 +454,6 @@
   void LoadLongConstant(XmmRegister dst, int64_t value);
   void LoadDoubleConstant(XmmRegister dst, double value);
 
-  void DoubleNegate(XmmRegister d);
-  void FloatNegate(XmmRegister f);
-
-  void DoubleAbs(XmmRegister reg);
-
   void LockCmpxchgl(const Address& address, Register reg) {
     lock()->cmpxchgl(address, reg);
   }
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index d901673..fccb510 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -105,6 +105,18 @@
   DriverStr(expected, "movl");
 }
 
+TEST_F(AssemblerX86Test, psrlq) {
+  GetAssembler()->psrlq(x86::XMM0, CreateImmediate(32));
+  const char* expected = "psrlq $0x20, %xmm0\n";
+  DriverStr(expected, "psrlq");
+}
+
+TEST_F(AssemblerX86Test, punpckldq) {
+  GetAssembler()->punpckldq(x86::XMM0, x86::XMM1);
+  const char* expected = "punpckldq %xmm1, %xmm0\n";
+  DriverStr(expected, "punpckldq");
+}
+
 TEST_F(AssemblerX86Test, LoadLongConstant) {
   GetAssembler()->LoadLongConstant(x86::XMM0, 51);
   const char* expected =
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 474d8a9..556fa9b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -57,7 +57,8 @@
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xE8);
   static const int kSize = 5;
-  EmitLabel(label, kSize);
+  // Offset by one because we already have emitted the opcode.
+  EmitLabel(label, kSize - 1);
 }
 
 void X86_64Assembler::pushq(CpuRegister reg) {
@@ -184,6 +185,20 @@
   EmitImmediate(imm);
 }
 
+
+void X86_64Assembler::cmov(Condition c, CpuRegister dst, CpuRegister src) {
+  cmov(c, dst, src, true);
+}
+
+void X86_64Assembler::cmov(Condition c, CpuRegister dst, CpuRegister src, bool is64bit) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex(false, is64bit, dst.NeedsRex(), false, src.NeedsRex());
+  EmitUint8(0x0F);
+  EmitUint8(0x40 + c);
+  EmitRegisterOperand(dst.LowBits(), src.LowBits());
+}
+
+
 void X86_64Assembler::movzxb(CpuRegister dst, CpuRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitOptionalByteRegNormalizingRex32(dst, src);
@@ -313,6 +328,14 @@
 }
 
 
+void X86_64Assembler::leal(CpuRegister dst, const Address& src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x8D);
+  EmitOperand(dst.LowBits(), src);
+}
+
+
 void X86_64Assembler::movaps(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitOptionalRex32(dst, src);
@@ -369,19 +392,26 @@
 
 
 void X86_64Assembler::movd(XmmRegister dst, CpuRegister src) {
+  movd(dst, src, true);
+}
+
+void X86_64Assembler::movd(CpuRegister dst, XmmRegister src) {
+  movd(dst, src, true);
+}
+
+void X86_64Assembler::movd(XmmRegister dst, CpuRegister src, bool is64bit) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
-  EmitRex64(dst, src);
+  EmitOptionalRex(false, is64bit, dst.NeedsRex(), false, src.NeedsRex());
   EmitUint8(0x0F);
   EmitUint8(0x6E);
   EmitOperand(dst.LowBits(), Operand(src));
 }
 
-
-void X86_64Assembler::movd(CpuRegister dst, XmmRegister src) {
+void X86_64Assembler::movd(CpuRegister dst, XmmRegister src, bool is64bit) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
-  EmitRex64(src, dst);
+  EmitOptionalRex(false, is64bit, src.NeedsRex(), false, dst.NeedsRex());
   EmitUint8(0x0F);
   EmitUint8(0x7E);
   EmitOperand(src.LowBits(), Operand(dst));
@@ -475,6 +505,13 @@
 }
 
 
+void X86_64Assembler::fsts(const Address& dst) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xD9);
+  EmitOperand(2, dst);
+}
+
+
 void X86_64Assembler::fstps(const Address& dst) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xD9);
@@ -663,9 +700,19 @@
 
 
 void X86_64Assembler::cvttss2si(CpuRegister dst, XmmRegister src) {
+  cvttss2si(dst, src, false);
+}
+
+
+void X86_64Assembler::cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF3);
-  EmitOptionalRex32(dst, src);
+  if (is64bit) {
+    // Emit a REX.W prefix if the operand size is 64 bits.
+    EmitRex64(dst, src);
+  } else {
+    EmitOptionalRex32(dst, src);
+  }
   EmitUint8(0x0F);
   EmitUint8(0x2C);
   EmitXmmRegisterOperand(dst.LowBits(), src);
@@ -673,9 +720,19 @@
 
 
 void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src) {
+  cvttsd2si(dst, src, false);
+}
+
+
+void X86_64Assembler::cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF2);
-  EmitOptionalRex32(dst, src);
+  if (is64bit) {
+    // Emit a REX.W prefix if the operand size is 64 bits.
+    EmitRex64(dst, src);
+  } else {
+    EmitOptionalRex32(dst, src);
+  }
   EmitUint8(0x0F);
   EmitUint8(0x2C);
   EmitXmmRegisterOperand(dst.LowBits(), src);
@@ -806,6 +863,39 @@
   EmitOperand(dst.LowBits(), src);
 }
 
+void X86_64Assembler::andpd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x54);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::andps(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x54);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x56);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::orps(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0x56);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
 
 void X86_64Assembler::fldl(const Address& src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -814,6 +904,13 @@
 }
 
 
+void X86_64Assembler::fstl(const Address& dst) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xDD);
+  EmitOperand(2, dst);
+}
+
+
 void X86_64Assembler::fstpl(const Address& dst) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xDD);
@@ -821,6 +918,14 @@
 }
 
 
+void X86_64Assembler::fstsw() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x9B);
+  EmitUint8(0xDF);
+  EmitUint8(0xE0);
+}
+
+
 void X86_64Assembler::fnstcw(const Address& dst) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xD9);
@@ -891,6 +996,19 @@
   EmitUint8(0xF2);
 }
 
+void X86_64Assembler::fucompp() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xDA);
+  EmitUint8(0xE9);
+}
+
+
+void X86_64Assembler::fprem() {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xD9);
+  EmitUint8(0xF8);
+}
+
 
 void X86_64Assembler::xchgl(CpuRegister dst, CpuRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
@@ -1047,6 +1165,14 @@
 }
 
 
+void X86_64Assembler::testl(CpuRegister reg, const Address& address) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(reg, address);
+  EmitUint8(0x85);
+  EmitOperand(reg.LowBits(), address);
+}
+
+
 void X86_64Assembler::testl(CpuRegister reg, const Immediate& immediate) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   // For registers that have a byte variant (RAX, RBX, RCX, and RDX)
@@ -1737,6 +1863,20 @@
   EmitUint8(0xC0 + dst.LowBits());
 }
 
+void X86_64Assembler::bswapl(CpuRegister dst) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex(false, false, false, false, dst.NeedsRex());
+  EmitUint8(0x0F);
+  EmitUint8(0xC8 + dst.LowBits());
+}
+
+void X86_64Assembler::bswapq(CpuRegister dst) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex(false, true, false, false, dst.NeedsRex());
+  EmitUint8(0x0F);
+  EmitUint8(0xC8 + dst.LowBits());
+}
+
 
 void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) {
   // TODO: Need to have a code constants table.
@@ -1748,38 +1888,6 @@
 }
 
 
-void X86_64Assembler::FloatNegate(XmmRegister f) {
-  static const struct {
-    uint32_t a;
-    uint32_t b;
-    uint32_t c;
-    uint32_t d;
-  } float_negate_constant __attribute__((aligned(16))) =
-      { 0x80000000, 0x00000000, 0x80000000, 0x00000000 };
-  xorps(f, Address::Absolute(reinterpret_cast<uintptr_t>(&float_negate_constant)));
-}
-
-
-void X86_64Assembler::DoubleNegate(XmmRegister d) {
-  static const struct {
-    uint64_t a;
-    uint64_t b;
-  } double_negate_constant __attribute__((aligned(16))) =
-      {0x8000000000000000LL, 0x8000000000000000LL};
-  xorpd(d, Address::Absolute(reinterpret_cast<uintptr_t>(&double_negate_constant)));
-}
-
-
-void X86_64Assembler::DoubleAbs(XmmRegister reg) {
-  static const struct {
-    uint64_t a;
-    uint64_t b;
-  } double_abs_constant __attribute__((aligned(16))) =
-      {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
-  andpd(reg, Address::Absolute(reinterpret_cast<uintptr_t>(&double_abs_constant)));
-}
-
-
 void X86_64Assembler::Align(int alignment, int offset) {
   CHECK(IsPowerOfTwo(alignment));
   // Emit nop instruction until the real position is aligned.
@@ -1997,6 +2105,10 @@
   EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
 }
 
+void X86_64Assembler::EmitRex64(CpuRegister dst, XmmRegister src) {
+  EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
+}
+
 void X86_64Assembler::EmitRex64(CpuRegister dst, const Operand& operand) {
   uint8_t rex = 0x48 | operand.rex();  // REX.W000
   if (dst.NeedsRex()) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 6e71e4a..a1c704e 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -178,20 +178,20 @@
   }
 
   void Init(CpuRegister base_in, int32_t disp) {
-    if (disp == 0 && base_in.AsRegister() != RBP) {
+    if (disp == 0 && base_in.LowBits() != RBP) {
       SetModRM(0, base_in);
-      if (base_in.AsRegister() == RSP) {
+      if (base_in.LowBits() == RSP) {
         SetSIB(TIMES_1, CpuRegister(RSP), base_in);
       }
     } else if (disp >= -128 && disp <= 127) {
       SetModRM(1, base_in);
-      if (base_in.AsRegister() == RSP) {
+      if (base_in.LowBits() == RSP) {
         SetSIB(TIMES_1, CpuRegister(RSP), base_in);
       }
       SetDisp8(disp);
     } else {
       SetModRM(2, base_in);
-      if (base_in.AsRegister() == RSP) {
+      if (base_in.LowBits() == RSP) {
         SetSIB(TIMES_1, CpuRegister(RSP), base_in);
       }
       SetDisp32(disp);
@@ -208,7 +208,7 @@
 
   Address(CpuRegister base_in, CpuRegister index_in, ScaleFactor scale_in, int32_t disp) {
     CHECK_NE(index_in.AsRegister(), RSP);  // Illegal addressing mode.
-    if (disp == 0 && base_in.AsRegister() != RBP) {
+    if (disp == 0 && base_in.LowBits() != RBP) {
       SetModRM(0, CpuRegister(RSP));
       SetSIB(scale_in, index_in, base_in);
     } else if (disp >= -128 && disp <= 127) {
@@ -276,6 +276,9 @@
   void movl(const Address& dst, CpuRegister src);
   void movl(const Address& dst, const Immediate& imm);
 
+  void cmov(Condition c, CpuRegister dst, CpuRegister src);  // This is the 64b version.
+  void cmov(Condition c, CpuRegister dst, CpuRegister src, bool is64bit);
+
   void movzxb(CpuRegister dst, CpuRegister src);
   void movzxb(CpuRegister dst, const Address& src);
   void movsxb(CpuRegister dst, CpuRegister src);
@@ -293,6 +296,7 @@
   void movw(const Address& dst, const Immediate& imm);
 
   void leaq(CpuRegister dst, const Address& src);
+  void leal(CpuRegister dst, const Address& src);
 
   void movaps(XmmRegister dst, XmmRegister src);
 
@@ -303,8 +307,10 @@
   void movsxd(CpuRegister dst, CpuRegister src);
   void movsxd(CpuRegister dst, const Address& src);
 
-  void movd(XmmRegister dst, CpuRegister src);
-  void movd(CpuRegister dst, XmmRegister src);
+  void movd(XmmRegister dst, CpuRegister src);  // Note: this is the r64 version, formally movq.
+  void movd(CpuRegister dst, XmmRegister src);  // Note: this is the r64 version, formally movq.
+  void movd(XmmRegister dst, CpuRegister src, bool is64bit);
+  void movd(CpuRegister dst, XmmRegister src, bool is64bit);
 
   void addss(XmmRegister dst, XmmRegister src);
   void addss(XmmRegister dst, const Address& src);
@@ -340,7 +346,9 @@
   void cvtsd2ss(XmmRegister dst, XmmRegister src);
 
   void cvttss2si(CpuRegister dst, XmmRegister src);  // Note: this is the r32 version.
+  void cvttss2si(CpuRegister dst, XmmRegister src, bool is64bit);
   void cvttsd2si(CpuRegister dst, XmmRegister src);  // Note: this is the r32 version.
+  void cvttsd2si(CpuRegister dst, XmmRegister src, bool is64bit);
 
   void cvtdq2pd(XmmRegister dst, XmmRegister src);
 
@@ -358,12 +366,23 @@
   void xorps(XmmRegister dst, XmmRegister src);
 
   void andpd(XmmRegister dst, const Address& src);
+  void andpd(XmmRegister dst, XmmRegister src);
+  void andps(XmmRegister dst, XmmRegister src);
+
+  void orpd(XmmRegister dst, XmmRegister src);
+  void orps(XmmRegister dst, XmmRegister src);
 
   void flds(const Address& src);
   void fstps(const Address& dst);
+  void fsts(const Address& dst);
 
   void fldl(const Address& src);
   void fstpl(const Address& dst);
+  void fstl(const Address& dst);
+
+  void fstsw();
+
+  void fucompp();
 
   void fnstcw(const Address& dst);
   void fldcw(const Address& src);
@@ -378,6 +397,7 @@
   void fsin();
   void fcos();
   void fptan();
+  void fprem();
 
   void xchgl(CpuRegister dst, CpuRegister src);
   void xchgq(CpuRegister dst, CpuRegister src);
@@ -397,6 +417,7 @@
   void cmpq(const Address& address, const Immediate& imm);
 
   void testl(CpuRegister reg1, CpuRegister reg2);
+  void testl(CpuRegister reg, const Address& address);
   void testl(CpuRegister reg, const Immediate& imm);
 
   void testq(CpuRegister reg1, CpuRegister reg2);
@@ -502,6 +523,9 @@
 
   void setcc(Condition condition, CpuRegister dst);
 
+  void bswapl(CpuRegister dst);
+  void bswapq(CpuRegister dst);
+
   //
   // Macros for High-level operations.
   //
@@ -510,11 +534,6 @@
 
   void LoadDoubleConstant(XmmRegister dst, double value);
 
-  void DoubleNegate(XmmRegister d);
-  void FloatNegate(XmmRegister f);
-
-  void DoubleAbs(XmmRegister reg);
-
   void LockCmpxchgl(const Address& address, CpuRegister reg) {
     lock()->cmpxchgl(address, reg);
   }
@@ -688,6 +707,7 @@
   void EmitRex64(CpuRegister dst, CpuRegister src);
   void EmitRex64(CpuRegister dst, const Operand& operand);
   void EmitRex64(XmmRegister dst, CpuRegister src);
+  void EmitRex64(CpuRegister dst, XmmRegister src);
 
   // Emit a REX prefix to normalize byte registers plus necessary register bit encodings.
   void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, CpuRegister src);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index c8e923c..6df4144 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -330,7 +330,6 @@
     assembler->shlq(*reg, shifter);
     str << "shlq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n";
   }
-  printf("%s\n", str.str().c_str());
 
   return str.str();
 }
@@ -536,10 +535,16 @@
       x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
   GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::Address(
       x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
+  GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
+      x86_64::CpuRegister(x86_64::R13), 0));
+  GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
+      x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0));
   const char* expected =
     "movl 0xc(%RDI,%RBX,4), %EAX\n"
     "movl 0xc(%RDI,%R9,4), %EAX\n"
-    "movl 0xc(%RDI,%R9,4), %R8d\n";
+    "movl 0xc(%RDI,%R9,4), %R8d\n"
+    "movl (%R13), %EAX\n"
+    "movl (%R13,%R9,1), %EAX\n";
 
   DriverStr(expected, "movl");
 }
@@ -684,6 +689,22 @@
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
 }
 
+TEST_F(AssemblerX86_64Test, Andps) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::andps, "andps %{reg2}, %{reg1}"), "andps");
+}
+
+TEST_F(AssemblerX86_64Test, Andpd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd");
+}
+
+TEST_F(AssemblerX86_64Test, Orps) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps");
+}
+
+TEST_F(AssemblerX86_64Test, Orpd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
+}
+
 // X87
 
 std::string x87_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
@@ -752,6 +773,14 @@
 // MISC //
 //////////
 
+TEST_F(AssemblerX86_64Test, Bswapl) {
+  DriverStr(Repeatr(&x86_64::X86_64Assembler::bswapl, "bswap %{reg}"), "bswapl");
+}
+
+TEST_F(AssemblerX86_64Test, Bswapq) {
+  DriverStr(RepeatR(&x86_64::X86_64Assembler::bswapq, "bswap %{reg}"), "bswapq");
+}
+
 std::string setcc_test_fn(AssemblerX86_64Test::Base* assembler_test,
                           x86_64::X86_64Assembler* assembler) {
   // From Condition
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 0ef20d6..8afd443 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -35,7 +35,7 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := dalvikvm32
 LOCAL_MODULE_STEM_64 := dalvikvm64
-include external/libcxx/libcxx.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_EXECUTABLE)
 
 # Create symlink for the primary version target.
@@ -68,7 +68,7 @@
 LOCAL_MULTILIB := both
 LOCAL_MODULE_STEM_32 := dalvikvm32
 LOCAL_MODULE_STEM_64 := dalvikvm64
-include external/libcxx/libcxx.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_HOST_EXECUTABLE)
 
 # Create symlink for the primary version target.
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 4f39c42..3f15964 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -24,22 +24,30 @@
 # TODO: Remove this when the framework (installd) supports pushing the
 # right instruction-set parameter for the primary architecture.
 ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),)
-  dex2oat_arch := 64
+  dex2oat_target_arch := 64
 else
-  dex2oat_arch := 32
+  dex2oat_target_arch := 32
+endif
+
+# We need to explcitly give the arch, as giving 'both' will make the
+# build-art-executable rule compile dex2oat for 64bits.
+ifeq ($(HOST_PREFER_32_BIT),true)
+  dex2oat_host_arch := 32
+else
+  dex2oat_host_arch := both
 endif
 
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler,art/compiler,target,ndebug,$(dex2oat_arch)))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler,art/compiler,target,ndebug,$(dex2oat_target_arch)))
 endif
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler,art/compiler,target,debug,$(dex2oat_arch)))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler,art/compiler,target,debug,$(dex2oat_target_arch)))
 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-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug))
+  $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
 endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index a1ac2f0..e607e15 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -25,10 +25,6 @@
 #include <string>
 #include <vector>
 
-#ifndef __APPLE__
-#include <malloc.h>  // For mallinfo
-#endif
-
 #if defined(__linux__) && defined(__arm__)
 #include <sys/personality.h>
 #include <sys/utsname.h>
@@ -38,6 +34,7 @@
 #include <cutils/trace.h>
 
 #include "arch/instruction_set_features.h"
+#include "arch/mips/instruction_set_features_mips.h"
 #include "base/dumpable.h"
 #include "base/stl_util.h"
 #include "base/stringpiece.h"
@@ -47,7 +44,7 @@
 #include "compiler.h"
 #include "compiler_callbacks.h"
 #include "dex_file-inl.h"
-#include "dex/pass_driver_me_opts.h"
+#include "dex/pass_manager.h"
 #include "dex/verification_results.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/quick/dex_file_to_method_inliner_map.h"
@@ -134,9 +131,6 @@
   UsageError("  --oat-symbols=<file.oat>: specifies the oat output destination with full symbols.");
   UsageError("      Example: --oat-symbols=/symbols/system/framework/boot.oat");
   UsageError("");
-  UsageError("  --bitcode=<file.bc>: specifies the optional bitcode filename.");
-  UsageError("      Example: --bitcode=/system/framework/boot.bc");
-  UsageError("");
   UsageError("  --image=<file.art>: specifies the output image filename.");
   UsageError("      Example: --image=/system/framework/boot.art");
   UsageError("");
@@ -154,7 +148,7 @@
   UsageError("      Example: --android-root=out/host/linux-x86");
   UsageError("      Default: $ANDROID_ROOT");
   UsageError("");
-  UsageError("  --instruction-set=(arm|arm64|mips|x86|x86_64): compile for a particular");
+  UsageError("  --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): compile for a particular");
   UsageError("      instruction set.");
   UsageError("      Example: --instruction-set=x86");
   UsageError("      Default: arm");
@@ -166,12 +160,10 @@
   UsageError("  --compile-pic: Force indirect use of code, methods, and classes");
   UsageError("      Default: disabled");
   UsageError("");
-  UsageError("  --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
+  UsageError("  --compiler-backend=(Quick|Optimizing): select compiler backend");
   UsageError("      set.");
-  UsageError("      Example: --compiler-backend=Portable");
-  if (kUsePortableCompiler) {
-    UsageError("      Default: Portable");
-  } else if (kUseOptimizingCompiler) {
+  UsageError("      Example: --compiler-backend=Optimizing");
+  if (kUseOptimizingCompiler) {
     UsageError("      Default: Optimizing");
   } else {
     UsageError("      Default: Quick");
@@ -225,8 +217,6 @@
   UsageError("      Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
   UsageError("      Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
   UsageError("");
-  UsageError("  --host: used with Portable backend to link against host runtime libraries");
-  UsageError("");
   UsageError("  --dump-timing: display a breakdown of where time was spent");
   UsageError("");
   UsageError("  --include-patch-information: Include patching information so the generated code");
@@ -259,6 +249,12 @@
   UsageError("      Used to specify a pass specific option. The setting itself must be integer.");
   UsageError("      Separator used between options is a comma.");
   UsageError("");
+  UsageError("  --swap-file=<file-name>:  specifies a file to use for swap.");
+  UsageError("      Example: --swap-file=/data/tmp/swap.001");
+  UsageError("");
+  UsageError("  --swap-fd=<file-descriptor>:  specifies a file to use for swap (by descriptor).");
+  UsageError("      Example: --swap-fd=10");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -267,7 +263,7 @@
 // during development when fatal aborts lead to a cascade of failures
 // that result in a deadlock.
 class WatchDog {
-// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using Log which uses locks
+// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using LOG which uses locks
 #undef CHECK_PTHREAD_CALL
 #define CHECK_WATCH_DOG_PTHREAD_CALL(call, args, what) \
   do { \
@@ -330,41 +326,23 @@
             message.c_str());
   }
 
-  static void Warn(const std::string& message) {
-    Message('W', message);
-  }
-
   [[noreturn]] static void Fatal(const std::string& message) {
     Message('F', message);
     exit(1);
   }
 
   void Wait() {
-    bool warning = true;
-    CHECK_GT(kWatchDogTimeoutSeconds, kWatchDogWarningSeconds);
     // TODO: tune the multiplier for GC verification, the following is just to make the timeout
     //       large.
     int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
-    timespec warning_ts;
-    InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogWarningSeconds * 1000, 0, &warning_ts);
     timespec timeout_ts;
     InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts);
     const char* reason = "dex2oat watch dog thread waiting";
     CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
     while (!shutting_down_) {
-      int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_,
-                                                         warning ? &warning_ts
-                                                                 : &timeout_ts));
+      int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &timeout_ts));
       if (rc == ETIMEDOUT) {
-        std::string message(StringPrintf("dex2oat did not finish after %d seconds",
-                                         warning ? kWatchDogWarningSeconds
-                                                 : kWatchDogTimeoutSeconds));
-        if (warning) {
-          Warn(message.c_str());
-          warning = false;
-        } else {
-          Fatal(message.c_str());
-        }
+        Fatal(StringPrintf("dex2oat did not finish after %d seconds", kWatchDogTimeoutSeconds));
       } else if (rc != 0) {
         std::string message(StringPrintf("pthread_cond_timedwait failed: %s",
                                          strerror(errno)));
@@ -378,12 +356,8 @@
   // Debug builds are slower so they have larger timeouts.
   static const unsigned int kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
 
-  static const unsigned int kWatchDogWarningSeconds = kUsePortableCompiler ?
-      kSlowdownFactor * 2 * 60 :   // 2 minutes scaled by kSlowdownFactor (portable).
-      kSlowdownFactor * 1 * 60;    // 1 minute scaled by kSlowdownFactor  (not-portable).
-  static const unsigned int kWatchDogTimeoutSeconds = kUsePortableCompiler ?
-      kSlowdownFactor * 30 * 60 :  // 30 minutes scaled by kSlowdownFactor (portable).
-      kSlowdownFactor * 6 * 60;    // 6 minutes scaled by kSlowdownFactor  (not-portable).
+  // 6 minutes scaled by kSlowdownFactor.
+  static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 6 * 60;
 
   bool is_watch_dog_enabled_;
   bool shutting_down_;
@@ -426,12 +400,29 @@
   *parsed_value = value;
 }
 
+static constexpr size_t kMinDexFilesForSwap = 2;
+static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
+
+static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
+  if (is_image) {
+    // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+    return false;
+  }
+  if (dex_files.size() < kMinDexFilesForSwap) {
+    // If there are less dex files than the threshold, assume it's gonna be fine.
+    return false;
+  }
+  size_t dex_files_size = 0;
+  for (const auto* dex_file : dex_files) {
+    dex_files_size += dex_file->GetHeader().file_size_;
+  }
+  return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
+}
+
 class Dex2Oat FINAL {
  public:
   explicit Dex2Oat(TimingLogger* timings) :
-      compiler_kind_(kUsePortableCompiler
-          ? Compiler::kPortable
-          : (kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick)),
+      compiler_kind_(kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick),
       instruction_set_(kRuntimeISA),
       // Take the default set of instruction features from the build.
       method_inliner_map_(),
@@ -451,10 +442,19 @@
       dump_passes_(false),
       dump_timing_(false),
       dump_slow_timing_(kIsDebugBuild),
+      swap_fd_(-1),
       timings_(timings) {}
 
   ~Dex2Oat() {
-    LogCompletionTime();  // Needs to be before since it accesses the runtime.
+    // Free opened dex files before deleting the runtime_, because ~DexFile
+    // uses MemMap, which is shut down by ~Runtime.
+    class_path_files_.clear();
+    opened_dex_files_.clear();
+
+    // Log completion time before deleting the runtime_, because this accesses
+    // the runtime.
+    LogCompletionTime();
+
     if (kIsDebugBuild || (RUNNING_ON_VALGRIND != 0)) {
       delete runtime_;  // See field declaration for why this is manual.
     }
@@ -490,12 +490,13 @@
     // Profile file to use
     double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
 
-    bool print_pass_options = false;
     bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
     bool include_debug_symbols = kIsDebugBuild;
     bool watch_dog_enabled = true;
     bool generate_gdb_information = kIsDebugBuild;
 
+    PassManagerOptions pass_manager_options;
+
     std::string error_msg;
 
     for (int i = 0; i < argc; i++) {
@@ -547,8 +548,6 @@
         }
       } else if (option.starts_with("--oat-location=")) {
         oat_location_ = option.substr(strlen("--oat-location=")).data();
-      } else if (option.starts_with("--bitcode=")) {
-        bitcode_filename_ = option.substr(strlen("--bitcode=")).data();
       } else if (option.starts_with("--image=")) {
         image_filename_ = option.substr(strlen("--image=")).data();
       } else if (option.starts_with("--image-classes=")) {
@@ -573,7 +572,7 @@
       } else if (option.starts_with("--instruction-set=")) {
         StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
         // StringPiece is not necessarily zero-terminated, so need to make a copy and ensure it.
-        std::unique_ptr<char> buf(new char[instruction_set_str.length() + 1]);
+        std::unique_ptr<char[]> buf(new char[instruction_set_str.length() + 1]);
         strncpy(buf.get(), instruction_set_str.data(), instruction_set_str.length());
         buf.get()[instruction_set_str.length()] = 0;
         instruction_set_ = GetInstructionSetFromString(buf.get());
@@ -609,8 +608,6 @@
           compiler_kind_ = Compiler::kQuick;
         } else if (backend_str == "Optimizing") {
           compiler_kind_ = Compiler::kOptimizing;
-        } else if (backend_str == "Portable") {
-          compiler_kind_ = Compiler::kPortable;
         } else {
           Usage("Unknown compiler backend: %s", backend_str.data());
         }
@@ -672,6 +669,8 @@
         dump_timing_ = true;
       } else if (option == "--dump-passes") {
         dump_passes_ = true;
+      } else if (option.starts_with("--dump-cfg=")) {
+        dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
       } else if (option == "--dump-stats") {
         dump_stats_ = true;
       } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
@@ -687,30 +686,30 @@
       } else if (option.starts_with("--top-k-profile-threshold=")) {
         ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold);
       } else if (option == "--print-pass-names") {
-        PassDriverMEOpts::PrintPassNames();
+        pass_manager_options.SetPrintPassNames(true);
       } else if (option.starts_with("--disable-passes=")) {
-        std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
-        PassDriverMEOpts::CreateDefaultPassList(disable_passes);
+        const std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
+        pass_manager_options.SetDisablePassList(disable_passes);
       } else if (option.starts_with("--print-passes=")) {
-        std::string print_passes = option.substr(strlen("--print-passes=")).data();
-        PassDriverMEOpts::SetPrintPassList(print_passes);
+        const std::string print_passes = option.substr(strlen("--print-passes=")).data();
+        pass_manager_options.SetPrintPassList(print_passes);
       } else if (option == "--print-all-passes") {
-        PassDriverMEOpts::SetPrintAllPasses();
+        pass_manager_options.SetPrintAllPasses();
       } else if (option.starts_with("--dump-cfg-passes=")) {
-        std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
-        PassDriverMEOpts::SetDumpPassList(dump_passes_string);
+        const std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
+        pass_manager_options.SetDumpPassList(dump_passes_string);
       } else if (option == "--print-pass-options") {
-        print_pass_options = true;
+        pass_manager_options.SetPrintPassOptions(true);
       } else if (option.starts_with("--pass-options=")) {
-        std::string options = option.substr(strlen("--pass-options=")).data();
-        PassDriverMEOpts::SetOverriddenPassOptions(options);
+        const std::string options = option.substr(strlen("--pass-options=")).data();
+        pass_manager_options.SetOverriddenPassOptions(options);
       } else if (option == "--include-patch-information") {
         include_patch_information = true;
       } else if (option == "--no-include-patch-information") {
         include_patch_information = false;
       } else if (option.starts_with("--verbose-methods=")) {
-        // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages conditional
-        //       on having verbost methods.
+        // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
+        //       conditional on having verbost methods.
         gLogVerbosity.compiler = false;
         Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
       } else if (option.starts_with("--dump-init-failures=")) {
@@ -723,6 +722,16 @@
                      << "failures.";
           init_failure_output_.reset();
         }
+      } else if (option.starts_with("--swap-file=")) {
+        swap_file_name_ = option.substr(strlen("--swap-file=")).data();
+      } else if (option.starts_with("--swap-fd=")) {
+        const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data();
+        if (!ParseInt(swap_fd_str, &swap_fd_)) {
+          Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str);
+        }
+        if (swap_fd_ < 0) {
+          Usage("--swap-fd passed a negative value %d", swap_fd_);
+        }
       } else {
         Usage("Unknown argument %s", option.data());
       }
@@ -855,7 +864,14 @@
     }
 
     if (compiler_filter_string == nullptr) {
-      if (instruction_set_ == kMips64) {
+      if (instruction_set_ == kMips &&
+          reinterpret_cast<const MipsInstructionSetFeatures*>(instruction_set_features_.get())->
+          IsR6()) {
+        // For R6, only interpreter mode is working.
+        // TODO: fix compiler for Mips32r6.
+        compiler_filter_string = "interpret-only";
+      } else if (instruction_set_ == kMips64) {
+        // For Mips64, can only compile in interpreter mode.
         // TODO: fix compiler for Mips64.
         compiler_filter_string = "interpret-only";
       } else if (image_) {
@@ -909,10 +925,6 @@
         break;
     }
 
-    if (print_pass_options) {
-      PassDriverMEOpts::PrintPassOptions();
-    }
-
     compiler_options_.reset(new CompilerOptions(compiler_filter,
                                                 huge_method_threshold,
                                                 large_method_threshold,
@@ -927,12 +939,10 @@
                                                 implicit_so_checks,
                                                 implicit_suspend_checks,
                                                 compile_pic,
-  #ifdef ART_SEA_IR_MODE
-                                                true,
-  #endif
                                                 verbose_methods_.empty() ?
                                                     nullptr :
                                                     &verbose_methods_,
+                                                new PassManagerOptions(pass_manager_options),
                                                 init_failure_output_.get()));
 
     // Done with usage checks, enable watchdog if requested
@@ -960,7 +970,8 @@
     }
   }
 
-  // Check whether the oat output file is writable, and open it for later.
+  // Check whether the oat output file is writable, and open it for later. Also open a swap file,
+  // if a name is given.
   bool OpenFile() {
     bool create_file = !oat_unstripped_.empty();  // as opposed to using open file descriptor
     if (create_file) {
@@ -984,25 +995,51 @@
       oat_file_->Erase();
       return false;
     }
+
+    // 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.
+    //
+    // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We
+    // will immediately unlink to satisfy the swap fd assumption.
+    if (swap_fd_ == -1 && !swap_file_name_.empty()) {
+      std::unique_ptr<File> swap_file(OS::CreateEmptyFile(swap_file_name_.c_str()));
+      if (swap_file.get() == nullptr) {
+        PLOG(ERROR) << "Failed to create swap file: " << swap_file_name_;
+        return false;
+      }
+      swap_fd_ = swap_file->Fd();
+      swap_file->MarkUnchecked();     // We don't we to track this, it will be unlinked immediately.
+      swap_file->DisableAutoClose();  // We'll handle it ourselves, the File object will be
+                                      // released immediately.
+      unlink(swap_file_name_.c_str());
+    }
+
     return true;
   }
 
+  void EraseOatFile() {
+    DCHECK(oat_file_.get() != nullptr);
+    oat_file_->Erase();
+    oat_file_.reset();
+  }
+
   // Set up the environment for compilation. Includes starting the runtime and loading/opening the
   // boot class path.
   bool Setup() {
     TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
     RuntimeOptions runtime_options;
-    std::vector<const DexFile*> boot_class_path;
     art::MemMap::Init();  // For ZipEntry::ExtractToMemMap.
     if (boot_image_option_.empty()) {
-      size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, boot_class_path);
-      if (failure_count > 0) {
-        LOG(ERROR) << "Failed to open some dex files: " << failure_count;
-        return false;
-      }
-      runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+      std::string boot_class_path = "-Xbootclasspath:";
+      boot_class_path += Join(dex_filenames_, ':');
+      runtime_options.push_back(std::make_pair(boot_class_path, nullptr));
+      std::string boot_class_path_locations = "-Xbootclasspath-locations:";
+      boot_class_path_locations += Join(dex_locations_, ':');
+      runtime_options.push_back(std::make_pair(boot_class_path_locations, nullptr));
     } else {
-      runtime_options.push_back(std::make_pair(boot_image_option_.c_str(), nullptr));
+      runtime_options.push_back(std::make_pair(boot_image_option_, nullptr));
     }
     for (size_t i = 0; i < runtime_args_.size(); i++) {
       runtime_options.push_back(std::make_pair(runtime_args_[i], nullptr));
@@ -1080,25 +1117,32 @@
               << error_msg;
           return false;
         }
-        if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &dex_files_)) {
+        if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &opened_dex_files_)) {
           LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location_
               << "': " << error_msg;
           return false;
         }
+        for (auto& dex_file : opened_dex_files_) {
+          dex_files_.push_back(dex_file.get());
+        }
         ATRACE_END();
       } else {
-        size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, dex_files_);
+        size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, &opened_dex_files_);
         if (failure_count > 0) {
           LOG(ERROR) << "Failed to open some dex files: " << failure_count;
           return false;
         }
+        for (auto& dex_file : opened_dex_files_) {
+          dex_files_.push_back(dex_file.get());
+        }
       }
 
       constexpr bool kSaveDexInput = false;
       if (kSaveDexInput) {
         for (size_t i = 0; i < dex_files_.size(); ++i) {
           const DexFile* dex_file = dex_files_[i];
-          std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
+          std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex",
+                                                 getpid(), i));
           std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
           if (tmp_file.get() == nullptr) {
             PLOG(ERROR) << "Failed to open file " << tmp_file_name
@@ -1120,11 +1164,25 @@
       }
     }
 
+    // If we use a swap file, ensure we are above the threshold to make it necessary.
+    if (swap_fd_ != -1) {
+      if (!UseSwap(image_, dex_files_)) {
+        close(swap_fd_);
+        swap_fd_ = -1;
+        LOG(INFO) << "Decided to run without swap.";
+      } else {
+        LOG(INFO) << "Accepted running with swap.";
+      }
+    }
+    // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that.
+
     /*
      * If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
      * Don't bother to check if we're doing the image.
      */
-    if (!image_ && compiler_options_->IsCompilationEnabled() && compiler_kind_ == Compiler::kQuick) {
+    if (!image_ &&
+        compiler_options_->IsCompilationEnabled() &&
+        compiler_kind_ == Compiler::kQuick) {
       size_t num_methods = 0;
       for (size_t i = 0; i != dex_files_.size(); ++i) {
         const DexFile* dex_file = dex_files_[i];
@@ -1150,9 +1208,13 @@
     Thread* self = Thread::Current();
     if (!boot_image_option_.empty()) {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      std::vector<const DexFile*> class_path_files(dex_files_);
-      OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
+      OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
       ScopedObjectAccess soa(self);
+      std::vector<const DexFile*> class_path_files(dex_files_);
+      for (auto& class_path_file : class_path_files_) {
+        class_path_files.push_back(class_path_file.get());
+      }
+
       for (size_t i = 0; i < class_path_files.size(); i++) {
         class_linker->RegisterDexFile(*class_path_files[i]);
       }
@@ -1175,11 +1237,11 @@
                                      thread_count_,
                                      dump_stats_,
                                      dump_passes_,
+                                     dump_cfg_file_name_,
                                      compiler_phases_timings_.get(),
+                                     swap_fd_,
                                      profile_file_));
 
-    driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename_);
-
     driver_->CompileAll(class_loader, dex_files_, timings_);
   }
 
@@ -1301,7 +1363,6 @@
       if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(),
                              oat_file_.get())) {
         LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
-        oat_file_->Erase();
         return false;
       }
     }
@@ -1338,7 +1399,7 @@
       std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
       std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
       size_t buffer_size = 8192;
-      std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
+      std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
       while (true) {
         int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size));
         if (bytes_read <= 0) {
@@ -1347,52 +1408,15 @@
         bool write_ok = out->WriteFully(buffer.get(), bytes_read);
         CHECK(write_ok);
       }
-      if (kUsePortableCompiler) {
-        oat_file_.reset(out.release());
-      } else {
-        if (out->FlushCloseOrErase() != 0) {
-          PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
-          return false;
-        }
+      if (out->FlushCloseOrErase() != 0) {
+        PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
+        return false;
       }
       VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
     }
     return true;
   }
 
-  // Run the ElfStripper. Currently only relevant for the portable compiler.
-  bool Strip() {
-    if (kUsePortableCompiler) {
-      // Portable includes debug symbols unconditionally. If we are not supposed to create them,
-      // strip them now. Quick generates debug symbols only when the flag(s) are set.
-      if (!compiler_options_->GetIncludeDebugSymbols()) {
-        CHECK(oat_file_.get() != nullptr && oat_file_->IsOpened());
-
-        TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_);
-        // Strip unneeded sections for target
-        off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET);
-        CHECK_EQ(0, seek_actual);
-        std::string error_msg;
-        if (!ElfFile::Strip(oat_file_.get(), &error_msg)) {
-          LOG(ERROR) << "Failed to strip elf file: " << error_msg;
-          oat_file_->Erase();
-          return false;
-        }
-
-        if (!FlushCloseOatFile()) {
-          return false;
-        }
-
-        // We wrote the oat file successfully, and want to keep it.
-        VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location_;
-      } else {
-        VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location_;
-      }
-    }
-
-    return true;
-  }
-
   bool FlushOatFile() {
     if (oat_file_.get() != nullptr) {
       TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
@@ -1442,7 +1466,8 @@
  private:
   static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
                              const std::vector<const char*>& dex_locations,
-                             std::vector<const DexFile*>& dex_files) {
+                             std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+    DCHECK(dex_files != nullptr) << "OpenDexFiles out-param is NULL";
     size_t failure_count = 0;
     for (size_t i = 0; i < dex_filenames.size(); i++) {
       const char* dex_filename = dex_filenames[i];
@@ -1453,7 +1478,7 @@
         LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
         continue;
       }
-      if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) {
+      if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
         LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
         ++failure_count;
       }
@@ -1473,10 +1498,12 @@
     return false;
   }
 
-  // Appends to dex_files any elements of class_path that it doesn't already
-  // contain. This will open those dex files as necessary.
+  // Appends to opened_dex_files any elements of class_path that dex_files
+  // doesn't already contain. This will open those dex files as necessary.
   static void OpenClassPathFiles(const std::string& class_path,
-                                 std::vector<const DexFile*>& dex_files) {
+                                 std::vector<const DexFile*> dex_files,
+                                 std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
+    DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is NULL";
     std::vector<std::string> parsed;
     Split(class_path, ':', &parsed);
     // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained.
@@ -1486,7 +1513,7 @@
         continue;
       }
       std::string error_msg;
-      if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, &dex_files)) {
+      if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, opened_dex_files)) {
         LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
       }
     }
@@ -1609,20 +1636,9 @@
   }
 
   void LogCompletionTime() {
-    std::ostringstream mallinfostr;
-#ifdef HAVE_MALLOC_H
-    struct mallinfo info = mallinfo();
-    const size_t allocated_space = static_cast<size_t>(info.uordblks);
-    const size_t free_space = static_cast<size_t>(info.fordblks);
-    mallinfostr << " native alloc=" << PrettySize(allocated_space) << " free="
-        << PrettySize(free_space);
-#endif
-    const ArenaPool* arena_pool = driver_->GetArenaPool();
-    gc::Heap* heap = Runtime::Current()->GetHeap();
     LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
-              << " (threads: " << thread_count_ << ")"
-              << " arena alloc=" << PrettySize(arena_pool->GetBytesAllocated())
-              << " java alloc=" << PrettySize(heap->GetBytesAllocated()) << mallinfostr.str();
+              << " (threads: " << thread_count_ << ") "
+              << driver_->GetMemoryUsageString(kIsDebugBuild || VLOG_IS_ON(compiler));
   }
 
   std::unique_ptr<CompilerOptions> compiler_options_;
@@ -1637,6 +1653,9 @@
   DexFileToMethodInlinerMap method_inliner_map_;
   std::unique_ptr<QuickCompilerCallbacks> callbacks_;
 
+  // Ownership for the class path files.
+  std::vector<std::unique_ptr<const DexFile>> class_path_files_;
+
   // Not a unique_ptr as we want to just exit on non-debug builds, not bringing the runtime down
   // in an orderly fashion. The destructor takes care of deleting this.
   Runtime* runtime_;
@@ -1650,7 +1669,6 @@
   std::string oat_location_;
   std::string oat_filename_;
   int oat_fd_;
-  std::string bitcode_filename_;
   std::vector<const char*> dex_filenames_;
   std::vector<const char*> dex_locations_;
   int zip_fd_;
@@ -1670,12 +1688,16 @@
   bool is_host_;
   std::string android_root_;
   std::vector<const DexFile*> dex_files_;
+  std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
   std::unique_ptr<CompilerDriver> driver_;
   std::vector<std::string> verbose_methods_;
   bool dump_stats_;
   bool dump_passes_;
   bool dump_timing_;
   bool dump_slow_timing_;
+  std::string dump_cfg_file_name_;
+  std::string swap_file_name_;
+  int swap_fd_;
   std::string profile_file_;  // Profile file to use
   TimingLogger* timings_;
   std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
@@ -1684,7 +1706,6 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
 };
 
-const unsigned int WatchDog::kWatchDogWarningSeconds;
 const unsigned int WatchDog::kWatchDogTimeoutSeconds;
 
 static void b13564922() {
@@ -1712,6 +1733,7 @@
 
   // Create the boot.oat.
   if (!dex2oat.CreateOatFile()) {
+    dex2oat.EraseOatFile();
     return EXIT_FAILURE;
   }
 
@@ -1737,11 +1759,6 @@
     return EXIT_FAILURE;
   }
 
-  // Strip, if necessary.
-  if (!dex2oat.Strip()) {
-    return EXIT_FAILURE;
-  }
-
   // FlushClose again, as stripping might have re-opened the oat file.
   if (!dex2oat.FlushCloseOatFile()) {
     return EXIT_FAILURE;
@@ -1756,6 +1773,7 @@
 
   // Create the app oat.
   if (!dex2oat.CreateOatFile()) {
+    dex2oat.EraseOatFile();
     return EXIT_FAILURE;
   }
 
@@ -1781,11 +1799,6 @@
     return EXIT_FAILURE;
   }
 
-  // Strip, if necessary.
-  if (!dex2oat.Strip()) {
-    return EXIT_FAILURE;
-  }
-
   // Flush and close the file.
   if (!dex2oat.FlushCloseOatFile()) {
     return EXIT_FAILURE;
@@ -1813,6 +1826,7 @@
   LOG(INFO) << CommandLine();
 
   if (!dex2oat.Setup()) {
+    dex2oat.EraseOatFile();
     return EXIT_FAILURE;
   }
 
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index 3ad2941..c9aa8c8 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -23,6 +23,7 @@
 	disassembler_arm.cc \
 	disassembler_arm64.cc \
 	disassembler_mips.cc \
+	disassembler_mips64.cc \
 	disassembler_x86.cc
 
 # $(1): target or host
@@ -83,7 +84,7 @@
 
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
-  include external/libcxx/libcxx.mk
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
   # For disassembler_arm64.
   ifeq ($$(art_ndebug_or_debug),debug)
      LOCAL_SHARED_LIBRARIES += libvixld
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index bf68204d..fbc8dbb 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -23,6 +23,7 @@
 #include "disassembler_arm.h"
 #include "disassembler_arm64.h"
 #include "disassembler_mips.h"
+#include "disassembler_mips64.h"
 #include "disassembler_x86.h"
 
 namespace art {
@@ -34,6 +35,8 @@
     return new arm64::DisassemblerArm64(options);
   } else if (instruction_set == kMips) {
     return new mips::DisassemblerMips(options);
+  } else if (instruction_set == kMips64) {
+    return new mips64::DisassemblerMips64(options);
   } else if (instruction_set == kX86) {
     return new x86::DisassemblerX86(options, false);
   } else if (instruction_set == kX86_64) {
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 9243b1a..31e653b 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -21,6 +21,7 @@
 #include <ostream>
 #include <sstream>
 
+#include "arch/arm/registers_arm.h"
 #include "base/logging.h"
 #include "base/stringprintf.h"
 #include "thread.h"
@@ -148,15 +149,15 @@
   ThumbRegister(uint16_t instruction, uint16_t at_bit) : ArmRegister((instruction >> at_bit) & 0x7) {}
 };
 
-struct Rm {
-  explicit Rm(uint32_t instruction) : shift((instruction >> 4) & 0xff), rm(instruction & 0xf) {}
-  uint32_t shift;
+struct RmLslImm2 {
+  explicit RmLslImm2(uint32_t instr) : imm2((instr >> 4) & 0x3), rm(instr & 0xf) {}
+  uint32_t imm2;
   ArmRegister rm;
 };
-std::ostream& operator<<(std::ostream& os, const Rm& r) {
+std::ostream& operator<<(std::ostream& os, const RmLslImm2& r) {
   os << r.rm;
-  if (r.shift != 0) {
-    os << "-shift-" << r.shift;  // TODO
+  if (r.imm2 != 0) {
+    os << ", lsl #" << r.imm2;
   }
   return os;
 }
@@ -397,7 +398,74 @@
   uint64_t bit_a = (imm8 >> 7) & 1;
   uint64_t bit_b = (imm8 >> 6) & 1;
   uint64_t slice = imm8 & 0x3f;
-  return (bit_a << 31) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
+  return (bit_a << 63) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
+}
+
+enum T2LitType {
+  kT2LitInvalid,
+  kT2LitUByte,
+  kT2LitSByte,
+  kT2LitUHalf,
+  kT2LitSHalf,
+  kT2LitUWord,
+  kT2LitSWord,
+  kT2LitHexWord,
+  kT2LitULong,
+  kT2LitSLong,
+  kT2LitHexLong,
+};
+std::ostream& operator<<(std::ostream& os, T2LitType type) {
+  return os << static_cast<int>(type);
+}
+
+void DumpThumb2Literal(std::ostream& args, const uint8_t* instr_ptr, uint32_t U, uint32_t imm32,
+                       T2LitType type) {
+  // Literal offsets (imm32) are not required to be aligned so we may need unaligned access.
+  typedef const int16_t unaligned_int16_t __attribute__ ((aligned (1)));
+  typedef const uint16_t unaligned_uint16_t __attribute__ ((aligned (1)));
+  typedef const int32_t unaligned_int32_t __attribute__ ((aligned (1)));
+  typedef const uint32_t unaligned_uint32_t __attribute__ ((aligned (1)));
+  typedef const int64_t unaligned_int64_t __attribute__ ((aligned (1)));
+  typedef const uint64_t unaligned_uint64_t __attribute__ ((aligned (1)));
+
+  uintptr_t pc = RoundDown(reinterpret_cast<intptr_t>(instr_ptr) + 4, 4);
+  uintptr_t lit_adr = U ? pc + imm32 : pc - imm32;
+  args << "  ; ";
+  switch (type) {
+    case kT2LitUByte:
+      args << *reinterpret_cast<const uint8_t*>(lit_adr);
+      break;
+    case kT2LitSByte:
+      args << *reinterpret_cast<const int8_t*>(lit_adr);
+      break;
+    case kT2LitUHalf:
+      args << *reinterpret_cast<const unaligned_uint16_t*>(lit_adr);
+      break;
+    case kT2LitSHalf:
+      args << *reinterpret_cast<const unaligned_int16_t*>(lit_adr);
+      break;
+    case kT2LitUWord:
+      args << *reinterpret_cast<const unaligned_uint32_t*>(lit_adr);
+      break;
+    case kT2LitSWord:
+      args << *reinterpret_cast<const unaligned_int32_t*>(lit_adr);
+      break;
+    case kT2LitHexWord:
+      args << StringPrintf("0x%08x", *reinterpret_cast<const unaligned_uint32_t*>(lit_adr));
+      break;
+    case kT2LitULong:
+      args << *reinterpret_cast<const unaligned_uint64_t*>(lit_adr);
+      break;
+    case kT2LitSLong:
+      args << *reinterpret_cast<const unaligned_int64_t*>(lit_adr);
+      break;
+    case kT2LitHexLong:
+      args << StringPrintf("0x%" PRIx64, *reinterpret_cast<unaligned_int64_t*>(lit_adr));
+      break;
+    default:
+      LOG(FATAL) << "Invalid type: " << type;
+      break;
+  }
 }
 
 size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
@@ -756,10 +824,7 @@
                 args << d << ", [" << Rn << ", #" << ((U == 1) ? "" : "-")
                      << (imm8 << 2) << "]";
                 if (Rn.r == 15 && U == 1) {
-                  intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
-                  lit_adr = RoundDown(lit_adr, 4) + 4 + (imm8 << 2);
-                  typedef const int64_t unaligned_int64_t __attribute__ ((aligned (2)));
-                  args << StringPrintf("  ; 0x%" PRIx64, *reinterpret_cast<unaligned_int64_t*>(lit_adr));
+                  DumpThumb2Literal(args, instr_ptr, U, imm8 << 2, kT2LitHexLong);
                 }
               } else if (Rn.r == 13 && W == 1 && U == L) {  // VPUSH/VPOP
                 opcode << (L == 1 ? "vpop" : "vpush");
@@ -1227,164 +1292,141 @@
       break;
     case 3:
       switch (op2) {
-        case 0x00: case 0x02: case 0x04: case 0x06:  // 000xxx0
-        case 0x08: case 0x09: case 0x0A: case 0x0C: case 0x0E: {
-          // Store single data item
-          // |111|11|100|000|0|0000|1111|110000|000000|
-          // |5 3|21|098|765|4|3  0|5  2|10   6|5    0|
-          // |---|--|---|---|-|----|----|------|------|
-          // |332|22|222|222|2|1111|1111|110000|000000|
-          // |1 9|87|654|321|0|9  6|5  2|10   6|5    0|
-          // |---|--|---|---|-|----|----|------|------|
-          // |111|11|000|op3|0|    |    |  op4 |      |
-          uint32_t op3 = (instr >> 21) & 7;
-          // uint32_t op4 = (instr >> 6) & 0x3F;
-          switch (op3) {
-            case 0x0: case 0x4: {
-              // {ST,LD}RB Rt,[Rn,#+/-imm12]    - 111 11 00 0 1 00 0 nnnn tttt 1 PUWii ii iiii
-              // {ST,LD}RB Rt,[Rn,#+/-imm8]     - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
-              // {ST,LD}RB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
-              ArmRegister Rn(instr, 16);
-              ArmRegister Rt(instr, 12);
-              opcode << (HasBitSet(instr, 20) ? "ldrb" : "strb");
-              if (HasBitSet(instr, 23)) {
-                uint32_t imm12 = instr & 0xFFF;
-                args << Rt << ", [" << Rn << ",#" << imm12 << "]";
-              } else if ((instr & 0x800) != 0) {
-                uint32_t imm8 = instr & 0xFF;
-                args << Rt << ", [" << Rn << ",#" << imm8 << "]";
-              } else {
-                uint32_t imm2 = (instr >> 4) & 3;
-                ArmRegister Rm(instr, 0);
-                args << Rt << ", [" << Rn << ", " << Rm;
-                if (imm2 != 0) {
-                  args << ", " << "lsl #" << imm2;
-                }
-                args << "]";
-              }
-              break;
-            }
-            case 0x1: case 0x5: {
-              // STRH Rt,[Rn,#+/-imm12]    - 111 11 00 0 1 01 0 nnnn tttt 1 PUWii ii iiii
-              // STRH Rt,[Rn,#+/-imm8]     - 111 11 00 0 0 01 0 nnnn tttt 1 PUWii ii iiii
-              // STRH Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 01 0 nnnn tttt 0 00000 ii mmmm
-              ArmRegister Rn(instr, 16);
-              ArmRegister Rt(instr, 12);
-              opcode << "strh";
-              if (HasBitSet(instr, 23)) {
-                uint32_t imm12 = instr & 0xFFF;
-                args << Rt << ", [" << Rn << ",#" << imm12 << "]";
-              } else if ((instr & 0x800) != 0) {
-                uint32_t imm8 = instr & 0xFF;
-                args << Rt << ", [" << Rn << ",#" << imm8 << "]";
-              } else {
-                uint32_t imm2 = (instr >> 4) & 3;
-                ArmRegister Rm(instr, 0);
-                args << Rt << ", [" << Rn << ", " << Rm;
-                if (imm2 != 0) {
-                  args << ", " << "lsl #" << imm2;
-                }
-                args << "]";
-              }
-              break;
-            }
-            case 0x2: case 0x6: {
-              ArmRegister Rn(instr, 16);
-              ArmRegister Rt(instr, 12);
-              if (op3 == 2) {
-                if ((instr & 0x800) != 0) {
-                  // STR Rt, [Rn, #imm8] - 111 11 000 010 0 nnnn tttt 1PUWiiiiiiii
-                  uint32_t P = (instr >> 10) & 1;
-                  uint32_t U = (instr >> 9) & 1;
-                  uint32_t W = (instr >> 8) & 1;
-                  uint32_t imm8 = instr & 0xFF;
-                  int32_t imm32 = (imm8 << 24) >> 24;  // sign-extend imm8
-                  if (Rn.r == 13 && P == 1 && U == 0 && W == 1 && imm32 == 4) {
-                    opcode << "push";
-                    args << "{" << Rt << "}";
-                  } else if (Rn.r == 15 || (P == 0 && W == 0)) {
-                    opcode << "UNDEFINED";
-                  } else {
-                    if (P == 1 && U == 1 && W == 0) {
-                      opcode << "strt";
-                    } else {
-                      opcode << "str";
-                    }
-                    args << Rt << ", [" << Rn;
-                    if (P == 0 && W == 1) {
-                      args << "], #" << imm32;
-                    } else {
-                      args << ", #" << imm32 << "]";
-                      if (W == 1) {
-                        args << "!";
-                      }
-                    }
-                  }
-                } else {
-                  // STR Rt, [Rn, Rm, LSL #imm2] - 111 11 000 010 0 nnnn tttt 000000iimmmm
-                  ArmRegister Rm(instr, 0);
-                  uint32_t imm2 = (instr >> 4) & 3;
-                  opcode << "str.w";
-                  args << Rt << ", [" << Rn << ", " << Rm;
-                  if (imm2 != 0) {
-                    args << ", lsl #" << imm2;
-                  }
-                  args << "]";
-                }
-              } else if (op3 == 6) {
-                // STR.W Rt, [Rn, #imm12] - 111 11 000 110 0 nnnn tttt iiiiiiiiiiii
-                uint32_t imm12 = instr & 0xFFF;
-                opcode << "str.w";
-                args << Rt << ", [" << Rn << ", #" << imm12 << "]";
-              }
-              break;
-            }
-          }
-
+        case 0x07: case 0x0F: case 0x17: case 0x1F: {  // Explicitly UNDEFINED, A6.3.
+          opcode << "UNDEFINED";
           break;
         }
-        case 0x03: case 0x0B: case 0x11: case 0x13: case 0x19: case 0x1B: {  // 00xx011
-          // Load byte/halfword
-          // |111|11|10|0 0|00|0|0000|1111|110000|000000|
-          // |5 3|21|09|8 7|65|4|3  0|5  2|10   6|5    0|
-          // |---|--|--|---|--|-|----|----|------|------|
-          // |332|22|22|2 2|22|2|1111|1111|110000|000000|
-          // |1 9|87|65|4 3|21|0|9  6|5  2|10   6|5    0|
-          // |---|--|--|---|--|-|----|----|------|------|
-          // |111|11|00|op3|01|1| Rn | Rt | op4  |      |
-          // |111|11| op2       |    |    | imm12       |
-          uint32_t op3 = (instr >> 23) & 3;
+        case 0x06: case 0x0E: {  // "Store single data item" undefined opcodes, A6.3.10.
+          opcode << "UNDEFINED [store]";
+          break;
+        }
+        case 0x15: case 0x1D: {  // "Load word" undefined opcodes, A6.3.7.
+          opcode << "UNDEFINED [load]";
+          break;
+        }
+        case 0x10: case 0x12: case 0x14: case 0x16: case 0x18: case 0x1A: case 0x1C: case 0x1E: {
+          opcode << "UNKNOWN " << op2 << " [SIMD]";
+          break;
+        }
+        case 0x01: case 0x00: case 0x09: case 0x08:   // {LD,ST}RB{,T}
+        case 0x03: case 0x02: case 0x0B: case 0x0A:   // {LD,ST}RH{,T}
+        case 0x05: case 0x04: case 0x0D: case 0x0C:   // {LD,ST}R{,T}
+        case 0x11:            case 0x19:              // LDRSB{,T} (no signed store)
+        case 0x13:            case 0x1B: {            // LDRSH{,T} (no signed store)
+          // Load:
+          // (Store is the same except that l==0 and always s==0 below.)
+          //                       00s.whl (sign, word, half, load)
+          // LDR{S}B  imm12: 11111|00s1001| Rn | Rt |imm12             (0x09)
+          // LDR{S}B   imm8: 11111|00s0001| Rn | Rt |1PUW|imm8         (0x01)
+          // LDR{S}BT  imm8: 11111|00s0001| Rn | Rt |1110|imm8         (0x01)
+          // LDR{S}B    lit: 11111|00sU001|1111| Rt |imm12             (0x01/0x09)
+          // LDR{S}B    reg: 11111|00s0001| Rn | Rt |000000|imm2| Rm   (0x01)
+          // LDR{S}H  imm12: 11111|00s1011| Rn | Rt |imm12             (0x0B)
+          // LDR{S}H   imm8: 11111|00s0011| Rn | Rt |1PUW|imm8         (0x03)
+          // LDR{S}HT  imm8: 11111|00s0011| Rn | Rt |1110|imm8         (0x03)
+          // LDR{S}H    lit: 11111|00sU011|1111| Rt |imm12             (0x03/0x0B)
+          // LDR{S}H    reg: 11111|00s0011| Rn | Rt |000000|imm2| Rm   (0x03)
+          // LDR      imm12: 11111|0001101| Rn | Rt |imm12             (0x0D)
+          // LDR       imm8: 11111|0000101| Rn | Rt |1PUW|imm8         (0x05)
+          // LDRT      imm8: 11111|0000101| Rn | Rt |1110|imm8         (0x05)
+          // LDR        lit: 11111|000U101|1111| Rt |imm12             (0x05/0x0D)
+          // LDR        reg: 11111|0000101| Rn | Rt |000000|imm2| Rm   (0x05)
+          //
+          // If Rt == 15, instead of load we have preload:
+          // PLD{W}   imm12: 11111|00010W1| Rn |1111|imm12             (0x09/0x0B)
+          // PLD{W}    imm8: 11111|00000W1| Rn |1111|1100|imm8         (0x01/0x03); -imm8
+          // PLD        lit: 11111|000U001|1111|1111|imm12             (0x01/0x09)
+          // PLD{W}     reg: 11111|00000W1| Rn |1111|000000|imm2| Rm   (0x01/0x03)
+          // PLI      imm12: 11111|0011001| Rn |1111|imm12             (0x19)
+          // PLI       imm8: 11111|0010001| Rn |1111|1100|imm8         (0x11); -imm8
+          // PLI        lit: 11111|001U001|1111|1111|imm12             (0x01/0x09)
+          // PLI        reg: 11111|0010001| Rn |1111|000000|imm2| Rm   (0x01/0x03)
+
+          bool is_load = HasBitSet(instr, 20);
+          bool is_half = HasBitSet(instr, 21);  // W for PLD/PLDW.
+          bool is_word = HasBitSet(instr, 22);
+          bool is_signed = HasBitSet(instr, 24);
           ArmRegister Rn(instr, 16);
           ArmRegister Rt(instr, 12);
-          if (Rt.r != 15) {
-            if (op3 == 1) {
-              // LDRH.W Rt, [Rn, #imm12]       - 111 11 00 01 011 nnnn tttt iiiiiiiiiiii
-              uint32_t imm12 = instr & 0xFFF;
-              opcode << "ldrh.w";
-              args << Rt << ", [" << Rn << ", #" << imm12 << "]";
-              if (Rn.r == 9) {
-                args << "  ; ";
-                Thread::DumpThreadOffset<4>(args, imm12);
-              } else if (Rn.r == 15) {
-                intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
-                lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
-                args << StringPrintf("  ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
+          uint32_t imm12 = instr & 0xFFF;
+          uint32_t U = (instr >> 23) & 1;  // U for imm12
+          uint32_t imm8 = instr & 0xFF;
+          uint32_t op4 = (instr >> 8) & 0xF;  // 1PUW for imm8
+          if (Rt.r == PC && is_load && !is_word) {
+            // PLD, PLDW, PLI
+            const char* pld_pli = (is_signed ? "pli" : "pld");
+            const char* w = (is_half ? "w" : "");
+            if (is_signed && !is_half) {
+              opcode << "UNDEFINED [PLI+W]";
+            } else if (Rn.r == PC || U != 0u) {
+              opcode << pld_pli << w;
+              args << "[" << Rn << ", #" << (U != 0u ? "" : "-") << imm12 << "]";
+              if (Rn.r == PC && is_half) {
+                args << " (UNPREDICTABLE)";
               }
-            } else if (op3 == 3) {
-              // LDRSH.W Rt, [Rn, #imm12]      - 111 11 00 11 011 nnnn tttt iiiiiiiiiiii
-              // LDRSB.W Rt, [Rn, #imm12]      - 111 11 00 11 001 nnnn tttt iiiiiiiiiiii
-              uint32_t imm12 = instr & 0xFFF;
-              opcode << (HasBitSet(instr, 20) ? "ldrsb.w" : "ldrsh.w");
-              args << Rt << ", [" << Rn << ", #" << imm12 << "]";
-              if (Rn.r == 9) {
-                args << "  ; ";
-                Thread::DumpThreadOffset<4>(args, imm12);
-              } else if (Rn.r == 15) {
-                intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
-                lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
-                args << StringPrintf("  ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
-              }
+            } else if ((instr & 0xFC0) == 0) {
+              opcode << pld_pli << w;
+              RmLslImm2 Rm(instr);
+              args << "[" << Rn << ", " << Rm << "]";
+            } else if (op4 == 0xC) {
+              opcode << pld_pli << w;
+              args << "[" << Rn << ", #-" << imm8 << "]";
+            } else {
+              opcode << "UNDEFINED [~" << pld_pli << "]";
             }
+            break;
+          }
+          const char* ldr_str = is_load ? "ldr" : "str";
+          const char* sign = is_signed ? "s" : "";
+          const char* type = is_word ? "" : is_half ? "h" : "b";
+          bool unpred = (Rt.r == SP && !is_word) || (Rt.r == PC && !is_load);
+          if (Rn.r == PC && !is_load) {
+            opcode << "UNDEFINED [STR-lit]";
+            unpred = false;
+          } else if (Rn.r == PC || U != 0u) {
+            // Load/store with imm12 (load literal if Rn.r == PC; there's no store literal).
+            opcode << ldr_str << sign << type << ".w";
+            args << Rt << ", [" << Rn << ", #" << (U != 0u ? "" : "-") << imm12 << "]";
+            if (Rn.r == TR && is_load) {
+              args << "  ; ";
+              Thread::DumpThreadOffset<4>(args, imm12);
+            } else if (Rn.r == PC) {
+              T2LitType lit_type[] = {
+                  kT2LitUByte, kT2LitUHalf, kT2LitHexWord, kT2LitInvalid,
+                  kT2LitUByte, kT2LitUHalf, kT2LitHexWord, kT2LitInvalid,
+                  kT2LitSByte, kT2LitSHalf, kT2LitInvalid, kT2LitInvalid,
+                  kT2LitSByte, kT2LitSHalf, kT2LitInvalid, kT2LitInvalid,
+              };
+              DCHECK_LT(op2 >> 1, arraysize(lit_type));
+              DCHECK_NE(lit_type[op2 >> 1], kT2LitInvalid);
+              DumpThumb2Literal(args, instr_ptr, U, imm12, lit_type[op2 >> 1]);
+            }
+          } else if ((instr & 0xFC0) == 0) {
+            opcode << ldr_str << sign << type << ".w";
+            RmLslImm2 Rm(instr);
+            args << Rt << ", [" << Rn << ", " << Rm << "]";
+            unpred = unpred || (Rm.rm.r == SP) || (Rm.rm.r == PC);
+          } else if (is_word && Rn.r == SP && imm8 == 4 && op4 == (is_load ? 0xB : 0xD)) {
+            opcode << (is_load ? "pop" : "push") << ".w";
+            args << Rn;
+            unpred = unpred || (Rn.r == SP);
+          } else if ((op4 & 5) == 0) {
+            opcode << "UNDEFINED [P = W = 0 for " << ldr_str << "]";
+            unpred = false;
+          } else {
+            uint32_t P = (instr >> 10) & 1;
+            U = (instr >> 9) & 1;
+            uint32_t W = (instr >> 8) & 1;
+            bool pre_index = (P != 0 && W == 1);
+            bool post_index = (P == 0 && W == 1);
+            const char* t = (P != 0 && U != 0 && W == 0) ? "t" : "";  // Unprivileged load/store?
+            opcode << ldr_str << sign << type << t << ".w";
+            args << Rt << ", [" << Rn << (post_index ? "]" : "") << ", #" << (U != 0 ? "" : "-")
+                << imm8 << (post_index ? "" : "]") << (pre_index ? "!" : "");
+            unpred = (W != 0 && Rn.r == Rt.r);
+          }
+          if (unpred) {
+            args << " (UNPREDICTABLE)";
           }
           break;
         }
@@ -1413,75 +1455,6 @@
           }  // else unknown instruction
           break;
         }
-        case 0x05: case 0x0D: case 0x15: case 0x1D: {  // 00xx101
-          // Load word
-          // |111|11|10|0 0|00|0|0000|1111|110000|000000|
-          // |5 3|21|09|8 7|65|4|3  0|5  2|10   6|5    0|
-          // |---|--|--|---|--|-|----|----|------|------|
-          // |332|22|22|2 2|22|2|1111|1111|110000|000000|
-          // |1 9|87|65|4 3|21|0|9  6|5  2|10   6|5    0|
-          // |---|--|--|---|--|-|----|----|------|------|
-          // |111|11|00|op3|10|1| Rn | Rt | op4  |      |
-          // |111|11| op2       |    |    | imm12       |
-          uint32_t op3 = (instr >> 23) & 3;
-          uint32_t op4 = (instr >> 6) & 0x3F;
-          ArmRegister Rn(instr, 16);
-          ArmRegister Rt(instr, 12);
-          if (op3 == 1 || Rn.r == 15) {
-            // LDR.W Rt, [Rn, #imm12]          - 111 11 00 00 101 nnnn tttt iiiiiiiiiiii
-            // LDR.W Rt, [PC, #imm12]          - 111 11 00 0x 101 1111 tttt iiiiiiiiiiii
-            uint32_t imm12 = instr & 0xFFF;
-            opcode << "ldr.w";
-            args << Rt << ", [" << Rn << ", #" << imm12 << "]";
-            if (Rn.r == 9) {
-              args << "  ; ";
-              Thread::DumpThreadOffset<4>(args, imm12);
-            } else if (Rn.r == 15) {
-              intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
-              lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
-              args << StringPrintf("  ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
-            }
-          } else if (op4 == 0) {
-            // LDR.W Rt, [Rn, Rm{, LSL #imm2}] - 111 11 00 00 101 nnnn tttt 000000iimmmm
-            uint32_t imm2 = (instr >> 4) & 0xF;
-            ArmRegister rm(instr, 0);
-            opcode << "ldr.w";
-            args << Rt << ", [" << Rn << ", " << rm;
-            if (imm2 != 0) {
-              args << ", lsl #" << imm2;
-            }
-            args << "]";
-          } else {
-            bool p = (instr & (1 << 10)) != 0;
-            bool w = (instr & (1 << 8)) != 0;
-            bool u = (instr & (1 << 9)) != 0;
-            if (p && u && !w) {
-              // LDRT Rt, [Rn, #imm8]            - 111 11 00 00 101 nnnn tttt 1110iiiiiiii
-              uint32_t imm8 = instr & 0xFF;
-              opcode << "ldrt";
-              args << Rt << ", [" << Rn << ", #" << imm8 << "]";
-            } else if (Rn.r == 13 && !p && u && w && (instr & 0xff) == 4) {
-              // POP
-              opcode << "pop";
-              args << "{" << Rt << "}";
-           } else {
-              bool wback = !p || w;
-              uint32_t offset = (instr & 0xff);
-              opcode << "ldr.w";
-              args << Rt << ",";
-              if (p && !wback) {
-                args << "[" << Rn << ", #" << offset << "]";
-              } else if (p && wback) {
-                args << "[" << Rn << ", #" << offset << "]!";
-              } else if (!p && wback) {
-                args << "[" << Rn << "], #" << offset;
-              } else {
-                LOG(FATAL) << p << " " << w;
-              }
-            }
-          }
-          break;
-        }
       default:      // more formats
         if ((op2 >> 4) == 2) {      // 010xxxx
           // data processing (register)
@@ -1498,7 +1471,7 @@
         } else if ((op2 >> 3) == 6) {       // 0110xxx
           // Multiply, multiply accumulate, and absolute difference
           op1 = (instr >> 20) & 0x7;
-          op2 = (instr >> 4) & 0x2;
+          op2 = (instr >> 4) & 0x1;
           ArmRegister Ra(instr, 12);
           ArmRegister Rn(instr, 16);
           ArmRegister Rm(instr, 0);
@@ -1808,6 +1781,23 @@
           DumpBranchTarget(args, instr_ptr + 4, imm32);
           break;
         }
+        case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+        case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: {
+          opcode << "push";
+          args << RegisterList((instr & 0xFF) | ((instr & 0x100) << 6));
+          break;
+        }
+        case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+        case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: {
+          opcode << "pop";
+          args << RegisterList((instr & 0xFF) | ((instr & 0x100) << 7));
+          break;
+        }
+        case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: {
+          opcode << "bkpt";
+          args << "#" << (instr & 0xFF);
+          break;
+        }
         case 0x50: case 0x51:    // 101000x
         case 0x52: case 0x53:    // 101001x
         case 0x56: case 0x57: {  // 101011x
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index bd3bebf..4ff44b4 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,7 +18,7 @@
 
 #include <inttypes.h>
 
-#include <ostream>
+#include <sstream>
 
 #include "base/logging.h"
 #include "base/stringprintf.h"
@@ -27,22 +27,23 @@
 namespace art {
 namespace arm64 {
 
+// This enumeration should mirror the declarations in
+// runtime/arch/arm64/registers_arm64.h. We do not include that file to
+// avoid a dependency on libart.
+enum {
+  TR  = 18,
+  ETR = 21,
+  IP0 = 16,
+  IP1 = 17,
+  FP  = 29,
+  LR  = 30
+};
+
 void CustomDisassembler::AppendRegisterNameToOutput(
     const vixl::Instruction* instr,
     const vixl::CPURegister& reg) {
   USE(instr);
   if (reg.IsRegister()) {
-    // This enumeration should mirror the declarations in
-    // runtime/arch/arm64/registers_arm64.h. We do not include that file to
-    // avoid a dependency on libart.
-    enum {
-      TR  = 18,
-      ETR = 21,
-      IP0 = 16,
-      IP1 = 17,
-      FP  = 29,
-      LR  = 30
-    };
     switch (reg.code()) {
       case IP0: AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0"); return;
       case IP1: AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1"); return;
@@ -66,16 +67,7 @@
     return;
   }
 
-  char* buffer = buffer_;
-  char* buffer_end = buffer_ + buffer_size_;
-
-  // Find the end position in the buffer.
-  while ((*buffer != 0) && (buffer < buffer_end)) {
-    ++buffer;
-  }
-
   void* data_address = instr->LiteralAddress<void*>();
-  ptrdiff_t buf_size_remaining = buffer_end - buffer;
   vixl::Instr op = instr->Mask(vixl::LoadLiteralMask);
 
   switch (op) {
@@ -84,14 +76,14 @@
     case vixl::LDRSW_x_lit: {
       int64_t data = op == vixl::LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address)
                                            : *reinterpret_cast<int32_t*>(data_address);
-      snprintf(buffer, buf_size_remaining, " (0x%" PRIx64 " / %" PRId64 ")", data, data);
+      AppendToOutput(" (0x%" PRIx64 " / %" PRId64 ")", data, data);
       break;
     }
     case vixl::LDR_s_lit:
     case vixl::LDR_d_lit: {
       double data = (op == vixl::LDR_s_lit) ? *reinterpret_cast<float*>(data_address)
                                             : *reinterpret_cast<double*>(data_address);
-      snprintf(buffer, buf_size_remaining, " (%g)", data);
+      AppendToOutput(" (%g)", data);
       break;
     }
     default:
@@ -99,6 +91,17 @@
   }
 }
 
+void CustomDisassembler::VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) {
+  Disassembler::VisitLoadStoreUnsignedOffset(instr);
+
+  if (instr->Rn() == TR) {
+    int64_t offset = instr->ImmLSUnsigned() << instr->SizeLS();
+    std::ostringstream tmp_stream;
+    Thread::DumpThreadOffset<8>(tmp_stream, static_cast<uint32_t>(offset));
+    AppendToOutput(" (%s)", tmp_stream.str().c_str());
+  }
+}
+
 size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
   const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin);
   decoder.Decode(instr);
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index a370b8d..57f11c8 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -34,11 +34,14 @@
       vixl::Disassembler(), read_literals_(read_literals) {}
 
   // Use register aliases in the disassembly.
-  virtual void AppendRegisterNameToOutput(const vixl::Instruction* instr,
-                                          const vixl::CPURegister& reg) OVERRIDE;
+  void AppendRegisterNameToOutput(const vixl::Instruction* instr,
+                                  const vixl::CPURegister& reg) OVERRIDE;
 
   // Improve the disassembly of literal load instructions.
-  virtual void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+  void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+
+  // Improve the disassembly of thread offset.
+  void VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) OVERRIDE;
 
  private:
   // Indicate if the disassembler should read data loaded from literal pools.
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 97c06f1..7442c70 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -138,7 +138,9 @@
   { kITypeMask, 41u << kOpcodeShift, "sh", "TO", },
   { kITypeMask, 43u << kOpcodeShift, "sw", "TO", },
   { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },
+  { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", },
   { kITypeMask, 57u << kOpcodeShift, "swc1", "tO", },
+  { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", },
 
   // Floating point.
   { kFpMask,                kCop1 | 0, "add", "fdst" },
diff --git a/disassembler/disassembler_mips64.cc b/disassembler/disassembler_mips64.cc
new file mode 100644
index 0000000..2d3239f
--- /dev/null
+++ b/disassembler/disassembler_mips64.cc
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+#include "disassembler_mips64.h"
+
+#include <ostream>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "thread.h"
+
+namespace art {
+namespace mips64 {
+
+
+struct Mips64Instruction {
+  uint32_t    mask;
+  uint32_t    value;
+  const char* name;
+  const char* args_fmt;
+
+  bool Matches(uint32_t instruction) const {
+    return (instruction & mask) == value;
+  }
+};
+
+static const uint32_t kOpcodeShift = 26;
+static const uint32_t kCop1 = (17 << kOpcodeShift);
+static const uint32_t kITypeMask = (0x3f << kOpcodeShift);
+static const uint32_t kJTypeMask = (0x3f << kOpcodeShift);
+static const uint32_t kRTypeMask = ((0x3f << kOpcodeShift) | (0x3f));
+static const uint32_t kSpecial2Mask = (0x3f << kOpcodeShift);
+static const uint32_t kFpMask = kRTypeMask;
+
+static const Mips64Instruction gMips64Instructions[] = {
+  // "sll r0, r0, 0" is the canonical "nop", used in delay slots.
+  { 0xffffffff, 0, "nop", "" },
+
+  // R-type instructions.
+  { kRTypeMask, 0, "sll", "DTA", },
+  // 0, 1, movci
+  { kRTypeMask, 2, "srl", "DTA", },
+  { kRTypeMask, 3, "sra", "DTA", },
+  { kRTypeMask, 4, "sllv", "DTS", },
+  { kRTypeMask, 6, "srlv", "DTS", },
+  { kRTypeMask, 7, "srav", "DTS", },
+  { kRTypeMask, 8, "jr", "S", },
+  // rd = 31 is implicit.
+  { kRTypeMask | (0x1f << 11), 9 | (31 << 11), "jalr", "S", },
+  { kRTypeMask, 9, "jalr", "DS", },  // General case.
+  { kRTypeMask | (0x1f << 6), 10, "movz", "DST", },
+  { kRTypeMask | (0x1f << 6), 11, "movn", "DST", },
+  { kRTypeMask, 12, "syscall", "", },  // TODO: code
+  { kRTypeMask, 13, "break", "", },  // TODO: code
+  { kRTypeMask, 15, "sync", "", },  // TODO: type
+  { kRTypeMask, 16, "mfhi", "D", },
+  { kRTypeMask, 17, "mthi", "S", },
+  { kRTypeMask, 18, "mflo", "D", },
+  { kRTypeMask, 19, "mtlo", "S", },
+  { kRTypeMask, 24, "mult", "ST", },
+  { kRTypeMask, 25, "multu", "ST", },
+  { kRTypeMask, 26, "div", "ST", },
+  { kRTypeMask, 27, "divu", "ST", },
+  { kRTypeMask, 32, "add", "DST", },
+  { kRTypeMask, 33, "addu", "DST", },
+  { kRTypeMask, 34, "sub", "DST", },
+  { kRTypeMask, 35, "subu", "DST", },
+  { kRTypeMask, 36, "and", "DST", },
+  { kRTypeMask, 37, "or", "DST", },
+  { kRTypeMask, 38, "xor", "DST", },
+  { kRTypeMask, 39, "nor", "DST", },
+  { kRTypeMask, 42, "slt", "DST", },
+  { kRTypeMask, 43, "sltu", "DST", },
+  { kRTypeMask, 44, "dadd", "DST", },
+  { kRTypeMask, 45, "daddu", "DST", },
+  { kRTypeMask, 46, "dsub", "DST", },
+  { kRTypeMask, 47, "dsubu", "DST", },
+  // 0, 48, tge
+  // 0, 49, tgeu
+  // 0, 50, tlt
+  // 0, 51, tltu
+  // 0, 52, teq
+  // 0, 54, tne
+
+  // SPECIAL2
+  { kSpecial2Mask | 0x7ff, (28 << kOpcodeShift) | 2, "mul", "DST" },
+  { kSpecial2Mask | 0x7ff, (28 << kOpcodeShift) | 32, "clz", "DS" },
+  { kSpecial2Mask | 0x7ff, (28 << kOpcodeShift) | 36, "dclz", "DS" },
+  { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 0, "madd", "ST" },
+  { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 1, "maddu", "ST" },
+  { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 2, "mul", "DST" },
+  { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 4, "msub", "ST" },
+  { kSpecial2Mask | 0xffff, (28 << kOpcodeShift) | 5, "msubu", "ST" },
+  { kSpecial2Mask | 0x3f, (28 << kOpcodeShift) | 0x3f, "sdbbp", "" },
+
+  // J-type instructions.
+  { kJTypeMask, 2 << kOpcodeShift, "j", "L" },
+  { kJTypeMask, 3 << kOpcodeShift, "jal", "L" },
+
+  // I-type instructions.
+  { kITypeMask, 4 << kOpcodeShift, "beq", "STB" },
+  { kITypeMask, 5 << kOpcodeShift, "bne", "STB" },
+  { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (1 << 16), "bgez", "SB" },
+  { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (0 << 16), "bltz", "SB" },
+  { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (2 << 16), "bltzl", "SB" },
+  { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (16 << 16), "bltzal", "SB" },
+  { kITypeMask | (0x1f << 16), 1 << kOpcodeShift | (18 << 16), "bltzall", "SB" },
+  { kITypeMask | (0x1f << 16), 6 << kOpcodeShift | (0 << 16), "blez", "SB" },
+  { kITypeMask | (0x1f << 16), 7 << kOpcodeShift | (0 << 16), "bgtz", "SB" },
+
+  { 0xffff0000, (4 << kOpcodeShift), "b", "B" },
+  { 0xffff0000, (1 << kOpcodeShift) | (17 << 16), "bal", "B" },
+
+  { kITypeMask, 8 << kOpcodeShift, "addi", "TSi", },
+  { kITypeMask, 9 << kOpcodeShift, "addiu", "TSi", },
+  { kITypeMask, 10 << kOpcodeShift, "slti", "TSi", },
+  { kITypeMask, 11 << kOpcodeShift, "sltiu", "TSi", },
+  { kITypeMask, 12 << kOpcodeShift, "andi", "TSi", },
+  { kITypeMask, 13 << kOpcodeShift, "ori", "TSi", },
+  { kITypeMask, 14 << kOpcodeShift, "ori", "TSi", },
+  { kITypeMask, 15 << kOpcodeShift, "lui", "TI", },
+
+  { kITypeMask, 24 << kOpcodeShift, "daddi", "TSi", },
+  { kITypeMask, 25 << kOpcodeShift, "daddiu", "TSi", },
+
+
+  { kITypeMask, 32u << kOpcodeShift, "lb", "TO", },
+  { kITypeMask, 33u << kOpcodeShift, "lh", "TO", },
+  { kITypeMask, 35u << kOpcodeShift, "lw", "TO", },
+  { kITypeMask, 36u << kOpcodeShift, "lbu", "TO", },
+  { kITypeMask, 37u << kOpcodeShift, "lhu", "TO", },
+  { kITypeMask, 40u << kOpcodeShift, "sb", "TO", },
+  { kITypeMask, 41u << kOpcodeShift, "sh", "TO", },
+  { kITypeMask, 43u << kOpcodeShift, "sw", "TO", },
+  { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },
+  { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", },
+  { kITypeMask, 55u << kOpcodeShift, "ld", "TO", },
+  { kITypeMask, 57u << kOpcodeShift, "swc1", "tO", },
+  { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", },
+  { kITypeMask, 63u << kOpcodeShift, "sd", "TO", },
+
+  // Floating point.
+  { kFpMask,                kCop1 | 0, "add", "fdst" },
+  { kFpMask,                kCop1 | 1, "sub", "fdst" },
+  { kFpMask,                kCop1 | 2, "mul", "fdst" },
+  { kFpMask,                kCop1 | 3, "div", "fdst" },
+  { kFpMask | (0x1f << 16), kCop1 | 4, "sqrt", "fdst" },
+  { kFpMask | (0x1f << 16), kCop1 | 5, "abs", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 6, "mov", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 7, "neg", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 8, "round.l", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 9, "trunc.l", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 10, "ceil.l", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 11, "floor.l", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 12, "round.w", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 13, "trunc.w", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 14, "ceil.w", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 15, "floor.w", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 32, "cvt.s", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 33, "cvt.d", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 36, "cvt.w", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 37, "cvt.l", "fds" },
+  { kFpMask | (0x1f << 16), kCop1 | 38, "cvt.ps", "fds" },
+};
+
+static uint32_t ReadU32(const uint8_t* ptr) {
+  // We only support little-endian MIPS64.
+  return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+}
+
+static void DumpMips64(std::ostream& os, const uint8_t* instr_ptr) {
+  uint32_t instruction = ReadU32(instr_ptr);
+
+  uint32_t rs = (instruction >> 21) & 0x1f;  // I-type, R-type.
+  uint32_t rt = (instruction >> 16) & 0x1f;  // I-type, R-type.
+  uint32_t rd = (instruction >> 11) & 0x1f;  // R-type.
+  uint32_t sa = (instruction >>  6) & 0x1f;  // R-type.
+
+  std::string opcode;
+  std::ostringstream args;
+
+  // TODO: remove this!
+  uint32_t op = (instruction >> 26) & 0x3f;
+  uint32_t function = (instruction & 0x3f);  // R-type.
+  opcode = StringPrintf("op=%d fn=%d", op, function);
+
+  for (size_t i = 0; i < arraysize(gMips64Instructions); ++i) {
+    if (gMips64Instructions[i].Matches(instruction)) {
+      opcode = gMips64Instructions[i].name;
+      for (const char* args_fmt = gMips64Instructions[i].args_fmt; *args_fmt; ++args_fmt) {
+        switch (*args_fmt) {
+          case 'A':  // sa (shift amount).
+            args << sa;
+            break;
+          case 'B':  // Branch offset.
+            {
+              int32_t offset = static_cast<int16_t>(instruction & 0xffff);
+              offset <<= 2;
+              offset += 4;  // Delay slot.
+              args << StringPrintf("%p  ; %+d", instr_ptr + offset, offset);
+            }
+            break;
+          case 'D': args << 'r' << rd; break;
+          case 'd': args << 'f' << rd; break;
+          case 'f':  // Floating point "fmt".
+            {
+              size_t fmt = (instruction >> 21) & 0x7;  // TODO: other fmts?
+              switch (fmt) {
+                case 0: opcode += ".s"; break;
+                case 1: opcode += ".d"; break;
+                case 4: opcode += ".w"; break;
+                case 5: opcode += ".l"; break;
+                case 6: opcode += ".ps"; break;
+                default: opcode += ".?"; break;
+              }
+              continue;  // No ", ".
+            }
+            break;
+          case 'I':  // Upper 16-bit immediate.
+            args << reinterpret_cast<void*>((instruction & 0xffff) << 16);
+            break;
+          case 'i':  // Sign-extended lower 16-bit immediate.
+            args << static_cast<int16_t>(instruction & 0xffff);
+            break;
+          case 'L':  // Jump label.
+            {
+              // TODO: is this right?
+              uint32_t instr_index = (instruction & 0x1ffffff);
+              uint32_t target = (instr_index << 2);
+              target |= (reinterpret_cast<uintptr_t>(instr_ptr + 4)
+                        & 0xf0000000);
+              args << reinterpret_cast<void*>(target);
+            }
+            break;
+          case 'O':  // +x(rs)
+            {
+              int32_t offset = static_cast<int16_t>(instruction & 0xffff);
+              args << StringPrintf("%+d(r%d)", offset, rs);
+              if (rs == 17) {
+                args << "  ; ";
+                Thread::DumpThreadOffset<8>(args, offset);
+              }
+            }
+            break;
+          case 'S': args << 'r' << rs; break;
+          case 's': args << 'f' << rs; break;
+          case 'T': args << 'r' << rt; break;
+          case 't': args << 'f' << rt; break;
+        }
+        if (*(args_fmt + 1)) {
+          args << ", ";
+        }
+      }
+      break;
+    }
+  }
+
+  os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str())
+     << args.str() << '\n';
+}
+
+size_t DisassemblerMips64::Dump(std::ostream& os, const uint8_t* begin) {
+  DumpMips64(os, begin);
+  return 4;
+}
+
+void DisassemblerMips64::Dump(std::ostream& os, const uint8_t* begin,
+                            const uint8_t* end) {
+  for (const uint8_t* cur = begin; cur < end; cur += 4) {
+    DumpMips64(os, cur);
+  }
+}
+
+}  // namespace mips64
+}  // namespace art
diff --git a/disassembler/disassembler_mips64.h b/disassembler/disassembler_mips64.h
new file mode 100644
index 0000000..06efdc8
--- /dev/null
+++ b/disassembler/disassembler_mips64.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_MIPS64_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_MIPS64_H_
+
+#include <vector>
+
+#include "disassembler.h"
+
+namespace art {
+namespace mips64 {
+
+class DisassemblerMips64 FINAL : public Disassembler {
+ public:
+  explicit DisassemblerMips64(DisassemblerOptions* options) : Disassembler(options) {}
+
+  size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE;
+  void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DisassemblerMips64);
+};
+
+}  // namespace mips64
+}  // namespace art
+
+#endif  // ART_DISASSEMBLER_DISASSEMBLER_MIPS64_H_
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index b3af8a6..203488d 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -80,8 +80,6 @@
   }
 }
 
-enum RegFile { GPR, MMX, SSE };
-
 static void DumpAnyReg(std::ostream& os, uint8_t rex, size_t reg,
                        bool byte_operand, uint8_t size_override, RegFile reg_file) {
   if (reg_file == GPR) {
@@ -121,12 +119,6 @@
   DumpAddrReg(os, rex, reg_num);
 }
 
-static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) {
-  bool rex_x = (rex & REX_X) != 0;
-  uint8_t reg_num = rex_x ? (reg + 8) : reg;
-  DumpAddrReg(os, rex, reg_num);
-}
-
 static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg,
                           bool byte_operand, uint8_t size_override) {
   bool rex_b = (rex & REX_B) != 0;
@@ -155,6 +147,102 @@
   }
 }
 
+// Do not inline to avoid Clang stack frame problems. b/18733806
+NO_INLINE
+static std::string DumpCodeHex(const uint8_t* begin, const uint8_t* end) {
+  std::stringstream hex;
+  for (size_t i = 0; begin + i < end; ++i) {
+    hex << StringPrintf("%02X", begin[i]);
+  }
+  return hex.str();
+}
+
+std::string DisassemblerX86::DumpAddress(uint8_t mod, uint8_t rm, uint8_t rex64, uint8_t rex_w,
+                                         bool no_ops, bool byte_operand, bool byte_second_operand,
+                                         uint8_t* prefix, bool load, RegFile src_reg_file,
+                                         RegFile dst_reg_file, const uint8_t** instr,
+                                         uint32_t* address_bits) {
+  std::ostringstream address;
+  if (mod == 0 && rm == 5) {
+    if (!supports_rex_) {  // Absolute address.
+      *address_bits = *reinterpret_cast<const uint32_t*>(*instr);
+      address << StringPrintf("[0x%x]", *address_bits);
+    } else {  // 64-bit RIP relative addressing.
+      address << StringPrintf("[RIP + 0x%x]",  *reinterpret_cast<const uint32_t*>(*instr));
+    }
+    (*instr) += 4;
+  } else if (rm == 4 && mod != 3) {  // SIB
+    uint8_t sib = **instr;
+    (*instr)++;
+    uint8_t scale = (sib >> 6) & 3;
+    uint8_t index = (sib >> 3) & 7;
+    uint8_t base = sib & 7;
+    address << "[";
+
+    // REX.x is bit 3 of index.
+    if ((rex64 & REX_X) != 0) {
+      index += 8;
+    }
+
+    // Mod = 0 && base = 5 (ebp): no base (ignores REX.b).
+    bool has_base = false;
+    if (base != 5 || mod != 0) {
+      has_base = true;
+      DumpBaseReg(address, rex64, base);
+    }
+
+    // Index = 4 (esp/rsp) is disallowed.
+    if (index != 4) {
+      if (has_base) {
+        address << " + ";
+      }
+      DumpAddrReg(address, rex64, index);
+      if (scale != 0) {
+        address << StringPrintf(" * %d", 1 << scale);
+      }
+    }
+
+    if (mod == 0) {
+      if (base == 5) {
+        if (index != 4) {
+          address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(*instr));
+        } else {
+          // 64-bit low 32-bit absolute address, redundant absolute address encoding on 32-bit.
+          *address_bits = *reinterpret_cast<const uint32_t*>(*instr);
+          address << StringPrintf("%d", *address_bits);
+        }
+        (*instr) += 4;
+      }
+    } else if (mod == 1) {
+      address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(*instr));
+      (*instr)++;
+    } else if (mod == 2) {
+      address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(*instr));
+      (*instr) += 4;
+    }
+    address << "]";
+  } else {
+    if (mod == 3) {
+      if (!no_ops) {
+        DumpRmReg(address, rex_w, rm, byte_operand || byte_second_operand,
+                  prefix[2], load ? src_reg_file : dst_reg_file);
+      }
+    } else {
+      address << "[";
+      DumpBaseReg(address, rex64, rm);
+      if (mod == 1) {
+        address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(*instr));
+        (*instr)++;
+      } else if (mod == 2) {
+        address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(*instr));
+        (*instr) += 4;
+      }
+      address << "]";
+    }
+  }
+  return address.str();
+}
+
 size_t DisassemblerX86::DumpInstruction(std::ostream& os, const uint8_t* instr) {
   const uint8_t* begin_instr = instr;
   bool have_prefixes = true;
@@ -201,7 +289,12 @@
   bool reg_is_opcode = false;
   size_t immediate_bytes = 0;
   size_t branch_bytes = 0;
-  std::ostringstream opcode;
+  std::string opcode_tmp;    // Storage to keep StringPrintf result alive.
+  const char* opcode0 = "";  // Prefix part.
+  const char* opcode1 = "";  // Main opcode.
+  const char* opcode2 = "";  // Sub-opcode. E.g., jump type.
+  const char* opcode3 = "";  // Mod-rm part.
+  const char* opcode4 = "";  // Suffix part.
   bool store = false;  // stores to memory (ie rm is on the left)
   bool load = false;  // loads from memory (ie rm is on the right)
   bool byte_operand = false;  // true when the opcode is dealing with byte operands
@@ -220,12 +313,12 @@
                      rm8_r8, rm32_r32, \
                      r8_rm8, r32_rm32, \
                      ax8_i8, ax32_i32) \
-  case rm8_r8:   opcode << #opname; store = true; has_modrm = true; byte_operand = true; break; \
-  case rm32_r32: opcode << #opname; store = true; has_modrm = true; break; \
-  case r8_rm8:   opcode << #opname; load = true; has_modrm = true; byte_operand = true; break; \
-  case r32_rm32: opcode << #opname; load = true; has_modrm = true; break; \
-  case ax8_i8:   opcode << #opname; ax = true; immediate_bytes = 1; byte_operand = true; break; \
-  case ax32_i32: opcode << #opname; ax = true; immediate_bytes = 4; break;
+  case rm8_r8:   opcode1 = #opname; store = true; has_modrm = true; byte_operand = true; break; \
+  case rm32_r32: opcode1 = #opname; store = true; has_modrm = true; break; \
+  case r8_rm8:   opcode1 = #opname; load = true; has_modrm = true; byte_operand = true; break; \
+  case r32_rm32: opcode1 = #opname; load = true; has_modrm = true; break; \
+  case ax8_i8:   opcode1 = #opname; ax = true; immediate_bytes = 1; byte_operand = true; break; \
+  case ax32_i32: opcode1 = #opname; ax = true; immediate_bytes = 4; break;
 
 DISASSEMBLER_ENTRY(add,
   0x00 /* RegMem8/Reg8 */,     0x01 /* RegMem32/Reg32 */,
@@ -262,65 +355,67 @@
 
 #undef DISASSEMBLER_ENTRY
   case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
-    opcode << "push";
+    opcode1 = "push";
     reg_in_opcode = true;
     target_specific = true;
     break;
   case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F:
-    opcode << "pop";
+    opcode1 = "pop";
     reg_in_opcode = true;
     target_specific = true;
     break;
   case 0x63:
     if ((rex & REX_W) != 0) {
-      opcode << "movsxd";
+      opcode1 = "movsxd";
       has_modrm = true;
       load = true;
     } else {
       // In 32-bit mode (!supports_rex_) this is ARPL, with no REX prefix the functionality is the
       // same as 'mov' but the use of the instruction is discouraged.
-      opcode << StringPrintf("unknown opcode '%02X'", *instr);
+      opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+      opcode1 = opcode_tmp.c_str();
     }
     break;
-  case 0x68: opcode << "push"; immediate_bytes = 4; break;
-  case 0x69: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 4; break;
-  case 0x6A: opcode << "push"; immediate_bytes = 1; break;
-  case 0x6B: opcode << "imul"; load = true; has_modrm = true; immediate_bytes = 1; break;
+  case 0x68: opcode1 = "push"; immediate_bytes = 4; break;
+  case 0x69: opcode1 = "imul"; load = true; has_modrm = true; immediate_bytes = 4; break;
+  case 0x6A: opcode1 = "push"; immediate_bytes = 1; break;
+  case 0x6B: opcode1 = "imul"; load = true; has_modrm = true; immediate_bytes = 1; break;
   case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
   case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
     static const char* condition_codes[] =
     {"o", "no", "b/nae/c", "nb/ae/nc", "z/eq",  "nz/ne", "be/na", "nbe/a",
      "s", "ns", "p/pe",    "np/po",    "l/nge", "nl/ge", "le/ng", "nle/g"
     };
-    opcode << "j" << condition_codes[*instr & 0xF];
+    opcode1 = "j";
+    opcode2 = condition_codes[*instr & 0xF];
     branch_bytes = 1;
     break;
   case 0x86: case 0x87:
-    opcode << "xchg";
+    opcode1 = "xchg";
     store = true;
     has_modrm = true;
     byte_operand = (*instr == 0x86);
     break;
-  case 0x88: opcode << "mov"; store = true; has_modrm = true; byte_operand = true; break;
-  case 0x89: opcode << "mov"; store = true; has_modrm = true; break;
-  case 0x8A: opcode << "mov"; load = true; has_modrm = true; byte_operand = true; break;
-  case 0x8B: opcode << "mov"; load = true; has_modrm = true; break;
+  case 0x88: opcode1 = "mov"; store = true; has_modrm = true; byte_operand = true; break;
+  case 0x89: opcode1 = "mov"; store = true; has_modrm = true; break;
+  case 0x8A: opcode1 = "mov"; load = true; has_modrm = true; byte_operand = true; break;
+  case 0x8B: opcode1 = "mov"; load = true; has_modrm = true; break;
 
   case 0x0F:  // 2 byte extended opcode
     instr++;
     switch (*instr) {
       case 0x10: case 0x11:
         if (prefix[0] == 0xF2) {
-          opcode << "movsd";
+          opcode1 = "movsd";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
-          opcode << "movss";
+          opcode1 = "movss";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[2] == 0x66) {
-          opcode << "movupd";
+          opcode1 = "movupd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "movups";
+          opcode1 = "movups";
         }
         has_modrm = true;
         src_reg_file = dst_reg_file = SSE;
@@ -329,10 +424,10 @@
         break;
       case 0x12: case 0x13:
         if (prefix[2] == 0x66) {
-          opcode << "movlpd";
+          opcode1 = "movlpd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0) {
-          opcode << "movlps";
+          opcode1 = "movlps";
         }
         has_modrm = true;
         src_reg_file = dst_reg_file = SSE;
@@ -341,10 +436,10 @@
         break;
       case 0x16: case 0x17:
         if (prefix[2] == 0x66) {
-          opcode << "movhpd";
+          opcode1 = "movhpd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0) {
-          opcode << "movhps";
+          opcode1 = "movhps";
         }
         has_modrm = true;
         src_reg_file = dst_reg_file = SSE;
@@ -353,10 +448,10 @@
         break;
       case 0x28: case 0x29:
         if (prefix[2] == 0x66) {
-          opcode << "movapd";
+          opcode1 = "movapd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0) {
-          opcode << "movaps";
+          opcode1 = "movaps";
         }
         has_modrm = true;
         src_reg_file = dst_reg_file = SSE;
@@ -365,16 +460,16 @@
         break;
       case 0x2A:
         if (prefix[2] == 0x66) {
-          opcode << "cvtpi2pd";
+          opcode1 = "cvtpi2pd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF2) {
-          opcode << "cvtsi2sd";
+          opcode1 = "cvtsi2sd";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
-          opcode << "cvtsi2ss";
+          opcode1 = "cvtsi2ss";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "cvtpi2ps";
+          opcode1 = "cvtpi2ps";
         }
         load = true;
         has_modrm = true;
@@ -382,16 +477,16 @@
         break;
       case 0x2C:
         if (prefix[2] == 0x66) {
-          opcode << "cvttpd2pi";
+          opcode1 = "cvttpd2pi";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF2) {
-          opcode << "cvttsd2si";
+          opcode1 = "cvttsd2si";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
-          opcode << "cvttss2si";
+          opcode1 = "cvttss2si";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "cvttps2pi";
+          opcode1 = "cvttps2pi";
         }
         load = true;
         has_modrm = true;
@@ -399,30 +494,30 @@
         break;
       case 0x2D:
         if (prefix[2] == 0x66) {
-          opcode << "cvtpd2pi";
+          opcode1 = "cvtpd2pi";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF2) {
-          opcode << "cvtsd2si";
+          opcode1 = "cvtsd2si";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
-          opcode << "cvtss2si";
+          opcode1 = "cvtss2si";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "cvtps2pi";
+          opcode1 = "cvtps2pi";
         }
         load = true;
         has_modrm = true;
         src_reg_file = SSE;
         break;
       case 0x2E:
-        opcode << "u";
+        opcode0 = "u";
         FALLTHROUGH_INTENDED;
       case 0x2F:
         if (prefix[2] == 0x66) {
-          opcode << "comisd";
+          opcode1 = "comisd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "comiss";
+          opcode1 = "comiss";
         }
         has_modrm = true;
         load = true;
@@ -433,31 +528,33 @@
         if (prefix[2] == 0x66) {
           switch (*instr) {
             case 0x01:
-              opcode << "phaddw";
+              opcode1 = "phaddw";
               prefix[2] = 0;
               has_modrm = true;
               load = true;
               src_reg_file = dst_reg_file = SSE;
               break;
             case 0x02:
-              opcode << "phaddd";
+              opcode1 = "phaddd";
               prefix[2] = 0;
               has_modrm = true;
               load = true;
               src_reg_file = dst_reg_file = SSE;
               break;
             case 0x40:
-              opcode << "pmulld";
+              opcode1 = "pmulld";
               prefix[2] = 0;
               has_modrm = true;
               load = true;
               src_reg_file = dst_reg_file = SSE;
               break;
             default:
-              opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+              opcode_tmp = StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+              opcode1 = opcode_tmp.c_str();
           }
         } else {
-          opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+          opcode_tmp = StringPrintf("unknown opcode '0F 38 %02X'", *instr);
+          opcode1 = opcode_tmp.c_str();
         }
         break;
       case 0x3A:  // 3 byte extended opcode
@@ -465,7 +562,7 @@
         if (prefix[2] == 0x66) {
           switch (*instr) {
             case 0x14:
-              opcode << "pextrb";
+              opcode1 = "pextrb";
               prefix[2] = 0;
               has_modrm = true;
               store = true;
@@ -473,7 +570,7 @@
               immediate_bytes = 1;
               break;
             case 0x16:
-              opcode << "pextrd";
+              opcode1 = "pextrd";
               prefix[2] = 0;
               has_modrm = true;
               store = true;
@@ -481,48 +578,51 @@
               immediate_bytes = 1;
               break;
             default:
-              opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+              opcode_tmp = StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+              opcode1 = opcode_tmp.c_str();
           }
         } else {
-          opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+          opcode_tmp = StringPrintf("unknown opcode '0F 3A %02X'", *instr);
+          opcode1 = opcode_tmp.c_str();
         }
         break;
       case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
       case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F:
-        opcode << "cmov" << condition_codes[*instr & 0xF];
+        opcode1 = "cmov";
+        opcode2 = condition_codes[*instr & 0xF];
         has_modrm = true;
         load = true;
         break;
       case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
       case 0x58: case 0x59: case 0x5C: case 0x5D: case 0x5E: case 0x5F: {
         switch (*instr) {
-          case 0x50: opcode << "movmsk"; break;
-          case 0x51: opcode << "sqrt"; break;
-          case 0x52: opcode << "rsqrt"; break;
-          case 0x53: opcode << "rcp"; break;
-          case 0x54: opcode << "and"; break;
-          case 0x55: opcode << "andn"; break;
-          case 0x56: opcode << "or"; break;
-          case 0x57: opcode << "xor"; break;
-          case 0x58: opcode << "add"; break;
-          case 0x59: opcode << "mul"; break;
-          case 0x5C: opcode << "sub"; break;
-          case 0x5D: opcode << "min"; break;
-          case 0x5E: opcode << "div"; break;
-          case 0x5F: opcode << "max"; break;
+          case 0x50: opcode1 = "movmsk"; break;
+          case 0x51: opcode1 = "sqrt"; break;
+          case 0x52: opcode1 = "rsqrt"; break;
+          case 0x53: opcode1 = "rcp"; break;
+          case 0x54: opcode1 = "and"; break;
+          case 0x55: opcode1 = "andn"; break;
+          case 0x56: opcode1 = "or"; break;
+          case 0x57: opcode1 = "xor"; break;
+          case 0x58: opcode1 = "add"; break;
+          case 0x59: opcode1 = "mul"; break;
+          case 0x5C: opcode1 = "sub"; break;
+          case 0x5D: opcode1 = "min"; break;
+          case 0x5E: opcode1 = "div"; break;
+          case 0x5F: opcode1 = "max"; break;
           default: LOG(FATAL) << "Unreachable"; UNREACHABLE();
         }
         if (prefix[2] == 0x66) {
-          opcode << "pd";
+          opcode2 = "pd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF2) {
-          opcode << "sd";
+          opcode2 = "sd";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
-          opcode << "ss";
+          opcode2 = "ss";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "ps";
+          opcode2 = "ps";
         }
         load = true;
         has_modrm = true;
@@ -531,16 +631,16 @@
       }
       case 0x5A:
         if (prefix[2] == 0x66) {
-          opcode << "cvtpd2ps";
+          opcode1 = "cvtpd2ps";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF2) {
-          opcode << "cvtsd2ss";
+          opcode1 = "cvtsd2ss";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
-          opcode << "cvtss2sd";
+          opcode1 = "cvtss2sd";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "cvtps2pd";
+          opcode1 = "cvtps2pd";
         }
         load = true;
         has_modrm = true;
@@ -548,15 +648,15 @@
         break;
       case 0x5B:
         if (prefix[2] == 0x66) {
-          opcode << "cvtps2dq";
+          opcode1 = "cvtps2dq";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF2) {
-          opcode << "bad opcode F2 0F 5B";
+          opcode1 = "bad opcode F2 0F 5B";
         } else if (prefix[0] == 0xF3) {
-          opcode << "cvttps2dq";
+          opcode1 = "cvttps2dq";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << "cvtdq2ps";
+          opcode1 = "cvtdq2ps";
         }
         load = true;
         has_modrm = true;
@@ -570,10 +670,10 @@
           src_reg_file = dst_reg_file = MMX;
         }
         switch (*instr) {
-          case 0x60: opcode << "punpcklbw"; break;
-          case 0x61: opcode << "punpcklwd"; break;
-          case 0x62: opcode << "punpckldq"; break;
-          case 0x6c: opcode << "punpcklqdq"; break;
+          case 0x60: opcode1 = "punpcklbw"; break;
+          case 0x61: opcode1 = "punpcklwd"; break;
+          case 0x62: opcode1 = "punpckldq"; break;
+          case 0x6c: opcode1 = "punpcklqdq"; break;
         }
         load = true;
         has_modrm = true;
@@ -585,43 +685,44 @@
         } else {
           dst_reg_file = MMX;
         }
-        opcode << "movd";
+        opcode1 = "movd";
         load = true;
         has_modrm = true;
         break;
       case 0x6F:
         if (prefix[2] == 0x66) {
           src_reg_file = dst_reg_file = SSE;
-          opcode << "movdqa";
+          opcode1 = "movdqa";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[0] == 0xF3) {
           src_reg_file = dst_reg_file = SSE;
-          opcode << "movdqu";
+          opcode1 = "movdqu";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
           dst_reg_file = MMX;
-          opcode << "movq";
+          opcode1 = "movq";
         }
         load = true;
         has_modrm = true;
         break;
       case 0x70:
         if (prefix[2] == 0x66) {
-          opcode << "pshufd";
+          opcode1 = "pshufd";
           prefix[2] = 0;
           has_modrm = true;
           store = true;
           src_reg_file = dst_reg_file = SSE;
           immediate_bytes = 1;
         } else if (prefix[0] == 0xF2) {
-          opcode << "pshuflw";
+          opcode1 = "pshuflw";
           prefix[0] = 0;
           has_modrm = true;
           store = true;
           src_reg_file = dst_reg_file = SSE;
           immediate_bytes = 1;
         } else {
-          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode1 = opcode_tmp.c_str();
         }
         break;
       case 0x71:
@@ -674,13 +775,14 @@
         break;
       case 0x7C:
         if (prefix[0] == 0xF2) {
-          opcode << "haddps";
+          opcode1 = "haddps";
           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else if (prefix[2] == 0x66) {
-          opcode << "haddpd";
+          opcode1 = "haddpd";
           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
         } else {
-          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode1 = opcode_tmp.c_str();
           break;
         }
         src_reg_file = dst_reg_file = SSE;
@@ -694,43 +796,45 @@
         } else {
           src_reg_file = MMX;
         }
-        opcode << "movd";
+        opcode1 = "movd";
         has_modrm = true;
         store = true;
         break;
       case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
       case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
-        opcode << "j" << condition_codes[*instr & 0xF];
+        opcode1 = "j";
+        opcode2 = condition_codes[*instr & 0xF];
         branch_bytes = 4;
         break;
       case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
       case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
-        opcode << "set" << condition_codes[*instr & 0xF];
+        opcode1 = "set";
+        opcode2 = condition_codes[*instr & 0xF];
         modrm_opcodes = nullptr;
         reg_is_opcode = true;
         has_modrm = true;
         store = true;
         break;
       case 0xA4:
-        opcode << "shld";
+        opcode1 = "shld";
         has_modrm = true;
         load = true;
         immediate_bytes = 1;
         break;
       case 0xA5:
-        opcode << "shld";
+        opcode1 = "shld";
         has_modrm = true;
         load = true;
         cx = true;
         break;
       case 0xAC:
-        opcode << "shrd";
+        opcode1 = "shrd";
         has_modrm = true;
         load = true;
         immediate_bytes = 1;
         break;
       case 0xAD:
-        opcode << "shrd";
+        opcode1 = "shrd";
         has_modrm = true;
         load = true;
         cx = true;
@@ -778,61 +882,62 @@
         }
         break;
       case 0xAF:
-        opcode << "imul";
+        opcode1 = "imul";
         has_modrm = true;
         load = true;
         break;
       case 0xB1:
-        opcode << "cmpxchg";
+        opcode1 = "cmpxchg";
         has_modrm = true;
         store = true;
         break;
       case 0xB6:
-        opcode << "movzxb";
+        opcode1 = "movzxb";
         has_modrm = true;
         load = true;
         byte_second_operand = true;
         break;
       case 0xB7:
-        opcode << "movzxw";
+        opcode1 = "movzxw";
         has_modrm = true;
         load = true;
         break;
       case 0xBE:
-        opcode << "movsxb";
+        opcode1 = "movsxb";
         has_modrm = true;
         load = true;
         byte_second_operand = true;
         rex |= (rex == 0 ? 0 : REX_W);
         break;
       case 0xBF:
-        opcode << "movsxw";
+        opcode1 = "movsxw";
         has_modrm = true;
         load = true;
         break;
       case 0xC3:
-        opcode << "movnti";
+        opcode1 = "movnti";
         store = true;
         has_modrm = true;
         break;
       case 0xC5:
         if (prefix[2] == 0x66) {
-          opcode << "pextrw";
+          opcode1 = "pextrw";
           prefix[2] = 0;
           has_modrm = true;
           store = true;
           src_reg_file = SSE;
           immediate_bytes = 1;
         } else {
-          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode1 = opcode_tmp.c_str();
         }
         break;
       case 0xC6:
         if (prefix[2] == 0x66) {
-          opcode << "shufpd";
+          opcode1 = "shufpd";
           prefix[2] = 0;
         } else {
-          opcode << "shufps";
+          opcode1 = "shufps";
         }
         has_modrm = true;
         store = true;
@@ -849,7 +954,7 @@
         store = true;
         break;
       case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
-        opcode << "bswap";
+        opcode1 = "bswap";
         reg_in_opcode = true;
         break;
       case 0xD4:
@@ -859,7 +964,7 @@
         } else {
           src_reg_file = dst_reg_file = MMX;
         }
-        opcode << "paddq";
+        opcode1 = "paddq";
         prefix[2] = 0;
         has_modrm = true;
         load = true;
@@ -871,20 +976,21 @@
         } else {
           src_reg_file = dst_reg_file = MMX;
         }
-        opcode << "pand";
+        opcode1 = "pand";
         prefix[2] = 0;
         has_modrm = true;
         load = true;
         break;
       case 0xD5:
         if (prefix[2] == 0x66) {
-          opcode << "pmullw";
+          opcode1 = "pmullw";
           prefix[2] = 0;
           has_modrm = true;
           load = true;
           src_reg_file = dst_reg_file = SSE;
         } else {
-          opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+          opcode1 = opcode_tmp.c_str();
         }
         break;
       case 0xEB:
@@ -894,7 +1000,7 @@
         } else {
           src_reg_file = dst_reg_file = MMX;
         }
-        opcode << "por";
+        opcode1 = "por";
         prefix[2] = 0;
         has_modrm = true;
         load = true;
@@ -906,7 +1012,7 @@
         } else {
           src_reg_file = dst_reg_file = MMX;
         }
-        opcode << "pxor";
+        opcode1 = "pxor";
         prefix[2] = 0;
         has_modrm = true;
         load = true;
@@ -927,22 +1033,23 @@
           src_reg_file = dst_reg_file = MMX;
         }
         switch (*instr) {
-          case 0xF4: opcode << "pmuludq"; break;
-          case 0xF6: opcode << "psadbw"; break;
-          case 0xF8: opcode << "psubb"; break;
-          case 0xF9: opcode << "psubw"; break;
-          case 0xFA: opcode << "psubd"; break;
-          case 0xFB: opcode << "psubq"; break;
-          case 0xFC: opcode << "paddb"; break;
-          case 0xFD: opcode << "paddw"; break;
-          case 0xFE: opcode << "paddd"; break;
+          case 0xF4: opcode1 = "pmuludq"; break;
+          case 0xF6: opcode1 = "psadbw"; break;
+          case 0xF8: opcode1 = "psubb"; break;
+          case 0xF9: opcode1 = "psubw"; break;
+          case 0xFA: opcode1 = "psubd"; break;
+          case 0xFB: opcode1 = "psubq"; break;
+          case 0xFC: opcode1 = "paddb"; break;
+          case 0xFD: opcode1 = "paddw"; break;
+          case 0xFE: opcode1 = "paddd"; break;
         }
         prefix[2] = 0;
         has_modrm = true;
         load = true;
         break;
       default:
-        opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
+        opcode_tmp = StringPrintf("unknown opcode '0F %02X'", *instr);
+        opcode1 = opcode_tmp.c_str();
         break;
     }
     break;
@@ -956,38 +1063,39 @@
     immediate_bytes = *instr == 0x81 ? 4 : 1;
     break;
   case 0x84: case 0x85:
-    opcode << "test";
+    opcode1 = "test";
     has_modrm = true;
     load = true;
     byte_operand = (*instr & 1) == 0;
     break;
   case 0x8D:
-    opcode << "lea";
+    opcode1 = "lea";
     has_modrm = true;
     load = true;
     break;
   case 0x8F:
-    opcode << "pop";
+    opcode1 = "pop";
     has_modrm = true;
     reg_is_opcode = true;
     store = true;
     break;
   case 0x99:
-    opcode << "cdq";
+    opcode1 = "cdq";
     break;
   case 0x9B:
     if (instr[1] == 0xDF && instr[2] == 0xE0) {
-      opcode << "fstsw\tax";
+      opcode1 = "fstsw\tax";
       instr += 2;
     } else {
-      opcode << StringPrintf("unknown opcode '%02X'", *instr);
+      opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+      opcode1 = opcode_tmp.c_str();
     }
     break;
   case 0xAF:
-    opcode << (prefix[2] == 0x66 ? "scasw" : "scasl");
+    opcode1 = (prefix[2] == 0x66 ? "scasw" : "scasl");
     break;
   case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
-    opcode << "mov";
+    opcode1 = "mov";
     immediate_bytes = 1;
     byte_operand = true;
     reg_in_opcode = true;
@@ -995,12 +1103,12 @@
     break;
   case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF:
     if ((rex & REX_W) != 0) {
-      opcode << "movabsq";
+      opcode1 = "movabsq";
       immediate_bytes = 8;
       reg_in_opcode = true;
       break;
     }
-    opcode << "mov";
+    opcode1 = "mov";
     immediate_bytes = 4;
     reg_in_opcode = true;
     break;
@@ -1016,7 +1124,7 @@
     cx = (*instr == 0xD2) || (*instr == 0xD3);
     byte_operand = (*instr == 0xC0);
     break;
-  case 0xC3: opcode << "ret"; break;
+  case 0xC3: opcode1 = "ret"; break;
   case 0xC6:
     static const char* c6_opcodes[] = {"mov",        "unknown-c6", "unknown-c6",
                                        "unknown-c6", "unknown-c6", "unknown-c6",
@@ -1038,10 +1146,10 @@
     has_modrm = true;
     reg_is_opcode = true;
     break;
-  case 0xCC: opcode << "int 3"; break;
+  case 0xCC: opcode1 = "int 3"; break;
   case 0xD9:
     if (instr[1] == 0xF8) {
-      opcode << "fprem";
+      opcode1 = "fprem";
       instr++;
     } else {
       static const char* d9_opcodes[] = {"flds", "unknown-d9", "fsts", "fstps", "fldenv", "fldcw",
@@ -1054,10 +1162,11 @@
     break;
   case 0xDA:
     if (instr[1] == 0xE9) {
-      opcode << "fucompp";
+      opcode1 = "fucompp";
       instr++;
     } else {
-      opcode << StringPrintf("unknown opcode '%02X'", *instr);
+      opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+      opcode1 = opcode_tmp.c_str();
     }
     break;
   case 0xDB:
@@ -1087,11 +1196,11 @@
     has_modrm = true;
     reg_is_opcode = true;
     break;
-  case 0xE3: opcode << "jecxz"; branch_bytes = 1; break;
-  case 0xE8: opcode << "call"; branch_bytes = 4; break;
-  case 0xE9: opcode << "jmp"; branch_bytes = 4; break;
-  case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
-  case 0xF5: opcode << "cmc"; break;
+  case 0xE3: opcode1 = "jecxz"; branch_bytes = 1; break;
+  case 0xE8: opcode1 = "call"; branch_bytes = 4; break;
+  case 0xE9: opcode1 = "jmp"; branch_bytes = 4; break;
+  case 0xEB: opcode1 = "jmp"; branch_bytes = 1; break;
+  case 0xF5: opcode1 = "cmc"; break;
   case 0xF6: case 0xF7:
     static const char* f7_opcodes[] = {
         "test", "unknown-f7", "not", "neg", "mul edx:eax, eax *",
@@ -1120,7 +1229,8 @@
     }
     break;
   default:
-    opcode << StringPrintf("unknown opcode '%02X'", *instr);
+    opcode_tmp = StringPrintf("unknown opcode '%02X'", *instr);
+    opcode1 = opcode_tmp.c_str();
     break;
   }
   std::ostringstream args;
@@ -1141,84 +1251,21 @@
     uint8_t mod = modrm >> 6;
     uint8_t reg_or_opcode = (modrm >> 3) & 7;
     uint8_t rm = modrm & 7;
-    std::ostringstream address;
-    if (mod == 0 && rm == 5) {
-      if (!supports_rex_) {  // Absolute address.
-        address_bits = *reinterpret_cast<const uint32_t*>(instr);
-        address << StringPrintf("[0x%x]", address_bits);
-      } else {  // 64-bit RIP relative addressing.
-        address << StringPrintf("[RIP + 0x%x]",  *reinterpret_cast<const uint32_t*>(instr));
-      }
-      instr += 4;
-    } else if (rm == 4 && mod != 3) {  // SIB
-      uint8_t sib = *instr;
-      instr++;
-      uint8_t scale = (sib >> 6) & 3;
-      uint8_t index = (sib >> 3) & 7;
-      uint8_t base = sib & 7;
-      address << "[";
-      if (base != 5 || mod != 0) {
-        DumpBaseReg(address, rex64, base);
-        if (index != 4) {
-          address << " + ";
-        }
-      }
-      if (index != 4) {
-        DumpIndexReg(address, rex64, index);
-        if (scale != 0) {
-          address << StringPrintf(" * %d", 1 << scale);
-        }
-      }
-      if (mod == 0) {
-        if (base == 5) {
-          if (index != 4) {
-            address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
-          } else {
-            // 64-bit low 32-bit absolute address, redundant absolute address encoding on 32-bit.
-            address_bits = *reinterpret_cast<const uint32_t*>(instr);
-            address << StringPrintf("%d", address_bits);
-          }
-          instr += 4;
-        }
-      } else if (mod == 1) {
-        address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
-        instr++;
-      } else if (mod == 2) {
-        address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
-        instr += 4;
-      }
-      address << "]";
-    } else {
-      if (mod == 3) {
-        if (!no_ops) {
-          DumpRmReg(address, rex_w, rm, byte_operand || byte_second_operand,
-                    prefix[2], load ? src_reg_file : dst_reg_file);
-        }
-      } else {
-        address << "[";
-        DumpBaseReg(address, rex64, rm);
-        if (mod == 1) {
-          address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
-          instr++;
-        } else if (mod == 2) {
-          address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
-          instr += 4;
-        }
-        address << "]";
-      }
-    }
+    std::string address = DumpAddress(mod, rm, rex64, rex_w, no_ops, byte_operand,
+                                      byte_second_operand, prefix, load, src_reg_file, dst_reg_file,
+                                      &instr, &address_bits);
 
     if (reg_is_opcode && modrm_opcodes != nullptr) {
-      opcode << modrm_opcodes[reg_or_opcode];
+      opcode3 = modrm_opcodes[reg_or_opcode];
     }
 
     // Add opcode suffixes to indicate size.
     if (byte_operand) {
-      opcode << 'b';
+      opcode4 = "b";
     } else if ((rex & REX_W) != 0) {
-      opcode << 'q';
+      opcode4 = "q";
     } else if (prefix[2] == 0x66) {
-      opcode << 'w';
+      opcode4 = "w";
     }
 
     if (load) {
@@ -1227,11 +1274,11 @@
         args << ", ";
       }
       DumpSegmentOverride(args, prefix[1]);
-      args << address.str();
+      args << address;
     } else {
       DCHECK(store);
       DumpSegmentOverride(args, prefix[1]);
-      args << address.str();
+      args << address;
       if (!reg_is_opcode) {
         args << ", ";
         DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], src_reg_file);
@@ -1289,21 +1336,17 @@
     args << "  ; ";
     Thread::DumpThreadOffset<8>(args, address_bits);
   }
-  std::stringstream hex;
-  for (size_t i = 0; begin_instr + i < instr; ++i) {
-    hex << StringPrintf("%02X", begin_instr[i]);
-  }
-  std::stringstream prefixed_opcode;
+  const char* prefix_str;
   switch (prefix[0]) {
-    case 0xF0: prefixed_opcode << "lock "; break;
-    case 0xF2: prefixed_opcode << "repne "; break;
-    case 0xF3: prefixed_opcode << "repe "; break;
-    case 0: break;
+    case 0xF0: prefix_str = "lock "; break;
+    case 0xF2: prefix_str = "repne "; break;
+    case 0xF3: prefix_str = "repe "; break;
+    case 0: prefix_str = ""; break;
     default: LOG(FATAL) << "Unreachable"; UNREACHABLE();
   }
-  prefixed_opcode << opcode.str();
   os << FormatInstructionPointer(begin_instr)
-     << StringPrintf(": %22s    \t%-7s ", hex.str().c_str(), prefixed_opcode.str().c_str())
+     << StringPrintf(": %22s    \t%-7s%s%s%s%s%s ", DumpCodeHex(begin_instr, instr).c_str(),
+                     prefix_str, opcode0, opcode1, opcode2, opcode3, opcode4)
      << args.str() << '\n';
   return instr - begin_instr;
 }  // NOLINT(readability/fn_size)
diff --git a/disassembler/disassembler_x86.h b/disassembler/disassembler_x86.h
index f448662..71c3e41 100644
--- a/disassembler/disassembler_x86.h
+++ b/disassembler/disassembler_x86.h
@@ -22,6 +22,8 @@
 namespace art {
 namespace x86 {
 
+enum RegFile { GPR, MMX, SSE };
+
 class DisassemblerX86 FINAL : public Disassembler {
  public:
   DisassemblerX86(DisassemblerOptions* options, bool supports_rex)
@@ -33,6 +35,11 @@
  private:
   size_t DumpInstruction(std::ostream& os, const uint8_t* instr);
 
+  std::string DumpAddress(uint8_t mod, uint8_t rm, uint8_t rex64, uint8_t rex_w, bool no_ops,
+                          bool byte_operand, bool byte_second_operand, uint8_t* prefix, bool load,
+                          RegFile src_reg_file, RegFile dst_reg_file, const uint8_t** instr,
+                          uint32_t* address_bits);
+
   const bool supports_rex_;
 
   DISALLOW_COPY_AND_ASSIGN(DisassemblerX86);
diff --git a/imgdiag/Android.mk b/imgdiag/Android.mk
new file mode 100644
index 0000000..d5d7c22
--- /dev/null
+++ b/imgdiag/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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
+
+IMGDIAG_SRC_FILES := \
+	imgdiag.cc
+
+# Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+# that the image it's analyzing be the same ISA as the runtime ISA.
+
+# Build variants {target,host} x {debug,ndebug} x {32,64}
+$(eval $(call build-art-multi-executable,imgdiag,$(IMGDIAG_SRC_FILES),libart-compiler libbacktrace,libcutils,libziparchive-host,art/compiler,both))
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
new file mode 100644
index 0000000..9b57ecb
--- /dev/null
+++ b/imgdiag/imgdiag.cc
@@ -0,0 +1,951 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+
+#include "base/unix_file/fd_file.h"
+#include "base/stringprintf.h"
+#include "gc/space/image_space.h"
+#include "gc/heap.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/art_method-inl.h"
+#include "image.h"
+#include "scoped_thread_state_change.h"
+#include "os.h"
+#include "gc_map.h"
+
+#include "cmdline.h"
+#include "backtrace/BacktraceMap.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <signal.h>
+
+namespace art {
+
+class ImgDiagDumper {
+ public:
+  explicit ImgDiagDumper(std::ostream* os,
+                       const ImageHeader& image_header,
+                       const char* image_location,
+                       pid_t image_diff_pid)
+      : os_(os),
+        image_header_(image_header),
+        image_location_(image_location),
+        image_diff_pid_(image_diff_pid) {}
+
+  bool Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    std::ostream& os = *os_;
+    os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
+
+    os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
+
+    bool ret = true;
+    if (image_diff_pid_ >= 0) {
+      os << "IMAGE DIFF PID (" << image_diff_pid_ << "): ";
+      ret = DumpImageDiff(image_diff_pid_);
+      os << "\n\n";
+    } else {
+      os << "IMAGE DIFF PID: disabled\n\n";
+    }
+
+    os << std::flush;
+
+    return ret;
+  }
+
+ private:
+  static bool EndsWith(const std::string& str, const std::string& suffix) {
+    return str.size() >= suffix.size() &&
+           str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+  }
+
+  // Return suffix of the file path after the last /. (e.g. /foo/bar -> bar, bar -> bar)
+  static std::string BaseName(const std::string& str) {
+    size_t idx = str.rfind("/");
+    if (idx == std::string::npos) {
+      return str;
+    }
+
+    return str.substr(idx + 1);
+  }
+
+  bool DumpImageDiff(pid_t image_diff_pid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    std::ostream& os = *os_;
+
+    {
+      struct stat sts;
+      std::string proc_pid_str = StringPrintf("/proc/%ld", static_cast<long>(image_diff_pid));  // NOLINT [runtime/int]
+      if (stat(proc_pid_str.c_str(), &sts) == -1) {
+        os << "Process does not exist";
+        return false;
+      }
+    }
+
+    // Open /proc/$pid/maps to view memory maps
+    auto proc_maps = std::unique_ptr<BacktraceMap>(BacktraceMap::Create(image_diff_pid));
+    if (proc_maps == nullptr) {
+      os << "Could not read backtrace maps";
+      return false;
+    }
+
+    bool found_boot_map = false;
+    backtrace_map_t boot_map = backtrace_map_t();
+    // Find the memory map only for boot.art
+    for (const backtrace_map_t& map : *proc_maps) {
+      if (EndsWith(map.name, GetImageLocationBaseName())) {
+        if ((map.flags & PROT_WRITE) != 0) {
+          boot_map = map;
+          found_boot_map = true;
+          break;
+        }
+        // In actuality there's more than 1 map, but the second one is read-only.
+        // The one we care about is the write-able map.
+        // The readonly maps are guaranteed to be identical, so its not interesting to compare
+        // them.
+      }
+    }
+
+    if (!found_boot_map) {
+      os << "Could not find map for " << GetImageLocationBaseName();
+      return false;
+    }
+
+    // Future idea: diff against zygote so we can ignore the shared dirty pages.
+    return DumpImageDiffMap(image_diff_pid, boot_map);
+  }
+
+    // Look at /proc/$pid/mem and only diff the things from there
+  bool DumpImageDiffMap(pid_t image_diff_pid, const backtrace_map_t& boot_map)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    std::ostream& os = *os_;
+    const size_t pointer_size = InstructionSetPointerSize(
+        Runtime::Current()->GetInstructionSet());
+
+    std::string file_name = StringPrintf("/proc/%ld/mem", static_cast<long>(image_diff_pid));  // NOLINT [runtime/int]
+
+    size_t boot_map_size = boot_map.end - boot_map.start;
+
+    // Open /proc/$pid/mem as a file
+    auto map_file = std::unique_ptr<File>(OS::OpenFileForReading(file_name.c_str()));
+    if (map_file == nullptr) {
+      os << "Failed to open " << file_name << " for reading";
+      return false;
+    }
+
+    // Memory-map /proc/$pid/mem subset from the boot map
+    CHECK(boot_map.end >= boot_map.start);
+
+    std::string error_msg;
+
+    // Walk the bytes and diff against our boot image
+    const ImageHeader& boot_image_header = GetBootImageHeader();
+
+    os << "\nObserving boot image header at address "
+       << reinterpret_cast<const void*>(&boot_image_header)
+       << "\n\n";
+
+    const uint8_t* image_begin_unaligned = boot_image_header.GetImageBegin();
+    const uint8_t* image_end_unaligned = image_begin_unaligned + boot_image_header.GetImageSize();
+
+    // Adjust range to nearest page
+    const uint8_t* image_begin = AlignDown(image_begin_unaligned, kPageSize);
+    const uint8_t* image_end = AlignUp(image_end_unaligned, kPageSize);
+
+    ptrdiff_t page_off_begin = boot_image_header.GetImageBegin() - image_begin;
+
+    if (reinterpret_cast<uintptr_t>(image_begin) > boot_map.start ||
+        reinterpret_cast<uintptr_t>(image_end) < boot_map.end) {
+      // Sanity check that we aren't trying to read a completely different boot image
+      os << "Remote boot map is out of range of local boot map: " <<
+        "local begin " << reinterpret_cast<const void*>(image_begin) <<
+        ", local end " << reinterpret_cast<const void*>(image_end) <<
+        ", remote begin " << reinterpret_cast<const void*>(boot_map.start) <<
+        ", remote end " << reinterpret_cast<const void*>(boot_map.end);
+      return false;
+      // If we wanted even more validation we could map the ImageHeader from the file
+    }
+
+    std::vector<uint8_t> remote_contents(boot_map_size);
+    if (!map_file->PreadFully(&remote_contents[0], boot_map_size, boot_map.start)) {
+      os << "Could not fully read file " << file_name;
+      return false;
+    }
+
+    std::string page_map_file_name = StringPrintf("/proc/%ld/pagemap",
+                                                  static_cast<long>(image_diff_pid));  // NOLINT [runtime/int]
+    auto page_map_file = std::unique_ptr<File>(OS::OpenFileForReading(page_map_file_name.c_str()));
+    if (page_map_file == nullptr) {
+      os << "Failed to open " << page_map_file_name << " for reading: " << strerror(errno);
+      return false;
+    }
+
+    // Not truly clean, mmap-ing boot.art again would be more pristine, but close enough
+    const char* clean_page_map_file_name = "/proc/self/pagemap";
+    auto clean_page_map_file = std::unique_ptr<File>(
+        OS::OpenFileForReading(clean_page_map_file_name));
+    if (clean_page_map_file == nullptr) {
+      os << "Failed to open " << clean_page_map_file_name << " for reading: " << strerror(errno);
+      return false;
+    }
+
+    auto kpage_flags_file = std::unique_ptr<File>(OS::OpenFileForReading("/proc/kpageflags"));
+    if (kpage_flags_file == nullptr) {
+      os << "Failed to open /proc/kpageflags for reading: " << strerror(errno);
+      return false;
+    }
+
+    auto kpage_count_file = std::unique_ptr<File>(OS::OpenFileForReading("/proc/kpagecount"));
+    if (kpage_count_file == nullptr) {
+      os << "Failed to open /proc/kpagecount for reading:" << strerror(errno);
+      return false;
+    }
+
+    std::set<size_t> dirty_page_set_remote;  // Set of the remote virtual page indices that are dirty
+    std::set<size_t> dirty_page_set_local;   // Set of the local virtual page indices that are dirty
+
+    size_t different_int32s = 0;
+    size_t different_bytes = 0;
+    size_t different_pages = 0;
+    size_t virtual_page_idx = 0;   // Virtual page number (for an absolute memory address)
+    size_t page_idx = 0;           // Page index relative to 0
+    size_t previous_page_idx = 0;  // Previous page index relative to 0
+    size_t dirty_pages = 0;
+    size_t private_pages = 0;
+    size_t private_dirty_pages = 0;
+
+    // Iterate through one page at a time. Boot map begin/end already implicitly aligned.
+    for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += kPageSize) {
+      ptrdiff_t offset = begin - boot_map.start;
+
+      // We treat the image header as part of the memory map for now
+      // If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
+      // But it might still be interesting to see if any of the ImageHeader data mutated
+      const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&boot_image_header) + offset;
+      uint8_t* remote_ptr = &remote_contents[offset];
+
+      if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) {
+        different_pages++;
+
+        // Count the number of 32-bit integers that are different.
+        for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) {
+          uint32_t* remote_ptr_int32 = reinterpret_cast<uint32_t*>(remote_ptr);
+          const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr);
+
+          if (remote_ptr_int32[i] != local_ptr_int32[i]) {
+            different_int32s++;
+          }
+        }
+      }
+    }
+
+    // Iterate through one byte at a time.
+    for (uintptr_t begin = boot_map.start; begin != boot_map.end; ++begin) {
+      previous_page_idx = page_idx;
+      ptrdiff_t offset = begin - boot_map.start;
+
+      // We treat the image header as part of the memory map for now
+      // If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
+      // But it might still be interesting to see if any of the ImageHeader data mutated
+      const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&boot_image_header) + offset;
+      uint8_t* remote_ptr = &remote_contents[offset];
+
+      virtual_page_idx = reinterpret_cast<uintptr_t>(local_ptr) / kPageSize;
+
+      // Calculate the page index, relative to the 0th page where the image begins
+      page_idx = (offset + page_off_begin) / kPageSize;
+      if (*local_ptr != *remote_ptr) {
+        // Track number of bytes that are different
+        different_bytes++;
+      }
+
+      // Independently count the # of dirty pages on the remote side
+      size_t remote_virtual_page_idx = begin / kPageSize;
+      if (previous_page_idx != page_idx) {
+        uint64_t page_count = 0xC0FFEE;
+        // TODO: virtual_page_idx needs to be from the same process
+        int dirtiness = (IsPageDirty(page_map_file.get(),        // Image-diff-pid procmap
+                                     clean_page_map_file.get(),  // Self procmap
+                                     kpage_flags_file.get(),
+                                     kpage_count_file.get(),
+                                     remote_virtual_page_idx,    // potentially "dirty" page
+                                     virtual_page_idx,           // true "clean" page
+                                     &page_count,
+                                     &error_msg));
+        if (dirtiness < 0) {
+          os << error_msg;
+          return false;
+        } else if (dirtiness > 0) {
+          dirty_pages++;
+          dirty_page_set_remote.insert(dirty_page_set_remote.end(), remote_virtual_page_idx);
+          dirty_page_set_local.insert(dirty_page_set_local.end(), virtual_page_idx);
+        }
+
+        bool is_dirty = dirtiness > 0;
+        bool is_private = page_count == 1;
+
+        if (page_count == 1) {
+          private_pages++;
+        }
+
+        if (is_dirty && is_private) {
+          private_dirty_pages++;
+        }
+      }
+    }
+
+    // Walk each object in the remote image space and compare it against ours
+    size_t different_objects = 0;
+    std::map<mirror::Class*, int /*count*/> dirty_object_class_map;
+    // Track only the byte-per-byte dirtiness (in bytes)
+    std::map<mirror::Class*, int /*byte_count*/> dirty_object_byte_count;
+    // Track the object-by-object dirtiness (in bytes)
+    std::map<mirror::Class*, int /*byte_count*/> dirty_object_size_in_bytes;
+    std::map<mirror::Class*, int /*count*/> clean_object_class_map;
+
+    std::map<mirror::Class*, std::string> class_to_descriptor_map;
+
+    std::map<off_t /* field offset */, int /* count */> art_method_field_dirty_count;
+    std::vector<mirror::ArtMethod*> art_method_dirty_objects;
+
+    std::map<off_t /* field offset */, int /* count */> class_field_dirty_count;
+    std::vector<mirror::Class*> class_dirty_objects;
+
+    // List of local objects that are clean, but located on dirty pages.
+    std::vector<mirror::Object*> false_dirty_objects;
+    std::map<mirror::Class*, int /*byte_count*/> false_dirty_byte_count;
+    std::map<mirror::Class*, int /*object_count*/> false_dirty_object_count;
+    std::map<mirror::Class*, std::vector<mirror::Object*>> false_dirty_objects_map;
+    size_t false_dirty_object_bytes = 0;
+
+    // Remote pointers to dirty objects
+    std::map<mirror::Class*, std::vector<mirror::Object*>> dirty_objects_by_class;
+    // Look up remote classes by their descriptor
+    std::map<std::string, mirror::Class*> remote_class_map;
+    // Look up local classes by their descriptor
+    std::map<std::string, mirror::Class*> local_class_map;
+
+    size_t dirty_object_bytes = 0;
+    {
+      const uint8_t* begin_image_ptr = image_begin_unaligned;
+      const uint8_t* end_image_ptr = image_end_unaligned;
+
+      const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment);
+      while (reinterpret_cast<const uintptr_t>(current)
+             < reinterpret_cast<const uintptr_t>(end_image_ptr)) {
+        CHECK_ALIGNED(current, kObjectAlignment);
+        mirror::Object* obj = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current));
+
+        // Sanity check that we are reading a real object
+        CHECK(obj->GetClass() != nullptr) << "Image object at address " << obj << " has null class";
+        if (kUseBakerOrBrooksReadBarrier) {
+          obj->AssertReadBarrierPointer();
+        }
+
+        // Iterate every page this object belongs to
+        bool on_dirty_page = false;
+        size_t page_off = 0;
+        size_t current_page_idx;
+        uintptr_t object_address;
+        do {
+          object_address = reinterpret_cast<uintptr_t>(current);
+          current_page_idx = object_address / kPageSize + page_off;
+
+          if (dirty_page_set_local.find(current_page_idx) != dirty_page_set_local.end()) {
+            // This object is on a dirty page
+            on_dirty_page = true;
+          }
+
+          page_off++;
+        } while ((current_page_idx * kPageSize) <
+                 RoundUp(object_address + obj->SizeOf(), kObjectAlignment));
+
+        mirror::Class* klass = obj->GetClass();
+
+        bool different_object = false;
+
+        // Check against the other object and see if they are different
+        ptrdiff_t offset = current - begin_image_ptr;
+        const uint8_t* current_remote = &remote_contents[offset];
+        mirror::Object* remote_obj = reinterpret_cast<mirror::Object*>(
+            const_cast<uint8_t*>(current_remote));
+        if (memcmp(current, current_remote, obj->SizeOf()) != 0) {
+          different_objects++;
+          dirty_object_bytes += obj->SizeOf();
+
+          ++dirty_object_class_map[klass];
+
+          // Go byte-by-byte and figure out what exactly got dirtied
+          size_t dirty_byte_count_per_object = 0;
+          for (size_t i = 0; i < obj->SizeOf(); ++i) {
+            if (current[i] != current_remote[i]) {
+              dirty_byte_count_per_object++;
+            }
+          }
+          dirty_object_byte_count[klass] += dirty_byte_count_per_object;
+          dirty_object_size_in_bytes[klass] += obj->SizeOf();
+
+          different_object = true;
+
+          dirty_objects_by_class[klass].push_back(remote_obj);
+        } else {
+          ++clean_object_class_map[klass];
+        }
+
+        std::string descriptor = GetClassDescriptor(klass);
+        if (different_object) {
+          if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+            // this is a "Class"
+            mirror::Class* obj_as_class  = reinterpret_cast<mirror::Class*>(remote_obj);
+
+            // print the fields that are dirty
+            for (size_t i = 0; i < obj->SizeOf(); ++i) {
+              if (current[i] != current_remote[i]) {
+                class_field_dirty_count[i]++;
+              }
+            }
+
+            class_dirty_objects.push_back(obj_as_class);
+          } else if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
+            // this is an ArtMethod
+            mirror::ArtMethod* art_method = reinterpret_cast<mirror::ArtMethod*>(remote_obj);
+
+            // print the fields that are dirty
+            for (size_t i = 0; i < obj->SizeOf(); ++i) {
+              if (current[i] != current_remote[i]) {
+                art_method_field_dirty_count[i]++;
+              }
+            }
+
+            art_method_dirty_objects.push_back(art_method);
+          }
+        } else if (on_dirty_page) {
+          // This object was either never mutated or got mutated back to the same value.
+          // TODO: Do I want to distinguish a "different" vs a "dirty" page here?
+          false_dirty_objects.push_back(obj);
+          false_dirty_objects_map[klass].push_back(obj);
+          false_dirty_object_bytes += obj->SizeOf();
+          false_dirty_byte_count[obj->GetClass()] += obj->SizeOf();
+          false_dirty_object_count[obj->GetClass()] += 1;
+        }
+
+        if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+          local_class_map[descriptor] = reinterpret_cast<mirror::Class*>(obj);
+          remote_class_map[descriptor] = reinterpret_cast<mirror::Class*>(remote_obj);
+        }
+
+        // Unconditionally store the class descriptor in case we need it later
+        class_to_descriptor_map[klass] = descriptor;
+        current += RoundUp(obj->SizeOf(), kObjectAlignment);
+      }
+    }
+
+    // Looking at only dirty pages, figure out how many of those bytes belong to dirty objects.
+    float true_dirtied_percent = dirty_object_bytes * 1.0f / (dirty_pages * kPageSize);
+    size_t false_dirty_pages = dirty_pages - different_pages;
+
+    os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
+       << reinterpret_cast<void*>(boot_map.end) << ") had: \n  "
+       << different_bytes << " differing bytes, \n  "
+       << different_int32s << " differing int32s, \n  "
+       << different_objects << " different objects, \n  "
+       << dirty_object_bytes << " different object [bytes], \n  "
+       << false_dirty_objects.size() << " false dirty objects,\n  "
+       << false_dirty_object_bytes << " false dirty object [bytes], \n  "
+       << true_dirtied_percent << " different objects-vs-total in a dirty page;\n  "
+       << different_pages << " different pages; \n  "
+       << dirty_pages << " pages are dirty; \n  "
+       << false_dirty_pages << " pages are false dirty; \n  "
+       << private_pages << " pages are private; \n  "
+       << private_dirty_pages << " pages are Private_Dirty\n  "
+       << "";
+
+    // vector of pairs (int count, Class*)
+    auto dirty_object_class_values = SortByValueDesc(dirty_object_class_map);
+    auto clean_object_class_values = SortByValueDesc(clean_object_class_map);
+
+    os << "\n" << "  Dirty object count by class:\n";
+    for (const auto& vk_pair : dirty_object_class_values) {
+      int dirty_object_count = vk_pair.first;
+      mirror::Class* klass = vk_pair.second;
+      int object_sizes = dirty_object_size_in_bytes[klass];
+      float avg_dirty_bytes_per_class = dirty_object_byte_count[klass] * 1.0f / object_sizes;
+      float avg_object_size = object_sizes * 1.0f / dirty_object_count;
+      const std::string& descriptor = class_to_descriptor_map[klass];
+      os << "    " << PrettyClass(klass) << " ("
+         << "objects: " << dirty_object_count << ", "
+         << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", "
+         << "avg object size: " << avg_object_size << ", "
+         << "class descriptor: '" << descriptor << "'"
+         << ")\n";
+
+      constexpr size_t kMaxAddressPrint = 5;
+      if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
+        os << "      sample object addresses: ";
+        for (size_t i = 0; i < art_method_dirty_objects.size() && i < kMaxAddressPrint; ++i) {
+          auto art_method = art_method_dirty_objects[i];
+
+          os << reinterpret_cast<void*>(art_method) << ", ";
+        }
+        os << "\n";
+
+        os << "      dirty byte +offset:count list = ";
+        auto art_method_field_dirty_count_sorted = SortByValueDesc(art_method_field_dirty_count);
+        for (auto pair : art_method_field_dirty_count_sorted) {
+          off_t offset = pair.second;
+          int count = pair.first;
+
+          os << "+" << offset << ":" << count << ", ";
+        }
+
+        os << "\n";
+
+        os << "      field contents:\n";
+        const auto& dirty_objects_list = dirty_objects_by_class[klass];
+        for (mirror::Object* obj : dirty_objects_list) {
+          // remote method
+          auto art_method = reinterpret_cast<mirror::ArtMethod*>(obj);
+
+          // remote class
+          mirror::Class* remote_declaring_class =
+            FixUpRemotePointer(art_method->GetDeclaringClass(), remote_contents, boot_map);
+
+          // local class
+          mirror::Class* declaring_class =
+            RemoteContentsPointerToLocal(remote_declaring_class,
+                                         remote_contents,
+                                         boot_image_header);
+
+          os << "        " << reinterpret_cast<void*>(obj) << " ";
+          os << "  entryPointFromJni: "
+             << reinterpret_cast<const void*>(
+                    art_method->GetEntryPointFromJniPtrSize(pointer_size)) << ", ";
+          os << "  entryPointFromInterpreter: "
+             << reinterpret_cast<const void*>(
+                    art_method->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size))
+             << ", ";
+          os << "  entryPointFromQuickCompiledCode: "
+             << reinterpret_cast<const void*>(
+                    art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
+             << ", ";
+          os << "  isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
+          os << "  class_status (local): " << declaring_class->GetStatus();
+          os << "  class_status (remote): " << remote_declaring_class->GetStatus();
+          os << "\n";
+        }
+      }
+      if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) {
+        os << "       sample object addresses: ";
+        for (size_t i = 0; i < class_dirty_objects.size() && i < kMaxAddressPrint; ++i) {
+          auto class_ptr = class_dirty_objects[i];
+
+          os << reinterpret_cast<void*>(class_ptr) << ", ";
+        }
+        os << "\n";
+
+        os << "       dirty byte +offset:count list = ";
+        auto class_field_dirty_count_sorted = SortByValueDesc(class_field_dirty_count);
+        for (auto pair : class_field_dirty_count_sorted) {
+          off_t offset = pair.second;
+          int count = pair.first;
+
+          os << "+" << offset << ":" << count << ", ";
+        }
+        os << "\n";
+
+        os << "      field contents:\n";
+        const auto& dirty_objects_list = dirty_objects_by_class[klass];
+        for (mirror::Object* obj : dirty_objects_list) {
+          // remote class object
+          auto remote_klass = reinterpret_cast<mirror::Class*>(obj);
+
+          // local class object
+          auto local_klass = RemoteContentsPointerToLocal(remote_klass,
+                                                          remote_contents,
+                                                          boot_image_header);
+
+          os << "        " << reinterpret_cast<void*>(obj) << " ";
+          os << "  class_status (remote): " << remote_klass->GetStatus() << ", ";
+          os << "  class_status (local): " << local_klass->GetStatus();
+          os << "\n";
+        }
+      }
+    }
+
+    auto false_dirty_object_class_values = SortByValueDesc(false_dirty_object_count);
+
+    os << "\n" << "  False-dirty object count by class:\n";
+    for (const auto& vk_pair : false_dirty_object_class_values) {
+      int object_count = vk_pair.first;
+      mirror::Class* klass = vk_pair.second;
+      int object_sizes = false_dirty_byte_count[klass];
+      float avg_object_size = object_sizes * 1.0f / object_count;
+      const std::string& descriptor = class_to_descriptor_map[klass];
+      os << "    " << PrettyClass(klass) << " ("
+         << "objects: " << object_count << ", "
+         << "avg object size: " << avg_object_size << ", "
+         << "total bytes: " << object_sizes << ", "
+         << "class descriptor: '" << descriptor << "'"
+         << ")\n";
+
+      if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) {
+        auto& art_method_false_dirty_objects = false_dirty_objects_map[klass];
+
+        os << "      field contents:\n";
+        for (mirror::Object* obj : art_method_false_dirty_objects) {
+          // local method
+          auto art_method = reinterpret_cast<mirror::ArtMethod*>(obj);
+
+          // local class
+          mirror::Class* declaring_class = art_method->GetDeclaringClass();
+
+          os << "        " << reinterpret_cast<void*>(obj) << " ";
+          os << "  entryPointFromJni: "
+             << reinterpret_cast<const void*>(
+                    art_method->GetEntryPointFromJniPtrSize(pointer_size)) << ", ";
+          os << "  entryPointFromInterpreter: "
+             << reinterpret_cast<const void*>(
+                    art_method->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size))
+             << ", ";
+          os << "  entryPointFromQuickCompiledCode: "
+             << reinterpret_cast<const void*>(
+                    art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
+             << ", ";
+          os << "  isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
+          os << "  class_status (local): " << declaring_class->GetStatus();
+          os << "\n";
+        }
+      }
+    }
+
+    os << "\n" << "  Clean object count by class:\n";
+    for (const auto& vk_pair : clean_object_class_values) {
+      os << "    " << PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n";
+    }
+
+    return true;
+  }
+
+  // Fixup a remote pointer that we read from a foreign boot.art to point to our own memory.
+  // Returned pointer will point to inside of remote_contents.
+  template <typename T>
+  static T* FixUpRemotePointer(T* remote_ptr,
+                               std::vector<uint8_t>& remote_contents,
+                               const backtrace_map_t& boot_map) {
+    if (remote_ptr == nullptr) {
+      return nullptr;
+    }
+
+    uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr);
+
+    CHECK_LE(boot_map.start, remote);
+    CHECK_GT(boot_map.end, remote);
+
+    off_t boot_offset = remote - boot_map.start;
+
+    return reinterpret_cast<T*>(&remote_contents[boot_offset]);
+  }
+
+  template <typename T>
+  static T* RemoteContentsPointerToLocal(T* remote_ptr,
+                                         std::vector<uint8_t>& remote_contents,
+                                         const ImageHeader& image_header) {
+    if (remote_ptr == nullptr) {
+      return nullptr;
+    }
+
+    uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr);
+    ptrdiff_t boot_offset = remote - &remote_contents[0];
+
+    const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset;
+
+    return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr));
+  }
+
+  static std::string GetClassDescriptor(mirror::Class* klass)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CHECK(klass != nullptr);
+
+    std::string descriptor;
+    const char* descriptor_str = klass->GetDescriptor(&descriptor);
+
+    return std::string(descriptor_str);
+  }
+
+  template <typename K, typename V>
+  static std::vector<std::pair<V, K>> SortByValueDesc(const std::map<K, V> map) {
+    // Store value->key so that we can use the default sort from pair which
+    // sorts by value first and then key
+    std::vector<std::pair<V, K>> value_key_vector;
+
+    for (const auto& kv_pair : map) {
+      value_key_vector.push_back(std::make_pair(kv_pair.second, kv_pair.first));
+    }
+
+    // Sort in reverse (descending order)
+    std::sort(value_key_vector.rbegin(), value_key_vector.rend());
+    return value_key_vector;
+  }
+
+  static bool GetPageFrameNumber(File* page_map_file,
+                                size_t virtual_page_index,
+                                uint64_t* page_frame_number,
+                                std::string* error_msg) {
+    CHECK(page_map_file != nullptr);
+    CHECK(page_frame_number != nullptr);
+    CHECK(error_msg != nullptr);
+
+    constexpr size_t kPageMapEntrySize = sizeof(uint64_t);
+    constexpr uint64_t kPageFrameNumberMask = (1ULL << 55) - 1;  // bits 0-54 [in /proc/$pid/pagemap]
+    constexpr uint64_t kPageSoftDirtyMask = (1ULL << 55);  // bit 55 [in /proc/$pid/pagemap]
+
+    uint64_t page_map_entry = 0;
+
+    // Read 64-bit entry from /proc/$pid/pagemap to get the physical page frame number
+    if (!page_map_file->PreadFully(&page_map_entry, kPageMapEntrySize,
+                                  virtual_page_index * kPageMapEntrySize)) {
+      *error_msg = StringPrintf("Failed to read the virtual page index entry from %s",
+                                page_map_file->GetPath().c_str());
+      return false;
+    }
+
+    // TODO: seems useless, remove this.
+    bool soft_dirty = (page_map_entry & kPageSoftDirtyMask) != 0;
+    if ((false)) {
+      LOG(VERBOSE) << soft_dirty;  // Suppress unused warning
+      UNREACHABLE();
+    }
+
+    *page_frame_number = page_map_entry & kPageFrameNumberMask;
+
+    return true;
+  }
+
+  static int IsPageDirty(File* page_map_file,
+                         File* clean_page_map_file,
+                         File* kpage_flags_file,
+                         File* kpage_count_file,
+                         size_t virtual_page_idx,
+                         size_t clean_virtual_page_idx,
+                         // Out parameters:
+                         uint64_t* page_count, std::string* error_msg) {
+    CHECK(page_map_file != nullptr);
+    CHECK(clean_page_map_file != nullptr);
+    CHECK_NE(page_map_file, clean_page_map_file);
+    CHECK(kpage_flags_file != nullptr);
+    CHECK(kpage_count_file != nullptr);
+    CHECK(page_count != nullptr);
+    CHECK(error_msg != nullptr);
+
+    // Constants are from https://www.kernel.org/doc/Documentation/vm/pagemap.txt
+
+    constexpr size_t kPageFlagsEntrySize = sizeof(uint64_t);
+    constexpr size_t kPageCountEntrySize = sizeof(uint64_t);
+    constexpr uint64_t kPageFlagsDirtyMask = (1ULL << 4);  // in /proc/kpageflags
+    constexpr uint64_t kPageFlagsNoPageMask = (1ULL << 20);  // in /proc/kpageflags
+    constexpr uint64_t kPageFlagsMmapMask = (1ULL << 11);  // in /proc/kpageflags
+
+    uint64_t page_frame_number = 0;
+    if (!GetPageFrameNumber(page_map_file, virtual_page_idx, &page_frame_number, error_msg)) {
+      return -1;
+    }
+
+    uint64_t page_frame_number_clean = 0;
+    if (!GetPageFrameNumber(clean_page_map_file, clean_virtual_page_idx, &page_frame_number_clean,
+                            error_msg)) {
+      return -1;
+    }
+
+    // Read 64-bit entry from /proc/kpageflags to get the dirty bit for a page
+    uint64_t kpage_flags_entry = 0;
+    if (!kpage_flags_file->PreadFully(&kpage_flags_entry,
+                                     kPageFlagsEntrySize,
+                                     page_frame_number * kPageFlagsEntrySize)) {
+      *error_msg = StringPrintf("Failed to read the page flags from %s",
+                                kpage_flags_file->GetPath().c_str());
+      return -1;
+    }
+
+    // Read 64-bit entyry from /proc/kpagecount to get mapping counts for a page
+    if (!kpage_count_file->PreadFully(page_count /*out*/,
+                                     kPageCountEntrySize,
+                                     page_frame_number * kPageCountEntrySize)) {
+      *error_msg = StringPrintf("Failed to read the page count from %s",
+                                kpage_count_file->GetPath().c_str());
+      return -1;
+    }
+
+    // There must be a page frame at the requested address.
+    CHECK_EQ(kpage_flags_entry & kPageFlagsNoPageMask, 0u);
+    // The page frame must be memory mapped
+    CHECK_NE(kpage_flags_entry & kPageFlagsMmapMask, 0u);
+
+    // Page is dirty, i.e. has diverged from file, if the 4th bit is set to 1
+    bool flags_dirty = (kpage_flags_entry & kPageFlagsDirtyMask) != 0;
+
+    // page_frame_number_clean must come from the *same* process
+    // but a *different* mmap than page_frame_number
+    if (flags_dirty) {
+      CHECK_NE(page_frame_number, page_frame_number_clean);
+    }
+
+    return page_frame_number != page_frame_number_clean;
+  }
+
+  static const ImageHeader& GetBootImageHeader() {
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    gc::space::ImageSpace* image_space = heap->GetImageSpace();
+    CHECK(image_space != nullptr);
+    const ImageHeader& image_header = image_space->GetImageHeader();
+    return image_header;
+  }
+
+ private:
+  // Return the image location, stripped of any directories, e.g. "boot.art" or "core.art"
+  std::string GetImageLocationBaseName() const {
+    return BaseName(std::string(image_location_));
+  }
+
+  std::ostream* os_;
+  const ImageHeader& image_header_;
+  const char* image_location_;
+  pid_t image_diff_pid_;  // Dump image diff against boot.art if pid is non-negative
+
+  DISALLOW_COPY_AND_ASSIGN(ImgDiagDumper);
+};
+
+static int DumpImage(Runtime* runtime, const char* image_location,
+                     std::ostream* os, pid_t image_diff_pid) {
+  ScopedObjectAccess soa(Thread::Current());
+  gc::Heap* heap = runtime->GetHeap();
+  gc::space::ImageSpace* image_space = heap->GetImageSpace();
+  CHECK(image_space != nullptr);
+  const ImageHeader& image_header = image_space->GetImageHeader();
+  if (!image_header.IsValid()) {
+    fprintf(stderr, "Invalid image header %s\n", image_location);
+    return EXIT_FAILURE;
+  }
+
+  ImgDiagDumper img_diag_dumper(os, image_header, image_location, image_diff_pid);
+
+  bool success = img_diag_dumper.Dump();
+  return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+struct ImgDiagArgs : public CmdlineArgs {
+ protected:
+  using Base = CmdlineArgs;
+
+  virtual ParseStatus ParseCustom(const StringPiece& option,
+                                  std::string* error_msg) OVERRIDE {
+    {
+      ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+      if (base_parse != kParseUnknownArgument) {
+        return base_parse;
+      }
+    }
+
+    if (option.starts_with("--image-diff-pid=")) {
+      const char* image_diff_pid = option.substr(strlen("--image-diff-pid=")).data();
+
+      if (!ParseInt(image_diff_pid, &image_diff_pid_)) {
+        *error_msg = "Image diff pid out of range";
+        return kParseError;
+      }
+    } else {
+      return kParseUnknownArgument;
+    }
+
+    return kParseOk;
+  }
+
+  virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE {
+    // Perform the parent checks.
+    ParseStatus parent_checks = Base::ParseChecks(error_msg);
+    if (parent_checks != kParseOk) {
+      return parent_checks;
+    }
+
+    // Perform our own checks.
+
+    if (kill(image_diff_pid_,
+             /*sig*/0) != 0) {  // No signal is sent, perform error-checking only.
+      // Check if the pid exists before proceeding.
+      if (errno == ESRCH) {
+        *error_msg = "Process specified does not exist";
+      } else {
+        *error_msg = StringPrintf("Failed to check process status: %s", strerror(errno));
+      }
+      return kParseError;
+    } else if (instruction_set_ != kRuntimeISA) {
+      // Don't allow different ISAs since the images are ISA-specific.
+      // Right now the code assumes both the runtime ISA and the remote ISA are identical.
+      *error_msg = "Must use the default runtime ISA; changing ISA is not supported.";
+      return kParseError;
+    }
+
+    return kParseOk;
+  }
+
+  virtual std::string GetUsage() const {
+    std::string usage;
+
+    usage +=
+        "Usage: imgdiag [options] ...\n"
+        "    Example: imgdiag --image-diff-pid=$(pidof dex2oat)\n"
+        "    Example: adb shell imgdiag --image-diff-pid=$(pid zygote)\n"
+        "\n";
+
+    usage += Base::GetUsage();
+
+    usage +=  // Optional.
+        "  --image-diff-pid=<pid>: provide the PID of a process whose boot.art you want to diff.\n"
+        "      Example: --image-diff-pid=$(pid zygote)\n"
+        "\n";
+
+    return usage;
+  }
+
+ public:
+  pid_t image_diff_pid_ = -1;
+};
+
+struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
+  virtual bool ExecuteWithRuntime(Runtime* runtime) {
+    CHECK(args_ != nullptr);
+
+    return DumpImage(runtime,
+                     args_->boot_image_location_,
+                     args_->os_,
+                     args_->image_diff_pid_) == EXIT_SUCCESS;
+  }
+};
+
+}  // namespace art
+
+int main(int argc, char** argv) {
+  art::ImgDiagMain main;
+  return main.Main(argc, argv);
+}
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
new file mode 100644
index 0000000..1ac7930
--- /dev/null
+++ b/imgdiag/imgdiag_test.cc
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "common_runtime_test.h"
+
+#include "runtime/os.h"
+#include "runtime/arch/instruction_set.h"
+#include "runtime/utils.h"
+#include "runtime/gc/space/image_space.h"
+#include "runtime/gc/heap.h"
+#include "base/stringprintf.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace art {
+
+static const char* kImgDiagDiffPid = "--image-diff-pid";
+static const char* kImgDiagBootImage = "--boot-image";
+static const char* kImgDiagBinaryName = "imgdiag";
+
+class ImgDiagTest : public CommonRuntimeTest {
+ protected:
+  virtual void SetUp() {
+    CommonRuntimeTest::SetUp();
+
+    // We loaded the runtime with an explicit image. Therefore the image space must exist.
+    gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+    ASSERT_TRUE(image_space != nullptr);
+    boot_image_location_ = image_space->GetImageLocation();
+  }
+
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    // Needs to live until CommonRuntimeTest::SetUp finishes, since we pass it a cstring.
+    runtime_args_image_ = StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
+    options->push_back(std::make_pair(runtime_args_image_, nullptr));
+  }
+
+  // Path to the imgdiag(d?)[32|64] binary.
+  std::string GetImgDiagFilePath() {
+    std::string root = GetTestAndroidRoot();
+
+    root += "/bin/";
+    root += kImgDiagBinaryName;
+
+    if (kIsDebugBuild) {
+      root += "d";
+    }
+
+    std::string root32 = root + "32";
+    // If we have both a 32-bit and a 64-bit build, the 32-bit file will have a 32 suffix.
+    if (OS::FileExists(root32.c_str()) && !Is64BitInstructionSet(kRuntimeISA)) {
+      return root32;
+    // Only a single build exists, so the filename never has an extra suffix.
+    } else {
+      return root;
+    }
+  }
+
+  // Run imgdiag with a custom boot image location.
+  bool Exec(pid_t image_diff_pid, const std::string& boot_image, std::string* error_msg) {
+    // Invoke 'img_diag' against the current process.
+    // This should succeed because we have a runtime and so it should
+    // be able to map in the boot.art and do a diff for it.
+    std::string file_path = GetImgDiagFilePath();
+    EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+
+    // Run imgdiag --image-diff-pid=$image_diff_pid and wait until it's done with a 0 exit code.
+    std::string diff_pid_args;
+    {
+      std::stringstream diff_pid_args_ss;
+      diff_pid_args_ss << kImgDiagDiffPid << "=" << image_diff_pid;
+      diff_pid_args = diff_pid_args_ss.str();
+    }
+    std::string boot_image_args;
+    {
+      boot_image_args = boot_image_args + kImgDiagBootImage + "=" + boot_image;
+    }
+
+    std::vector<std::string> exec_argv = { file_path, diff_pid_args, boot_image_args };
+
+    return ::art::Exec(exec_argv, error_msg);
+  }
+
+  // Run imgdiag with the default boot image location.
+  bool ExecDefaultBootImage(pid_t image_diff_pid, std::string* error_msg) {
+    return Exec(image_diff_pid, boot_image_location_, error_msg);
+  }
+
+ private:
+  std::string runtime_args_image_;
+  std::string boot_image_location_;
+};
+
+#if defined (ART_TARGET)
+TEST_F(ImgDiagTest, ImageDiffPidSelf) {
+#else
+// Can't run this test on the host, it will fail when trying to open /proc/kpagestats
+// because it's root read-only.
+TEST_F(ImgDiagTest, DISABLED_ImageDiffPidSelf) {
+#endif
+  // Invoke 'img_diag' against the current process.
+  // This should succeed because we have a runtime and so it should
+  // be able to map in the boot.art and do a diff for it.
+
+  // Run imgdiag --image-diff-pid=$(self pid) and wait until it's done with a 0 exit code.
+  std::string error_msg;
+  ASSERT_TRUE(ExecDefaultBootImage(getpid(), &error_msg)) << "Failed to execute -- because: "
+                                                          << error_msg;
+}
+
+TEST_F(ImgDiagTest, ImageDiffBadPid) {
+  // Invoke 'img_diag' against a non-existing process. This should fail.
+
+  // Run imgdiag --image-diff-pid=some_bad_pid and wait until it's done with a 0 exit code.
+  std::string error_msg;
+  ASSERT_FALSE(ExecDefaultBootImage(-12345, &error_msg)) << "Incorrectly executed";
+  UNUSED(error_msg);
+}
+
+}  // namespace art
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index b8a3b49..f01afc5 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -21,19 +21,8 @@
 OATDUMP_SRC_FILES := \
 	oatdump.cc
 
-ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
-endif
-ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libartd-compiler,art/disassembler art/compiler,target,debug))
-endif
-
-ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
-endif
-ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libartd-compiler,art/disassembler art/compiler,host,debug))
-endif
+# Build variants {target,host} x {debug,ndebug}
+$(eval $(call build-art-multi-executable,oatdump,$(OATDUMP_SRC_FILES),libart-compiler libart-disassembler,libcutils,,art/compiler art/disassembler))
 
 ########################################################################
 # oatdump targets
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b048833..931cca7 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -19,12 +19,13 @@
 
 #include <fstream>
 #include <iostream>
+#include <map>
+#include <set>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
 #include "arch/instruction_set_features.h"
-#include "base/stringpiece.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "class_linker-inl.h"
@@ -45,12 +46,10 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "noop_compiler_callbacks.h"
 #include "oat.h"
 #include "oat_file-inl.h"
 #include "os.h"
 #include "output_stream.h"
-#include "runtime.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
@@ -60,58 +59,10 @@
 #include "vmap_table.h"
 #include "well_known_classes.h"
 
-namespace art {
+#include <sys/stat.h>
+#include "cmdline.h"
 
-static void usage() {
-  fprintf(stderr,
-          "Usage: oatdump [options] ...\n"
-          "    Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n"
-          "    Example: adb shell oatdump --image=/system/framework/boot.art\n"
-          "\n");
-  fprintf(stderr,
-          "  --oat-file=<file.oat>: specifies an input oat filename.\n"
-          "      Example: --oat-file=/system/framework/boot.oat\n"
-          "\n");
-  fprintf(stderr,
-          "  --image=<file.art>: specifies an input image filename.\n"
-          "      Example: --image=/system/framework/boot.art\n"
-          "\n");
-  fprintf(stderr,
-          "  --boot-image=<file.art>: provide the image file for the boot class path.\n"
-          "      Example: --boot-image=/system/framework/boot.art\n"
-          "\n");
-  fprintf(stderr,
-          "  --instruction-set=(arm|arm64|mips|x86|x86_64): for locating the image\n"
-          "      file based on the image location set.\n"
-          "      Example: --instruction-set=x86\n"
-          "      Default: %s\n"
-          "\n",
-          GetInstructionSetString(kRuntimeISA));
-  fprintf(stderr,
-          "  --output=<file> may be used to send the output to a file.\n"
-          "      Example: --output=/tmp/oatdump.txt\n"
-          "\n");
-  fprintf(stderr,
-          "  --dump:raw_mapping_table enables dumping of the mapping table.\n"
-          "      Example: --dump:raw_mapping_table\n"
-          "\n");
-  fprintf(stderr,
-          "  --dump:raw_mapping_table enables dumping of the GC map.\n"
-          "      Example: --dump:raw_gc_map\n"
-          "\n");
-  fprintf(stderr,
-          "  --no-dump:vmap may be used to disable vmap dumping.\n"
-          "      Example: --no-dump:vmap\n"
-          "\n");
-  fprintf(stderr,
-          "  --no-disassemble may be used to disable disassembly.\n"
-          "      Example: --no-disassemble\n"
-          "\n");
-  fprintf(stderr,
-          "  --method-filter=<method name>: only dumps methods that contain the filter.\n"
-          "      Example: --method-filter=foo\n"
-          "\n");
-}
+namespace art {
 
 const char* image_roots_descriptions_[] = {
   "kResolutionMethod",
@@ -360,15 +311,14 @@
                    bool dump_vmap,
                    bool disassemble_code,
                    bool absolute_addresses,
-                   const char* method_filter,
-                   Handle<mirror::ClassLoader>* class_loader)
+                   const char* method_filter)
     : dump_raw_mapping_table_(dump_raw_mapping_table),
       dump_raw_gc_map_(dump_raw_gc_map),
       dump_vmap_(dump_vmap),
       disassemble_code_(disassemble_code),
       absolute_addresses_(absolute_addresses),
       method_filter_(method_filter),
-      class_loader_(class_loader) {}
+      class_loader_(nullptr) {}
 
   const bool dump_raw_mapping_table_;
   const bool dump_raw_gc_map_;
@@ -442,12 +392,6 @@
                            GetInterpreterToCompiledCodeBridgeOffset);
     DUMP_OAT_HEADER_OFFSET("JNI DLSYM LOOKUP",
                            GetJniDlsymLookupOffset);
-    DUMP_OAT_HEADER_OFFSET("PORTABLE IMT CONFLICT TRAMPOLINE",
-                           GetPortableImtConflictTrampolineOffset);
-    DUMP_OAT_HEADER_OFFSET("PORTABLE RESOLUTION TRAMPOLINE",
-                           GetPortableResolutionTrampolineOffset);
-    DUMP_OAT_HEADER_OFFSET("PORTABLE TO INTERPRETER BRIDGE",
-                           GetPortableToInterpreterBridgeOffset);
     DUMP_OAT_HEADER_OFFSET("QUICK GENERIC JNI TRAMPOLINE",
                            GetQuickGenericJniTrampolineOffset);
     DUMP_OAT_HEADER_OFFSET("QUICK IMT CONFLICT TRAMPOLINE",
@@ -851,11 +795,6 @@
       } else {
         const void* code = oat_method.GetQuickCode();
         uint32_t code_size = oat_method.GetQuickCodeSize();
-        if (code == nullptr) {
-          code = oat_method.GetPortableCode();
-          code_size = oat_method.GetPortableCodeSize();
-          code_size_offset = 0;
-        }
         uint32_t code_offset = oat_method.GetCodeOffset();
         uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
         uint64_t aligned_code_end = aligned_code_begin + code_size;
@@ -1054,23 +993,12 @@
       return;  // No GC map.
     }
     const void* quick_code = oat_method.GetQuickCode();
-    if (quick_code != nullptr) {
-      NativePcOffsetToReferenceMap map(gc_map_raw);
-      for (size_t entry = 0; entry < map.NumEntries(); entry++) {
-        const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(quick_code) +
-            map.GetNativePcOffset(entry);
-        os << StringPrintf("%p", native_pc);
-        DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
-      }
-    } else {
-      const void* portable_code = oat_method.GetPortableCode();
-      CHECK(portable_code != nullptr);
-      verifier::DexPcToReferenceMap map(gc_map_raw);
-      for (size_t entry = 0; entry < map.NumEntries(); entry++) {
-        uint32_t dex_pc = map.GetDexPc(entry);
-        os << StringPrintf("0x%08x", dex_pc);
-        DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
-      }
+    NativePcOffsetToReferenceMap map(gc_map_raw);
+    for (size_t entry = 0; entry < map.NumEntries(); entry++) {
+      const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(quick_code) +
+          map.GetNativePcOffset(entry);
+      os << StringPrintf("%p", native_pc);
+      DumpGcMapRegisters(os, oat_method, code_item, map.RegWidth() * 8, map.GetBitMap(entry));
     }
   }
 
@@ -1228,16 +1156,15 @@
   void DumpCode(std::ostream& os, verifier::MethodVerifier* verifier,
                 const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item,
                 bool bad_input, size_t code_size) {
-    const void* portable_code = oat_method.GetPortableCode();
     const void* quick_code = oat_method.GetQuickCode();
 
     if (code_size == 0) {
       code_size = oat_method.GetQuickCodeSize();
     }
-    if ((code_size == 0) || ((portable_code == nullptr) && (quick_code == nullptr))) {
+    if (code_size == 0 || quick_code == nullptr) {
       os << "NO CODE!\n";
       return;
-    } else if (quick_code != nullptr) {
+    } else {
       const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
       size_t offset = 0;
       while (offset < code_size) {
@@ -1255,9 +1182,6 @@
           }
         }
       }
-    } else {
-      CHECK(portable_code != nullptr);
-      CHECK_EQ(code_size, 0U);  // TODO: disassembly of portable is currently not supported.
     }
   }
 
@@ -1636,7 +1560,6 @@
           state->oat_dumper_->GetOatInstructionSet());
       mirror::ArtMethod* method = obj->AsArtMethod();
       if (method->IsNative()) {
-        // TODO: portable dumping.
         DCHECK(method->GetNativeGcMap(image_pointer_size) == nullptr) << PrettyMethod(method);
         DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
         bool first_occurrence;
@@ -1679,7 +1602,6 @@
           state->stats_.vmap_table_bytes += vmap_table_bytes;
         }
 
-        // TODO: portable dumping.
         const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method);
         const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method);
         uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
@@ -2011,45 +1933,6 @@
   DISALLOW_COPY_AND_ASSIGN(ImageDumper);
 };
 
-static NoopCompilerCallbacks callbacks;
-
-static Runtime* StartRuntime(const char* boot_image_location, const char* image_location,
-                             InstructionSet instruction_set) {
-  RuntimeOptions options;
-  std::string image_option;
-  std::string oat_option;
-  std::string boot_image_option;
-  std::string boot_oat_option;
-
-  // We are more like a compiler than a run-time. We don't want to execute code.
-  options.push_back(std::make_pair("compilercallbacks", &callbacks));
-
-  if (boot_image_location != nullptr) {
-    boot_image_option += "-Ximage:";
-    boot_image_option += boot_image_location;
-    options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
-  }
-  if (image_location != nullptr) {
-    image_option += "-Ximage:";
-    image_option += image_location;
-    options.push_back(std::make_pair(image_option.c_str(), nullptr));
-  }
-  options.push_back(
-      std::make_pair("imageinstructionset",
-                     reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
-
-  if (!Runtime::Create(options, false)) {
-    fprintf(stderr, "Failed to create runtime\n");
-    return nullptr;
-  }
-
-  // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
-  // give it away now and then switch to a more manageable ScopedObjectAccess.
-  Thread::Current()->TransitionFromRunnableToSuspended(kNative);
-
-  return Runtime::Current();
-}
-
 static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options,
                      std::ostream* os) {
   // Dumping the image, no explicit class loader.
@@ -2065,7 +1948,9 @@
     fprintf(stderr, "Invalid image header %s\n", image_location);
     return EXIT_FAILURE;
   }
+
   ImageDumper image_dumper(os, *image_space, image_header, options);
+
   bool success = image_dumper.Dump();
   return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
@@ -2083,13 +1968,13 @@
   ScopedObjectAccess soa(self);
   ClassLinker* class_linker = runtime->GetClassLinker();
   class_linker->RegisterOatFile(oat_file);
-  std::vector<const DexFile*> dex_files;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
   for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
     std::string error_msg;
-    const DexFile* dex_file = odf->OpenDexFile(&error_msg);
+    std::unique_ptr<const DexFile> dex_file = odf->OpenDexFile(&error_msg);
     CHECK(dex_file != nullptr) << error_msg;
     class_linker->RegisterDexFile(*dex_file);
-    dex_files.push_back(dex_file);
+    dex_files.push_back(std::move(dex_file));
   }
 
   // Need a class loader.
@@ -2098,7 +1983,11 @@
       soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
   jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
   // Fake that we're a compiler.
-  runtime->SetCompileTimeClassPath(class_loader, dex_files);
+  std::vector<const DexFile*> class_path;
+  for (auto& dex_file : dex_files) {
+    class_path.push_back(dex_file.get());
+  }
+  runtime->SetCompileTimeClassPath(class_loader, class_path);
 
   // Use the class loader while dumping.
   StackHandleScope<1> scope(self);
@@ -2124,7 +2013,8 @@
 static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
                    std::ostream* os) {
   std::string error_msg;
-  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
+  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
+                                    &error_msg);
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
@@ -2139,7 +2029,8 @@
 
 static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
   std::string error_msg;
-  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false, &error_msg);
+  OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
+                                    &error_msg);
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
@@ -2158,86 +2049,110 @@
   return EXIT_SUCCESS;
 }
 
-struct OatdumpArgs {
-  bool Parse(int argc, char** argv) {
-    // Skip over argv[0].
-    argv++;
-    argc--;
+struct OatdumpArgs : public CmdlineArgs {
+ protected:
+  using Base = CmdlineArgs;
 
-    if (argc == 0) {
-      fprintf(stderr, "No arguments specified\n");
-      usage();
-      return false;
-    }
-
-    for (int i = 0; i < argc; i++) {
-      const StringPiece option(argv[i]);
-      if (option.starts_with("--oat-file=")) {
-        oat_filename_ = option.substr(strlen("--oat-file=")).data();
-      } else if (option.starts_with("--image=")) {
-        image_location_ = option.substr(strlen("--image=")).data();
-      } else if (option.starts_with("--boot-image=")) {
-        boot_image_location_ = option.substr(strlen("--boot-image=")).data();
-      } else if (option.starts_with("--instruction-set=")) {
-        StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
-        instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
-        if (instruction_set_ == kNone) {
-          fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
-          usage();
-          return false;
-        }
-      } else if (option =="--dump:raw_mapping_table") {
-        dump_raw_mapping_table_ = true;
-      } else if (option == "--dump:raw_gc_map") {
-        dump_raw_gc_map_ = true;
-      } else if (option == "--no-dump:vmap") {
-        dump_vmap_ = false;
-      } else if (option == "--no-disassemble") {
-        disassemble_code_ = false;
-      } else if (option.starts_with("--output=")) {
-        output_name_ = option.substr(strlen("--output=")).ToString();
-        const char* filename = output_name_.c_str();
-        out_.reset(new std::ofstream(filename));
-        if (!out_->good()) {
-          fprintf(stderr, "Failed to open output filename %s\n", filename);
-          usage();
-          return false;
-        }
-        os_ = out_.get();
-      } else if (option.starts_with("--symbolize=")) {
-        oat_filename_ = option.substr(strlen("--symbolize=")).data();
-        symbolize_ = true;
-      } else if (option.starts_with("--method-filter=")) {
-        method_filter_ = option.substr(strlen("--method-filter=")).data();
-      } else {
-        fprintf(stderr, "Unknown argument %s\n", option.data());
-        usage();
-        return false;
+  virtual ParseStatus ParseCustom(const StringPiece& option,
+                                  std::string* error_msg) OVERRIDE {
+    {
+      ParseStatus base_parse = Base::ParseCustom(option, error_msg);
+      if (base_parse != kParseUnknownArgument) {
+        return base_parse;
       }
     }
 
-    if (image_location_ == nullptr && oat_filename_ == nullptr) {
-      fprintf(stderr, "Either --image or --oat must be specified\n");
-      return false;
+    if (option.starts_with("--oat-file=")) {
+      oat_filename_ = option.substr(strlen("--oat-file=")).data();
+    } else if (option.starts_with("--image=")) {
+      image_location_ = option.substr(strlen("--image=")).data();
+    } else if (option =="--dump:raw_mapping_table") {
+      dump_raw_mapping_table_ = true;
+    } else if (option == "--dump:raw_gc_map") {
+      dump_raw_gc_map_ = true;
+    } else if (option == "--no-dump:vmap") {
+      dump_vmap_ = false;
+    } else if (option == "--no-disassemble") {
+      disassemble_code_ = false;
+    } else if (option.starts_with("--symbolize=")) {
+      oat_filename_ = option.substr(strlen("--symbolize=")).data();
+      symbolize_ = true;
+    } else if (option.starts_with("--method-filter=")) {
+      method_filter_ = option.substr(strlen("--method-filter=")).data();
+    } else {
+      return kParseUnknownArgument;
     }
 
-    if (image_location_ != nullptr && oat_filename_ != nullptr) {
-      fprintf(stderr, "Either --image or --oat must be specified but not both\n");
-      return false;
-    }
-
-    return true;
+    return kParseOk;
   }
 
+  virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE {
+    // Infer boot image location from the image location if possible.
+    if (boot_image_location_ == nullptr) {
+      boot_image_location_ = image_location_;
+    }
+
+    // Perform the parent checks.
+    ParseStatus parent_checks = Base::ParseChecks(error_msg);
+    if (parent_checks != kParseOk) {
+      return parent_checks;
+    }
+
+    // Perform our own checks.
+    if (image_location_ == nullptr && oat_filename_ == nullptr) {
+      *error_msg = "Either --image or --oat-file must be specified";
+      return kParseError;
+    } else if (image_location_ != nullptr && oat_filename_ != nullptr) {
+      *error_msg = "Either --image or --oat-file must be specified but not both";
+      return kParseError;
+    }
+
+    return kParseOk;
+  }
+
+  virtual std::string GetUsage() const {
+    std::string usage;
+
+    usage +=
+        "Usage: oatdump [options] ...\n"
+        "    Example: oatdump --image=$ANDROID_PRODUCT_OUT/system/framework/boot.art\n"
+        "    Example: adb shell oatdump --image=/system/framework/boot.art\n"
+        "\n"
+        // Either oat-file or image is required.
+        "  --oat-file=<file.oat>: specifies an input oat filename.\n"
+        "      Example: --oat-file=/system/framework/boot.oat\n"
+        "\n"
+        "  --image=<file.art>: specifies an input image location.\n"
+        "      Example: --image=/system/framework/boot.art\n"
+        "\n";
+
+    usage += Base::GetUsage();
+
+    usage +=  // Optional.
+        "  --dump:raw_mapping_table enables dumping of the mapping table.\n"
+        "      Example: --dump:raw_mapping_table\n"
+        "\n"
+        "  --dump:raw_mapping_table enables dumping of the GC map.\n"
+        "      Example: --dump:raw_gc_map\n"
+        "\n"
+        "  --no-dump:vmap may be used to disable vmap dumping.\n"
+        "      Example: --no-dump:vmap\n"
+        "\n"
+        "  --no-disassemble may be used to disable disassembly.\n"
+        "      Example: --no-disassemble\n"
+        "\n"
+        "  --method-filter=<method name>: only dumps methods that contain the filter.\n"
+        "      Example: --method-filter=foo\n"
+        "\n";
+
+    return usage;
+  }
+
+ public:
   const char* oat_filename_ = nullptr;
   const char* method_filter_ = "";
   const char* image_location_ = nullptr;
-  const char* boot_image_location_ = nullptr;
-  InstructionSet instruction_set_ = kRuntimeISA;
   std::string elf_filename_prefix_;
-  std::ostream* os_ = &std::cout;
-  std::unique_ptr<std::ofstream> out_;
-  std::string output_name_;
   bool dump_raw_mapping_table_ = false;
   bool dump_raw_gc_map_ = false;
   bool dump_vmap_ = true;
@@ -2245,55 +2160,61 @@
   bool symbolize_ = false;
 };
 
-static int oatdump(int argc, char** argv) {
-  InitLogging(argv);
+struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
+  virtual bool NeedsRuntime() OVERRIDE {
+    CHECK(args_ != nullptr);
 
-  OatdumpArgs args;
-  if (!args.Parse(argc, argv)) {
-    return EXIT_FAILURE;
+    // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
+    bool absolute_addresses = (args_->oat_filename_ == nullptr);
+
+    oat_dumper_options_ = std::unique_ptr<OatDumperOptions>(new OatDumperOptions(
+        args_->dump_raw_mapping_table_,
+        args_->dump_raw_gc_map_,
+        args_->dump_vmap_,
+        args_->disassemble_code_,
+        absolute_addresses,
+        args_->method_filter_));
+
+    return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
+          !args_->symbolize_;
   }
 
-  // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
-  bool absolute_addresses = (args.oat_filename_ == nullptr);
+  virtual bool ExecuteWithoutRuntime() OVERRIDE {
+    CHECK(args_ != nullptr);
+    CHECK(args_->oat_filename_ != nullptr);
 
-  std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(
-      args.dump_raw_mapping_table_,
-      args.dump_raw_gc_map_,
-      args.dump_vmap_,
-      args.disassemble_code_,
-      absolute_addresses,
-      args.method_filter_,
-      nullptr));
-
-  std::unique_ptr<Runtime> runtime;
-  if ((args.boot_image_location_ != nullptr || args.image_location_ != nullptr) &&
-      !args.symbolize_) {
-    // If we have a boot image option, try to start the runtime; except when just symbolizing.
-    runtime.reset(StartRuntime(args.boot_image_location_,
-                               args.image_location_,
-                               args.instruction_set_));
-  } else {
     MemMap::Init();
-  }
 
-  if (args.oat_filename_ != nullptr) {
-    if (args.symbolize_) {
-      return SymbolizeOat(args.oat_filename_, args.output_name_);
+    if (args_->symbolize_) {
+      return SymbolizeOat(args_->oat_filename_, args_->output_name_) == EXIT_SUCCESS;
     } else {
-      return DumpOat(runtime.get(), args.oat_filename_, oat_dumper_options.release(), args.os_);
+      return DumpOat(nullptr,
+                     args_->oat_filename_,
+                     oat_dumper_options_.release(),
+                     args_->os_) == EXIT_SUCCESS;
     }
   }
 
-  if (runtime.get() == nullptr) {
-    // We need the runtime when printing an image.
-    return EXIT_FAILURE;
+  virtual bool ExecuteWithRuntime(Runtime* runtime) {
+    CHECK(args_ != nullptr);
+
+    if (args_->oat_filename_ != nullptr) {
+      return DumpOat(runtime,
+                     args_->oat_filename_,
+                     oat_dumper_options_.release(),
+                     args_->os_) == EXIT_SUCCESS;
+    }
+
+    return DumpImage(runtime, args_->image_location_, oat_dumper_options_.release(), args_->os_)
+      == EXIT_SUCCESS;
   }
 
-  return DumpImage(runtime.get(), args.image_location_, oat_dumper_options.release(), args.os_);
-}
+  std::unique_ptr<OatDumperOptions> oat_dumper_options_;
+};
 
 }  // namespace art
 
 int main(int argc, char** argv) {
-  return art::oatdump(argc, argv);
+  art::OatdumpMain main;
+  return main.Main(argc, argv);
 }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 68fd15b..2059a96 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -48,23 +48,6 @@
 
 namespace art {
 
-static InstructionSet ElfISAToInstructionSet(Elf32_Word isa) {
-  switch (isa) {
-    case EM_ARM:
-      return kArm;
-    case EM_AARCH64:
-      return kArm64;
-    case EM_386:
-      return kX86;
-    case EM_X86_64:
-      return kX86_64;
-    case EM_MIPS:
-      return kMips;
-    default:
-      return kNone;
-  }
-}
-
 static bool LocationToFilename(const std::string& location, InstructionSet isa,
                                std::string* filename) {
   bool has_system = false;
@@ -212,7 +195,7 @@
       LOG(ERROR) << "unable to read elf header";
       return false;
     }
-    isa = ElfISAToInstructionSet(elf_hdr.e_machine);
+    isa = GetInstructionSetFromELF(elf_hdr.e_machine, elf_hdr.e_flags);
   }
   const char* isa_name = GetInstructionSetString(isa);
   std::string image_filename;
@@ -540,12 +523,6 @@
   const size_t pointer_size = InstructionSetPointerSize(isa_);
   // Just update the entry points if it looks like we should.
   // TODO: sanity check all the pointers' values
-  uintptr_t portable = reinterpret_cast<uintptr_t>(
-      object->GetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(pointer_size));
-  if (portable != 0) {
-    copy->SetEntryPointFromPortableCompiledCodePtrSize(reinterpret_cast<void*>(portable + delta_),
-                                                       pointer_size);
-  }
   uintptr_t quick= reinterpret_cast<uintptr_t>(
       object->GetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(pointer_size));
   if (quick != 0) {
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 7dfdb75..4714610 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -31,9 +31,7 @@
   base/stringprintf.cc \
   base/timing_logger.cc \
   base/unix_file/fd_file.cc \
-  base/unix_file/null_file.cc \
   base/unix_file/random_access_file_utils.cc \
-  base/unix_file/string_file.cc \
   check_jni.cc \
   class_linker.cc \
   common_throws.cc \
@@ -66,9 +64,11 @@
   gc/space/image_space.cc \
   gc/space/large_object_space.cc \
   gc/space/malloc_space.cc \
+  gc/space/region_space.cc \
   gc/space/rosalloc_space.cc \
   gc/space/space.cc \
   gc/space/zygote_space.cc \
+  gc/task_processor.cc \
   hprof/hprof.cc \
   image.cc \
   indirect_reference_table.cc \
@@ -139,6 +139,7 @@
   reference_table.cc \
   reflection.cc \
   runtime.cc \
+  runtime_options.cc \
   signal_catcher.cc \
   stack.cc \
   thread.cc \
@@ -171,6 +172,8 @@
   arch/arm64/registers_arm64.cc \
   arch/mips/instruction_set_features_mips.cc \
   arch/mips/registers_mips.cc \
+  arch/mips64/instruction_set_features_mips64.cc \
+  arch/mips64/registers_mips64.cc \
   arch/x86/instruction_set_features_x86.cc \
   arch/x86/registers_x86.cc \
   arch/x86_64/registers_x86_64.cc \
@@ -178,17 +181,6 @@
   entrypoints/interpreter/interpreter_entrypoints.cc \
   entrypoints/jni/jni_entrypoints.cc \
   entrypoints/math_entrypoints.cc \
-  entrypoints/portable/portable_alloc_entrypoints.cc \
-  entrypoints/portable/portable_cast_entrypoints.cc \
-  entrypoints/portable/portable_dexcache_entrypoints.cc \
-  entrypoints/portable/portable_field_entrypoints.cc \
-  entrypoints/portable/portable_fillarray_entrypoints.cc \
-  entrypoints/portable/portable_invoke_entrypoints.cc \
-  entrypoints/portable/portable_jni_entrypoints.cc \
-  entrypoints/portable/portable_lock_entrypoints.cc \
-  entrypoints/portable/portable_thread_entrypoints.cc \
-  entrypoints/portable/portable_throw_entrypoints.cc \
-  entrypoints/portable/portable_trampoline_entrypoints.cc \
   entrypoints/quick/quick_alloc_entrypoints.cc \
   entrypoints/quick/quick_cast_entrypoints.cc \
   entrypoints/quick/quick_deoptimization_entrypoints.cc \
@@ -223,7 +215,6 @@
   arch/arm/instruction_set_features_assembly_tests.S \
   arch/arm/jni_entrypoints_arm.S \
   arch/arm/memcmp16_arm.S \
-  arch/arm/portable_entrypoints_arm.S \
   arch/arm/quick_entrypoints_arm.S \
   arch/arm/quick_entrypoints_cc_arm.cc \
   arch/arm/thread_arm.cc \
@@ -234,7 +225,6 @@
   arch/arm64/entrypoints_init_arm64.cc \
   arch/arm64/jni_entrypoints_arm64.S \
   arch/arm64/memcmp16_arm64.S \
-  arch/arm64/portable_entrypoints_arm64.S \
   arch/arm64/quick_entrypoints_arm64.S \
   arch/arm64/thread_arm64.cc \
   monitor_pool.cc \
@@ -245,7 +235,6 @@
   arch/x86/entrypoints_init_x86.cc \
   arch/x86/jni_entrypoints_x86.S \
   arch/x86/memcmp16_x86.S \
-  arch/x86/portable_entrypoints_x86.S \
   arch/x86/quick_entrypoints_x86.S \
   arch/x86/thread_x86.cc \
   arch/x86/fault_handler_x86.cc
@@ -260,7 +249,6 @@
   arch/x86_64/entrypoints_init_x86_64.cc \
   arch/x86_64/jni_entrypoints_x86_64.S \
   arch/x86_64/memcmp16_x86_64.S \
-  arch/x86_64/portable_entrypoints_x86_64.S \
   arch/x86_64/quick_entrypoints_x86_64.S \
   arch/x86_64/thread_x86_64.cc \
   monitor_pool.cc \
@@ -274,14 +262,19 @@
   arch/mips/entrypoints_init_mips.cc \
   arch/mips/jni_entrypoints_mips.S \
   arch/mips/memcmp16_mips.S \
-  arch/mips/portable_entrypoints_mips.S \
   arch/mips/quick_entrypoints_mips.S \
   arch/mips/thread_mips.cc \
   arch/mips/fault_handler_mips.cc
 
-ifeq ($(TARGET_ARCH),mips64)
-$(info TODOMips64: $(LOCAL_PATH)/Android.mk Add mips64 specific runtime files)
-endif # TARGET_ARCH != mips64
+LIBART_TARGET_SRC_FILES_mips64 := \
+  arch/mips64/context_mips64.cc \
+  arch/mips64/entrypoints_init_mips64.cc \
+  arch/mips64/jni_entrypoints_mips64.S \
+  arch/mips64/memcmp16_mips64.S \
+  arch/mips64/quick_entrypoints_mips64.S \
+  arch/mips64/thread_mips64.cc \
+  monitor_pool.cc \
+  arch/mips64/fault_handler_mips64.cc
 
 LIBART_HOST_SRC_FILES := \
   $(LIBART_COMMON_SRC_FILES) \
@@ -303,10 +296,12 @@
   base/unix_file/fd_file.h \
   dex_file.h \
   dex_instruction.h \
+  gc_root.h \
   gc/allocator/rosalloc.h \
   gc/collector/gc_type.h \
   gc/allocator_type.h \
   gc/collector_type.h \
+  gc/space/region_space.h \
   gc/space/space.h \
   gc/heap.h \
   instrumentation.h \
@@ -327,9 +322,6 @@
   verifier/method_verifier.h
 
 LIBART_CFLAGS := -DBUILDING_LIBART=1
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-  LIBART_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
-endif
 
 ifeq ($(MALLOC_IMPL),dlmalloc)
   LIBART_CFLAGS += -DUSE_DLMALLOC
@@ -467,10 +459,11 @@
   endif
 
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES)
+  LOCAL_C_INCLUDES += art/cmdline
   LOCAL_C_INCLUDES += art/sigchainlib
+  LOCAL_C_INCLUDES += art
 
   LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain
-  include external/libcxx/libcxx.mk
   LOCAL_SHARED_LIBRARIES += libbacktrace
   ifeq ($$(art_target_or_host),target)
     LOCAL_SHARED_LIBRARIES += libdl
@@ -485,14 +478,6 @@
     # For ashmem_create_region.
     LOCAL_STATIC_LIBRARIES += libcutils
   endif
-  ifeq ($$(ART_USE_PORTABLE_COMPILER),true)
-    include $$(LLVM_GEN_INTRINSICS_MK)
-    ifeq ($$(art_target_or_host),target)
-      include $$(LLVM_DEVICE_BUILD_MK)
-    else # host
-      include $$(LLVM_HOST_BUILD_MK)
-    endif
-  endif
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $$(LOCAL_PATH)/Android.mk
 
@@ -500,6 +485,8 @@
     LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH)
   endif
 
+  LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
   ifeq ($$(art_target_or_host),target)
     ifneq ($$(art_ndebug_or_debug),debug)
       # Leave the symbols in the shared library so that stack unwinders can
@@ -551,6 +538,7 @@
 LIBART_TARGET_SRC_FILES_x86 :=
 LIBART_TARGET_SRC_FILES_x86_64 :=
 LIBART_TARGET_SRC_FILES_mips :=
+LIBART_TARGET_SRC_FILES_mips64 :=
 LIBART_HOST_SRC_FILES :=
 LIBART_HOST_SRC_FILES_32 :=
 LIBART_HOST_SRC_FILES_64 :=
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index cac500c..ab6b00b 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -82,6 +82,16 @@
 #undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
 }
 
+namespace mips64 {
+#include "arch/mips64/asm_support_mips64.h"
+static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsOnlyCalleeSave = FRAME_SIZE_REFS_ONLY_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+static constexpr size_t kFrameSizeRefsAndArgsCalleeSave = FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE;
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+}
+
 namespace x86 {
 #include "arch/x86/asm_support_x86.h"
 static constexpr size_t kFrameSizeSaveAllCalleeSave = FRAME_SIZE_SAVE_ALL_CALLEE_SAVE;
@@ -124,6 +134,13 @@
                  mips::kFrameSizeRefsAndArgsCalleeSave);
 }
 
+TEST_F(ArchTest, MIPS64) {
+  CheckFrameSize(InstructionSet::kMips64, Runtime::kSaveAll, mips64::kFrameSizeSaveAllCalleeSave);
+  CheckFrameSize(InstructionSet::kMips64, Runtime::kRefsOnly, mips64::kFrameSizeRefsOnlyCalleeSave);
+  CheckFrameSize(InstructionSet::kMips64, Runtime::kRefsAndArgs,
+                 mips64::kFrameSizeRefsAndArgsCalleeSave);
+}
+
 TEST_F(ArchTest, X86) {
   CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, x86::kFrameSizeSaveAllCalleeSave);
   CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, x86::kFrameSizeRefsOnlyCalleeSave);
diff --git a/runtime/arch/arm/context_arm.cc b/runtime/arch/arm/context_arm.cc
index 9e8d282..c181e43 100644
--- a/runtime/arch/arm/context_arm.cc
+++ b/runtime/arch/arm/context_arm.cc
@@ -67,26 +67,18 @@
   }
 }
 
-bool ArmContext::SetGPR(uint32_t reg, uintptr_t value) {
+void ArmContext::SetGPR(uint32_t reg, uintptr_t value) {
   DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+  DCHECK(IsAccessibleGPR(reg));
   DCHECK_NE(gprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
-  if (gprs_[reg] != nullptr) {
-    *gprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *gprs_[reg] = value;
 }
 
-bool ArmContext::SetFPR(uint32_t reg, uintptr_t value) {
+void ArmContext::SetFPR(uint32_t reg, uintptr_t value) {
   DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfSRegisters));
+  DCHECK(IsAccessibleFPR(reg));
   DCHECK_NE(fprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
-  if (fprs_[reg] != nullptr) {
-    *fprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *fprs_[reg] = value;
 }
 
 void ArmContext::SmashCallerSaves() {
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h
index e894f16..1ca973e 100644
--- a/runtime/arch/arm/context_arm.h
+++ b/runtime/arch/arm/context_arm.h
@@ -37,13 +37,16 @@
   void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSP(uintptr_t new_sp) OVERRIDE {
-    bool success = SetGPR(SP, new_sp);
-    CHECK(success) << "Failed to set SP register";
+    SetGPR(SP, new_sp);
   }
 
   void SetPC(uintptr_t new_pc) OVERRIDE {
-    bool success = SetGPR(PC, new_pc);
-    CHECK(success) << "Failed to set PC register";
+    SetGPR(PC, new_pc);
+  }
+
+  bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+    return gprs_[reg] != nullptr;
   }
 
   uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
@@ -51,31 +54,26 @@
     return gprs_[reg];
   }
 
-  bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  uintptr_t GetGPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
-    if (gprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *gprs_[reg];
-      return true;
-    }
+    DCHECK(IsAccessibleGPR(reg));
+    return *gprs_[reg];
   }
 
-  bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
-  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfSRegisters));
-    if (fprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *fprs_[reg];
-      return true;
-    }
+    return fprs_[reg] != nullptr;
   }
 
-  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfSRegisters));
+    DCHECK(IsAccessibleFPR(reg));
+    return *fprs_[reg];
+  }
+
+  void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
   void SmashCallerSaves() OVERRIDE;
   void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 85a0dd2..ce0e614 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -16,7 +16,6 @@
 
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -49,7 +48,7 @@
 extern "C" int64_t __aeabi_ldivmod(int64_t, int64_t);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
-                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+                     QuickEntryPoints* qpoints) {
   // Interpreter
   ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
   ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -57,10 +56,6 @@
   // JNI
   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
 
-  // Portable
-  ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
-  ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
   // Alloc
   ResetQuickAllocEntryPoints(qpoints);
 
diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S
deleted file mode 100644
index 89ac1f7..0000000
--- a/runtime/arch/arm/portable_entrypoints_arm.S
+++ /dev/null
@@ -1,169 +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 "asm_support_arm.S"
-
-    /*
-     * Portable invocation stub.
-     * On entry:
-     *   r0 = method pointer
-     *   r1 = argument array or NULL for no argument methods
-     *   r2 = size of argument array in bytes
-     *   r3 = (managed) thread pointer
-     *   [sp] = JValue* result
-     *   [sp + 4] = result type char
-     */
-ENTRY art_portable_invoke_stub
-    push   {r0, r4, r5, r9, r11, lr}       @ spill regs
-    .save  {r0, r4, r5, r9, r11, lr}
-    .cfi_adjust_cfa_offset 24
-    .cfi_rel_offset r0, 0
-    .cfi_rel_offset r4, 4
-    .cfi_rel_offset r5, 8
-    .cfi_rel_offset r9, 12
-    .cfi_rel_offset r11, 16
-    .cfi_rel_offset lr, 20
-    mov    r11, sp                         @ save the stack pointer
-    .cfi_def_cfa_register r11
-    @.movsp r11
-    mov    r9, r3                          @ move managed thread pointer into r9
-    mov    r4, #SUSPEND_CHECK_INTERVAL     @ reset r4 to suspend check interval
-    add    r5, r2, #16                     @ create space for method pointer in frame
-    and    r5, #0xFFFFFFF0                 @ align frame size to 16 bytes
-    sub    sp, r5                          @ reserve stack space for argument array
-    add    r0, sp, #4                      @ pass stack pointer + method ptr as dest for memcpy
-    bl     memcpy                          @ memcpy (dest, src, bytes)
-    ldr    r0, [r11]                       @ restore method*
-    ldr    r1, [sp, #4]                    @ copy arg value for r1
-    ldr    r2, [sp, #8]                    @ copy arg value for r2
-    ldr    r3, [sp, #12]                   @ copy arg value for r3
-    mov    ip, #0                          @ set ip to 0
-    str    ip, [sp]                        @ store NULL for method* at bottom of frame
-    add    sp, #16                         @ first 4 args are not passed on stack for portable
-    ldr    ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32]  @ get pointer to the code
-    blx    ip                              @ call the method
-    mov    sp, r11                         @ restore the stack pointer
-    ldr    ip, [sp, #24]                   @ load the result pointer
-    strd   r0, [ip]                        @ store r0/r1 into result pointer
-    pop    {r0, r4, r5, r9, r11, lr}       @ restore spill regs
-    .cfi_adjust_cfa_offset -24
-    bx     lr
-END art_portable_invoke_stub
-
-    .extern artPortableProxyInvokeHandler
-ENTRY art_portable_proxy_invoke_handler
-    @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
-    @ TODO: just save the registers that are needed in artPortableProxyInvokeHandler.
-    push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
-    .save {r1-r3, r5-r8, r10-r11, lr}
-    .cfi_adjust_cfa_offset 40
-    .cfi_rel_offset r1, 0
-    .cfi_rel_offset r2, 4
-    .cfi_rel_offset r3, 8
-    .cfi_rel_offset r5, 12
-    .cfi_rel_offset r6, 16
-    .cfi_rel_offset r7, 20
-    .cfi_rel_offset r8, 24
-    .cfi_rel_offset r10, 28
-    .cfi_rel_offset r11, 32
-    .cfi_rel_offset lr, 36
-    sub sp, #8                        @ 2 words of space, bottom word will hold Method*
-    .pad #8
-    .cfi_adjust_cfa_offset 8
-    @ Begin argument set up.
-    str     r0, [sp, #0]           @ place proxy method at bottom of frame
-    mov     r2, r9                 @ pass Thread::Current
-    mov     r3, sp                 @ pass SP
-    blx     artPortableProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
-    ldr     r12, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
-    ldr     lr,  [sp, #44]         @ restore lr
-    add     sp,  #48               @ pop frame
-    .cfi_adjust_cfa_offset -48
-    bx      lr                     @ return
-END art_portable_proxy_invoke_handler
-
-    .extern artPortableResolutionTrampoline
-ENTRY art_portable_resolution_trampoline
-    @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
-    @ TODO: just save the registers that are needed in artPortableResolutionTrampoline.
-    push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
-    .save {r1-r3, r5-r8, r10-r11, lr}
-    .cfi_adjust_cfa_offset 40
-    .cfi_rel_offset r1, 0
-    .cfi_rel_offset r2, 4
-    .cfi_rel_offset r3, 8
-    .cfi_rel_offset r5, 12
-    .cfi_rel_offset r6, 16
-    .cfi_rel_offset r7, 20
-    .cfi_rel_offset r8, 24
-    .cfi_rel_offset r10, 28
-    .cfi_rel_offset r11, 32
-    .cfi_rel_offset lr, 36
-    sub sp, #8                     @ 2 words of space, bottom word will hold Method*
-    .pad #8
-    .cfi_adjust_cfa_offset 8
-    mov     r2, r9                 @ pass Thread::Current
-    mov     r3, sp                 @ pass SP
-    blx     artPortableResolutionTrampoline  @ (Method* called, receiver, Thread*, SP)
-    cmp     r0, #0                 @ is code pointer null?
-    beq     1f                     @ goto exception
-    mov     r12, r0
-    ldr  r0, [sp, #0]              @ load resolved method in r0
-    ldr  r1, [sp, #8]              @ restore non-callee save r1
-    ldrd r2, [sp, #12]             @ restore non-callee saves r2-r3
-    ldr  lr, [sp, #44]             @ restore lr
-    add  sp, #48                   @ rewind sp
-    .cfi_adjust_cfa_offset -48
-    bx      r12                    @ tail-call into actual code
-1:
-    ldr  r1, [sp, #8]          @ restore non-callee save r1
-    ldrd r2, [sp, #12]         @ restore non-callee saves r2-r3
-    ldr  lr, [sp, #44]         @ restore lr
-    add  sp, #48               @ rewind sp
-    .cfi_adjust_cfa_offset -48
-    bx lr
-END art_portable_resolution_trampoline
-
-    .extern artPortableToInterpreterBridge
-ENTRY art_portable_to_interpreter_bridge
-    @ Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
-    @ TODO: just save the registers that are needed in artPortableToInterpreterBridge.
-    push {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
-    .save {r1-r3, r5-r8, r10-r11, lr}
-    .cfi_adjust_cfa_offset 40
-    .cfi_rel_offset r1, 0
-    .cfi_rel_offset r2, 4
-    .cfi_rel_offset r3, 8
-    .cfi_rel_offset r5, 12
-    .cfi_rel_offset r6, 16
-    .cfi_rel_offset r7, 20
-    .cfi_rel_offset r8, 24
-    .cfi_rel_offset r10, 28
-    .cfi_rel_offset r11, 32
-    .cfi_rel_offset lr, 36
-    sub sp, #8                     @ 2 words of space, bottom word will hold Method*
-    .pad #8
-    .cfi_adjust_cfa_offset 8
-    mov     r1, r9                 @ pass Thread::Current
-    mov     r2, sp                 @ pass SP
-    blx     artPortableToInterpreterBridge    @ (Method* method, Thread*, SP)
-    ldr     lr,  [sp, #44]         @ restore lr
-    add     sp,  #48               @ pop frame
-    .cfi_adjust_cfa_offset -48
-    bx      lr                     @ return
-END art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1782db5..fec1ce5 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -29,7 +29,6 @@
      */
 .macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp1, rTemp2
     push {r4-r11, lr}                             @ 9 words (36 bytes) of callee saves.
-    .save {r4-r11, lr}
     .cfi_adjust_cfa_offset 36
     .cfi_rel_offset r4, 0
     .cfi_rel_offset r5, 4
@@ -41,10 +40,8 @@
     .cfi_rel_offset r11, 28
     .cfi_rel_offset lr, 32
     vpush {s16-s31}                               @ 16 words (64 bytes) of floats.
-    .pad #64
     .cfi_adjust_cfa_offset 64
     sub sp, #12                                   @ 3 words of space, bottom word will hold Method*
-    .pad #12
     .cfi_adjust_cfa_offset 12
     RUNTIME_CURRENT1 \rTemp1, \rTemp2             @ Load Runtime::Current into rTemp1.
     THIS_LOAD_REQUIRES_READ_BARRIER
@@ -64,7 +61,6 @@
      */
 .macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp1, rTemp2
     push {r5-r8, r10-r11, lr}                     @ 7 words of callee saves
-    .save {r5-r8, r10-r11, lr}
     .cfi_adjust_cfa_offset 28
     .cfi_rel_offset r5, 0
     .cfi_rel_offset r6, 4
@@ -74,7 +70,6 @@
     .cfi_rel_offset r11, 20
     .cfi_rel_offset lr, 24
     sub sp, #4                                    @ bottom word will hold Method*
-    .pad #4
     .cfi_adjust_cfa_offset 4
     RUNTIME_CURRENT2 \rTemp1, \rTemp2             @ Load Runtime::Current into rTemp1.
     THIS_LOAD_REQUIRES_READ_BARRIER
@@ -90,6 +85,7 @@
 
 .macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     add sp, #4               @ bottom word holds Method*
+    .cfi_adjust_cfa_offset -4
     pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
     .cfi_restore r5
     .cfi_restore r6
@@ -97,7 +93,8 @@
     .cfi_restore r8
     .cfi_restore r10
     .cfi_restore r11
-    .cfi_adjust_cfa_offset -FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+    .cfi_restore lr
+    .cfi_adjust_cfa_offset -28
 .endm
 
 .macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
@@ -111,7 +108,6 @@
      */
 .macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
     push {r1-r3, r5-r8, r10-r11, lr}   @ 10 words of callee saves and args.
-    .save {r1-r3, r5-r8, r10-r11, lr}
     .cfi_adjust_cfa_offset 40
     .cfi_rel_offset r1, 0
     .cfi_rel_offset r2, 4
@@ -124,10 +120,8 @@
     .cfi_rel_offset r11, 32
     .cfi_rel_offset lr, 36
     vpush {s0-s15}                     @ 16 words of float args.
-    .pad #64
     .cfi_adjust_cfa_offset 64
     sub sp, #8                         @ 2 words of space, bottom word will hold Method*
-    .pad #8
     .cfi_adjust_cfa_offset 8
     // Ugly compile-time check, but we only have the preprocessor.
 #if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 64 + 8)
@@ -166,6 +160,7 @@
     .cfi_restore r8
     .cfi_restore r10
     .cfi_restore r11
+    .cfi_restore lr
     .cfi_adjust_cfa_offset -40
 .endm
 
@@ -238,6 +233,11 @@
     DELIVER_PENDING_EXCEPTION
 .endm
 
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+    RETURN_IF_RESULT_IS_NON_ZERO
+    DELIVER_PENDING_EXCEPTION
+.endm
+
 // Macros taking opportunity of code similarities for downcalls with referrer for non-wide fields.
 .macro  ONE_ARG_REF_DOWNCALL name, entrypoint, return
     .extern \entrypoint
@@ -263,13 +263,12 @@
 END \name
 .endm
 
-.macro  THREE_ARG_REF_DOWNCALL name, entrypoint, return
+.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return
     .extern \entrypoint
 ENTRY \name
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12  @ save callee saves in case of GC
     ldr    r3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
     str    r9, [sp, #-16]!               @ expand the frame and pass Thread::Current
-    .pad #16
     .cfi_adjust_cfa_offset 16
     bl     \entrypoint                   @ (field_idx, Object*, new_val, referrer, Thread*)
     add    sp, #16                       @ release out args
@@ -335,7 +334,6 @@
     mov    r3, r9                         @ pass Thread::Current
     mov    r12, sp
     str    r12, [sp, #-16]!               @ expand the frame and pass SP
-    .pad #16
     .cfi_adjust_cfa_offset 16
     bl     \cxx_name                      @ (method_idx, this, caller, Thread*, SP)
     add    sp, #16                        @ strip the extra frame
@@ -386,14 +384,17 @@
      *  +-------------------------+
      */
 ENTRY art_quick_invoke_stub_internal
-    push   {r4, r9, r11, lr}               @ spill regs
-    .save  {r4, r9, r11, lr}
-    .pad #16
+    push   {r4, r5, r6, r7, r8, r9, r10, r11, lr}               @ spill regs
     .cfi_adjust_cfa_offset 16
     .cfi_rel_offset r4, 0
-    .cfi_rel_offset r9, 4
-    .cfi_rel_offset r11, 8
-    .cfi_rel_offset lr, 12
+    .cfi_rel_offset r5, 4
+    .cfi_rel_offset r6, 8
+    .cfi_rel_offset r7, 12
+    .cfi_rel_offset r8, 16
+    .cfi_rel_offset r9, 20
+    .cfi_rel_offset r10, 24
+    .cfi_rel_offset r11, 28
+    .cfi_rel_offset lr, 32
     mov    r11, sp                         @ save the stack pointer
     .cfi_def_cfa_register r11
 
@@ -410,10 +411,10 @@
     mov    ip, #0                          @ set ip to 0
     str    ip, [sp]                        @ store NULL for method* at bottom of frame
 
-    ldr    ip, [r11, #28]                  @ load fp register argument array pointer
+    ldr    ip, [r11, #48]                  @ load fp register argument array pointer
     vldm   ip, {s0-s15}                    @ copy s0 - s15
 
-    ldr    ip, [r11, #24]                  @ load core register argument array pointer
+    ldr    ip, [r11, #44]                  @ load core register argument array pointer
     mov    r0, r4                          @ restore method*
     add    ip, ip, #4                      @ skip r0
     ldm    ip, {r1-r3}                     @ copy r1 - r3
@@ -428,20 +429,14 @@
     mov    sp, r11                         @ restore the stack pointer
     .cfi_def_cfa_register sp
 
-    ldr    r4, [sp, #20]                   @ load result_is_float
-    ldr    r9, [sp, #16]                   @ load the result pointer
+    ldr    r4, [sp, #40]                   @ load result_is_float
+    ldr    r9, [sp, #36]                   @ load the result pointer
     cmp    r4, #0
     ite    eq
     strdeq r0, [r9]                        @ store r0/r1 into result pointer
     vstrne d0, [r9]                        @ store s0-s1/d0 into result pointer
 
-    pop    {r4, r9, r11, lr}               @ restore spill regs
-    .cfi_restore r4
-    .cfi_restore r9
-    .cfi_restore r11
-    .cfi_restore lr
-    .cfi_adjust_cfa_offset -16
-    bx     lr
+    pop    {r4, r5, r6, r7, r8, r9, r10, r11, pc}               @ restore spill regs
 END art_quick_invoke_stub_internal
 
     /*
@@ -544,25 +539,26 @@
     .extern artThrowClassCastException
 ENTRY art_quick_check_cast
     push {r0-r1, lr}                    @ save arguments, link register and pad
-    .save {r0-r1, lr}
     .cfi_adjust_cfa_offset 12
     .cfi_rel_offset r0, 0
     .cfi_rel_offset r1, 4
     .cfi_rel_offset lr, 8
     sub sp, #4
-    .pad #4
     .cfi_adjust_cfa_offset 4
     bl artIsAssignableFromCode
     cbz    r0, .Lthrow_class_cast_exception
     add sp, #4
     .cfi_adjust_cfa_offset -4
     pop {r0-r1, pc}
+    .cfi_adjust_cfa_offset 4        @ Reset unwind info so following code unwinds.
 .Lthrow_class_cast_exception:
     add sp, #4
     .cfi_adjust_cfa_offset -4
     pop {r0-r1, lr}
+    .cfi_adjust_cfa_offset -12
     .cfi_restore r0
     .cfi_restore r1
+    .cfi_restore lr
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3  // save all registers as basis for long jump context
     mov r2, r9                      @ pass Thread::Current
     b   artThrowClassCastException  @ (Class*, Class*, Thread*)
@@ -611,7 +607,6 @@
     blx lr
 .Lcheck_assignability:
     push {r0-r2, lr}             @ save arguments
-    .save {r0-r2, lr}
     .cfi_adjust_cfa_offset 16
     .cfi_rel_offset r0, 0
     .cfi_rel_offset r1, 4
@@ -635,11 +630,7 @@
     blx lr
 .Lthrow_array_store_exception:
     pop {r0-r2, lr}
-    .cfi_restore r0
-    .cfi_restore r1
-    .cfi_restore r2
-    .cfi_restore lr
-    .cfi_adjust_cfa_offset -16
+    /* No need to repeat restore cfi directives, the ones above apply here. */
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3, ip
     mov r1, r2
     mov r2, r9                     @ pass Thread::Current
@@ -647,50 +638,35 @@
     bkpt                           @ unreached
 END art_quick_aput_obj
 
-    /*
-     * Entry from managed code when uninitialized static storage, this stub will run the class
-     * initializer and deliver the exception on error. On success the static storage base is
-     * returned.
-     */
-    .extern artInitializeStaticStorageFromCode
-ENTRY art_quick_initialize_static_storage
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3    @ save callee saves in case of GC
-    mov    r2, r9                              @ pass Thread::Current
-    @ artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*)
-    bl     artInitializeStaticStorageFromCode
+// Macro to facilitate adding new allocation entrypoints.
+.macro TWO_ARG_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r2, r3  @ save callee saves in case of GC
+    mov    r2, r9                     @ pass Thread::Current
+    bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_NON_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_initialize_static_storage
+    \return
+END \name
+.endm
 
-    /*
-     * Entry from managed code when dex cache misses for a type_idx
-     */
-    .extern artInitializeTypeFromCode
-ENTRY art_quick_initialize_type
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3    @ save callee saves in case of GC
-    mov    r2, r9                              @ pass Thread::Current
-    @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
-    bl     artInitializeTypeFromCode
+// Macro to facilitate adding new array allocation entrypoints.
+.macro THREE_ARG_DOWNCALL name, entrypoint, return
+    .extern \entrypoint
+ENTRY \name
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r3, r12  @ save callee saves in case of GC
+    mov    r3, r9                     @ pass Thread::Current
+    @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
+    bl     \entrypoint
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_NON_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_initialize_type
+    \return
+END \name
+.endm
 
-    /*
-     * Entry from managed code when type_idx needs to be checked for access and dex cache may also
-     * miss.
-     */
-    .extern artInitializeTypeAndVerifyAccessFromCode
-ENTRY art_quick_initialize_type_and_verify_access
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3    @ save callee saves in case of GC
-    mov    r2, r9                              @ pass Thread::Current
-    @ artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Method* referrer, Thread*)
-    bl     artInitializeTypeAndVerifyAccessFromCode
-    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_NON_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_initialize_type_and_verify_access
+TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 
     /*
      * Called by managed code to resolve a static field and load a non-wide value.
@@ -762,7 +738,6 @@
     mov    r2, r1                        @ pass other half of wide argument
     ldr    r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
     str    r9, [sp, #-16]!               @ expand the frame and pass Thread::Current
-    .pad #16
     .cfi_adjust_cfa_offset 16
     bl     artSet64StaticFromCode        @ (field_idx, referrer, new_val, Thread*)
     add    sp, #16                       @ release out args
@@ -787,10 +762,8 @@
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr  @ save callee saves in case of GC
     ldr    r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE]  @ pass referrer
     str    r9, [sp, #-12]!               @ expand the frame and pass Thread::Current
-    .pad #12
     .cfi_adjust_cfa_offset 12
     str    r12, [sp, #-4]!               @ expand the frame and pass the referrer
-    .pad #4
     .cfi_adjust_cfa_offset 4
     bl     artSet64InstanceFromCode      @ (field_idx, Object*, new_val, Method* referrer, Thread*)
     add    sp, #16                       @ release out args
@@ -806,43 +779,7 @@
      * R1 holds the string index. The fast path check for hit in strings cache has already been
      * performed.
      */
-    .extern artResolveStringFromCode
-ENTRY art_quick_resolve_string
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3  @ save callee saves in case of GC
-    mov    r2, r9                     @ pass Thread::Current
-    @ artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*)
-    bl     artResolveStringFromCode
-    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    RETURN_IF_RESULT_IS_NON_ZERO
-    DELIVER_PENDING_EXCEPTION
-END art_quick_resolve_string
-
-// Macro to facilitate adding new allocation entrypoints.
-.macro TWO_ARG_DOWNCALL name, entrypoint, return
-    .extern \entrypoint
-ENTRY \name
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r2, r3  @ save callee saves in case of GC
-    mov    r2, r9                     @ pass Thread::Current
-    bl     \entrypoint     @ (uint32_t type_idx, Method* method, Thread*)
-    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    \return
-    DELIVER_PENDING_EXCEPTION
-END \name
-.endm
-
-// Macro to facilitate adding new array allocation entrypoints.
-.macro THREE_ARG_DOWNCALL name, entrypoint, return
-    .extern \entrypoint
-ENTRY \name
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME  r3, r12  @ save callee saves in case of GC
-    mov    r3, r9                     @ pass Thread::Current
-    @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
-    bl     \entrypoint
-    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    \return
-    DELIVER_PENDING_EXCEPTION
-END \name
-.endm
+TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALL_ALLOC_ENTRYPOINTS
@@ -1002,6 +939,9 @@
     // store into fpr, for when it's a fpr return...
     vmov d0, r0, r1
     bx lr      // ret
+    // Undo the unwinding information from above since it doesn't apply below.
+    .cfi_def_cfa_register r10
+    .cfi_adjust_cfa_offset FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
 
 .Lentry_error:
     mov sp, r10
@@ -1056,12 +996,10 @@
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3  @ set up frame knowing r2 and r3 must be dead on exit
     mov   r12, sp        @ remember bottom of caller's frame
     push  {r0-r1}        @ save return value
-    .save {r0-r1}
     .cfi_adjust_cfa_offset 8
     .cfi_rel_offset r0, 0
     .cfi_rel_offset r1, 4
     sub   sp, #8         @ space for return value argument
-    .pad #8
     .cfi_adjust_cfa_offset 8
     strd r0, [sp]        @ r0/r1 -> [sp] for fpr_res
     mov   r2, r0         @ pass return value as gpr_res
@@ -1075,6 +1013,7 @@
     mov   r2, r0         @ link register saved by instrumentation
     mov   lr, r1         @ r1 is holding link register if we're to bounce to deoptimize
     pop   {r0, r1}       @ restore return value
+    .cfi_adjust_cfa_offset -8
     .cfi_restore r0
     .cfi_restore r1
     add sp, #32          @ remove callee save frame
@@ -1114,7 +1053,6 @@
     /* mul-long vAA, vBB, vCC */
 ENTRY art_quick_mul_long
     push    {r9 - r10}
-    .save {r9 - r10}
     .cfi_adjust_cfa_offset 8
     .cfi_rel_offset r9, 0
     .cfi_rel_offset r10, 4
@@ -1207,7 +1145,6 @@
      */
 ENTRY art_quick_indexof
     push {r4, r10-r11, lr} @ 4 words of callee saves
-    .save {r4, r10-r11, lr}
     .cfi_adjust_cfa_offset 16
     .cfi_rel_offset r4, 0
     .cfi_rel_offset r10, 4
@@ -1324,7 +1261,6 @@
 1:                        @ Same strings, return.
 
     push {r4, r7-r12, lr} @ 8 words - keep alignment
-    .save {r4, r7-r12, lr}
     .cfi_adjust_cfa_offset 32
     .cfi_rel_offset r4, 0
     .cfi_rel_offset r7, 4
@@ -1465,7 +1401,6 @@
     add   sp, #4
     .cfi_adjust_cfa_offset -4
     pop   {pc}
-    .cfi_adjust_cfa_offset -4
 END art_quick_fmod
 
     /* float fmodf(float a, float b) */
@@ -1482,7 +1417,6 @@
     add   sp, #4
     .cfi_adjust_cfa_offset -4
     pop   {pc}
-    .cfi_adjust_cfa_offset -4
 END art_quick_fmod
 
     /* int64_t art_d2l(double d) */
diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
index e21e6c1..a3acd7e 100644
--- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc
+++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
@@ -75,7 +75,14 @@
         }
         break;
       case 'J':
+        if (gpr_index == 1 && !kArm32QuickCodeUseSoftFloat) {
+          // Don't use r1-r2 as a register pair, move to r2-r3 instead.
+          gpr_index++;
+        }
         if (gpr_index < arraysize(core_reg_args)) {
+          // Note that we don't need to do this if two registers are not available
+          // when !kArm32QuickCodeUseSoftFloat. We do it anyway to leave this
+          // code simple.
           core_reg_args[gpr_index++] = args[arg_index];
         }
         ++arg_index;
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 0a31480..7fc0555 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -70,27 +70,19 @@
   }
 }
 
-bool Arm64Context::SetGPR(uint32_t reg, uintptr_t value) {
+void Arm64Context::SetGPR(uint32_t reg, uintptr_t value) {
   DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
   DCHECK_NE(reg, static_cast<uint32_t>(XZR));
+  DCHECK(IsAccessibleGPR(reg));
   DCHECK_NE(gprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
-  if (gprs_[reg] != nullptr) {
-    *gprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *gprs_[reg] = value;
 }
 
-bool Arm64Context::SetFPR(uint32_t reg, uintptr_t value) {
+void Arm64Context::SetFPR(uint32_t reg, uintptr_t value) {
   DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfDRegisters));
+  DCHECK(IsAccessibleFPR(reg));
   DCHECK_NE(fprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
-  if (fprs_[reg] != nullptr) {
-    *fprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *fprs_[reg] = value;
 }
 
 void Arm64Context::SmashCallerSaves() {
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index d9a433b..6a4485b 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -37,13 +37,16 @@
   void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSP(uintptr_t new_sp) OVERRIDE {
-    bool success = SetGPR(SP, new_sp);
-    CHECK(success) << "Failed to set SP register";
+    SetGPR(SP, new_sp);
   }
 
   void SetPC(uintptr_t new_lr) OVERRIDE {
-    bool success = SetGPR(LR, new_lr);
-    CHECK(success) << "Failed to set LR register";
+    SetGPR(LR, new_lr);
+  }
+
+  bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
+    return gprs_[reg] != nullptr;
   }
 
   uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
@@ -51,31 +54,26 @@
     return gprs_[reg];
   }
 
-  bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  uintptr_t GetGPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfXRegisters));
-    if (gprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *gprs_[reg];
-      return true;
-    }
+    DCHECK(IsAccessibleGPR(reg));
+    return *gprs_[reg];
   }
 
-  bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
-  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfDRegisters));
-    if (fprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *fprs_[reg];
-      return true;
-    }
+    return fprs_[reg] != nullptr;
   }
 
-  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfDRegisters));
+    DCHECK(IsAccessibleFPR(reg));
+    return *fprs_[reg];
+  }
+
+  void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
   void SmashCallerSaves() OVERRIDE;
   void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 2d26c03..e68d41d 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -16,7 +16,6 @@
 
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -39,7 +38,7 @@
 
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
-                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+                     QuickEntryPoints* qpoints) {
   // Interpreter
   ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
   ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -47,10 +46,6 @@
   // JNI
   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
 
-  // Portable
-  ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
-  ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
   // Alloc
   ResetQuickAllocEntryPoints(qpoints);
 
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 4415935..770073b5 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -499,7 +499,7 @@
 
 .macro INVOKE_STUB_CREATE_FRAME
 
-SAVE_SIZE=6*8   // x4, x5, xSUSPEND, SP, LR & FP saved.
+SAVE_SIZE=15*8   // x4, x5, x20, x21, x22, x23, x24, x25, x26, x27, x28, xSUSPEND, SP, LR, FP saved.
 SAVE_SIZE_AND_METHOD=SAVE_SIZE+STACK_REFERENCE_SIZE
 
 
@@ -515,6 +515,25 @@
     .cfi_def_cfa_register x10              // before this.
     .cfi_adjust_cfa_offset SAVE_SIZE
 
+    str x28, [x10, #112]
+    .cfi_rel_offset x28, 112
+
+    stp x26, x27, [x10, #96]
+    .cfi_rel_offset x26, 96
+    .cfi_rel_offset x27, 104
+
+    stp x24, x25, [x10, #80]
+    .cfi_rel_offset x24, 80
+    .cfi_rel_offset x25, 88
+
+    stp x22, x23, [x10, #64]
+    .cfi_rel_offset x22, 64
+    .cfi_rel_offset x23, 72
+
+    stp x20, x21, [x10, #48]
+    .cfi_rel_offset x20, 48
+    .cfi_rel_offset x21, 56
+
     stp x9, xSUSPEND, [x10, #32]           // Save old stack pointer and xSUSPEND
     .cfi_rel_offset sp, 32
     .cfi_rel_offset x19, 40
@@ -573,6 +592,25 @@
     .cfi_restore x4
     .cfi_restore x5
 
+    ldr x28, [xFP, #112]
+    .cfi_restore x28
+
+    ldp x26, x27, [xFP, #96]
+    .cfi_restore x26
+    .cfi_restore x27
+
+    ldp x24, x25, [xFP, #80]
+    .cfi_restore x24
+    .cfi_restore x25
+
+    ldp x22, x23, [xFP, #64]
+    .cfi_restore x22
+    .cfi_restore x23
+
+    ldp x20, x21, [xFP, #48]
+    .cfi_restore x20
+    .cfi_restore x21
+
     // Store result (w0/x0/s0/d0) appropriately, depending on resultType.
     ldrb w10, [x5]
 
@@ -1191,7 +1229,6 @@
     bl     \entrypoint                // (uint32_t type_idx, Method* method, Thread*)
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
-    DELIVER_PENDING_EXCEPTION
 END \name
 .endm
 
@@ -1204,7 +1241,6 @@
     bl     \entrypoint
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     \return
-    DELIVER_PENDING_EXCEPTION
 END \name
 .endm
 
@@ -1245,6 +1281,13 @@
 END \name
 .endm
 
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+    cbz w0, 1f                 // result zero branch over
+    ret                        // return
+1:
+    DELIVER_PENDING_EXCEPTION
+.endm
+
     /*
      * Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
      * failure.
@@ -1256,10 +1299,10 @@
      * initializer and deliver the exception on error. On success the static storage base is
      * returned.
      */
-TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 
-TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
-TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 
 ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
 ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
@@ -1307,7 +1350,7 @@
      * w1 holds the string index. The fast path check for hit in strings cache has already been
      * performed.
      */
-TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 
 // Generate the allocation entrypoints for each allocator.
 GENERATE_ALL_ALLOC_ENTRYPOINTS
diff --git a/runtime/arch/context.cc b/runtime/arch/context.cc
index b1700bb..bf40a3f 100644
--- a/runtime/arch/context.cc
+++ b/runtime/arch/context.cc
@@ -20,8 +20,10 @@
 #include "arm/context_arm.h"
 #elif defined(__aarch64__)
 #include "arm64/context_arm64.h"
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
 #include "mips/context_mips.h"
+#elif defined(__mips__) && defined(__LP64__)
+#include "mips64/context_mips64.h"
 #elif defined(__i386__)
 #include "x86/context_x86.h"
 #elif defined(__x86_64__)
@@ -37,8 +39,10 @@
   return new arm::ArmContext();
 #elif defined(__aarch64__)
   return new arm64::Arm64Context();
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
   return new mips::MipsContext();
+#elif defined(__mips__) && defined(__LP64__)
+  return new mips64::Mips64Context();
 #elif defined(__i386__)
   return new x86::X86Context();
 #elif defined(__x86_64__)
diff --git a/runtime/arch/context.h b/runtime/arch/context.h
index 20a84dd..ed8cab0 100644
--- a/runtime/arch/context.h
+++ b/runtime/arch/context.h
@@ -49,24 +49,30 @@
   // Sets the program counter value.
   virtual void SetPC(uintptr_t new_pc) = 0;
 
+  // Returns whether the given GPR is accessible (read or write).
+  virtual bool IsAccessibleGPR(uint32_t reg) = 0;
+
   // Gets the given GPRs address.
   virtual uintptr_t* GetGPRAddress(uint32_t reg) = 0;
 
-  // Reads the given GPR. Returns true if we successfully read the register and
-  // set its value into 'val', returns false otherwise.
-  virtual bool GetGPR(uint32_t reg, uintptr_t* val) = 0;
+  // Reads the given GPR. The caller is responsible for checking the register
+  // is accessible with IsAccessibleGPR.
+  virtual uintptr_t GetGPR(uint32_t reg) = 0;
 
-  // Sets the given GPR. Returns true if we successfully write the given value
-  // into the register, returns false otherwise.
-  virtual bool SetGPR(uint32_t reg, uintptr_t value) = 0;
+  // Sets the given GPR. The caller is responsible for checking the register
+  // is accessible with IsAccessibleGPR.
+  virtual void SetGPR(uint32_t reg, uintptr_t value) = 0;
 
-  // Reads the given FPR. Returns true if we successfully read the register and
-  // set its value into 'val', returns false otherwise.
-  virtual bool GetFPR(uint32_t reg, uintptr_t* val) = 0;
+  // Returns whether the given FPR is accessible (read or write).
+  virtual bool IsAccessibleFPR(uint32_t reg) = 0;
 
-  // Sets the given FPR. Returns true if we successfully write the given value
-  // into the register, returns false otherwise.
-  virtual bool SetFPR(uint32_t reg, uintptr_t value) = 0;
+  // Reads the given FPR. The caller is responsible for checking the register
+  // is accessible with IsAccessibleFPR.
+  virtual uintptr_t GetFPR(uint32_t reg) = 0;
+
+  // Sets the given FPR. The caller is responsible for checking the register
+  // is accessible with IsAccessibleFPR.
+  virtual void SetFPR(uint32_t reg, uintptr_t value) = 0;
 
   // Smashes the caller save registers. If we're throwing, we don't want to return bogus values.
   virtual void SmashCallerSaves() = 0;
diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc
index 92fa727..81ca010 100644
--- a/runtime/arch/instruction_set.cc
+++ b/runtime/arch/instruction_set.cc
@@ -16,6 +16,8 @@
 
 #include "instruction_set.h"
 
+// Explicitly include our own elf.h to avoid Linux and other dependencies.
+#include "../elf.h"
 #include "globals.h"
 
 namespace art {
@@ -57,12 +59,35 @@
   } else if (strcmp("mips", isa_str) == 0) {
     return kMips;
   } else if (strcmp("mips64", isa_str) == 0) {
-    return kMips;
+    return kMips64;
   }
 
   return kNone;
 }
 
+InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags) {
+  switch (e_machine) {
+    case EM_ARM:
+      return kArm;
+    case EM_AARCH64:
+      return kArm64;
+    case EM_386:
+      return kX86;
+    case EM_X86_64:
+      return kX86_64;
+    case EM_MIPS: {
+      if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 ||
+          (e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
+        return kMips;
+      } else if ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) {
+        return kMips64;
+      }
+      break;
+    }
+  }
+  return kNone;
+}
+
 size_t GetInstructionSetAlignment(InstructionSet isa) {
   switch (isa) {
     case kArm:
@@ -76,6 +101,8 @@
     case kX86_64:
       return kX86Alignment;
     case kMips:
+      // Fall-through.
+    case kMips64:
       return kMipsAlignment;
     case kNone:
       LOG(FATAL) << "ISA kNone does not have alignment.";
@@ -88,6 +115,7 @@
 
 static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
 static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
+static constexpr size_t kMips64StackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
 
 static constexpr size_t kArmStackOverflowReservedBytes =    8 * KB;
 static constexpr size_t kArm64StackOverflowReservedBytes =  8 * KB;
@@ -106,6 +134,9 @@
     case kMips:
       return kMipsStackOverflowReservedBytes;
 
+    case kMips64:
+      return kMips64StackOverflowReservedBytes;
+
     case kX86:
       return kX86StackOverflowReservedBytes;
 
diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h
index e413880..9cfd2eb 100644
--- a/runtime/arch/instruction_set.h
+++ b/runtime/arch/instruction_set.h
@@ -40,8 +40,10 @@
 static constexpr InstructionSet kRuntimeISA = kArm;
 #elif defined(__aarch64__)
 static constexpr InstructionSet kRuntimeISA = kArm64;
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
 static constexpr InstructionSet kRuntimeISA = kMips;
+#elif defined(__mips__) && defined(__LP64__)
+static constexpr InstructionSet kRuntimeISA = kMips64;
 #elif defined(__i386__)
 static constexpr InstructionSet kRuntimeISA = kX86;
 #elif defined(__x86_64__)
@@ -78,6 +80,8 @@
 // Note: Returns kNone when the string cannot be parsed to a known value.
 InstructionSet GetInstructionSetFromString(const char* instruction_set);
 
+InstructionSet GetInstructionSetFromELF(uint16_t e_machine, uint32_t e_flags);
+
 static inline size_t GetInstructionSetPointerSize(InstructionSet isa) {
   switch (isa) {
     case kArm:
@@ -145,6 +149,8 @@
       return 8;
     case kMips:
       return 4;
+    case kMips64:
+      return 8;
     case kNone:
       LOG(FATAL) << "ISA kNone does not have spills.";
       UNREACHABLE();
@@ -168,6 +174,8 @@
       return 8;
     case kMips:
       return 4;
+    case kMips64:
+      return 8;
     case kNone:
       LOG(FATAL) << "ISA kNone does not have spills.";
       UNREACHABLE();
@@ -186,8 +194,8 @@
 // On x86, ARM32 and MIPS, this is given for a *scalar* 64bit value. The definition thus *must* be
 // uint64_t or long long int.
 //
-// On x86_64 and ARM64, structs are decomposed for allocation, so we can create a structs of two
-// size_t-sized values.
+// On x86_64, ARM64 and MIPS64, structs are decomposed for allocation, so we can create a structs of
+// two size_t-sized values.
 //
 // We need two operations:
 //
@@ -202,7 +210,7 @@
 //            when the garbage collector can move objects concurrently. Ensure that required locks
 //            are held when using!
 
-#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+#if defined(__i386__) || defined(__arm__) || (defined(__mips__) && !defined(__LP64__))
 typedef uint64_t TwoWordReturn;
 
 // Encodes method_ptr==nullptr and code_ptr==nullptr
@@ -218,7 +226,7 @@
   return ((hi64 << 32) | lo32);
 }
 
-#elif defined(__x86_64__) || defined(__aarch64__)
+#elif defined(__x86_64__) || defined(__aarch64__) || (defined(__mips__) && defined(__LP64__))
 struct TwoWordReturn {
   uintptr_t lo;
   uintptr_t hi;
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index 1072562..1fd1dea 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -23,6 +23,7 @@
 #include "arm/instruction_set_features_arm.h"
 #include "arm64/instruction_set_features_arm64.h"
 #include "mips/instruction_set_features_mips.h"
+#include "mips64/instruction_set_features_mips64.h"
 #include "x86/instruction_set_features_x86.h"
 #include "x86_64/instruction_set_features_x86_64.h"
 
@@ -43,6 +44,9 @@
     case kMips:
       result = MipsInstructionSetFeatures::FromVariant(variant, error_msg);
       break;
+    case kMips64:
+      result = Mips64InstructionSetFeatures::FromVariant(variant, error_msg);
+      break;
     case kX86:
       result = X86InstructionSetFeatures::FromVariant(variant, error_msg);
       break;
@@ -71,6 +75,9 @@
     case kMips:
       result = MipsInstructionSetFeatures::FromBitmap(bitmap);
       break;
+    case kMips64:
+      result = Mips64InstructionSetFeatures::FromBitmap(bitmap);
+      break;
     case kX86:
       result = X86InstructionSetFeatures::FromBitmap(bitmap);
       break;
@@ -98,6 +105,9 @@
     case kMips:
       result = MipsInstructionSetFeatures::FromCppDefines();
       break;
+    case kMips64:
+      result = Mips64InstructionSetFeatures::FromCppDefines();
+      break;
     case kX86:
       result = X86InstructionSetFeatures::FromCppDefines();
       break;
@@ -125,6 +135,9 @@
     case kMips:
       result = MipsInstructionSetFeatures::FromCpuInfo();
       break;
+    case kMips64:
+      result = Mips64InstructionSetFeatures::FromCpuInfo();
+      break;
     case kX86:
       result = X86InstructionSetFeatures::FromCpuInfo();
       break;
@@ -151,6 +164,9 @@
     case kMips:
       result = MipsInstructionSetFeatures::FromHwcap();
       break;
+    case kMips64:
+      result = Mips64InstructionSetFeatures::FromHwcap();
+      break;
     case kX86:
       result = X86InstructionSetFeatures::FromHwcap();
       break;
@@ -177,6 +193,9 @@
     case kMips:
       result = MipsInstructionSetFeatures::FromAssembly();
       break;
+    case kMips64:
+      result = Mips64InstructionSetFeatures::FromAssembly();
+      break;
     case kX86:
       result = X86InstructionSetFeatures::FromAssembly();
       break;
@@ -250,6 +269,11 @@
   return down_cast<const MipsInstructionSetFeatures*>(this);
 }
 
+const Mips64InstructionSetFeatures* InstructionSetFeatures::AsMips64InstructionSetFeatures() const {
+  DCHECK_EQ(kMips64, GetInstructionSet());
+  return down_cast<const Mips64InstructionSetFeatures*>(this);
+}
+
 const X86InstructionSetFeatures* InstructionSetFeatures::AsX86InstructionSetFeatures() const {
   DCHECK(kX86 == GetInstructionSet() || kX86_64 == GetInstructionSet());
   return down_cast<const X86InstructionSetFeatures*>(this);
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index 2c6e699..e4513ef 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -28,6 +28,7 @@
 class ArmInstructionSetFeatures;
 class Arm64InstructionSetFeatures;
 class MipsInstructionSetFeatures;
+class Mips64InstructionSetFeatures;
 class X86InstructionSetFeatures;
 class X86_64InstructionSetFeatures;
 
@@ -87,6 +88,9 @@
   // Down cast this MipsInstructionFeatures.
   const MipsInstructionSetFeatures* AsMipsInstructionSetFeatures() const;
 
+  // Down cast this Mips64InstructionFeatures.
+  const Mips64InstructionSetFeatures* AsMips64InstructionSetFeatures() const;
+
   // Down cast this X86InstructionFeatures.
   const X86InstructionSetFeatures* AsX86InstructionSetFeatures() const;
 
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 932ef32..2f3cf18 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -28,6 +28,7 @@
   EXPECT_EQ(kX86, GetInstructionSetFromString("x86"));
   EXPECT_EQ(kX86_64, GetInstructionSetFromString("x86_64"));
   EXPECT_EQ(kMips, GetInstructionSetFromString("mips"));
+  EXPECT_EQ(kMips64, GetInstructionSetFromString("mips64"));
   EXPECT_EQ(kNone, GetInstructionSetFromString("none"));
   EXPECT_EQ(kNone, GetInstructionSetFromString("random-string"));
 }
@@ -39,6 +40,7 @@
   EXPECT_STREQ("x86", GetInstructionSetString(kX86));
   EXPECT_STREQ("x86_64", GetInstructionSetString(kX86_64));
   EXPECT_STREQ("mips", GetInstructionSetString(kMips));
+  EXPECT_STREQ("mips64", GetInstructionSetString(kMips64));
   EXPECT_STREQ("none", GetInstructionSetString(kNone));
 }
 
diff --git a/runtime/arch/memcmp16.h b/runtime/arch/memcmp16.h
index 4b9fb8e..c449a14 100644
--- a/runtime/arch/memcmp16.h
+++ b/runtime/arch/memcmp16.h
@@ -30,7 +30,7 @@
 //
 // In both cases, MemCmp16 is declared.
 
-#if defined(__aarch64__) || defined(__arm__) || defined(__mips) || defined(__i386__) || defined(__x86_64__)
+#if defined(__aarch64__) || defined(__arm__) || defined(__mips__) || defined(__i386__) || defined(__x86_64__)
 
 extern "C" uint32_t __memcmp16(const uint16_t* s0, const uint16_t* s1, size_t count);
 #define MemCmp16 __memcmp16
diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S
index 0d18f1a..eea6537 100644
--- a/runtime/arch/mips/asm_support_mips.S
+++ b/runtime/arch/mips/asm_support_mips.S
@@ -66,5 +66,54 @@
     END \name
 .endm
 
+#if defined(__mips_isa_rev) && __mips_isa_rev > 2
+  /* mips32r5 & mips32r6 have mthc1 op, and have 64-bit fp regs,
+     and in FPXX abi we avoid referring to odd-numbered fp regs */
+
+/* LDu: Load 64-bit floating-point value to float reg feven,
+   from unaligned (mod-4-aligned) mem location disp(base) */
+.macro LDu feven,fodd,disp,base,temp
+  l.s   \feven, \disp(\base)
+  lw    \temp, \disp+4(\base)
+  mthc1 \temp, \feven
+.endm
+
+/* SDu: Store 64-bit floating-point value from float reg feven,
+   to unaligned (mod-4-aligned) mem location disp(base) */
+.macro SDu feven,fodd,disp,base,temp
+  mfhc1 \temp, \feven
+  s.s   \feven, \disp(\base)
+  sw    \temp, \disp+4(\base)
+.endm
+
+/* MTD: Move double, from general regpair (reven,rodd)
+        to float regpair (feven,fodd) */
+.macro MTD reven,rodd,feven,fodd
+  mtc1  \reven, \feven
+  mthc1 \rodd, \feven
+.endm
+
+#else
+  /* mips32r1 has no mthc1 op;
+     mips32r1 and mips32r2 use 32-bit floating point register mode (FR=0),
+     and always hold doubles as (feven, fodd) fp reg pair */
+
+.macro LDu feven,fodd,disp,base,temp
+  l.s   \feven, \disp(\base)
+  l.s   \fodd,  \disp+4(\base)
+.endm
+
+.macro SDu feven,fodd,disp,base,temp
+  s.s   \feven, \disp(\base)
+  s.s   \fodd,  \disp+4(\base)
+.endm
+
+.macro MTD reven,rodd,feven,fodd
+  mtc1  \reven, \feven
+  mtc1  \rodd, \fodd
+.endm
+
+#endif  /* mips_isa_rev */
+
 
 #endif  // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_S_
diff --git a/runtime/arch/mips/context_mips.cc b/runtime/arch/mips/context_mips.cc
index e1f6c06..6c0ab98 100644
--- a/runtime/arch/mips/context_mips.cc
+++ b/runtime/arch/mips/context_mips.cc
@@ -67,26 +67,18 @@
   }
 }
 
-bool MipsContext::SetGPR(uint32_t reg, uintptr_t value) {
+void MipsContext::SetGPR(uint32_t reg, uintptr_t value) {
   CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+  DCHECK(IsAccessibleGPR(reg));
   CHECK_NE(gprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
-  if (gprs_[reg] != nullptr) {
-    *gprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *gprs_[reg] = value;
 }
 
-bool MipsContext::SetFPR(uint32_t reg, uintptr_t value) {
+void MipsContext::SetFPR(uint32_t reg, uintptr_t value) {
   CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFRegisters));
+  DCHECK(IsAccessibleFPR(reg));
   CHECK_NE(fprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
-  if (fprs_[reg] != nullptr) {
-    *fprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *fprs_[reg] = value;
 }
 
 void MipsContext::SmashCallerSaves() {
diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h
index f2ee335..d8a0b67 100644
--- a/runtime/arch/mips/context_mips.h
+++ b/runtime/arch/mips/context_mips.h
@@ -36,13 +36,16 @@
   void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSP(uintptr_t new_sp) OVERRIDE {
-    bool success = SetGPR(SP, new_sp);
-    CHECK(success) << "Failed to set SP register";
+    SetGPR(SP, new_sp);
   }
 
   void SetPC(uintptr_t new_pc) OVERRIDE {
-    bool success = SetGPR(RA, new_pc);
-    CHECK(success) << "Failed to set RA register";
+    SetGPR(RA, new_pc);
+  }
+
+  bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+    CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
+    return gprs_[reg] != nullptr;
   }
 
   uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
@@ -50,31 +53,26 @@
     return gprs_[reg];
   }
 
-  bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  uintptr_t GetGPR(uint32_t reg) OVERRIDE {
     CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters));
-    if (gprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *gprs_[reg];
-      return true;
-    }
+    DCHECK(IsAccessibleGPR(reg));
+    return *gprs_[reg];
   }
 
-  bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
-  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
     CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFRegisters));
-    if (fprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *fprs_[reg];
-      return true;
-    }
+    return fprs_[reg] != nullptr;
   }
 
-  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+    CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFRegisters));
+    DCHECK(IsAccessibleFPR(reg));
+    return *fprs_[reg];
+  }
+
+  void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
   void SmashCallerSaves() OVERRIDE;
   void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index e86aa1c..1a661c4 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -17,7 +17,6 @@
 #include "atomic.h"
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -60,7 +59,7 @@
 extern "C" int64_t __moddi3(int64_t, int64_t);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
-                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+                     QuickEntryPoints* qpoints) {
   // Interpreter
   ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
   ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -68,10 +67,6 @@
   // JNI
   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
 
-  // Portable
-  ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
-  ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
   // Alloc
   ResetQuickAllocEntryPoints(qpoints);
 
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index 11be2a8..00ab613 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -25,52 +25,82 @@
 namespace art {
 
 const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromVariant(
-    const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED) {
-  if (variant != "default") {
-    std::ostringstream os;
-    LOG(WARNING) << "Unexpected CPU variant for Mips using defaults: " << variant;
-  }
+    const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
+
   bool smp = true;  // Conservative default.
   bool fpu_32bit = true;
-  bool mips_isa_gte2 = true;
-  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+  bool mips_isa_gte2 = false;
+  bool r6 = false;
+
+  // Override defaults based on variant string.
+  // Only care if it is R1, R2 or R6 and we assume all CPUs will have a FP unit.
+  constexpr const char* kMips32Prefix = "mips32r";
+  const size_t kPrefixLength = strlen(kMips32Prefix);
+  if (variant.compare(0, kPrefixLength, kMips32Prefix, kPrefixLength) == 0 &&
+      variant.size() > kPrefixLength) {
+    if (variant[kPrefixLength] >= '6') {
+      fpu_32bit = false;
+      r6 = true;
+    }
+    if (variant[kPrefixLength] >= '2') {
+      mips_isa_gte2 = true;
+    }
+  } else if (variant == "default") {
+    // Default variant is: smp = true, has fpu, is gte2, is not r6. This is the traditional
+    // setting.
+    mips_isa_gte2 = true;
+  } else {
+    LOG(WARNING) << "Unexpected CPU variant for Mips32 using defaults: " << variant;
+  }
+
+  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
 }
 
 const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
   bool smp = (bitmap & kSmpBitfield) != 0;
   bool fpu_32bit = (bitmap & kFpu32Bitfield) != 0;
   bool mips_isa_gte2 = (bitmap & kIsaRevGte2Bitfield) != 0;
-  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+  bool r6 = (bitmap & kR6) != 0;
+  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
 }
 
 const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCppDefines() {
+  // Assume conservative defaults.
   const bool smp = true;
+  bool fpu_32bit = true;
+  bool mips_isa_gte2 = false;
+  bool r6 = false;
 
-  // TODO: here we assume the FPU is always 32-bit.
-  const bool fpu_32bit = true;
-
-#if __mips_isa_rev >= 2
-  const bool mips_isa_gte2 = true;
-#else
-  const bool mips_isa_gte2 = false;
+  // Override defaults based on compiler flags.
+#if (_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS32R5) || defined(_MIPS_ARCH_MIPS32R6)
+  mips_isa_gte2 = true;
 #endif
 
-  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+#if defined(_MIPS_ARCH_MIPS32R6)
+  r6 = true;
+  fpu_32bit = false;
+#endif
+
+  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
 }
 
 const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCpuInfo() {
   // Look in /proc/cpuinfo for features we need.  Only use this when we can guarantee that
   // the kernel puts the appropriate feature flags in here.  Sometimes it doesn't.
+  // Assume conservative defaults.
   bool smp = false;
+  bool fpu_32bit = true;
+  bool mips_isa_gte2 = false;
+  bool r6 = false;
 
-  // TODO: here we assume the FPU is always 32-bit.
-  const bool fpu_32bit = true;
+  // Override defaults based on compiler flags.
+#if (_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS32R5) || defined(_MIPS_ARCH_MIPS32R6)
+  mips_isa_gte2 = true;
+#endif
 
-  // TODO: here we assume all MIPS processors are >= v2.
-#if __mips_isa_rev >= 2
-  const bool mips_isa_gte2 = true;
-#else
-  const bool mips_isa_gte2 = false;
+#if defined(_MIPS_ARCH_MIPS32R6)
+  r6 = true;
+  fpu_32bit = false;
 #endif
 
   std::ifstream in("/proc/cpuinfo");
@@ -89,7 +119,7 @@
   } else {
     LOG(ERROR) << "Failed to open /proc/cpuinfo";
   }
-  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
 }
 
 const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromHwcap() {
@@ -109,13 +139,15 @@
   const MipsInstructionSetFeatures* other_as_mips = other->AsMipsInstructionSetFeatures();
   return (IsSmp() == other->IsSmp()) &&
       (fpu_32bit_ == other_as_mips->fpu_32bit_) &&
-      (mips_isa_gte2_ == other_as_mips->mips_isa_gte2_);
+      (mips_isa_gte2_ == other_as_mips->mips_isa_gte2_) &&
+      (r6_ == other_as_mips->r6_);
 }
 
 uint32_t MipsInstructionSetFeatures::AsBitmap() const {
   return (IsSmp() ? kSmpBitfield : 0) |
       (fpu_32bit_ ? kFpu32Bitfield : 0) |
-      (mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0);
+      (mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0) |
+      (r6_ ? kR6 : 0);
 }
 
 std::string MipsInstructionSetFeatures::GetFeatureString() const {
@@ -135,6 +167,9 @@
   } else {
     result += ",-mips2";
   }
+  if (r6_) {
+    result += ",r6";
+  }  // Suppress non-r6.
   return result;
 }
 
@@ -142,6 +177,7 @@
     const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
   bool fpu_32bit = fpu_32bit_;
   bool mips_isa_gte2 = mips_isa_gte2_;
+  bool r6 = r6_;
   for (auto i = features.begin(); i != features.end(); i++) {
     std::string feature = Trim(*i);
     if (feature == "fpu32") {
@@ -152,12 +188,16 @@
       mips_isa_gte2 = true;
     } else if (feature == "-mips2") {
       mips_isa_gte2 = false;
+    } else if (feature == "r6") {
+      r6 = true;
+    } else if (feature == "-r6") {
+      r6 = false;
     } else {
       *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
       return nullptr;
     }
   }
-  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2);
+  return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6);
 }
 
 }  // namespace art
diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h
index f7c64fe..aac436e 100644
--- a/runtime/arch/mips/instruction_set_features_mips.h
+++ b/runtime/arch/mips/instruction_set_features_mips.h
@@ -67,6 +67,10 @@
     return fpu_32bit_;
   }
 
+  bool IsR6() const {
+    return r6_;
+  }
+
   virtual ~MipsInstructionSetFeatures() {}
 
  protected:
@@ -76,19 +80,21 @@
                                  std::string* error_msg) const OVERRIDE;
 
  private:
-  MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2)
-      : InstructionSetFeatures(smp), fpu_32bit_(fpu_32bit),  mips_isa_gte2_(mips_isa_gte2) {
-  }
+  MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2, bool r6)
+      : InstructionSetFeatures(smp), fpu_32bit_(fpu_32bit),  mips_isa_gte2_(mips_isa_gte2), r6_(r6)
+  {}
 
   // Bitmap positions for encoding features as a bitmap.
   enum {
     kSmpBitfield = 1,
     kFpu32Bitfield = 2,
     kIsaRevGte2Bitfield = 4,
+    kR6 = 8,
   };
 
   const bool fpu_32bit_;
   const bool mips_isa_gte2_;
+  const bool r6_;
 
   DISALLOW_COPY_AND_ASSIGN(MipsInstructionSetFeatures);
 };
diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S
index 9a79467..fbc81d5 100644
--- a/runtime/arch/mips/jni_entrypoints_mips.S
+++ b/runtime/arch/mips/jni_entrypoints_mips.S
@@ -47,9 +47,9 @@
     addiu $sp, $sp, 32          # restore the stack
     .cfi_adjust_cfa_offset -32
     move  $t9, $v0              # put method code result in $t9
-    jr    $t9                   # leaf call to method's code
+    jalr  $zero, $t9            # leaf call to method's code
     nop
 .Lno_native_code_found:
-    jr    $ra
+    jalr  $zero, $ra
     nop
 END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S
deleted file mode 100644
index 8d418e8..0000000
--- a/runtime/arch/mips/portable_entrypoints_mips.S
+++ /dev/null
@@ -1,132 +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 "asm_support_mips.S"
-
-    .set noreorder
-    .balign 4
-
-    .extern artPortableProxyInvokeHandler
-ENTRY art_portable_proxy_invoke_handler
-    # Fake callee save ref and args frame set up, note portable doesn't use callee save frames.
-    # TODO: just save the registers that are needed in artPortableProxyInvokeHandler.
-    addiu  $sp, $sp, -64
-    .cfi_adjust_cfa_offset 64
-    sw     $ra, 60($sp)
-    .cfi_rel_offset 31, 60
-    sw     $s8, 56($sp)
-    .cfi_rel_offset 30, 56
-    sw     $gp, 52($sp)
-    .cfi_rel_offset 28, 52
-    sw     $s7, 48($sp)
-    .cfi_rel_offset 23, 48
-    sw     $s6, 44($sp)
-    .cfi_rel_offset 22, 44
-    sw     $s5, 40($sp)
-    .cfi_rel_offset 21, 40
-    sw     $s4, 36($sp)
-    .cfi_rel_offset 20, 36
-    sw     $s3, 32($sp)
-    .cfi_rel_offset 19, 32
-    sw     $s2, 28($sp)
-    .cfi_rel_offset 18, 28
-    sw     $a3, 12($sp)
-    .cfi_rel_offset 7, 12
-    sw     $a2, 8($sp)
-    .cfi_rel_offset 6, 8
-    sw     $a1, 4($sp)
-    .cfi_rel_offset 5, 4
-    # Begin argument set up.
-    sw      $a0, 0($sp)            # place proxy method at bottom of frame
-    move    $a2, rSELF             # pass Thread::Current
-    jal     artPortableProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
-    move    $a3, $sp               # pass $sp
-    lw      $ra, 60($sp)           # restore $ra
-    jr      $ra
-    addiu   $sp, $sp, 64           # pop frame
-    .cfi_adjust_cfa_offset -64
-END art_portable_proxy_invoke_handler
-
-    /*
-     * Invocation stub for portable code.
-     * On entry:
-     *   a0 = method pointer
-     *   a1 = argument array or NULL for no argument methods
-     *   a2 = size of argument array in bytes
-     *   a3 = (managed) thread pointer
-     *   [sp + 16] = JValue* result
-     *   [sp + 20] = result type char
-     */
-ENTRY art_portable_invoke_stub
-    sw    $a0, 0($sp)           # save out a0
-    addiu $sp, $sp, -16         # spill s0, s1, fp, ra
-    .cfi_adjust_cfa_offset 16
-    sw    $ra, 12($sp)
-    .cfi_rel_offset 31, 12
-    sw    $fp, 8($sp)
-    .cfi_rel_offset 30, 8
-    sw    $s1, 4($sp)
-    .cfi_rel_offset 17, 4
-    sw    $s0, 0($sp)
-    .cfi_rel_offset 16, 0
-    move  $fp, $sp              # save sp in fp
-    .cfi_def_cfa_register 30
-    move  $s1, $a3              # move managed thread pointer into s1
-    addiu $s0, $zero, SUSPEND_CHECK_INTERVAL  # reset s0 to suspend check interval. TODO: unused?
-    addiu $t0, $a2, 16          # create space for method pointer in frame
-    srl   $t0, $t0, 3           # shift the frame size right 3
-    sll   $t0, $t0, 3           # shift the frame size left 3 to align to 16 bytes
-    subu  $sp, $sp, $t0         # reserve stack space for argument array
-    addiu $a0, $sp, 4           # pass stack pointer + method ptr as dest for memcpy
-    jal   memcpy                # (dest, src, bytes)
-    addiu $sp, $sp, -16         # make space for argument slots for memcpy
-    addiu $sp, $sp, 16          # restore stack after memcpy
-    lw    $a0, 16($fp)          # restore method*
-    lw    $a1, 4($sp)           # copy arg value for a1
-    lw    $a2, 8($sp)           # copy arg value for a2
-    lw    $a3, 12($sp)          # copy arg value for a3
-    lw    $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32($a0)  # get pointer to the code
-    jalr  $t9                   # call the method
-    sw    $zero, 0($sp)         # store NULL for method* at bottom of frame
-    move  $sp, $fp              # restore the stack
-    lw    $s0, 0($sp)
-    .cfi_restore 16
-    lw    $s1, 4($sp)
-    .cfi_restore 17
-    lw    $fp, 8($sp)
-    .cfi_restore 30
-    lw    $ra, 12($sp)
-    .cfi_restore 31
-    addiu $sp, $sp, 16
-    .cfi_adjust_cfa_offset -16
-    lw    $t0, 16($sp)          # get result pointer
-    lw    $t1, 20($sp)          # get result type char
-    li    $t2, 68               # put char 'D' into t2
-    beq   $t1, $t2, 1f          # branch if result type char == 'D'
-    li    $t3, 70               # put char 'F' into t3
-    beq   $t1, $t3, 1f          # branch if result type char == 'F'
-    sw    $v0, 0($t0)           # store the result
-    jr    $ra
-    sw    $v1, 4($t0)           # store the other half of the result
-1:
-    s.s   $f0, 0($t0)           # store floating point result
-    jr    $ra
-    s.s   $f1, 4($t0)           # store other half of floating point result
-END art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 44feee6..df2feb7 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -154,7 +154,7 @@
 
 .macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
-    jr     $ra
+    jalr   $zero, $ra
     nop
 .endm
 
@@ -190,12 +190,12 @@
     .cfi_rel_offset 19, 32
     sw     $s2, 28($sp)
     .cfi_rel_offset 18, 28
-    sw     $a3, 12($sp)
-    .cfi_rel_offset 7, 12
-    sw     $a2, 8($sp)
-    .cfi_rel_offset 6, 8
-    sw     $a1, 4($sp)
-    .cfi_rel_offset 5, 4
+    sw     $a3, 24($sp)
+    .cfi_rel_offset 7, 24
+    sw     $a2, 20($sp)
+    .cfi_rel_offset 6, 20
+    sw     $a1, 16($sp)
+    .cfi_rel_offset 5, 16
     # bottom will hold Method*
 .endm
 
@@ -257,11 +257,11 @@
     .cfi_restore 19
     lw     $s2, 28($sp)
     .cfi_restore 18
-    lw     $a3, 12($sp)
+    lw     $a3, 24($sp)
     .cfi_restore 7
-    lw     $a2, 8($sp)
+    lw     $a2, 20($sp)
     .cfi_restore 6
-    lw     $a1, 4($sp)
+    lw     $a1, 16($sp)
     .cfi_restore 5
     addiu  $sp, $sp, 64           # pop frame
     .cfi_adjust_cfa_offset -64
@@ -274,7 +274,7 @@
 .macro DELIVER_PENDING_EXCEPTION
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME     # save callee saves for throw
     la      $t9, artDeliverPendingExceptionFromCode
-    jr      $t9                          # artDeliverPendingExceptionFromCode(Thread*)
+    jalr    $zero, $t9                   # artDeliverPendingExceptionFromCode(Thread*)
     move    $a0, rSELF                   # pass Thread::Current
 .endm
 
@@ -283,7 +283,7 @@
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bnez   $t0, 1f                       # success if no exception is pending
     nop
-    jr     $ra
+    jalr   $zero, $ra
     nop
 1:
     DELIVER_PENDING_EXCEPTION
@@ -293,17 +293,17 @@
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     bnez   $v0, 1f                       # success?
     nop
-    jr     $ra                           # return on success
+    jalr   $zero, $ra                    # return on success
     nop
 1:
     DELIVER_PENDING_EXCEPTION
 .endm
 
-.macro RETURN_IF_RESULT_IS_NON_ZERO
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
     RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
     beqz   $v0, 1f                       # success?
     nop
-    jr     $ra                           # return on success
+    jalr   $zero, $ra                    # return on success
     nop
 1:
     DELIVER_PENDING_EXCEPTION
@@ -314,38 +314,23 @@
      * FIXME: just guessing about the shape of the jmpbuf.  Where will pc be?
      */
 ENTRY art_quick_do_long_jump
-    l.s     $f0, 0($a1)
-    l.s     $f1, 4($a1)
-    l.s     $f2, 8($a1)
-    l.s     $f3, 12($a1)
-    l.s     $f4, 16($a1)
-    l.s     $f5, 20($a1)
-    l.s     $f6, 24($a1)
-    l.s     $f7, 28($a1)
-    l.s     $f8, 32($a1)
-    l.s     $f9, 36($a1)
-    l.s     $f10, 40($a1)
-    l.s     $f11, 44($a1)
-    l.s     $f12, 48($a1)
-    l.s     $f13, 52($a1)
-    l.s     $f14, 56($a1)
-    l.s     $f15, 60($a1)
-    l.s     $f16, 64($a1)
-    l.s     $f17, 68($a1)
-    l.s     $f18, 72($a1)
-    l.s     $f19, 76($a1)
-    l.s     $f20, 80($a1)
-    l.s     $f21, 84($a1)
-    l.s     $f22, 88($a1)
-    l.s     $f23, 92($a1)
-    l.s     $f24, 96($a1)
-    l.s     $f25, 100($a1)
-    l.s     $f26, 104($a1)
-    l.s     $f27, 108($a1)
-    l.s     $f28, 112($a1)
-    l.s     $f29, 116($a1)
-    l.s     $f30, 120($a1)
-    l.s     $f31, 124($a1)
+    LDu  $f0,  $f1,   0*8, $a1, $t1
+    LDu  $f2,  $f3,   1*8, $a1, $t1
+    LDu  $f4,  $f5,   2*8, $a1, $t1
+    LDu  $f6,  $f7,   3*8, $a1, $t1
+    LDu  $f8,  $f9,   4*8, $a1, $t1
+    LDu  $f10, $f11,  5*8, $a1, $t1
+    LDu  $f12, $f13,  6*8, $a1, $t1
+    LDu  $f14, $f15,  7*8, $a1, $t1
+    LDu  $f16, $f17,  8*8, $a1, $t1
+    LDu  $f18, $f19,  9*8, $a1, $t1
+    LDu  $f20, $f21, 10*8, $a1, $t1
+    LDu  $f22, $f23, 11*8, $a1, $t1
+    LDu  $f24, $f25, 12*8, $a1, $t1
+    LDu  $f26, $f27, 13*8, $a1, $t1
+    LDu  $f28, $f29, 14*8, $a1, $t1
+    LDu  $f30, $f31, 15*8, $a1, $t1
+
     .set push
     .set nomacro
     .set noat
@@ -380,7 +365,7 @@
     lw      $ra, 124($a0)
     lw      $a0, 16($a0)
     move    $v0, $zero          # clear result registers r0 and r1
-    jr      $ra                 # do long jump
+    jalr    $zero, $ra          # do long jump
     move    $v1, $zero
 END art_quick_do_long_jump
 
@@ -392,7 +377,7 @@
 ENTRY art_quick_deliver_exception
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artDeliverExceptionFromCode
-    jr   $t9                        # artDeliverExceptionFromCode(Throwable*, Thread*)
+    jalr $zero, $t9                 # artDeliverExceptionFromCode(Throwable*, Thread*)
     move $a1, rSELF                 # pass Thread::Current
 END art_quick_deliver_exception
 
@@ -403,7 +388,7 @@
 ENTRY art_quick_throw_null_pointer_exception
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artThrowNullPointerExceptionFromCode
-    jr   $t9                        # artThrowNullPointerExceptionFromCode(Thread*)
+    jalr $zero, $t9                 # artThrowNullPointerExceptionFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
 END art_quick_throw_null_pointer_exception
 
@@ -414,7 +399,7 @@
 ENTRY art_quick_throw_div_zero
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artThrowDivZeroFromCode
-    jr   $t9                        # artThrowDivZeroFromCode(Thread*)
+    jalr $zero, $t9                 # artThrowDivZeroFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
 END art_quick_throw_div_zero
 
@@ -425,7 +410,7 @@
 ENTRY art_quick_throw_array_bounds
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artThrowArrayBoundsFromCode
-    jr   $t9                        # artThrowArrayBoundsFromCode(index, limit, Thread*)
+    jalr $zero, $t9                 # artThrowArrayBoundsFromCode(index, limit, Thread*)
     move $a2, rSELF                 # pass Thread::Current
 END art_quick_throw_array_bounds
 
@@ -436,7 +421,7 @@
 ENTRY art_quick_throw_stack_overflow
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artThrowStackOverflowFromCode
-    jr   $t9                        # artThrowStackOverflowFromCode(Thread*)
+    jalr $zero, $t9                 # artThrowStackOverflowFromCode(Thread*)
     move $a0, rSELF                 # pass Thread::Current
 END art_quick_throw_stack_overflow
 
@@ -447,7 +432,7 @@
 ENTRY art_quick_throw_no_such_method
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artThrowNoSuchMethodFromCode
-    jr   $t9                        # artThrowNoSuchMethodFromCode(method_idx, Thread*)
+    jalr $zero, $t9                 # artThrowNoSuchMethodFromCode(method_idx, Thread*)
     move $a1, rSELF                 # pass Thread::Current
 END art_quick_throw_no_such_method
 
@@ -480,7 +465,7 @@
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     beqz  $v0, 1f
     move  $t9, $v1                        # save $v0->code_
-    jr    $t9
+    jalr  $zero, $t9
     nop
 1:
     DELIVER_PENDING_EXCEPTION
@@ -555,12 +540,12 @@
     li    $t3, 70               # put char 'F' into t3
     beq   $t1, $t3, 1f          # branch if result type char == 'F'
     sw    $v0, 0($t0)           # store the result
-    jr    $ra
+    jalr  $zero, $ra
     sw    $v1, 4($t0)           # store the other half of the result
 1:
-    s.s   $f0, 0($t0)           # store floating point result
-    jr    $ra
-    s.s   $f1, 4($t0)           # store other half of floating point result
+    SDu   $f0, $f1, 0, $t0, $t1 # store floating point result
+    jalr  $zero, $ra
+    nop
 END art_quick_invoke_stub
 
     /*
@@ -619,7 +604,7 @@
     addiu  $sp, $sp, 16
     beqz   $v0, .Lthrow_class_cast_exception
     lw     $ra, 12($sp)
-    jr     $ra
+    jalr   $zero, $ra
     addiu  $sp, $sp, 16
     .cfi_adjust_cfa_offset -16
 .Lthrow_class_cast_exception:
@@ -630,7 +615,7 @@
     .cfi_adjust_cfa_offset -16
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     la   $t9, artThrowClassCastException
-    jr   $t9                        # artThrowClassCastException (Class*, Class*, Thread*)
+    jalr $zero, $t9                 # artThrowClassCastException (Class*, Class*, Thread*)
     move $a2, rSELF                 # pass Thread::Current
 END art_quick_check_cast
 
@@ -672,13 +657,13 @@
     srl $t1, $a0, 7
     add $t1, $t1, $t0
     sb  $t0, ($t1)
-    jr  $ra
+    jalr $zero, $ra
     nop
 .Ldo_aput_null:
     sll $a1, $a1, 2
     add $t0, $a0, $a1
     sw  $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)
-    jr  $ra
+    jalr $zero, $ra
     nop
 .Lcheck_assignability:
     addiu  $sp, $sp, -32
@@ -699,56 +684,18 @@
     lw     $a2, 8($sp)
     lw     $a1, 4($sp)
     lw     $a0, 0($sp)
-    add    $sp, 32
+    addiu  $sp, 32
     .cfi_adjust_cfa_offset -32
     bnez   $v0, .Ldo_aput
     nop
     SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
     move $a1, $a2
     la   $t9, artThrowArrayStoreException
-    jr   $t9                        # artThrowArrayStoreException(Class*, Class*, Thread*)
+    jalr $zero, $t9                 # artThrowArrayStoreException(Class*, Class*, Thread*)
     move $a2, rSELF                 # pass Thread::Current
 END art_quick_aput_obj
 
     /*
-     * Entry from managed code when uninitialized static storage, this stub will run the class
-     * initializer and deliver the exception on error. On success the static storage base is
-     * returned.
-     */
-    .extern artInitializeStaticStorageFromCode
-ENTRY art_quick_initialize_static_storage
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME           # save callee saves in case of GC
-    # artInitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*)
-    jal     artInitializeStaticStorageFromCode
-    move    $a2, rSELF                          # pass Thread::Current
-    RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_initialize_static_storage
-
-    /*
-     * Entry from managed code when dex cache misses for a type_idx.
-     */
-    .extern artInitializeTypeFromCode
-ENTRY art_quick_initialize_type
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME          # save callee saves in case of GC
-    # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
-    jal     artInitializeTypeFromCode
-    move    $a2, rSELF                         # pass Thread::Current
-    RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_initialize_type
-
-    /*
-     * Entry from managed code when type_idx needs to be checked for access and dex cache may also
-     * miss.
-     */
-    .extern artInitializeTypeAndVerifyAccessFromCode
-ENTRY art_quick_initialize_type_and_verify_access
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME          # save callee saves in case of GC
-    # artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*)
-    jal     artInitializeTypeAndVerifyAccessFromCode
-    move    $a2, rSELF                         # pass Thread::Current
-    RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_initialize_type_and_verify_access
-    /*
      * Called by managed code to resolve a static field and load a boolean primitive value.
      */
     .extern artGetBooleanStaticFromCode
@@ -954,6 +901,7 @@
     .extern artSet64StaticFromCode
 ENTRY art_quick_set64_static
     lw     $a1, 0($sp)                   # pass referrer's Method*
+                                         # 64 bit new_val is in a2:a3 pair
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME    # save callee saves in case of GC
     jal    artSet64StaticFromCode        # (field_idx, referrer, new_val, Thread*)
     sw     rSELF, 16($sp)                # pass Thread::Current
@@ -1014,6 +962,7 @@
     .extern artSet64InstanceFromCode
 ENTRY art_quick_set64_instance
     lw     $t1, 0($sp)                   # load referrer's Method*
+                                         # 64 bit new_val is in a2:a3 pair
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME    # save callee saves in case of GC
     sw     rSELF, 20($sp)                # pass Thread::Current
     jal    artSet64InstanceFromCode      # (field_idx, Object*, new_val, referrer, Thread*)
@@ -1033,22 +982,6 @@
     RETURN_IF_ZERO
 END art_quick_set_obj_instance
 
-    /*
-     * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
-     * exception on error. On success the String is returned. R0 holds the referring method,
-     * R1 holds the string index. The fast path check for hit in strings cache has already been
-     * performed.
-     */
-    .extern artResolveStringFromCode
-ENTRY art_quick_resolve_string
-    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
-    # artResolveStringFromCode(Method* referrer, uint32_t string_idx, Thread*)
-    jal     artResolveStringFromCode
-    move    $a2, rSELF                # pass Thread::Current
-    RETURN_IF_RESULT_IS_NON_ZERO
-END art_quick_resolve_string
-
-
 // Macro to facilitate adding new allocation entrypoints.
 .macro TWO_ARG_DOWNCALL name, entrypoint, return
     .extern \entrypoint
@@ -1074,14 +1007,40 @@
 GENERATE_ALL_ALLOC_ENTRYPOINTS
 
     /*
+     * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
+     * exception on error. On success the String is returned. R0 holds the referring method,
+     * R1 holds the string index. The fast path check for hit in strings cache has already been
+     * performed.
+     */
+TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+    /*
+     * Entry from managed code when uninitialized static storage, this stub will run the class
+     * initializer and deliver the exception on error. On success the static storage base is
+     * returned.
+     */
+TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+    /*
+     * Entry from managed code when dex cache misses for a type_idx.
+     */
+TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+    /*
+     * Entry from managed code when type_idx needs to be checked for access and dex cache may also
+     * miss.
+     */
+TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+
+    /*
      * Called by managed code when the value in rSUSPEND has been decremented to 0.
      */
     .extern artTestSuspendFromCode
 ENTRY art_quick_test_suspend
     lh     $a0, THREAD_FLAGS_OFFSET(rSELF)
     bnez   $a0, 1f
-    addi  rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL   # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
-    jr     $ra
+    addiu  rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL   # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+    jalr   $zero, $ra
     nop
 1:
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME          # save callee saves for stack crawl
@@ -1103,9 +1062,10 @@
     lw      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     bnez    $t0, 1f
-    mtc1    $v0, $f0               # place return value to FP return value
-    jr      $ra
-    mtc1    $v1, $f1               # place return value to FP return value
+    # don't care if $v0 and/or $v1 are modified, when exception branch taken
+    MTD     $v0, $v1, $f0, $f1          # move float value to return value
+    jalr    $zero, $ra
+    nop
 1:
     DELIVER_PENDING_EXCEPTION
 END art_quick_proxy_invoke_handler
@@ -1121,7 +1081,7 @@
     add     $a0, $t0               # get address of target method
     lw      $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0)  # load the target method
     la      $t9, art_quick_invoke_interface_trampoline
-    jr      $t9
+    jalr    $zero, $t9
 END art_quick_imt_conflict_trampoline
 
     .extern artQuickResolutionTrampoline
@@ -1134,7 +1094,7 @@
     lw      $a0, ARG_SLOT_SIZE($sp)       # load resolved method to $a0
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     move    $t9, $v0               # code pointer must be in $t9 to generate the global pointer
-    jr      $v0                    # tail call to method
+    jalr    $zero, $v0             # tail call to method
     nop
 1:
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
@@ -1191,9 +1151,9 @@
     # tear dpown the callee-save frame
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
 
-    mtc1    $v0, $f0               # place return value to FP return value
-    jr      $ra
-    mtc1    $v1, $f1               # place return value to FP return value
+    MTD     $v0, $v1, $f0, $f1     # move float value to return value
+    jalr    $zero, $ra
+    nop
 
 1:
     move    $sp, $s8               # tear down the alloca
@@ -1211,9 +1171,10 @@
     lw      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     bnez    $t0, 1f
-    mtc1    $v0, $f0               # place return value to FP return value
-    jr      $ra
-    mtc1    $v1, $f1               # place return value to FP return value
+    # don't care if $v0 and/or $v1 are modified, when exception branch taken
+    MTD     $v0, $v1, $f0, $f1                  # move float value to return value
+    jalr    $zero, $ra
+    nop
 1:
     DELIVER_PENDING_EXCEPTION
 END art_quick_to_interpreter_bridge
@@ -1248,12 +1209,10 @@
     sw       $v0, 12($sp)
     .cfi_rel_offset 2, 32
     sw       $v1, 8($sp)
-    .cfi_rel_offset 3, 36 
-    s.s      $f0, 4($sp)
-    s.s      $f1, 0($sp)
+    .cfi_rel_offset 3, 36
+    s.d      $f0, 0($sp)
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
-    s.s      $f0, 16($sp)   # pass fpr result
-    s.s      $f1, 20($sp)
+    s.d      $f0, 16($sp)   # pass fpr result
     move     $a2, $v0       # pass gpr result
     move     $a3, $v1
     addiu    $a1, $sp, ARG_SLOT_SIZE   # pass $sp (remove arg slots)
@@ -1264,9 +1223,8 @@
     addiu    $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE  # args slot + refs_only callee save frame
     lw       $v0, 12($sp)   # restore return values
     lw       $v1, 8($sp)
-    l.s      $f0, 4($sp)
-    l.s      $f1, 0($sp)
-    jr       $t0            # return
+    l.d      $f0, 0($sp)
+    jalr     $zero, $t0     # return
     addiu    $sp, $sp, 16   # remove temp storage from stack
     .cfi_adjust_cfa_offset -16
 END art_quick_instrumentation_exit
@@ -1300,11 +1258,15 @@
     srl     $a0, 1
     srl     $a0, $v1                         #  alo<- alo >> (32-(shift&31))
     sll     $v1, $a1, $a2                    #  rhi<- ahi << (shift&31)
-    or      $v1, $a0                         #  rhi<- rhi | alo
     andi    $a2, 0x20                        #  shift< shift & 0x20
-    movn    $v1, $v0, $a2                    #  rhi<- rlo (if shift&0x20)
-    jr      $ra
-    movn    $v0, $zero, $a2                  #  rlo<- 0  (if shift&0x20)
+    beqz    $a2, 1f
+    or      $v1, $a0                         #  rhi<- rhi | alo
+
+    move    $v1, $v0                         #  rhi<- rlo (if shift&0x20)
+    move    $v0, $zero                       #  rlo<- 0 (if shift&0x20)
+
+1:  jalr    $zero, $ra
+    nop
 END art_quick_shl_long
 
     /*
@@ -1324,11 +1286,15 @@
     not     $a0, $a2                         #  alo<- 31-shift (shift is 5b)
     sll     $a1, 1
     sll     $a1, $a0                         #  ahi<- ahi << (32-(shift&31))
-    or      $v0, $a1                         #  rlo<- rlo | ahi
     andi    $a2, 0x20                        #  shift & 0x20
-    movn    $v0, $v1, $a2                    #  rlo<- rhi (if shift&0x20)
-    jr      $ra
-    movn    $v1, $a3, $a2                    #  rhi<- sign(ahi) (if shift&0x20)
+    beqz    $a2, 1f
+    or      $v0, $a1                         #  rlo<- rlo | ahi
+
+    move    $v0, $v1                         #  rlo<- rhi (if shift&0x20)
+    move    $v1, $a3                         #  rhi<- sign(ahi) (if shift&0x20)
+
+1:  jalr    $zero, $ra
+    nop
 END art_quick_shr_long
 
     /*
@@ -1348,11 +1314,15 @@
     not     $a0, $a2                         #  alo<- 31-shift (shift is 5b)
     sll     $a1, 1
     sll     $a1, $a0                         #  ahi<- ahi << (32-(shift&31))
-    or      $v0, $a1                         #  rlo<- rlo | ahi
     andi    $a2, 0x20                        #  shift & 0x20
-    movn    $v0, $v1, $a2                    #  rlo<- rhi (if shift&0x20)
-    jr      $ra
-    movn    $v1, $zero, $a2                  #  rhi<- 0 (if shift&0x20)
+    beqz    $a2, 1f
+    or      $v0, $a1                         #  rlo<- rlo | ahi
+
+    move    $v0, $v1                         #  rlo<- rhi (if shift&0x20)
+    move    $v1, $zero                       #  rhi<- 0 (if shift&0x20)
+
+1:  jalr    $zero, $ra
+    nop
 END art_quick_ushr_long
 
 UNIMPLEMENTED art_quick_indexof
diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S
new file mode 100644
index 0000000..10976bb
--- /dev/null
+++ b/runtime/arch/mips64/asm_support_mips64.S
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
+#define ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
+
+#include "asm_support_mips64.h"
+
+// Define special registers.
+
+// Register holding suspend check count down.
+#define rSUSPEND $s0
+// Register holding Thread::Current().
+#define rSELF $s1
+
+
+    //  Declare a function called name, sets up $gp.
+.macro ENTRY name
+    .type \name, %function
+    .global \name
+    // Cache alignment for function entry.
+    .balign 16
+\name:
+    .cfi_startproc
+    // Ensure we get a sane starting CFA.
+    .cfi_def_cfa $sp,0
+    // Load $gp. We expect that ".set noreorder" is in effect.
+    .cpload $t9
+    // Declare a local convenience label to be branched to when $gp is already set up.
+.L\name\()_gp_set:
+.endm
+
+     // Declare a function called name, doesn't set up $gp.
+.macro ENTRY_NO_GP name
+    .type \name, %function
+    .global \name
+    // Cache alignment for function entry.
+    .balign 16
+\name:
+    .cfi_startproc
+     // Ensure we get a sane starting CFA.
+    .cfi_def_cfa $sp,0
+.endm
+
+.macro END name
+    .cfi_endproc
+    .size \name, .-\name
+.endm
+
+.macro UNIMPLEMENTED name
+    ENTRY \name
+    break
+    break
+    END \name
+.endm
+
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
diff --git a/runtime/arch/mips64/asm_support_mips64.h b/runtime/arch/mips64/asm_support_mips64.h
new file mode 100644
index 0000000..995fcf3
--- /dev/null
+++ b/runtime/arch/mips64/asm_support_mips64.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_
+
+#include "asm_support.h"
+
+// 64 ($f24-$f31) + 64 ($s0-$s7) + 8 ($gp) + 8 ($s8) + 8 ($ra) + 1x8 bytes padding
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 160
+// 48 ($s2-$s7) + 8 ($gp) + 8 ($s8) + 8 ($ra) + 1x8 bytes padding
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 80
+// $f12-$f19, $a1-$a7, $s2-$s7 + $gp + $s8 + $ra, 16 total + 1x8 bytes padding + method*
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 208
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_H_
diff --git a/runtime/arch/mips64/context_mips64.cc b/runtime/arch/mips64/context_mips64.cc
new file mode 100644
index 0000000..1c96bd4
--- /dev/null
+++ b/runtime/arch/mips64/context_mips64.cc
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#include "context_mips64.h"
+
+#include "mirror/art_method-inl.h"
+#include "quick/quick_method_frame_info.h"
+#include "util.h"
+
+namespace art {
+namespace mips64 {
+
+static constexpr uintptr_t gZero = 0;
+
+void Mips64Context::Reset() {
+  for (size_t i = 0; i < kNumberOfGpuRegisters; i++) {
+    gprs_[i] = nullptr;
+  }
+  for (size_t i = 0; i < kNumberOfFpuRegisters; i++) {
+    fprs_[i] = nullptr;
+  }
+  gprs_[SP] = &sp_;
+  gprs_[RA] = &ra_;
+  // Initialize registers with easy to spot debug values.
+  sp_ = Mips64Context::kBadGprBase + SP;
+  ra_ = Mips64Context::kBadGprBase + RA;
+}
+
+void Mips64Context::FillCalleeSaves(const StackVisitor& fr) {
+  mirror::ArtMethod* method = fr.GetMethod();
+  const QuickMethodFrameInfo frame_info = method->GetQuickFrameInfo();
+  size_t spill_count = POPCOUNT(frame_info.CoreSpillMask());
+  size_t fp_spill_count = POPCOUNT(frame_info.FpSpillMask());
+  if (spill_count > 0) {
+    // Lowest number spill is farthest away, walk registers and fill into context.
+    int j = 1;
+    for (size_t i = 0; i < kNumberOfGpuRegisters; i++) {
+      if (((frame_info.CoreSpillMask() >> i) & 1) != 0) {
+        gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_info.FrameSizeInBytes());
+        j++;
+      }
+    }
+  }
+  if (fp_spill_count > 0) {
+    // Lowest number spill is farthest away, walk registers and fill into context.
+    int j = 1;
+    for (size_t i = 0; i < kNumberOfFpuRegisters; i++) {
+      if (((frame_info.FpSpillMask() >> i) & 1) != 0) {
+        fprs_[i] = fr.CalleeSaveAddress(spill_count + fp_spill_count - j,
+                                        frame_info.FrameSizeInBytes());
+        j++;
+      }
+    }
+  }
+}
+
+void Mips64Context::SetGPR(uint32_t reg, uintptr_t value) {
+  CHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+  DCHECK(IsAccessibleGPR(reg));
+  CHECK_NE(gprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
+  *gprs_[reg] = value;
+}
+
+void Mips64Context::SetFPR(uint32_t reg, uintptr_t value) {
+  CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFpuRegisters));
+  DCHECK(IsAccessibleFPR(reg));
+  CHECK_NE(fprs_[reg], &gZero);  // Can't overwrite this static value since they are never reset.
+  *fprs_[reg] = value;
+}
+
+void Mips64Context::SmashCallerSaves() {
+  // This needs to be 0 because we want a null/zero return value.
+  gprs_[V0] = const_cast<uintptr_t*>(&gZero);
+  gprs_[V1] = const_cast<uintptr_t*>(&gZero);
+  gprs_[A1] = nullptr;
+  gprs_[A0] = nullptr;
+  gprs_[A2] = nullptr;
+  gprs_[A3] = nullptr;
+  gprs_[A4] = nullptr;
+  gprs_[A5] = nullptr;
+  gprs_[A6] = nullptr;
+  gprs_[A7] = nullptr;
+
+  // f0-f23 are caller-saved; f24-f31 are callee-saved.
+  fprs_[F0] = nullptr;
+  fprs_[F1] = nullptr;
+  fprs_[F2] = nullptr;
+  fprs_[F3] = nullptr;
+  fprs_[F4] = nullptr;
+  fprs_[F5] = nullptr;
+  fprs_[F6] = nullptr;
+  fprs_[F7] = nullptr;
+  fprs_[F8] = nullptr;
+  fprs_[F9] = nullptr;
+  fprs_[F10] = nullptr;
+  fprs_[F11] = nullptr;
+  fprs_[F12] = nullptr;
+  fprs_[F13] = nullptr;
+  fprs_[F14] = nullptr;
+  fprs_[F15] = nullptr;
+  fprs_[F16] = nullptr;
+  fprs_[F17] = nullptr;
+  fprs_[F18] = nullptr;
+  fprs_[F19] = nullptr;
+  fprs_[F20] = nullptr;
+  fprs_[F21] = nullptr;
+  fprs_[F22] = nullptr;
+  fprs_[F23] = nullptr;
+}
+
+extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
+
+void Mips64Context::DoLongJump() {
+  uintptr_t gprs[kNumberOfGpuRegisters];
+  uintptr_t fprs[kNumberOfFpuRegisters];
+  for (size_t i = 0; i < kNumberOfGpuRegisters; ++i) {
+    gprs[i] = gprs_[i] != nullptr ? *gprs_[i] : Mips64Context::kBadGprBase + i;
+  }
+  for (size_t i = 0; i < kNumberOfFpuRegisters; ++i) {
+    fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : Mips64Context::kBadFprBase + i;
+  }
+  art_quick_do_long_jump(gprs, fprs);
+}
+
+}  // namespace mips64
+}  // namespace art
diff --git a/runtime/arch/mips64/context_mips64.h b/runtime/arch/mips64/context_mips64.h
new file mode 100644
index 0000000..1046723
--- /dev/null
+++ b/runtime/arch/mips64/context_mips64.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_
+
+#include "arch/context.h"
+#include "base/logging.h"
+#include "registers_mips64.h"
+
+namespace art {
+namespace mips64 {
+
+class Mips64Context : public Context {
+ public:
+  Mips64Context() {
+    Reset();
+  }
+  virtual ~Mips64Context() {}
+
+  void Reset() OVERRIDE;
+
+  void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void SetSP(uintptr_t new_sp) OVERRIDE {
+    SetGPR(SP, new_sp);
+  }
+
+  void SetPC(uintptr_t new_pc) OVERRIDE {
+    SetGPR(RA, new_pc);
+  }
+
+  bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+    return gprs_[reg] != nullptr;
+  }
+
+  uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+    return gprs_[reg];
+  }
+
+  uintptr_t GetGPR(uint32_t reg) OVERRIDE {
+    CHECK_LT(reg, static_cast<uint32_t>(kNumberOfGpuRegisters));
+    DCHECK(IsAccessibleGPR(reg));
+    return *gprs_[reg];
+  }
+
+  void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+
+  bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
+    CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFpuRegisters));
+    return fprs_[reg] != nullptr;
+  }
+
+  uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+    CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFpuRegisters));
+    DCHECK(IsAccessibleFPR(reg));
+    return *fprs_[reg];
+  }
+
+  void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+
+  void SmashCallerSaves() OVERRIDE;
+  void DoLongJump() OVERRIDE;
+
+ private:
+  // Pointers to registers in the stack, initialized to NULL except for the special cases below.
+  uintptr_t* gprs_[kNumberOfGpuRegisters];
+  uint64_t* fprs_[kNumberOfFpuRegisters];
+  // Hold values for sp and ra (return address) if they are not located within a stack frame.
+  uintptr_t sp_, ra_;
+};
+}  // namespace mips64
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
new file mode 100644
index 0000000..4a3bf02
--- /dev/null
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+#include "atomic.h"
+#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/quick/quick_alloc_entrypoints.h"
+#include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/math_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
+
+namespace art {
+
+// Cast entrypoints.
+extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
+                                            const mirror::Class* ref_class);
+// Math entrypoints.
+extern int32_t CmpgDouble(double a, double b);
+extern int32_t CmplDouble(double a, double b);
+extern int32_t CmpgFloat(float a, float b);
+extern int32_t CmplFloat(float a, float b);
+extern "C" int64_t artLmul(int64_t a, int64_t b);
+extern "C" int64_t artLdiv(int64_t a, int64_t b);
+extern "C" int64_t artLmod(int64_t a, int64_t b);
+
+// Math conversions.
+extern "C" int32_t __fixsfsi(float op1);      // FLOAT_TO_INT
+extern "C" int32_t __fixdfsi(double op1);     // DOUBLE_TO_INT
+extern "C" float __floatdisf(int64_t op1);    // LONG_TO_FLOAT
+extern "C" double __floatdidf(int64_t op1);   // LONG_TO_DOUBLE
+extern "C" int64_t __fixsfdi(float op1);      // FLOAT_TO_LONG
+extern "C" int64_t __fixdfdi(double op1);     // DOUBLE_TO_LONG
+
+// Single-precision FP arithmetics.
+extern "C" float fmodf(float a, float b);      // REM_FLOAT[_2ADDR]
+
+// Double-precision FP arithmetics.
+extern "C" double fmod(double a, double b);     // REM_DOUBLE[_2ADDR]
+
+// Long long arithmetics - REM_LONG[_2ADDR] and DIV_LONG[_2ADDR]
+extern "C" int64_t __divdi3(int64_t, int64_t);
+extern "C" int64_t __moddi3(int64_t, int64_t);
+
+void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
+                     QuickEntryPoints* qpoints) {
+  // Interpreter
+  ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
+  ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
+
+  // JNI
+  jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
+
+  // Alloc
+  ResetQuickAllocEntryPoints(qpoints);
+
+  // Cast
+  qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
+  qpoints->pCheckCast = art_quick_check_cast;
+
+  // DexCache
+  qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
+  qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
+  qpoints->pInitializeType = art_quick_initialize_type;
+  qpoints->pResolveString = art_quick_resolve_string;
+
+  // Field
+  qpoints->pSet8Instance = art_quick_set8_instance;
+  qpoints->pSet8Static = art_quick_set8_static;
+  qpoints->pSet16Instance = art_quick_set16_instance;
+  qpoints->pSet16Static = art_quick_set16_static;
+  qpoints->pSet32Instance = art_quick_set32_instance;
+  qpoints->pSet32Static = art_quick_set32_static;
+  qpoints->pSet64Instance = art_quick_set64_instance;
+  qpoints->pSet64Static = art_quick_set64_static;
+  qpoints->pSetObjInstance = art_quick_set_obj_instance;
+  qpoints->pSetObjStatic = art_quick_set_obj_static;
+  qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+  qpoints->pGetByteInstance = art_quick_get_byte_instance;
+  qpoints->pGetCharInstance = art_quick_get_char_instance;
+  qpoints->pGetShortInstance = art_quick_get_short_instance;
+  qpoints->pGet32Instance = art_quick_get32_instance;
+  qpoints->pGet64Instance = art_quick_get64_instance;
+  qpoints->pGetObjInstance = art_quick_get_obj_instance;
+  qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+  qpoints->pGetByteStatic = art_quick_get_byte_static;
+  qpoints->pGetCharStatic = art_quick_get_char_static;
+  qpoints->pGetShortStatic = art_quick_get_short_static;
+  qpoints->pGet32Static = art_quick_get32_static;
+  qpoints->pGet64Static = art_quick_get64_static;
+  qpoints->pGetObjStatic = art_quick_get_obj_static;
+
+  // Array
+  qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+  qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+  qpoints->pAputObject = art_quick_aput_obj;
+  qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
+
+  // JNI
+  qpoints->pJniMethodStart = JniMethodStart;
+  qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
+  qpoints->pJniMethodEnd = JniMethodEnd;
+  qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
+  qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
+  qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+  qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
+
+  // Locks
+  qpoints->pLockObject = art_quick_lock_object;
+  qpoints->pUnlockObject = art_quick_unlock_object;
+
+  // Math
+  qpoints->pCmpgDouble = CmpgDouble;
+  qpoints->pCmpgFloat = CmpgFloat;
+  qpoints->pCmplDouble = CmplDouble;
+  qpoints->pCmplFloat = CmplFloat;
+  qpoints->pFmod = fmod;
+  qpoints->pL2d = art_l2d;
+  qpoints->pFmodf = fmodf;
+  qpoints->pL2f = art_l2f;
+  qpoints->pD2iz = art_d2i;
+  qpoints->pF2iz = art_f2i;
+  qpoints->pIdivmod = NULL;
+  qpoints->pD2l = art_d2l;
+  qpoints->pF2l = art_f2l;
+  qpoints->pLdiv = artLdiv;
+  qpoints->pLmod = artLmod;
+  qpoints->pLmul = artLmul;
+  qpoints->pShlLong = NULL;
+  qpoints->pShrLong = NULL;
+  qpoints->pUshrLong = NULL;
+
+  // Intrinsics
+  qpoints->pIndexOf = art_quick_indexof;
+  qpoints->pStringCompareTo = art_quick_string_compareto;
+  qpoints->pMemcpy = memcpy;
+
+  // Invocation
+  qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
+  qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
+  qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
+  qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
+  qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
+  qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
+  qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
+  qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check;
+
+  // Thread
+  qpoints->pTestSuspend = art_quick_test_suspend;
+
+  // Throws
+  qpoints->pDeliverException = art_quick_deliver_exception;
+  qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
+  qpoints->pThrowDivZero = art_quick_throw_div_zero;
+  qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
+  qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
+  qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+
+  // TODO - use lld/scd instructions for Mips64
+  // Atomic 64-bit load/store
+  qpoints->pA64Load = QuasiAtomic::Read64;
+  qpoints->pA64Store = QuasiAtomic::Write64;
+};
+
+}  // namespace art
diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc
new file mode 100644
index 0000000..7b5cd49
--- /dev/null
+++ b/runtime/arch/mips64/fault_handler_mips64.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+
+#include "fault_handler.h"
+#include <sys/ucontext.h>
+#include "base/macros.h"
+#include "globals.h"
+#include "base/logging.h"
+#include "base/hex_dump.h"
+
+
+//
+// Mips64 specific fault handler functions.
+//
+
+namespace art {
+
+void FaultManager::HandleNestedSignal(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                      void* context ATTRIBUTE_UNUSED) {
+}
+
+void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED,
+                                             void* context ATTRIBUTE_UNUSED,
+                                             mirror::ArtMethod** out_method ATTRIBUTE_UNUSED,
+                                             uintptr_t* out_return_pc ATTRIBUTE_UNUSED,
+                                             uintptr_t* out_sp ATTRIBUTE_UNUSED) {
+}
+
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                void* context ATTRIBUTE_UNUSED) {
+  return false;
+}
+
+bool SuspensionHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                               void* context ATTRIBUTE_UNUSED) {
+  return false;
+}
+
+bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
+                                  void* context ATTRIBUTE_UNUSED) {
+  return false;
+}
+}       // namespace art
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
new file mode 100644
index 0000000..26478cb
--- /dev/null
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include "instruction_set_features_mips64.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "base/stringprintf.h"
+#include "utils.h"  // For Trim.
+
+namespace art {
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromVariant(
+    const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
+  // TODO: r6 variant.
+  if (variant != "default") {
+    std::ostringstream os;
+    LOG(WARNING) << "Unexpected CPU variant for Mips64 using defaults: " << variant;
+  }
+  bool smp = true;  // Conservative default.
+  return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
+  bool smp = (bitmap & kSmpBitfield) != 0;
+  return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromCppDefines() {
+  const bool smp = true;
+
+  return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromCpuInfo() {
+  // Look in /proc/cpuinfo for features we need.  Only use this when we can guarantee that
+  // the kernel puts the appropriate feature flags in here.  Sometimes it doesn't.
+  bool smp = false;
+
+  std::ifstream in("/proc/cpuinfo");
+  if (!in.fail()) {
+    while (!in.eof()) {
+      std::string line;
+      std::getline(in, line);
+      if (!in.eof()) {
+        LOG(INFO) << "cpuinfo line: " << line;
+        if (line.find("processor") != std::string::npos && line.find(": 1") != std::string::npos) {
+          smp = true;
+        }
+      }
+    }
+    in.close();
+  } else {
+    LOG(ERROR) << "Failed to open /proc/cpuinfo";
+  }
+  return new Mips64InstructionSetFeatures(smp);
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromHwcap() {
+  UNIMPLEMENTED(WARNING);
+  return FromCppDefines();
+}
+
+const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromAssembly() {
+  UNIMPLEMENTED(WARNING);
+  return FromCppDefines();
+}
+
+bool Mips64InstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
+  if (kMips64 != other->GetInstructionSet()) {
+    return false;
+  }
+  return (IsSmp() == other->IsSmp());
+}
+
+uint32_t Mips64InstructionSetFeatures::AsBitmap() const {
+  return (IsSmp() ? kSmpBitfield : 0);
+}
+
+std::string Mips64InstructionSetFeatures::GetFeatureString() const {
+  std::string result;
+  if (IsSmp()) {
+    result += "smp";
+  } else {
+    result += "-smp";
+  }
+  return result;
+}
+
+const InstructionSetFeatures* Mips64InstructionSetFeatures::AddFeaturesFromSplitString(
+    const bool smp, const std::vector<std::string>& features, std::string* error_msg) const {
+  auto i = features.begin();
+  if (i != features.end()) {
+    // We don't have any features.
+    std::string feature = Trim(*i);
+    *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
+    return nullptr;
+  }
+  return new Mips64InstructionSetFeatures(smp);
+}
+
+}  // namespace art
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.h b/runtime/arch/mips64/instruction_set_features_mips64.h
new file mode 100644
index 0000000..d5d6012
--- /dev/null
+++ b/runtime/arch/mips64/instruction_set_features_mips64.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_INSTRUCTION_SET_FEATURES_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_INSTRUCTION_SET_FEATURES_MIPS64_H_
+
+#include "arch/instruction_set_features.h"
+
+namespace art {
+
+// Instruction set features relevant to the MIPS64 architecture.
+class Mips64InstructionSetFeatures FINAL : public InstructionSetFeatures {
+ public:
+  // Process a CPU variant string like "r4000" and create InstructionSetFeatures.
+  static const Mips64InstructionSetFeatures* FromVariant(const std::string& variant,
+                                                        std::string* error_msg);
+
+  // Parse a bitmap and create an InstructionSetFeatures.
+  static const Mips64InstructionSetFeatures* FromBitmap(uint32_t bitmap);
+
+  // Turn C pre-processor #defines into the equivalent instruction set features.
+  static const Mips64InstructionSetFeatures* FromCppDefines();
+
+  // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
+  static const Mips64InstructionSetFeatures* FromCpuInfo();
+
+  // Process the auxiliary vector AT_HWCAP entry and use kRuntimeISA to produce
+  // InstructionSetFeatures.
+  static const Mips64InstructionSetFeatures* FromHwcap();
+
+  // Use assembly tests of the current runtime (ie kRuntimeISA) to determine the
+  // InstructionSetFeatures. This works around kernel bugs in AT_HWCAP and /proc/cpuinfo.
+  static const Mips64InstructionSetFeatures* FromAssembly();
+
+  bool Equals(const InstructionSetFeatures* other) const OVERRIDE;
+
+  InstructionSet GetInstructionSet() const OVERRIDE {
+    return kMips64;
+  }
+
+  uint32_t AsBitmap() const OVERRIDE;
+
+  std::string GetFeatureString() const OVERRIDE;
+
+  virtual ~Mips64InstructionSetFeatures() {}
+
+ protected:
+  // Parse a vector of the form "fpu32", "mips2" adding these to a new Mips64InstructionSetFeatures.
+  virtual const InstructionSetFeatures*
+      AddFeaturesFromSplitString(const bool smp, const std::vector<std::string>& features,
+                                 std::string* error_msg) const OVERRIDE;
+
+ private:
+  explicit Mips64InstructionSetFeatures(bool smp) : InstructionSetFeatures(smp) {
+  }
+
+  // Bitmap positions for encoding features as a bitmap.
+  enum {
+    kSmpBitfield = 1,
+  };
+
+  DISALLOW_COPY_AND_ASSIGN(Mips64InstructionSetFeatures);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_INSTRUCTION_SET_FEATURES_MIPS64_H_
diff --git a/runtime/arch/mips64/instruction_set_features_mips64_test.cc b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
new file mode 100644
index 0000000..dc34506
--- /dev/null
+++ b/runtime/arch/mips64/instruction_set_features_mips64_test.cc
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#include "instruction_set_features_mips64.h"
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(Mips64InstructionSetFeaturesTest, Mips64Features) {
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> mips64_features(
+      InstructionSetFeatures::FromVariant(kMips64, "default", &error_msg));
+  ASSERT_TRUE(mips64_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(mips64_features->GetInstructionSet(), kMips64);
+  EXPECT_TRUE(mips64_features->Equals(mips64_features.get()));
+  EXPECT_STREQ("smp", mips64_features->GetFeatureString().c_str());
+  EXPECT_EQ(mips64_features->AsBitmap(), 1U);
+}
+
+}  // namespace art
diff --git a/runtime/arch/mips64/jni_entrypoints_mips64.S b/runtime/arch/mips64/jni_entrypoints_mips64.S
new file mode 100644
index 0000000..90fd3ee
--- /dev/null
+++ b/runtime/arch/mips64/jni_entrypoints_mips64.S
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include "asm_support_mips64.S"
+
+    .set noreorder
+    .balign 16
+
+    /*
+     * Jni dlsym lookup stub.
+     */
+    .extern artFindNativeMethod
+ENTRY art_jni_dlsym_lookup_stub
+    daddiu $sp, $sp, -80        # save a0-a7 and $ra
+    .cfi_adjust_cfa_offset 80
+    sd     $ra, 64($sp)
+    .cfi_rel_offset 31, 64
+    sw     $a7, 56($sp)
+    .cfi_rel_offset 11, 56
+    sw     $a6, 48($sp)
+    .cfi_rel_offset 10, 48
+    sw     $a5, 40($sp)
+    .cfi_rel_offset 9, 40
+    sw     $a4, 32($sp)
+    .cfi_rel_offset 8, 32
+    sw     $a3, 24($sp)
+    .cfi_rel_offset 7, 24
+    sw     $a2, 16($sp)
+    .cfi_rel_offset 6, 16
+    sw     $a1, 8($sp)
+    .cfi_rel_offset 5, 8
+    sw     $a0, 0($sp)
+    .cfi_rel_offset 4, 0
+    jal    artFindNativeMethod  # (Thread*)
+    move   $a0, $s1             # pass Thread::Current()
+    ld     $a0, 0($sp)          # restore registers from stack
+    .cfi_restore 4
+    ld     $a1, 8($sp)
+    .cfi_restore 5
+    ld     $a2, 16($sp)
+    .cfi_restore 6
+    ld     $a3, 24($sp)
+    .cfi_restore 7
+    ld     $a4, 32($sp)
+    .cfi_restore 8
+    ld     $a5, 40($sp)
+    .cfi_restore 9
+    ld     $a6, 48($sp)
+    .cfi_restore 10
+    ld     $a7, 56($sp)
+    .cfi_restore 11
+    ld     $ra, 64($sp)
+    .cfi_restore 31
+    beq    $v0, $zero, .Lno_native_code_found
+    daddiu $sp, $sp, 80         # restore the stack
+    .cfi_adjust_cfa_offset -80
+    move   $t9, $v0             # put method code result in $t9
+    jalr   $zero, $t9           # leaf call to method's code
+    nop
+.Lno_native_code_found:
+    jalr   $zero, $ra
+    nop
+END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/mips64/memcmp16_mips64.S b/runtime/arch/mips64/memcmp16_mips64.S
new file mode 100644
index 0000000..962977e
--- /dev/null
+++ b/runtime/arch/mips64/memcmp16_mips64.S
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_MEMCMP16_MIPS64_S_
+#define ART_RUNTIME_ARCH_MIPS64_MEMCMP16_MIPS64_S_
+
+#include "asm_support_mips64.S"
+
+.set noreorder
+
+// u4 __memcmp16(const u2*, const u2*, size_t);
+ENTRY_NO_GP __memcmp16
+  move  $t0, $zero
+  move  $t1, $zero
+  beqz  $a2, done       /* 0 length string */
+  nop
+  beq   $a0, $a1, done  /* addresses are identical */
+  nop
+
+1:
+  lhu   $t0, 0($a0)
+  lhu   $t1, 0($a1)
+  bne   $t0, $t1, done
+  nop
+  daddu $a0, 2
+  daddu $a1, 2
+  dsubu $a2, 1
+  bnez  $a2, 1b
+  nop
+
+done:
+  dsubu $v0, $t0, $t1
+  j     $ra
+  nop
+END __memcmp16
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_MEMCMP16_MIPS64_S_
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
new file mode 100644
index 0000000..60e692b
--- /dev/null
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -0,0 +1,936 @@
+/*
+ * 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.
+ */
+
+#include "asm_support_mips64.S"
+
+#include "arch/quick_alloc_entrypoints.S"
+
+    .set noreorder
+    .balign 16
+
+    /* Deliver the given exception */
+    .extern artDeliverExceptionFromCode
+    /* Deliver an exception pending on a thread */
+    .extern artDeliverPendingExceptionFromCode
+
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kSaveAll)
+     * callee-save: padding + $f24-$f31 + $s0-$s7 + $gp + $ra + $s8 = 19 total + 1x8 bytes padding
+     */
+.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+    daddiu $sp, $sp, -160
+    .cfi_adjust_cfa_offset 160
+
+     // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 160)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(MIPS64) size not as expected."
+#endif
+
+    sd     $ra, 152($sp)
+    .cfi_rel_offset 31, 152
+    sd     $s8, 144($sp)
+    .cfi_rel_offset 30, 144
+    sd     $gp, 136($sp)
+    .cfi_rel_offset 28, 136
+    sd     $s7, 128($sp)
+    .cfi_rel_offset 23, 128
+    sd     $s6, 120($sp)
+    .cfi_rel_offset 22, 120
+    sd     $s5, 112($sp)
+    .cfi_rel_offset 21, 112
+    sd     $s4, 104($sp)
+    .cfi_rel_offset 20, 104
+    sd     $s3,  96($sp)
+    .cfi_rel_offset 19, 96
+    sd     $s2,  88($sp)
+    .cfi_rel_offset 18, 88
+    sd     $s1,  80($sp)
+    .cfi_rel_offset 17, 80
+    sd     $s0,  72($sp)
+    .cfi_rel_offset 16, 72
+
+    // FP callee-saves
+    s.d    $f31, 64($sp)
+    s.d    $f30, 56($sp)
+    s.d    $f29, 48($sp)
+    s.d    $f28, 40($sp)
+    s.d    $f27, 32($sp)
+    s.d    $f26, 24($sp)
+    s.d    $f25, 16($sp)
+    s.d    $f24,  8($sp)
+
+    # load appropriate callee-save-method
+    ld      $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+    ld      $v0, 0($v0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    ld      $v0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($v0)
+    sw      $v0, 0($sp)                                # Place Method* at bottom of stack.
+    sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kRefsOnly). Restoration assumes
+     * non-moving GC.
+     * Does not include rSUSPEND or rSELF
+     * callee-save: padding + $s2-$s7 + $gp + $ra + $s8 = 9 total + 1x8 bytes padding
+     */
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+    daddiu $sp, $sp, -80
+    .cfi_adjust_cfa_offset 80
+
+    // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 80)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(MIPS64) size not as expected."
+#endif
+
+    sd     $ra, 72($sp)
+    .cfi_rel_offset 31, 72
+    sd     $s8, 64($sp)
+    .cfi_rel_offset 30, 64
+    sd     $gp, 56($sp)
+    .cfi_rel_offset 28, 56
+    sd     $s7, 48($sp)
+    .cfi_rel_offset 23, 48
+    sd     $s6, 40($sp)
+    .cfi_rel_offset 22, 40
+    sd     $s5, 32($sp)
+    .cfi_rel_offset 21, 32
+    sd     $s4, 24($sp)
+    .cfi_rel_offset 20, 24
+    sd     $s3, 16($sp)
+    .cfi_rel_offset 19, 16
+    sd     $s2, 8($sp)
+    .cfi_rel_offset 18, 8
+    # load appropriate callee-save-method
+    ld      $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+    ld      $v0, 0($v0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    ld      $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
+    sw      $v0, 0($sp)                                # Place Method* at bottom of stack.
+    sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    ld     $ra, 72($sp)
+    .cfi_restore 31
+    ld     $s8, 64($sp)
+    .cfi_restore 30
+    ld     $gp, 56($sp)
+    .cfi_restore 28
+    ld     $s7, 48($sp)
+    .cfi_restore 23
+    ld     $s6, 40($sp)
+    .cfi_restore 22
+    ld     $s5, 32($sp)
+    .cfi_restore 21
+    ld     $s4, 24($sp)
+    .cfi_restore 20
+    ld     $s3, 16($sp)
+    .cfi_restore 19
+    ld     $s2, 8($sp)
+    .cfi_restore 18
+    daddiu $sp, $sp, 80
+    .cfi_adjust_cfa_offset -80
+.endm
+
+.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    ld     $ra, 72($sp)
+    .cfi_restore 31
+    ld     $s8, 64($sp)
+    .cfi_restore 30
+    ld     $gp, 56($sp)
+    .cfi_restore 28
+    ld     $s7, 48($sp)
+    .cfi_restore 23
+    ld     $s6, 40($sp)
+    .cfi_restore 22
+    ld     $s5, 32($sp)
+    .cfi_restore 21
+    ld     $s4, 24($sp)
+    .cfi_restore 20
+    ld     $s3, 16($sp)
+    .cfi_restore 19
+    ld     $s2, 8($sp)
+    .cfi_restore 18
+    jalr   $zero, $ra
+    daddiu $sp, $sp, 80
+    .cfi_adjust_cfa_offset -80
+.endm
+
+// This assumes the top part of these stack frame types are identical.
+#define REFS_AND_ARGS_MINUS_REFS_SIZE (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes
+     * non-moving GC.
+     * callee-save: padding + $f12-$f19 + $a1-$a7 + $s2-$s7 + $gp + $ra + $s8 = 24 total + 1 words padding + Method*
+     */
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+    daddiu  $sp, $sp, -208
+    .cfi_adjust_cfa_offset 208
+
+    // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 208)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(MIPS64) size not as expected."
+#endif
+
+    sd     $ra, 200($sp)           # = kQuickCalleeSaveFrame_RefAndArgs_LrOffset
+    .cfi_rel_offset 31, 200
+    sd     $s8, 192($sp)
+    .cfi_rel_offset 30, 192
+    sd     $gp, 184($sp)
+    .cfi_rel_offset 28, 184
+    sd     $s7, 176($sp)
+    .cfi_rel_offset 23, 176
+    sd     $s6, 168($sp)
+    .cfi_rel_offset 22, 168
+    sd     $s5, 160($sp)
+    .cfi_rel_offset 21, 160
+    sd     $s4, 152($sp)
+    .cfi_rel_offset 20, 152
+    sd     $s3, 144($sp)
+    .cfi_rel_offset 19, 144
+    sd     $s2, 136($sp)
+    .cfi_rel_offset 18, 136
+
+    sd     $a7, 128($sp)
+    .cfi_rel_offset 11, 128
+    sd     $a6, 120($sp)
+    .cfi_rel_offset 10, 120
+    sd     $a5, 112($sp)
+    .cfi_rel_offset 9, 112
+    sd     $a4, 104($sp)
+    .cfi_rel_offset 8, 104
+    sd     $a3,  96($sp)
+    .cfi_rel_offset 7, 96
+    sd     $a2,  88($sp)
+    .cfi_rel_offset 6, 88
+    sd     $a1,  80($sp)           # = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset
+    .cfi_rel_offset 5, 80
+
+    s.d    $f19, 72($sp)
+    s.d    $f18, 64($sp)
+    s.d    $f17, 56($sp)
+    s.d    $f16, 48($sp)
+    s.d    $f15, 40($sp)
+    s.d    $f14, 32($sp)
+    s.d    $f13, 24($sp)           # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset
+    s.d    $f12, 16($sp)           # This isn't necessary to store.
+
+    # 1x8 bytes paddig + Method*
+    ld      $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+    ld      $v0, 0($v0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    ld      $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
+    sw      $v0, 0($sp)                                # Place Method* at bottom of stack.
+    sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+    # load appropriate callee-save-method
+    ld      $v0, %got(_ZN3art7Runtime9instance_E)($gp)
+    ld      $v0, 0($v0)
+    THIS_LOAD_REQUIRES_READ_BARRIER
+    ld      $v0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($v0)
+    sw      $v0, 0($sp)                                # Place Method* at bottom of stack.
+    sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.
+.endm
+
+.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    ld     $ra, 200($sp)
+    .cfi_restore 31
+    ld     $s8, 192($sp)
+    .cfi_restore 30
+    ld     $gp, 184($sp)
+    .cfi_restore 28
+    ld     $s7, 176($sp)
+    .cfi_restore 23
+    ld     $s6, 168($sp)
+    .cfi_restore 22
+    ld     $s5, 160($sp)
+    .cfi_restore 21
+    ld     $s4, 152($sp)
+    .cfi_restore 20
+    ld     $s3, 144($sp)
+    .cfi_restore 19
+    ld     $s2, 136($sp)
+    .cfi_restore 18
+
+    ld     $a7, 128($sp)
+    .cfi_restore 11
+    ld     $a6, 120($sp)
+    .cfi_restore 10
+    ld     $a5, 112($sp)
+    .cfi_restore 9
+    ld     $a4, 104($sp)
+    .cfi_restore 8
+    ld     $a3,  96($sp)
+    .cfi_restore 7
+    ld     $a2,  88($sp)
+    .cfi_restore 6
+    ld     $a1,  80($sp)
+    .cfi_restore 5
+
+    l.d    $f19, 72($sp)
+    l.d    $f18, 64($sp)
+    l.d    $f17, 56($sp)
+    l.d    $f16, 48($sp)
+    l.d    $f15, 40($sp)
+    l.d    $f14, 32($sp)
+    l.d    $f13, 24($sp)
+    l.d    $f12, 16($sp)
+
+    daddiu $sp, $sp, 208
+    .cfi_adjust_cfa_offset -208
+.endm
+
+    /*
+     * Macro that set calls through to artDeliverPendingExceptionFromCode,
+     * where the pending
+     * exception is Thread::Current()->exception_
+     */
+.macro DELIVER_PENDING_EXCEPTION
+    SETUP_SAVE_ALL_CALLEE_SAVE_FRAME     # save callee saves for throw
+    dla     $t9, artDeliverPendingExceptionFromCode
+    jalr    $zero, $t9                   # artDeliverPendingExceptionFromCode(Thread*)
+    move    $a0, rSELF                   # pass Thread::Current
+.endm
+
+.macro RETURN_IF_NO_EXCEPTION
+    ld     $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    bne    $t0, $zero, 1f                      # success if no exception is pending
+    nop
+    jalr   $zero, $ra
+    nop
+1:
+    DELIVER_PENDING_EXCEPTION
+.endm
+
+.macro RETURN_IF_ZERO
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    bne    $v0, $zero, 1f                # success?
+    nop
+    jalr   $zero, $ra                    # return on success
+    nop
+1:
+    DELIVER_PENDING_EXCEPTION
+.endm
+
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    beq    $v0, $zero, 1f                # success?
+    nop
+    jalr   $zero, $ra                    # return on success
+    nop
+1:
+    DELIVER_PENDING_EXCEPTION
+.endm
+
+    /*
+     * On entry $a0 is uint32_t* gprs_ and $a1 is uint32_t* fprs_
+     * FIXME: just guessing about the shape of the jmpbuf.  Where will pc be?
+     */
+ENTRY art_quick_do_long_jump
+    l.d     $f0, 0($a1)
+    l.d     $f1, 8($a1)
+    l.d     $f2, 16($a1)
+    l.d     $f3, 24($a1)
+    l.d     $f4, 32($a1)
+    l.d     $f5, 40($a1)
+    l.d     $f6, 48($a1)
+    l.d     $f7, 56($a1)
+    l.d     $f8, 64($a1)
+    l.d     $f9, 72($a1)
+    l.d     $f10, 80($a1)
+    l.d     $f11, 88($a1)
+    l.d     $f12, 96($a1)
+    l.d     $f13, 104($a1)
+    l.d     $f14, 112($a1)
+    l.d     $f15, 120($a1)
+    l.d     $f16, 128($a1)
+    l.d     $f17, 136($a1)
+    l.d     $f18, 144($a1)
+    l.d     $f19, 152($a1)
+    l.d     $f20, 160($a1)
+    l.d     $f21, 168($a1)
+    l.d     $f22, 176($a1)
+    l.d     $f23, 184($a1)
+    l.d     $f24, 192($a1)
+    l.d     $f25, 200($a1)
+    l.d     $f26, 208($a1)
+    l.d     $f27, 216($a1)
+    l.d     $f28, 224($a1)
+    l.d     $f29, 232($a1)
+    l.d     $f30, 240($a1)
+    l.d     $f31, 248($a1)
+    .set push
+    .set nomacro
+    .set noat
+# no need to load zero
+    ld      $at, 8($a0)
+    .set pop
+    ld      $v0, 16($a0)
+    ld      $v1, 24($a0)
+# a0 has to be loaded last
+    ld      $a1, 40($a0)
+    ld      $a2, 48($a0)
+    ld      $a3, 56($a0)
+    ld      $a4, 64($a0)
+    ld      $a5, 72($a0)
+    ld      $a6, 80($a0)
+    ld      $a7, 88($a0)
+    ld      $t0, 96($a0)
+    ld      $t1, 104($a0)
+    ld      $t2, 112($a0)
+    ld      $t3, 120($a0)
+    ld      $s0, 128($a0)
+    ld      $s1, 136($a0)
+    ld      $s2, 144($a0)
+    ld      $s3, 152($a0)
+    ld      $s4, 160($a0)
+    ld      $s5, 168($a0)
+    ld      $s6, 176($a0)
+    ld      $s7, 184($a0)
+    ld      $t8, 192($a0)
+    ld      $t9, 200($a0)
+# no need to load k0, k1
+    ld      $gp, 224($a0)
+    ld      $sp, 232($a0)
+    ld      $s8, 240($a0)
+    ld      $ra, 248($a0)
+    ld      $a0, 32($a0)
+    move    $v0, $zero          # clear result registers v0 and v1
+    jalr    $zero, $ra          # do long jump
+    move    $v1, $zero
+END art_quick_do_long_jump
+
+UNIMPLEMENTED art_quick_deliver_exception
+UNIMPLEMENTED art_quick_throw_null_pointer_exception
+UNIMPLEMENTED art_quick_throw_div_zero
+UNIMPLEMENTED art_quick_throw_array_bounds
+UNIMPLEMENTED art_quick_throw_stack_overflow
+UNIMPLEMENTED art_quick_throw_no_such_method
+
+UNIMPLEMENTED art_quick_invoke_interface_trampoline
+UNIMPLEMENTED art_quick_invoke_interface_trampoline_with_access_check
+
+UNIMPLEMENTED art_quick_invoke_static_trampoline_with_access_check
+UNIMPLEMENTED art_quick_invoke_direct_trampoline_with_access_check
+UNIMPLEMENTED art_quick_invoke_super_trampoline_with_access_check
+UNIMPLEMENTED art_quick_invoke_virtual_trampoline_with_access_check
+
+    # On entry:
+    #   t0 = shorty
+    #   t1 = ptr to arg_array
+    #   t2 = number of argument bytes remain
+    #   v0 = ptr to stack frame where to copy arg_array
+    # This macro modifies t3, t9 and v0
+.macro LOOP_OVER_SHORTY_LOADING_REG gpu, fpu, label
+    lbu    $t3, 0($t0)           # get argument type from shorty
+    beqz   $t3, \label
+    daddiu $t0, 1
+    li     $t9, 68               # put char 'D' into t9
+    beq    $t9, $t3, 1f          # branch if result type char == 'D'
+    li     $t9, 70               # put char 'F' into t9
+    beq    $t9, $t3, 2f          # branch if result type char == 'F'
+    li     $t9, 74               # put char 'J' into t9
+    beq    $t9, $t3, 3f          # branch if result type char == 'J'
+    nop
+    lwu    $\gpu, 0($t1)
+    sw     $\gpu, 0($v0)
+    daddiu $v0, 4
+    daddiu $t1, 4
+    b      4f
+    daddiu $t2, -4               # delay slot
+
+1:  # found double
+    lwu    $t3, 0($t1)
+    mtc1   $t3, $\fpu
+    sw     $t3, 0($v0)
+    lwu    $t3, 4($t1)
+    mthc1  $t3, $\fpu
+    sw     $t3, 4($v0)
+    daddiu $v0, 8
+    daddiu $t1, 8
+    b      4f
+    daddiu $t2, -8               # delay slot
+
+2:  # found float
+    lwu    $t3, 0($t1)
+    mtc1   $t3, $\fpu
+    sw     $t3, 0($v0)
+    daddiu $v0, 4
+    daddiu $t1, 4
+    b      4f
+    daddiu $t2, -4               # delay slot
+
+3:  # found long (8 bytes)
+    lwu    $t3, 0($t1)
+    sw     $t3, 0($v0)
+    lwu    $t9, 4($t1)
+    sw     $t9, 4($v0)
+    dsll   $t9, $t9, 32
+    or     $\gpu, $t9, $t3
+    daddiu $v0, 8
+    daddiu $t1, 8
+    daddiu $t2, -8
+4:
+.endm
+
+    /*
+     * Invocation stub for quick code.
+     * On entry:
+     *   a0 = method pointer
+     *   a1 = argument array that must at least contain the this ptr.
+     *   a2 = size of argument array in bytes
+     *   a3 = (managed) thread pointer
+     *   a4 = JValue* result
+     *   a5 = shorty
+     */
+ENTRY art_quick_invoke_stub
+    # push a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra onto the stack
+    daddiu $sp, $sp, -48
+    .cfi_adjust_cfa_offset 48
+    sd     $ra, 40($sp)
+    .cfi_rel_offset 31, 40
+    sd     $s8, 32($sp)
+    .cfi_rel_offset 30, 32
+    sd     $s1, 24($sp)
+    .cfi_rel_offset 17, 24
+    sd     $s0, 16($sp)
+    .cfi_rel_offset 16, 16
+    sd     $a5, 8($sp)
+    .cfi_rel_offset 9, 8
+    sd     $a4, 0($sp)
+    .cfi_rel_offset 8, 0
+
+    daddiu $s0, $zero, SUSPEND_CHECK_INTERVAL   # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+    move   $s1, $a3              # move managed thread pointer into s1 (rSELF)
+    move   $s8, $sp              # save sp in s8 (fp)
+
+    daddiu $t3, $a2, 20          # add 4 for method* and 16 for stack alignment
+    dsrl   $t3, $t3, 4           # shift the frame size right 4
+    dsll   $t3, $t3, 4           # shift the frame size left 4 to align to 16 bytes
+    dsubu  $sp, $sp, $t3         # reserve stack space for argument array
+
+    daddiu $t0, $a5, 1           # t0 = shorty[1] (skip 1 for return type)
+    daddiu $t1, $a1, 4           # t1 = ptr to arg_array[4] (skip this ptr)
+    daddiu $t2, $a2, -4          # t2 = number of argument bytes remain (skip this ptr)
+    daddiu $v0, $sp, 8           # v0 points to where to copy arg_array
+    LOOP_OVER_SHORTY_LOADING_REG a2, f14, call_fn
+    LOOP_OVER_SHORTY_LOADING_REG a3, f15, call_fn
+    LOOP_OVER_SHORTY_LOADING_REG a4, f16, call_fn
+    LOOP_OVER_SHORTY_LOADING_REG a5, f17, call_fn
+    LOOP_OVER_SHORTY_LOADING_REG a6, f18, call_fn
+    LOOP_OVER_SHORTY_LOADING_REG a7, f19, call_fn
+
+    # copy arguments onto stack (t2 should be multiples of 4)
+    ble    $t2, $zero, call_fn   # t2 = number of argument bytes remain
+1:
+    lw     $t3, 0($t1)           # load from argument array
+    daddiu $t1, $t1, 4
+    sw     $t3, 0($v0)           # save to stack
+    daddiu $t2, -4
+    bgt    $t2, $zero, 1b        # t2 = number of argument bytes remain
+    daddiu $v0, $v0, 4
+
+call_fn:
+    # call method (a0 and a1 have been untouched)
+    lwu    $a1, 0($a1)           # make a1 = this ptr
+    sw     $a1, 4($sp)           # copy this ptr (skip 4 bytes for method*)
+    sw     $zero, 0($sp)         # store NULL for method* at bottom of frame
+    ld     $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64($a0)  # get pointer to the code
+    jalr   $t9                   # call the method
+    nop
+    move   $sp, $s8              # restore sp
+
+    # pop a4, a5, s1(rSELF), s8, ra off of the stack
+    ld     $a4, 0($sp)
+    .cfi_restore 8
+    ld     $a5, 8($sp)
+    .cfi_restore 9
+    ld     $s0, 16($sp)
+    .cfi_restore 16
+    ld     $s1, 24($sp)
+    .cfi_restore 17
+    ld     $s8, 32($sp)
+    .cfi_restore 30
+    ld     $ra, 40($sp)
+    .cfi_restore 31
+    daddiu $sp, $sp, 48
+    .cfi_adjust_cfa_offset -48
+
+    # a4 = JValue* result
+    # a5 = shorty string
+    lbu   $t1, 0($a5)           # get result type from shorty
+    li    $t2, 68               # put char 'D' into t2
+    beq   $t1, $t2, 1f          # branch if result type char == 'D'
+    li    $t3, 70               # put char 'F' into t3
+    beq   $t1, $t3, 1f          # branch if result type char == 'F'
+    sw    $v0, 0($a4)           # store the result
+    dsrl  $v1, $v0, 32
+    jalr  $zero, $ra
+    sw    $v1, 4($a4)           # store the other half of the result
+1:
+    mfc1  $v0, $f0
+    mfhc1 $v1, $f0
+    sw    $v0, 0($a4)           # store the result
+    jalr  $zero, $ra
+    sw    $v1, 4($a4)           # store the other half of the result
+END art_quick_invoke_stub
+
+    /*
+     * Invocation static stub for quick code.
+     * On entry:
+     *   a0 = method pointer
+     *   a1 = argument array that must at least contain the this ptr.
+     *   a2 = size of argument array in bytes
+     *   a3 = (managed) thread pointer
+     *   a4 = JValue* result
+     *   a5 = shorty
+     */
+ENTRY art_quick_invoke_static_stub
+
+    # push a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra, onto the stack
+    daddiu $sp, $sp, -48
+    .cfi_adjust_cfa_offset 48
+    sd     $ra, 40($sp)
+    .cfi_rel_offset 31, 40
+    sd     $s8, 32($sp)
+    .cfi_rel_offset 30, 32
+    sd     $s1, 24($sp)
+    .cfi_rel_offset 17, 24
+    sd     $s0, 16($sp)
+    .cfi_rel_offset 16, 16
+    sd     $a5, 8($sp)
+    .cfi_rel_offset 9, 8
+    sd     $a4, 0($sp)
+    .cfi_rel_offset 8, 0
+
+    daddiu $s0, $zero, SUSPEND_CHECK_INTERVAL   # reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+    move   $s1, $a3              # move managed thread pointer into s1 (rSELF)
+    move   $s8, $sp              # save sp in s8 (fp)
+
+    daddiu $t3, $a2, 20          # add 4 for method* and 16 for stack alignment
+    dsrl   $t3, $t3, 4           # shift the frame size right 4
+    dsll   $t3, $t3, 4           # shift the frame size left 4 to align to 16 bytes
+    dsubu  $sp, $sp, $t3         # reserve stack space for argument array
+
+    daddiu $t0, $a5, 1           # t0 = shorty[1] (skip 1 for return type)
+    move   $t1, $a1              # t1 = arg_array
+    move   $t2, $a2              # t2 = number of argument bytes remain
+    daddiu $v0, $sp, 4           # v0 points to where to copy arg_array
+    LOOP_OVER_SHORTY_LOADING_REG a1, f13, call_sfn
+    LOOP_OVER_SHORTY_LOADING_REG a2, f14, call_sfn
+    LOOP_OVER_SHORTY_LOADING_REG a3, f15, call_sfn
+    LOOP_OVER_SHORTY_LOADING_REG a4, f16, call_sfn
+    LOOP_OVER_SHORTY_LOADING_REG a5, f17, call_sfn
+    LOOP_OVER_SHORTY_LOADING_REG a6, f18, call_sfn
+    LOOP_OVER_SHORTY_LOADING_REG a7, f19, call_sfn
+
+    # copy arguments onto stack (t2 should be multiples of 4)
+    ble    $t2, $zero, call_sfn  # t2 = number of argument bytes remain
+1:
+    lw     $t3, 0($t1)           # load from argument array
+    daddiu $t1, $t1, 4
+    sw     $t3, 0($v0)           # save to stack
+    daddiu $t2, -4
+    bgt    $t2, $zero, 1b        # t2 = number of argument bytes remain
+    daddiu $v0, $v0, 4
+
+call_sfn:
+    # call method (a0 has been untouched)
+    sw     $zero, 0($sp)         # store NULL for method* at bottom of frame
+    ld     $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64($a0)  # get pointer to the code
+    jalr   $t9                   # call the method
+    nop
+    move   $sp, $s8              # restore sp
+
+    # pop a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra off of the stack
+    ld     $a4, 0($sp)
+    .cfi_restore 8
+    ld     $a5, 8($sp)
+    .cfi_restore 9
+    ld     $s0, 16($sp)
+    .cfi_restore 16
+    ld     $s1, 24($sp)
+    .cfi_restore 17
+    ld     $s8, 32($sp)
+    .cfi_restore 30
+    ld     $ra, 40($sp)
+    .cfi_restore 31
+    daddiu $sp, $sp, 48
+    .cfi_adjust_cfa_offset -48
+
+    # a4 = JValue* result
+    # a5 = shorty string
+    lbu   $t1, 0($a5)           # get result type from shorty
+    li    $t2, 68               # put char 'D' into t2
+    beq   $t1, $t2, 1f          # branch if result type char == 'D'
+    li    $t3, 70               # put char 'F' into t3
+    beq   $t1, $t3, 1f          # branch if result type char == 'F'
+    sw    $v0, 0($a4)           # store the result
+    dsrl  $v1, $v0, 32
+    jalr  $zero, $ra
+    sw    $v1, 4($a4)           # store the other half of the result
+1:
+    mfc1  $v0, $f0
+    mfhc1 $v1, $f0
+    sw    $v0, 0($a4)           # store the result
+    jalr  $zero, $ra
+    sw    $v1, 4($a4)           # store the other half of the result
+END art_quick_invoke_static_stub
+
+
+
+UNIMPLEMENTED art_quick_handle_fill_data
+UNIMPLEMENTED art_quick_lock_object
+UNIMPLEMENTED art_quick_unlock_object
+UNIMPLEMENTED art_quick_check_cast
+UNIMPLEMENTED art_quick_aput_obj_with_null_and_bound_check
+UNIMPLEMENTED art_quick_aput_obj_with_bound_check
+UNIMPLEMENTED art_quick_aput_obj
+UNIMPLEMENTED art_quick_initialize_static_storage
+UNIMPLEMENTED art_quick_initialize_type
+UNIMPLEMENTED art_quick_initialize_type_and_verify_access
+UNIMPLEMENTED art_quick_get_boolean_static
+UNIMPLEMENTED art_quick_get_byte_static
+UNIMPLEMENTED art_quick_get_char_static
+UNIMPLEMENTED art_quick_get_short_static
+UNIMPLEMENTED art_quick_get32_static
+UNIMPLEMENTED art_quick_get64_static
+UNIMPLEMENTED art_quick_get_obj_static
+UNIMPLEMENTED art_quick_get_boolean_instance
+UNIMPLEMENTED art_quick_get_byte_instance
+UNIMPLEMENTED art_quick_get_char_instance
+UNIMPLEMENTED art_quick_get_short_instance
+UNIMPLEMENTED art_quick_get32_instance
+UNIMPLEMENTED art_quick_get64_instance
+UNIMPLEMENTED art_quick_get_obj_instance
+UNIMPLEMENTED art_quick_set8_static
+UNIMPLEMENTED art_quick_set16_static
+UNIMPLEMENTED art_quick_set32_static
+UNIMPLEMENTED art_quick_set64_static
+UNIMPLEMENTED art_quick_set_obj_static
+UNIMPLEMENTED art_quick_set8_instance
+UNIMPLEMENTED art_quick_set16_instance
+UNIMPLEMENTED art_quick_set32_instance
+UNIMPLEMENTED art_quick_set64_instance
+UNIMPLEMENTED art_quick_set_obj_instance
+UNIMPLEMENTED art_quick_resolve_string
+
+// Macro to facilitate adding new allocation entrypoints.
+.macro TWO_ARG_DOWNCALL name, entrypoint, return
+ENTRY \name
+    break
+    break
+END \name
+.endm
+
+.macro THREE_ARG_DOWNCALL name, entrypoint, return
+ENTRY \name
+    break
+    break
+END \name
+.endm
+
+// Generate the allocation entrypoints for each allocator.
+GENERATE_ALL_ALLOC_ENTRYPOINTS
+
+UNIMPLEMENTED art_quick_test_suspend
+
+    /*
+     * Called by managed code that is attempting to call a method on a proxy class. On entry
+     * r0 holds the proxy method; r1, r2 and r3 may contain arguments.
+     */
+    .extern artQuickProxyInvokeHandler
+ENTRY art_quick_proxy_invoke_handler
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    sd      $a0, 0($sp)            # place proxy method at bottom of frame
+    move    $a2, rSELF             # pass Thread::Current
+    jal     artQuickProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)
+    move    $a3, $sp               # pass $sp
+    ld      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    daddiu  $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE  # skip a0-a7 and f12-f19
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    bne     $t0, $zero, 1f
+    dmtc1   $v0, $f0               # place return value to FP return value
+    jalr    $zero, $ra
+    dmtc1   $v1, $f1               # place return value to FP return value
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_proxy_invoke_handler
+
+UNIMPLEMENTED art_quick_imt_conflict_trampoline
+
+    .extern artQuickResolutionTrampoline
+ENTRY art_quick_resolution_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    move    $a2, rSELF             # pass Thread::Current
+    jal     artQuickResolutionTrampoline  # (Method* called, receiver, Thread*, SP)
+    move    $a3, $sp               # pass $sp
+    beq     $v0, $zero, 1f
+    lwu     $a0, 0($sp)            # load resolved method in $a0
+                                   # artQuickResolutionTrampoline puts resolved method in *SP
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    move    $t9, $v0               # code pointer must be in $t9 to generate the global pointer
+    jalr    $zero, $t9             # tail call to method
+    nop
+1:
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    DELIVER_PENDING_EXCEPTION
+END art_quick_resolution_trampoline
+
+    .extern artQuickGenericJniTrampoline
+    .extern artQuickGenericJniEndTrampoline
+ENTRY art_quick_generic_jni_trampoline
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
+    sd      $a0, 0($sp)            # store native ArtMethod* to bottom of stack
+    move    $s8, $sp               # save $sp
+
+    # prepare for call to artQuickGenericJniTrampoline(Thread*, SP)
+    move    $a0, rSELF             # pass Thread::Current
+    move    $a1, $sp               # pass $sp
+    jal     artQuickGenericJniTrampoline   # (Thread*, SP)
+    daddiu  $sp, $sp, -5120        # reserve space on the stack
+
+    # The C call will have registered the complete save-frame on success.
+    # The result of the call is:
+    # v0: ptr to native code, 0 on error.
+    # v1: ptr to the bottom of the used area of the alloca, can restore stack till here.
+    beq     $v0, $zero, 1f         # check entry error
+    move    $t9, $v0               # save the code ptr
+    move    $sp, $v1               # release part of the alloca
+
+    # Load parameters from stack into registers
+    ld      $a0,   0($sp)
+    ld      $a1,   8($sp)
+    ld      $a2,  16($sp)
+    ld      $a3,  24($sp)
+    ld      $a4,  32($sp)
+    ld      $a5,  40($sp)
+    ld      $a6,  48($sp)
+    ld      $a7,  56($sp)
+    # Load FPRs the same as GPRs. Look at BuildNativeCallFrameStateMachine.
+    l.d     $f12,  0($sp)
+    l.d     $f13,  8($sp)
+    l.d     $f14, 16($sp)
+    l.d     $f15, 24($sp)
+    l.d     $f16, 32($sp)
+    l.d     $f17, 40($sp)
+    l.d     $f18, 48($sp)
+    l.d     $f19, 56($sp)
+    jalr    $t9                    # native call
+    daddiu  $sp, $sp, 64
+
+    # result sign extension is handled in C code
+    # prepare for call to artQuickGenericJniEndTrampoline(Thread*, result, result_f)
+    move    $a0, rSELF             # pass Thread::Current
+    move    $a1, $v0
+    jal     artQuickGenericJniEndTrampoline
+    dmfc1   $a2, $f0
+
+    ld      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    bne     $t0, $zero, 2f         # check for pending exceptions
+    move    $sp, $s8               # tear down the alloca
+
+    # tear dpown the callee-save frame
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+
+    jalr    $zero, $ra
+    dmtc1   $v0, $f0               # place return value to FP return value
+
+1:
+    move    $sp, $s8               # tear down the alloca
+2:
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    DELIVER_PENDING_EXCEPTION
+END art_quick_generic_jni_trampoline
+
+    .extern artQuickToInterpreterBridge
+ENTRY art_quick_to_interpreter_bridge
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    move    $a1, rSELF             # pass Thread::Current
+    jal     artQuickToInterpreterBridge    # (Method* method, Thread*, SP)
+    move    $a2, $sp               # pass $sp
+    ld      $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+    daddiu  $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE  # skip a0-a7 and f12-f19
+    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+    bne     $t0, $zero, 1f
+    dmtc1   $v0, $f0               # place return value to FP return value
+    jalr    $zero, $ra
+    dmtc1   $v1, $f1               # place return value to FP return value
+1:
+    DELIVER_PENDING_EXCEPTION
+END art_quick_to_interpreter_bridge
+
+    /*
+     * Routine that intercepts method calls and returns.
+     */
+    .extern artInstrumentationMethodEntryFromCode
+    .extern artInstrumentationMethodExitFromCode
+ENTRY art_quick_instrumentation_entry
+    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    daddiu   $sp, $sp, -16     # space for saving arg0
+    .cfi_adjust_cfa_offset 16
+    sd       $a0, 0($sp)       # save arg0
+    move     $a3, $ra          # pass $ra
+    jal      artInstrumentationMethodEntryFromCode  # (Method*, Object*, Thread*, RA)
+    move     $a2, rSELF        # pass Thread::Current
+    move     $t9, $v0          # $t9 holds reference to code
+    ld       $a0, 0($sp)       # restore arg0
+    daddiu   $sp, $sp, 16      # remove args
+    .cfi_adjust_cfa_offset -16
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+    jalr     $t9               # call method
+    nop
+END art_quick_instrumentation_entry
+    /* intentional fallthrough */
+    .global art_quick_instrumentation_exit
+art_quick_instrumentation_exit:
+    .cfi_startproc
+    daddiu   $t9, $ra, 4       # put current address into $t9 to rebuild $gp
+    .cpload  $t9
+    move     $ra, $zero        # link register is to here, so clobber with 0 for later checks
+    SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+    move     $t0, $sp          # remember bottom of caller's frame
+    daddiu   $sp, $sp, -16     # save return values and set up args
+    .cfi_adjust_cfa_offset 16
+    sd       $v0, 0($sp)
+    .cfi_rel_offset 2, 0
+    s.d      $f0, 8($sp)
+    mov.d    $f15, $f0         # pass fpr result
+    move     $a2, $v0          # pass gpr result
+    move     $a1, $t0          # pass $sp
+    jal      artInstrumentationMethodExitFromCode  # (Thread*, SP, gpr_res, fpr_res)
+    move     $a0, rSELF        # pass Thread::Current
+    move     $t0, $v0          # set aside returned link register
+    move     $ra, $v1          # set link register for deoptimization
+    ld       $v0, 0($sp)       # restore return values
+    l.d      $f0, 8($sp)
+    jalr     $zero, $t0        # return
+    daddiu   $sp, $sp, 16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE  # 16 bytes of saved values + ref_only callee save frame
+    .cfi_adjust_cfa_offset -(16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
+END art_quick_instrumentation_exit
+
+UNIMPLEMENTED art_quick_deoptimize
+UNIMPLEMENTED art_quick_indexof
+UNIMPLEMENTED art_quick_string_compareto
diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/quick_method_frame_info_mips64.h
new file mode 100644
index 0000000..de55e81
--- /dev/null
+++ b/runtime/arch/mips64/quick_method_frame_info_mips64.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
+
+#include "quick/quick_method_frame_info.h"
+#include "registers_mips64.h"
+#include "runtime.h"  // for Runtime::CalleeSaveType.
+
+namespace art {
+namespace mips64 {
+
+static constexpr uint32_t kMips64CalleeSaveRefSpills =
+    (1 << art::mips64::S2) | (1 << art::mips64::S3) | (1 << art::mips64::S4) |
+    (1 << art::mips64::S5) | (1 << art::mips64::S6) | (1 << art::mips64::S7) |
+    (1 << art::mips64::GP) | (1 << art::mips64::S8);
+static constexpr uint32_t kMips64CalleeSaveArgSpills =
+    (1 << art::mips64::A1) | (1 << art::mips64::A2) | (1 << art::mips64::A3) |
+    (1 << art::mips64::A4) | (1 << art::mips64::A5) | (1 << art::mips64::A6) |
+    (1 << art::mips64::A7);
+static constexpr uint32_t kMips64CalleeSaveAllSpills =
+    (1 << art::mips64::S0) | (1 << art::mips64::S1);
+
+static constexpr uint32_t kMips64CalleeSaveFpRefSpills = 0;
+static constexpr uint32_t kMips64CalleeSaveFpArgSpills =
+    (1 << art::mips64::F12) | (1 << art::mips64::F13) | (1 << art::mips64::F14) |
+    (1 << art::mips64::F15) | (1 << art::mips64::F16) | (1 << art::mips64::F17) |
+    (1 << art::mips64::F18) | (1 << art::mips64::F19);
+// F12 should not be necessary to spill, as A0 is always in use.
+static constexpr uint32_t kMips64CalleeSaveFpAllSpills =
+    (1 << art::mips64::F24) | (1 << art::mips64::F25) | (1 << art::mips64::F26) |
+    (1 << art::mips64::F27) | (1 << art::mips64::F28) | (1 << art::mips64::F29) |
+    (1 << art::mips64::F30) | (1 << art::mips64::F31);
+
+constexpr uint32_t Mips64CalleeSaveCoreSpills(Runtime::CalleeSaveType type) {
+  return kMips64CalleeSaveRefSpills |
+      (type == Runtime::kRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) |
+      (type == Runtime::kSaveAll ? kMips64CalleeSaveAllSpills : 0) | (1 << art::mips64::RA);
+}
+
+constexpr uint32_t Mips64CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
+  return kMips64CalleeSaveFpRefSpills |
+      (type == Runtime::kRefsAndArgs ? kMips64CalleeSaveFpArgSpills: 0) |
+      (type == Runtime::kSaveAll ? kMips64CalleeSaveFpAllSpills : 0);
+}
+
+constexpr uint32_t Mips64CalleeSaveFrameSize(Runtime::CalleeSaveType type) {
+  return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ +
+                  POPCOUNT(Mips64CalleeSaveFpSpills(type))   /* fprs */ +
+                  + 1 /* Method* */) * kMips64PointerSize, kStackAlignment);
+}
+
+constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(Runtime::CalleeSaveType type) {
+  return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type),
+                              Mips64CalleeSaveCoreSpills(type),
+                              Mips64CalleeSaveFpSpills(type));
+}
+
+}  // namespace mips64
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_QUICK_METHOD_FRAME_INFO_MIPS64_H_
diff --git a/runtime/arch/mips64/registers_mips64.cc b/runtime/arch/mips64/registers_mips64.cc
new file mode 100644
index 0000000..4959208
--- /dev/null
+++ b/runtime/arch/mips64/registers_mips64.cc
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "registers_mips64.h"
+
+#include <ostream>
+
+namespace art {
+namespace mips64 {
+
+static const char* kRegisterNames[] = {
+  "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+  "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+  "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+  "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
+};
+
+std::ostream& operator<<(std::ostream& os, const GpuRegister& rhs) {
+  if (rhs >= ZERO && rhs < kNumberOfGpuRegisters) {
+    os << kRegisterNames[rhs];
+  } else {
+    os << "GpuRegister[" << static_cast<int>(rhs) << "]";
+  }
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const FpuRegister& rhs) {
+  if (rhs >= F0 && rhs < kNumberOfFpuRegisters) {
+    os << "f" << static_cast<int>(rhs);
+  } else {
+    os << "FpuRegister[" << static_cast<int>(rhs) << "]";
+  }
+  return os;
+}
+
+}  // namespace mips64
+}  // namespace art
diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h
new file mode 100644
index 0000000..38bc8f2
--- /dev/null
+++ b/runtime/arch/mips64/registers_mips64.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_MIPS64_REGISTERS_MIPS64_H_
+#define ART_RUNTIME_ARCH_MIPS64_REGISTERS_MIPS64_H_
+
+#include <iosfwd>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "globals.h"
+
+namespace art {
+namespace mips64 {
+
+enum GpuRegister {
+  ZERO =  0,
+  AT   =  1,  // Assembler temporary.
+  V0   =  2,  // Values.
+  V1   =  3,
+  A0   =  4,  // Arguments.
+  A1   =  5,
+  A2   =  6,
+  A3   =  7,
+  A4   =  8,
+  A5   =  9,
+  A6   = 10,
+  A7   = 11,
+  T0   = 12,  // Temporaries.
+  T1   = 13,
+  T2   = 14,
+  T3   = 15,
+  S0   = 16,  // Saved values.
+  S1   = 17,
+  S2   = 18,
+  S3   = 19,
+  S4   = 20,
+  S5   = 21,
+  S6   = 22,
+  S7   = 23,
+  T8   = 24,  // More temporaries.
+  T9   = 25,
+  K0   = 26,  // Reserved for trap handler.
+  K1   = 27,
+  GP   = 28,  // Global pointer.
+  SP   = 29,  // Stack pointer.
+  S8   = 30,  // Saved value/frame pointer.
+  RA   = 31,  // Return address.
+  kNumberOfGpuRegisters = 32,
+  kNoGpuRegister = -1  // Signals an illegal register.
+};
+std::ostream& operator<<(std::ostream& os, const GpuRegister& rhs);
+
+// Values for floating point registers.
+enum FpuRegister {
+  F0  =  0,
+  F1  =  1,
+  F2  =  2,
+  F3  =  3,
+  F4  =  4,
+  F5  =  5,
+  F6  =  6,
+  F7  =  7,
+  F8  =  8,
+  F9  =  9,
+  F10 = 10,
+  F11 = 11,
+  F12 = 12,
+  F13 = 13,
+  F14 = 14,
+  F15 = 15,
+  F16 = 16,
+  F17 = 17,
+  F18 = 18,
+  F19 = 19,
+  F20 = 20,
+  F21 = 21,
+  F22 = 22,
+  F23 = 23,
+  F24 = 24,
+  F25 = 25,
+  F26 = 26,
+  F27 = 27,
+  F28 = 28,
+  F29 = 29,
+  F30 = 30,
+  F31 = 31,
+  kNumberOfFpuRegisters = 32,
+  kNoFpuRegister = -1,
+};
+std::ostream& operator<<(std::ostream& os, const FpuRegister& rhs);
+
+}  // namespace mips64
+}  // namespace art
+
+#endif  // ART_RUNTIME_ARCH_MIPS64_REGISTERS_MIPS64_H_
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/runtime/arch/mips64/thread_mips64.cc
similarity index 60%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to runtime/arch/mips64/thread_mips64.cc
index 9e2c030..c55537c 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/runtime/arch/mips64/thread_mips64.cc
@@ -14,17 +14,21 @@
  * limitations under the License.
  */
 
-#include "asm_support_arm64.S"
+#include "thread.h"
 
-    /*
-     * Portable invocation stub.
-     */
-UNIMPLEMENTED art_portable_invoke_stub
+#include "asm_support_mips64.h"
+#include "base/logging.h"
 
-UNIMPLEMENTED art_portable_proxy_invoke_handler
+namespace art {
 
-UNIMPLEMENTED art_portable_resolution_trampoline
+void Thread::InitCpu() {
+  CHECK_EQ(THREAD_FLAGS_OFFSET, ThreadFlagsOffset<8>().Int32Value());
+  CHECK_EQ(THREAD_CARD_TABLE_OFFSET, CardTableOffset<8>().Int32Value());
+  CHECK_EQ(THREAD_EXCEPTION_OFFSET, ExceptionOffset<8>().Int32Value());
+}
 
-UNIMPLEMENTED art_portable_to_interpreter_bridge
+void Thread::CleanupCpu() {
+  // Do nothing.
+}
 
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+}  // namespace art
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index 632c5f3..037c26e 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -16,25 +16,25 @@
 
 .macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix
 // Called by managed code to allocate an object.
-TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object\c_suffix, artAllocObjectFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an object of a resolved class.
-TWO_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an object of an initialized class.
-TWO_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an object when the caller doesn't know whether it has access
 // to the created type.
-TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check\c_suffix, artAllocObjectFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array.
-THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array of a resolve class.
-THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array when the caller doesn't know whether it has access
 // to the created type.
-THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 // Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
 .endm
 
 .macro GENERATE_ALL_ALLOC_ENTRYPOINTS
@@ -46,4 +46,8 @@
 GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented
 GENERATE_ALLOC_ENTRYPOINTS _tlab, TLAB
 GENERATE_ALLOC_ENTRYPOINTS _tlab_instrumented, TLABInstrumented
+GENERATE_ALLOC_ENTRYPOINTS _region, Region
+GENERATE_ALLOC_ENTRYPOINTS _region_instrumented, RegionInstrumented
+GENERATE_ALLOC_ENTRYPOINTS _region_tlab, RegionTLAB
+GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented
 .endm
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 0fcd297..6acc2a7 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -272,9 +272,9 @@
         ".cfi_adjust_cfa_offset -16\n\t"
         : "=a" (result)
           // Use the result from rax
-        : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "m"(referrer)
+        : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "c"(referrer)
           // This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into rax
-        : "rbx", "rcx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+        : "rbx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
           "memory");  // clobber all
     // TODO: Should we clobber the other registers?
 #else
@@ -302,7 +302,7 @@
 #if defined(__i386__)
     // TODO: Set the thread?
     __asm__ __volatile__(
-        "movd %[hidden], %%xmm0\n\t"
+        "movd %[hidden], %%xmm7\n\t"
         "subl $12, %%esp\n\t"       // Align stack.
         "pushl %[referrer]\n\t"     // Store referrer
         "call *%%edi\n\t"           // Call the stub
@@ -799,6 +799,9 @@
 }
 
 TEST_F(StubTest, UnlockObject) {
+  // This will lead to monitor error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   TestUnlockObject(this);
 }
 
@@ -992,6 +995,9 @@
   TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
 
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+  // This will lead to OOM  error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   // TODO: Check the "Unresolved" allocation stubs
 
   Thread* self = Thread::Current();
@@ -1116,6 +1122,9 @@
 #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
   // TODO: Check the "Unresolved" allocation stubs
 
+  // This will lead to OOM  error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   Thread* self = Thread::Current();
   // Create an object
   ScopedObjectAccess soa(self);
@@ -1139,8 +1148,8 @@
   if ((false)) {
     // Use an arbitrary method from c to use as referrer
     size_t result = Invoke3(static_cast<size_t>(c->GetDexTypeIndex()),    // type_idx
-                            reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)),  // arbitrary
                             10U,
+                            reinterpret_cast<size_t>(c_obj->GetVirtualMethod(0)),  // arbitrary
                             StubTest::GetEntrypoint(self, kQuickAllocArray),
                             self);
 
@@ -1155,7 +1164,8 @@
   {
     // We can use nullptr in the second argument as we do not need a method here (not used in
     // resolved/initialized cases)
-    size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr), 10U,
+    size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 10U,
+                            reinterpret_cast<size_t>(nullptr),
                             StubTest::GetEntrypoint(self, kQuickAllocArrayResolved),
                             self);
     EXPECT_FALSE(self->IsExceptionPending()) << PrettyTypeOf(self->GetException(nullptr));
@@ -1173,8 +1183,9 @@
 
   // Out-of-memory.
   {
-    size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(nullptr),
+    size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()),
                             GB,  // that should fail...
+                            reinterpret_cast<size_t>(nullptr),
                             StubTest::GetEntrypoint(self, kQuickAllocArrayResolved),
                             self);
 
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index fea16da..122428b 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -25,6 +25,8 @@
     #define MACRO1(macro_name, macro_arg1) .macro macro_name
     #define MACRO2(macro_name, macro_arg1, macro_args2) .macro macro_name
     #define MACRO3(macro_name, macro_arg1, macro_args2, macro_args3) .macro macro_name
+    #define MACRO4(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4) .macro macro_name
+    #define MACRO5(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4, macro_arg5) .macro macro_name
     #define END_MACRO .endmacro
 
     // Clang's as(1) uses $0, $1, and so on for macro arguments.
@@ -43,6 +45,8 @@
     #define MACRO1(macro_name, macro_arg1) .macro macro_name macro_arg1
     #define MACRO2(macro_name, macro_arg1, macro_arg2) .macro macro_name macro_arg1, macro_arg2
     #define MACRO3(macro_name, macro_arg1, macro_arg2, macro_arg3) .macro macro_name macro_arg1, macro_arg2, macro_arg3
+    #define MACRO4(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4) .macro macro_name macro_arg1, macro_arg2, macro_arg3, macro_arg4
+    #define MACRO5(macro_name, macro_arg1, macro_arg2, macro_arg3, macro_arg4, macro_arg5) .macro macro_name macro_arg1, macro_arg2, macro_arg3, macro_arg4, macro_arg5
     #define END_MACRO .endm
 
     // Regular gas(1) uses \argument_name for macro arguments.
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 5a88f80..b0a6017 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -21,6 +21,8 @@
 
 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 32
 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
-#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 32
+
+// 32 bytes for GPRs and 32 bytes for FPRs.
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE (32 + 32)
 
 #endif  // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc
index 49aa326..4ea4684 100644
--- a/runtime/arch/x86/context_x86.cc
+++ b/runtime/arch/x86/context_x86.cc
@@ -30,6 +30,9 @@
   for (size_t  i = 0; i < kNumberOfCpuRegisters; i++) {
     gprs_[i] = nullptr;
   }
+  for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) {
+    fprs_[i] = nullptr;
+  }
   gprs_[ESP] = &esp_;
   // Initialize registers with easy to spot debug values.
   esp_ = X86Context::kBadGprBase + ESP;
@@ -40,7 +43,7 @@
   mirror::ArtMethod* method = fr.GetMethod();
   const QuickMethodFrameInfo frame_info = method->GetQuickFrameInfo();
   size_t spill_count = POPCOUNT(frame_info.CoreSpillMask());
-  DCHECK_EQ(frame_info.FpSpillMask(), 0u);
+  size_t fp_spill_count = POPCOUNT(frame_info.FpSpillMask());
   if (spill_count > 0) {
     // Lowest number spill is farthest away, walk registers and fill into context.
     int j = 2;  // Offset j to skip return address spill.
@@ -51,6 +54,24 @@
       }
     }
   }
+  if (fp_spill_count > 0) {
+    // Lowest number spill is farthest away, walk registers and fill into context.
+    size_t j = 2;  // Offset j to skip return address spill.
+    size_t fp_spill_size_in_words = fp_spill_count * 2;
+    for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) {
+      if (((frame_info.FpSpillMask() >> i) & 1) != 0) {
+        // There are 2 pieces to each XMM register, to match VR size.
+        fprs_[2*i] = reinterpret_cast<uint32_t*>(
+            fr.CalleeSaveAddress(spill_count + fp_spill_size_in_words - j,
+                                 frame_info.FrameSizeInBytes()));
+        fprs_[2*i+1] = reinterpret_cast<uint32_t*>(
+            fr.CalleeSaveAddress(spill_count + fp_spill_size_in_words - j - 1,
+                                 frame_info.FrameSizeInBytes()));
+        // Two void* per XMM register.
+        j += 2;
+      }
+    }
+  }
 }
 
 void X86Context::SmashCallerSaves() {
@@ -59,27 +80,21 @@
   gprs_[EDX] = const_cast<uintptr_t*>(&gZero);
   gprs_[ECX] = nullptr;
   gprs_[EBX] = nullptr;
+  memset(&fprs_[0], '\0', sizeof(fprs_));
 }
 
-bool X86Context::SetGPR(uint32_t reg, uintptr_t value) {
+void X86Context::SetGPR(uint32_t reg, uintptr_t value) {
   CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+  DCHECK(IsAccessibleGPR(reg));
   CHECK_NE(gprs_[reg], &gZero);
-  if (gprs_[reg] != nullptr) {
-    *gprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *gprs_[reg] = value;
 }
 
-bool X86Context::GetFPR(uint32_t reg ATTRIBUTE_UNUSED, uintptr_t* val ATTRIBUTE_UNUSED) {
-  LOG(FATAL) << "Floating-point registers are all caller save in X86";
-  UNREACHABLE();
-}
-
-bool X86Context::SetFPR(uint32_t reg ATTRIBUTE_UNUSED, uintptr_t value ATTRIBUTE_UNUSED) {
-  LOG(FATAL) << "Floating-point registers are all caller save in X86";
-  UNREACHABLE();
+void X86Context::SetFPR(uint32_t reg, uintptr_t value) {
+  CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+  DCHECK(IsAccessibleFPR(reg));
+  CHECK_NE(fprs_[reg], reinterpret_cast<const uint32_t*>(&gZero));
+  *fprs_[reg] = value;
 }
 
 void X86Context::DoLongJump() {
@@ -90,17 +105,30 @@
   for (size_t i = 0; i < kNumberOfCpuRegisters; ++i) {
     gprs[kNumberOfCpuRegisters - i - 1] = gprs_[i] != nullptr ? *gprs_[i] : X86Context::kBadGprBase + i;
   }
+  uint32_t fprs[kNumberOfFloatRegisters];
+  for (size_t i = 0; i < kNumberOfFloatRegisters; ++i) {
+    fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : X86Context::kBadFprBase + i;
+  }
   // We want to load the stack pointer one slot below so that the ret will pop eip.
   uintptr_t esp = gprs[kNumberOfCpuRegisters - ESP - 1] - sizeof(intptr_t);
   gprs[kNumberOfCpuRegisters] = esp;
   *(reinterpret_cast<uintptr_t*>(esp)) = eip_;
   __asm__ __volatile__(
+      "movl %1, %%ebx\n\t"          // Address base of FPRs.
+      "movsd 0(%%ebx), %%xmm0\n\t"  // Load up XMM0-XMM7.
+      "movsd 8(%%ebx), %%xmm1\n\t"
+      "movsd 16(%%ebx), %%xmm2\n\t"
+      "movsd 24(%%ebx), %%xmm3\n\t"
+      "movsd 32(%%ebx), %%xmm4\n\t"
+      "movsd 40(%%ebx), %%xmm5\n\t"
+      "movsd 48(%%ebx), %%xmm6\n\t"
+      "movsd 56(%%ebx), %%xmm7\n\t"
       "movl %0, %%esp\n\t"  // ESP points to gprs.
       "popal\n\t"           // Load all registers except ESP and EIP with values in gprs.
       "popl %%esp\n\t"      // Load stack pointer.
       "ret\n\t"             // From higher in the stack pop eip.
       :  // output.
-      : "g"(&gprs[0])  // input.
+      : "g"(&gprs[0]), "g"(&fprs[0]) // input.
       :);  // clobber.
 #else
   UNIMPLEMENTED(FATAL);
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index 01c8b82..c66a9dc 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -36,43 +36,64 @@
   void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSP(uintptr_t new_sp) OVERRIDE {
-    bool success = SetGPR(ESP, new_sp);
-    CHECK(success) << "Failed to set ESP register";
+    SetGPR(ESP, new_sp);
   }
 
   void SetPC(uintptr_t new_pc) OVERRIDE {
     eip_ = new_pc;
   }
 
+  bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+    return gprs_[reg] != nullptr;
+  }
+
   uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
     return gprs_[reg];
   }
 
-  bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  uintptr_t GetGPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
-    if (gprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *gprs_[reg];
-      return true;
-    }
+    DCHECK(IsAccessibleGPR(reg));
+    return *gprs_[reg];
   }
 
-  bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
-  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE;
+  bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+    return fprs_[reg] != nullptr;
+  }
 
-  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+    DCHECK(IsAccessibleFPR(reg));
+    return *fprs_[reg];
+  }
+
+  void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
   void SmashCallerSaves() OVERRIDE;
   void DoLongJump() OVERRIDE;
 
  private:
-  // Pointers to register locations, floating point registers are all caller save. Values are
-  // initialized to NULL or the special registers below.
+  // Pretend XMM registers are made of uin32_t pieces, because they are manipulated
+  // in uint32_t chunks.
+  enum {
+    XMM0_0 = 0, XMM0_1,
+    XMM1_0, XMM1_1,
+    XMM2_0, XMM2_1,
+    XMM3_0, XMM3_1,
+    XMM4_0, XMM4_1,
+    XMM5_0, XMM5_1,
+    XMM6_0, XMM6_1,
+    XMM7_0, XMM7_1,
+    kNumberOfFloatRegisters};
+
+  // Pointers to register locations. Values are initialized to NULL or the special registers below.
   uintptr_t* gprs_[kNumberOfCpuRegisters];
+  uint32_t* fprs_[kNumberOfFloatRegisters];
   // Hold values for esp and eip if they are not located within a stack frame. EIP is somewhat
   // special in that it cannot be encoded normally as a register operand to an instruction (except
   // in 64bit addressing modes).
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 48d6c80..7cdd2fc 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -16,7 +16,6 @@
 
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -30,7 +29,7 @@
                                             const mirror::Class* ref_class);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
-                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+                     QuickEntryPoints* qpoints) {
   // Interpreter
   ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge;
   ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge;
@@ -38,10 +37,6 @@
   // JNI
   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
 
-  // Portable
-  ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
-  ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
   // Alloc
   ResetQuickAllocEntryPoints(qpoints);
 
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
deleted file mode 100644
index 1f0900e..0000000
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ /dev/null
@@ -1,131 +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 "asm_support_x86.S"
-
-    /*
-     * Portable invocation stub.
-     * On entry:
-     *   [sp] = return address
-     *   [sp + 4] = method pointer
-     *   [sp + 8] = argument array or NULL for no argument methods
-     *   [sp + 12] = size of argument array in bytes
-     *   [sp + 16] = (managed) thread pointer
-     *   [sp + 20] = JValue* result
-     *   [sp + 24] = result type char
-     */
-DEFINE_FUNCTION art_portable_invoke_stub
-    PUSH ebp                      // save ebp
-    PUSH ebx                      // save ebx
-    mov %esp, %ebp                // copy value of stack pointer into base pointer
-    CFI_DEF_CFA_REGISTER(ebp)
-    mov 20(%ebp), %ebx            // get arg array size
-    addl LITERAL(28), %ebx        // reserve space for return addr, method*, ebx, and ebp in frame
-    andl LITERAL(0xFFFFFFF0), %ebx    // align frame size to 16 bytes
-    subl LITERAL(12), %ebx        // remove space for return address, ebx, and ebp
-    subl %ebx, %esp               // reserve stack space for argument array
-    SETUP_GOT_NOSAVE ebx          // reset ebx to GOT table
-    lea  4(%esp), %eax            // use stack pointer + method ptr as dest for memcpy
-    pushl 20(%ebp)                // push size of region to memcpy
-    pushl 16(%ebp)                // push arg array as source of memcpy
-    pushl %eax                    // push stack pointer as destination of memcpy
-    call PLT_SYMBOL(memcpy)       // (void*, const void*, size_t)
-    addl LITERAL(12), %esp        // pop arguments to memcpy
-    mov 12(%ebp), %eax            // move method pointer into eax
-    mov %eax, (%esp)              // push method pointer onto stack
-    call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32(%eax) // call the method
-    mov %ebp, %esp                // restore stack pointer
-    POP ebx                       // pop ebx
-    POP ebp                       // pop ebp
-    mov 20(%esp), %ecx            // get result pointer
-    cmpl LITERAL(68), 24(%esp)    // test if result type char == 'D'
-    je .Lreturn_double_portable
-    cmpl LITERAL(70), 24(%esp)    // test if result type char == 'F'
-    je .Lreturn_float_portable
-    mov %eax, (%ecx)              // store the result
-    mov %edx, 4(%ecx)             // store the other half of the result
-    ret
-.Lreturn_double_portable:
-    fstpl (%ecx)                  // store the floating point result as double
-    ret
-.Lreturn_float_portable:
-    fstps (%ecx)                  // store the floating point result as float
-    ret
-END_FUNCTION art_portable_invoke_stub
-
-DEFINE_FUNCTION art_portable_proxy_invoke_handler
-    PUSH ebp                        // Set up frame.
-    movl %esp, %ebp
-    CFI_DEF_CFA_REGISTER(%ebp)
-    subl LITERAL(8), %esp           // Align stack
-    leal 8(%ebp), %edx              // %edx = ArtMethod** called_addr
-    movl 12(%ebp), %ecx             // %ecx = receiver
-    movl 0(%edx), %eax              // %eax = ArtMethod* called
-    pushl %edx                      // Pass called_addr.
-    pushl %fs:THREAD_SELF_OFFSET    // Pass thread.
-    pushl %ecx                      // Pass receiver.
-    pushl %eax                      // Pass called.
-    call SYMBOL(artPortableProxyInvokeHandler)  // (called, receiver, Thread*, &called)
-    leave
-    CFI_RESTORE(%ebp)
-    CFI_DEF_CFA(%esp, 4)
-    movd %eax, %xmm0              // Place return value also into floating point return value.
-    movd %edx, %xmm1
-    punpckldq %xmm1, %xmm0
-    ret
-END_FUNCTION art_portable_proxy_invoke_handler
-
-DEFINE_FUNCTION art_portable_resolution_trampoline
-  PUSH ebp                        // Set up frame.
-  movl %esp, %ebp
-  CFI_DEF_CFA_REGISTER(%ebp)
-  subl LITERAL(8), %esp           // Align stack
-  leal 8(%ebp), %edx              // %edx = ArtMethod** called_addr
-  movl 12(%ebp), %ecx             // %ecx = receiver
-  movl 0(%edx), %eax              // %eax = ArtMethod* called
-  pushl %edx                      // Pass called_addr.
-  pushl %fs:THREAD_SELF_OFFSET    // Pass thread.
-  pushl %ecx                      // Pass receiver.
-  pushl %eax                      // Pass called.
-  call SYMBOL(artPortableResolutionTrampoline)  // (called, receiver, Thread*, &called)
-  leave
-  CFI_RESTORE(%ebp)
-  CFI_DEF_CFA(%esp, 4)
-  testl %eax, %eax
-  jz  .Lresolve_fail
-  jmp * %eax
-.Lresolve_fail:                   // Resolution failed, return with exception pending.
-  ret
-END_FUNCTION art_portable_resolution_trampoline
-
-DEFINE_FUNCTION art_portable_to_interpreter_bridge
-  PUSH ebp                        // Set up frame.
-  movl %esp, %ebp
-  CFI_DEF_CFA_REGISTER(%ebp)
-  subl LITERAL(12), %esp           // Align stack
-  leal 8(%ebp), %edx              // %edx = ArtMethod** called_addr
-  movl 0(%edx), %eax              // %eax = ArtMethod* called
-  pushl %edx                      // Pass called_addr.
-  pushl %fs:THREAD_SELF_OFFSET    // Pass thread.
-  pushl %eax                      // Pass called.
-  call SYMBOL(artPortableToInterpreterBridge)  // (called, Thread*, &called)
-  leave
-  CFI_RESTORE(%ebp)
-  CFI_DEF_CFA(%esp, 4)
-  ret
-END_FUNCTION art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 1ce01c4..beacd49 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -90,6 +90,15 @@
     PUSH ebx  // Save args
     PUSH edx
     PUSH ecx
+    // Create space for FPR args.
+    subl MACRO_LITERAL(4 * 8), %esp
+    CFI_ADJUST_CFA_OFFSET(4 * 8)
+    // Save FPRs.
+    movsd %xmm0, 0(%esp)
+    movsd %xmm1, 8(%esp)
+    movsd %xmm2, 16(%esp)
+    movsd %xmm3, 24(%esp)
+
     SETUP_GOT_NOSAVE VAR(got_reg, 0)
     // Load Runtime::instance_ from GOT.
     movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
@@ -102,7 +111,7 @@
 
     // Ugly compile-time check, but we only have the preprocessor.
     // Last +4: implicit return address pushed on stack when caller made call.
-#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 7*4 + 4)
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 7*4 + 4*8 + 4)
 #error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86) size not as expected."
 #endif
 END_MACRO
@@ -112,20 +121,39 @@
      * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) where the method is passed in EAX.
      */
 MACRO0(SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_EAX)
+    // Save callee and GPR args, mixed together to agree with core spills bitmap.
     PUSH edi  // Save callee saves
     PUSH esi
     PUSH ebp
     PUSH ebx  // Save args
     PUSH edx
     PUSH ecx
+
+    // Create space for FPR args.
+    subl MACRO_LITERAL(32), %esp
+    CFI_ADJUST_CFA_OFFSET(32)
+
+    // Save FPRs.
+    movsd %xmm0, 0(%esp)
+    movsd %xmm1, 8(%esp)
+    movsd %xmm2, 16(%esp)
+    movsd %xmm3, 24(%esp)
+
     PUSH eax  // Store the ArtMethod reference at the bottom of the stack.
     // Store esp as the stop quick frame.
     movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
 END_MACRO
 
 MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME)
-    addl MACRO_LITERAL(4), %esp  // Remove padding
-    CFI_ADJUST_CFA_OFFSET(-4)
+    // Restore FPRs. EAX is still on the stack.
+    movsd 4(%esp), %xmm0
+    movsd 12(%esp), %xmm1
+    movsd 20(%esp), %xmm2
+    movsd 28(%esp), %xmm3
+
+    addl MACRO_LITERAL(36), %esp  // Remove FPRs and EAX.
+    CFI_ADJUST_CFA_OFFSET(-36)
+
     POP ecx  // Restore args except eax
     POP edx
     POP ebx
@@ -134,6 +162,30 @@
     POP edi
 END_MACRO
 
+// Restore register and jump to routine
+// Inputs:  EDI contains pointer to code.
+// Notes: Need to pop EAX too (restores Method*)
+MACRO0(RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME_AND_JUMP)
+    POP eax  // Restore Method*
+
+    // Restore FPRs.
+    movsd 0(%esp), %xmm0
+    movsd 8(%esp), %xmm1
+    movsd 16(%esp), %xmm2
+    movsd 24(%esp), %xmm3
+
+    addl MACRO_LITERAL(32), %esp  // Remove FPRs.
+    CFI_ADJUST_CFA_OFFSET(-32)
+
+    POP ecx  // Restore args except eax
+    POP edx
+    POP ebx
+    POP ebp  // Restore callee saves
+    POP esi
+    xchgl 0(%esp),%edi // restore EDI and place code pointer as only value on stack
+    ret
+END_MACRO
+
     /*
      * Macro that set calls through to artDeliverPendingExceptionFromCode, where the pending
      * exception is Thread::Current()->exception_.
@@ -243,13 +295,14 @@
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
     SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME ebx, ebx
     movl %esp, %edx  // remember SP
+
     // Outgoing argument set up
     subl MACRO_LITERAL(12), %esp  // alignment padding
     CFI_ADJUST_CFA_OFFSET(12)
     PUSH edx                      // pass SP
     pushl %fs:THREAD_SELF_OFFSET  // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    pushl 32(%edx)                // pass caller Method*
+    pushl 32+32(%edx)             // pass caller Method*
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH ecx                      // pass arg2
     PUSH eax                      // pass arg1
@@ -257,6 +310,17 @@
     movl %edx, %edi               // save code pointer in EDI
     addl MACRO_LITERAL(36), %esp  // Pop arguments skip eax
     CFI_ADJUST_CFA_OFFSET(-36)
+
+    // Restore FPRs.
+    movsd 0(%esp), %xmm0
+    movsd 8(%esp), %xmm1
+    movsd 16(%esp), %xmm2
+    movsd 24(%esp), %xmm3
+
+    // Remove space for FPR args.
+    addl MACRO_LITERAL(4 * 8), %esp
+    CFI_ADJUST_CFA_OFFSET(-4 * 8)
+
     POP ecx  // Restore args except eax
     POP edx
     POP ebx
@@ -284,7 +348,63 @@
 INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
 
     /*
-     * Quick invocation stub.
+     * Helper for quick invocation stub to set up XMM registers.
+     * Increments shorty and arg_array and clobbers temp_char.
+     * Branches to finished if it encounters the end of the shorty.
+     */
+MACRO5(LOOP_OVER_SHORTY_LOADING_XMMS, xmm_reg, shorty, arg_array, temp_char, finished)
+1: // LOOP
+    movb (REG_VAR(shorty, 1)), REG_VAR(temp_char, 3)  // temp_char := *shorty
+    addl MACRO_LITERAL(1), REG_VAR(shorty, 1)         // shorty++
+    cmpb MACRO_LITERAL(0), REG_VAR(temp_char, 3)      // if (temp_char == '\0')
+    je RAW_VAR(finished, 4)                           //   goto finished
+    cmpb MACRO_LITERAL(68), REG_VAR(temp_char, 3)     // if (temp_char == 'D')
+    je 2f                                             //   goto FOUND_DOUBLE
+    cmpb MACRO_LITERAL(70), REG_VAR(temp_char, 3)     // if (temp_char == 'F')
+    je 3f                                             //   goto FOUND_FLOAT
+    addl MACRO_LITERAL(4), REG_VAR(arg_array, 2)      // arg_array++
+    //  Handle extra space in arg array taken by a long.
+    cmpb MACRO_LITERAL(74), REG_VAR(temp_char, 3)     // if (temp_char != 'J')
+    jne 1b                                            //   goto LOOP
+    addl MACRO_LITERAL(4), REG_VAR(arg_array, 2)      // arg_array++
+    jmp 1b                                            // goto LOOP
+2:  // FOUND_DOUBLE
+    movsd (REG_VAR(arg_array, 2)), REG_VAR(xmm_reg, 0)
+    addl MACRO_LITERAL(8), REG_VAR(arg_array, 2)      // arg_array+=2
+    jmp 4f
+3:  // FOUND_FLOAT
+    movss (REG_VAR(arg_array, 2)), REG_VAR(xmm_reg, 0)
+    addl MACRO_LITERAL(4), REG_VAR(arg_array, 2)      // arg_array++
+4:
+END_MACRO
+
+    /*
+     * Helper for quick invocation stub to set up GPR registers.
+     * Increments shorty and arg_array, and returns the current short character in
+     * temp_char. Branches to finished if it encounters the end of the shorty.
+     */
+MACRO4(SKIP_OVER_FLOATS, shorty, arg_array, temp_char, finished)
+1: // LOOP:
+    movb (REG_VAR(shorty, 0)), REG_VAR(temp_char, 2)  // temp_char := *shorty
+    addl MACRO_LITERAL(1), REG_VAR(shorty, 0)         // shorty++
+    cmpb MACRO_LITERAL(0), REG_VAR(temp_char, 2)      // if (temp_char == '\0')
+    je RAW_VAR(finished, 3)                           //   goto finished
+    cmpb MACRO_LITERAL(70), REG_VAR(temp_char, 2)     // if (temp_char == 'F')
+    je 3f                                             //   goto SKIP_FLOAT
+    cmpb MACRO_LITERAL(68), REG_VAR(temp_char, 2)     // if (temp_char == 'D')
+    je 4f                                             //   goto SKIP_DOUBLE
+    jmp 5f                                            // goto end
+3:  // SKIP_FLOAT
+    addl MACRO_LITERAL(4), REG_VAR(arg_array, 1)      // arg_array++
+    jmp 1b                                            // goto LOOP
+4:  // SKIP_DOUBLE
+    addl MACRO_LITERAL(8), REG_VAR(arg_array, 1)      // arg_array+=2
+    jmp 1b                                            // goto LOOP
+5:
+END_MACRO
+
+  /*
+     * Quick invocation stub (non-static).
      * On entry:
      *   [sp] = return address
      *   [sp + 4] = method pointer
@@ -295,30 +415,73 @@
      *   [sp + 24] = shorty
      */
 DEFINE_FUNCTION art_quick_invoke_stub
+    // Save the non-volatiles.
     PUSH ebp                      // save ebp
     PUSH ebx                      // save ebx
+    PUSH esi                      // save esi
+    PUSH edi                      // save edi
+    // Set up argument XMM registers.
+    mov 24+16(%esp), %esi         // ESI := shorty + 1  ; ie skip return arg character.
+    addl LITERAL(1), %esi
+    mov 8+16(%esp), %edi          // EDI := arg_array + 4 ; ie skip this pointer.
+    addl LITERAL(4), %edi
+    // Clobbers ESI, EDI, EAX.
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm0, esi, edi, al, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm1, esi, edi, al, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm2, esi, edi, al, .Lxmm_setup_finished
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm3, esi, edi, al, .Lxmm_setup_finished
+    .balign 16
+.Lxmm_setup_finished:
     mov %esp, %ebp                // copy value of stack pointer into base pointer
     CFI_DEF_CFA_REGISTER(ebp)
-    mov 20(%ebp), %ebx            // get arg array size
-    addl LITERAL(28), %ebx        // reserve space for return addr, method*, ebx, and ebp in frame
-    andl LITERAL(0xFFFFFFF0), %ebx    // align frame size to 16 bytes
-    subl LITERAL(12), %ebx        // remove space for return address, ebx, and ebp
+    mov 28(%ebp), %ebx            // get arg array size
+    // reserve space for return addr, method*, ebx, ebp, esi, and edi in frame
+    addl LITERAL(36), %ebx
+    // align frame size to 16 bytes
+    andl LITERAL(0xFFFFFFF0), %ebx
+    subl LITERAL(20), %ebx        // remove space for return address, ebx, ebp, esi and edi
     subl %ebx, %esp               // reserve stack space for argument array
-    SETUP_GOT_NOSAVE ebx          // clobbers ebx (harmless here)
-    lea  4(%esp), %eax            // use stack pointer + method ptr as dest for memcpy
-    pushl 20(%ebp)                // push size of region to memcpy
-    pushl 16(%ebp)                // push arg array as source of memcpy
-    pushl %eax                    // push stack pointer as destination of memcpy
-    call PLT_SYMBOL(memcpy)       // (void*, const void*, size_t)
-    addl LITERAL(12), %esp        // pop arguments to memcpy
+
     movl LITERAL(0), (%esp)       // store NULL for method*
-    mov 12(%ebp), %eax            // move method pointer into eax
-    mov 4(%esp), %ecx             // copy arg1 into ecx
-    mov 8(%esp), %edx             // copy arg2 into edx
-    mov 12(%esp), %ebx            // copy arg3 into ebx
+
+    // Copy arg array into stack.
+    movl 28(%ebp), %ecx           // ECX = size of args
+    movl 24(%ebp), %esi           // ESI = argument array
+    leal 4(%esp), %edi            // EDI = just after Method* in stack arguments
+    rep movsb                     // while (ecx--) { *edi++ = *esi++ }
+
+    mov 40(%ebp), %esi            // ESI := shorty + 1  ; ie skip return arg character.
+    addl LITERAL(1), %esi
+    mov 24(%ebp), %edi            // EDI := arg_array
+    mov 0(%edi), %ecx             // ECX := this pointer
+    addl LITERAL(4), %edi         // EDI := arg_array + 4 ; ie skip this pointer.
+
+    // Enumerate the possible cases for loading GPRS.
+    // edx (and maybe ebx):
+    SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished
+    cmpb LITERAL(74), %al         // if (al == 'J') goto FOUND_LONG
+    je .LfirstLong
+    // Must be an integer value.
+    movl (%edi), %edx
+    addl LITERAL(4), %edi         // arg_array++
+
+    // Now check ebx
+    SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished
+    // Must be first word of a long, or an integer. First word of long doesn't
+    // go into EBX, but can be loaded there anyways, as it is harmless.
+    movl (%edi), %ebx
+    jmp .Lgpr_setup_finished
+.LfirstLong:
+    movl (%edi), %edx
+    movl 4(%edi), %ebx
+    // Nothing left to load.
+.Lgpr_setup_finished:
+    mov 20(%ebp), %eax            // move method pointer into eax
     call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method
     mov %ebp, %esp                // restore stack pointer
     CFI_DEF_CFA_REGISTER(esp)
+    POP edi                       // pop edi
+    POP esi                       // pop esi
     POP ebx                       // pop ebx
     POP ebp                       // pop ebp
     mov 20(%esp), %ecx            // get result pointer
@@ -338,6 +501,123 @@
     ret
 END_FUNCTION art_quick_invoke_stub
 
+  /*
+     * Quick invocation stub (static).
+     * On entry:
+     *   [sp] = return address
+     *   [sp + 4] = method pointer
+     *   [sp + 8] = argument array or NULL for no argument methods
+     *   [sp + 12] = size of argument array in bytes
+     *   [sp + 16] = (managed) thread pointer
+     *   [sp + 20] = JValue* result
+     *   [sp + 24] = shorty
+     */
+DEFINE_FUNCTION art_quick_invoke_static_stub
+    // Save the non-volatiles.
+    PUSH ebp                      // save ebp
+    PUSH ebx                      // save ebx
+    PUSH esi                      // save esi
+    PUSH edi                      // save edi
+    // Set up argument XMM registers.
+    mov 24+16(%esp), %esi         // ESI := shorty + 1  ; ie skip return arg character.
+    addl LITERAL(1), %esi
+    mov 8+16(%esp), %edi          // EDI := arg_array
+    // Clobbers ESI, EDI, EAX.
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm0, esi, edi, al, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm1, esi, edi, al, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm2, esi, edi, al, .Lxmm_setup_finished2
+    LOOP_OVER_SHORTY_LOADING_XMMS xmm3, esi, edi, al, .Lxmm_setup_finished2
+    .balign 16
+.Lxmm_setup_finished2:
+    mov %esp, %ebp                // copy value of stack pointer into base pointer
+    CFI_DEF_CFA_REGISTER(ebp)
+    mov 28(%ebp), %ebx            // get arg array size
+    // reserve space for return addr, method*, ebx, ebp, esi, and edi in frame
+    addl LITERAL(36), %ebx
+    // align frame size to 16 bytes
+    andl LITERAL(0xFFFFFFF0), %ebx
+    subl LITERAL(20), %ebx        // remove space for return address, ebx, ebp, esi and edi
+    subl %ebx, %esp               // reserve stack space for argument array
+
+    movl LITERAL(0), (%esp)       // store NULL for method*
+
+    // Copy arg array into stack.
+    movl 28(%ebp), %ecx           // ECX = size of args
+    movl 24(%ebp), %esi           // ESI = argument array
+    leal 4(%esp), %edi            // EDI = just after Method* in stack arguments
+    rep movsb                     // while (ecx--) { *edi++ = *esi++ }
+
+    mov 40(%ebp), %esi            // ESI := shorty + 1  ; ie skip return arg character.
+    addl LITERAL(1), %esi
+    mov 24(%ebp), %edi            // EDI := arg_array
+
+    // Enumerate the possible cases for loading GPRS.
+    // ecx (and maybe edx)
+    SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+    cmpb LITERAL(74), %al         // if (al == 'J') goto FOUND_LONG
+    je .LfirstLong2
+    // Must be an integer value.  Load into ECX.
+    movl (%edi), %ecx
+    addl LITERAL(4), %edi         // arg_array++
+
+    // Now check edx (and maybe ebx).
+    SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+    cmpb LITERAL(74), %al         // if (al == 'J') goto FOUND_LONG
+    je .LSecondLong2
+    // Must be an integer.  Load into EDX.
+    movl (%edi), %edx
+    addl LITERAL(4), %edi         // arg_array++
+
+    // Is there anything for ebx?
+    SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+    // Must be first word of a long, or an integer. First word of long doesn't
+    // go into EBX, but can be loaded there anyways, as it is harmless.
+    movl (%edi), %ebx
+    jmp .Lgpr_setup_finished2
+.LSecondLong2:
+    // EDX:EBX is long.  That is all.
+    movl (%edi), %edx
+    movl 4(%edi), %ebx
+    jmp .Lgpr_setup_finished2
+.LfirstLong2:
+    // ECX:EDX is a long
+    movl (%edi), %ecx
+    movl 4(%edi), %edx
+    addl LITERAL(8), %edi         // arg_array += 2
+
+    // Anything for EBX?
+    SKIP_OVER_FLOATS esi, edi, al, .Lgpr_setup_finished2
+    // Must be first word of a long, or an integer. First word of long doesn't
+    // go into EBX, but can be loaded there anyways, as it is harmless.
+    movl (%edi), %ebx
+    jmp .Lgpr_setup_finished2
+    // Nothing left to load.
+.Lgpr_setup_finished2:
+    mov 20(%ebp), %eax            // move method pointer into eax
+    call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method
+    mov %ebp, %esp                // restore stack pointer
+    CFI_DEF_CFA_REGISTER(esp)
+    POP edi                       // pop edi
+    POP esi                       // pop esi
+    POP ebx                       // pop ebx
+    POP ebp                       // pop ebp
+    mov 20(%esp), %ecx            // get result pointer
+    mov %eax, (%ecx)              // store the result assuming its a long, int or Object*
+    mov %edx, 4(%ecx)             // store the other half of the result
+    mov 24(%esp), %edx            // get the shorty
+    cmpb LITERAL(68), (%edx)      // test if result type char == 'D'
+    je .Lreturn_double_quick2
+    cmpb LITERAL(70), (%edx)      // test if result type char == 'F'
+    je .Lreturn_float_quick2
+    ret
+.Lreturn_double_quick2:
+    movsd %xmm0, (%ecx)           // store the floating point result
+    ret
+.Lreturn_float_quick2:
+    movss %xmm0, (%ecx)           // store the floating point result
+    ret
+END_FUNCTION art_quick_invoke_static_stub
+
 MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
     DEFINE_FUNCTION RAW_VAR(c_name, 0)
     SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx  // save ref containing registers for GC
@@ -590,6 +870,46 @@
 GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
 
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+
 TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
 TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
 TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
@@ -796,20 +1116,20 @@
 NO_ARG_DOWNCALL art_quick_test_suspend, artTestSuspendFromCode, ret
 
 DEFINE_FUNCTION art_quick_d2l
-    PUSH eax                      // alignment padding
-    PUSH ecx                      // pass arg2 a.hi
-    PUSH eax                      // pass arg1 a.lo
-    call SYMBOL(art_d2l)      // (jdouble a)
+    subl LITERAL(12), %esp        // alignment padding, room for argument
+    CFI_ADJUST_CFA_OFFSET(12)
+    movsd %xmm0, 0(%esp)          // arg a
+    call SYMBOL(art_d2l)          // (jdouble a)
     addl LITERAL(12), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-12)
     ret
 END_FUNCTION art_quick_d2l
 
 DEFINE_FUNCTION art_quick_f2l
-    subl LITERAL(8), %esp         // alignment padding
-    CFI_ADJUST_CFA_OFFSET(8)
-    PUSH eax                      // pass arg1 a
-    call SYMBOL(art_f2l)      // (jfloat a)
+    subl LITERAL(12), %esp        // alignment padding
+    CFI_ADJUST_CFA_OFFSET(12)
+    movss %xmm0, 0(%esp)          // arg a
+    call SYMBOL(art_f2l)          // (jfloat a)
     addl LITERAL(12), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-12)
     ret
@@ -969,8 +1289,8 @@
     movd %eax, %xmm0              // place return value also into floating point return value
     movd %edx, %xmm1
     punpckldq %xmm1, %xmm0
-    addl LITERAL(44), %esp        // pop arguments
-    CFI_ADJUST_CFA_OFFSET(-44)
+    addl LITERAL(76), %esp        // pop arguments
+    CFI_ADJUST_CFA_OFFSET(-76)
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_proxy_invoke_handler
 
@@ -982,7 +1302,7 @@
     PUSH ecx
     movl 8(%esp), %eax            // load caller Method*
     movl MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax  // load dex_cache_resolved_methods
-    movd %xmm0, %ecx              // get target method index stored in xmm0
+    movd %xmm7, %ecx              // get target method index stored in xmm0
     movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax  // load the target method
     POP ecx
     jmp SYMBOL(art_quick_invoke_interface_trampoline)
@@ -1001,14 +1321,7 @@
     addl LITERAL(16), %esp        // pop arguments
     test %eax, %eax               // if code pointer is NULL goto deliver pending exception
     jz 1f
-    POP eax                       // called method
-    POP ecx                       // restore args
-    POP edx
-    POP ebx
-    POP ebp                       // restore callee saves except EDI
-    POP esi
-    xchgl 0(%esp),%edi            // restore EDI and place code pointer as only value on stack
-    ret                           // tail call into method
+    RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME_AND_JUMP
 1:
     RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
     DELIVER_PENDING_EXCEPTION
@@ -1042,7 +1355,6 @@
     movl %edx, %esp
 
     // On x86 there are no registers passed, so nothing to pop here.
-
     // Native call.
     call *%eax
 
@@ -1069,8 +1381,10 @@
     jnz .Lexception_in_native
 
     // Tear down the callee-save frame.
-    addl LITERAL(4), %esp     // Remove padding
-    CFI_ADJUST_CFA_OFFSET(-4)
+    // Remove space for FPR args and EAX
+    addl LITERAL(4 + 4 * 8), %esp
+    CFI_ADJUST_CFA_OFFSET(-(4 + 4 * 8))
+
     POP ecx
     addl LITERAL(4), %esp     // Avoid edx, as it may be part of the result.
     CFI_ADJUST_CFA_OFFSET(-4)
@@ -1100,12 +1414,21 @@
     CFI_ADJUST_CFA_OFFSET(4)
     PUSH eax                      // pass  method
     call SYMBOL(artQuickToInterpreterBridge)  // (method, Thread*, SP)
-    movd %eax, %xmm0              // place return value also into floating point return value
-    movd %edx, %xmm1
-    punpckldq %xmm1, %xmm0
     addl LITERAL(16), %esp        // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
-    RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+
+    // Return eax:edx in xmm0 also.
+    movd %eax, %xmm0
+    movd %edx, %xmm1
+    punpckldq %xmm1, %xmm0
+
+    addl LITERAL(48), %esp        // Remove FPRs and EAX, ECX, EDX, EBX.
+    CFI_ADJUST_CFA_OFFSET(-48)
+
+    POP ebp  // Restore callee saves
+    POP esi
+    POP edi
+
     RETURN_OR_DELIVER_PENDING_EXCEPTION    // return or deliver exception
 END_FUNCTION art_quick_to_interpreter_bridge
 
@@ -1125,18 +1448,25 @@
     PUSH eax                      // Pass Method*.
     call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR)
     addl LITERAL(28), %esp        // Pop arguments upto saved Method*.
-    movl 28(%esp), %edi           // Restore edi.
-    movl %eax, 28(%esp)           // Place code* over edi, just under return pc.
+    movl 60(%esp), %edi           // Restore edi.
+    movl %eax, 60(%esp)           // Place code* over edi, just under return pc.
     movl SYMBOL(art_quick_instrumentation_exit)@GOT(%ebx), %ebx
     // Place instrumentation exit as return pc. ebx holds the GOT computed on entry.
-    movl %ebx, 32(%esp)
-    movl (%esp), %eax             // Restore eax.
-    movl 8(%esp), %ecx            // Restore ecx.
-    movl 12(%esp), %edx           // Restore edx.
-    movl 16(%esp), %ebx           // Restore ebx.
-    movl 20(%esp), %ebp           // Restore ebp.
-    movl 24(%esp), %esi           // Restore esi.
-    addl LITERAL(28), %esp        // Wind stack back upto code*.
+    movl %ebx, 64(%esp)
+    movl 0(%esp), %eax           // Restore eax.
+    // Restore FPRs (extra 4 bytes of offset due to EAX push at top).
+    movsd 8(%esp), %xmm0
+    movsd 16(%esp), %xmm1
+    movsd 24(%esp), %xmm2
+    movsd 32(%esp), %xmm3
+
+    // Restore GPRs.
+    movl 40(%esp), %ecx           // Restore ecx.
+    movl 48(%esp), %edx           // Restore edx.
+    movl 48(%esp), %ebx           // Restore ebx.
+    movl 52(%esp), %ebp           // Restore ebp.
+    movl 56(%esp), %esi           // Restore esi.
+    addl LITERAL(60), %esp        // Wind stack back upto code*.
     ret                           // Call method (and pop).
 END_FUNCTION art_quick_instrumentation_entry
 
diff --git a/runtime/arch/x86/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h
index b9dc0d8..9bba531 100644
--- a/runtime/arch/x86/quick_method_frame_info_x86.h
+++ b/runtime/arch/x86/quick_method_frame_info_x86.h
@@ -24,25 +24,44 @@
 namespace art {
 namespace x86 {
 
+enum XMM {
+  XMM0 = 0,
+  XMM1 = 1,
+  XMM2 = 2,
+  XMM3 = 3,
+  XMM4 = 4,
+  XMM5 = 5,
+  XMM6 = 6,
+  XMM7 = 7,
+};
+
 static constexpr uint32_t kX86CalleeSaveRefSpills =
     (1 << art::x86::EBP) | (1 << art::x86::ESI) | (1 << art::x86::EDI);
 static constexpr uint32_t kX86CalleeSaveArgSpills =
     (1 << art::x86::ECX) | (1 << art::x86::EDX) | (1 << art::x86::EBX);
+static constexpr uint32_t kX86CalleeSaveFpArgSpills =
+    (1 << art::x86::XMM0) | (1 << art::x86::XMM1) |
+    (1 << art::x86::XMM2) | (1 << art::x86::XMM3);
 
 constexpr uint32_t X86CalleeSaveCoreSpills(Runtime::CalleeSaveType type) {
   return kX86CalleeSaveRefSpills | (type == Runtime::kRefsAndArgs ? kX86CalleeSaveArgSpills : 0) |
       (1 << art::x86::kNumberOfCpuRegisters);  // fake return address callee save
 }
 
+constexpr uint32_t X86CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
+    return type == Runtime::kRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0;
+}
+
 constexpr uint32_t X86CalleeSaveFrameSize(Runtime::CalleeSaveType type) {
   return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ +
+                  2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ +
                   1 /* Method* */) * kX86PointerSize, kStackAlignment);
 }
 
 constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(Runtime::CalleeSaveType type) {
   return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type),
                               X86CalleeSaveCoreSpills(type),
-                              0u);
+                              X86CalleeSaveFpSpills(type));
 }
 
 }  // namespace x86
diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc
index 6e9b99c..cdc2ec7 100644
--- a/runtime/arch/x86_64/context_x86_64.cc
+++ b/runtime/arch/x86_64/context_x86_64.cc
@@ -91,26 +91,18 @@
   fprs_[XMM11] = nullptr;
 }
 
-bool X86_64Context::SetGPR(uint32_t reg, uintptr_t value) {
+void X86_64Context::SetGPR(uint32_t reg, uintptr_t value) {
   CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+  DCHECK(IsAccessibleGPR(reg));
   CHECK_NE(gprs_[reg], &gZero);
-  if (gprs_[reg] != nullptr) {
-    *gprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *gprs_[reg] = value;
 }
 
-bool X86_64Context::SetFPR(uint32_t reg, uintptr_t value) {
+void X86_64Context::SetFPR(uint32_t reg, uintptr_t value) {
   CHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+  DCHECK(IsAccessibleFPR(reg));
   CHECK_NE(fprs_[reg], reinterpret_cast<const uint64_t*>(&gZero));
-  if (fprs_[reg] != nullptr) {
-    *fprs_[reg] = value;
-    return true;
-  } else {
-    return false;
-  }
+  *fprs_[reg] = value;
 }
 
 extern "C" void art_quick_do_long_jump(uintptr_t*, uintptr_t*);
diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h
index 902c3b9..0dda06e 100644
--- a/runtime/arch/x86_64/context_x86_64.h
+++ b/runtime/arch/x86_64/context_x86_64.h
@@ -36,44 +36,43 @@
   void FillCalleeSaves(const StackVisitor& fr) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SetSP(uintptr_t new_sp) OVERRIDE {
-    bool success = SetGPR(RSP, new_sp);
-    CHECK(success) << "Failed to set RSP register";
+    SetGPR(RSP, new_sp);
   }
 
   void SetPC(uintptr_t new_pc) OVERRIDE {
     rip_ = new_pc;
   }
 
+  bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
+    return gprs_[reg] != nullptr;
+  }
+
   uintptr_t* GetGPRAddress(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
     return gprs_[reg];
   }
 
-  bool GetGPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  uintptr_t GetGPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCpuRegisters));
-    if (gprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *gprs_[reg];
-      return true;
-    }
+    DCHECK(IsAccessibleGPR(reg));
+    return *gprs_[reg];
   }
 
-  bool SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  void SetGPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
-  bool GetFPR(uint32_t reg, uintptr_t* val) OVERRIDE {
+  bool IsAccessibleFPR(uint32_t reg) OVERRIDE {
     DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
-    if (fprs_[reg] == nullptr) {
-      return false;
-    } else {
-      DCHECK(val != nullptr);
-      *val = *fprs_[reg];
-      return true;
-    }
+    return fprs_[reg] != nullptr;
   }
 
-  bool SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
+  uintptr_t GetFPR(uint32_t reg) OVERRIDE {
+    DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfFloatRegisters));
+    DCHECK(IsAccessibleFPR(reg));
+    return *fprs_[reg];
+  }
+
+  void SetFPR(uint32_t reg, uintptr_t value) OVERRIDE;
 
   void SmashCallerSaves() OVERRIDE;
   void DoLongJump() OVERRIDE;
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index a2766f7..b25d7a7 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -16,7 +16,6 @@
 
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "entrypoints/quick/quick_default_externs.h"
 #include "entrypoints/quick/quick_entrypoints.h"
@@ -31,9 +30,9 @@
                                                    const mirror::Class* ref_class);
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
-                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
+                     QuickEntryPoints* qpoints) {
 #if defined(__APPLE__)
-  UNUSED(ipoints, jpoints, ppoints, qpoints);
+  UNUSED(ipoints, jpoints, qpoints);
   UNIMPLEMENTED(FATAL);
 #else
   // Interpreter
@@ -43,10 +42,6 @@
   // JNI
   jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
 
-  // Portable
-  ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline;
-  ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
-
   // Alloc
   ResetQuickAllocEntryPoints(qpoints);
 
diff --git a/runtime/arch/x86_64/portable_entrypoints_x86_64.S b/runtime/arch/x86_64/portable_entrypoints_x86_64.S
deleted file mode 100644
index 3a54005..0000000
--- a/runtime/arch/x86_64/portable_entrypoints_x86_64.S
+++ /dev/null
@@ -1,30 +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 "asm_support_x86_64.S"
-
-    /*
-     * Portable invocation stub.
-     */
-UNIMPLEMENTED art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_proxy_invoke_handler
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a80e7d2..c865541 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -487,15 +487,21 @@
     PUSH rbp                      // Save rbp.
     PUSH r8                       // Save r8/result*.
     PUSH r9                       // Save r9/shorty*.
+    PUSH rbx                      // Save native callee save rbx
+    PUSH r12                      // Save native callee save r12
+    PUSH r13                      // Save native callee save r13
+    PUSH r14                      // Save native callee save r14
+    PUSH r15                      // Save native callee save r15
     movq %rsp, %rbp               // Copy value of stack pointer into base pointer.
     CFI_DEF_CFA_REGISTER(rbp)
 
     movl %edx, %r10d
-    addl LITERAL(60), %edx        // Reserve space for return addr, StackReference<method>, rbp,
-                                  // r8 and r9 in frame.
-    andl LITERAL(0xFFFFFFF0), %edx    // Align frame size to 16 bytes.
-    subl LITERAL(32), %edx        // Remove space for return address, rbp, r8 and r9.
-    subq %rdx, %rsp               // Reserve stack space for argument array.
+    addl LITERAL(100), %edx        // Reserve space for return addr, StackReference<method>, rbp,
+                                   // r8, r9, rbx, r12, r13, r14, and r15 in frame.
+    andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
+    subl LITERAL(72), %edx         // Remove space for return address, rbp, r8, r9, rbx, r12,
+                                   // r13, r14, and r15
+    subq %rdx, %rsp                // Reserve stack space for argument array.
 
 #if (STACK_REFERENCE_SIZE != 4)
 #error "STACK_REFERENCE_SIZE(X86_64) size not as expected."
@@ -503,15 +509,15 @@
     movl LITERAL(0), (%rsp)       // Store NULL for method*
 
     movl %r10d, %ecx              // Place size of args in rcx.
-    movq %rdi, %rax               // RAX := method to be called
-    movq %rsi, %r11               // R11 := arg_array
-    leaq 4(%rsp), %rdi            // Rdi is pointing just above the StackReference<method> in the
+    movq %rdi, %rax               // rax := method to be called
+    movq %rsi, %r11               // r11 := arg_array
+    leaq 4(%rsp), %rdi            // rdi is pointing just above the StackReference<method> in the
                                   // stack arguments.
     // Copy arg array into stack.
     rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
-    leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character
-    movq %rax, %rdi               // RDI := method to be called
-    movl (%r11), %esi             // RSI := this pointer
+    leaq 1(%r9), %r10             // r10 := shorty + 1  ; ie skip return arg character
+    movq %rax, %rdi               // rdi := method to be called
+    movl (%r11), %esi             // rsi := this pointer
     addq LITERAL(4), %r11         // arg_array++
     LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished
     LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished
@@ -520,8 +526,12 @@
 .Lgpr_setup_finished:
     call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
     movq %rbp, %rsp               // Restore stack pointer.
-    CFI_DEF_CFA_REGISTER(rsp)
-    POP r9                        // Pop r9 - shorty*.
+    POP r15                       // Pop r15
+    POP r14                       // Pop r14
+    POP r13                       // Pop r13
+    POP r12                       // Pop r12
+    POP rbx                       // Pop rbx
+    POP r9                        // Pop r9 - shorty*
     POP r8                        // Pop r8 - result*.
     POP rbp                       // Pop rbp
     cmpb LITERAL(68), (%r9)       // Test if result type char == 'D'.
@@ -531,10 +541,10 @@
     movq %rax, (%r8)              // Store the result assuming its a long, int or Object*
     ret
 .Lreturn_double_quick:
-    movsd %xmm0, (%r8)           // Store the double floating point result.
+    movsd %xmm0, (%r8)            // Store the double floating point result.
     ret
 .Lreturn_float_quick:
-    movss %xmm0, (%r8)           // Store the floating point result.
+    movss %xmm0, (%r8)            // Store the floating point result.
     ret
 #endif  // __APPLE__
 END_FUNCTION art_quick_invoke_stub
@@ -571,30 +581,36 @@
     PUSH rbp                      // Save rbp.
     PUSH r8                       // Save r8/result*.
     PUSH r9                       // Save r9/shorty*.
+    PUSH rbx                      // Save rbx
+    PUSH r12                      // Save r12
+    PUSH r13                      // Save r13
+    PUSH r14                      // Save r14
+    PUSH r15                      // Save r15
     movq %rsp, %rbp               // Copy value of stack pointer into base pointer.
     CFI_DEF_CFA_REGISTER(rbp)
 
     movl %edx, %r10d
-    addl LITERAL(60), %edx        // Reserve space for return addr, StackReference<method>, rbp,
-                                  // r8 and r9 in frame.
-    andl LITERAL(0xFFFFFFF0), %edx    // Align frame size to 16 bytes.
-    subl LITERAL(32), %edx        // Remove space for return address, rbp, r8 and r9.
-    subq %rdx, %rsp               // Reserve stack space for argument array.
+    addl LITERAL(100), %edx        // Reserve space for return addr, StackReference<method>, rbp,
+                                   // r8, r9, r12, r13, r14, and r15 in frame.
+    andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
+    subl LITERAL(72), %edx         // Remove space for return address, rbp, r8, r9, rbx, r12,
+                                   // r13, r14, and r15.
+    subq %rdx, %rsp                // Reserve stack space for argument array.
 
 #if (STACK_REFERENCE_SIZE != 4)
 #error "STACK_REFERENCE_SIZE(X86_64) size not as expected."
 #endif
-    movl LITERAL(0), (%rsp)       // Store NULL for method*
+    movl LITERAL(0), (%rsp)        // Store NULL for method*
 
-    movl %r10d, %ecx              // Place size of args in rcx.
-    movq %rdi, %rax               // RAX := method to be called
-    movq %rsi, %r11               // R11 := arg_array
-    leaq 4(%rsp), %rdi            // Rdi is pointing just above the StackReference<method> in the
-                                  // stack arguments.
+    movl %r10d, %ecx               // Place size of args in rcx.
+    movq %rdi, %rax                // rax := method to be called
+    movq %rsi, %r11                // r11 := arg_array
+    leaq 4(%rsp), %rdi             // rdi is pointing just above the StackReference<method> in the
+                                   // stack arguments.
     // Copy arg array into stack.
-    rep movsb                     // while (rcx--) { *rdi++ = *rsi++ }
-    leaq 1(%r9), %r10             // R10 := shorty + 1  ; ie skip return arg character
-    movq %rax, %rdi               // RDI := method to be called
+    rep movsb                      // while (rcx--) { *rdi++ = *rsi++ }
+    leaq 1(%r9), %r10              // r10 := shorty + 1  ; ie skip return arg character
+    movq %rax, %rdi                // rdi := method to be called
     LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, .Lgpr_setup_finished2
     LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished2
     LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished2
@@ -602,22 +618,26 @@
     LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
 .Lgpr_setup_finished2:
     call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
-    movq %rbp, %rsp               // Restore stack pointer.
-    CFI_DEF_CFA_REGISTER(rsp)
-    POP r9                        // Pop r9 - shorty*.
-    POP r8                        // Pop r8 - result*.
-    POP rbp                       // Pop rbp
-    cmpb LITERAL(68), (%r9)       // Test if result type char == 'D'.
+    movq %rbp, %rsp                // Restore stack pointer.
+    POP r15                        // Pop r15
+    POP r14                        // Pop r14
+    POP r13                        // Pop r13
+    POP r12                        // Pop r12
+    POP rbx                        // Pop rbx
+    POP r9                         // Pop r9 - shorty*.
+    POP r8                         // Pop r8 - result*.
+    POP rbp                        // Pop rbp
+    cmpb LITERAL(68), (%r9)        // Test if result type char == 'D'.
     je .Lreturn_double_quick2
-    cmpb LITERAL(70), (%r9)       // Test if result type char == 'F'.
+    cmpb LITERAL(70), (%r9)        // Test if result type char == 'F'.
     je .Lreturn_float_quick2
-    movq %rax, (%r8)              // Store the result assuming its a long, int or Object*
+    movq %rax, (%r8)               // Store the result assuming its a long, int or Object*
     ret
 .Lreturn_double_quick2:
-    movsd %xmm0, (%r8)           // Store the double floating point result.
+    movsd %xmm0, (%r8)             // Store the double floating point result.
     ret
 .Lreturn_float_quick2:
-    movss %xmm0, (%r8)           // Store the floating point result.
+    movss %xmm0, (%r8)             // Store the floating point result.
     ret
 #endif  // __APPLE__
 END_FUNCTION art_quick_invoke_static_stub
@@ -883,6 +903,46 @@
 GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
 GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
 
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+
 TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
 TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
 TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h
index 8b0dc07..dda1d5f 100644
--- a/runtime/arch/x86_64/registers_x86_64.h
+++ b/runtime/arch/x86_64/registers_x86_64.h
@@ -43,6 +43,7 @@
   R13 = 13,
   R14 = 14,
   R15 = 15,
+  kLastCpuRegister = 15,
   kNumberOfCpuRegisters = 16,
   kNoRegister = -1  // Signals an illegal register.
 };
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 7454cca..a35e05b 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -148,18 +148,10 @@
 ADD_TEST_EQ(MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET,
             art::mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())
 
-#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32     (40 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32,
-            art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value())
-
 #define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32        (36 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32,
             art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
 
-#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64     (56 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64,
-            art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value())
-
 #define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64        (48 + MIRROR_OBJECT_HEADER_SIZE)
 ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64,
             art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
diff --git a/runtime/atomic.h b/runtime/atomic.h
index cf61277..87de506 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -46,6 +46,9 @@
 class QuasiAtomic {
 #if defined(__mips__) && !defined(__LP64__)
   static constexpr bool kNeedSwapMutexes = true;
+#elif defined(__mips__) && defined(__LP64__)
+  // TODO - mips64 still need this for Cas64 ???
+  static constexpr bool kNeedSwapMutexes = true;
 #else
   static constexpr bool kNeedSwapMutexes = false;
 #endif
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 5a8fbb3..66ee870 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -52,7 +52,7 @@
   // Pass function is called by the last thread, the count will
   // be decremented to zero and a Broadcast will be made on the
   // condition variable, thus waking this up.
-  if (count_ != 0) {
+  while (count_ != 0) {
     condition_.Wait(self);
   }
 }
@@ -62,7 +62,18 @@
   SetCountLocked(self, count_ + delta);
   bool timed_out = false;
   if (count_ != 0) {
-    timed_out = condition_.TimedWait(self, timeout_ms, 0);
+    uint32_t timeout_ns = 0;
+    uint64_t abs_timeout = NanoTime() + MsToNs(timeout_ms);
+    for (;;) {
+      timed_out = condition_.TimedWait(self, timeout_ms, timeout_ns);
+      if (timed_out || count_ == 0) return timed_out;
+      // Compute time remaining on timeout.
+      uint64_t now = NanoTime();
+      int64_t time_left = abs_timeout - now;
+      if (time_left <= 0) return true;
+      timeout_ns = time_left % (1000*1000);
+      timeout_ms = time_left / (1000*1000);
+    }
   }
   return timed_out;
 }
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 5ca88e8..0e7f61e 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -14,6 +14,16 @@
  * limitations under the License.
  */
 
+// CAUTION: THIS IS NOT A FULLY GENERAL BARRIER API.
+
+// It may either be used as a "latch" or single-use barrier, or it may be reused under
+// very limited conditions, e.g. if only Pass(), but not Wait() is called.  Unlike a standard
+// latch API, it is possible to initialize the latch to a count of zero, repeatedly call
+// Pass() or Wait(), and only then set the count using the Increment() method.  Threads at
+// a Wait() are only awoken if the count reaches zero AFTER the decrement is applied.
+// This works because, also unlike most latch APIs, there is no way to Wait() without
+// decrementing the count, and thus nobody can spuriosly wake up on the initial zero.
+
 #ifndef ART_RUNTIME_BARRIER_H_
 #define ART_RUNTIME_BARRIER_H_
 
@@ -22,20 +32,23 @@
 
 namespace art {
 
+// TODO: Maybe give this a better name.
 class Barrier {
  public:
   explicit Barrier(int count);
   virtual ~Barrier();
 
-  // Pass through the barrier, decrements the count but does not block.
+  // Pass through the barrier, decrement the count but do not block.
   void Pass(Thread* self);
 
   // Wait on the barrier, decrement the count.
   void Wait(Thread* self);
 
-  // Set the count to a new value, if the value is 0 then everyone waiting on the condition
-  // variable is resumed.
-  void Init(Thread* self, int count);
+  // The following three calls are only safe if we somehow know that no other thread both
+  // - has been woken up, and
+  // - has not left the Wait() or Increment() call.
+  // If these calls are made in that situation, the offending thread is likely to go back
+  // to sleep, resulting in a deadlock.
 
   // Increment the count by delta, wait on condition if count is non zero.
   void Increment(Thread* self, int delta) LOCKS_EXCLUDED(lock_);
@@ -44,6 +57,10 @@
   // true if time out occurred.
   bool Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
 
+  // Set the count to a new value.  This should only be used if there is no possibility that
+  // another thread is still in Wait().  See above.
+  void Init(Thread* self, int count);
+
  private:
   void SetCountLocked(Thread* self, int count) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index de348dc..f68a5d4 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -27,22 +27,17 @@
 namespace art {
 class CheckWaitTask : public Task {
  public:
-  CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2,
-                   AtomicInteger* count3)
+  CheckWaitTask(Barrier* barrier, AtomicInteger* count1, AtomicInteger* count2)
       : barrier_(barrier),
         count1_(count1),
-        count2_(count2),
-        count3_(count3) {}
+        count2_(count2) {}
 
   void Run(Thread* self) {
-    LOG(INFO) << "Before barrier 1 " << *self;
+    LOG(INFO) << "Before barrier" << *self;
     ++*count1_;
     barrier_->Wait(self);
     ++*count2_;
-    LOG(INFO) << "Before barrier 2 " << *self;
-    barrier_->Wait(self);
-    ++*count3_;
-    LOG(INFO) << "After barrier 2 " << *self;
+    LOG(INFO) << "After barrier" << *self;
   }
 
   virtual void Finalize() {
@@ -53,7 +48,6 @@
   Barrier* const barrier_;
   AtomicInteger* const count1_;
   AtomicInteger* const count2_;
-  AtomicInteger* const count3_;
 };
 
 class BarrierTest : public CommonRuntimeTest {
@@ -67,31 +61,27 @@
 TEST_F(BarrierTest, CheckWait) {
   Thread* self = Thread::Current();
   ThreadPool thread_pool("Barrier test thread pool", num_threads);
-  Barrier barrier(0);
+  Barrier barrier(num_threads + 1);  // One extra Wait() in main thread.
+  Barrier timeout_barrier(0);  // Only used for sleeping on timeout.
   AtomicInteger count1(0);
   AtomicInteger count2(0);
-  AtomicInteger count3(0);
   for (int32_t i = 0; i < num_threads; ++i) {
-    thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2, &count3));
+    thread_pool.AddTask(self, new CheckWaitTask(&barrier, &count1, &count2));
   }
   thread_pool.StartWorkers(self);
-  barrier.Increment(self, num_threads);
-  // At this point each thread should have passed through the barrier. The first count should be
-  // equal to num_threads.
-  EXPECT_EQ(num_threads, count1.LoadRelaxed());
-  // Count 3 should still be zero since no thread should have gone past the second barrier.
-  EXPECT_EQ(0, count3.LoadRelaxed());
-  // Now lets tell the threads to pass again.
-  barrier.Increment(self, num_threads);
-  // Count 2 should be equal to num_threads since each thread must have passed the second barrier
-  // at this point.
-  EXPECT_EQ(num_threads, count2.LoadRelaxed());
+  while (count1.LoadRelaxed() != num_threads) {
+    timeout_barrier.Increment(self, 1, 100);  // sleep 100 msecs
+  }
+  // Count 2 should still be zero since no thread should have gone past the barrier.
+  EXPECT_EQ(0, count2.LoadRelaxed());
+  // Perform one additional Wait(), allowing pool threads to proceed.
+  barrier.Wait(self);
   // Wait for all the threads to finish.
   thread_pool.Wait(self, true, false);
-  // All three counts should be equal to num_threads now.
-  EXPECT_EQ(count1.LoadRelaxed(), count2.LoadRelaxed());
-  EXPECT_EQ(count2.LoadRelaxed(), count3.LoadRelaxed());
-  EXPECT_EQ(num_threads, count3.LoadRelaxed());
+  // Both counts should be equal to num_threads now.
+  EXPECT_EQ(count1.LoadRelaxed(), num_threads);
+  EXPECT_EQ(count2.LoadRelaxed(), num_threads);
+  timeout_barrier.Init(self, 0);  // Reset to zero for destruction.
 }
 
 class CheckPassTask : public Task {
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index 5a09c96..8720f0e 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -62,7 +62,7 @@
   kAllocatorTagRememberedSet,
   kAllocatorTagModUnionCardSet,
   kAllocatorTagModUnionReferenceArray,
-  kAllocatorTagJNILibrarires,
+  kAllocatorTagJNILibraries,
   kAllocatorTagCompileTimeClassPath,
   kAllocatorTagOatFile,
   kAllocatorTagDexFileVerifier,
diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h
index dc13dd5..39b19e5 100644
--- a/runtime/base/bit_vector-inl.h
+++ b/runtime/base/bit_vector-inl.h
@@ -29,7 +29,7 @@
   return bit_index_ == other.bit_index_;
 }
 
-inline int BitVector::IndexIterator::operator*() const {
+inline uint32_t BitVector::IndexIterator::operator*() const {
   DCHECK_LT(bit_index_, BitSize());
   return bit_index_;
 }
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 4390180..c3e24a7 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -276,6 +276,10 @@
   }
 }
 
+#if defined(__clang__) && defined(__ARM_64BIT_STATE)
+// b/19180814 When POPCOUNT is inlined, boot up failed on arm64 devices.
+__attribute__((optnone))
+#endif
 uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
   uint32_t word_end = WordIndex(end);
   uint32_t partial_word_bits = end & 0x1f;
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index 1e28a27..557a2ec 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -52,7 +52,7 @@
       return !(*this == other);
     }
 
-    int operator*() const;
+    uint32_t operator*() const;
 
     IndexIterator& operator++();
 
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index 31fd0e7..fe3313d1 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -57,10 +57,10 @@
 
   BitVector::IndexIterator iterator = bv.Indexes().begin();
   EXPECT_TRUE(iterator != bv.Indexes().end());
-  EXPECT_EQ(0, *iterator);
+  EXPECT_EQ(0u, *iterator);
   ++iterator;
   EXPECT_TRUE(iterator != bv.Indexes().end());
-  EXPECT_EQ(static_cast<int>(kBits - 1), *iterator);
+  EXPECT_EQ(kBits - 1u, *iterator);
   ++iterator;
   EXPECT_TRUE(iterator == bv.Indexes().end());
 }
diff --git a/runtime/base/dumpable.h b/runtime/base/dumpable.h
index 3c316cc..9bc4089 100644
--- a/runtime/base/dumpable.h
+++ b/runtime/base/dumpable.h
@@ -17,6 +17,8 @@
 #ifndef ART_RUNTIME_BASE_DUMPABLE_H_
 #define ART_RUNTIME_BASE_DUMPABLE_H_
 
+#include <ostream>
+
 #include "base/macros.h"
 
 namespace art {
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
index b329a31..812ed86 100644
--- a/runtime/base/histogram-inl.h
+++ b/runtime/base/histogram-inl.h
@@ -35,10 +35,13 @@
     DCHECK_GT(new_max, max_);
     GrowBuckets(new_max);
   }
-
   BucketiseValue(value);
 }
 
+template <class Value> inline void Histogram<Value>::AdjustAndAddValue(Value value) {
+  AddValue(value / kAdjust);
+}
+
 template <class Value> inline Histogram<Value>::Histogram(const char* name)
     : kAdjust(0),
       kInitialBucketCount(0),
diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h
index 1e12be8..78f6e1c 100644
--- a/runtime/base/histogram.h
+++ b/runtime/base/histogram.h
@@ -46,6 +46,7 @@
   // This is the expected constructor when creating new Histograms.
   Histogram(const char* name, Value initial_bucket_width, size_t max_buckets = 100);
   void AddValue(Value);
+  void AdjustAndAddValue(Value);  // Add a value after dividing it by kAdjust.
   // Builds the cumulative distribution function from the frequency data.
   // Accumulative summation of frequencies.
   // cumulative_freq[i] = sum(frequency[j] : 0 < j < i )
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index b781d60..0764b87 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -16,6 +16,8 @@
 
 #include "logging.h"
 
+#include <iostream>
+#include <limits>
 #include <sstream>
 
 #include "base/mutex.h"
@@ -42,6 +44,19 @@
 static std::unique_ptr<std::string> gProgramInvocationName;
 static std::unique_ptr<std::string> gProgramInvocationShortName;
 
+// Print INTERNAL_FATAL messages directly instead of at destruction time. This only works on the
+// host right now: for the device, a stream buf collating output into lines and calling LogLine or
+// lower-level logging is necessary.
+#ifdef HAVE_ANDROID_OS
+static constexpr bool kPrintInternalFatalDirectly = false;
+#else
+static constexpr bool kPrintInternalFatalDirectly = !kIsTargetBuild;
+#endif
+
+static bool PrintDirectly(LogSeverity severity) {
+  return kPrintInternalFatalDirectly && severity == INTERNAL_FATAL;
+}
+
 const char* GetCmdLine() {
   return (gCmdLine.get() != nullptr) ? gCmdLine->c_str() : nullptr;
 }
@@ -169,31 +184,39 @@
 
 LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, int error)
   : data_(new LogMessageData(file, line, severity, error)) {
+  if (PrintDirectly(severity)) {
+    static const char* log_characters = "VDIWEFF";
+    CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
+    stream() << ProgramInvocationShortName() << " " << log_characters[static_cast<size_t>(severity)]
+             << " " << getpid() << " " << ::art::GetTid() << " " << file << ":" <<  line << "]";
+  }
 }
 LogMessage::~LogMessage() {
-  if (data_->GetSeverity() < gMinimumLogSeverity) {
-    return;  // No need to format something we're not going to output.
-  }
+  if (!PrintDirectly(data_->GetSeverity())) {
+    if (data_->GetSeverity() < gMinimumLogSeverity) {
+      return;  // No need to format something we're not going to output.
+    }
 
-  // Finish constructing the message.
-  if (data_->GetError() != -1) {
-    data_->GetBuffer() << ": " << strerror(data_->GetError());
-  }
-  std::string msg(data_->ToString());
+    // Finish constructing the message.
+    if (data_->GetError() != -1) {
+      data_->GetBuffer() << ": " << strerror(data_->GetError());
+    }
+    std::string msg(data_->ToString());
 
-  // Do the actual logging with the lock held.
-  {
-    MutexLock mu(Thread::Current(), *Locks::logging_lock_);
-    if (msg.find('\n') == std::string::npos) {
-      LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str());
-    } else {
-      msg += '\n';
-      size_t i = 0;
-      while (i < msg.size()) {
-        size_t nl = msg.find('\n', i);
-        msg[nl] = '\0';
-        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
-        i = nl + 1;
+    // Do the actual logging with the lock held.
+    {
+      MutexLock mu(Thread::Current(), *Locks::logging_lock_);
+      if (msg.find('\n') == std::string::npos) {
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), msg.c_str());
+      } else {
+        msg += '\n';
+        size_t i = 0;
+        while (i < msg.size()) {
+          size_t nl = msg.find('\n', i);
+          msg[nl] = '\0';
+          LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
+          i = nl + 1;
+        }
       }
     }
   }
@@ -205,6 +228,9 @@
 }
 
 std::ostream& LogMessage::stream() {
+  if (PrintDirectly(data_->GetSeverity())) {
+    return std::cerr;
+  }
   return data_->GetBuffer();
 }
 
@@ -239,8 +265,25 @@
 void LogMessage::LogLineLowStack(const char* file, unsigned int line, LogSeverity log_severity,
                                  const char* message) {
 #ifdef HAVE_ANDROID_OS
-  // TODO: be more conservative on stack usage here.
-  LogLine(file, line, log_severity, message);
+  // Use android_writeLog() to avoid stack-based buffers used by android_printLog().
+  const char* tag = ProgramInvocationShortName();
+  int priority = kLogSeverityToAndroidLogPriority[log_severity];
+  char* buf = nullptr;
+  size_t buf_size = 0u;
+  if (priority == ANDROID_LOG_FATAL) {
+    // Allocate buffer for snprintf(buf, buf_size, "%s:%u] %s", file, line, message) below.
+    // If allocation fails, fall back to printing only the message.
+    buf_size = strlen(file) + 1 /* ':' */ + std::numeric_limits<typeof(line)>::max_digits10 +
+        2 /* "] " */ + strlen(message) + 1 /* terminating 0 */;
+    buf = reinterpret_cast<char*>(malloc(buf_size));
+  }
+  if (buf != nullptr) {
+    snprintf(buf, buf_size, "%s:%u] %s", file, line, message);
+    android_writeLog(priority, tag, buf);
+    free(buf);
+  } else {
+    android_writeLog(priority, tag, message);
+  }
 #else
   static const char* log_characters = "VDIWEFF";
   CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
@@ -260,4 +303,13 @@
 #endif
 }
 
+ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
+  old_ = gMinimumLogSeverity;
+  gMinimumLogSeverity = level;
+}
+
+ScopedLogSeverity::~ScopedLogSeverity() {
+  gMinimumLogSeverity = old_;
+}
+
 }  // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index ae83e33..cc1a4a1 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -254,6 +254,16 @@
   DISALLOW_COPY_AND_ASSIGN(LogMessage);
 };
 
+// Allows to temporarily change the minimum severity level for logging.
+class ScopedLogSeverity {
+ public:
+  explicit ScopedLogSeverity(LogSeverity level);
+  ~ScopedLogSeverity();
+
+ private:
+  LogSeverity old_;
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_LOGGING_H_
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 66d6fab..f705469 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -158,6 +158,8 @@
 #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
 #endif
 
+#define NO_INLINE __attribute__ ((noinline))
+
 #if defined (__APPLE__)
 #define HOT_ATTR
 #define COLD_ATTR
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index aa2aefc..6e00cc7 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -319,16 +319,25 @@
   exclusive_owner_ = 0;
 }
 
+// Helper to ignore the lock requirement.
+static bool IsShuttingDown() NO_THREAD_SAFETY_ANALYSIS {
+  Runtime* runtime = Runtime::Current();
+  return runtime == nullptr || runtime->IsShuttingDownLocked();
+}
+
 Mutex::~Mutex() {
+  bool shutting_down = IsShuttingDown();
 #if ART_USE_FUTEXES
   if (state_.LoadRelaxed() != 0) {
-    Runtime* runtime = Runtime::Current();
-    bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current());
     LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_;
   } else {
-    CHECK_EQ(exclusive_owner_, 0U)  << "unexpectedly found an owner on unlocked mutex " << name_;
-    CHECK_EQ(num_contenders_.LoadSequentiallyConsistent(), 0)
-        << "unexpectedly found a contender on mutex " << name_;
+    if (exclusive_owner_ != 0) {
+      LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found an owner on unlocked mutex "
+                                           << name_;
+    }
+    if (num_contenders_.LoadSequentiallyConsistent() != 0) {
+      LOG(shutting_down ? WARNING : FATAL) << "unexpectedly found a contender on mutex " << name_;
+    }
   }
 #else
   // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread
@@ -338,8 +347,6 @@
     errno = rc;
     // TODO: should we just not log at all if shutting down? this could be the logging mutex!
     MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
-    Runtime* runtime = Runtime::Current();
-    bool shutting_down = (runtime == NULL) || runtime->IsShuttingDownLocked();
     PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_;
   }
 #endif
@@ -430,7 +437,18 @@
 }
 
 void Mutex::ExclusiveUnlock(Thread* self) {
-  DCHECK(self == NULL || self == Thread::Current());
+  if (kIsDebugBuild && self != nullptr && self != Thread::Current()) {
+    std::string name1 = "<null>";
+    std::string name2 = "<null>";
+    if (self != nullptr) {
+      self->GetThreadName(name1);
+    }
+    if (Thread::Current() != nullptr) {
+      Thread::Current()->GetThreadName(name2);
+    }
+    LOG(FATAL) << GetName() << " level=" << level_ << " self=" << name1
+               << " Thread::Current()=" << name2;
+  }
   AssertHeld(self);
   DCHECK_NE(exclusive_owner_, 0U);
   recursion_count_--;
@@ -598,7 +616,7 @@
 #if ART_USE_FUTEXES
   bool done = false;
   timespec end_abs_ts;
-  InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &end_abs_ts);
+  InitTimeSpec(true, CLOCK_MONOTONIC, ms, ns, &end_abs_ts);
   do {
     int32_t cur_state = state_.LoadRelaxed();
     if (cur_state == 0) {
@@ -607,7 +625,7 @@
     } else {
       // Failed to acquire, hang up.
       timespec now_abs_ts;
-      InitTimeSpec(true, CLOCK_REALTIME, 0, 0, &now_abs_ts);
+      InitTimeSpec(true, CLOCK_MONOTONIC, 0, 0, &now_abs_ts);
       timespec rel_ts;
       if (ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
         return false;  // Timed out.
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 9c93cc6..745b209 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -60,6 +60,7 @@
   kThreadSuspendCountLock,
   kAbortLock,
   kJdwpSocketLock,
+  kRegionSpaceRegionLock,
   kReferenceQueueSoftReferencesLock,
   kReferenceQueuePhantomReferencesLock,
   kReferenceQueueFinalizerReferencesLock,
@@ -70,6 +71,7 @@
   kRosAllocBracketLock,
   kRosAllocBulkFreeLock,
   kAllocSpaceLock,
+  kBumpPointerSpaceBlockLock,
   kDexFileMethodInlinerLock,
   kDexFileToMethodInlinerMapLock,
   kMarkSweepMarkStackLock,
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index ff9f40c..3c5565c 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -57,8 +57,8 @@
 // If container is NULL, this function is a no-op.
 //
 // As an alternative to calling STLDeleteElements() directly, consider
-// ElementDeleter (defined below), which ensures that your container's elements
-// are deleted when the ElementDeleter goes out of scope.
+// using a container of std::unique_ptr, which ensures that your container's
+// elements are deleted when the container goes out of scope.
 template <class T>
 void STLDeleteElements(T *container) {
   if (!container) return;
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 6e5e7a1..f272d88 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -178,10 +178,16 @@
   return fd_ >= 0;
 }
 
-bool FdFile::ReadFully(void* buffer, size_t byte_count) {
+static ssize_t ReadIgnoreOffset(int fd, void *buf, size_t count, off_t offset) {
+  DCHECK_EQ(offset, 0);
+  return read(fd, buf, count);
+}
+
+template <ssize_t (*read_func)(int, void*, size_t, off_t)>
+static bool ReadFullyGeneric(int fd, void* buffer, size_t byte_count, size_t offset) {
   char* ptr = static_cast<char*>(buffer);
   while (byte_count > 0) {
-    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd_, ptr, byte_count));
+    ssize_t bytes_read = TEMP_FAILURE_RETRY(read_func(fd, ptr, byte_count, offset));
     if (bytes_read <= 0) {
       // 0: end of file
       // -1: error
@@ -189,10 +195,19 @@
     }
     byte_count -= bytes_read;  // Reduce the number of remaining bytes.
     ptr += bytes_read;  // Move the buffer forward.
+    offset += static_cast<size_t>(bytes_read);  // Move the offset forward.
   }
   return true;
 }
 
+bool FdFile::ReadFully(void* buffer, size_t byte_count) {
+  return ReadFullyGeneric<ReadIgnoreOffset>(fd_, buffer, byte_count, 0);
+}
+
+bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
+  return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
+}
+
 bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
   const char* ptr = static_cast<const char*>(buffer);
   moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
@@ -241,4 +256,8 @@
   return (flush_result != 0) ? flush_result : close_result;
 }
 
+void FdFile::MarkUnchecked() {
+  guard_state_ = GuardState::kNoCheck;
+}
+
 }  // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 8db2ee4..d51fbd6 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -74,6 +74,7 @@
   }
   void DisableAutoClose();
   bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
+  bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
   bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
 
   // This enum is public so that we can define the << operator over it.
@@ -84,6 +85,9 @@
     kNoCheck         // Do not check for the current file instance.
   };
 
+  // WARNING: Only use this when you know what you're doing!
+  void MarkUnchecked();
+
  protected:
   // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
   // given warning if the current state is or exceeds warn_threshold.
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index a7e5b96..388f717 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -76,4 +76,38 @@
   EXPECT_FALSE(file.ReadFully(&buffer, 4));
 }
 
+template <size_t Size>
+static void NullTerminateCharArray(char (&array)[Size]) {
+  array[Size - 1] = '\0';
+}
+
+TEST_F(FdFileTest, ReadFullyWithOffset) {
+  // New scratch file, zero-length.
+  art::ScratchFile tmp;
+  FdFile file;
+  ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR));
+  EXPECT_GE(file.Fd(), 0);
+  EXPECT_TRUE(file.IsOpened());
+
+  char ignore_prefix[20] = {'a', };
+  NullTerminateCharArray(ignore_prefix);
+  char read_suffix[10] = {'b', };
+  NullTerminateCharArray(read_suffix);
+
+  off_t offset = 0;
+  // Write scratch data to file that we can read back into.
+  EXPECT_TRUE(file.Write(ignore_prefix, sizeof(ignore_prefix), offset));
+  offset += sizeof(ignore_prefix);
+  EXPECT_TRUE(file.Write(read_suffix, sizeof(read_suffix), offset));
+
+  ASSERT_EQ(file.Flush(), 0);
+
+  // Reading at an offset should only produce 'bbbb...', since we ignore the 'aaa...' prefix.
+  char buffer[sizeof(read_suffix)];
+  EXPECT_TRUE(file.PreadFully(buffer, sizeof(read_suffix), offset));
+  EXPECT_STREQ(&read_suffix[0], &buffer[0]);
+
+  ASSERT_EQ(file.Close(), 0);
+}
+
 }  // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.cc b/runtime/base/unix_file/null_file.cc
deleted file mode 100644
index 322c25a..0000000
--- a/runtime/base/unix_file/null_file.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 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 "base/unix_file/null_file.h"
-#include <errno.h>
-
-namespace unix_file {
-
-NullFile::NullFile() {
-}
-
-NullFile::~NullFile() {
-}
-
-int NullFile::Close() {
-  return 0;
-}
-
-int NullFile::Flush() {
-  return 0;
-}
-
-int64_t NullFile::Read(char* buf ATTRIBUTE_UNUSED, int64_t byte_count ATTRIBUTE_UNUSED,
-                       int64_t offset) const {
-  if (offset < 0) {
-    return -EINVAL;
-  }
-  return 0;
-}
-
-int NullFile::SetLength(int64_t new_length) {
-  if (new_length < 0) {
-    return -EINVAL;
-  }
-  return 0;
-}
-
-int64_t NullFile::GetLength() const {
-  return 0;
-}
-
-int64_t NullFile::Write(const char* buf ATTRIBUTE_UNUSED, int64_t byte_count ATTRIBUTE_UNUSED,
-                        int64_t offset) {
-  if (offset < 0) {
-    return -EINVAL;
-  }
-  return byte_count;
-}
-
-}  // namespace unix_file
diff --git a/runtime/base/unix_file/null_file.h b/runtime/base/unix_file/null_file.h
deleted file mode 100644
index 3394731..0000000
--- a/runtime/base/unix_file/null_file.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2009 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_BASE_UNIX_FILE_NULL_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
-
-#include "base/unix_file/random_access_file.h"
-#include "base/macros.h"
-
-namespace unix_file {
-
-// A RandomAccessFile implementation equivalent to /dev/null. Writes are
-// discarded, and there's no data to be read. Callers could use FdFile in
-// conjunction with /dev/null, but that's not portable and costs a file
-// descriptor. NullFile is "free".
-//
-// Thread safe.
-class NullFile : public RandomAccessFile {
- public:
-  NullFile();
-  virtual ~NullFile();
-
-  // RandomAccessFile API.
-  virtual int Close();
-  virtual int Flush();
-  virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
-  virtual int SetLength(int64_t new_length);
-  virtual int64_t GetLength() const;
-  virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NullFile);
-};
-
-}  // namespace unix_file
-
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_NULL_FILE_H_
diff --git a/runtime/base/unix_file/null_file_test.cc b/runtime/base/unix_file/null_file_test.cc
deleted file mode 100644
index 410fdfc..0000000
--- a/runtime/base/unix_file/null_file_test.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2009 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 "base/unix_file/null_file.h"
-
-#include <errno.h>
-
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class NullFileTest : public testing::Test { };
-
-TEST_F(NullFileTest, Read) {
-  NullFile f;
-  char buf[256];
-  // You can't read a negative number of bytes...
-  ASSERT_EQ(-EINVAL, f.Read(buf, 0, -1));
-  // ...but everything else is fine (though you'll get no data).
-  ASSERT_EQ(0, f.Read(buf, 128, 0));
-  ASSERT_EQ(0, f.Read(buf, 128, 128));
-}
-
-TEST_F(NullFileTest, SetLength) {
-  NullFile f;
-  // You can't set a negative length...
-  ASSERT_EQ(-EINVAL, f.SetLength(-1));
-  // ...but everything else is fine.
-  ASSERT_EQ(0, f.SetLength(0));
-  ASSERT_EQ(0, f.SetLength(128));
-}
-
-TEST_F(NullFileTest, GetLength) {
-  const std::string content("hello");
-  NullFile f;
-  // The length is always 0.
-  ASSERT_EQ(0, f.GetLength());
-  ASSERT_EQ(content.size(), static_cast<uint64_t>(f.Write(content.data(), content.size(), 0)));
-  ASSERT_EQ(0, f.GetLength());
-}
-
-TEST_F(NullFileTest, Write) {
-  const std::string content("hello");
-  NullFile f;
-  // You can't write at a negative offset...
-  ASSERT_EQ(-EINVAL, f.Write(content.data(), content.size(), -128));
-  // But you can write anywhere else...
-  ASSERT_EQ(content.size(), static_cast<uint64_t>(f.Write(content.data(), content.size(), 0)));
-  ASSERT_EQ(content.size(), static_cast<uint64_t>(f.Write(content.data(), content.size(), 128)));
-  // ...though the file will remain empty.
-  ASSERT_EQ(0, f.GetLength());
-}
-
-}  // namespace unix_file
diff --git a/runtime/base/unix_file/random_access_file_utils_test.cc b/runtime/base/unix_file/random_access_file_utils_test.cc
deleted file mode 100644
index 9457d22..0000000
--- a/runtime/base/unix_file/random_access_file_utils_test.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2009 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 "base/unix_file/random_access_file_utils.h"
-#include "base/unix_file/fd_file.h"
-#include "base/unix_file/string_file.h"
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class RandomAccessFileUtilsTest : public testing::Test { };
-
-TEST_F(RandomAccessFileUtilsTest, CopyFile) {
-  StringFile src;
-  StringFile dst;
-
-  const std::string content("hello");
-  src.Assign(content);
-  ASSERT_EQ(src.ToStringPiece(), content);
-  ASSERT_EQ(dst.ToStringPiece(), "");
-
-  ASSERT_TRUE(CopyFile(src, &dst));
-  ASSERT_EQ(src.ToStringPiece(), dst.ToStringPiece());
-}
-
-TEST_F(RandomAccessFileUtilsTest, BadSrc) {
-  FdFile src(-1, false);
-  StringFile dst;
-  ASSERT_FALSE(CopyFile(src, &dst));
-}
-
-TEST_F(RandomAccessFileUtilsTest, BadDst) {
-  StringFile src;
-  FdFile dst(-1, false);
-
-  // We need some source content to trigger a write.
-  // Copying an empty file is a no-op.
-  src.Assign("hello");
-
-  ASSERT_FALSE(CopyFile(src, &dst));
-}
-
-}  // namespace unix_file
diff --git a/runtime/base/unix_file/string_file.cc b/runtime/base/unix_file/string_file.cc
deleted file mode 100644
index ff0d0fa..0000000
--- a/runtime/base/unix_file/string_file.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2009 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 "base/unix_file/string_file.h"
-#include <errno.h>
-#include <algorithm>
-#include "base/logging.h"
-
-namespace unix_file {
-
-StringFile::StringFile() {
-}
-
-StringFile::~StringFile() {
-}
-
-int StringFile::Close() {
-  return 0;
-}
-
-int StringFile::Flush() {
-  return 0;
-}
-
-int64_t StringFile::Read(char *buf, int64_t byte_count, int64_t offset) const {
-  CHECK(buf);
-  CHECK_GE(byte_count, 0);
-
-  if (offset < 0) {
-    return -EINVAL;
-  }
-
-  const int64_t available_bytes = std::min(byte_count, GetLength() - offset);
-  if (available_bytes < 0) {
-    return 0;  // Not an error, but nothing for us to do, either.
-  }
-  memcpy(buf, data_.data() + offset, available_bytes);
-  return available_bytes;
-}
-
-int StringFile::SetLength(int64_t new_length) {
-  if (new_length < 0) {
-    return -EINVAL;
-  }
-  data_.resize(new_length);
-  return 0;
-}
-
-int64_t StringFile::GetLength() const {
-  return data_.size();
-}
-
-int64_t StringFile::Write(const char *buf, int64_t byte_count, int64_t offset) {
-  CHECK(buf);
-  CHECK_GE(byte_count, 0);
-
-  if (offset < 0) {
-    return -EINVAL;
-  }
-
-  if (byte_count == 0) {
-    return 0;
-  }
-
-  // FUSE seems happy to allow writes past the end. (I'd guess it doesn't
-  // synthesize a write of zero bytes so that we're free to implement sparse
-  // files.) GNU as(1) seems to require such writes. Those files are small.
-  const int64_t bytes_past_end = offset - GetLength();
-  if (bytes_past_end > 0) {
-    data_.append(bytes_past_end, '\0');
-  }
-
-  data_.replace(offset, byte_count, buf, byte_count);
-  return byte_count;
-}
-
-void StringFile::Assign(const art::StringPiece &new_data) {
-  data_.assign(new_data.data(), new_data.size());
-}
-
-const art::StringPiece StringFile::ToStringPiece() const {
-  return data_;
-}
-
-}  // namespace unix_file
diff --git a/runtime/base/unix_file/string_file.h b/runtime/base/unix_file/string_file.h
deleted file mode 100644
index 26904f8..0000000
--- a/runtime/base/unix_file/string_file.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2009 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_BASE_UNIX_FILE_STRING_FILE_H_
-#define ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/stringpiece.h"
-#include "base/unix_file/random_access_file.h"
-
-namespace unix_file {
-
-// A RandomAccessFile implementation backed by a std::string. (That is, all data is
-// kept in memory.)
-//
-// Not thread safe.
-class StringFile : public RandomAccessFile {
- public:
-  StringFile();
-  virtual ~StringFile();
-
-  // RandomAccessFile API.
-  virtual int Close();
-  virtual int Flush();
-  virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
-  virtual int SetLength(int64_t new_length);
-  virtual int64_t GetLength() const;
-  virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
-
-  // Bonus API.
-  void Assign(const art::StringPiece& new_data);
-  const art::StringPiece ToStringPiece() const;
-
- private:
-  std::string data_;
-
-  DISALLOW_COPY_AND_ASSIGN(StringFile);
-};
-
-}  // namespace unix_file
-
-#endif  // ART_RUNTIME_BASE_UNIX_FILE_STRING_FILE_H_
diff --git a/runtime/base/unix_file/string_file_test.cc b/runtime/base/unix_file/string_file_test.cc
deleted file mode 100644
index 8821461..0000000
--- a/runtime/base/unix_file/string_file_test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2009 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 "base/unix_file/string_file.h"
-#include "base/unix_file/random_access_file_test.h"
-#include "gtest/gtest.h"
-
-namespace unix_file {
-
-class StringFileTest : public RandomAccessFileTest {
- protected:
-  virtual RandomAccessFile* MakeTestFile() {
-    return new StringFile;
-  }
-};
-
-TEST_F(StringFileTest, Read) {
-  TestRead();
-}
-
-TEST_F(StringFileTest, SetLength) {
-  TestSetLength();
-}
-
-TEST_F(StringFileTest, Write) {
-  TestWrite();
-}
-
-}  // namespace unix_file
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
new file mode 100644
index 0000000..cf7977e
--- /dev/null
+++ b/runtime/base/variant_map.h
@@ -0,0 +1,453 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_BASE_VARIANT_MAP_H_
+#define ART_RUNTIME_BASE_VARIANT_MAP_H_
+
+#include <memory.h>
+#include <map>
+#include <utility>
+
+namespace art {
+
+//
+// A variant map is a heterogenous, type safe key->value map. It allows
+// for multiple different value types to be stored dynamically in the same map.
+//
+// It provides the following interface in a nutshell:
+//
+// struct VariantMap {
+//   template <typename TValue>
+//   TValue* Get(Key<T> key);  // nullptr if the value was never set, otherwise the value.
+//
+//   template <typename TValue>
+//   void Set(Key<T> key, TValue value);
+// };
+//
+// Since the key is strongly typed at compile-time, it is impossible to accidentally
+// read/write a value with a different type than the key at either compile-time or run-time.
+//
+// Do not use VariantMap/VariantMapKey directly. Instead subclass each of them and use
+// the subclass, for example:
+//
+// template <typename TValue>
+// struct FruitMapKey : VariantMapKey<TValue> {
+//   FruitMapKey() {}
+// };
+//
+// struct FruitMap : VariantMap<FruitMap, FruitMapKey> {
+//   // This 'using' line is necessary to inherit the variadic constructor.
+//   using VariantMap<FruitMap, FruitMapKey>::VariantMap;
+//
+//   // Make the next '4' usages of Key slightly shorter to type.
+//   template <typename TValue>
+//   using Key = FruitMapKey<TValue>;
+//
+//   static const Key<int> Apple;
+//   static const Key<double> Orange;
+//   static const Key<std::string> Banana;
+// };
+//
+// const FruitMap::Key<int> FruitMap::Apple;
+// const FruitMap::Key<double> FruitMap::Orange;
+// const FruitMap::Key<std::string> Banana;
+//
+// See variant_map_test.cc for more examples.
+//
+
+// Implementation details for VariantMap.
+namespace detail {
+  // Allocate a unique counter value each time it's called.
+  struct VariantMapKeyCounterAllocator {
+    static size_t AllocateCounter() {
+      static size_t counter = 0;
+      counter++;
+
+      return counter;
+    }
+  };
+
+  // Type-erased version of VariantMapKey<T>
+  struct VariantMapKeyRaw {
+    // TODO: this may need to call a virtual function to support string comparisons
+    bool operator<(const VariantMapKeyRaw& other) const {
+      return key_counter_ < other.key_counter_;
+    }
+
+    // The following functions need to be virtual since we don't know the compile-time type anymore:
+
+    // Clone the key, creating a copy of the contents.
+    virtual VariantMapKeyRaw* Clone() const = 0;
+
+    // Delete a value whose runtime type is that of the non-erased key's TValue.
+    virtual void ValueDelete(void* value) const = 0;
+
+    // Clone a value whose runtime type is that of the non-erased key's TValue.
+    virtual void* ValueClone(void* value) const = 0;
+
+    // Compare one key to another (same as operator<).
+    virtual bool Compare(const VariantMapKeyRaw* other) const {
+      if (other == nullptr) {
+        return false;
+      }
+      return key_counter_ < other->key_counter_;
+    }
+
+    virtual ~VariantMapKeyRaw() {}
+
+   protected:
+    VariantMapKeyRaw()
+        : key_counter_(VariantMapKeyCounterAllocator::AllocateCounter()) {}
+    // explicit VariantMapKeyRaw(size_t counter)
+    //     : key_counter_(counter) {}
+
+    size_t GetCounter() const {
+      return key_counter_;
+    }
+
+   protected:
+    // Avoid the object slicing problem; use Clone() instead.
+    VariantMapKeyRaw(const VariantMapKeyRaw& other) = default;
+    VariantMapKeyRaw(VariantMapKeyRaw&& other) = default;
+
+   private:
+    size_t key_counter_;  // Runtime type ID. Unique each time a new type is reified.
+  };
+}  // namespace detail
+
+// The base type for keys used by the VariantMap. Users must subclass this type.
+template <typename TValue>
+struct VariantMapKey : detail::VariantMapKeyRaw {
+  // Instantiate a default value for this key. If an explicit default value was provided
+  // then that is used. Otherwise, the default value for the type TValue{} is returned.
+  TValue CreateDefaultValue() const {
+    if (default_value_ == nullptr) {
+      return TValue{};  // NOLINT [readability/braces] [4]
+    } else {
+      return TValue(*default_value_);
+    }
+  }
+
+ protected:
+  // explicit VariantMapKey(size_t counter) : detail::VariantMapKeyRaw(counter) {}
+  explicit VariantMapKey(const TValue& default_value)
+    : default_value_(std::make_shared<TValue>(default_value)) {}
+  explicit VariantMapKey(TValue&& default_value)
+    : default_value_(std::make_shared<TValue>(default_value)) {}
+  VariantMapKey() {}
+  virtual ~VariantMapKey() {}
+
+ private:
+  virtual VariantMapKeyRaw* Clone() const {
+    return new VariantMapKey<TValue>(*this);
+  }
+
+  virtual void* ValueClone(void* value) const {
+    if (value == nullptr) {
+      return nullptr;
+    }
+
+    TValue* strong_value = reinterpret_cast<TValue*>(value);
+    return new TValue(*strong_value);
+  }
+
+  virtual void ValueDelete(void* value) const {
+    if (value == nullptr) {
+      return;
+    }
+
+    // Smartly invoke the proper delete/delete[]/etc
+    const std::default_delete<TValue> deleter = std::default_delete<TValue>();
+    deleter(reinterpret_cast<TValue*>(value));
+  }
+
+  VariantMapKey(const VariantMapKey& other) = default;
+  VariantMapKey(VariantMapKey&& other) = default;
+
+  template <typename Base, template <typename TV> class TKey> friend struct VariantMap;
+
+  // Store a prototype of the key's default value, for usage with VariantMap::GetOrDefault
+  std::shared_ptr<TValue> default_value_;
+};
+
+// Implementation details for a stringified VariantMapStringKey.
+namespace detail {
+  struct VariantMapStringKeyRegistry {
+    // TODO
+  };
+}  // namespace detail
+
+// Alternative base type for all keys used by VariantMap, supports runtime strings as the name.
+template <typename TValue>
+struct VariantMapStringKey : VariantMapKey<TValue> {
+  explicit VariantMapStringKey(const char* name)
+      :   // VariantMapKey(/*std::hash<std::string>()(name)*/),
+        name_(name) {
+  }
+
+ private:
+  const char* name_;
+};
+
+// A variant map allows type-safe heteregeneous key->value mappings.
+// All possible key types must be specified at compile-time. Values may be added/removed
+// at runtime.
+template <typename Base, template <typename TV> class TKey>
+struct VariantMap {
+  // Allow users of this static interface to use the key type.
+  template <typename TValue>
+  using Key = TKey<TValue>;
+
+  // Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed.
+  // A null value is returned only when the key does not exist in this map.
+  template <typename TValue>
+  const TValue* Get(const TKey<TValue>& key) const {
+    return GetValuePtr(key);
+  }
+
+  // Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed.
+  // A null value is returned only when the key does not exist in this map.
+  template <typename TValue>
+  TValue* Get(const TKey<TValue>& key) {
+    return GetValuePtr(key);
+  }
+
+  // Lookup the value from the key. If it was not set in the map, return the default value.
+  // The default value is either the key's default, or TValue{} if the key doesn't have a default.
+  template <typename TValue>
+  TValue GetOrDefault(const TKey<TValue>& key) const {
+    auto* ptr = Get(key);
+    return (ptr == nullptr) ? key.CreateDefaultValue() : *ptr;
+  }
+
+ private:
+  // TODO: move to detail, or make it more generic like a ScopeGuard(function)
+  template <typename TValue>
+  struct ScopedRemove {
+    ScopedRemove(VariantMap& map, const TKey<TValue>& key) : map_(map), key_(key) {}
+    ~ScopedRemove() {
+      map_.Remove(key_);
+    }
+
+    VariantMap& map_;
+    const TKey<TValue>& key_;
+  };
+
+ public:
+  // Release the value from the key. If it was not set in the map, returns the default value.
+  // If the key was set, it is removed as a side effect.
+  template <typename TValue>
+  TValue ReleaseOrDefault(const TKey<TValue>& key) {
+    ScopedRemove<TValue> remove_on_return(*this, key);
+
+    TValue* ptr = Get(key);
+    if (ptr != nullptr) {
+      return std::move(*ptr);
+    } else {
+      TValue default_value = key.CreateDefaultValue();
+      return std::move(default_value);
+    }
+  }
+
+  // See if a value is stored for this key.
+  template <typename TValue>
+  bool Exists(const TKey<TValue>& key) const {
+    return GetKeyValueIterator(key) != storage_map_.end();
+  }
+
+  // Set a value for a given key, overwriting the previous value if any.
+  template <typename TValue>
+  void Set(const TKey<TValue>& key, const TValue& value) {
+    Remove(key);
+    storage_map_.insert({{key.Clone(), new TValue(value)}});
+  }
+
+  // Set a value for a given key, only if there was no previous value before.
+  // Returns true if the value was set, false if a previous value existed.
+  template <typename TValue>
+  bool SetIfMissing(const TKey<TValue>& key, const TValue& value) {
+    TValue* ptr = Get(key);
+    if (ptr == nullptr) {
+      Set(key, value);
+      return true;
+    }
+    return false;
+  }
+
+  // Remove the value for a given key, or a no-op if there was no previously set value.
+  template <typename TValue>
+  void Remove(const TKey<TValue>& key) {
+    StaticAssertKeyType<TValue>();
+
+    auto&& it = GetKeyValueIterator(key);
+    if (it != storage_map_.end()) {
+      key.ValueDelete(it->second);
+      delete it->first;
+      storage_map_.erase(it);
+    }
+  }
+
+  // Remove all key/value pairs.
+  void Clear() {
+    DeleteStoredValues();
+    storage_map_.clear();
+  }
+
+  // How many key/value pairs are stored in this map.
+  size_t Size() const {
+    return storage_map_.size();
+  }
+
+  // Construct an empty map.
+  explicit VariantMap() {}
+
+  template <typename ... TKeyValue>
+  explicit VariantMap(const TKeyValue& ... key_value_list) {
+    static_assert(sizeof...(TKeyValue) % 2 == 0, "Must be an even number of key/value elements");
+    InitializeParameters(key_value_list...);
+  }
+
+  // Create a new map from an existing map, copying all the key/value pairs.
+  VariantMap(const VariantMap& other) {
+    operator=(other);
+  }
+
+  // Copy the key/value pairs from the other map into this one. Existing key/values are cleared.
+  VariantMap& operator=(const VariantMap& other) {
+    if (this == &other) {
+      return *this;
+    }
+
+    Clear();
+
+    for (auto&& kv_pair : other.storage_map_) {
+      const detail::VariantMapKeyRaw* raw_key_other = kv_pair.first;
+      void* value = kv_pair.second;
+
+      detail::VariantMapKeyRaw* cloned_raw_key = raw_key_other->Clone();
+      void* cloned_value = raw_key_other->ValueClone(value);
+
+      storage_map_.insert({{ cloned_raw_key, cloned_value }});
+    }
+
+    return *this;
+  }
+
+  // Create a new map by moving an existing map into this one. The other map becomes empty.
+  VariantMap(VariantMap&& other) {
+    operator=(std::forward<VariantMap>(other));
+  }
+
+  // Move the existing map's key/value pairs into this one. The other map becomes empty.
+  VariantMap& operator=(VariantMap&& other) {
+    if (this != &other) {
+      Clear();
+      storage_map_.swap(other.storage_map_);
+      other.storage_map_.clear();
+    }
+    return *this;
+  }
+
+  ~VariantMap() {
+    DeleteStoredValues();
+  }
+
+ private:
+  void InitializeParameters() {}
+
+  template <typename TK, typename TValue, typename ... Rest>
+  void InitializeParameters(const TK& key, const TValue& value, const Rest& ... rest) {
+    static_assert(
+        std::is_same<TK, TKey<TValue>>::value, "The 0th/2nd/4th/etc parameters must be a key");
+
+    const TKey<TValue>& key_refined = key;
+
+    Set(key_refined, value);
+    InitializeParameters(rest...);
+  }
+
+  // Custom key comparator for std::map, needed since we are storing raw pointers as the keys.
+  struct KeyComparator {
+    bool operator()(const detail::VariantMapKeyRaw* lhs,
+                    const detail::VariantMapKeyRaw* rhs) const {
+      if (lhs == nullptr) {
+        return lhs != rhs;
+      }
+
+      return lhs->Compare(rhs);
+    }
+  };
+
+  // Map of key pointers to value pointers. Pointers are never null.
+  using StorageMap = std::map<const detail::VariantMapKeyRaw*, void*, KeyComparator>;
+
+  template <typename TValue>
+  typename StorageMap::iterator GetKeyValueIterator(const TKey<TValue>& key) {
+    StaticAssertKeyType<TValue>();
+
+    const TKey<TValue>* key_ptr = &key;
+    const detail::VariantMapKeyRaw* raw_ptr = key_ptr;
+    return storage_map_.find(raw_ptr);
+  }
+
+  template <typename TValue>
+  typename StorageMap::const_iterator GetKeyValueIterator(const TKey<TValue>& key) const {
+    StaticAssertKeyType<TValue>();
+
+    const TKey<TValue>* key_ptr = &key;
+    const detail::VariantMapKeyRaw* raw_ptr = key_ptr;
+    return storage_map_.find(raw_ptr);
+  }
+
+  template <typename TValue>
+  TValue* GetValuePtr(const TKey<TValue>& key) {
+    return const_cast<TValue*>(GetValueConstPtr(key));
+  }
+
+  template <typename TValue>
+  const TValue* GetValuePtr(const TKey<TValue>& key) const {
+    return GetValueConstPtr(key);
+  }
+
+  template <typename TValue>
+  const TValue* GetValueConstPtr(const TKey<TValue>& key) const {
+    auto&& it = GetKeyValueIterator(key);
+    if (it == storage_map_.end()) {
+      return nullptr;
+    }
+
+    return reinterpret_cast<const TValue*>(it->second);
+  }
+
+  template <typename TValue>
+  static void StaticAssertKeyType() {
+    static_assert(std::is_base_of<VariantMapKey<TValue>, TKey<TValue>>::value,
+                  "The provided key type (TKey) must be a subclass of VariantMapKey");
+  }
+
+  void DeleteStoredValues() {
+    for (auto&& kv_pair : storage_map_) {
+      kv_pair.first->ValueDelete(kv_pair.second);
+      delete kv_pair.first;
+    }
+  }
+
+  StorageMap storage_map_;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_VARIANT_MAP_H_
diff --git a/runtime/base/variant_map_test.cc b/runtime/base/variant_map_test.cc
new file mode 100644
index 0000000..827de46
--- /dev/null
+++ b/runtime/base/variant_map_test.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "variant_map.h"
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+                                        reinterpret_cast<void*>(NULL));
+
+namespace art {
+
+namespace {
+  template <typename TValue>
+  struct FruitMapKey : VariantMapKey<TValue> {
+    FruitMapKey() {}
+  };
+
+  struct FruitMap : VariantMap<FruitMap, FruitMapKey> {
+    // This 'using' line is necessary to inherit the variadic constructor.
+    using VariantMap<FruitMap, FruitMapKey>::VariantMap;
+
+    // Make the next '4' usages of Key slightly shorter to type.
+    template <typename TValue>
+    using Key = FruitMapKey<TValue>;
+
+    static const Key<int> Apple;
+    static const Key<double> Orange;
+  };
+
+  const FruitMap::Key<int> FruitMap::Apple;
+  const FruitMap::Key<double> FruitMap::Orange;
+}  // namespace
+
+TEST(VariantMaps, BasicReadWrite) {
+  FruitMap fm;
+
+  EXPECT_NULL(fm.Get(FruitMap::Apple));
+  EXPECT_FALSE(fm.Exists(FruitMap::Apple));
+  EXPECT_NULL(fm.Get(FruitMap::Orange));
+  EXPECT_FALSE(fm.Exists(FruitMap::Orange));
+
+  fm.Set(FruitMap::Apple, 1);
+  EXPECT_NULL(fm.Get(FruitMap::Orange));
+  EXPECT_EQ(1, *fm.Get(FruitMap::Apple));
+  EXPECT_TRUE(fm.Exists(FruitMap::Apple));
+
+  fm.Set(FruitMap::Apple, 5);
+  EXPECT_NULL(fm.Get(FruitMap::Orange));
+  EXPECT_EQ(5, *fm.Get(FruitMap::Apple));
+  EXPECT_TRUE(fm.Exists(FruitMap::Apple));
+
+  fm.Set(FruitMap::Orange, 555.0);
+  EXPECT_EQ(5, *fm.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(555.0, *fm.Get(FruitMap::Orange));
+  EXPECT_EQ(size_t(2), fm.Size());
+
+  fm.Remove(FruitMap::Apple);
+  EXPECT_FALSE(fm.Exists(FruitMap::Apple));
+
+  fm.Clear();
+  EXPECT_EQ(size_t(0), fm.Size());
+  EXPECT_FALSE(fm.Exists(FruitMap::Orange));
+}
+
+TEST(VariantMaps, RuleOfFive) {
+  // Test empty constructor
+  FruitMap fmEmpty;
+  EXPECT_EQ(size_t(0), fmEmpty.Size());
+
+  // Test empty constructor
+  FruitMap fmFilled;
+  fmFilled.Set(FruitMap::Apple, 1);
+  fmFilled.Set(FruitMap::Orange, 555.0);
+  EXPECT_EQ(size_t(2), fmFilled.Size());
+
+  // Test copy constructor
+  FruitMap fmEmptyCopy(fmEmpty);
+  EXPECT_EQ(size_t(0), fmEmptyCopy.Size());
+
+  // Test copy constructor
+  FruitMap fmFilledCopy(fmFilled);
+  EXPECT_EQ(size_t(2), fmFilledCopy.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmFilledCopy.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmFilledCopy.Get(FruitMap::Orange));
+
+  // Test operator=
+  FruitMap fmFilledCopy2;
+  fmFilledCopy2 = fmFilled;
+  EXPECT_EQ(size_t(2), fmFilledCopy2.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmFilledCopy2.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmFilledCopy2.Get(FruitMap::Orange));
+
+  // Test move constructor
+  FruitMap fmMoved(std::move(fmFilledCopy));
+  EXPECT_EQ(size_t(0), fmFilledCopy.Size());
+  EXPECT_EQ(size_t(2), fmMoved.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmMoved.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmMoved.Get(FruitMap::Orange));
+
+  // Test operator= move
+  FruitMap fmMoved2;
+  fmMoved2.Set(FruitMap::Apple, 12345);  // This value will be clobbered after the move
+
+  fmMoved2 = std::move(fmFilledCopy2);
+  EXPECT_EQ(size_t(0), fmFilledCopy2.Size());
+  EXPECT_EQ(size_t(2), fmMoved2.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmMoved2.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmMoved2.Get(FruitMap::Orange));
+}
+
+TEST(VariantMaps, VariadicConstructors) {
+  // Variadic constructor, 1 kv/pair
+  FruitMap fmApple(FruitMap::Apple, 12345);
+  EXPECT_EQ(size_t(1), fmApple.Size());
+  EXPECT_EQ(12345, *fmApple.Get(FruitMap::Apple));
+
+  // Variadic constructor, 2 kv/pair
+  FruitMap fmAppleAndOrange(FruitMap::Apple,   12345,
+                            FruitMap::Orange,  100.0);
+  EXPECT_EQ(size_t(2), fmAppleAndOrange.Size());
+  EXPECT_EQ(12345, *fmAppleAndOrange.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(100.0, *fmAppleAndOrange.Get(FruitMap::Orange));
+}
+
+TEST(VariantMaps, ReleaseOrDefault) {
+  FruitMap fmAppleAndOrange(FruitMap::Apple,   12345,
+                            FruitMap::Orange,  100.0);
+
+  int apple = fmAppleAndOrange.ReleaseOrDefault(FruitMap::Apple);
+  EXPECT_EQ(12345, apple);
+
+  // Releasing will also remove the Apple key.
+  EXPECT_EQ(size_t(1), fmAppleAndOrange.Size());
+
+  // Releasing again yields a default value.
+  int apple2 = fmAppleAndOrange.ReleaseOrDefault(FruitMap::Apple);
+  EXPECT_EQ(0, apple2);
+}
+
+TEST(VariantMaps, GetOrDefault) {
+  FruitMap fm(FruitMap::Apple,   12345);
+
+  // Apple gives the expected value we set.
+  int apple = fm.GetOrDefault(FruitMap::Apple);
+  EXPECT_EQ(12345, apple);
+
+  // Map is still 1.
+  EXPECT_EQ(size_t(1), fm.Size());
+
+  // Orange gives back a default value, since it's not in the map.
+  double orange = fm.GetOrDefault(FruitMap::Orange);
+  EXPECT_DOUBLE_EQ(0.0, orange);
+}
+
+}  // namespace art
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index 4fe3852..93062a7 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -82,7 +82,7 @@
           CHECK(stack_mask.LoadBit(dex_register_map.GetValue(reg) >> 2));
           break;
         case DexRegisterMap::kInRegister:
-          CHECK_NE(register_mask & dex_register_map.GetValue(reg), 0u);
+          CHECK_NE(register_mask & (1 << dex_register_map.GetValue(reg)), 0u);
           break;
         case DexRegisterMap::kInFpuRegister:
           // In Fpu register, should not be a reference.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 6aab632..25750bb 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -21,6 +21,7 @@
 #include <memory>
 #include <queue>
 #include <string>
+#include <unistd.h>
 #include <utility>
 #include <vector>
 
@@ -237,9 +238,7 @@
       log_new_dex_caches_roots_(false),
       log_new_class_table_roots_(false),
       intern_table_(intern_table),
-      portable_resolution_trampoline_(nullptr),
       quick_resolution_trampoline_(nullptr),
-      portable_imt_conflict_trampoline_(nullptr),
       quick_imt_conflict_trampoline_(nullptr),
       quick_generic_jni_trampoline_(nullptr),
       quick_to_interpreter_bridge_trampoline_(nullptr),
@@ -247,7 +246,7 @@
   memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
 }
 
-void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class_path) {
+void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
   VLOG(startup) << "ClassLinker::Init";
   CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it.";
 
@@ -378,7 +377,8 @@
   Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle(
     AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize())));
   CHECK(java_lang_reflect_ArtMethod.Get() != nullptr);
-  java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
+  size_t pointer_size = GetInstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
+  java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(pointer_size));
   SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get());
   java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
   mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get());
@@ -406,9 +406,10 @@
   // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
   // these roots.
   CHECK_NE(0U, boot_class_path.size());
-  for (const DexFile* dex_file : boot_class_path) {
-    CHECK(dex_file != nullptr);
+  for (auto& dex_file : boot_class_path) {
+    CHECK(dex_file.get() != nullptr);
     AppendToBootClassPath(self, *dex_file);
+    opened_dex_files_.push_back(std::move(dex_file));
   }
 
   // now we can use FindSystemClass
@@ -575,6 +576,23 @@
                FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
   mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
 
+  // Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly
+  // initialized.
+  {
+    const DexFile& dex_file = java_lang_Object->GetDexFile();
+    const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
+    CHECK(void_string_id != nullptr);
+    uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id);
+    const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index);
+    CHECK(void_type_id != nullptr);
+    uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
+    // Now we resolve void type so the dex cache contains it. We use java.lang.Object class
+    // as referrer so the used dex cache is core's one.
+    mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get());
+    CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
+    self->AssertNoPendingException();
+  }
+
   FinishInit(self);
 
   VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
@@ -704,7 +722,14 @@
     argv.push_back(compiler_options[i].c_str());
   }
 
-  return Exec(argv, error_msg);
+  if (!Exec(argv, error_msg)) {
+    // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
+    // died. Ignore unlink failure, propagate the original error.
+    TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
+    return false;
+  }
+
+  return true;
 }
 
 const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
@@ -771,7 +796,7 @@
                                          const uint32_t* dex_location_checksum,
                                          bool generated,
                                          std::vector<std::string>* error_msgs,
-                                         std::vector<const DexFile*>* dex_files) {
+                                         std::vector<std::unique_ptr<const DexFile>>* dex_files) {
   if (oat_file == nullptr) {
     return false;
   }
@@ -818,12 +843,12 @@
     }
 
     if (success) {
-      const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
-      if (dex_file == nullptr) {
+      std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+      if (dex_file.get() == nullptr) {
         success = false;
         error_msgs->push_back(error_msg);
       } else {
-        dex_files->push_back(dex_file);
+        dex_files->push_back(std::move(dex_file));
       }
     }
 
@@ -841,14 +866,7 @@
   if (success) {
     return true;
   } else {
-    // Free all the dex files we have loaded.
-    auto it = dex_files->begin() + old_size;
-    auto it_end = dex_files->end();
-    for (; it != it_end; it++) {
-      delete *it;
-    }
-    dex_files->erase(dex_files->begin() + old_size, it_end);
-
+    dex_files->erase(dex_files->begin() + old_size, dex_files->end());
     return false;
   }
 }
@@ -859,7 +877,7 @@
 // multidex ahead of time.
 bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
                                       std::vector<std::string>* error_msgs,
-                                      std::vector<const DexFile*>* dex_files) {
+                                      std::vector<std::unique_ptr<const DexFile>>* dex_files) {
   // 1) Check whether we have an open oat file.
   // This requires a dex checksum, use the "primary" one.
   uint32_t dex_location_checksum;
@@ -1209,15 +1227,15 @@
                                 error_msg->c_str());
       return false;
     }
-    dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
+    dex_file = oat_dex_file->OpenDexFile(error_msg);
   } else {
     bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
                                                  kRuntimeISA, error_msg);
     if (!verified) {
       return false;
     }
-    dex_file.reset(oat_file->GetOatDexFile(dex_location,
-                                           dex_location_checksum)->OpenDexFile(error_msg));
+    dex_file = oat_file->GetOatDexFile(dex_location,
+                                       dex_location_checksum)->OpenDexFile(error_msg);
   }
   return dex_file.get() != nullptr;
 }
@@ -1601,20 +1619,19 @@
                        error_msg);
 }
 
-static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
   ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
-
   DCHECK(obj != nullptr);
   DCHECK(class_linker != nullptr);
+  size_t pointer_size = class_linker->image_pointer_size_;
 
   if (obj->IsArtMethod()) {
     mirror::ArtMethod* method = obj->AsArtMethod();
     if (!method->IsNative()) {
-      method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
+      method->SetEntryPointFromInterpreterPtrSize(artInterpreterToInterpreterBridge, pointer_size);
       if (method != Runtime::Current()->GetResolutionMethod()) {
-        method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-        method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
+        method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
+                                                          pointer_size);
       }
     }
   }
@@ -1635,9 +1652,7 @@
   const char* image_file_location = oat_file.GetOatHeader().
       GetStoreValueByKey(OatHeader::kImageLocationKey);
   CHECK(image_file_location == nullptr || *image_file_location == 0);
-  portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline();
   quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
-  portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
   quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
   quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
   quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
@@ -1665,8 +1680,8 @@
                                                                      nullptr);
     CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
     std::string error_msg;
-    const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
-    if (dex_file == nullptr) {
+    std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+    if (dex_file.get() == nullptr) {
       LOG(FATAL) << "Failed to open dex file " << dex_file_location
                  << " from within oat file " << oat_file.GetLocation()
                  << " error '" << error_msg << "'";
@@ -1675,7 +1690,8 @@
 
     CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
 
-    AppendToBootClassPath(*dex_file, dex_cache);
+    AppendToBootClassPath(*dex_file.get(), dex_cache);
+    opened_dex_files_.push_back(std::move(dex_file));
   }
 
   // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
@@ -1697,8 +1713,8 @@
   }
 
   // Set entry point to interpreter if in InterpretOnly mode.
-  if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
-    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  Runtime* runtime = Runtime::Current();
+  if (!runtime->IsCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
     heap->VisitObjects(InitFromImageInterpretOnlyCallback, this);
   }
 
@@ -1732,23 +1748,23 @@
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
   if ((flags & kVisitRootFlagAllRoots) != 0) {
     for (GcRoot<mirror::Class>& root : class_table_) {
-      root.VisitRoot(callback, arg, 0, kRootStickyClass);
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
     }
     for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
-      root.VisitRoot(callback, arg, 0, kRootStickyClass);
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
     }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
       mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
-      root.VisitRoot(callback, arg, 0, kRootStickyClass);
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
       mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
       if (UNLIKELY(new_ref != old_ref)) {
         // Uh ohes, GC moved a root in the log. Need to search the class_table and update the
         // corresponding object. This is slow, but luckily for us, this may only happen with a
         // concurrent moving GC.
         auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
-        class_table_.Erase(it);
-        class_table_.Insert(GcRoot<mirror::Class>(new_ref));
+        DCHECK(it != class_table_.end());
+        *it = GcRoot<mirror::Class>(new_ref);
       }
     }
   }
@@ -1768,17 +1784,17 @@
 // reinit references to when reinitializing a ClassLinker from a
 // mapped image.
 void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
-  class_roots_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   Thread* self = Thread::Current();
   {
     ReaderMutexLock mu(self, dex_lock_);
     if ((flags & kVisitRootFlagAllRoots) != 0) {
       for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
-        dex_cache.VisitRoot(callback, arg, 0, kRootVMInternal);
+        dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
       }
     } else if ((flags & kVisitRootFlagNewRoots) != 0) {
       for (size_t index : new_dex_cache_roots_) {
-        dex_caches_[index].VisitRoot(callback, arg, 0, kRootVMInternal);
+        dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal));
       }
     }
     if ((flags & kVisitRootFlagClearRootLog) != 0) {
@@ -1791,12 +1807,10 @@
     }
   }
   VisitClassRoots(callback, arg, flags);
-  array_iftable_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   DCHECK(!array_iftable_.IsNull());
   for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
-    if (!find_array_class_cache_[i].IsNull()) {
-      find_array_class_cache_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
-    }
+    find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   }
 }
 
@@ -1907,7 +1921,6 @@
   mirror::ShortArray::ResetArrayClass();
   mirror::Throwable::ResetClass();
   mirror::StackTraceElement::ResetClass();
-  STLDeleteElements(&boot_class_path_);
   STLDeleteElements(&oat_files_);
 }
 
@@ -2341,6 +2354,18 @@
 
   Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));
 
+  // Instrumentation may have updated entrypoints for all methods of all
+  // classes. However it could not update methods of this class while we
+  // were loading it. Now the class is resolved, we can update entrypoints
+  // as required by instrumentation.
+  if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+    // We must be in the kRunnable state to prevent instrumentation from
+    // suspending all threads to update entrypoints while we are doing it
+    // for this class.
+    DCHECK_EQ(self->GetState(), kRunnable);
+    Runtime::Current()->GetInstrumentation()->InstallStubsForClass(new_class_h.Get());
+  }
+
   /*
    * We send CLASS_PREPARE events to the debugger from here.  The
    * definition of "preparation" is creating the static fields for a
@@ -2502,9 +2527,6 @@
     if (method->IsNative()) {
       // No code and native? Use generic trampoline.
       result = GetQuickGenericJniStub();
-    } else if (method->IsPortableCompiled()) {
-      // No code? Do we expect portable code?
-      result = GetQuickToPortableBridge();
     } else {
       // No code? You must mean to go into the interpreter.
       result = GetQuickToInterpreterBridge();
@@ -2513,36 +2535,6 @@
   return result;
 }
 
-const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method,
-                                               bool* have_portable_code) {
-  CHECK(!method->IsAbstract()) << PrettyMethod(method);
-  *have_portable_code = false;
-  if (method->IsProxyMethod()) {
-    return GetPortableProxyInvokeHandler();
-  }
-  bool found;
-  OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
-  const void* result = nullptr;
-  const void* quick_code = nullptr;
-  if (found) {
-    result = oat_method.GetPortableCode();
-    quick_code = oat_method.GetQuickCode();
-  }
-
-  if (result == nullptr) {
-    if (quick_code == nullptr) {
-      // No code? You must mean to go into the interpreter.
-      result = GetPortableToInterpreterBridge();
-    } else {
-      // No code? But there's quick code, so use a bridge.
-      result = GetPortableToQuickBridge();
-    }
-  } else {
-    *have_portable_code = true;
-  }
-  return result;
-}
-
 const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) {
   if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
     return nullptr;
@@ -2552,15 +2544,6 @@
   return found ? oat_method.GetQuickCode() : nullptr;
 }
 
-const void* ClassLinker::GetOatMethodPortableCodeFor(mirror::ArtMethod* method) {
-  if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
-    return nullptr;
-  }
-  bool found;
-  OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
-  return found ? oat_method.GetPortableCode() : nullptr;
-}
-
 const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
                                             uint32_t method_idx) {
   bool found;
@@ -2572,34 +2555,15 @@
   return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
 }
 
-const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
-                                               uint32_t method_idx) {
-  bool found;
-  OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
-  if (!found) {
-    return nullptr;
-  }
-  uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
-  return oat_class.GetOatMethod(oat_method_idx).GetPortableCode();
-}
-
 // Returns true if the method must run with interpreter, false otherwise.
-static bool NeedsInterpreter(
-    mirror::ArtMethod* method, const void* quick_code, const void* portable_code)
+static bool NeedsInterpreter(mirror::ArtMethod* method, const void* quick_code)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if ((quick_code == nullptr) && (portable_code == nullptr)) {
+  if (quick_code == nullptr) {
     // No code: need interpreter.
     // May return true for native code, in the case of generic JNI
     // DCHECK(!method->IsNative());
     return true;
   }
-#ifdef ART_SEA_IR_MODE
-  ScopedObjectAccess soa(Thread::Current());
-  if (std::string::npos != PrettyMethod(method).find("fibonacci")) {
-    LOG(INFO) << "Found " << PrettyMethod(method);
-    return false;
-  }
-#endif
   // If interpreter mode is enabled, every method (except native and proxy) must
   // be run with interpreter.
   return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&
@@ -2642,37 +2606,22 @@
       // Only update static methods.
       continue;
     }
-    const void* portable_code = nullptr;
     const void* quick_code = nullptr;
     if (has_oat_class) {
       OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
-      portable_code = oat_method.GetPortableCode();
       quick_code = oat_method.GetQuickCode();
     }
-    const bool enter_interpreter = NeedsInterpreter(method, quick_code, portable_code);
-    bool have_portable_code = false;
+    const bool enter_interpreter = NeedsInterpreter(method, quick_code);
     if (enter_interpreter) {
       // Use interpreter entry point.
       // Check whether the method is native, in which case it's generic JNI.
-      if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) {
+      if (quick_code == nullptr && method->IsNative()) {
         quick_code = GetQuickGenericJniStub();
-        portable_code = GetPortableToQuickBridge();
       } else {
-        portable_code = GetPortableToInterpreterBridge();
         quick_code = GetQuickToInterpreterBridge();
       }
-    } else {
-      if (portable_code == nullptr) {
-        portable_code = GetPortableToQuickBridge();
-      } else {
-        have_portable_code = true;
-      }
-      if (quick_code == nullptr) {
-        quick_code = GetQuickToPortableBridge();
-      }
     }
-    runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code, portable_code,
-                                                     have_portable_code);
+    runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code);
   }
   // Ignore virtual methods on the iterator.
 }
@@ -2687,7 +2636,6 @@
   }
   // Method shouldn't have already been linked.
   DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-  DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
   if (oat_class != nullptr) {
     // Every kind of method should at least get an invoke stub from the oat_method.
     // non-abstract methods also get their code pointers.
@@ -2697,8 +2645,7 @@
 
   // Install entry point from interpreter.
   bool enter_interpreter = NeedsInterpreter(method.Get(),
-                                            method->GetEntryPointFromQuickCompiledCode(),
-                                            method->GetEntryPointFromPortableCompiledCode());
+                                            method->GetEntryPointFromQuickCompiledCode());
   if (enter_interpreter && !method->IsNative()) {
     method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
   } else {
@@ -2707,33 +2654,21 @@
 
   if (method->IsAbstract()) {
     method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-    method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
     return;
   }
 
-  bool have_portable_code = false;
   if (method->IsStatic() && !method->IsConstructor()) {
     // For static methods excluding the class initializer, install the trampoline.
     // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
     // after initializing class (see ClassLinker::InitializeClass method).
     method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
-    method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub());
   } else if (enter_interpreter) {
     if (!method->IsNative()) {
       // Set entry point from compiled code if there's no code or in interpreter only mode.
       method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-      method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
     } else {
       method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
-      method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
     }
-  } else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
-    DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-    have_portable_code = true;
-    method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
-  } else {
-    DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
-    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
   }
 
   if (method->IsNative()) {
@@ -2748,12 +2683,6 @@
       DCHECK(IsQuickGenericJniStub(entry_point) || IsQuickResolutionStub(entry_point));
     }
   }
-
-  // Allow instrumentation its chance to hijack code.
-  runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
-                                                   method->GetEntryPointFromQuickCompiledCode(),
-                                                   method->GetEntryPointFromPortableCompiledCode(),
-                                                   have_portable_code);
 }
 
 
@@ -2769,9 +2698,6 @@
   CHECK(descriptor != nullptr);
 
   klass->SetClass(GetClassRoot(kJavaLangClass));
-  if (kUseBakerOrBrooksReadBarrier) {
-    klass->AssertReadBarrierPointer();
-  }
   uint32_t access_flags = dex_class_def.GetJavaAccessFlags();
   CHECK_EQ(access_flags & ~kAccJavaFlagsMask, 0U);
   klass->SetAccessFlags(access_flags);
@@ -3708,6 +3634,19 @@
     return false;
   }
 
+  // We may be running with a preopted oat file but without image. In this case,
+  // we don't skip verification of preverified classes to ensure we initialize
+  // dex caches with all types resolved during verification.
+  // We need to trust image classes, as these might be coming out of a pre-opted, quickened boot
+  // image (that we just failed loading), and the verifier can't be run on quickened opcodes when
+  // the runtime isn't started. On the other hand, app classes can be re-verified even if they are
+  // already pre-opted, as then the runtime is started.
+  if (!Runtime::Current()->IsCompiler() &&
+      !Runtime::Current()->GetHeap()->HasImageSpace() &&
+      klass->GetClassLoader() != nullptr) {
+    return false;
+  }
+
   uint16_t class_def_index = klass->GetDexClassDefIndex();
   oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
   if (oat_file_class_status == mirror::Class::kStatusVerified ||
@@ -4047,7 +3986,6 @@
   // At runtime the method looks like a reference and argument saving method, clone the code
   // related parameters from this method.
   method->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
-  method->SetEntryPointFromPortableCompiledCode(GetPortableProxyInvokeHandler());
   method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
 
   return method;
@@ -4283,6 +4221,14 @@
       WrapExceptionInInitializer(klass);
       klass->SetStatus(mirror::Class::kStatusError, self);
       success = false;
+    } else if (Runtime::Current()->IsTransactionAborted()) {
+      // The exception thrown when the transaction aborted has been caught and cleared
+      // so we need to throw it again now.
+      LOG(WARNING) << "Return from class initializer of " << PrettyDescriptor(klass.Get())
+                   << " without exception while transaction was aborted: re-throw it now.";
+      Runtime::Current()->ThrowInternalErrorForAbortedTransaction(self);
+      klass->SetStatus(mirror::Class::kStatusError, self);
+      success = false;
     } else {
       RuntimeStats* global_stats = Runtime::Current()->GetStats();
       RuntimeStats* thread_stats = self->GetStats();
@@ -4352,7 +4298,9 @@
   {
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType()));
-    if (UNLIKELY(method2->GetReturnType() != return_type.Get())) {
+    mirror::Class* other_return_type = method2->GetReturnType();
+    // NOTE: return_type.Get() must be sequenced after method2->GetReturnType().
+    if (UNLIKELY(other_return_type != return_type.Get())) {
       return false;
     }
   }
@@ -4368,11 +4316,13 @@
     return false;
   }
   for (uint32_t i = 0; i < num_types; ++i) {
-    mirror::Class* param_type =
-        method1->GetClassFromTypeIndex(types1->GetTypeItem(i).type_idx_, true);
+    StackHandleScope<1> hs(self);
+    Handle<mirror::Class> param_type(hs.NewHandle(
+        method1->GetClassFromTypeIndex(types1->GetTypeItem(i).type_idx_, true)));
     mirror::Class* other_param_type =
         method2->GetClassFromTypeIndex(types2->GetTypeItem(i).type_idx_, true);
-    if (UNLIKELY(param_type != other_param_type)) {
+    // NOTE: param_type.Get() must be sequenced after method2->GetClassFromTypeIndex(...).
+    if (UNLIKELY(param_type.Get() != other_param_type)) {
       return false;
     }
   }
@@ -4551,6 +4501,171 @@
   return true;
 }
 
+static void CountMethodsAndFields(ClassDataItemIterator& dex_data,
+                                  size_t* virtual_methods,
+                                  size_t* direct_methods,
+                                  size_t* static_fields,
+                                  size_t* instance_fields) {
+  *virtual_methods = *direct_methods = *static_fields = *instance_fields = 0;
+
+  while (dex_data.HasNextStaticField()) {
+    dex_data.Next();
+    (*static_fields)++;
+  }
+  while (dex_data.HasNextInstanceField()) {
+    dex_data.Next();
+    (*instance_fields)++;
+  }
+  while (dex_data.HasNextDirectMethod()) {
+    (*direct_methods)++;
+    dex_data.Next();
+  }
+  while (dex_data.HasNextVirtualMethod()) {
+    (*virtual_methods)++;
+    dex_data.Next();
+  }
+  DCHECK(!dex_data.HasNext());
+}
+
+static void DumpClass(std::ostream& os,
+                      const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+                      const char* suffix) {
+  ClassDataItemIterator dex_data(dex_file, dex_file.GetClassData(dex_class_def));
+  os << dex_file.GetClassDescriptor(dex_class_def) << suffix << ":\n";
+  os << " Static fields:\n";
+  while (dex_data.HasNextStaticField()) {
+    const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+    dex_data.Next();
+  }
+  os << " Instance fields:\n";
+  while (dex_data.HasNextInstanceField()) {
+    const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
+    dex_data.Next();
+  }
+  os << " Direct methods:\n";
+  while (dex_data.HasNextDirectMethod()) {
+    const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+    dex_data.Next();
+  }
+  os << " Virtual methods:\n";
+  while (dex_data.HasNextVirtualMethod()) {
+    const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
+    os << "  " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
+    dex_data.Next();
+  }
+}
+
+static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+                               const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) {
+  std::ostringstream os;
+  DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
+  DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
+  return os.str();
+}
+
+
+// Very simple structural check on whether the classes match. Only compares the number of
+// methods and fields.
+static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
+                                  const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2,
+                                  std::string* error_msg) {
+  ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
+  ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
+
+  // Counters for current dex file.
+  size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
+  CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1,
+                        &dex_instance_fields1);
+  // Counters for compile-time dex file.
+  size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
+  CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2,
+                        &dex_instance_fields2);
+
+  if (dex_virtual_methods1 != dex_virtual_methods2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s", dex_virtual_methods1,
+                              dex_virtual_methods2, class_dump.c_str());
+    return false;
+  }
+  if (dex_direct_methods1 != dex_direct_methods2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s", dex_direct_methods1,
+                              dex_direct_methods2, class_dump.c_str());
+    return false;
+  }
+  if (dex_static_fields1 != dex_static_fields2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s", dex_static_fields1,
+                              dex_static_fields2, class_dump.c_str());
+    return false;
+  }
+  if (dex_instance_fields1 != dex_instance_fields2) {
+    std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
+    *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s", dex_instance_fields1,
+                              dex_instance_fields2, class_dump.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+// Checks whether a the super-class changed from what we had at compile-time. This would
+// invalidate quickening.
+static bool CheckSuperClassChange(Handle<mirror::Class> klass,
+                                  const DexFile& dex_file,
+                                  const DexFile::ClassDef& class_def,
+                                  mirror::Class* super_class)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Check for unexpected changes in the superclass.
+  // Quick check 1) is the super_class class-loader the boot class loader? This always has
+  // precedence.
+  if (super_class->GetClassLoader() != nullptr &&
+      // Quick check 2) different dex cache? Breaks can only occur for different dex files,
+      // which is implied by different dex cache.
+      klass->GetDexCache() != super_class->GetDexCache()) {
+    // Now comes the expensive part: things can be broken if (a) the klass' dex file has a
+    // definition for the super-class, and (b) the files are in separate oat files. The oat files
+    // are referenced from the dex file, so do (b) first. Only relevant if we have oat files.
+    const OatFile* class_oat_file = dex_file.GetOatFile();
+    if (class_oat_file != nullptr) {
+      const OatFile* loaded_super_oat_file = super_class->GetDexFile().GetOatFile();
+      if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) {
+        // Now check (a).
+        const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_);
+        if (super_class_def != nullptr) {
+          // Uh-oh, we found something. Do our check.
+          std::string error_msg;
+          if (!SimpleStructuralCheck(dex_file, *super_class_def,
+                                     super_class->GetDexFile(), *super_class->GetClassDef(),
+                                     &error_msg)) {
+            // Print a warning to the log. This exception might be caught, e.g., as common in test
+            // drivers. When the class is later tried to be used, we re-throw a new instance, as we
+            // only save the type of the exception.
+            LOG(WARNING) << "Incompatible structural change detected: " <<
+                StringPrintf(
+                    "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+                    PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+                    class_oat_file->GetLocation().c_str(),
+                    loaded_super_oat_file->GetLocation().c_str(),
+                    error_msg.c_str());
+            ThrowIncompatibleClassChangeError(klass.Get(),
+                "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
+                PrettyType(super_class_def->class_idx_, dex_file).c_str(),
+                class_oat_file->GetLocation().c_str(),
+                loaded_super_oat_file->GetLocation().c_str(),
+                error_msg.c_str());
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
 bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {
   CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus());
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
@@ -4570,6 +4685,11 @@
     }
     CHECK(super_class->IsResolved());
     klass->SetSuperClass(super_class);
+
+    if (!CheckSuperClassChange(klass, dex_file, class_def, super_class)) {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      return false;
+    }
   }
   const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
   if (interfaces != nullptr) {
@@ -5366,7 +5486,8 @@
     klass->SetNumReferenceInstanceFields(num_reference_fields);
     if (!klass->IsVariableSize()) {
       if (klass->DescriptorEquals("Ljava/lang/reflect/ArtMethod;")) {
-        klass->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
+        size_t pointer_size = GetInstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
+        klass->SetObjectSize(mirror::ArtMethod::InstanceSize(pointer_size));
       } else {
         std::string temp;
         DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
@@ -5773,8 +5894,7 @@
   }
 }
 
-static OatFile::OatMethod CreateOatMethod(const void* code, bool is_portable) {
-  CHECK_EQ(kUsePortableCompiler, is_portable);
+static OatFile::OatMethod CreateOatMethod(const void* code) {
   CHECK(code != nullptr);
   const uint8_t* base = reinterpret_cast<const uint8_t*>(code);  // Base of data points at code.
   base -= sizeof(void*);  // Move backward so that code_offset != 0.
@@ -5782,21 +5902,11 @@
   return OatFile::OatMethod(base, code_offset);
 }
 
-bool ClassLinker::IsPortableResolutionStub(const void* entry_point) const {
-  return (entry_point == GetPortableResolutionStub()) ||
-      (portable_resolution_trampoline_ == entry_point);
-}
-
 bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
   return (entry_point == GetQuickResolutionStub()) ||
       (quick_resolution_trampoline_ == entry_point);
 }
 
-bool ClassLinker::IsPortableToInterpreterBridge(const void* entry_point) const {
-  return (entry_point == GetPortableToInterpreterBridge());
-  // TODO: portable_to_interpreter_bridge_trampoline_ == entry_point;
-}
-
 bool ClassLinker::IsQuickToInterpreterBridge(const void* entry_point) const {
   return (entry_point == GetQuickToInterpreterBridge()) ||
       (quick_to_interpreter_bridge_trampoline_ == entry_point);
@@ -5811,32 +5921,22 @@
   return GetQuickGenericJniStub();
 }
 
-void ClassLinker::SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code,
-                                               bool is_portable) const {
-  OatFile::OatMethod oat_method = CreateOatMethod(method_code, is_portable);
+void ClassLinker::SetEntryPointsToCompiledCode(mirror::ArtMethod* method,
+                                               const void* method_code) const {
+  OatFile::OatMethod oat_method = CreateOatMethod(method_code);
   oat_method.LinkMethod(method);
   method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
-  // Create bridges to transition between different kinds of compiled bridge.
-  if (method->GetEntryPointFromPortableCompiledCode() == nullptr) {
-    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
-  } else {
-    CHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-    method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
-    method->SetIsPortableCompiled();
-  }
 }
 
 void ClassLinker::SetEntryPointsToInterpreter(mirror::ArtMethod* method) const {
   if (!method->IsNative()) {
     method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
-    method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
     method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
   } else {
     const void* quick_method_code = GetQuickGenericJniStub();
-    OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code, false);
+    OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code);
     oat_method.LinkMethod(method);
     method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
-    method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
   }
 }
 
@@ -5963,4 +6063,34 @@
   return ComputeModifiedUtf8Hash(descriptor);
 }
 
+bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) {
+  // Non-image methods don't use direct code pointer.
+  if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) {
+    return false;
+  }
+  if (m->IsPrivate()) {
+    // The method can only be called inside its own oat file. Therefore it won't be called using
+    // its direct code if the oat file has been compiled in PIC mode.
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile();
+    const OatFile::OatDexFile* oat_dex_file = class_linker->FindOpenedOatDexFileForDexFile(dex_file);
+    if (oat_dex_file == nullptr) {
+      // No oat file: the method has not been compiled.
+      return false;
+    }
+    const OatFile* oat_file = oat_dex_file->GetOatFile();
+    return oat_file != nullptr && !oat_file->IsPic();
+  } else {
+    // The method can be called outside its own oat file. Therefore it won't be called using its
+    // direct code pointer only if all loaded oat files have been compiled in PIC mode.
+    ReaderMutexLock mu(Thread::Current(), dex_lock_);
+    for (const OatFile* oat_file : oat_files_) {
+      if (!oat_file->IsPic()) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index b78d0b5..6570c5f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -105,7 +105,7 @@
   ~ClassLinker();
 
   // Initialize class linker by bootstraping from dex files.
-  void InitWithoutImage(const std::vector<const DexFile*>& boot_class_path)
+  void InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Initialize class linker from one or more images.
@@ -324,7 +324,7 @@
   // (if multidex) into the given vector.
   bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
                            std::vector<std::string>* error_msgs,
-                           std::vector<const DexFile*>* dex_files)
+                           std::vector<std::unique_ptr<const DexFile>>* dex_files)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
   // Returns true if the given oat file has the same image checksum as the image it is paired with.
@@ -392,22 +392,16 @@
   // Get the oat code for a method when its class isn't yet initialized
   const void* GetQuickOatCodeFor(mirror::ArtMethod* method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const void* GetPortableOatCodeFor(mirror::ArtMethod* method, bool* have_portable_code)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get the oat code for a method from a method index.
   const void* GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const void* GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get compiled code for a method, return null if no code
   // exists. This is unlike Get..OatCodeFor which will return a bridge
   // or interpreter entrypoint.
   const void* GetOatMethodQuickCodeFor(mirror::ArtMethod* method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const void* GetOatMethodPortableCodeFor(mirror::ArtMethod* method)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   pid_t GetClassesLockOwner();  // For SignalCatcher.
   pid_t GetDexLockOwner();  // For SignalCatcher.
@@ -416,15 +410,9 @@
 
   static const char* GetClassRootDescriptor(ClassRoot class_root);
 
-  // Is the given entry point portable code to run the resolution stub?
-  bool IsPortableResolutionStub(const void* entry_point) const;
-
   // Is the given entry point quick code to run the resolution stub?
   bool IsQuickResolutionStub(const void* entry_point) const;
 
-  // Is the given entry point portable code to bridge into the interpreter?
-  bool IsPortableToInterpreterBridge(const void* entry_point) const;
-
   // Is the given entry point quick code to bridge into the interpreter?
   bool IsQuickToInterpreterBridge(const void* entry_point) const;
 
@@ -436,8 +424,7 @@
   }
 
   // Set the entrypoints up for method to the given code.
-  void SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code,
-                                    bool is_portable) const
+  void SetEntryPointsToCompiledCode(mirror::ArtMethod* method, const void* method_code) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Set the entrypoints up for method to the enter the interpreter.
@@ -471,7 +458,14 @@
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Returns true if the method can be called with its direct code pointer, false otherwise.
+  bool MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
+  static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   const OatFile::OatMethod FindOatMethodFor(mirror::ArtMethod* method, bool* found)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -728,6 +722,7 @@
   const void* GetRuntimeQuickGenericJniStub() const;
 
   std::vector<const DexFile*> boot_class_path_;
+  std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 
   mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);
@@ -798,9 +793,7 @@
 
   // Trampolines within the image the bounce to runtime entrypoints. Done so that there is a single
   // patch point within the image. TODO: make these proper relocations.
-  const void* portable_resolution_trampoline_;
   const void* quick_resolution_trampoline_;
-  const void* portable_imt_conflict_trampoline_;
   const void* quick_imt_conflict_trampoline_;
   const void* quick_generic_jni_trampoline_;
   const void* quick_to_interpreter_bridge_trampoline_;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ac078aa..64e129c 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -342,32 +342,30 @@
     }
   }
 
-  void AssertDexFile(const DexFile* dex, mirror::ClassLoader* class_loader)
+  void AssertDexFile(const DexFile& dex, mirror::ClassLoader* class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ASSERT_TRUE(dex != nullptr);
-
     // Verify all the classes defined in this file
-    for (size_t i = 0; i < dex->NumClassDefs(); i++) {
-      const DexFile::ClassDef& class_def = dex->GetClassDef(i);
-      const char* descriptor = dex->GetClassDescriptor(class_def);
+    for (size_t i = 0; i < dex.NumClassDefs(); i++) {
+      const DexFile::ClassDef& class_def = dex.GetClassDef(i);
+      const char* descriptor = dex.GetClassDescriptor(class_def);
       AssertDexFileClass(class_loader, descriptor);
     }
     // Verify all the types referenced by this file
-    for (size_t i = 0; i < dex->NumTypeIds(); i++) {
-      const DexFile::TypeId& type_id = dex->GetTypeId(i);
-      const char* descriptor = dex->GetTypeDescriptor(type_id);
+    for (size_t i = 0; i < dex.NumTypeIds(); i++) {
+      const DexFile::TypeId& type_id = dex.GetTypeId(i);
+      const char* descriptor = dex.GetTypeDescriptor(type_id);
       AssertDexFileClass(class_loader, descriptor);
     }
     class_linker_->VisitRoots(TestRootVisitor, nullptr, kVisitRootFlagAllRoots);
     // Verify the dex cache has resolution methods in all resolved method slots
-    mirror::DexCache* dex_cache = class_linker_->FindDexCache(*dex);
+    mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
     mirror::ObjectArray<mirror::ArtMethod>* resolved_methods = dex_cache->GetResolvedMethods();
     for (size_t i = 0; i < static_cast<size_t>(resolved_methods->GetLength()); i++) {
-      EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex->GetLocation() << " i=" << i;
+      EXPECT_TRUE(resolved_methods->Get(i) != nullptr) << dex.GetLocation() << " i=" << i;
     }
   }
 
-  static void TestRootVisitor(mirror::Object** root, void*, uint32_t, RootType) {
+  static void TestRootVisitor(mirror::Object** root, void*, const RootInfo&) {
     EXPECT_TRUE(*root != nullptr);
   }
 };
@@ -744,7 +742,8 @@
 
 TEST_F(ClassLinkerTest, LibCore) {
   ScopedObjectAccess soa(Thread::Current());
-  AssertDexFile(java_lang_dex_file_, nullptr);
+  ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+  AssertDexFile(*java_lang_dex_file_, nullptr);
 }
 
 // The first reference array element must be a multiple of 4 bytes from the
@@ -1137,4 +1136,24 @@
   CheckPreverified(statics.Get(), true);
 }
 
+TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) {
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+
+  // java.lang.Object is a bootstrap class.
+  Handle<mirror::Class> jlo_class(
+      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+  ASSERT_TRUE(jlo_class.Get() != nullptr);
+  EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded());
+
+  // Statics is not a bootstrap class.
+  Handle<mirror::Class> statics(
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
+  ASSERT_TRUE(statics.Get() != nullptr);
+  EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded());
+}
+
 }  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 03b33e9..b7ffd60 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -20,6 +20,7 @@
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <ScopedLocalRef.h>
+#include <stdlib.h>
 
 #include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
 #include "base/macros.h"
@@ -43,6 +44,11 @@
 #include "well_known_classes.h"
 
 int main(int argc, char **argv) {
+  // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
+  // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
+  // everything else. In case you want to see all messages, comment out the line.
+  setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
   art::InitLogging(argv);
   LOG(::art::INFO) << "Running main() from common_runtime_test.cc...";
   testing::InitGoogleTest(&argc, argv);
@@ -84,21 +90,29 @@
   return file_->Fd();
 }
 
-void ScratchFile::Unlink() {
-  if (!OS::FileExists(filename_.c_str())) {
-    return;
-  }
+void ScratchFile::Close() {
   if (file_.get() != nullptr) {
     if (file_->FlushCloseOrErase() != 0) {
       PLOG(WARNING) << "Error closing scratch file.";
     }
   }
+}
+
+void ScratchFile::Unlink() {
+  if (!OS::FileExists(filename_.c_str())) {
+    return;
+  }
+  Close();
   int unlink_result = unlink(filename_.c_str());
   CHECK_EQ(0, unlink_result);
 }
 
 CommonRuntimeTest::CommonRuntimeTest() {}
-CommonRuntimeTest::~CommonRuntimeTest() {}
+CommonRuntimeTest::~CommonRuntimeTest() {
+  // Ensure the dex files are cleaned up before the runtime.
+  loaded_dex_files_.clear();
+  runtime_.reset();
+}
 
 void CommonRuntimeTest::SetUpAndroidRoot() {
   if (IsHost()) {
@@ -169,16 +183,23 @@
   }
 }
 
+std::string CommonRuntimeTest::GetCoreArtLocation() {
+  return GetCoreFileLocation("art");
+}
 
-const DexFile* CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
-  std::vector<const DexFile*> dex_files;
+std::string CommonRuntimeTest::GetCoreOatLocation() {
+  return GetCoreFileLocation("oat");
+}
+
+std::unique_ptr<const DexFile> CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
   std::string error_msg;
   if (!DexFile::Open(location, location, &error_msg, &dex_files)) {
     LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
-    return nullptr;
+    UNREACHABLE();
   } else {
     CHECK_EQ(1U, dex_files.size()) << "Expected only one dex file in " << location;
-    return dex_files[0];
+    return std::move(dex_files[0]);
   }
 }
 
@@ -190,24 +211,20 @@
   int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
   ASSERT_EQ(mkdir_result, 0);
 
-  MemMap::Init();  // For LoadExpectSingleDexFile
-
-  std::string error_msg;
-  java_lang_dex_file_ = LoadExpectSingleDexFile(GetLibCoreDexFileName().c_str());
-  boot_class_path_.push_back(java_lang_dex_file_);
-
   std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
   std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
 
   callbacks_.reset(new NoopCompilerCallbacks());
 
   RuntimeOptions options;
-  options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
+  std::string boot_class_path_string = "-Xbootclasspath:" + GetLibCoreDexFileName();
+  options.push_back(std::make_pair(boot_class_path_string, nullptr));
   options.push_back(std::make_pair("-Xcheck:jni", nullptr));
-  options.push_back(std::make_pair(min_heap_string.c_str(), nullptr));
-  options.push_back(std::make_pair(max_heap_string.c_str(), nullptr));
+  options.push_back(std::make_pair(min_heap_string, nullptr));
+  options.push_back(std::make_pair(max_heap_string, nullptr));
   options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
   SetUpRuntimeOptions(&options);
+
   if (!Runtime::Create(options, false)) {
     LOG(FATAL) << "Failed to create runtime";
     return;
@@ -216,6 +233,9 @@
   class_linker_ = runtime_->GetClassLinker();
   class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
   class_linker_->RunRootClinits();
+  boot_class_path_ = class_linker_->GetBootClassPath();
+  java_lang_dex_file_ = boot_class_path_[0];
+
 
   // Runtime::Create acquired the mutator_lock_ that is normally given away when we
   // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
@@ -228,6 +248,11 @@
   // pool is created by the runtime.
   runtime_->GetHeap()->CreateThreadPool();
   runtime_->GetHeap()->VerifyHeap();  // Check for heap corruption before the test
+
+  // Get the boot class path from the runtime so it can be used in tests.
+  boot_class_path_ = class_linker_->GetBootClassPath();
+  ASSERT_FALSE(boot_class_path_.empty());
+  java_lang_dex_file_ = boot_class_path_[0];
 }
 
 void CommonRuntimeTest::ClearDirectory(const char* dirpath) {
@@ -274,8 +299,6 @@
   IcuCleanupFn icu_cleanup_fn = reinterpret_cast<IcuCleanupFn>(sym);
   (*icu_cleanup_fn)();
 
-  STLDeleteElements(&opened_dex_files_);
-
   Runtime::Current()->GetHeap()->VerifyHeap();  // Check for heap corruption after the test
 }
 
@@ -312,7 +335,7 @@
 #define ART_TARGET_NATIVETEST_DIR_STRING ""
 #endif
 
-std::vector<const DexFile*> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
   CHECK(name != nullptr);
   std::string filename;
   if (IsHost()) {
@@ -325,28 +348,30 @@
   filename += name;
   filename += ".jar";
   std::string error_msg;
-  std::vector<const DexFile*> dex_files;
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
   bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files);
   CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
-  for (const DexFile* dex_file : dex_files) {
+  for (auto& dex_file : dex_files) {
     CHECK_EQ(PROT_READ, dex_file->GetPermissions());
     CHECK(dex_file->IsReadOnly());
   }
-  opened_dex_files_.insert(opened_dex_files_.end(), dex_files.begin(), dex_files.end());
   return dex_files;
 }
 
-const DexFile* CommonRuntimeTest::OpenTestDexFile(const char* name) {
-  std::vector<const DexFile*> vector = OpenTestDexFiles(name);
+std::unique_ptr<const DexFile> CommonRuntimeTest::OpenTestDexFile(const char* name) {
+  std::vector<std::unique_ptr<const DexFile>> vector = OpenTestDexFiles(name);
   EXPECT_EQ(1U, vector.size());
-  return vector[0];
+  return std::move(vector[0]);
 }
 
 jobject CommonRuntimeTest::LoadDex(const char* dex_name) {
-  std::vector<const DexFile*> dex_files = OpenTestDexFiles(dex_name);
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+  std::vector<const DexFile*> class_path;
   CHECK_NE(0U, dex_files.size());
-  for (const DexFile* dex_file : dex_files) {
+  for (auto& dex_file : dex_files) {
+    class_path.push_back(dex_file.get());
     class_linker_->RegisterDexFile(*dex_file);
+    loaded_dex_files_.push_back(std::move(dex_file));
   }
   Thread* self = Thread::Current();
   JNIEnvExt* env = self->GetJniEnv();
@@ -354,10 +379,25 @@
       env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
   jobject class_loader = env->NewGlobalRef(class_loader_local.get());
   self->SetClassLoaderOverride(class_loader_local.get());
-  Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files);
+  Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path);
   return class_loader;
 }
 
+std::string CommonRuntimeTest::GetCoreFileLocation(const char* suffix) {
+  CHECK(suffix != nullptr);
+
+  std::string location;
+  if (IsHost()) {
+    const char* host_dir = getenv("ANDROID_HOST_OUT");
+    CHECK(host_dir != NULL);
+    location = StringPrintf("%s/framework/core.%s", host_dir, suffix);
+  } else {
+    location = StringPrintf("/data/art-test/core.%s", suffix);
+  }
+
+  return location;
+}
+
 CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
   vm_->SetCheckJniAbortHook(Hook, &actual_);
 }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index bd0dbaa..9efea84 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -55,6 +55,7 @@
 
   int GetFd() const;
 
+  void Close();
   void Unlink();
 
  private:
@@ -75,12 +76,21 @@
   CommonRuntimeTest();
   ~CommonRuntimeTest();
 
+  // Gets the path of the libcore dex file.
+  static std::string GetLibCoreDexFileName();
+
  protected:
   static bool IsHost() {
     return !kIsTargetBuild;
   }
 
-  const DexFile* LoadExpectSingleDexFile(const char* location);
+  // File location to core.art, e.g. $ANDROID_HOST_OUT/system/framework/core.art
+  static std::string GetCoreArtLocation();
+
+  // File location to core.oat, e.g. $ANDROID_HOST_OUT/system/framework/core.oat
+  static std::string GetCoreOatLocation();
+
+  std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location);
 
   virtual void SetUp();
 
@@ -91,32 +101,35 @@
 
   virtual void TearDown();
 
-  // Gets the path of the libcore dex file.
-  std::string GetLibCoreDexFileName();
-
   // Gets the path of the specified dex file for host or target.
-  std::string GetDexFileName(const std::string& jar_prefix);
+  static std::string GetDexFileName(const std::string& jar_prefix);
 
   std::string GetTestAndroidRoot();
 
-  std::vector<const DexFile*> OpenTestDexFiles(const char* name)
+  std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  const DexFile* OpenTestDexFile(const char* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   jobject LoadDex(const char* dex_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   std::string android_data_;
   std::string dalvik_cache_;
-  const DexFile* java_lang_dex_file_;  // owned by runtime_
-  std::vector<const DexFile*> boot_class_path_;
+
   std::unique_ptr<Runtime> runtime_;
-  // Owned by the runtime
+
+  // The class_linker_, java_lang_dex_file_, and boot_class_path_ are all
+  // owned by the runtime.
   ClassLinker* class_linker_;
+  const DexFile* java_lang_dex_file_;
+  std::vector<const DexFile*> boot_class_path_;
 
  private:
+  static std::string GetCoreFileLocation(const char* suffix);
+
   std::unique_ptr<CompilerCallbacks> callbacks_;
-  std::vector<const DexFile*> opened_dex_files_;
+  std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_;
 };
 
 // Sets a CheckJni abort hook to catch failures. Note that this will cause CheckJNI to carry on
@@ -138,16 +151,6 @@
   DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
 };
 
-// TODO: These tests were disabled for portable when we went to having
-// MCLinker link LLVM ELF output because we no longer just have code
-// blobs in memory. We'll need to dlopen to load and relocate
-// temporary output to resurrect these tests.
-#define TEST_DISABLED_FOR_PORTABLE() \
-  if (kUsePortableCompiler) { \
-    printf("WARNING: TEST DISABLED FOR PORTABLE\n"); \
-    return; \
-  }
-
 // TODO: When heap reference poisoning works with the compiler, get rid of this.
 #define TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING() \
   if (kPoisonHeapReferences) { \
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 846216c..f5b4354 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -418,6 +418,10 @@
       break;
     }
     case Instruction::IGET_QUICK:
+    case Instruction::IGET_BOOLEAN_QUICK:
+    case Instruction::IGET_BYTE_QUICK:
+    case Instruction::IGET_CHAR_QUICK:
+    case Instruction::IGET_SHORT_QUICK:
     case Instruction::IGET_WIDE_QUICK:
     case Instruction::IGET_OBJECT_QUICK: {
       // Since we replaced the field index, we ask the verifier to tell us which
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index d5cba50..678b29a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -105,18 +105,17 @@
 
 jobject Dbg::TypeCache::Add(mirror::Class* t) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
-  int32_t hash_code = t->IdentityHashCode();
+  JNIEnv* const env = soa.Env();
+  ScopedLocalRef<jobject> local_ref(soa.Env(), soa.AddLocalReference<jobject>(t));
+  const int32_t hash_code = soa.Decode<mirror::Class*>(local_ref.get())->IdentityHashCode();
   auto range = objects_.equal_range(hash_code);
   for (auto it = range.first; it != range.second; ++it) {
-    if (soa.Decode<mirror::Class*>(it->second) == t) {
+    if (soa.Decode<mirror::Class*>(it->second) == soa.Decode<mirror::Class*>(local_ref.get())) {
       // Found a matching weak global, return it.
       return it->second;
     }
   }
-  JNIEnv* env = soa.Env();
-  const jobject local_ref = soa.AddLocalReference<jobject>(t);
-  const jobject weak_global = env->NewWeakGlobalRef(local_ref);
-  env->DeleteLocalRef(local_ref);
+  const jobject weak_global = env->NewWeakGlobalRef(local_ref.get());
   objects_.insert(std::make_pair(hash_code, weak_global));
   return weak_global;
 }
@@ -298,9 +297,6 @@
 // Was there a -Xrunjdwp or -agentlib:jdwp= argument on the command line?
 static bool gJdwpConfigured = false;
 
-// Broken-down JDWP options. (Only valid if IsJdwpConfigured() is true.)
-static JDWP::JdwpOptions gJdwpOptions;
-
 // Runtime JDWP state.
 static JDWP::JdwpState* gJdwpState = nullptr;
 static bool gDebuggerConnected;  // debugger or DDMS is connected.
@@ -342,19 +338,18 @@
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
-void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                RootType root_type) {
+void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
   if (receiver != nullptr) {
-    callback(&receiver, arg, tid, root_type);
+    callback(&receiver, arg, root_info);
   }
   if (thread != nullptr) {
-    callback(&thread, arg, tid, root_type);
+    callback(&thread, arg, root_info);
   }
   if (klass != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&klass), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&klass), arg, root_info);
   }
   if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
   }
 }
 
@@ -366,21 +361,18 @@
   method = nullptr;
 }
 
-void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                   RootType root_type) {
-  if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
+  if (method_ != nullptr) {
+    callback(reinterpret_cast<mirror::Object**>(&method_), arg, root_info);
   }
 }
 
-bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const {
-  return dex_pcs.find(dex_pc) == dex_pcs.end();
+void SingleStepControl::AddDexPc(uint32_t dex_pc) {
+  dex_pcs_.insert(dex_pc);
 }
 
-void SingleStepControl::Clear() {
-  is_active = false;
-  method = nullptr;
-  dex_pcs.clear();
+bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const {
+  return dex_pcs_.find(dex_pc) == dex_pcs_.end();
 }
 
 static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc)
@@ -537,119 +529,13 @@
   }
 }
 
-/*
- * Handle one of the JDWP name/value pairs.
- *
- * JDWP options are:
- *  help: if specified, show help message and bail
- *  transport: may be dt_socket or dt_shmem
- *  address: for dt_socket, "host:port", or just "port" when listening
- *  server: if "y", wait for debugger to attach; if "n", attach to debugger
- *  timeout: how long to wait for debugger to connect / listen
- *
- * Useful with server=n (these aren't supported yet):
- *  onthrow=<exception-name>: connect to debugger when exception thrown
- *  onuncaught=y|n: connect to debugger when uncaught exception thrown
- *  launch=<command-line>: launch the debugger itself
- *
- * The "transport" option is required, as is "address" if server=n.
- */
-static bool ParseJdwpOption(const std::string& name, const std::string& value) {
-  if (name == "transport") {
-    if (value == "dt_socket") {
-      gJdwpOptions.transport = JDWP::kJdwpTransportSocket;
-    } else if (value == "dt_android_adb") {
-      gJdwpOptions.transport = JDWP::kJdwpTransportAndroidAdb;
-    } else {
-      LOG(ERROR) << "JDWP transport not supported: " << value;
-      return false;
-    }
-  } else if (name == "server") {
-    if (value == "n") {
-      gJdwpOptions.server = false;
-    } else if (value == "y") {
-      gJdwpOptions.server = true;
-    } else {
-      LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
-      return false;
-    }
-  } else if (name == "suspend") {
-    if (value == "n") {
-      gJdwpOptions.suspend = false;
-    } else if (value == "y") {
-      gJdwpOptions.suspend = true;
-    } else {
-      LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
-      return false;
-    }
-  } else if (name == "address") {
-    /* this is either <port> or <host>:<port> */
-    std::string port_string;
-    gJdwpOptions.host.clear();
-    std::string::size_type colon = value.find(':');
-    if (colon != std::string::npos) {
-      gJdwpOptions.host = value.substr(0, colon);
-      port_string = value.substr(colon + 1);
-    } else {
-      port_string = value;
-    }
-    if (port_string.empty()) {
-      LOG(ERROR) << "JDWP address missing port: " << value;
-      return false;
-    }
-    char* end;
-    uint64_t port = strtoul(port_string.c_str(), &end, 10);
-    if (*end != '\0' || port > 0xffff) {
-      LOG(ERROR) << "JDWP address has junk in port field: " << value;
-      return false;
-    }
-    gJdwpOptions.port = port;
-  } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
-    /* valid but unsupported */
-    LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
-  } else {
-    LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
-  }
-
-  return true;
-}
-
-/*
- * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
- * "transport=dt_socket,address=8000,server=y,suspend=n"
- */
-bool Dbg::ParseJdwpOptions(const std::string& options) {
-  VLOG(jdwp) << "ParseJdwpOptions: " << options;
-
-  std::vector<std::string> pairs;
-  Split(options, ',', &pairs);
-
-  for (size_t i = 0; i < pairs.size(); ++i) {
-    std::string::size_type equals = pairs[i].find('=');
-    if (equals == std::string::npos) {
-      LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
-      return false;
-    }
-    ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
-  }
-
-  if (gJdwpOptions.transport == JDWP::kJdwpTransportUnknown) {
-    LOG(ERROR) << "Must specify JDWP transport: " << options;
-  }
-  if (!gJdwpOptions.server && (gJdwpOptions.host.empty() || gJdwpOptions.port == 0)) {
-    LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
-    return false;
-  }
-
-  gJdwpConfigured = true;
-  return true;
-}
-
-void Dbg::StartJdwp() {
+void Dbg::StartJdwp(const JDWP::JdwpOptions* jdwp_options) {
+  gJdwpConfigured = (jdwp_options != nullptr);
   if (!gJdwpAllowed || !IsJdwpConfigured()) {
     // No JDWP for you!
     return;
   }
+  DCHECK_NE(jdwp_options->transport, JDWP::kJdwpTransportUnknown);
 
   CHECK(gRegistry == nullptr);
   gRegistry = new ObjectRegistry;
@@ -657,7 +543,7 @@
   // Init JDWP if the debugger is enabled. This may connect out to a
   // debugger, passively listen for a debugger, or block waiting for a
   // debugger.
-  gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
+  gJdwpState = JDWP::JdwpState::Create(jdwp_options);
   if (gJdwpState == nullptr) {
     // We probably failed because some other process has the port already, which means that
     // if we don't abort the user is likely to think they're talking to us when they're actually
@@ -669,9 +555,7 @@
   // This may cause us to suspend all threads.
   if (gJdwpState->IsActive()) {
     ScopedObjectAccess soa(Thread::Current());
-    if (!gJdwpState->PostVMStart()) {
-      LOG(WARNING) << "Failed to post 'start' message to debugger";
-    }
+    gJdwpState->PostVMStart();
   }
 }
 
@@ -821,10 +705,15 @@
     }
     gDebuggerActive = false;
   }
-  gRegistry->Clear();
-  gDebuggerConnected = false;
   CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
   runtime->GetThreadList()->ResumeAll();
+
+  {
+    ScopedObjectAccess soa(self);
+    gRegistry->Clear();
+  }
+
+  gDebuggerConnected = false;
 }
 
 bool Dbg::IsDebuggerActive() {
@@ -2193,6 +2082,7 @@
     case kWaitingForJniOnLoad:
     case kWaitingForMethodTracingStart:
     case kWaitingForSignalCatcherOutput:
+    case kWaitingForVisitObjects:
     case kWaitingInMainDebuggerLoop:
     case kWaitingInMainSignalCatcherLoop:
     case kWaitingPerformingGc:
@@ -2569,7 +2459,7 @@
     VLOG(jdwp) << "    --> slot " << slot << " " << reqSigByte;
 
     size_t width = Dbg::GetTagWidth(reqSigByte);
-    uint8_t* ptr = expandBufAddSpace(pReply, width+1);
+    uint8_t* ptr = expandBufAddSpace(pReply, width + 1);
     JDWP::JdwpError error = Dbg::GetLocalValue(visitor, soa, slot, reqSigByte, ptr, width);
     if (error != JDWP::ERR_NONE) {
       return error;
@@ -2917,24 +2807,23 @@
   // If the debugger is single-stepping one of our threads, check to
   // see if we're that thread and we've reached a step point.
   const SingleStepControl* single_step_control = thread->GetSingleStepControl();
-  DCHECK(single_step_control != nullptr);
-  if (single_step_control->is_active) {
+  if (single_step_control != nullptr) {
     CHECK(!m->IsNative());
-    if (single_step_control->step_depth == JDWP::SD_INTO) {
+    if (single_step_control->GetStepDepth() == JDWP::SD_INTO) {
       // Step into method calls.  We break when the line number
       // or method pointer changes.  If we're in SS_MIN mode, we
       // always stop.
-      if (single_step_control->method != m) {
+      if (single_step_control->GetMethod() != m) {
         event_flags |= kSingleStep;
         VLOG(jdwp) << "SS new method";
-      } else if (single_step_control->step_size == JDWP::SS_MIN) {
+      } else if (single_step_control->GetStepSize() == JDWP::SS_MIN) {
         event_flags |= kSingleStep;
         VLOG(jdwp) << "SS new instruction";
       } else if (single_step_control->ContainsDexPc(dex_pc)) {
         event_flags |= kSingleStep;
         VLOG(jdwp) << "SS new line";
       }
-    } else if (single_step_control->step_depth == JDWP::SD_OVER) {
+    } else if (single_step_control->GetStepDepth() == JDWP::SD_OVER) {
       // Step over method calls.  We break when the line number is
       // different and the frame depth is <= the original frame
       // depth.  (We can't just compare on the method, because we
@@ -2943,13 +2832,13 @@
 
       int stack_depth = GetStackDepth(thread);
 
-      if (stack_depth < single_step_control->stack_depth) {
+      if (stack_depth < single_step_control->GetStackDepth()) {
         // Popped up one or more frames, always trigger.
         event_flags |= kSingleStep;
         VLOG(jdwp) << "SS method pop";
-      } else if (stack_depth == single_step_control->stack_depth) {
+      } else if (stack_depth == single_step_control->GetStackDepth()) {
         // Same depth, see if we moved.
-        if (single_step_control->step_size == JDWP::SS_MIN) {
+        if (single_step_control->GetStepSize() == JDWP::SS_MIN) {
           event_flags |= kSingleStep;
           VLOG(jdwp) << "SS new instruction";
         } else if (single_step_control->ContainsDexPc(dex_pc)) {
@@ -2958,7 +2847,7 @@
         }
       }
     } else {
-      CHECK_EQ(single_step_control->step_depth, JDWP::SD_OUT);
+      CHECK_EQ(single_step_control->GetStepDepth(), JDWP::SD_OUT);
       // Return from the current method.  We break when the frame
       // depth pops up.
 
@@ -2967,7 +2856,7 @@
       // function, rather than the end of the returning function.
 
       int stack_depth = GetStackDepth(thread);
-      if (stack_depth < single_step_control->stack_depth) {
+      if (stack_depth < single_step_control->GetStackDepth()) {
         event_flags |= kSingleStep;
         VLOG(jdwp) << "SS method pop";
       }
@@ -3194,7 +3083,7 @@
   Handle<mirror::ArtMethod> method(hs.NewHandle(m));
   verifier::MethodVerifier verifier(self, dex_cache->GetDexFile(), dex_cache, class_loader,
                                     &m->GetClassDef(), code_item, m->GetDexMethodIndex(), method,
-                                    m->GetAccessFlags(), false, true, false);
+                                    m->GetAccessFlags(), false, true, false, true);
   // Note: we don't need to verify the method.
   return InlineMethodAnalyser::AnalyseMethodCode(&verifier, nullptr);
 }
@@ -3235,8 +3124,12 @@
   }
 }
 
+// Returns the deoptimization kind required to set a breakpoint in a method.
+// If a breakpoint has already been set, we also return the first breakpoint
+// through the given 'existing_brkpt' pointer.
 static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self,
-                                                                 mirror::ArtMethod* m)
+                                                                 mirror::ArtMethod* m,
+                                                                 const Breakpoint** existing_brkpt)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (!Dbg::RequiresDeoptimization()) {
     // We already run in interpreter-only mode so we don't need to deoptimize anything.
@@ -3244,12 +3137,14 @@
                << PrettyMethod(m);
     return DeoptimizationRequest::kNothing;
   }
-  const Breakpoint* existing_breakpoint;
+  const Breakpoint* first_breakpoint;
   {
     ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
-    existing_breakpoint = FindFirstBreakpointForMethod(m);
+    first_breakpoint = FindFirstBreakpointForMethod(m);
+    *existing_brkpt = first_breakpoint;
   }
-  if (existing_breakpoint == nullptr) {
+
+  if (first_breakpoint == nullptr) {
     // There is no breakpoint on this method yet: we need to deoptimize. If this method may be
     // inlined, we deoptimize everything; otherwise we deoptimize only this method.
     // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension.
@@ -3264,8 +3159,16 @@
       ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
       const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr;
       if (is_compiled) {
-        VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
-        return DeoptimizationRequest::kSelectiveDeoptimization;
+        // If the method may be called through its direct code pointer (without loading
+        // its updated entrypoint), we need full deoptimization to not miss the breakpoint.
+        if (class_linker->MayBeCalledWithDirectCodePointer(m)) {
+          VLOG(jdwp) << "Need full deoptimization because of possible direct code call "
+                     << "into image for compiled method " << PrettyMethod(m);
+          return DeoptimizationRequest::kFullDeoptimization;
+        } else {
+          VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
+          return DeoptimizationRequest::kSelectiveDeoptimization;
+        }
       } else {
         // Method is not compiled: we don't need to deoptimize.
         VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m);
@@ -3276,7 +3179,7 @@
     // There is at least one breakpoint for this method: we don't need to deoptimize.
     // Let's check that all breakpoints are configured the same way for deoptimization.
     VLOG(jdwp) << "Breakpoint already set: no deoptimization is required";
-    DeoptimizationRequest::Kind deoptimization_kind = existing_breakpoint->GetDeoptimizationKind();
+    DeoptimizationRequest::Kind deoptimization_kind = first_breakpoint->GetDeoptimizationKind();
     if (kIsDebugBuild) {
       ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
       SanityCheckExistingBreakpoints(m, deoptimization_kind);
@@ -3292,7 +3195,9 @@
   mirror::ArtMethod* m = FromMethodId(location->method_id);
   DCHECK(m != nullptr) << "No method for method id " << location->method_id;
 
-  const DeoptimizationRequest::Kind deoptimization_kind = GetRequiredDeoptimizationKind(self, m);
+  const Breakpoint* existing_breakpoint = nullptr;
+  const DeoptimizationRequest::Kind deoptimization_kind =
+      GetRequiredDeoptimizationKind(self, m, &existing_breakpoint);
   req->SetKind(deoptimization_kind);
   if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
     req->SetMethod(m);
@@ -3304,7 +3209,15 @@
 
   {
     WriterMutexLock mu(self, *Locks::breakpoint_lock_);
-    gBreakpoints.push_back(Breakpoint(m, location->dex_pc, deoptimization_kind));
+    // If there is at least one existing breakpoint on the same method, the new breakpoint
+    // must have the same deoptimization kind than the existing breakpoint(s).
+    DeoptimizationRequest::Kind breakpoint_deoptimization_kind;
+    if (existing_breakpoint != nullptr) {
+      breakpoint_deoptimization_kind = existing_breakpoint->GetDeoptimizationKind();
+    } else {
+      breakpoint_deoptimization_kind = deoptimization_kind;
+    }
+    gBreakpoints.push_back(Breakpoint(m, location->dex_pc, breakpoint_deoptimization_kind));
     VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": "
                << gBreakpoints[gBreakpoints.size() - 1];
   }
@@ -3421,20 +3334,11 @@
     return sts.GetError();
   }
 
-  //
-  // Work out what Method* we're in, the current line number, and how deep the stack currently
+  // Work out what ArtMethod* we're in, the current line number, and how deep the stack currently
   // is for step-out.
-  //
-
   struct SingleStepStackVisitor : public StackVisitor {
-    explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control,
-                                    int32_t* line_number)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        : StackVisitor(thread, nullptr), single_step_control_(single_step_control),
-          line_number_(line_number) {
-      DCHECK_EQ(single_step_control_, thread->GetSingleStepControl());
-      single_step_control_->method = nullptr;
-      single_step_control_->stack_depth = 0;
+    explicit SingleStepStackVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        : StackVisitor(thread, nullptr), stack_depth(0), method(nullptr), line_number(-1) {
     }
 
     // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -3442,38 +3346,32 @@
     bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
       mirror::ArtMethod* m = GetMethod();
       if (!m->IsRuntimeMethod()) {
-        ++single_step_control_->stack_depth;
-        if (single_step_control_->method == nullptr) {
+        ++stack_depth;
+        if (method == nullptr) {
           mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
-          single_step_control_->method = m;
-          *line_number_ = -1;
+          method = m;
           if (dex_cache != nullptr) {
             const DexFile& dex_file = *dex_cache->GetDexFile();
-            *line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc());
+            line_number = dex_file.GetLineNumFromPC(m, GetDexPc());
           }
         }
       }
       return true;
     }
 
-    SingleStepControl* const single_step_control_;
-    int32_t* const line_number_;
+    int stack_depth;
+    mirror::ArtMethod* method;
+    int32_t line_number;
   };
 
   Thread* const thread = sts.GetThread();
-  SingleStepControl* const single_step_control = thread->GetSingleStepControl();
-  DCHECK(single_step_control != nullptr);
-  int32_t line_number = -1;
-  SingleStepStackVisitor visitor(thread, single_step_control, &line_number);
+  SingleStepStackVisitor visitor(thread);
   visitor.WalkStack();
 
-  //
   // Find the dex_pc values that correspond to the current line, for line-based single-stepping.
-  //
-
   struct DebugCallbackContext {
-    explicit DebugCallbackContext(SingleStepControl* single_step_control_cb, int32_t line_number_cb,
-                                  const DexFile::CodeItem* code_item)
+    explicit DebugCallbackContext(SingleStepControl* single_step_control_cb,
+                                  int32_t line_number_cb, const DexFile::CodeItem* code_item)
       : single_step_control_(single_step_control_cb), line_number_(line_number_cb),
         code_item_(code_item), last_pc_valid(false), last_pc(0) {
     }
@@ -3491,7 +3389,7 @@
       } else if (context->last_pc_valid) {  // and the line number is new
         // Add everything from the last entry up until here to the set
         for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) {
-          context->single_step_control_->dex_pcs.insert(dex_pc);
+          context->single_step_control_->AddDexPc(dex_pc);
         }
         context->last_pc_valid = false;
       }
@@ -3503,7 +3401,7 @@
       if (last_pc_valid) {
         size_t end = code_item_->insns_size_in_code_units_;
         for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) {
-          single_step_control_->dex_pcs.insert(dex_pc);
+          single_step_control_->AddDexPc(dex_pc);
         }
       }
     }
@@ -3514,8 +3412,14 @@
     bool last_pc_valid;
     uint32_t last_pc;
   };
-  single_step_control->dex_pcs.clear();
-  mirror::ArtMethod* m = single_step_control->method;
+
+  // Allocate single step.
+  SingleStepControl* single_step_control = new SingleStepControl(step_size, step_depth,
+                                                                 visitor.stack_depth,
+                                                                 visitor.method);
+  CHECK(single_step_control != nullptr) << "Failed to allocate SingleStepControl";
+  mirror::ArtMethod* m = single_step_control->GetMethod();
+  const int32_t line_number = visitor.line_number;
   if (!m->IsNative()) {
     const DexFile::CodeItem* const code_item = m->GetCodeItem();
     DebugCallbackContext context(single_step_control, line_number, code_item);
@@ -3523,23 +3427,18 @@
                                      DebugCallbackContext::Callback, nullptr, &context);
   }
 
-  //
-  // Everything else...
-  //
-
-  single_step_control->step_size = step_size;
-  single_step_control->step_depth = step_depth;
-  single_step_control->is_active = true;
+  // Activate single-step in the thread.
+  thread->ActivateSingleStepControl(single_step_control);
 
   if (VLOG_IS_ON(jdwp)) {
     VLOG(jdwp) << "Single-step thread: " << *thread;
-    VLOG(jdwp) << "Single-step step size: " << single_step_control->step_size;
-    VLOG(jdwp) << "Single-step step depth: " << single_step_control->step_depth;
-    VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->method);
+    VLOG(jdwp) << "Single-step step size: " << single_step_control->GetStepSize();
+    VLOG(jdwp) << "Single-step step depth: " << single_step_control->GetStepDepth();
+    VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->GetMethod());
     VLOG(jdwp) << "Single-step current line: " << line_number;
-    VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->stack_depth;
+    VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->GetStackDepth();
     VLOG(jdwp) << "Single-step dex_pc values:";
-    for (uint32_t dex_pc : single_step_control->dex_pcs) {
+    for (uint32_t dex_pc : single_step_control->GetDexPcs()) {
       VLOG(jdwp) << StringPrintf(" %#x", dex_pc);
     }
   }
@@ -3553,9 +3452,7 @@
   JDWP::JdwpError error;
   Thread* thread = DecodeThread(soa, thread_id, &error);
   if (error == JDWP::ERR_NONE) {
-    SingleStepControl* single_step_control = thread->GetSingleStepControl();
-    DCHECK(single_step_control != nullptr);
-    single_step_control->Clear();
+    thread->DeactivateSingleStepControl();
   }
 }
 
@@ -4067,6 +3964,10 @@
   }
 }
 
+JDWP::JdwpState* Dbg::GetJdwpState() {
+  return gJdwpState;
+}
+
 int Dbg::DdmHandleHpifChunk(HpifWhen when) {
   if (when == HPIF_WHEN_NOW) {
     DdmSendHeapInfo(when);
@@ -4230,10 +4131,15 @@
     Reset();
   }
 
-  static void HeapChunkCallback(void* start, void* end, size_t used_bytes, void* arg)
+  static void HeapChunkJavaCallback(void* start, void* end, size_t used_bytes, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
                             Locks::mutator_lock_) {
-    reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkCallback(start, end, used_bytes);
+    reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkJavaCallback(start, end, used_bytes);
+  }
+
+  static void HeapChunkNativeCallback(void* start, void* end, size_t used_bytes, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkNativeCallback(start, end, used_bytes);
   }
 
  private:
@@ -4247,72 +4153,85 @@
     pieceLenField_ = nullptr;
   }
 
-  void HeapChunkCallback(void* start, void* /*end*/, size_t used_bytes)
-      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
-                            Locks::mutator_lock_) {
+  bool IsNative() const {
+    return type_ == CHUNK_TYPE("NHSG");
+  }
+
+  // Returns true if the object is not an empty chunk.
+  bool ProcessRecord(void* start, size_t used_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Note: heap call backs cannot manipulate the heap upon which they are crawling, care is taken
     // in the following code not to allocate memory, by ensuring buf_ is of the correct size
     if (used_bytes == 0) {
-        if (start == nullptr) {
-            // Reset for start of new heap.
-            startOfNextMemoryChunk_ = nullptr;
-            Flush();
-        }
-        // Only process in use memory so that free region information
-        // also includes dlmalloc book keeping.
-        return;
+      if (start == nullptr) {
+        // Reset for start of new heap.
+        startOfNextMemoryChunk_ = nullptr;
+        Flush();
+      }
+      // Only process in use memory so that free region information
+      // also includes dlmalloc book keeping.
+      return false;
     }
-
-    /* If we're looking at the native heap, we'll just return
-     * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
-     */
-    bool native = type_ == CHUNK_TYPE("NHSG");
-
-    // TODO: I'm not sure using start of next chunk works well with multiple spaces. We shouldn't
-    // count gaps inbetween spaces as free memory.
     if (startOfNextMemoryChunk_ != nullptr) {
-        // Transmit any pending free memory. Native free memory of
-        // over kMaxFreeLen could be because of the use of mmaps, so
-        // don't report. If not free memory then start a new segment.
-        bool flush = true;
-        if (start > startOfNextMemoryChunk_) {
-            const size_t kMaxFreeLen = 2 * kPageSize;
-            void* freeStart = startOfNextMemoryChunk_;
-            void* freeEnd = start;
-            size_t freeLen = reinterpret_cast<char*>(freeEnd) - reinterpret_cast<char*>(freeStart);
-            if (!native || freeLen < kMaxFreeLen) {
-                AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), freeStart, freeLen);
-                flush = false;
-            }
+      // Transmit any pending free memory. Native free memory of over kMaxFreeLen could be because
+      // of the use of mmaps, so don't report. If not free memory then start a new segment.
+      bool flush = true;
+      if (start > startOfNextMemoryChunk_) {
+        const size_t kMaxFreeLen = 2 * kPageSize;
+        void* free_start = startOfNextMemoryChunk_;
+        void* free_end = start;
+        const size_t free_len =
+            reinterpret_cast<uintptr_t>(free_end) - reinterpret_cast<uintptr_t>(free_start);
+        if (!IsNative() || free_len < kMaxFreeLen) {
+          AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), free_start, free_len, IsNative());
+          flush = false;
         }
-        if (flush) {
-            startOfNextMemoryChunk_ = nullptr;
-            Flush();
-        }
+      }
+      if (flush) {
+        startOfNextMemoryChunk_ = nullptr;
+        Flush();
+      }
     }
-    mirror::Object* obj = reinterpret_cast<mirror::Object*>(start);
-
-    // Determine the type of this chunk.
-    // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
-    // If it's the same, we should combine them.
-    uint8_t state = ExamineObject(obj, native);
-    AppendChunk(state, start, used_bytes + chunk_overhead_);
-    startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
+    return true;
   }
 
-  void AppendChunk(uint8_t state, void* ptr, size_t length)
+  void HeapChunkNativeCallback(void* start, void* /*end*/, size_t used_bytes)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    if (ProcessRecord(start, used_bytes)) {
+      uint8_t state = ExamineNativeObject(start);
+      AppendChunk(state, start, used_bytes + chunk_overhead_, true /*is_native*/);
+      startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
+    }
+  }
+
+  void HeapChunkJavaCallback(void* start, void* /*end*/, size_t used_bytes)
+      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
+    if (ProcessRecord(start, used_bytes)) {
+      // Determine the type of this chunk.
+      // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
+      // If it's the same, we should combine them.
+      uint8_t state = ExamineJavaObject(reinterpret_cast<mirror::Object*>(start));
+      AppendChunk(state, start, used_bytes + chunk_overhead_, false /*is_native*/);
+      startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
+    }
+  }
+
+  void AppendChunk(uint8_t state, void* ptr, size_t length, bool is_native)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     // Make sure there's enough room left in the buffer.
     // We need to use two bytes for every fractional 256 allocation units used by the chunk plus
     // 17 bytes for any header.
-    size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17;
-    size_t bytesLeft = buf_.size() - (size_t)(p_ - &buf_[0]);
-    if (bytesLeft < needed) {
+    const size_t needed = ((RoundUp(length / ALLOCATION_UNIT_SIZE, 256) / 256) * 2) + 17;
+    size_t byte_left = &buf_.back() - p_;
+    if (byte_left < needed) {
+      if (is_native) {
+      // Cannot trigger memory allocation while walking native heap.
+        return;
+      }
       Flush();
     }
 
-    bytesLeft = buf_.size() - (size_t)(p_ - &buf_[0]);
-    if (bytesLeft < needed) {
+    byte_left = &buf_.back() - p_;
+    if (byte_left < needed) {
       LOG(WARNING) << "Chunk is too big to transmit (chunk_len=" << length << ", "
           << needed << " bytes)";
       return;
@@ -4330,43 +4249,38 @@
     *p_++ = length - 1;
   }
 
-  uint8_t ExamineObject(mirror::Object* o, bool is_native_heap)
+  uint8_t ExamineNativeObject(const void* p) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return p == nullptr ? HPSG_STATE(SOLIDITY_FREE, 0) : HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+  }
+
+  uint8_t ExamineJavaObject(mirror::Object* o)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     if (o == nullptr) {
       return HPSG_STATE(SOLIDITY_FREE, 0);
     }
-
     // It's an allocated chunk. Figure out what it is.
-
-    // If we're looking at the native heap, we'll just return
-    // (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks.
-    if (is_native_heap) {
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    if (!heap->IsLiveObjectLocked(o)) {
+      LOG(ERROR) << "Invalid object in managed heap: " << o;
       return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
     }
-
-    if (!Runtime::Current()->GetHeap()->IsLiveObjectLocked(o)) {
-      return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
-    }
-
     mirror::Class* c = o->GetClass();
     if (c == nullptr) {
       // The object was probably just created but hasn't been initialized yet.
       return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
     }
-
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+    if (!heap->IsValidObjectAddress(c)) {
       LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c;
       return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
     }
-
+    if (c->GetClass() == nullptr) {
+      LOG(ERROR) << "Null class of class " << c << " for object " << o;
+      return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+    }
     if (c->IsClassClass()) {
       return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
     }
-
     if (c->IsArrayClass()) {
-      if (o->IsObjectArray()) {
-        return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
-      }
       switch (c->GetComponentSize()) {
       case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
       case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
@@ -4374,7 +4288,6 @@
       case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
       }
     }
-
     return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
   }
 
@@ -4393,41 +4306,33 @@
 static void BumpPointerSpaceCallback(mirror::Object* obj, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
   const size_t size = RoundUp(obj->SizeOf(), kObjectAlignment);
-  HeapChunkContext::HeapChunkCallback(
+  HeapChunkContext::HeapChunkJavaCallback(
       obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + size), size, arg);
 }
 
 void Dbg::DdmSendHeapSegments(bool native) {
-  Dbg::HpsgWhen when;
-  Dbg::HpsgWhat what;
-  if (!native) {
-    when = gDdmHpsgWhen;
-    what = gDdmHpsgWhat;
-  } else {
-    when = gDdmNhsgWhen;
-    what = gDdmNhsgWhat;
-  }
+  Dbg::HpsgWhen when = native ? gDdmNhsgWhen : gDdmHpsgWhen;
+  Dbg::HpsgWhat what = native ? gDdmNhsgWhat : gDdmHpsgWhat;
   if (when == HPSG_WHEN_NEVER) {
     return;
   }
-
   // Figure out what kind of chunks we'll be sending.
-  CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast<int>(what);
+  CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS)
+      << static_cast<int>(what);
 
   // First, send a heap start chunk.
   uint8_t heap_id[4];
   JDWP::Set4BE(&heap_id[0], 1);  // Heap id (bogus; we only have one heap).
   Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
-
   Thread* self = Thread::Current();
-
   Locks::mutator_lock_->AssertSharedHeld(self);
 
   // Send a series of heap segment chunks.
-  HeapChunkContext context((what == HPSG_WHAT_MERGED_OBJECTS), native);
+  HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native);
   if (native) {
 #if defined(HAVE_ANDROID_OS) && defined(USE_DLMALLOC)
-    dlmalloc_inspect_all(HeapChunkContext::HeapChunkCallback, &context);
+    dlmalloc_inspect_all(HeapChunkContext::HeapChunkNativeCallback, &context);
+    HeapChunkContext::HeapChunkNativeCallback(nullptr, nullptr, 0, &context);  // Indicate end of a space.
 #else
     UNIMPLEMENTED(WARNING) << "Native heap inspection is only supported with dlmalloc";
 #endif
@@ -4439,7 +4344,7 @@
         // dlmalloc's chunk header is 2 * sizeof(size_t), but if the previous chunk is in use for an
         // allocation then the first sizeof(size_t) may belong to it.
         context.SetChunkOverhead(sizeof(size_t));
-        space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+        space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
       } else if (space->IsRosAllocSpace()) {
         context.SetChunkOverhead(0);
         // Need to acquire the mutator lock before the heap bitmap lock with exclusive access since
@@ -4449,7 +4354,7 @@
         tl->SuspendAll();
         {
           ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
-          space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+          space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
         }
         tl->ResumeAll();
         self->TransitionFromSuspendedToRunnable();
@@ -4457,6 +4362,19 @@
         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
         context.SetChunkOverhead(0);
         space->AsBumpPointerSpace()->Walk(BumpPointerSpaceCallback, &context);
+        HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
+      } else if (space->IsRegionSpace()) {
+        heap->IncrementDisableMovingGC(self);
+        self->TransitionFromRunnableToSuspended(kSuspended);
+        ThreadList* tl = Runtime::Current()->GetThreadList();
+        tl->SuspendAll();
+        ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+        context.SetChunkOverhead(0);
+        space->AsRegionSpace()->Walk(BumpPointerSpaceCallback, &context);
+        HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
+        tl->ResumeAll();
+        self->TransitionFromSuspendedToRunnable();
+        heap->DecrementDisableMovingGC(self);
       } else {
         UNIMPLEMENTED(WARNING) << "Not counting objects in space " << *space;
       }
@@ -4465,7 +4383,7 @@
     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     // Walk the large objects, these are not in the AllocSpace.
     context.SetChunkOverhead(0);
-    heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkCallback, &context);
+    heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
   }
 
   // Finally, send a heap end chunk.
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 9203163..6762608 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -28,6 +28,7 @@
 #include <string>
 #include <vector>
 
+#include "gc_root.h"
 #include "jdwp/jdwp.h"
 #include "jni.h"
 #include "jvalue.h"
@@ -87,7 +88,7 @@
   Mutex lock DEFAULT_MUTEX_ACQUIRED_AFTER;
   ConditionVariable cond GUARDED_BY(lock);
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void Clear();
@@ -97,39 +98,58 @@
 };
 
 // Thread local data-structure that holds fields for controlling single-stepping.
-struct SingleStepControl {
-  SingleStepControl()
-      : is_active(false), step_size(JDWP::SS_MIN), step_depth(JDWP::SD_INTO),
-        method(nullptr), stack_depth(0) {
+class SingleStepControl {
+ public:
+  SingleStepControl(JDWP::JdwpStepSize step_size, JDWP::JdwpStepDepth step_depth,
+                    int stack_depth, mirror::ArtMethod* method)
+      : step_size_(step_size), step_depth_(step_depth),
+        stack_depth_(stack_depth), method_(method) {
   }
 
-  // Are we single-stepping right now?
-  bool is_active;
+  JDWP::JdwpStepSize GetStepSize() const {
+    return step_size_;
+  }
 
+  JDWP::JdwpStepDepth GetStepDepth() const {
+    return step_depth_;
+  }
+
+  int GetStackDepth() const {
+    return stack_depth_;
+  }
+
+  mirror::ArtMethod* GetMethod() const {
+    return method_;
+  }
+
+  const std::set<uint32_t>& GetDexPcs() const {
+    return dex_pcs_;
+  }
+
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void AddDexPc(uint32_t dex_pc);
+
+  bool ContainsDexPc(uint32_t dex_pc) const;
+
+ private:
   // See JdwpStepSize and JdwpStepDepth for details.
-  JDWP::JdwpStepSize step_size;
-  JDWP::JdwpStepDepth step_depth;
+  const JDWP::JdwpStepSize step_size_;
+  const JDWP::JdwpStepDepth step_depth_;
+
+  // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
+  // single-step depth.
+  const int stack_depth_;
 
   // The location this single-step was initiated from.
   // A single-step is initiated in a suspended thread. We save here the current method and the
   // set of DEX pcs associated to the source line number where the suspension occurred.
   // This is used to support SD_INTO and SD_OVER single-step depths so we detect when a single-step
   // causes the execution of an instruction in a different method or at a different line number.
-  mirror::ArtMethod* method;
-  std::set<uint32_t> dex_pcs;
+  mirror::ArtMethod* method_;
+  std::set<uint32_t> dex_pcs_;
 
-  // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
-  // single-step depth.
-  int stack_depth;
-
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  bool ContainsDexPc(uint32_t dex_pc) const;
-
-  void Clear();
-
- private:
   DISALLOW_COPY_AND_ASSIGN(SingleStepControl);
 };
 
@@ -206,10 +226,9 @@
     std::multimap<int32_t, jobject> objects_;
   };
 
-  static bool ParseJdwpOptions(const std::string& options);
   static void SetJdwpAllowed(bool allowed);
 
-  static void StartJdwp();
+  static void StartJdwp(const JDWP::JdwpOptions* jdwp_options);
   static void StopJdwp();
 
   // Invoked by the GC in case we need to keep DDMS informed.
@@ -647,6 +666,8 @@
   static void SetJdwpLocation(JDWP::JdwpLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static JDWP::JdwpState* GetJdwpState();
+
  private:
   static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor,
                                        ScopedObjectAccessUnchecked& soa, int slot,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 4f36c64..94d62db 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -125,7 +125,8 @@
 }
 
 bool DexFile::Open(const char* filename, const char* location, std::string* error_msg,
-                   std::vector<const DexFile*>* dex_files) {
+                   std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is NULL";
   uint32_t magic;
   ScopedFd fd(OpenAndReadMagic(filename, &magic, error_msg));
   if (fd.get() == -1) {
@@ -139,7 +140,7 @@
     std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true,
                                                               error_msg));
     if (dex_file.get() != nullptr) {
-      dex_files->push_back(dex_file.release());
+      dex_files->push_back(std::move(dex_file));
       return true;
     } else {
       return false;
@@ -179,8 +180,8 @@
   }
 }
 
-const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify,
-                                 std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
+                                                 std::string* error_msg) {
   CHECK(location != nullptr);
   std::unique_ptr<MemMap> map;
   {
@@ -224,13 +225,14 @@
     return nullptr;
   }
 
-  return dex_file.release();
+  return dex_file;
 }
 
 const char* DexFile::kClassesDex = "classes.dex";
 
 bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg,
-                      std::vector<const  DexFile*>* dex_files) {
+                      std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+  DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is NULL";
   std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
   if (zip_archive.get() == nullptr) {
     DCHECK(!error_msg->empty());
@@ -239,21 +241,22 @@
   return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files);
 }
 
-const DexFile* DexFile::OpenMemory(const std::string& location,
-                                   uint32_t location_checksum,
-                                   MemMap* mem_map,
-                                   std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
+                                                   uint32_t location_checksum,
+                                                   MemMap* mem_map,
+                                                   std::string* error_msg) {
   return OpenMemory(mem_map->Begin(),
                     mem_map->Size(),
                     location,
                     location_checksum,
                     mem_map,
+                    nullptr,
                     error_msg);
 }
 
-const DexFile* DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
-                             const std::string& location, std::string* error_msg,
-                             ZipOpenErrorCode* error_code) {
+std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
+                                             const std::string& location, std::string* error_msg,
+                                             ZipOpenErrorCode* error_code) {
   CHECK(!location.empty());
   std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
   if (zip_entry.get() == NULL) {
@@ -287,11 +290,13 @@
     return nullptr;
   }
   *error_code = ZipOpenErrorCode::kNoError;
-  return dex_file.release();
+  return dex_file;
 }
 
 bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
-                          std::string* error_msg, std::vector<const DexFile*>* dex_files) {
+                          std::string* error_msg,
+                          std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+  DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is NULL";
   ZipOpenErrorCode error_code;
   std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg,
                                                &error_code));
@@ -299,7 +304,7 @@
     return false;
   } else {
     // Had at least classes.dex.
-    dex_files->push_back(dex_file.release());
+    dex_files->push_back(std::move(dex_file));
 
     // Now try some more.
     size_t i = 2;
@@ -318,7 +323,7 @@
         }
         break;
       } else {
-        dex_files->push_back(next_dex_file.release());
+        dex_files->push_back(std::move(next_dex_file));
       }
 
       i++;
@@ -329,24 +334,27 @@
 }
 
 
-const DexFile* DexFile::OpenMemory(const uint8_t* base,
-                                   size_t size,
-                                   const std::string& location,
-                                   uint32_t location_checksum,
-                                   MemMap* mem_map, std::string* error_msg) {
+std::unique_ptr<const DexFile> DexFile::OpenMemory(const uint8_t* base,
+                                                   size_t size,
+                                                   const std::string& location,
+                                                   uint32_t location_checksum,
+                                                   MemMap* mem_map,
+                                                   const OatFile* oat_file,
+                                                   std::string* error_msg) {
   CHECK_ALIGNED(base, 4);  // various dex file structures must be word aligned
-  std::unique_ptr<DexFile> dex_file(new DexFile(base, size, location, location_checksum, mem_map));
+  std::unique_ptr<DexFile> dex_file(
+      new DexFile(base, size, location, location_checksum, mem_map, oat_file));
   if (!dex_file->Init(error_msg)) {
-    return nullptr;
-  } else {
-    return dex_file.release();
+    dex_file.reset();
   }
+  return std::unique_ptr<const DexFile>(dex_file.release());
 }
 
 DexFile::DexFile(const uint8_t* base, size_t size,
                  const std::string& location,
                  uint32_t location_checksum,
-                 MemMap* mem_map)
+                 MemMap* mem_map,
+                 const OatFile* oat_file)
     : begin_(base),
       size_(size),
       location_(location),
@@ -360,7 +368,8 @@
       proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
       class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
       find_class_def_misses_(0),
-      class_def_index_(nullptr) {
+      class_def_index_(nullptr),
+      oat_file_(oat_file) {
   CHECK(begin_ != NULL) << GetLocation();
   CHECK_GT(size_, 0U) << GetLocation();
 }
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index d8b1525..e121a08 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -44,6 +44,7 @@
 }  // namespace mirror
 class ClassLinker;
 class MemMap;
+class OatFile;
 class Signature;
 template<class T> class Handle;
 class StringPiece;
@@ -385,19 +386,21 @@
 
   // Opens .dex files found in the container, guessing the container format based on file extension.
   static bool Open(const char* filename, const char* location, std::string* error_msg,
-                   std::vector<const DexFile*>* dex_files);
+                   std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
   // Opens .dex file, backed by existing memory
-  static const DexFile* Open(const uint8_t* base, size_t size,
-                             const std::string& location,
-                             uint32_t location_checksum,
-                             std::string* error_msg) {
-    return OpenMemory(base, size, location, location_checksum, NULL, error_msg);
+  static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
+                                             const std::string& location,
+                                             uint32_t location_checksum,
+                                             const OatFile* oat_file,
+                                             std::string* error_msg) {
+    return OpenMemory(base, size, location, location_checksum, NULL, oat_file, error_msg);
   }
 
   // Open all classesXXX.dex files from a zip archive.
   static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
-                          std::string* error_msg, std::vector<const DexFile*>* dex_files);
+                          std::string* error_msg,
+                          std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
   // Closes a .dex file.
   virtual ~DexFile();
@@ -890,13 +893,18 @@
   //     the dex_location where it's file name part has been made canonical.
   static std::string GetDexCanonicalLocation(const char* dex_location);
 
+  const OatFile* GetOatFile() const {
+    return oat_file_;
+  }
+
  private:
   // Opens a .dex file
-  static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg);
+  static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location,
+                                                 bool verify, std::string* error_msg);
 
   // Opens dex files from within a .jar, .zip, or .apk file
   static bool OpenZip(int fd, const std::string& location, std::string* error_msg,
-                      std::vector<const DexFile*>* dex_files);
+                      std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
   enum class ZipOpenErrorCode {  // private
     kNoError,
@@ -909,28 +917,30 @@
 
   // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-nullptr
   // return.
-  static const DexFile* Open(const ZipArchive& zip_archive, const char* entry_name,
-                             const std::string& location, std::string* error_msg,
-                             ZipOpenErrorCode* error_code);
+  static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, const char* entry_name,
+                                             const std::string& location, std::string* error_msg,
+                                             ZipOpenErrorCode* error_code);
 
   // Opens a .dex file at the given address backed by a MemMap
-  static const DexFile* OpenMemory(const std::string& location,
-                                   uint32_t location_checksum,
-                                   MemMap* mem_map,
-                                   std::string* error_msg);
+  static std::unique_ptr<const DexFile> OpenMemory(const std::string& location,
+                                                   uint32_t location_checksum,
+                                                   MemMap* mem_map,
+                                                   std::string* error_msg);
 
   // Opens a .dex file at the given address, optionally backed by a MemMap
-  static const DexFile* OpenMemory(const uint8_t* dex_file,
-                                   size_t size,
-                                   const std::string& location,
-                                   uint32_t location_checksum,
-                                   MemMap* mem_map,
-                                   std::string* error_msg);
+  static std::unique_ptr<const DexFile> OpenMemory(const uint8_t* dex_file,
+                                                   size_t size,
+                                                   const std::string& location,
+                                                   uint32_t location_checksum,
+                                                   MemMap* mem_map,
+                                                   const OatFile* oat_file,
+                                                   std::string* error_msg);
 
   DexFile(const uint8_t* base, size_t size,
           const std::string& location,
           uint32_t location_checksum,
-          MemMap* mem_map);
+          MemMap* mem_map,
+          const OatFile* oat_file);
 
   // Top-level initializer that calls other Init methods.
   bool Init(std::string* error_msg);
@@ -1013,6 +1023,10 @@
   };
   typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, UTF16HashCmp, UTF16HashCmp> Index;
   mutable Atomic<Index*> class_def_index_;
+
+  // The oat file this dex file was loaded from. May be null in case the dex file is not coming
+  // from an oat file, e.g., directly from an apk.
+  const OatFile* oat_file_;
 };
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
 
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index b304779..7f5a181 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -21,6 +21,7 @@
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
+#include "dex_file-inl.h"
 #include "os.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
@@ -31,8 +32,8 @@
 
 TEST_F(DexFileTest, Open) {
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* dex(OpenTestDexFile("Nested"));
-  ASSERT_TRUE(dex != NULL);
+  std::unique_ptr<const DexFile> dex(OpenTestDexFile("Nested"));
+  ASSERT_TRUE(dex.get() != NULL);
 }
 
 static const uint8_t kBase64Map[256] = {
@@ -132,8 +133,8 @@
   "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
   "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
 
-static const DexFile* OpenDexFileBase64(const char* base64,
-                                        const char* location) {
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+                                                        const char* location) {
   // decode base64
   CHECK(base64 != NULL);
   size_t length;
@@ -154,11 +155,11 @@
   // read dex file
   ScopedObjectAccess soa(Thread::Current());
   std::string error_msg;
-  std::vector<const DexFile*> tmp;
+  std::vector<std::unique_ptr<const DexFile>> tmp;
   bool success = DexFile::Open(location, location, &error_msg, &tmp);
   CHECK(success) << error_msg;
   EXPECT_EQ(1U, tmp.size());
-  const DexFile* dex_file = tmp[0];
+  std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
   EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
   EXPECT_TRUE(dex_file->IsReadOnly());
   return dex_file;
@@ -197,7 +198,7 @@
 
 TEST_F(DexFileTest, GetLocationChecksum) {
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* raw(OpenTestDexFile("Main"));
+  std::unique_ptr<const DexFile> raw(OpenTestDexFile("Main"));
   EXPECT_NE(raw->GetHeader().checksum_, raw->GetLocationChecksum());
 }
 
@@ -212,8 +213,8 @@
 
 TEST_F(DexFileTest, ClassDefs) {
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* raw(OpenTestDexFile("Nested"));
-  ASSERT_TRUE(raw != NULL);
+  std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
+  ASSERT_TRUE(raw.get() != nullptr);
   EXPECT_EQ(2U, raw->NumClassDefs());
 
   const DexFile::ClassDef& c0 = raw->GetClassDef(0);
@@ -225,8 +226,8 @@
 
 TEST_F(DexFileTest, GetMethodSignature) {
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
-  ASSERT_TRUE(raw != NULL);
+  std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+  ASSERT_TRUE(raw.get() != nullptr);
   EXPECT_EQ(1U, raw->NumClassDefs());
 
   const DexFile::ClassDef& class_def = raw->GetClassDef(0);
@@ -275,8 +276,8 @@
 
 TEST_F(DexFileTest, FindStringId) {
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
-  ASSERT_TRUE(raw != NULL);
+  std::unique_ptr<const DexFile> raw(OpenTestDexFile("GetMethodSignature"));
+  ASSERT_TRUE(raw.get() != nullptr);
   EXPECT_EQ(1U, raw->NumClassDefs());
 
   const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index ec1e5f0..00ca8a9 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -101,8 +101,9 @@
   return dst.release();
 }
 
-static const DexFile* OpenDexFileBase64(const char* base64, const char* location,
-                                        std::string* error_msg) {
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+                                                        const char* location,
+                                                        std::string* error_msg) {
   // decode base64
   CHECK(base64 != NULL);
   size_t length;
@@ -122,11 +123,11 @@
 
   // read dex file
   ScopedObjectAccess soa(Thread::Current());
-  std::vector<const DexFile*> tmp;
+  std::vector<std::unique_ptr<const DexFile>> tmp;
   bool success = DexFile::Open(location, location, error_msg, &tmp);
   CHECK(success) << error_msg;
   EXPECT_EQ(1U, tmp.size());
-  const DexFile* dex_file = tmp[0];
+  std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
   EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
   EXPECT_TRUE(dex_file->IsReadOnly());
   return dex_file;
@@ -166,8 +167,9 @@
   header->checksum_ = adler_checksum;
 }
 
-static const DexFile* FixChecksumAndOpen(uint8_t* bytes, size_t length, const char* location,
-                                         std::string* error_msg) {
+static std::unique_ptr<const DexFile> FixChecksumAndOpen(uint8_t* bytes, size_t length,
+                                                         const char* location,
+                                                         std::string* error_msg) {
   // Check data.
   CHECK(bytes != nullptr);
 
@@ -187,12 +189,12 @@
 
   // read dex file
   ScopedObjectAccess soa(Thread::Current());
-  std::vector<const DexFile*> tmp;
+  std::vector<std::unique_ptr<const DexFile>> tmp;
   if (!DexFile::Open(location, location, error_msg, &tmp)) {
     return nullptr;
   }
   EXPECT_EQ(1U, tmp.size());
-  const DexFile* dex_file = tmp[0];
+  std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
   EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
   EXPECT_TRUE(dex_file->IsReadOnly());
   return dex_file;
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index 05214a4..a90f424 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -257,10 +257,10 @@
   V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
   V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
   V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
-  V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xF2, UNUSED_F2, "unused-f2", k10x, false, kUnknown, 0, kVerifyError) \
+  V(0xEF, IGET_BOOLEAN_QUICK, "iget-boolean-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+  V(0xF0, IGET_BYTE_QUICK, "iget-byte-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+  V(0xF1, IGET_CHAR_QUICK, "iget-char-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+  V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, true, kFieldRef, kContinue | kThrow | kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
   V(0xF3, UNUSED_F3, "unused-f3", k10x, false, kUnknown, 0, kVerifyError) \
   V(0xF4, UNUSED_F4, "unused-f4", k10x, false, kUnknown, 0, kVerifyError) \
   V(0xF5, UNUSED_F5, "unused-f5", k10x, false, kUnknown, 0, kVerifyError) \
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 6597235..a22e274 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -488,6 +488,20 @@
     return false;
   }
 
+  // We'd also like to confirm a shstrtab in program_header_only_ mode (else Open() does this for
+  // us). This is usually the last in an oat file, and a good indicator of whether writing was
+  // successful (or the process crashed and left garbage).
+  if (program_header_only_) {
+    // It might not be mapped, but we can compare against the file size.
+    int64_t offset = static_cast<int64_t>(GetHeader().e_shoff +
+                                          (GetHeader().e_shstrndx * GetHeader().e_shentsize));
+    if (offset >= file_->GetLength()) {
+      *error_msg = StringPrintf("Shstrtab is not in the mapped ELF file: '%s'",
+                                file_->GetPath().c_str());
+      return false;
+    }
+  }
+
   return true;
 }
 
@@ -1299,30 +1313,7 @@
   CHECK(program_header_only_) << file_->GetPath();
 
   if (executable) {
-    InstructionSet elf_ISA = kNone;
-    switch (GetHeader().e_machine) {
-      case EM_ARM: {
-        elf_ISA = kArm;
-        break;
-      }
-      case EM_AARCH64: {
-        elf_ISA = kArm64;
-        break;
-      }
-      case EM_386: {
-        elf_ISA = kX86;
-        break;
-      }
-      case EM_X86_64: {
-        elf_ISA = kX86_64;
-        break;
-      }
-      case EM_MIPS: {
-        elf_ISA = kMips;
-        break;
-      }
-    }
-
+    InstructionSet elf_ISA = GetInstructionSetFromELF(GetHeader().e_machine, GetHeader().e_flags);
     if (elf_ISA != kRuntimeISA) {
       std::ostringstream oss;
       oss << "Expected ISA " << kRuntimeISA << " but found " << elf_ISA;
diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h
index 7b00bad..418d937 100644
--- a/runtime/elf_utils.h
+++ b/runtime/elf_utils.h
@@ -30,6 +30,8 @@
 #define EF_ARM_EABI_VER5 0x05000000
 #define EF_MIPS_ABI_O32 0x00001000
 #define EF_MIPS_ARCH_32R2 0x70000000
+#define EF_MIPS_ARCH_32R6 0x90000000
+#define EF_MIPS_ARCH_64R6 0xa0000000
 
 #define EI_ABIVERSION 8
 #define EM_ARM 40
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 67265a2..35579d6 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -172,8 +172,8 @@
 template <bool kAccessCheck>
 ALWAYS_INLINE
 inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
-                                      mirror::ArtMethod* method,
                                       int32_t component_count,
+                                      mirror::ArtMethod* method,
                                       bool* slow_path) {
   if (UNLIKELY(component_count < 0)) {
     ThrowNegativeArraySizeException(component_count);
@@ -208,12 +208,12 @@
 template <bool kAccessCheck, bool kInstrumented>
 ALWAYS_INLINE
 inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
-                                         mirror::ArtMethod* method,
                                          int32_t component_count,
+                                         mirror::ArtMethod* method,
                                          Thread* self,
                                          gc::AllocatorType allocator_type) {
   bool slow_path = false;
-  mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, method, component_count,
+  mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, component_count, method,
                                                        &slow_path);
   if (UNLIKELY(slow_path)) {
     if (klass == nullptr) {
@@ -231,8 +231,8 @@
 template <bool kAccessCheck, bool kInstrumented>
 ALWAYS_INLINE
 inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
-                                                 mirror::ArtMethod* method,
                                                  int32_t component_count,
+                                                 mirror::ArtMethod* method,
                                                  Thread* self,
                                                  gc::AllocatorType allocator_type) {
   DCHECK(klass != nullptr);
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index c329fe6..db51264 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -33,8 +33,8 @@
 namespace art {
 
 static inline mirror::Class* CheckFilledNewArrayAlloc(uint32_t type_idx,
-                                                      mirror::ArtMethod* referrer,
                                                       int32_t component_count,
+                                                      mirror::ArtMethod* referrer,
                                                       Thread* self,
                                                       bool access_check)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -76,11 +76,11 @@
 }
 
 // Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer,
-                                          int32_t component_count, Thread* self,
+mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count,
+                                          mirror::ArtMethod* referrer, Thread* self,
                                           bool access_check,
                                           gc::AllocatorType /* allocator_type */) {
-  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self,
+  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
                                                   access_check);
   if (UNLIKELY(klass == nullptr)) {
     return nullptr;
@@ -96,12 +96,12 @@
 
 // Helper function to allocate array for FILLED_NEW_ARRAY.
 mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
-                                                      mirror::ArtMethod* referrer,
                                                       int32_t component_count,
+                                                      mirror::ArtMethod* referrer,
                                                       Thread* self,
                                                       bool access_check,
                                                       gc::AllocatorType /* allocator_type */) {
-  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self,
+  mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
                                                   access_check);
   if (UNLIKELY(klass == nullptr)) {
     return nullptr;
@@ -183,14 +183,12 @@
         env->SetObjectField(exc.get(),
                             WellKnownClasses::java_lang_Throwable_stackTrace,
                             stack_trace_elem.get());
-
-        // Throw the exception.
-        ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-        self->SetException(throw_location,
-            reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get())));
       } else {
         error_msg = "Could not create stack trace.";
       }
+      // Throw the exception.
+      self->SetException(self->GetCurrentLocationForThrow(),
+                         reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get())));
     } else {
       // Could not allocate a string object.
       error_msg = "Couldn't throw new StackOverflowError because JNI NewStringUTF failed.";
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 0531122..77eec46 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -80,8 +80,8 @@
 
 template <bool kAccessCheck>
 ALWAYS_INLINE inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
-                                                    mirror::ArtMethod* method,
                                                     int32_t component_count,
+                                                    mirror::ArtMethod* method,
                                                     bool* slow_path)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -91,29 +91,30 @@
 // check.
 template <bool kAccessCheck, bool kInstrumented>
 ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
-                                                       mirror::ArtMethod* method,
                                                        int32_t component_count,
+                                                       mirror::ArtMethod* method,
                                                        Thread* self,
                                                        gc::AllocatorType allocator_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 template <bool kAccessCheck, bool kInstrumented>
 ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
-                                                               mirror::ArtMethod* method,
                                                                int32_t component_count,
+                                                               mirror::ArtMethod* method,
                                                                Thread* self,
                                                                gc::AllocatorType allocator_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
-                                                 int32_t component_count, Thread* self,
+extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, int32_t component_count,
+                                                 mirror::ArtMethod* method, Thread* self,
                                                  bool access_check,
                                                  gc::AllocatorType allocator_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
+                                                             int32_t component_count,
                                                              mirror::ArtMethod* method,
-                                                             int32_t component_count, Thread* self,
+                                                             Thread* self,
                                                              bool access_check,
                                                              gc::AllocatorType allocator_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index 3b47f24..28e19d4 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -15,6 +15,7 @@
  */
 
 #include "class_linker.h"
+#include "dex_file-inl.h"
 #include "interpreter/interpreter.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/object-inl.h"
@@ -47,13 +48,9 @@
     }
   }
   uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
-  if (kUsePortableCompiler) {
-    InvokeWithShadowFrame(self, shadow_frame, arg_offset, result);
-  } else {
-    method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
-                   (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
-                   result, method->GetShorty());
-  }
+  method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
+                 (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
+                 result, method->GetShorty());
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
deleted file mode 100644
index de95f7d..0000000
--- a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
+++ /dev/null
@@ -1,76 +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 "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-static constexpr gc::AllocatorType kPortableAllocatorType =
-    gc::kUseRosAlloc ? gc::kAllocatorTypeRosAlloc : gc::kAllocatorTypeDlMalloc;
-
-extern "C" mirror::Object* art_portable_alloc_object_from_code(uint32_t type_idx,
-                                                               mirror::ArtMethod* referrer,
-                                                               Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return AllocObjectFromCode<false, true>(type_idx, referrer, thread, kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_alloc_object_from_code_with_access_check(uint32_t type_idx,
-                                                                                 mirror::ArtMethod* referrer,
-                                                                                 Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return AllocObjectFromCode<true, true>(type_idx, referrer, thread, kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_alloc_array_from_code(uint32_t type_idx,
-                                                              mirror::ArtMethod* referrer,
-                                                              uint32_t length,
-                                                              Thread* self)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return AllocArrayFromCode<false, true>(type_idx, referrer, length, self,
-                                         kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_alloc_array_from_code_with_access_check(uint32_t type_idx,
-                                                                                mirror::ArtMethod* referrer,
-                                                                                uint32_t length,
-                                                                                Thread* self)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return AllocArrayFromCode<true, true>(type_idx, referrer, length, self,
-                                        kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code(uint32_t type_idx,
-                                                                        mirror::ArtMethod* referrer,
-                                                                        uint32_t length,
-                                                                        Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return CheckAndAllocArrayFromCodeInstrumented(type_idx, referrer, length, thread, false,
-                                                kPortableAllocatorType);
-}
-
-extern "C" mirror::Object* art_portable_check_and_alloc_array_from_code_with_access_check(uint32_t type_idx,
-                                                                                          mirror::ArtMethod* referrer,
-                                                                                          uint32_t length,
-                                                                                          Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return CheckAndAllocArrayFromCodeInstrumented(type_idx, referrer, length, thread, true,
-                                                kPortableAllocatorType);
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_cast_entrypoints.cc b/runtime/entrypoints/portable/portable_cast_entrypoints.cc
deleted file mode 100644
index 151b178..0000000
--- a/runtime/entrypoints/portable/portable_cast_entrypoints.cc
+++ /dev/null
@@ -1,57 +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 "common_throws.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" int32_t art_portable_is_assignable_from_code(mirror::Class* dest_type,
-                                                        mirror::Class* src_type)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(dest_type != NULL);
-  DCHECK(src_type != NULL);
-  return dest_type->IsAssignableFrom(src_type) ? 1 : 0;
-}
-
-extern "C" void art_portable_check_cast_from_code(mirror::Class* dest_type,
-                                                  mirror::Class* src_type)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(dest_type->IsClass()) << PrettyClass(dest_type);
-  DCHECK(src_type->IsClass()) << PrettyClass(src_type);
-  if (UNLIKELY(!dest_type->IsAssignableFrom(src_type))) {
-    ThrowClassCastException(dest_type, src_type);
-  }
-}
-
-extern "C" void art_portable_check_put_array_element_from_code(mirror::Object* element,
-                                                               mirror::Object* array)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  if (element == NULL) {
-    return;
-  }
-  DCHECK(array != NULL);
-  mirror::Class* array_class = array->GetClass();
-  DCHECK(array_class != NULL);
-  mirror::Class* component_type = array_class->GetComponentType();
-  mirror::Class* element_class = element->GetClass();
-  if (UNLIKELY(!component_type->IsAssignableFrom(element_class))) {
-    ThrowArrayStoreException(element_class, array_class);
-  }
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc b/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
deleted file mode 100644
index 9364c46..0000000
--- a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
+++ /dev/null
@@ -1,53 +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 "entrypoints/entrypoint_utils-inl.h"
-#include "gc/accounting/card_table-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" mirror::Object* art_portable_initialize_static_storage_from_code(uint32_t type_idx,
-                                                                            mirror::ArtMethod* referrer,
-                                                                            Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return ResolveVerifyAndClinit(type_idx, referrer, thread, true, false);
-}
-
-extern "C" mirror::Object* art_portable_initialize_type_from_code(uint32_t type_idx,
-                                                                  mirror::ArtMethod* referrer,
-                                                                  Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return ResolveVerifyAndClinit(type_idx, referrer, thread, false, false);
-}
-
-extern "C" mirror::Object* art_portable_initialize_type_and_verify_access_from_code(uint32_t type_idx,
-                                                                                    mirror::ArtMethod* referrer,
-                                                                                    Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // Called when caller isn't guaranteed to have access to a type and the dex cache may be
-  // unpopulated
-  return ResolveVerifyAndClinit(type_idx, referrer, thread, false, true);
-}
-
-extern "C" mirror::Object* art_portable_resolve_string_from_code(mirror::ArtMethod* referrer,
-                                                                 uint32_t string_idx)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return ResolveStringFromCode(referrer, string_idx);
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_entrypoints.h b/runtime/entrypoints/portable/portable_entrypoints.h
deleted file mode 100644
index 6f77e1c..0000000
--- a/runtime/entrypoints/portable/portable_entrypoints.h
+++ /dev/null
@@ -1,44 +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.
- */
-
-#ifndef ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ENTRYPOINTS_H_
-#define ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ENTRYPOINTS_H_
-
-#include "dex_file-inl.h"
-#include "runtime.h"
-
-namespace art {
-namespace mirror {
-  class ArtMethod;
-  class Object;
-}  // namespace mirror
-class Thread;
-
-#define PORTABLE_ENTRYPOINT_OFFSET(ptr_size, x) \
-    Thread::PortableEntryPointOffset<ptr_size>(OFFSETOF_MEMBER(PortableEntryPoints, x))
-
-// Pointers to functions that are called by code generated by compiler's adhering to the portable
-// compiler ABI.
-struct PACKED(4) PortableEntryPoints {
-  // Invocation
-  void (*pPortableImtConflictTrampoline)(mirror::ArtMethod*);
-  void (*pPortableResolutionTrampoline)(mirror::ArtMethod*);
-  void (*pPortableToInterpreterBridge)(mirror::ArtMethod*);
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ENTRYPOINTS_H_
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
deleted file mode 100644
index 371aca4..0000000
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ /dev/null
@@ -1,245 +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 "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_field-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" int32_t art_portable_set32_static_from_code(uint32_t field_idx,
-                                                       mirror::ArtMethod* referrer,
-                                                       int32_t new_value)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx,
-                               referrer,
-                               StaticPrimitiveWrite,
-                               sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set32<false>(field->GetDeclaringClass(), new_value);
-    return 0;
-  }
-  field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
-                                                        sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set32<false>(field->GetDeclaringClass(), new_value);
-    return 0;
-  }
-  return -1;
-}
-
-extern "C" int32_t art_portable_set64_static_from_code(uint32_t field_idx,
-                                                       mirror::ArtMethod* referrer,
-                                                       int64_t new_value)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set64<false>(field->GetDeclaringClass(), new_value);
-    return 0;
-  }
-  field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
-                                                        sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set64<false>(field->GetDeclaringClass(), new_value);
-    return 0;
-  }
-  return -1;
-}
-
-extern "C" int32_t art_portable_set_obj_static_from_code(uint32_t field_idx,
-                                                         mirror::ArtMethod* referrer,
-                                                         mirror::Object* new_value)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite,
-                                          sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->SetObj<false>(field->GetDeclaringClass(), new_value);
-    return 0;
-  }
-  field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(),
-                                                     sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->SetObj<false>(field->GetDeclaringClass(), new_value);
-    return 0;
-  }
-  return -1;
-}
-
-extern "C" int32_t art_portable_get32_static_from_code(uint32_t field_idx,
-                                                       mirror::ArtMethod* referrer)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get32(field->GetDeclaringClass());
-  }
-  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
-                                                       sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get32(field->GetDeclaringClass());
-  }
-  return 0;
-}
-
-extern "C" int64_t art_portable_get64_static_from_code(uint32_t field_idx,
-                                                       mirror::ArtMethod* referrer)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead, sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get64(field->GetDeclaringClass());
-  }
-  field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
-                                                       sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get64(field->GetDeclaringClass());
-  }
-  return 0;
-}
-
-extern "C" mirror::Object* art_portable_get_obj_static_from_code(uint32_t field_idx,
-                                                                 mirror::ArtMethod* referrer)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectRead,
-                                          sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    return field->GetObj(field->GetDeclaringClass());
-  }
-  field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, Thread::Current(),
-                                                    sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    return field->GetObj(field->GetDeclaringClass());
-  }
-  return 0;
-}
-
-extern "C" int32_t art_portable_set32_instance_from_code(uint32_t field_idx,
-                                                         mirror::ArtMethod* referrer,
-                                                         mirror::Object* obj, uint32_t new_value)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set32<false>(obj, new_value);
-    return 0;
-  }
-  field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
-                                                          sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set32<false>(obj, new_value);
-    return 0;
-  }
-  return -1;
-}
-
-extern "C" int32_t art_portable_set64_instance_from_code(uint32_t field_idx,
-                                                         mirror::ArtMethod* referrer,
-                                                         mirror::Object* obj, int64_t new_value)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set64<false>(obj, new_value);
-    return 0;
-  }
-  field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
-                                                          sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->Set64<false>(obj, new_value);
-    return 0;
-  }
-  return -1;
-}
-
-extern "C" int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx,
-                                                           mirror::ArtMethod* referrer,
-                                                           mirror::Object* obj,
-                                                           mirror::Object* new_value)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
-                                          sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->SetObj<false>(obj, new_value);
-    return 0;
-  }
-  field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(),
-                                                       sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    // Compiled code can't use transactional mode.
-    field->SetObj<false>(obj, new_value);
-    return 0;
-  }
-  return -1;
-}
-
-extern "C" int32_t art_portable_get32_instance_from_code(uint32_t field_idx,
-                                                         mirror::ArtMethod* referrer,
-                                                         mirror::Object* obj)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get32(obj);
-  }
-  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
-                                                         sizeof(uint32_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get32(obj);
-  }
-  return 0;
-}
-
-extern "C" int64_t art_portable_get64_instance_from_code(uint32_t field_idx,
-                                                         mirror::ArtMethod* referrer,
-                                                         mirror::Object* obj)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get64(obj);
-  }
-  field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
-                                                         sizeof(uint64_t));
-  if (LIKELY(field != NULL)) {
-    return field->Get64(obj);
-  }
-  return 0;
-}
-
-extern "C" mirror::Object* art_portable_get_obj_instance_from_code(uint32_t field_idx,
-                                                                   mirror::ArtMethod* referrer,
-                                                                   mirror::Object* obj)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectRead,
-                                          sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    return field->GetObj(obj);
-  }
-  field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, Thread::Current(),
-                                                      sizeof(mirror::HeapReference<mirror::Object>));
-  if (LIKELY(field != NULL)) {
-    return field->GetObj(obj);
-  }
-  return 0;
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
deleted file mode 100644
index afe769e..0000000
--- a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
+++ /dev/null
@@ -1,35 +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 "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "mirror/art_method-inl.h"
-
-namespace art {
-
-extern "C" void art_portable_fill_array_data_from_code(mirror::ArtMethod* method,
-                                                       uint32_t dex_pc,
-                                                       mirror::Array* array,
-                                                       uint32_t payload_offset)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  UNUSED(dex_pc);
-  const DexFile::CodeItem* code_item = method->GetCodeItem();
-  const Instruction::ArrayDataPayload* payload =
-      reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item->insns_ + payload_offset);
-  FillArrayData(array, payload);
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
deleted file mode 100644
index 6f9c083..0000000
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ /dev/null
@@ -1,118 +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 "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/dex_cache-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-template<InvokeType type, bool access_check>
-mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object,
-                                    mirror::ArtMethod* caller_method, Thread* self) {
-  mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method,
-                                             access_check, type);
-  if (UNLIKELY(method == NULL)) {
-    // Note: This can cause thread suspension.
-    self->AssertThreadSuspensionIsAllowable();
-    method = FindMethodFromCode<type, access_check>(method_idx, &this_object, &caller_method,
-                                                    self);
-    if (UNLIKELY(method == NULL)) {
-      CHECK(self->IsExceptionPending());
-      return 0;  // failure
-    }
-  }
-  DCHECK(!self->IsExceptionPending());
-  const void* code = method->GetEntryPointFromPortableCompiledCode();
-
-  // When we return, the caller will branch to this address, so it had better not be 0!
-  if (UNLIKELY(code == NULL)) {
-      LOG(FATAL) << "Code was NULL in method: " << PrettyMethod(method)
-                 << " location: " << method->GetDexFile()->GetLocation();
-  }
-  return method;
-}
-
-// Explicit template declarations of FindMethodHelper for all invoke types.
-#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check)                        \
-  template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)                                         \
-  mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx,               \
-                                                            mirror::Object* this_object,       \
-                                                            mirror::ArtMethod* caller_method,  \
-                                                            Thread* thread)
-#define EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(_type) \
-    EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, false);   \
-    EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, true)
-
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kStatic);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kDirect);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kVirtual);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kSuper);
-EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kInterface);
-
-#undef EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL
-#undef EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL
-
-extern "C" mirror::Object* art_portable_find_static_method_from_code_with_access_check(uint32_t method_idx,
-                                                                                       mirror::Object* this_object,
-                                                                                       mirror::ArtMethod* referrer,
-                                                                                       Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return FindMethodHelper<kStatic, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access_check(uint32_t method_idx,
-                                                                                       mirror::Object* this_object,
-                                                                                       mirror::ArtMethod* referrer,
-                                                                                       Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return FindMethodHelper<kDirect, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_access_check(uint32_t method_idx,
-                                                                                        mirror::Object* this_object,
-                                                                                        mirror::ArtMethod* referrer,
-                                                                                        Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return FindMethodHelper<kVirtual, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_check(uint32_t method_idx,
-                                                                                      mirror::Object* this_object,
-                                                                                      mirror::ArtMethod* referrer,
-                                                                                      Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return FindMethodHelper<kSuper, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_access_check(uint32_t method_idx,
-                                                                                          mirror::Object* this_object,
-                                                                                          mirror::ArtMethod* referrer,
-                                                                                          Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return FindMethodHelper<kInterface, true>(method_idx, this_object, referrer, thread);
-}
-
-extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t method_idx,
-                                                                        mirror::Object* this_object,
-                                                                        mirror::ArtMethod* referrer,
-                                                                        Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  return FindMethodHelper<kInterface, false>(method_idx, this_object, referrer, thread);
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_jni_entrypoints.cc b/runtime/entrypoints/portable/portable_jni_entrypoints.cc
deleted file mode 100644
index 0d0f21b..0000000
--- a/runtime/entrypoints/portable/portable_jni_entrypoints.cc
+++ /dev/null
@@ -1,99 +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 "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-#include "thread-inl.h"
-
-namespace art {
-
-// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_.
-extern "C" uint32_t art_portable_jni_method_start(Thread* self)
-    UNLOCK_FUNCTION(Locks::mutator_lock_) {
-  JNIEnvExt* env = self->GetJniEnv();
-  uint32_t saved_local_ref_cookie = env->local_ref_cookie;
-  env->local_ref_cookie = env->locals.GetSegmentState();
-  self->TransitionFromRunnableToSuspended(kNative);
-  return saved_local_ref_cookie;
-}
-
-extern "C" uint32_t art_portable_jni_method_start_synchronized(jobject to_lock, Thread* self)
-    UNLOCK_FUNCTION(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS {
-  self->DecodeJObject(to_lock)->MonitorEnter(self);
-  return art_portable_jni_method_start(self);
-}
-
-static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  JNIEnvExt* env = self->GetJniEnv();
-  env->locals.SetSegmentState(env->local_ref_cookie);
-  env->local_ref_cookie = saved_local_ref_cookie;
-}
-
-extern "C" void art_portable_jni_method_end(uint32_t saved_local_ref_cookie, Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
-  self->TransitionFromSuspendedToRunnable();
-  PopLocalReferences(saved_local_ref_cookie, self);
-}
-
-
-extern "C" void art_portable_jni_method_end_synchronized(uint32_t saved_local_ref_cookie,
-                                              jobject locked,
-                                              Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
-  self->TransitionFromSuspendedToRunnable();
-  UnlockJniSynchronizedMethod(locked, self);  // Must decode before pop.
-  PopLocalReferences(saved_local_ref_cookie, self);
-}
-
-extern "C" mirror::Object* art_portable_jni_method_end_with_reference(jobject result,
-                                                                      uint32_t saved_local_ref_cookie,
-                                                                      Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
-  self->TransitionFromSuspendedToRunnable();
-  mirror::Object* o = self->DecodeJObject(result);  // Must decode before pop.
-  PopLocalReferences(saved_local_ref_cookie, self);
-  // Process result.
-  if (UNLIKELY(self->GetJniEnv()->check_jni)) {
-    if (self->IsExceptionPending()) {
-      return NULL;
-    }
-    CheckReferenceResult(o, self);
-  }
-  return o;
-}
-
-extern "C" mirror::Object* art_portable_jni_method_end_with_reference_synchronized(jobject result,
-                                                                                   uint32_t saved_local_ref_cookie,
-                                                                                   jobject locked,
-                                                                                   Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) {
-  self->TransitionFromSuspendedToRunnable();
-  UnlockJniSynchronizedMethod(locked, self);  // Must decode before pop.
-  mirror::Object* o = self->DecodeJObject(result);
-  PopLocalReferences(saved_local_ref_cookie, self);
-  // Process result.
-  if (UNLIKELY(self->GetJniEnv()->check_jni)) {
-    if (self->IsExceptionPending()) {
-      return NULL;
-    }
-    CheckReferenceResult(o, self);
-  }
-  return o;
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_lock_entrypoints.cc b/runtime/entrypoints/portable/portable_lock_entrypoints.cc
deleted file mode 100644
index fcd3e9d..0000000
--- a/runtime/entrypoints/portable/portable_lock_entrypoints.cc
+++ /dev/null
@@ -1,40 +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 "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" void art_portable_lock_object_from_code(mirror::Object* obj, Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-    NO_THREAD_SAFETY_ANALYSIS /* EXCLUSIVE_LOCK_FUNCTION(Monitor::monitor_lock_) */ {
-  DCHECK(obj != nullptr);        // Assumed to have been checked before entry.
-  obj->MonitorEnter(thread);  // May block.
-  DCHECK(thread->HoldsLock(obj));
-  // Only possible exception is NPE and is handled before entry.
-  DCHECK(!thread->IsExceptionPending());
-}
-
-extern "C" void art_portable_unlock_object_from_code(mirror::Object* obj, Thread* thread)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-    NO_THREAD_SAFETY_ANALYSIS /* UNLOCK_FUNCTION(Monitor::monitor_lock_) */ {
-  DCHECK(obj != nullptr);  // Assumed to have been checked before entry.
-  // MonitorExit may throw exception.
-  obj->MonitorExit(thread);
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
deleted file mode 100644
index 95ac66c..0000000
--- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc
+++ /dev/null
@@ -1,94 +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 "mirror/art_method-inl.h"
-#include "verifier/dex_gc_map.h"
-#include "stack.h"
-#include "thread-inl.h"
-
-namespace art {
-
-class ShadowFrameCopyVisitor : public StackVisitor {
- public:
-  explicit ShadowFrameCopyVisitor(Thread* self) : StackVisitor(self, NULL), prev_frame_(NULL),
-      top_frame_(NULL) {}
-
-  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (IsShadowFrame()) {
-      ShadowFrame* cur_frame = GetCurrentShadowFrame();
-      size_t num_regs = cur_frame->NumberOfVRegs();
-      mirror::ArtMethod* method = cur_frame->GetMethod();
-      uint32_t dex_pc = cur_frame->GetDexPC();
-      ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, method, dex_pc);
-
-      const uint8_t* gc_map = method->GetNativeGcMap(sizeof(void*));
-      verifier::DexPcToReferenceMap dex_gc_map(gc_map);
-      const uint8_t* reg_bitmap = dex_gc_map.FindBitMap(dex_pc);
-      for (size_t reg = 0; reg < num_regs; ++reg) {
-        if (TestBitmap(reg, reg_bitmap)) {
-          new_frame->SetVRegReference(reg, cur_frame->GetVRegReference(reg));
-        } else {
-          new_frame->SetVReg(reg, cur_frame->GetVReg(reg));
-        }
-      }
-
-      if (prev_frame_ != NULL) {
-        prev_frame_->SetLink(new_frame);
-      } else {
-        top_frame_ = new_frame;
-      }
-      prev_frame_ = new_frame;
-    }
-    return true;
-  }
-
-  ShadowFrame* GetShadowFrameCopy() {
-    return top_frame_;
-  }
-
- private:
-  static bool TestBitmap(int reg, const uint8_t* reg_vector) {
-    return ((reg_vector[reg / 8] >> (reg % 8)) & 0x01) != 0;
-  }
-
-  ShadowFrame* prev_frame_;
-  ShadowFrame* top_frame_;
-};
-
-extern "C" void art_portable_test_suspend_from_code(Thread* self)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  self->CheckSuspend();
-  if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) {
-    // Save out the shadow frame to the heap
-    ShadowFrameCopyVisitor visitor(self);
-    visitor.WalkStack(true);
-    self->SetDeoptimizationShadowFrame(visitor.GetShadowFrameCopy());
-    self->SetDeoptimizationReturnValue(JValue());
-    self->SetException(ThrowLocation(), Thread::GetDeoptimizationException());
-  }
-}
-
-extern "C" ShadowFrame* art_portable_push_shadow_frame_from_code(Thread* thread,
-                                                                 ShadowFrame* new_shadow_frame,
-                                                                 mirror::ArtMethod* method,
-                                                                 uint32_t num_vregs) {
-  ShadowFrame* old_frame = thread->PushShadowFrame(new_shadow_frame);
-  new_shadow_frame->SetMethod(method);
-  new_shadow_frame->SetNumberOfVRegs(num_vregs);
-  return old_frame;
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_throw_entrypoints.cc b/runtime/entrypoints/portable/portable_throw_entrypoints.cc
deleted file mode 100644
index 4317358..0000000
--- a/runtime/entrypoints/portable/portable_throw_entrypoints.cc
+++ /dev/null
@@ -1,128 +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 "dex_instruction.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-
-namespace art {
-
-extern "C" void art_portable_throw_div_zero_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ThrowArithmeticExceptionDivideByZero();
-}
-
-extern "C" void art_portable_throw_array_bounds_from_code(int32_t index, int32_t length)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ThrowArrayIndexOutOfBoundsException(index, length);
-}
-
-extern "C" void art_portable_throw_no_such_method_from_code(int32_t method_idx)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ThrowNoSuchMethodError(method_idx);
-}
-
-extern "C" void art_portable_throw_null_pointer_exception_from_code(uint32_t dex_pc)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // TODO: remove dex_pc argument from caller.
-  UNUSED(dex_pc);
-  Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  ThrowNullPointerExceptionFromDexPC(throw_location);
-}
-
-extern "C" void art_portable_throw_stack_overflow_from_code() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ThrowStackOverflowError(Thread::Current());
-}
-
-extern "C" void art_portable_throw_exception_from_code(mirror::Throwable* exception)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  if (exception == NULL) {
-    ThrowNullPointerException(NULL, "throw with null exception");
-  } else {
-    self->SetException(throw_location, exception);
-  }
-}
-
-extern "C" void* art_portable_get_and_clear_exception(Thread* self)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(self->IsExceptionPending());
-  // TODO: make this inline.
-  mirror::Throwable* exception = self->GetException(NULL);
-  self->ClearException();
-  return exception;
-}
-
-extern "C" int32_t art_portable_find_catch_block_from_code(mirror::ArtMethod* current_method,
-                                                           uint32_t ti_offset)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  Thread* self = Thread::Current();  // TODO: make an argument.
-  ThrowLocation throw_location;
-  mirror::Throwable* exception = self->GetException(&throw_location);
-  // Check for special deoptimization exception.
-  if (UNLIKELY(reinterpret_cast<intptr_t>(exception) == -1)) {
-    return -1;
-  }
-  mirror::Class* exception_type = exception->GetClass();
-  StackHandleScope<1> hs(self);
-  const DexFile::CodeItem* code_item = current_method->GetCodeItem();
-  DCHECK_LT(ti_offset, code_item->tries_size_);
-  const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item, ti_offset);
-
-  int iter_index = 0;
-  int result = -1;
-  uint32_t catch_dex_pc = -1;
-  // Iterate over the catch handlers associated with dex_pc
-  for (CatchHandlerIterator it(*code_item, *try_item); it.HasNext(); it.Next()) {
-    uint16_t iter_type_idx = it.GetHandlerTypeIndex();
-    // Catch all case
-    if (iter_type_idx == DexFile::kDexNoIndex16) {
-      catch_dex_pc = it.GetHandlerAddress();
-      result = iter_index;
-      break;
-    }
-    // Does this catch exception type apply?
-    mirror::Class* iter_exception_type =
-        current_method->GetDexCacheResolvedType(iter_type_idx);
-    if (UNLIKELY(iter_exception_type == NULL)) {
-      // TODO: check, the verifier (class linker?) should take care of resolving all exception
-      //       classes early.
-      LOG(WARNING) << "Unresolved exception class when finding catch block: "
-          << current_method->GetTypeDescriptorFromTypeIdx(iter_type_idx);
-    } else if (iter_exception_type->IsAssignableFrom(exception_type)) {
-      catch_dex_pc = it.GetHandlerAddress();
-      result = iter_index;
-      break;
-    }
-    ++iter_index;
-  }
-  if (result != -1) {
-    // Handler found.
-    Runtime::Current()->GetInstrumentation()->ExceptionCaughtEvent(
-        self, throw_location, current_method, catch_dex_pc, exception);
-    // If the catch block has no move-exception then clear the exception for it.
-    const Instruction* first_catch_instr = Instruction::At(
-        &current_method->GetCodeItem()->insns_[catch_dex_pc]);
-    if (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION) {
-      self->ClearException();
-    }
-  }
-  return result;
-}
-
-}  // namespace art
diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
deleted file mode 100644
index 2a2771f..0000000
--- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
+++ /dev/null
@@ -1,496 +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.
- */
-
-#ifndef ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
-#define ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
-
-#include "dex_instruction-inl.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "entrypoints/runtime_asm_entrypoints.h"
-#include "interpreter/interpreter.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
-
-namespace art {
-
-class ShortyHelper {
- public:
-  ShortyHelper(const char* shorty, uint32_t shorty_len, bool is_static)
-      : shorty_(shorty), shorty_len_(shorty_len), is_static_(is_static) {
-  }
-
-  const char* GetShorty() const {
-    return shorty_;
-  }
-
-  uint32_t GetShortyLength() const {
-    return shorty_len_;
-  }
-
-  size_t NumArgs() const {
-    // "1 +" because the first in Args is the receiver.
-    // "- 1" because we don't count the return type.
-    return (is_static_ ? 0 : 1) + GetShortyLength() - 1;
-  }
-
-  // Get the primitive type associated with the given parameter.
-  Primitive::Type GetParamPrimitiveType(size_t param) const {
-    CHECK_LT(param, NumArgs());
-    if (is_static_) {
-      param++;  // 0th argument must skip return value at start of the shorty.
-    } else if (param == 0) {
-      return Primitive::kPrimNot;
-    }
-    return Primitive::GetType(shorty_[param]);
-  }
-
-  // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods.
-  bool IsParamALongOrDouble(size_t param) const {
-    Primitive::Type type = GetParamPrimitiveType(param);
-    return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
-  }
-
-  // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods.
-  bool IsParamAReference(size_t param) const {
-    return GetParamPrimitiveType(param) == Primitive::kPrimNot;
-  }
-
- private:
-  const char* const shorty_;
-  const uint32_t shorty_len_;
-  const bool is_static_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShortyHelper);
-};
-
-// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
-class PortableArgumentVisitor {
- public:
-// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
-// Size of Runtime::kRefAndArgs callee save frame.
-// Size of Method* and register parameters in out stack arguments.
-#if defined(__arm__)
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
-#define PORTABLE_STACK_ARG_SKIP 0
-#elif defined(__mips__)
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64
-#define PORTABLE_STACK_ARG_SKIP 16
-#elif defined(__i386__)
-// For x86 there are no register arguments and the stack pointer will point directly to the called
-// method argument passed by the caller.
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define PORTABLE_STACK_ARG_SKIP 4
-#elif defined(__x86_64__)
-// TODO: implement and check these.
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 16
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 96
-#define PORTABLE_STACK_ARG_SKIP 0
-#else
-// TODO: portable should be disabled for aarch64 for now.
-// #error "Unsupported architecture"
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define PORTABLE_STACK_ARG_SKIP 0
-#endif
-
-  PortableArgumentVisitor(ShortyHelper& caller_mh, mirror::ArtMethod** sp)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
-    caller_mh_(caller_mh),
-    args_in_regs_(ComputeArgsInRegs(caller_mh)),
-    num_params_(caller_mh.NumArgs()),
-    reg_args_(reinterpret_cast<uint8_t*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
-    stack_args_(reinterpret_cast<uint8_t*>(sp) + PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
-                + PORTABLE_STACK_ARG_SKIP),
-    cur_args_(reg_args_),
-    cur_arg_index_(0),
-    param_index_(0) {
-  }
-
-  virtual ~PortableArgumentVisitor() {}
-
-  virtual void Visit() = 0;
-
-  bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return caller_mh_.IsParamAReference(param_index_);
-  }
-
-  bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return caller_mh_.IsParamALongOrDouble(param_index_);
-  }
-
-  Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return caller_mh_.GetParamPrimitiveType(param_index_);
-  }
-
-  uint8_t* GetParamAddress() const {
-    return cur_args_ + (cur_arg_index_ * sizeof(void*));
-  }
-
-  void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    for (cur_arg_index_ = 0;  cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
-#if (defined(__arm__) || defined(__mips__))
-      if (IsParamALongOrDouble() && cur_arg_index_ == 2) {
-        break;
-      }
-#endif
-      Visit();
-      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
-      param_index_++;
-    }
-    cur_args_ = stack_args_;
-    cur_arg_index_ = 0;
-    while (param_index_ < num_params_) {
-#if (defined(__arm__) || defined(__mips__))
-      if (IsParamALongOrDouble() && cur_arg_index_ % 2 != 0) {
-        cur_arg_index_++;
-      }
-#endif
-      Visit();
-      cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
-      param_index_++;
-    }
-  }
-
- private:
-  static size_t ComputeArgsInRegs(ShortyHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#if (defined(__i386__))
-    UNUSED(mh);
-    return 0;
-#else
-    size_t args_in_regs = 0;
-    size_t num_params = mh.NumArgs();
-    for (size_t i = 0; i < num_params; i++) {
-      args_in_regs = args_in_regs + (mh.IsParamALongOrDouble(i) ? 2 : 1);
-      if (args_in_regs > 3) {
-        args_in_regs = 3;
-        break;
-      }
-    }
-    return args_in_regs;
-#endif
-  }
-  ShortyHelper& caller_mh_;
-  const size_t args_in_regs_;
-  const size_t num_params_;
-  uint8_t* const reg_args_;
-  uint8_t* const stack_args_;
-  uint8_t* cur_args_;
-  size_t cur_arg_index_;
-  size_t param_index_;
-};
-
-// Visits arguments on the stack placing them into the shadow frame.
-class BuildPortableShadowFrameVisitor : public PortableArgumentVisitor {
- public:
-  BuildPortableShadowFrameVisitor(ShortyHelper& caller_mh, mirror::ArtMethod** sp,
-      ShadowFrame& sf, size_t first_arg_reg) :
-    PortableArgumentVisitor(caller_mh, sp), sf_(sf), cur_reg_(first_arg_reg) { }
-  virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    Primitive::Type type = GetParamPrimitiveType();
-    switch (type) {
-      case Primitive::kPrimLong:  // Fall-through.
-      case Primitive::kPrimDouble:
-        sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress()));
-        ++cur_reg_;
-        break;
-      case Primitive::kPrimNot:
-        sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress()));
-        break;
-      case Primitive::kPrimBoolean:  // Fall-through.
-      case Primitive::kPrimByte:     // Fall-through.
-      case Primitive::kPrimChar:     // Fall-through.
-      case Primitive::kPrimShort:    // Fall-through.
-      case Primitive::kPrimInt:      // Fall-through.
-      case Primitive::kPrimFloat:
-        sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress()));
-        break;
-      case Primitive::kPrimVoid:
-        LOG(FATAL) << "UNREACHABLE";
-        UNREACHABLE();
-    }
-    ++cur_reg_;
-  }
-
- private:
-  ShadowFrame& sf_;
-  size_t cur_reg_;
-
-  DISALLOW_COPY_AND_ASSIGN(BuildPortableShadowFrameVisitor);
-};
-
-extern "C" uint64_t artPortableToInterpreterBridge(mirror::ArtMethod* method, Thread* self,
-                                                   mirror::ArtMethod** sp)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // Ensure we don't get thread suspension until the object arguments are safely in the shadow
-  // frame.
-  // FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
-
-  if (method->IsAbstract()) {
-    ThrowAbstractMethodError(method);
-    return 0;
-  } else {
-    const char* old_cause = self->StartAssertNoThreadSuspension("Building interpreter shadow frame");
-    StackHandleScope<2> hs(self);
-    uint32_t shorty_len;
-    const char* shorty = method->GetShorty(&shorty_len);
-    ShortyHelper mh(shorty, shorty_len, method->IsStatic());
-    const DexFile::CodeItem* code_item = method->GetCodeItem();
-    uint16_t num_regs = code_item->registers_size_;
-    void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
-    ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, NULL,  // No last shadow coming from quick.
-                                                  method, 0, memory));
-    size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
-    BuildPortableShadowFrameVisitor shadow_frame_builder(mh, sp,
-                                                 *shadow_frame, first_arg_reg);
-    shadow_frame_builder.VisitArguments();
-    // Push a transition back into managed code onto the linked list in thread.
-    ManagedStack fragment;
-    self->PushManagedStackFragment(&fragment);
-    self->PushShadowFrame(shadow_frame);
-    self->EndAssertNoThreadSuspension(old_cause);
-
-    if (method->IsStatic() && !method->GetDeclaringClass()->IsInitialized()) {
-      // Ensure static method's class is initialized.
-      Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
-      if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
-        DCHECK(Thread::Current()->IsExceptionPending());
-        self->PopManagedStackFragment(fragment);
-        return 0;
-      }
-    }
-
-    JValue result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame);
-    // Pop transition.
-    self->PopManagedStackFragment(fragment);
-    return result.GetJ();
-  }
-}
-
-// Visits arguments on the stack placing them into the args vector, Object* arguments are converted
-// to jobjects.
-class BuildPortableArgumentVisitor : public PortableArgumentVisitor {
- public:
-  BuildPortableArgumentVisitor(ShortyHelper& caller_mh, mirror::ArtMethod** sp,
-                               ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) :
-    PortableArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {}
-
-  virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    jvalue val;
-    Primitive::Type type = GetParamPrimitiveType();
-    switch (type) {
-      case Primitive::kPrimNot: {
-        mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress());
-        val.l = soa_.AddLocalReference<jobject>(obj);
-        break;
-      }
-      case Primitive::kPrimLong:  // Fall-through.
-      case Primitive::kPrimDouble:
-        val.j = *reinterpret_cast<jlong*>(GetParamAddress());
-        break;
-      case Primitive::kPrimBoolean:  // Fall-through.
-      case Primitive::kPrimByte:     // Fall-through.
-      case Primitive::kPrimChar:     // Fall-through.
-      case Primitive::kPrimShort:    // Fall-through.
-      case Primitive::kPrimInt:      // Fall-through.
-      case Primitive::kPrimFloat:
-        val.i =  *reinterpret_cast<jint*>(GetParamAddress());
-        break;
-      case Primitive::kPrimVoid:
-        LOG(FATAL) << "UNREACHABLE";
-        UNREACHABLE();
-    }
-    args_.push_back(val);
-  }
-
- private:
-  ScopedObjectAccessUnchecked& soa_;
-  std::vector<jvalue>& args_;
-
-  DISALLOW_COPY_AND_ASSIGN(BuildPortableArgumentVisitor);
-};
-
-// Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method
-// which is responsible for recording callee save registers. We explicitly place into jobjects the
-// incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a
-// field within the proxy object, which will box the primitive arguments and deal with error cases.
-extern "C" uint64_t artPortableProxyInvokeHandler(mirror::ArtMethod* proxy_method,
-                                                  mirror::Object* receiver,
-                                                  Thread* self, mirror::ArtMethod** sp)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
-  const char* old_cause =
-      self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
-  self->VerifyStack();
-  // Start new JNI local reference state.
-  JNIEnvExt* env = self->GetJniEnv();
-  ScopedObjectAccessUnchecked soa(env);
-  ScopedJniEnvLocalRefState env_state(env);
-  // Create local ref. copies of proxy method and the receiver.
-  jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver);
-
-  // Placing arguments into args vector and remove the receiver.
-  uint32_t shorty_len;
-  const char* shorty = proxy_method->GetShorty(&shorty_len);
-  ShortyHelper proxy_mh(shorty, shorty_len, false);
-  std::vector<jvalue> args;
-  BuildPortableArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args);
-  local_ref_visitor.VisitArguments();
-  args.erase(args.begin());
-
-  // Convert proxy method into expected interface method.
-  mirror::ArtMethod* interface_method = proxy_method->FindOverriddenMethod();
-  DCHECK(interface_method != NULL);
-  DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
-  jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method);
-
-  // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code
-  // that performs allocations.
-  self->EndAssertNoThreadSuspension(old_cause);
-  JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
-                                               rcvr_jobj, interface_method_jobj, args);
-  return result.GetJ();
-}
-
-// Lazily resolve a method for portable. Called by stub code.
-extern "C" const void* artPortableResolutionTrampoline(mirror::ArtMethod* called,
-                                                       mirror::Object* receiver,
-                                                       Thread* self,
-                                                       mirror::ArtMethod** called_addr)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  uint32_t dex_pc;
-  mirror::ArtMethod* caller = self->GetCurrentMethod(&dex_pc);
-
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  InvokeType invoke_type;
-  bool is_range;
-  if (called->IsRuntimeMethod()) {
-    const DexFile::CodeItem* code = caller->GetCodeItem();
-    CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-    const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
-    Instruction::Code instr_code = instr->Opcode();
-    switch (instr_code) {
-      case Instruction::INVOKE_DIRECT:
-        invoke_type = kDirect;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_DIRECT_RANGE:
-        invoke_type = kDirect;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_STATIC:
-        invoke_type = kStatic;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_STATIC_RANGE:
-        invoke_type = kStatic;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_SUPER:
-        invoke_type = kSuper;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_SUPER_RANGE:
-        invoke_type = kSuper;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_VIRTUAL:
-        invoke_type = kVirtual;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_VIRTUAL_RANGE:
-        invoke_type = kVirtual;
-        is_range = true;
-        break;
-      case Instruction::INVOKE_INTERFACE:
-        invoke_type = kInterface;
-        is_range = false;
-        break;
-      case Instruction::INVOKE_INTERFACE_RANGE:
-        invoke_type = kInterface;
-        is_range = true;
-        break;
-      default:
-        LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL);
-        // Avoid used uninitialized warnings.
-        invoke_type = kDirect;
-        is_range = true;
-    }
-    uint32_t dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
-    called = class_linker->ResolveMethod(Thread::Current(), dex_method_idx, &caller, invoke_type);
-    // Incompatible class change should have been handled in resolve method.
-    CHECK(!called->CheckIncompatibleClassChange(invoke_type));
-    // Refine called method based on receiver.
-    if (invoke_type == kVirtual) {
-      called = receiver->GetClass()->FindVirtualMethodForVirtual(called);
-    } else if (invoke_type == kInterface) {
-      called = receiver->GetClass()->FindVirtualMethodForInterface(called);
-    }
-  } else {
-    CHECK(called->IsStatic()) << PrettyMethod(called);
-    invoke_type = kStatic;
-    // Incompatible class change should have been handled in resolve method.
-    CHECK(!called->CheckIncompatibleClassChange(invoke_type));
-  }
-  const void* code = nullptr;
-  if (LIKELY(!self->IsExceptionPending())) {
-    // Ensure that the called method's class is initialized.
-    StackHandleScope<1> hs(self);
-    Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass()));
-    class_linker->EnsureInitialized(self, called_class, true, true);
-    if (LIKELY(called_class->IsInitialized())) {
-      code = called->GetEntryPointFromPortableCompiledCode();
-      // TODO: remove this after we solve the link issue.
-      if (code == nullptr) {
-        bool have_portable_code;
-        code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
-      }
-    } else if (called_class->IsInitializing()) {
-      if (invoke_type == kStatic) {
-        // Class is still initializing, go to oat and grab code (trampoline must be left in place
-        // until class is initialized to stop races between threads).
-        bool have_portable_code;
-        code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
-      } else {
-        // No trampoline for non-static methods.
-        code = called->GetEntryPointFromPortableCompiledCode();
-        // TODO: remove this after we solve the link issue.
-        if (code == nullptr) {
-          bool have_portable_code;
-          code = class_linker->GetPortableOatCodeFor(called, &have_portable_code);
-        }
-      }
-    } else {
-      DCHECK(called_class->IsErroneous());
-    }
-  }
-  if (LIKELY(code != nullptr)) {
-    // Expect class to at least be initializing.
-    DCHECK(called->GetDeclaringClass()->IsInitializing());
-    // Don't want infinite recursion.
-    DCHECK(!class_linker->IsPortableResolutionStub(code));
-    // Set up entry into main method
-    *called_addr = called;
-  }
-  return code;
-}
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index 9ffd199..8cd6ca6 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -27,6 +27,7 @@
 #include "arch/arm/quick_method_frame_info_arm.h"
 #include "arch/arm64/quick_method_frame_info_arm64.h"
 #include "arch/mips/quick_method_frame_info_mips.h"
+#include "arch/mips64/quick_method_frame_info_mips64.h"
 #include "arch/x86/quick_method_frame_info_x86.h"
 #include "arch/x86_64/quick_method_frame_info_x86_64.h"
 
@@ -76,6 +77,7 @@
   return (isa == kArm || isa == kThumb2) ? arm::ArmCalleeSaveFrameSize(type) :
          isa == kArm64 ? arm64::Arm64CalleeSaveFrameSize(type) :
          isa == kMips ? mips::MipsCalleeSaveFrameSize(type) :
+         isa == kMips64 ? mips64::Mips64CalleeSaveFrameSize(type) :
          isa == kX86 ? x86::X86CalleeSaveFrameSize(type) :
          isa == kX86_64 ? x86_64::X86_64CalleeSaveFrameSize(type) :
          isa == kNone ? (LOG(FATAL) << "kNone has no frame size", 0) :
@@ -88,6 +90,7 @@
   return (isa == kArm || isa == kThumb2) ? kArmPointerSize :
          isa == kArm64 ? kArm64PointerSize :
          isa == kMips ? kMipsPointerSize :
+         isa == kMips64 ? kMips64PointerSize :
          isa == kX86 ? kX86PointerSize :
          isa == kX86_64 ? kX86_64PointerSize :
          isa == kNone ? (LOG(FATAL) << "kNone has no pointer size", 0) :
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index c0b79b2..c049e3d 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -114,44 +114,44 @@
   return AllocObjectFromCode<true, instrumented_bool>(type_idx, method, self, allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+    uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocArrayFromCode<false, instrumented_bool>(type_idx, method, component_count, self, \
+  return AllocArrayFromCode<false, instrumented_bool>(type_idx, component_count, method, self, \
                                                       allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \
-    mirror::Class* klass, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+    mirror::Class* klass, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, method, component_count, self, \
+  return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, component_count, method, self, \
                                                               allocator_type); \
 } \
 extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+    uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
-  return AllocArrayFromCode<true, instrumented_bool>(type_idx, method, component_count, self, \
+  return AllocArrayFromCode<true, instrumented_bool>(type_idx, component_count, method, self, \
                                                      allocator_type); \
 } \
 extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+    uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
   if (!instrumented_bool) { \
-    return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, false, allocator_type); \
+    return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, false, allocator_type); \
   } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false, allocator_type); \
+    return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, false, allocator_type); \
   } \
 } \
 extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
-    uint32_t type_idx, mirror::ArtMethod* method, int32_t component_count, Thread* self) \
+    uint32_t type_idx, int32_t component_count, mirror::ArtMethod* method, Thread* self) \
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
   ScopedQuickEntrypointChecks sqec(self); \
   if (!instrumented_bool) { \
-    return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true, allocator_type); \
+    return CheckAndAllocArrayFromCode(type_idx, component_count, method, self, true, allocator_type); \
   } else { \
-    return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true, allocator_type); \
+    return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, true, allocator_type); \
   } \
 }
 
@@ -163,26 +163,28 @@
 GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RosAlloc, gc::kAllocatorTypeRosAlloc)
 GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(BumpPointer, gc::kAllocatorTypeBumpPointer)
 GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(TLAB, gc::kAllocatorTypeTLAB)
+GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(Region, gc::kAllocatorTypeRegion)
+GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RegionTLAB, gc::kAllocatorTypeRegionTLAB)
 
 #define GENERATE_ENTRYPOINTS(suffix) \
-extern "C" void* art_quick_alloc_array##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_resolved##suffix(void* klass, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_alloc_object_resolved##suffix(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_initialized##suffix(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(void* klass, void*, int32_t); \
-extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(void* klass, void* method); \
-extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, void* method); \
-extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, void*, int32_t); \
-extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, void*, int32_t); \
+extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object##suffix(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirror::Class* klass, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
 void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrumented) { \
   if (instrumented) { \
     qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \
@@ -213,6 +215,8 @@
 GENERATE_ENTRYPOINTS(_rosalloc)
 GENERATE_ENTRYPOINTS(_bump_pointer)
 GENERATE_ENTRYPOINTS(_tlab)
+GENERATE_ENTRYPOINTS(_region)
+GENERATE_ENTRYPOINTS(_region_tlab)
 #endif
 
 static bool entry_points_instrumented = false;
@@ -247,6 +251,16 @@
       SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented);
       return;
     }
+    case gc::kAllocatorTypeRegion: {
+      CHECK(kMovingCollector);
+      SetQuickAllocEntryPoints_region(qpoints, entry_points_instrumented);
+      return;
+    }
+    case gc::kAllocatorTypeRegionTLAB: {
+      CHECK(kMovingCollector);
+      SetQuickAllocEntryPoints_region_tlab(qpoints, entry_points_instrumented);
+      return;
+    }
     default:
       break;
   }
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 7d77721..b7e8d50 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -19,16 +19,25 @@
 
 #include <cstdint>
 
+namespace art {
+namespace mirror {
+class Array;
+class ArtMethod;
+class Class;
+class Object;
+}  // namespace mirror
+}  // namespace art
+
 // These are extern declarations of assembly stubs with common names.
 
 // Cast entrypoints.
-extern "C" void art_quick_check_cast(void*, void*);
+extern "C" void art_quick_check_cast(const art::mirror::Class*, const art::mirror::Class*);
 
 // DexCache entrypoints.
-extern "C" void* art_quick_initialize_static_storage(uint32_t, void*);
-extern "C" void* art_quick_initialize_type(uint32_t, void*);
-extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*);
-extern "C" void* art_quick_resolve_string(void*, uint32_t);
+extern "C" void* art_quick_initialize_static_storage(uint32_t, art::mirror::ArtMethod*);
+extern "C" void* art_quick_initialize_type(uint32_t, art::mirror::ArtMethod*);
+extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, art::mirror::ArtMethod*);
+extern "C" void* art_quick_resolve_string(uint32_t, art::mirror::ArtMethod*);
 
 // Field entrypoints.
 extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
@@ -57,14 +66,16 @@
 extern "C" void* art_quick_get_obj_static(uint32_t);
 
 // Array entrypoints.
-extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
-extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(art::mirror::Array*, int32_t,
+                                                             art::mirror::Object*);
+extern "C" void art_quick_aput_obj_with_bound_check(art::mirror::Array*, int32_t,
+                                                    art::mirror::Object*);
+extern "C" void art_quick_aput_obj(art::mirror::Array*, int32_t, art::mirror::Object*);
 extern "C" void art_quick_handle_fill_data(void*, void*);
 
 // Lock entrypoints.
-extern "C" void art_quick_lock_object(void*);
-extern "C" void art_quick_unlock_object(void*);
+extern "C" void art_quick_lock_object(art::mirror::Object*);
+extern "C" void art_quick_unlock_object(art::mirror::Object*);
 
 // Math entrypoints.
 extern "C" int64_t art_quick_d2l(double);
@@ -99,7 +110,7 @@
 extern "C" void art_quick_test_suspend();
 
 // Throw entrypoints.
-extern "C" void art_quick_deliver_exception(void*);
+extern "C" void art_quick_deliver_exception(art::mirror::Object*);
 extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit);
 extern "C" void art_quick_throw_div_zero();
 extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 2e7c8ba..348495d 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -55,8 +55,8 @@
   return ResolveVerifyAndClinit(type_idx, referrer, self, false, true);
 }
 
-extern "C" mirror::String* artResolveStringFromCode(mirror::ArtMethod* referrer,
-                                                    int32_t string_idx,
+extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx,
+                                                    mirror::ArtMethod* referrer,
                                                     Thread* self)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 8c108a8..db8c0e3 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -28,6 +28,7 @@
 namespace art {
 
 namespace mirror {
+class Array;
 class ArtMethod;
 class Class;
 class Object;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_enum.h b/runtime/entrypoints/quick/quick_entrypoints_enum.h
index 84158cd..5a95491 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_enum.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_enum.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_ENUM_H_
 
 #include "quick_entrypoints.h"
+#include "quick_entrypoints_enum.h"
 #include "thread.h"
 
 namespace art {
@@ -47,10 +48,20 @@
   #undef ENTRYPOINT_ENUM
   };
   LOG(FATAL) << "Unexpected trampoline " << static_cast<int>(trampoline);
-  return ThreadOffset<pointer_size>(-1);
+  UNREACHABLE();
 }
 
-}  // namespace art
+// Do a check functions to be able to test whether the right signature is used.
+template <QuickEntrypointEnum entrypoint, typename... Types>
+void CheckEntrypointTypes();
 
+#define ENTRYPOINT_ENUM(name, ...) \
+template <> inline void CheckEntrypointTypes<kQuick ## name, __VA_ARGS__>() {};  // NOLINT [readability/braces] [4]
+#include "quick_entrypoints_list.h"
+  QUICK_ENTRYPOINT_LIST(ENTRYPOINT_ENUM)
+#undef QUICK_ENTRYPOINT_LIST
+#undef ENTRYPOINT_ENUM
+
+}  // namespace art
 
 #endif  // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_ENUM_H_
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index fbc7913..da454f3 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -20,23 +20,23 @@
 // All quick entrypoints. Format is name, return type, argument types.
 
 #define QUICK_ENTRYPOINT_LIST(V) \
-  V(AllocArray, void*, uint32_t, void*, int32_t) \
-  V(AllocArrayResolved, void*, void*, void*, int32_t) \
-  V(AllocArrayWithAccessCheck, void*, uint32_t, void*, int32_t) \
-  V(AllocObject, void*, uint32_t, void*) \
-  V(AllocObjectResolved, void*, void*, void*) \
-  V(AllocObjectInitialized, void*, void*, void*) \
-  V(AllocObjectWithAccessCheck, void*, uint32_t, void*) \
-  V(CheckAndAllocArray, void*, uint32_t, void*, int32_t) \
-  V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, void*, int32_t) \
+  V(AllocArray, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+  V(AllocArrayResolved, void*, mirror::Class*, int32_t, mirror::ArtMethod*) \
+  V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+  V(AllocObject, void*, uint32_t, mirror::ArtMethod*) \
+  V(AllocObjectResolved, void*, mirror::Class*, mirror::ArtMethod*) \
+  V(AllocObjectInitialized, void*, mirror::Class*, mirror::ArtMethod*) \
+  V(AllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*) \
+  V(CheckAndAllocArray, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+  V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, mirror::ArtMethod*) \
 \
   V(InstanceofNonTrivial, uint32_t, const mirror::Class*, const mirror::Class*) \
-  V(CheckCast, void , void*, void*) \
+  V(CheckCast, void, const mirror::Class*, const mirror::Class*) \
 \
-  V(InitializeStaticStorage, void*, uint32_t, void*) \
-  V(InitializeTypeAndVerifyAccess, void*, uint32_t, void*) \
-  V(InitializeType, void*, uint32_t, void*) \
-  V(ResolveString, void*, void*, uint32_t) \
+  V(InitializeStaticStorage, void*, uint32_t, mirror::ArtMethod*) \
+  V(InitializeTypeAndVerifyAccess, void*, uint32_t, mirror::ArtMethod*) \
+  V(InitializeType, void*, uint32_t, mirror::ArtMethod*) \
+  V(ResolveString, void*, uint32_t, mirror::ArtMethod*) \
 \
   V(Set8Instance, int, uint32_t, void*, int8_t) \
   V(Set8Static, int, uint32_t, int8_t) \
@@ -63,21 +63,21 @@
   V(GetObjInstance, void*, uint32_t, void*) \
   V(GetObjStatic, void*, uint32_t) \
 \
-  V(AputObjectWithNullAndBoundCheck, void, void*, uint32_t, void*) \
-  V(AputObjectWithBoundCheck, void, void*, uint32_t, void*) \
-  V(AputObject, void, void*, uint32_t, void*) \
+  V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
+  V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
+  V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \
   V(HandleFillArrayData, void, void*, void*) \
 \
   V(JniMethodStart, uint32_t, Thread*) \
-  V(JniMethodStartSynchronized, uint32_t, jobject to_lock, Thread* self) \
-  V(JniMethodEnd, void, uint32_t cookie, Thread* self) \
-  V(JniMethodEndSynchronized, void, uint32_t cookie, jobject locked, Thread* self) \
-  V(JniMethodEndWithReference, mirror::Object*, jobject result, uint32_t cookie, Thread* self) \
-  V(JniMethodEndWithReferenceSynchronized, mirror::Object*, jobject result, uint32_t cookie, jobject locked, Thread* self) \
+  V(JniMethodStartSynchronized, uint32_t, jobject, Thread*) \
+  V(JniMethodEnd, void, uint32_t, Thread*) \
+  V(JniMethodEndSynchronized, void, uint32_t, jobject, Thread*) \
+  V(JniMethodEndWithReference, mirror::Object*, jobject, uint32_t, Thread*) \
+  V(JniMethodEndWithReferenceSynchronized, mirror::Object*, jobject, uint32_t, jobject, Thread*) \
   V(QuickGenericJniTrampoline, void, mirror::ArtMethod*) \
 \
-  V(LockObject, void, void*) \
-  V(UnlockObject, void, void*) \
+  V(LockObject, void, mirror::Object*) \
+  V(UnlockObject, void, mirror::Object*) \
 \
   V(CmpgDouble, int32_t, double, double) \
   V(CmpgFloat, int32_t, float, float) \
@@ -114,7 +114,7 @@
 \
   V(TestSuspend, void, void) \
 \
-  V(DeliverException, void, void*) \
+  V(DeliverException, void, mirror::Object*) \
   V(ThrowArrayBounds, void, int32_t, int32_t) \
   V(ThrowDivZero, void, void) \
   V(ThrowNoSuchMethod, void, int32_t) \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index cb81629..00251ff 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -59,10 +59,13 @@
   // | S0         |
   // |            |    4x2 bytes padding
   // | Method*    |  <- sp
+  static constexpr bool kSplitPairAcrossRegisterAndStack = kArm32QuickCodeUseSoftFloat;
+  static constexpr bool kAlignPairRegister = !kArm32QuickCodeUseSoftFloat;
   static constexpr bool kQuickSoftFloatAbi = kArm32QuickCodeUseSoftFloat;
   static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = !kArm32QuickCodeUseSoftFloat;
   static constexpr size_t kNumQuickGprArgs = 3;
   static constexpr size_t kNumQuickFprArgs = kArm32QuickCodeUseSoftFloat ? 0 : 16;
+  static constexpr bool kGprFprLockstep = false;
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
       arm::ArmCalleeSaveFpr1Offset(Runtime::kRefsAndArgs);  // Offset of first FPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
@@ -93,10 +96,13 @@
   // | D0         |
   // |            |    padding
   // | Method*    |  <- sp
+  static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+  static constexpr bool kAlignPairRegister = false;
   static constexpr bool kQuickSoftFloatAbi = false;  // This is a hard float ABI.
   static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 7;  // 7 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 8;  // 8 arguments passed in FPRs.
+  static constexpr bool kGprFprLockstep = false;
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =
       arm64::Arm64CalleeSaveFpr1Offset(Runtime::kRefsAndArgs);  // Offset of first FPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset =
@@ -106,7 +112,7 @@
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
   // The callee save frame is pointed to by SP.
   // | argN       |  |
   // | ...        |  |
@@ -121,16 +127,67 @@
   // | A2         |    arg2
   // | A1         |    arg1
   // | A0/Method* |  <- sp
+  static constexpr bool kSplitPairAcrossRegisterAndStack = true;
+  static constexpr bool kAlignPairRegister = false;
   static constexpr bool kQuickSoftFloatAbi = true;  // This is a soft float ABI.
   static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 3;  // 3 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 0;  // 0 arguments passed in FPRs.
+  static constexpr bool kGprFprLockstep = false;
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 16;  // Offset of first GPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 60;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
+#elif defined(__mips__) && defined(__LP64__)
+  // The callee save frame is pointed to by SP.
+  // | argN       |  |
+  // | ...        |  |
+  // | arg4       |  |
+  // | arg3 spill |  |  Caller's frame
+  // | arg2 spill |  |
+  // | arg1 spill |  |
+  // | Method*    | ---
+  // | RA         |
+  // | ...        |    callee saves
+  // | F7         |    f_arg7
+  // | F6         |    f_arg6
+  // | F5         |    f_arg5
+  // | F6         |    f_arg6
+  // | F5         |    f_arg5
+  // | F4         |    f_arg4
+  // | F3         |    f_arg3
+  // | F2         |    f_arg2
+  // | F1         |    f_arg1
+  // | F0         |    f_arg0
+  // | A7         |    arg7
+  // | A6         |    arg6
+  // | A5         |    arg5
+  // | A4         |    arg4
+  // | A3         |    arg3
+  // | A2         |    arg2
+  // | A1         |    arg1
+  // |            |    padding
+  // | A0/Method* |  <- sp
+  // NOTE: for Mip64, when A0 is skipped, F0 is also skipped.
+  static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+  static constexpr bool kAlignPairRegister = false;
+  static constexpr bool kQuickSoftFloatAbi = false;
+  static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
+  // These values are set to zeros because GPR and FPR register
+  // assignments for Mips64 are interleaved, which the current VisitArguments()
+  // function does not support.
+  static constexpr size_t kNumQuickGprArgs = 7;  // 7 arguments passed in GPRs.
+  static constexpr size_t kNumQuickFprArgs = 7;  // 7 arguments passed in FPRs.
+  static constexpr bool kGprFprLockstep = true;
+
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 24;  // Offset of first FPR arg (F1).
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80;  // Offset of first GPR arg (A1).
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 200;  // Offset of return address.
+  static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+    return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
+  }
 #elif defined(__i386__)
   // The callee save frame is pointed to by SP.
   // | argN        |  |
@@ -145,14 +202,21 @@
   // | EBX         |    arg3
   // | EDX         |    arg2
   // | ECX         |    arg1
+  // | XMM3        |    float arg 4
+  // | XMM2        |    float arg 3
+  // | XMM1        |    float arg 2
+  // | XMM0        |    float arg 1
   // | EAX/Method* |  <- sp
-  static constexpr bool kQuickSoftFloatAbi = true;  // This is a soft float ABI.
+  static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+  static constexpr bool kAlignPairRegister = false;
+  static constexpr bool kQuickSoftFloatAbi = false;  // This is a hard float ABI.
   static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 3;  // 3 arguments passed in GPRs.
-  static constexpr size_t kNumQuickFprArgs = 0;  // 0 arguments passed in FPRs.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4;  // Offset of first GPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28;  // Offset of return address.
+  static constexpr size_t kNumQuickFprArgs = 4;  // 4 arguments passed in FPRs.
+  static constexpr bool kGprFprLockstep = false;
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 4;  // Offset of first FPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4 + 4*8;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28 + 4*8;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
   }
@@ -184,10 +248,13 @@
   // | XMM0            |    float arg 1
   // | Padding         |
   // | RDI/Method*     |  <- sp
+  static constexpr bool kSplitPairAcrossRegisterAndStack = false;
+  static constexpr bool kAlignPairRegister = false;
   static constexpr bool kQuickSoftFloatAbi = false;  // This is a hard float ABI.
   static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false;
   static constexpr size_t kNumQuickGprArgs = 5;  // 5 arguments passed in GPRs.
   static constexpr size_t kNumQuickFprArgs = 8;  // 8 arguments passed in FPRs.
+  static constexpr bool kGprFprLockstep = false;
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16;  // Offset of first FPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80 + 4*8;  // Offset of first GPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168 + 4*8;  // Offset of return address.
@@ -208,6 +275,22 @@
 #endif
 
  public:
+  // Special handling for proxy methods. Proxy methods are instance methods so the
+  // 'this' object is the 1st argument. They also have the same frame layout as the
+  // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the
+  // 1st GPR.
+  static mirror::Object* GetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CHECK(sp->AsMirrorPtr()->IsProxyMethod());
+    CHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, sp->AsMirrorPtr()->GetFrameSizeInBytes());
+    CHECK_GT(kNumQuickGprArgs, 0u);
+    constexpr uint32_t kThisGprIndex = 0u;  // 'this' is in the 1st GPR.
+    size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
+        GprIndexToGprOffset(kThisGprIndex);
+    uint8_t* this_arg_address = reinterpret_cast<uint8_t*>(sp) + this_arg_offset;
+    return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address)->AsMirrorPtr();
+  }
+
   static mirror::ArtMethod* GetCallingMethod(StackReference<mirror::ArtMethod>* sp)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
@@ -288,14 +371,23 @@
   }
 
   uint64_t ReadSplitLongParam() const {
-    DCHECK(IsSplitLongOrDouble());
-    // Read low half from register.
-    uint64_t low_half = *reinterpret_cast<uint32_t*>(GetParamAddress());
-    // Read high half from the stack. As current stack_index_ indexes the argument, the high part
-    // index should be (stack_index_ + 1).
-    uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_
-        + (stack_index_ + 1) * kBytesStackArgLocation);
-    return (low_half & 0xffffffffULL) | (high_half << 32);
+    // The splitted long is always available through the stack.
+    return *reinterpret_cast<uint64_t*>(stack_args_
+        + stack_index_ * kBytesStackArgLocation);
+  }
+
+  void IncGprIndex() {
+    gpr_index_++;
+    if (kGprFprLockstep) {
+      fpr_index_++;
+    }
+  }
+
+  void IncFprIndex() {
+    fpr_index_++;
+    if (kGprFprLockstep) {
+      gpr_index_++;
+    }
   }
 
   void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -314,7 +406,7 @@
       Visit();
       stack_index_++;
       if (kNumQuickGprArgs > 0) {
-        gpr_index_++;
+        IncGprIndex();
       }
     }
     for (uint32_t shorty_index = 1; shorty_index < shorty_len_; ++shorty_index) {
@@ -330,7 +422,7 @@
           Visit();
           stack_index_++;
           if (gpr_index_ < kNumQuickGprArgs) {
-            gpr_index_++;
+            IncGprIndex();
           }
           break;
         case Primitive::kPrimFloat:
@@ -339,11 +431,11 @@
           stack_index_++;
           if (kQuickSoftFloatAbi) {
             if (gpr_index_ < kNumQuickGprArgs) {
-              gpr_index_++;
+              IncGprIndex();
             }
           } else {
             if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
-              fpr_index_++;
+              IncFprIndex();
               if (kQuickDoubleRegAlignedFloatBackFilled) {
                 // Double should not overlap with float.
                 // For example, if fpr_index_ = 3, fpr_double_index_ should be at least 4.
@@ -359,8 +451,18 @@
         case Primitive::kPrimDouble:
         case Primitive::kPrimLong:
           if (kQuickSoftFloatAbi || (cur_type_ == Primitive::kPrimLong)) {
+            if (cur_type_ == Primitive::kPrimLong && kAlignPairRegister && gpr_index_ == 0) {
+              // Currently, this is only for ARM, where the first available parameter register
+              // is R1. So we skip it, and use R2 instead.
+              IncGprIndex();
+            }
             is_split_long_or_double_ = (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) &&
                 ((gpr_index_ + 1) == kNumQuickGprArgs);
+            if (!kSplitPairAcrossRegisterAndStack && is_split_long_or_double_) {
+              // We don't want to split this. Pass over this register.
+              gpr_index_++;
+              is_split_long_or_double_ = false;
+            }
             Visit();
             if (kBytesStackArgLocation == 4) {
               stack_index_+= 2;
@@ -369,10 +471,10 @@
               stack_index_++;
             }
             if (gpr_index_ < kNumQuickGprArgs) {
-              gpr_index_++;
+              IncGprIndex();
               if (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) {
                 if (gpr_index_ < kNumQuickGprArgs) {
-                  gpr_index_++;
+                  IncGprIndex();
                 }
               }
             }
@@ -395,10 +497,10 @@
                 }
               }
             } else if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
-              fpr_index_++;
+              IncFprIndex();
               if (GetBytesPerFprSpillLocation(kRuntimeISA) == 4) {
                 if (fpr_index_ + 1 < kNumQuickFprArgs + 1) {
-                  fpr_index_++;
+                  IncFprIndex();
                 }
               }
             }
@@ -435,6 +537,13 @@
   bool is_split_long_or_double_;
 };
 
+// Returns the 'this' object of a proxy method. This function is only used by StackVisitor. It
+// allows to use the QuickArgumentVisitor constants without moving all the code in its own module.
+extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  return QuickArgumentVisitor::GetProxyThisObject(sp);
+}
+
 // Visits arguments on the stack placing them into the shadow frame.
 class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor {
  public:
@@ -832,6 +941,16 @@
           (caller->GetDexCacheResolvedMethod(update_dex_cache_method_index) != called)) {
         caller->SetDexCacheResolvedMethod(update_dex_cache_method_index, called);
       }
+    } else if (invoke_type == kStatic) {
+      const auto called_dex_method_idx = called->GetDexMethodIndex();
+      // For static invokes, we may dispatch to the static method in the superclass but resolve
+      // using the subclass. To prevent getting slow paths on each invoke, we force set the
+      // resolved method for the super class dex method index if we are in the same dex file.
+      // b/19175856
+      if (called->GetDexFile() == called_method.dex_file &&
+          called_method.dex_method_index != called_dex_method_idx) {
+        called->GetDexCache()->SetResolvedMethod(called_dex_method_idx, called);
+      }
     }
     // Ensure that the called method's class is initialized.
     StackHandleScope<1> hs(soa.Self());
@@ -911,7 +1030,8 @@
   static constexpr size_t kRegistersNeededForLong = 2;
   static constexpr size_t kRegistersNeededForDouble = 2;
   static constexpr bool kMultiRegistersAligned = true;
-  static constexpr bool kMultiRegistersWidened = false;
+  static constexpr bool kMultiFPRegistersWidened = false;
+  static constexpr bool kMultiGPRegistersWidened = false;
   static constexpr bool kAlignLongOnStack = true;
   static constexpr bool kAlignDoubleOnStack = true;
 #elif defined(__aarch64__)
@@ -922,10 +1042,11 @@
   static constexpr size_t kRegistersNeededForLong = 1;
   static constexpr size_t kRegistersNeededForDouble = 1;
   static constexpr bool kMultiRegistersAligned = false;
-  static constexpr bool kMultiRegistersWidened = false;
+  static constexpr bool kMultiFPRegistersWidened = false;
+  static constexpr bool kMultiGPRegistersWidened = false;
   static constexpr bool kAlignLongOnStack = false;
   static constexpr bool kAlignDoubleOnStack = false;
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
   static constexpr bool kNativeSoftFloatAbi = true;  // This is a hard float ABI.
   static constexpr size_t kNumNativeGprArgs = 4;  // 4 arguments passed in GPRs.
   static constexpr size_t kNumNativeFprArgs = 0;  // 0 arguments passed in FPRs.
@@ -933,9 +1054,23 @@
   static constexpr size_t kRegistersNeededForLong = 2;
   static constexpr size_t kRegistersNeededForDouble = 2;
   static constexpr bool kMultiRegistersAligned = true;
-  static constexpr bool kMultiRegistersWidened = true;
+  static constexpr bool kMultiFPRegistersWidened = true;
+  static constexpr bool kMultiGPRegistersWidened = false;
   static constexpr bool kAlignLongOnStack = true;
   static constexpr bool kAlignDoubleOnStack = true;
+#elif defined(__mips__) && defined(__LP64__)
+  // Let the code prepare GPRs only and we will load the FPRs with same data.
+  static constexpr bool kNativeSoftFloatAbi = true;
+  static constexpr size_t kNumNativeGprArgs = 8;
+  static constexpr size_t kNumNativeFprArgs = 0;
+
+  static constexpr size_t kRegistersNeededForLong = 1;
+  static constexpr size_t kRegistersNeededForDouble = 1;
+  static constexpr bool kMultiRegistersAligned = false;
+  static constexpr bool kMultiFPRegistersWidened = false;
+  static constexpr bool kMultiGPRegistersWidened = true;
+  static constexpr bool kAlignLongOnStack = false;
+  static constexpr bool kAlignDoubleOnStack = false;
 #elif defined(__i386__)
   // TODO: Check these!
   static constexpr bool kNativeSoftFloatAbi = false;  // Not using int registers for fp
@@ -945,7 +1080,8 @@
   static constexpr size_t kRegistersNeededForLong = 2;
   static constexpr size_t kRegistersNeededForDouble = 2;
   static constexpr bool kMultiRegistersAligned = false;  // x86 not using regs, anyways
-  static constexpr bool kMultiRegistersWidened = false;
+  static constexpr bool kMultiFPRegistersWidened = false;
+  static constexpr bool kMultiGPRegistersWidened = false;
   static constexpr bool kAlignLongOnStack = false;
   static constexpr bool kAlignDoubleOnStack = false;
 #elif defined(__x86_64__)
@@ -956,7 +1092,8 @@
   static constexpr size_t kRegistersNeededForLong = 1;
   static constexpr size_t kRegistersNeededForDouble = 1;
   static constexpr bool kMultiRegistersAligned = false;
-  static constexpr bool kMultiRegistersWidened = false;
+  static constexpr bool kMultiFPRegistersWidened = false;
+  static constexpr bool kMultiGPRegistersWidened = false;
   static constexpr bool kAlignLongOnStack = false;
   static constexpr bool kAlignDoubleOnStack = false;
 #else
@@ -1015,10 +1152,20 @@
   void AdvanceInt(uint32_t val) {
     if (HaveIntGpr()) {
       gpr_index_--;
-      PushGpr(val);
+      if (kMultiGPRegistersWidened) {
+        DCHECK_EQ(sizeof(uintptr_t), sizeof(int64_t));
+        PushGpr(static_cast<int64_t>(bit_cast<uint32_t, int32_t>(val)));
+      } else {
+        PushGpr(val);
+      }
     } else {
       stack_entries_++;
-      PushStack(val);
+      if (kMultiGPRegistersWidened) {
+        DCHECK_EQ(sizeof(uintptr_t), sizeof(int64_t));
+        PushStack(static_cast<int64_t>(bit_cast<uint32_t, int32_t>(val)));
+      } else {
+        PushStack(val);
+      }
       gpr_index_ = 0;
     }
   }
@@ -1080,7 +1227,7 @@
       if (HaveFloatFpr()) {
         fpr_index_--;
         if (kRegistersNeededForDouble == 1) {
-          if (kMultiRegistersWidened) {
+          if (kMultiFPRegistersWidened) {
             PushFpr8(bit_cast<double, uint64_t>(val));
           } else {
             // No widening, just use the bits.
@@ -1091,7 +1238,7 @@
         }
       } else {
         stack_entries_++;
-        if (kRegistersNeededForDouble == 1 && kMultiRegistersWidened) {
+        if (kRegistersNeededForDouble == 1 && kMultiFPRegistersWidened) {
           // Need to widen before storing: Note the "double" in the template instantiation.
           // Note: We need to jump through those hoops to make the compiler happy.
           DCHECK_EQ(sizeof(uintptr_t), sizeof(uint64_t));
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index db36a73..420e8db 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -28,66 +28,30 @@
   return reinterpret_cast<const void*>(art_jni_dlsym_lookup_stub);
 }
 
-// Return the address of portable stub code for handling IMT conflicts.
-extern "C" void art_portable_imt_conflict_trampoline(mirror::ArtMethod*);
-static inline const void* GetPortableImtConflictStub() {
-  return reinterpret_cast<const void*>(art_portable_imt_conflict_trampoline);
-}
-
 // Return the address of quick stub code for handling IMT conflicts.
 extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
 static inline const void* GetQuickImtConflictStub() {
   return reinterpret_cast<const void*>(art_quick_imt_conflict_trampoline);
 }
 
-// Return the address of portable stub code for bridging from portable code to the interpreter.
-extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
-static inline const void* GetPortableToInterpreterBridge() {
-  return reinterpret_cast<const void*>(art_portable_to_interpreter_bridge);
-}
-
 // Return the address of quick stub code for bridging from quick code to the interpreter.
 extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
 static inline const void* GetQuickToInterpreterBridge() {
   return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge);
 }
 
-// Return the address of portable stub code for bridging from portable code to quick.
-static inline const void* GetPortableToQuickBridge() {
-  // TODO: portable to quick bridge. Bug: 8196384
-  return GetPortableToInterpreterBridge();
-}
-
-// Return the address of quick stub code for bridging from quick code to portable.
-static inline const void* GetQuickToPortableBridge() {
-  // TODO: quick to portable bridge. Bug: 8196384
-  return GetQuickToInterpreterBridge();
-}
-
 // Return the address of quick stub code for handling JNI calls.
 extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*);
 static inline const void* GetQuickGenericJniStub() {
   return reinterpret_cast<const void*>(art_quick_generic_jni_trampoline);
 }
 
-// Return the address of portable stub code for handling transitions into the proxy invoke handler.
-extern "C" void art_portable_proxy_invoke_handler();
-static inline const void* GetPortableProxyInvokeHandler() {
-  return reinterpret_cast<const void*>(art_portable_proxy_invoke_handler);
-}
-
 // Return the address of quick stub code for handling transitions into the proxy invoke handler.
 extern "C" void art_quick_proxy_invoke_handler();
 static inline const void* GetQuickProxyInvokeHandler() {
   return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
 }
 
-// Return the address of portable stub code for resolving a method at first call.
-extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
-static inline const void* GetPortableResolutionStub() {
-  return reinterpret_cast<const void*>(art_portable_resolution_trampoline);
-}
-
 // Return the address of quick stub code for resolving a method at first call.
 extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
 static inline const void* GetQuickResolutionStub() {
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index cfd2a3d..daa24c9 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -130,8 +130,11 @@
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_top, thread_local_alloc_stack_end,
                         sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, sizeof(void*));
-    EXPECT_OFFSET_DIFF(Thread, tlsPtr_.held_mutexes, Thread, wait_mutex_,
-                       sizeof(void*) * kLockLevelCount + sizeof(void*), thread_tlsptr_end);
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, held_mutexes, nested_signal_state,
+                        sizeof(void*) * kLockLevelCount);
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, nested_signal_state, flip_function, sizeof(void*));
+    EXPECT_OFFSET_DIFF(Thread, tlsPtr_.flip_function, Thread, wait_mutex_, sizeof(void*),
+                       thread_tlsptr_end);
   }
 
   void CheckInterpreterEntryPoints() {
@@ -150,17 +153,6 @@
             + sizeof(void*) == sizeof(JniEntryPoints), JniEntryPoints_all);
   }
 
-  void CheckPortableEntryPoints() {
-    CHECKED(OFFSETOF_MEMBER(PortableEntryPoints, pPortableImtConflictTrampoline) == 0,
-            PortableEntryPoints_start_with_imt);
-    EXPECT_OFFSET_DIFFNP(PortableEntryPoints, pPortableImtConflictTrampoline,
-                         pPortableResolutionTrampoline, sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(PortableEntryPoints, pPortableResolutionTrampoline,
-                         pPortableToInterpreterBridge, sizeof(void*));
-    CHECKED(OFFSETOF_MEMBER(PortableEntryPoints, pPortableToInterpreterBridge)
-            + sizeof(void*) == sizeof(PortableEntryPoints), PortableEntryPoints_all);
-  }
-
   void CheckQuickEntryPoints() {
     CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pAllocArray) == 0,
                 QuickEntryPoints_start_with_allocarray);
@@ -296,10 +288,6 @@
   CheckJniEntryPoints();
 }
 
-TEST_F(EntrypointsOrderTest, PortableEntryPoints) {
-  CheckPortableEntryPoints();
-}
-
 TEST_F(EntrypointsOrderTest, QuickEntryPoints) {
   CheckQuickEntryPoints();
 }
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 580b541..1770658 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -19,6 +19,7 @@
 #include "class_linker.h"
 #include "common_runtime_test.h"
 #include "dex_file.h"
+#include "dex_file-inl.h"
 #include "gtest/gtest.h"
 #include "leb128.h"
 #include "mirror/class-inl.h"
@@ -174,61 +175,42 @@
   // ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t));
 
 
-  if (!kUsePortableCompiler) {
-    // Create three fake stack frames with mapping data created in SetUp. We map offset 3 in the
-    // code to dex pc 3.
-    const uint32_t dex_pc = 3;
+  // Create three fake stack frames with mapping data created in SetUp. We map offset 3 in the
+  // code to dex pc 3.
+  const uint32_t dex_pc = 3;
 
-    // Create the stack frame for the callee save method, expected by the runtime.
-    fake_stack.push_back(reinterpret_cast<uintptr_t>(save_method));
-    for (size_t i = 0; i < frame_info.FrameSizeInBytes() - 2 * sizeof(uintptr_t);
-         i += sizeof(uintptr_t)) {
-      fake_stack.push_back(0);
-    }
-
-    fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc));  // return pc
-
-    // Create/push fake 16byte stack frame for method g
-    fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
+  // Create the stack frame for the callee save method, expected by the runtime.
+  fake_stack.push_back(reinterpret_cast<uintptr_t>(save_method));
+  for (size_t i = 0; i < frame_info.FrameSizeInBytes() - 2 * sizeof(uintptr_t);
+       i += sizeof(uintptr_t)) {
     fake_stack.push_back(0);
-    fake_stack.push_back(0);
-    fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc));  // return pc
-
-    // Create/push fake 16byte stack frame for method f
-    fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
-    fake_stack.push_back(0);
-    fake_stack.push_back(0);
-    fake_stack.push_back(0xEBAD6070);  // return pc
-
-    // Push Method* of NULL to terminate the trace
-    fake_stack.push_back(0);
-
-    // Push null values which will become null incoming arguments.
-    fake_stack.push_back(0);
-    fake_stack.push_back(0);
-    fake_stack.push_back(0);
-
-    // Set up thread to appear as if we called out of method_g_ at pc dex 3
-    thread->SetTopOfStack(reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]));
-  } else {
-    // Create/push fake 20-byte shadow frame for method g
-    fake_stack.push_back(0);
-    fake_stack.push_back(0);
-    fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
-    fake_stack.push_back(3);
-    fake_stack.push_back(0);
-
-    // Create/push fake 20-byte shadow frame for method f
-    fake_stack.push_back(0);
-    fake_stack.push_back(0);
-    fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
-    fake_stack.push_back(3);
-    fake_stack.push_back(0);
-
-    thread->PushShadowFrame(reinterpret_cast<ShadowFrame*>(&fake_stack[5]));
-    thread->PushShadowFrame(reinterpret_cast<ShadowFrame*>(&fake_stack[0]));
   }
 
+  fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc));  // return pc
+
+  // Create/push fake 16byte stack frame for method g
+  fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
+  fake_stack.push_back(0);
+  fake_stack.push_back(0);
+  fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc));  // return pc
+
+  // Create/push fake 16byte stack frame for method f
+  fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
+  fake_stack.push_back(0);
+  fake_stack.push_back(0);
+  fake_stack.push_back(0xEBAD6070);  // return pc
+
+  // Push Method* of NULL to terminate the trace
+  fake_stack.push_back(0);
+
+  // Push null values which will become null incoming arguments.
+  fake_stack.push_back(0);
+  fake_stack.push_back(0);
+  fake_stack.push_back(0);
+
+  // Set up thread to appear as if we called out of method_g_ at pc dex 3
+  thread->SetTopOfStack(reinterpret_cast<StackReference<mirror::ArtMethod>*>(&fake_stack[0]));
+
   jobject internal = thread->CreateInternalStackTrace<false>(soa);
   ASSERT_TRUE(internal != nullptr);
   jobjectArray ste_array = Thread::InternalStackTraceToStackTraceElementArray(soa, internal);
@@ -253,12 +235,7 @@
   EXPECT_STREQ("f", trace_array->Get(1)->GetMethodName()->ToModifiedUtf8().c_str());
   EXPECT_EQ(22, trace_array->Get(1)->GetLineNumber());
 
-  if (!kUsePortableCompiler) {
-    thread->SetTopOfStack(nullptr);  // Disarm the assertion that no code is running when we detach.
-  } else {
-    thread->PopShadowFrame();
-    thread->PopShadowFrame();
-  }
+  thread->SetTopOfStack(nullptr);  // Disarm the assertion that no code is running when we detach.
 }
 
 }  // namespace art
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 94753d4..83f3ae1 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -340,7 +340,8 @@
   // TODO: check the GC maps to make sure it's an object.
   // Check that the class pointer inside the object is not null and is aligned.
   // TODO: Method might be not a heap address, and GetClass could fault.
-  mirror::Class* cls = method_obj->GetClass<kVerifyNone>();
+  // No read barrier because method_obj may not be a real object.
+  mirror::Class* cls = method_obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
   if (cls == nullptr) {
     VLOG(signals) << "not a class";
     return false;
@@ -440,4 +441,3 @@
 }
 
 }   // namespace art
-
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 929a1d2..72734e9 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -25,15 +25,34 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "mem_map.h"
+#include "stack.h"
 #include "utils.h"
 
 namespace art {
 namespace gc {
 namespace accounting {
 
+// Internal representation is StackReference<T>, so this only works with mirror::Object or it's
+// subclasses.
 template <typename T>
 class AtomicStack {
  public:
+  class ObjectComparator {
+   public:
+    // These two comparators are for std::binary_search.
+    bool operator()(const T* a, const StackReference<T>& b) const NO_THREAD_SAFETY_ANALYSIS {
+      return a < b.AsMirrorPtr();
+    }
+    bool operator()(const StackReference<T>& a, const T* b) const NO_THREAD_SAFETY_ANALYSIS {
+      return a.AsMirrorPtr() < b;
+    }
+    // This comparator is for std::sort.
+    bool operator()(const StackReference<T>& a, const StackReference<T>& b) const
+        NO_THREAD_SAFETY_ANALYSIS {
+      return a.AsMirrorPtr() < b.AsMirrorPtr();
+    }
+  };
+
   // Capacity is how many elements we can store in the stack.
   static AtomicStack* Create(const std::string& name, size_t growth_limit, size_t capacity) {
     std::unique_ptr<AtomicStack> mark_stack(new AtomicStack(name, growth_limit, capacity));
@@ -45,7 +64,7 @@
 
   void Reset() {
     DCHECK(mem_map_.get() != nullptr);
-    DCHECK(begin_ != NULL);
+    DCHECK(begin_ != nullptr);
     front_index_.StoreRelaxed(0);
     back_index_.StoreRelaxed(0);
     debug_is_sorted_ = true;
@@ -55,18 +74,20 @@
   // Beware: Mixing atomic pushes and atomic pops will cause ABA problem.
 
   // Returns false if we overflowed the stack.
-  bool AtomicPushBackIgnoreGrowthLimit(const T& value) {
+  bool AtomicPushBackIgnoreGrowthLimit(T* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return AtomicPushBackInternal(value, capacity_);
   }
 
   // Returns false if we overflowed the stack.
-  bool AtomicPushBack(const T& value) {
+  bool AtomicPushBack(T* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return AtomicPushBackInternal(value, growth_limit_);
   }
 
   // Atomically bump the back index by the given number of
   // slots. Returns false if we overflowed the stack.
-  bool AtomicBumpBack(size_t num_slots, T** start_address, T** end_address) {
+  bool AtomicBumpBack(size_t num_slots, StackReference<T>** start_address,
+                      StackReference<T>** end_address)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (kIsDebugBuild) {
       debug_is_sorted_ = false;
     }
@@ -80,41 +101,41 @@
         return false;
       }
     } while (!back_index_.CompareExchangeWeakRelaxed(index, new_index));
-    *start_address = &begin_[index];
-    *end_address = &begin_[new_index];
+    *start_address = begin_ + index;
+    *end_address = begin_ + new_index;
     if (kIsDebugBuild) {
       // Sanity check that the memory is zero.
       for (int32_t i = index; i < new_index; ++i) {
-        DCHECK_EQ(begin_[i], static_cast<T>(0))
+        DCHECK_EQ(begin_[i].AsMirrorPtr(), static_cast<T*>(nullptr))
             << "i=" << i << " index=" << index << " new_index=" << new_index;
       }
     }
     return true;
   }
 
-  void AssertAllZero() {
+  void AssertAllZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (kIsDebugBuild) {
       for (size_t i = 0; i < capacity_; ++i) {
-        DCHECK_EQ(begin_[i], static_cast<T>(0)) << "i=" << i;
+        DCHECK_EQ(begin_[i].AsMirrorPtr(), static_cast<T*>(nullptr)) << "i=" << i;
       }
     }
   }
 
-  void PushBack(const T& value) {
+  void PushBack(T* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (kIsDebugBuild) {
       debug_is_sorted_ = false;
     }
-    int32_t index = back_index_.LoadRelaxed();
+    const int32_t index = back_index_.LoadRelaxed();
     DCHECK_LT(static_cast<size_t>(index), growth_limit_);
     back_index_.StoreRelaxed(index + 1);
-    begin_[index] = value;
+    begin_[index].Assign(value);
   }
 
-  T PopBack() {
+  T* PopBack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK_GT(back_index_.LoadRelaxed(), front_index_.LoadRelaxed());
     // Decrement the back index non atomically.
     back_index_.StoreRelaxed(back_index_.LoadRelaxed() - 1);
-    return begin_[back_index_.LoadRelaxed()];
+    return begin_[back_index_.LoadRelaxed()].AsMirrorPtr();
   }
 
   // Take an item from the front of the stack.
@@ -140,12 +161,11 @@
     return back_index_.LoadRelaxed() - front_index_.LoadRelaxed();
   }
 
-  T* Begin() const {
-    return const_cast<T*>(begin_ + front_index_.LoadRelaxed());
+  StackReference<T>* Begin() const {
+    return begin_ + front_index_.LoadRelaxed();
   }
-
-  T* End() const {
-    return const_cast<T*>(begin_ + back_index_.LoadRelaxed());
+  StackReference<T>* End() const {
+    return begin_ + back_index_.LoadRelaxed();
   }
 
   size_t Capacity() const {
@@ -162,7 +182,7 @@
   void Sort() {
     int32_t start_back_index = back_index_.LoadRelaxed();
     int32_t start_front_index = front_index_.LoadRelaxed();
-    std::sort(Begin(), End());
+    std::sort(Begin(), End(), ObjectComparator());
     CHECK_EQ(start_back_index, back_index_.LoadRelaxed());
     CHECK_EQ(start_front_index, front_index_.LoadRelaxed());
     if (kIsDebugBuild) {
@@ -170,13 +190,18 @@
     }
   }
 
-  bool ContainsSorted(const T& value) const {
+  bool ContainsSorted(const T* value) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(debug_is_sorted_);
-    return std::binary_search(Begin(), End(), value);
+    return std::binary_search(Begin(), End(), value, ObjectComparator());
   }
 
-  bool Contains(const T& value) const {
-    return std::find(Begin(), End(), value) != End();
+  bool Contains(const T* value) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    for (auto cur = Begin(), end = End(); cur != end; ++cur) {
+      if (cur->AsMirrorPtr() == value) {
+        return true;
+      }
+    }
+    return false;
   }
 
  private:
@@ -191,7 +216,8 @@
   }
 
   // Returns false if we overflowed the stack.
-  bool AtomicPushBackInternal(const T& value, size_t limit) ALWAYS_INLINE {
+  bool AtomicPushBackInternal(T* value, size_t limit) ALWAYS_INLINE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (kIsDebugBuild) {
       debug_is_sorted_ = false;
     }
@@ -203,20 +229,20 @@
         return false;
       }
     } while (!back_index_.CompareExchangeWeakRelaxed(index, index + 1));
-    begin_[index] = value;
+    begin_[index].Assign(value);
     return true;
   }
 
   // Size in number of elements.
   void Init() {
     std::string error_msg;
-    mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T),
+    mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(begin_[0]),
                                         PROT_READ | PROT_WRITE, false, &error_msg));
     CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg;
     uint8_t* addr = mem_map_->Begin();
     CHECK(addr != NULL);
     debug_is_sorted_ = true;
-    begin_ = reinterpret_cast<T*>(addr);
+    begin_ = reinterpret_cast<StackReference<T>*>(addr);
     Reset();
   }
 
@@ -229,7 +255,7 @@
   // Front index, used for implementing PopFront.
   AtomicInteger front_index_;
   // Base of the atomic stack.
-  T* begin_;
+  StackReference<T>* begin_;
   // Current maximum which we can push back to, must be <= capacity_.
   size_t growth_limit_;
   // Maximum number of elements.
@@ -240,7 +266,7 @@
   DISALLOW_COPY_AND_ASSIGN(AtomicStack);
 };
 
-typedef AtomicStack<mirror::Object*> ObjectStack;
+typedef AtomicStack<mirror::Object> ObjectStack;
 
 }  // namespace accounting
 }  // namespace gc
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 15562e5..83ad33e 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -48,7 +48,7 @@
 #endif
 }
 
-template <typename Visitor>
+template <bool kClearCard, typename Visitor>
 inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
                               const Visitor& visitor, const uint8_t minimum_age) const {
   DCHECK_GE(scan_begin, reinterpret_cast<uint8_t*>(bitmap->HeapBegin()));
@@ -66,6 +66,9 @@
       uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
       bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
       ++cards_scanned;
+      if (kClearCard) {
+        *card_cur = 0;
+      }
     }
     ++card_cur;
   }
@@ -95,6 +98,9 @@
             << "card " << static_cast<size_t>(*card) << " intptr_t " << (start_word & 0xFF);
         bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
         ++cards_scanned;
+        if (kClearCard) {
+          *card = 0;
+        }
       }
       start_word >>= 8;
       start += kCardSize;
@@ -109,6 +115,9 @@
       uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
       bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
       ++cards_scanned;
+      if (kClearCard) {
+        *card_cur = 0;
+      }
     }
     ++card_cur;
   }
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index b7b6099..ca1e7c1 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -102,6 +102,26 @@
   mem_map_->MadviseDontNeedAndZero();
 }
 
+void CardTable::ClearCardRange(uint8_t* start, uint8_t* end) {
+  if (!kMadviseZeroes) {
+    memset(start, 0, end - start);
+    return;
+  }
+  CHECK_ALIGNED(reinterpret_cast<uintptr_t>(start), kCardSize);
+  CHECK_ALIGNED(reinterpret_cast<uintptr_t>(end), kCardSize);
+  static_assert(kCardClean == 0, "kCardClean must be 0");
+  uint8_t* start_card = CardFromAddr(start);
+  uint8_t* end_card = CardFromAddr(end);
+  uint8_t* round_start = AlignUp(start_card, kPageSize);
+  uint8_t* round_end = AlignDown(end_card, kPageSize);
+  if (round_start < round_end) {
+    madvise(round_start, round_end - round_start, MADV_DONTNEED);
+  }
+  // Handle unaligned regions at start / end.
+  memset(start_card, 0, std::min(round_start, end_card) - start_card);
+  memset(std::max(round_end, start_card), 0, end_card - std::max(round_end, start_card));
+}
+
 bool CardTable::AddrIsInCardTable(const void* addr) const {
   return IsValidCard(biased_begin_ + ((uintptr_t)addr >> kCardShift));
 }
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index 9bd3fba..3ea7651 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -101,7 +101,7 @@
 
   // For every dirty at least minumum age between begin and end invoke the visitor with the
   // specified argument. Returns how many cards the visitor was run on.
-  template <typename Visitor>
+  template <bool kClearCard, typename Visitor>
   size_t Scan(SpaceBitmap<kObjectAlignment>* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
               const Visitor& visitor,
               const uint8_t minimum_age = kCardDirty) const
@@ -113,6 +113,7 @@
 
   // Resets all of the bytes in the card table to clean.
   void ClearCardTable();
+  void ClearCardRange(uint8_t* start, uint8_t* end);
 
   // Resets all of the bytes in the card table which do not map to the image space.
   void ClearSpaceCards(space::ContinuousSpace* space);
diff --git a/runtime/gc/accounting/heap_bitmap-inl.h b/runtime/gc/accounting/heap_bitmap-inl.h
index 34c15c7..8fcc87d 100644
--- a/runtime/gc/accounting/heap_bitmap-inl.h
+++ b/runtime/gc/accounting/heap_bitmap-inl.h
@@ -105,6 +105,15 @@
   return nullptr;
 }
 
+inline LargeObjectBitmap* HeapBitmap::GetLargeObjectBitmap(const mirror::Object* obj) const {
+  for (const auto& bitmap : large_object_bitmaps_) {
+    if (LIKELY(bitmap->HasAddress(obj))) {
+      return bitmap;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace accounting
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h
index ca6dc46..245e074 100644
--- a/runtime/gc/accounting/heap_bitmap.h
+++ b/runtime/gc/accounting/heap_bitmap.h
@@ -27,6 +27,10 @@
 
 class Heap;
 
+namespace collector {
+  class ConcurrentCopying;
+}  // namespace collector
+
 namespace accounting {
 
 class HeapBitmap {
@@ -40,6 +44,7 @@
   bool AtomicTestAndSet(const mirror::Object* obj, const LargeObjectSetVisitor& visitor)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) ALWAYS_INLINE;
   ContinuousSpaceBitmap* GetContinuousSpaceBitmap(const mirror::Object* obj) const;
+  LargeObjectBitmap* GetLargeObjectBitmap(const mirror::Object* obj) const;
 
   void Walk(ObjectCallback* callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -78,6 +83,7 @@
       large_object_bitmaps_;
 
   friend class art::gc::Heap;
+  friend class art::gc::collector::ConcurrentCopying;
 };
 
 }  // namespace accounting
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 0a15e9e..b1ccc0b 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -26,6 +26,7 @@
 #include "gc/collector/mark_sweep-inl.h"
 #include "gc/heap.h"
 #include "gc/space/space.h"
+#include "gc/space/image_space.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/class-inl.h"
@@ -76,8 +77,9 @@
  public:
   ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg,
                                         space::ContinuousSpace* from_space,
+                                        space::ImageSpace* image_space,
                                         bool* contains_reference_to_other_space)
-    : callback_(callback), arg_(arg), from_space_(from_space),
+    : callback_(callback), arg_(arg), from_space_(from_space), image_space_(image_space),
       contains_reference_to_other_space_(contains_reference_to_other_space) {
   }
 
@@ -87,7 +89,7 @@
     // Only add the reference if it is non null and fits our criteria.
     mirror::HeapReference<Object>* obj_ptr = obj->GetFieldObjectReferenceAddr(offset);
     mirror::Object* ref = obj_ptr->AsMirrorPtr();
-    if (ref != nullptr && !from_space_->HasAddress(ref)) {
+    if (ref != nullptr && !from_space_->HasAddress(ref) && !image_space_->HasAddress(ref)) {
       *contains_reference_to_other_space_ = true;
       callback_(obj_ptr, arg_);
     }
@@ -98,6 +100,7 @@
   void* arg_;
   // Space which we are scanning
   space::ContinuousSpace* const from_space_;
+  space::ImageSpace* const image_space_;
   // Set if we have any references to another space.
   bool* const contains_reference_to_other_space_;
 };
@@ -105,16 +108,16 @@
 class ModUnionScanImageRootVisitor {
  public:
   ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg,
-                               space::ContinuousSpace* from_space,
+                               space::ContinuousSpace* from_space, space::ImageSpace* image_space,
                                bool* contains_reference_to_other_space)
-      : callback_(callback), arg_(arg), from_space_(from_space),
+      : callback_(callback), arg_(arg), from_space_(from_space), image_space_(image_space),
         contains_reference_to_other_space_(contains_reference_to_other_space) {}
 
   void operator()(Object* root) const
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(root != NULL);
-    ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_,
+    ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_, image_space_,
                                                       contains_reference_to_other_space_);
     root->VisitReferences<kMovingClasses>(ref_visitor, VoidFunctor());
   }
@@ -124,6 +127,7 @@
   void* const arg_;
   // Space which we are scanning
   space::ContinuousSpace* const from_space_;
+  space::ImageSpace* const image_space_;
   // Set if we have any references to another space.
   bool* const contains_reference_to_other_space_;
 };
@@ -331,9 +335,11 @@
 void ModUnionTableCardCache::UpdateAndMarkReferences(MarkHeapReferenceCallback* callback,
                                                      void* arg) {
   CardTable* card_table = heap_->GetCardTable();
+  space::ImageSpace* image_space = heap_->GetImageSpace();
   ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
   bool reference_to_other_space = false;
-  ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, &reference_to_other_space);
+  ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, image_space,
+                                            &reference_to_other_space);
   for (auto it = cleared_cards_.begin(), end = cleared_cards_.end(); it != end; ) {
     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(*it));
     DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h
new file mode 100644
index 0000000..84d5da3
--- /dev/null
+++ b/runtime/gc/accounting/read_barrier_table.h
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_GC_ACCOUNTING_READ_BARRIER_TABLE_H_
+#define ART_RUNTIME_GC_ACCOUNTING_READ_BARRIER_TABLE_H_
+
+#include "base/mutex.h"
+#include "gc/space/space.h"
+#include "globals.h"
+#include "mem_map.h"
+
+namespace art {
+namespace gc {
+namespace accounting {
+
+// Used to decide whether to take the read barrier fast/slow paths for
+// kUseTableLookupReadBarrier. If an entry is set, take the read
+// barrier slow path. There's an entry per region.
+class ReadBarrierTable {
+ public:
+  ReadBarrierTable() {
+    size_t capacity = static_cast<size_t>(kHeapCapacity / kRegionSize);
+    DCHECK_EQ(kHeapCapacity / kRegionSize,
+              static_cast<uint64_t>(static_cast<size_t>(kHeapCapacity / kRegionSize)));
+    std::string error_msg;
+    MemMap* mem_map = MemMap::MapAnonymous("read barrier table", nullptr, capacity,
+                                           PROT_READ | PROT_WRITE, false, &error_msg);
+    CHECK(mem_map != nullptr && mem_map->Begin() != nullptr)
+        << "couldn't allocate read barrier table: " << error_msg;
+    mem_map_.reset(mem_map);
+  }
+  void ClearForSpace(space::ContinuousSpace* space) {
+    uint8_t* entry_start = EntryFromAddr(space->Begin());
+    uint8_t* entry_end = EntryFromAddr(space->Limit());
+    memset(reinterpret_cast<void*>(entry_start), 0, entry_end - entry_start);
+  }
+  void Clear(uint8_t* start_addr, uint8_t* end_addr) {
+    DCHECK(IsValidHeapAddr(start_addr)) << start_addr;
+    DCHECK(IsValidHeapAddr(end_addr)) << end_addr;
+    DCHECK(IsAligned<kRegionSize>(start_addr));
+    DCHECK(IsAligned<kRegionSize>(end_addr));
+    uint8_t* entry_start = EntryFromAddr(start_addr);
+    uint8_t* entry_end = EntryFromAddr(end_addr);
+    memset(reinterpret_cast<void*>(entry_start), 0, entry_end - entry_start);
+  }
+  bool IsSet(const void* heap_addr) const {
+    DCHECK(IsValidHeapAddr(heap_addr)) << heap_addr;
+    uint8_t entry_value = *EntryFromAddr(heap_addr);
+    DCHECK(entry_value == 0 || entry_value == kSetEntryValue);
+    return entry_value == kSetEntryValue;
+  }
+  void ClearAll() {
+    mem_map_->MadviseDontNeedAndZero();
+  }
+  void SetAll() {
+    memset(mem_map_->Begin(), kSetEntryValue, mem_map_->Size());
+  }
+  bool IsAllCleared() const {
+    for (uint32_t* p = reinterpret_cast<uint32_t*>(mem_map_->Begin());
+         p < reinterpret_cast<uint32_t*>(mem_map_->End()); ++p) {
+      if (*p != 0) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // This should match RegionSpace::kRegionSize. static_assert'ed in concurrent_copying.h.
+  static constexpr size_t kRegionSize = 1 * MB;
+
+ private:
+  static constexpr uint64_t kHeapCapacity = 4ULL * GB;  // low 4gb.
+  static constexpr uint8_t kSetEntryValue = 0x01;
+
+  uint8_t* EntryFromAddr(const void* heap_addr) const {
+    DCHECK(IsValidHeapAddr(heap_addr)) << heap_addr;
+    uint8_t* entry_addr = mem_map_->Begin() + reinterpret_cast<uintptr_t>(heap_addr) / kRegionSize;
+    DCHECK(IsValidEntry(entry_addr)) << "heap_addr: " << heap_addr
+                                     << " entry_addr: " << reinterpret_cast<void*>(entry_addr);
+    return entry_addr;
+  }
+
+  bool IsValidHeapAddr(const void* heap_addr) const {
+#ifdef __LP64__
+    return reinterpret_cast<uint64_t>(heap_addr) < kHeapCapacity;
+#else
+    UNUSED(heap_addr);
+    return true;
+#endif
+  }
+
+  bool IsValidEntry(const uint8_t* entry_addr) const {
+    uint8_t* begin = mem_map_->Begin();
+    uint8_t* end = mem_map_->End();
+    return entry_addr >= begin && entry_addr < end;
+  }
+
+  std::unique_ptr<MemMap> mem_map_;
+};
+
+}  // namespace accounting
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_ACCOUNTING_READ_BARRIER_TABLE_H_
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index feb9565..f5d3b47 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -17,6 +17,7 @@
 #include "space_bitmap-inl.h"
 
 #include "base/stringprintf.h"
+#include "dex_file-inl.h"
 #include "mem_map.h"
 #include "mirror/object-inl.h"
 #include "mirror/class.h"
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index e73166b..7bc83ef 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -160,6 +160,12 @@
     return IndexToOffset<uint64_t>(Size() / sizeof(intptr_t));
   }
 
+  void SetHeapSize(size_t bytes) {
+    // TODO: Un-map the end of the mem map.
+    bitmap_size_ = OffsetToIndex(bytes) * sizeof(intptr_t);
+    CHECK_EQ(HeapSize(), bytes);
+  }
+
   uintptr_t HeapBegin() const {
     return heap_begin_;
   }
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 7c2474f..7996241 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1119,12 +1119,19 @@
   uint8_t* slot_base = reinterpret_cast<uint8_t*>(this) + headerSizes[idx];
   size_t num_slots = numOfSlots[idx];
   size_t bracket_size = IndexToBracketSize(idx);
-  DCHECK_EQ(slot_base + num_slots * bracket_size, reinterpret_cast<uint8_t*>(this) + numOfPages[idx] * kPageSize);
+  DCHECK_EQ(slot_base + num_slots * bracket_size,
+            reinterpret_cast<uint8_t*>(this) + numOfPages[idx] * kPageSize);
   size_t num_vec = RoundUp(num_slots, 32) / 32;
   size_t slots = 0;
+  const uint32_t* const tl_free_vecp = IsThreadLocal() ? ThreadLocalFreeBitMap() : nullptr;
   for (size_t v = 0; v < num_vec; v++, slots += 32) {
     DCHECK_GE(num_slots, slots);
     uint32_t vec = alloc_bit_map_[v];
+    if (tl_free_vecp != nullptr) {
+      // Clear out the set bits in the thread local free bitmap since these aren't actually
+      // allocated.
+      vec &= ~tl_free_vecp[v];
+    }
     size_t end = std::min(num_slots - slots, static_cast<size_t>(32));
     for (size_t i = 0; i < end; ++i) {
       bool is_allocated = ((vec >> i) & 0x1) != 0;
diff --git a/runtime/gc/allocator_type.h b/runtime/gc/allocator_type.h
index c6ebc73..f9a2ff6 100644
--- a/runtime/gc/allocator_type.h
+++ b/runtime/gc/allocator_type.h
@@ -30,6 +30,8 @@
   kAllocatorTypeDlMalloc,  // Use dlmalloc allocator, has entrypoints.
   kAllocatorTypeNonMoving,  // Special allocator for non moving objects, doesn't have entrypoints.
   kAllocatorTypeLOS,  // Large object space, also doesn't have entrypoints.
+  kAllocatorTypeRegion,
+  kAllocatorTypeRegionTLAB,
 };
 std::ostream& operator<<(std::ostream& os, const AllocatorType& rhs);
 
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 079eeba..734c935 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -16,10 +16,1635 @@
 
 #include "concurrent_copying.h"
 
+#include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/space/image_space.h"
+#include "gc/space/space.h"
+#include "intern_table.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+#include "well_known_classes.h"
+
 namespace art {
 namespace gc {
 namespace collector {
 
+ConcurrentCopying::ConcurrentCopying(Heap* heap, const std::string& name_prefix)
+    : GarbageCollector(heap,
+                       name_prefix + (name_prefix.empty() ? "" : " ") +
+                       "concurrent copying + mark sweep"),
+      region_space_(nullptr), gc_barrier_(new Barrier(0)), mark_queue_(2 * MB),
+      is_marking_(false), is_active_(false), is_asserting_to_space_invariant_(false),
+      heap_mark_bitmap_(nullptr), live_stack_freeze_size_(0),
+      skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
+      rb_table_(heap_->GetReadBarrierTable()),
+      force_evacuate_all_(false) {
+  static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
+                "The region space size and the read barrier table region size must match");
+  cc_heap_bitmap_.reset(new accounting::HeapBitmap(heap));
+  {
+    Thread* self = Thread::Current();
+    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    // Cache this so that we won't have to lock heap_bitmap_lock_ in
+    // Mark() which could cause a nested lock on heap_bitmap_lock_
+    // when GC causes a RB while doing GC or a lock order violation
+    // (class_linker_lock_ and heap_bitmap_lock_).
+    heap_mark_bitmap_ = heap->GetMarkBitmap();
+  }
+}
+
+ConcurrentCopying::~ConcurrentCopying() {
+}
+
+void ConcurrentCopying::RunPhases() {
+  CHECK(kUseBakerReadBarrier || kUseTableLookupReadBarrier);
+  CHECK(!is_active_);
+  is_active_ = true;
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertNotHeld(self);
+  {
+    ReaderMutexLock mu(self, *Locks::mutator_lock_);
+    InitializePhase();
+  }
+  FlipThreadRoots();
+  {
+    ReaderMutexLock mu(self, *Locks::mutator_lock_);
+    MarkingPhase();
+  }
+  // Verify no from space refs. This causes a pause.
+  if (kEnableNoFromSpaceRefsVerification || kIsDebugBuild) {
+    TimingLogger::ScopedTiming split("(Paused)VerifyNoFromSpaceReferences", GetTimings());
+    ScopedPause pause(this);
+    CheckEmptyMarkQueue();
+    if (kVerboseMode) {
+      LOG(INFO) << "Verifying no from-space refs";
+    }
+    VerifyNoFromSpaceReferences();
+    CheckEmptyMarkQueue();
+  }
+  {
+    ReaderMutexLock mu(self, *Locks::mutator_lock_);
+    ReclaimPhase();
+  }
+  FinishPhase();
+  CHECK(is_active_);
+  is_active_ = false;
+}
+
+void ConcurrentCopying::BindBitmaps() {
+  Thread* self = Thread::Current();
+  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  // Mark all of the spaces we never collect as immune.
+  for (const auto& space : heap_->GetContinuousSpaces()) {
+    if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect
+        || space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
+      CHECK(space->IsZygoteSpace() || space->IsImageSpace());
+      CHECK(immune_region_.AddContinuousSpace(space)) << "Failed to add space " << *space;
+      const char* bitmap_name = space->IsImageSpace() ? "cc image space bitmap" :
+          "cc zygote space bitmap";
+      // TODO: try avoiding using bitmaps for image/zygote to save space.
+      accounting::ContinuousSpaceBitmap* bitmap =
+          accounting::ContinuousSpaceBitmap::Create(bitmap_name, space->Begin(), space->Capacity());
+      cc_heap_bitmap_->AddContinuousSpaceBitmap(bitmap);
+      cc_bitmaps_.push_back(bitmap);
+    } else if (space == region_space_) {
+      accounting::ContinuousSpaceBitmap* bitmap =
+          accounting::ContinuousSpaceBitmap::Create("cc region space bitmap",
+                                                    space->Begin(), space->Capacity());
+      cc_heap_bitmap_->AddContinuousSpaceBitmap(bitmap);
+      cc_bitmaps_.push_back(bitmap);
+      region_space_bitmap_ = bitmap;
+    }
+  }
+}
+
+void ConcurrentCopying::InitializePhase() {
+  TimingLogger::ScopedTiming split("InitializePhase", GetTimings());
+  if (kVerboseMode) {
+    LOG(INFO) << "GC InitializePhase";
+    LOG(INFO) << "Region-space : " << reinterpret_cast<void*>(region_space_->Begin()) << "-"
+              << reinterpret_cast<void*>(region_space_->Limit());
+  }
+  CHECK(mark_queue_.IsEmpty());
+  immune_region_.Reset();
+  bytes_moved_.StoreRelaxed(0);
+  objects_moved_.StoreRelaxed(0);
+  if (GetCurrentIteration()->GetGcCause() == kGcCauseExplicit ||
+      GetCurrentIteration()->GetGcCause() == kGcCauseForNativeAlloc ||
+      GetCurrentIteration()->GetClearSoftReferences()) {
+    force_evacuate_all_ = true;
+  } else {
+    force_evacuate_all_ = false;
+  }
+  BindBitmaps();
+  if (kVerboseMode) {
+    LOG(INFO) << "force_evacuate_all=" << force_evacuate_all_;
+    LOG(INFO) << "Immune region: " << immune_region_.Begin() << "-" << immune_region_.End();
+    LOG(INFO) << "GC end of InitializePhase";
+  }
+}
+
+// Used to switch the thread roots of a thread from from-space refs to to-space refs.
+class ThreadFlipVisitor : public Closure {
+ public:
+  explicit ThreadFlipVisitor(ConcurrentCopying* concurrent_copying, bool use_tlab)
+      : concurrent_copying_(concurrent_copying), use_tlab_(use_tlab) {
+  }
+
+  virtual void Run(Thread* thread) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Note: self is not necessarily equal to thread since thread may be suspended.
+    Thread* self = Thread::Current();
+    CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc)
+        << thread->GetState() << " thread " << thread << " self " << self;
+    if (use_tlab_ && thread->HasTlab()) {
+      if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
+        // This must come before the revoke.
+        size_t thread_local_objects = thread->GetThreadLocalObjectsAllocated();
+        concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread);
+        reinterpret_cast<Atomic<size_t>*>(&concurrent_copying_->from_space_num_objects_at_first_pause_)->
+            FetchAndAddSequentiallyConsistent(thread_local_objects);
+      } else {
+        concurrent_copying_->region_space_->RevokeThreadLocalBuffers(thread);
+      }
+    }
+    if (kUseThreadLocalAllocationStack) {
+      thread->RevokeThreadLocalAllocationStack();
+    }
+    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    thread->VisitRoots(ConcurrentCopying::ProcessRootCallback, concurrent_copying_);
+    concurrent_copying_->GetBarrier().Pass(self);
+  }
+
+ private:
+  ConcurrentCopying* const concurrent_copying_;
+  const bool use_tlab_;
+};
+
+// Called back from Runtime::FlipThreadRoots() during a pause.
+class FlipCallback : public Closure {
+ public:
+  explicit FlipCallback(ConcurrentCopying* concurrent_copying)
+      : concurrent_copying_(concurrent_copying) {
+  }
+
+  virtual void Run(Thread* thread) OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ConcurrentCopying* cc = concurrent_copying_;
+    TimingLogger::ScopedTiming split("(Paused)FlipCallback", cc->GetTimings());
+    // Note: self is not necessarily equal to thread since thread may be suspended.
+    Thread* self = Thread::Current();
+    CHECK(thread == self);
+    Locks::mutator_lock_->AssertExclusiveHeld(self);
+    cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_);
+    cc->SwapStacks(self);
+    if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
+      cc->RecordLiveStackFreezeSize(self);
+      cc->from_space_num_objects_at_first_pause_ = cc->region_space_->GetObjectsAllocated();
+      cc->from_space_num_bytes_at_first_pause_ = cc->region_space_->GetBytesAllocated();
+    }
+    cc->is_marking_ = true;
+    if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
+      CHECK(Runtime::Current()->IsCompiler());
+      TimingLogger::ScopedTiming split2("(Paused)VisitTransactionRoots", cc->GetTimings());
+      Runtime::Current()->VisitTransactionRoots(ConcurrentCopying::ProcessRootCallback, cc);
+    }
+  }
+
+ private:
+  ConcurrentCopying* const concurrent_copying_;
+};
+
+// Switch threads that from from-space to to-space refs. Forward/mark the thread roots.
+void ConcurrentCopying::FlipThreadRoots() {
+  TimingLogger::ScopedTiming split("FlipThreadRoots", GetTimings());
+  if (kVerboseMode) {
+    LOG(INFO) << "time=" << region_space_->Time();
+    region_space_->DumpNonFreeRegions(LOG(INFO));
+  }
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertNotHeld(self);
+  gc_barrier_->Init(self, 0);
+  ThreadFlipVisitor thread_flip_visitor(this, heap_->use_tlab_);
+  FlipCallback flip_callback(this);
+  size_t barrier_count = Runtime::Current()->FlipThreadRoots(
+      &thread_flip_visitor, &flip_callback, this);
+  {
+    ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+    gc_barrier_->Increment(self, barrier_count);
+  }
+  is_asserting_to_space_invariant_ = true;
+  QuasiAtomic::ThreadFenceForConstructor();
+  if (kVerboseMode) {
+    LOG(INFO) << "time=" << region_space_->Time();
+    region_space_->DumpNonFreeRegions(LOG(INFO));
+    LOG(INFO) << "GC end of FlipThreadRoots";
+  }
+}
+
+void ConcurrentCopying::SwapStacks(Thread* self) {
+  heap_->SwapStacks(self);
+}
+
+void ConcurrentCopying::RecordLiveStackFreezeSize(Thread* self) {
+  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
+}
+
+// Used to visit objects in the immune spaces.
+class ConcurrentCopyingImmuneSpaceObjVisitor {
+ public:
+  explicit ConcurrentCopyingImmuneSpaceObjVisitor(ConcurrentCopying* cc)
+      : collector_(cc) {}
+
+  void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+    DCHECK(obj != nullptr);
+    DCHECK(collector_->immune_region_.ContainsObject(obj));
+    accounting::ContinuousSpaceBitmap* cc_bitmap =
+        collector_->cc_heap_bitmap_->GetContinuousSpaceBitmap(obj);
+    DCHECK(cc_bitmap != nullptr)
+        << "An immune space object must have a bitmap";
+    if (kIsDebugBuild) {
+      DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj))
+          << "Immune space object must be already marked";
+    }
+    // This may or may not succeed, which is ok.
+    if (kUseBakerReadBarrier) {
+      obj->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+    }
+    if (cc_bitmap->AtomicTestAndSet(obj)) {
+      // Already marked. Do nothing.
+    } else {
+      // Newly marked. Set the gray bit and push it onto the mark stack.
+      CHECK(!kUseBakerReadBarrier || obj->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+      collector_->PushOntoMarkStack<true>(obj);
+    }
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+class EmptyCheckpoint : public Closure {
+ public:
+  explicit EmptyCheckpoint(ConcurrentCopying* concurrent_copying)
+      : concurrent_copying_(concurrent_copying) {
+  }
+
+  virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
+    // Note: self is not necessarily equal to thread since thread may be suspended.
+    Thread* self = Thread::Current();
+    CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc)
+        << thread->GetState() << " thread " << thread << " self " << self;
+    // If thread is a running mutator, then act on behalf of the garbage collector.
+    // See the code in ThreadList::RunCheckpoint.
+    if (thread->GetState() == kRunnable) {
+      concurrent_copying_->GetBarrier().Pass(self);
+    }
+  }
+
+ private:
+  ConcurrentCopying* const concurrent_copying_;
+};
+
+// Concurrently mark roots that are guarded by read barriers and process the mark stack.
+void ConcurrentCopying::MarkingPhase() {
+  TimingLogger::ScopedTiming split("MarkingPhase", GetTimings());
+  if (kVerboseMode) {
+    LOG(INFO) << "GC MarkingPhase";
+  }
+  {
+    // Mark the image root. The WB-based collectors do not need to
+    // scan the image objects from roots by relying on the card table,
+    // but it's necessary for the RB to-space invariant to hold.
+    TimingLogger::ScopedTiming split1("VisitImageRoots", GetTimings());
+    gc::space::ImageSpace* image = heap_->GetImageSpace();
+    if (image != nullptr) {
+      mirror::ObjectArray<mirror::Object>* image_root = image->GetImageHeader().GetImageRoots();
+      mirror::Object* marked_image_root = Mark(image_root);
+      CHECK_EQ(image_root, marked_image_root) << "An image object does not move";
+      if (ReadBarrier::kEnableToSpaceInvariantChecks) {
+        AssertToSpaceInvariant(nullptr, MemberOffset(0), marked_image_root);
+      }
+    }
+  }
+  {
+    TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
+    Runtime::Current()->VisitConstantRoots(ProcessRootCallback, this);
+  }
+  {
+    TimingLogger::ScopedTiming split3("VisitInternTableRoots", GetTimings());
+    Runtime::Current()->GetInternTable()->VisitRoots(ProcessRootCallback,
+                                                     this, kVisitRootFlagAllRoots);
+  }
+  {
+    TimingLogger::ScopedTiming split4("VisitClassLinkerRoots", GetTimings());
+    Runtime::Current()->GetClassLinker()->VisitRoots(ProcessRootCallback,
+                                                     this, kVisitRootFlagAllRoots);
+  }
+  {
+    // TODO: don't visit the transaction roots if it's not active.
+    TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
+    Runtime::Current()->VisitNonThreadRoots(ProcessRootCallback, this);
+  }
+
+  // Immune spaces.
+  for (auto& space : heap_->GetContinuousSpaces()) {
+    if (immune_region_.ContainsSpace(space)) {
+      DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
+      accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+      ConcurrentCopyingImmuneSpaceObjVisitor visitor(this);
+      live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+                                    reinterpret_cast<uintptr_t>(space->Limit()),
+                                    visitor);
+    }
+  }
+
+  Thread* self = Thread::Current();
+  {
+    TimingLogger::ScopedTiming split6("ProcessMarkStack", GetTimings());
+    // Process the mark stack and issue an empty check point. If the
+    // mark stack is still empty after the check point, we're
+    // done. Otherwise, repeat.
+    ProcessMarkStack();
+    size_t count = 0;
+    while (!ProcessMarkStack()) {
+      ++count;
+      if (kVerboseMode) {
+        LOG(INFO) << "Issue an empty check point. " << count;
+      }
+      IssueEmptyCheckpoint();
+    }
+    // Need to ensure the mark stack is empty before reference
+    // processing to get rid of non-reference gray objects.
+    CheckEmptyMarkQueue();
+    // Enable the GetReference slow path and disallow access to the system weaks.
+    GetHeap()->GetReferenceProcessor()->EnableSlowPath();
+    Runtime::Current()->DisallowNewSystemWeaks();
+    QuasiAtomic::ThreadFenceForConstructor();
+    // Lock-unlock the system weak locks so that there's no thread in
+    // the middle of accessing system weaks.
+    Runtime::Current()->EnsureNewSystemWeaksDisallowed();
+    // Note: Do not issue a checkpoint from here to the
+    // SweepSystemWeaks call or else a deadlock due to
+    // WaitHoldingLocks() would occur.
+    if (kVerboseMode) {
+      LOG(INFO) << "Enabled the ref proc slow path & disabled access to system weaks.";
+      LOG(INFO) << "ProcessReferences";
+    }
+    ProcessReferences(self, true);
+    CheckEmptyMarkQueue();
+    if (kVerboseMode) {
+      LOG(INFO) << "SweepSystemWeaks";
+    }
+    SweepSystemWeaks(self);
+    if (kVerboseMode) {
+      LOG(INFO) << "SweepSystemWeaks done";
+    }
+    // Because hash_set::Erase() can call the hash function for
+    // arbitrary elements in the weak intern table in
+    // InternTable::Table::SweepWeaks(), the above SweepSystemWeaks()
+    // call may have marked some objects (strings) alive. So process
+    // the mark stack here once again.
+    ProcessMarkStack();
+    CheckEmptyMarkQueue();
+    // Disable marking.
+    if (kUseTableLookupReadBarrier) {
+      heap_->rb_table_->ClearAll();
+      DCHECK(heap_->rb_table_->IsAllCleared());
+    }
+    is_mark_queue_push_disallowed_.StoreSequentiallyConsistent(1);
+    is_marking_ = false;
+    if (kVerboseMode) {
+      LOG(INFO) << "AllowNewSystemWeaks";
+    }
+    Runtime::Current()->AllowNewSystemWeaks();
+    CheckEmptyMarkQueue();
+  }
+
+  if (kVerboseMode) {
+    LOG(INFO) << "GC end of MarkingPhase";
+  }
+}
+
+void ConcurrentCopying::IssueEmptyCheckpoint() {
+  Thread* self = Thread::Current();
+  EmptyCheckpoint check_point(this);
+  ThreadList* thread_list = Runtime::Current()->GetThreadList();
+  gc_barrier_->Init(self, 0);
+  size_t barrier_count = thread_list->RunCheckpoint(&check_point);
+  // If there are no threads to wait which implys that all the checkpoint functions are finished,
+  // then no need to release the mutator lock.
+  if (barrier_count == 0) {
+    return;
+  }
+  // Release locks then wait for all mutator threads to pass the barrier.
+  Locks::mutator_lock_->SharedUnlock(self);
+  {
+    ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+    gc_barrier_->Increment(self, barrier_count);
+  }
+  Locks::mutator_lock_->SharedLock(self);
+}
+
+mirror::Object* ConcurrentCopying::PopOffMarkStack() {
+  return mark_queue_.Dequeue();
+}
+
+template<bool kThreadSafe>
+void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
+  CHECK_EQ(is_mark_queue_push_disallowed_.LoadRelaxed(), 0)
+      << " " << to_ref << " " << PrettyTypeOf(to_ref);
+  if (kThreadSafe) {
+    CHECK(mark_queue_.Enqueue(to_ref)) << "Mark queue overflow";
+  } else {
+    CHECK(mark_queue_.EnqueueThreadUnsafe(to_ref)) << "Mark queue overflow";
+  }
+}
+
+accounting::ObjectStack* ConcurrentCopying::GetAllocationStack() {
+  return heap_->allocation_stack_.get();
+}
+
+accounting::ObjectStack* ConcurrentCopying::GetLiveStack() {
+  return heap_->live_stack_.get();
+}
+
+inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
+  DCHECK(region_space_->IsInFromSpace(from_ref));
+  LockWord lw = from_ref->GetLockWord(false);
+  if (lw.GetState() == LockWord::kForwardingAddress) {
+    mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
+    CHECK(fwd_ptr != nullptr);
+    return fwd_ptr;
+  } else {
+    return nullptr;
+  }
+}
+
+inline void ConcurrentCopying::SetFwdPtr(mirror::Object* from_ref, mirror::Object* to_ref) {
+  DCHECK(region_space_->IsInFromSpace(from_ref));
+  DCHECK(region_space_->IsInToSpace(to_ref) || heap_->GetNonMovingSpace()->HasAddress(to_ref));
+  LockWord lw = from_ref->GetLockWord(false);
+  DCHECK_NE(lw.GetState(), LockWord::kForwardingAddress);
+  from_ref->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref)), false);
+}
+
+// The following visitors are that used to verify that there's no
+// references to the from-space left after marking.
+class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor {
+ public:
+  explicit ConcurrentCopyingVerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+
+  void operator()(mirror::Object* ref) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    if (ref == nullptr) {
+      // OK.
+      return;
+    }
+    collector_->AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+    if (kUseBakerReadBarrier) {
+      if (collector_->RegionSpace()->IsInToSpace(ref)) {
+        CHECK(ref->GetReadBarrierPointer() == nullptr)
+            << "To-space ref " << ref << " " << PrettyTypeOf(ref)
+            << " has non-white rb_ptr " << ref->GetReadBarrierPointer();
+      } else {
+        CHECK(ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr() ||
+              (ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr() &&
+               collector_->IsOnAllocStack(ref)))
+            << "Non-moving/unevac from space ref " << ref << " " << PrettyTypeOf(ref)
+            << " has non-black rb_ptr " << ref->GetReadBarrierPointer()
+            << " but isn't on the alloc stack (and has white rb_ptr)."
+            << " Is it in the non-moving space="
+            << (collector_->GetHeap()->GetNonMovingSpace()->HasAddress(ref));
+      }
+    }
+  }
+
+  static void RootCallback(mirror::Object** root, void *arg, const RootInfo& /*root_info*/)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+    ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector);
+    DCHECK(root != nullptr);
+    visitor(*root);
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor {
+ public:
+  explicit ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+
+  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    mirror::Object* ref =
+        obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
+    ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_);
+    visitor(ref);
+  }
+  void operator()(mirror::Class* klass, mirror::Reference* ref) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    CHECK(klass->IsTypeOfReferenceClass());
+    this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor {
+ public:
+  explicit ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+  void operator()(mirror::Object* obj) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ObjectCallback(obj, collector_);
+  }
+  static void ObjectCallback(mirror::Object* obj, void *arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CHECK(obj != nullptr);
+    ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+    space::RegionSpace* region_space = collector->RegionSpace();
+    CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
+    ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor visitor(collector);
+    obj->VisitReferences<true>(visitor, visitor);
+    if (kUseBakerReadBarrier) {
+      if (collector->RegionSpace()->IsInToSpace(obj)) {
+        CHECK(obj->GetReadBarrierPointer() == nullptr)
+            << "obj=" << obj << " non-white rb_ptr " << obj->GetReadBarrierPointer();
+      } else {
+        CHECK(obj->GetReadBarrierPointer() == ReadBarrier::BlackPtr() ||
+              (obj->GetReadBarrierPointer() == ReadBarrier::WhitePtr() &&
+               collector->IsOnAllocStack(obj)))
+            << "Non-moving space/unevac from space ref " << obj << " " << PrettyTypeOf(obj)
+            << " has non-black rb_ptr " << obj->GetReadBarrierPointer()
+            << " but isn't on the alloc stack (and has white rb_ptr). Is it in the non-moving space="
+            << (collector->GetHeap()->GetNonMovingSpace()->HasAddress(obj));
+      }
+    }
+  }
+
+ private:
+  ConcurrentCopying* const collector_;
+};
+
+// Verify there's no from-space references left after the marking phase.
+void ConcurrentCopying::VerifyNoFromSpaceReferences() {
+  Thread* self = Thread::Current();
+  DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
+  ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor visitor(this);
+  // Roots.
+  {
+    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    Runtime::Current()->VisitRoots(
+        ConcurrentCopyingVerifyNoFromSpaceRefsVisitor::RootCallback, this);
+  }
+  // The to-space.
+  region_space_->WalkToSpace(ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor::ObjectCallback,
+                             this);
+  // Non-moving spaces.
+  {
+    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    heap_->GetMarkBitmap()->Visit(visitor);
+  }
+  // The alloc stack.
+  {
+    ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this);
+    for (auto* it = heap_->allocation_stack_->Begin(), *end = heap_->allocation_stack_->End();
+        it < end; ++it) {
+      mirror::Object* const obj = it->AsMirrorPtr();
+      if (obj != nullptr && obj->GetClass() != nullptr) {
+        // TODO: need to call this only if obj is alive?
+        ref_visitor(obj);
+        visitor(obj);
+      }
+    }
+  }
+  // TODO: LOS. But only refs in LOS are classes.
+}
+
+// The following visitors are used to assert the to-space invariant.
+class ConcurrentCopyingAssertToSpaceInvariantRefsVisitor {
+ public:
+  explicit ConcurrentCopyingAssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+
+  void operator()(mirror::Object* ref) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    if (ref == nullptr) {
+      // OK.
+      return;
+    }
+    collector_->AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+  }
+  static void RootCallback(mirror::Object** root, void *arg, const RootInfo& /*root_info*/)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+    ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector);
+    DCHECK(root != nullptr);
+    visitor(*root);
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingAssertToSpaceInvariantFieldVisitor {
+ public:
+  explicit ConcurrentCopyingAssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+
+  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    mirror::Object* ref =
+        obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
+    ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_);
+    visitor(ref);
+  }
+  void operator()(mirror::Class* klass, mirror::Reference* /* ref */) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    CHECK(klass->IsTypeOfReferenceClass());
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+class ConcurrentCopyingAssertToSpaceInvariantObjectVisitor {
+ public:
+  explicit ConcurrentCopyingAssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+  void operator()(mirror::Object* obj) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    ObjectCallback(obj, collector_);
+  }
+  static void ObjectCallback(mirror::Object* obj, void *arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CHECK(obj != nullptr);
+    ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
+    space::RegionSpace* region_space = collector->RegionSpace();
+    CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
+    collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj);
+    ConcurrentCopyingAssertToSpaceInvariantFieldVisitor visitor(collector);
+    obj->VisitReferences<true>(visitor, visitor);
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+bool ConcurrentCopying::ProcessMarkStack() {
+  if (kVerboseMode) {
+    LOG(INFO) << "ProcessMarkStack. ";
+  }
+  size_t count = 0;
+  mirror::Object* to_ref;
+  while ((to_ref = PopOffMarkStack()) != nullptr) {
+    ++count;
+    DCHECK(!region_space_->IsInFromSpace(to_ref));
+    if (kUseBakerReadBarrier) {
+      DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
+          << " " << to_ref << " " << to_ref->GetReadBarrierPointer()
+          << " is_marked=" << IsMarked(to_ref);
+    }
+    // Scan ref fields.
+    Scan(to_ref);
+    // Mark the gray ref as white or black.
+    if (kUseBakerReadBarrier) {
+      DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
+          << " " << to_ref << " " << to_ref->GetReadBarrierPointer()
+          << " is_marked=" << IsMarked(to_ref);
+    }
+    if (to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
+        to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
+        !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())) {
+      // Leave References gray so that GetReferent() will trigger RB.
+      CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref;
+    } else {
+      if (kUseBakerReadBarrier) {
+        if (region_space_->IsInToSpace(to_ref)) {
+          // If to-space, change from gray to white.
+          bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
+                                                             ReadBarrier::WhitePtr());
+          CHECK(success) << "Must succeed as we won the race.";
+          CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+        } else {
+          // If non-moving space/unevac from space, change from gray
+          // to black. We can't change gray to white because it's not
+          // safe to use CAS if two threads change values in opposite
+          // directions (A->B and B->A). So, we change it to black to
+          // indicate non-moving objects that have been marked
+          // through. Note we'd need to change from black to white
+          // later (concurrently).
+          bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
+                                                             ReadBarrier::BlackPtr());
+          CHECK(success) << "Must succeed as we won the race.";
+          CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+        }
+      }
+    }
+    if (ReadBarrier::kEnableToSpaceInvariantChecks || kIsDebugBuild) {
+      ConcurrentCopyingAssertToSpaceInvariantObjectVisitor visitor(this);
+      visitor(to_ref);
+    }
+  }
+  // Return true if the stack was empty.
+  return count == 0;
+}
+
+void ConcurrentCopying::CheckEmptyMarkQueue() {
+  if (!mark_queue_.IsEmpty()) {
+    while (!mark_queue_.IsEmpty()) {
+      mirror::Object* obj = mark_queue_.Dequeue();
+      if (kUseBakerReadBarrier) {
+        mirror::Object* rb_ptr = obj->GetReadBarrierPointer();
+        LOG(INFO) << "On mark queue : " << obj << " " << PrettyTypeOf(obj) << " rb_ptr=" << rb_ptr
+                  << " is_marked=" << IsMarked(obj);
+      } else {
+        LOG(INFO) << "On mark queue : " << obj << " " << PrettyTypeOf(obj)
+                  << " is_marked=" << IsMarked(obj);
+      }
+    }
+    LOG(FATAL) << "mark queue is not empty";
+  }
+}
+
+void ConcurrentCopying::SweepSystemWeaks(Thread* self) {
+  TimingLogger::ScopedTiming split("SweepSystemWeaks", GetTimings());
+  ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  Runtime::Current()->SweepSystemWeaks(IsMarkedCallback, this);
+}
+
+void ConcurrentCopying::Sweep(bool swap_bitmaps) {
+  {
+    TimingLogger::ScopedTiming t("MarkStackAsLive", GetTimings());
+    accounting::ObjectStack* live_stack = heap_->GetLiveStack();
+    if (kEnableFromSpaceAccountingCheck) {
+      CHECK_GE(live_stack_freeze_size_, live_stack->Size());
+    }
+    heap_->MarkAllocStackAsLive(live_stack);
+    live_stack->Reset();
+  }
+  CHECK(mark_queue_.IsEmpty());
+  TimingLogger::ScopedTiming split("Sweep", GetTimings());
+  for (const auto& space : GetHeap()->GetContinuousSpaces()) {
+    if (space->IsContinuousMemMapAllocSpace()) {
+      space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
+      if (space == region_space_ || immune_region_.ContainsSpace(space)) {
+        continue;
+      }
+      TimingLogger::ScopedTiming split2(
+          alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings());
+      RecordFree(alloc_space->Sweep(swap_bitmaps));
+    }
+  }
+  SweepLargeObjects(swap_bitmaps);
+}
+
+void ConcurrentCopying::SweepLargeObjects(bool swap_bitmaps) {
+  TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
+  RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+}
+
+class ConcurrentCopyingClearBlackPtrsVisitor {
+ public:
+  explicit ConcurrentCopyingClearBlackPtrsVisitor(ConcurrentCopying* cc)
+      : collector_(cc) {}
+  void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+    DCHECK(obj != nullptr);
+    DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj)) << obj;
+    DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << obj;
+    obj->SetReadBarrierPointer(ReadBarrier::WhitePtr());
+    DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
+  }
+
+ private:
+  ConcurrentCopying* const collector_;
+};
+
+// Clear the black ptrs in non-moving objects back to white.
+void ConcurrentCopying::ClearBlackPtrs() {
+  CHECK(kUseBakerReadBarrier);
+  TimingLogger::ScopedTiming split("ClearBlackPtrs", GetTimings());
+  ConcurrentCopyingClearBlackPtrsVisitor visitor(this);
+  for (auto& space : heap_->GetContinuousSpaces()) {
+    if (space == region_space_) {
+      continue;
+    }
+    accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
+    if (kVerboseMode) {
+      LOG(INFO) << "ClearBlackPtrs: " << *space << " bitmap: " << *mark_bitmap;
+    }
+    mark_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+                                  reinterpret_cast<uintptr_t>(space->Limit()),
+                                  visitor);
+  }
+  space::LargeObjectSpace* large_object_space = heap_->GetLargeObjectsSpace();
+  large_object_space->GetMarkBitmap()->VisitMarkedRange(
+      reinterpret_cast<uintptr_t>(large_object_space->Begin()),
+      reinterpret_cast<uintptr_t>(large_object_space->End()),
+      visitor);
+  // Objects on the allocation stack?
+  if (ReadBarrier::kEnableReadBarrierInvariantChecks || kIsDebugBuild) {
+    size_t count = GetAllocationStack()->Size();
+    auto* it = GetAllocationStack()->Begin();
+    auto* end = GetAllocationStack()->End();
+    for (size_t i = 0; i < count; ++i, ++it) {
+      CHECK_LT(it, end);
+      mirror::Object* obj = it->AsMirrorPtr();
+      if (obj != nullptr) {
+        // Must have been cleared above.
+        CHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
+      }
+    }
+  }
+}
+
+void ConcurrentCopying::ReclaimPhase() {
+  TimingLogger::ScopedTiming split("ReclaimPhase", GetTimings());
+  if (kVerboseMode) {
+    LOG(INFO) << "GC ReclaimPhase";
+  }
+  Thread* self = Thread::Current();
+
+  {
+    // Double-check that the mark stack is empty.
+    // Note: need to set this after VerifyNoFromSpaceRef().
+    is_asserting_to_space_invariant_ = false;
+    QuasiAtomic::ThreadFenceForConstructor();
+    if (kVerboseMode) {
+      LOG(INFO) << "Issue an empty check point. ";
+    }
+    IssueEmptyCheckpoint();
+    // Disable the check.
+    is_mark_queue_push_disallowed_.StoreSequentiallyConsistent(0);
+    CheckEmptyMarkQueue();
+  }
+
+  {
+    // Record freed objects.
+    TimingLogger::ScopedTiming split2("RecordFree", GetTimings());
+    // Don't include thread-locals that are in the to-space.
+    uint64_t from_bytes = region_space_->GetBytesAllocatedInFromSpace();
+    uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace();
+    uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace();
+    uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace();
+    uint64_t to_bytes = bytes_moved_.LoadSequentiallyConsistent();
+    uint64_t to_objects = objects_moved_.LoadSequentiallyConsistent();
+    if (kEnableFromSpaceAccountingCheck) {
+      CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects);
+      CHECK_EQ(from_space_num_bytes_at_first_pause_, from_bytes + unevac_from_bytes);
+    }
+    CHECK_LE(to_objects, from_objects);
+    CHECK_LE(to_bytes, from_bytes);
+    int64_t freed_bytes = from_bytes - to_bytes;
+    int64_t freed_objects = from_objects - to_objects;
+    if (kVerboseMode) {
+      LOG(INFO) << "RecordFree:"
+                << " from_bytes=" << from_bytes << " from_objects=" << from_objects
+                << " unevac_from_bytes=" << unevac_from_bytes << " unevac_from_objects=" << unevac_from_objects
+                << " to_bytes=" << to_bytes << " to_objects=" << to_objects
+                << " freed_bytes=" << freed_bytes << " freed_objects=" << freed_objects
+                << " from_space size=" << region_space_->FromSpaceSize()
+                << " unevac_from_space size=" << region_space_->UnevacFromSpaceSize()
+                << " to_space size=" << region_space_->ToSpaceSize();
+      LOG(INFO) << "(before) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
+    }
+    RecordFree(ObjectBytePair(freed_objects, freed_bytes));
+    if (kVerboseMode) {
+      LOG(INFO) << "(after) num_bytes_allocated=" << heap_->num_bytes_allocated_.LoadSequentiallyConsistent();
+    }
+  }
+
+  {
+    TimingLogger::ScopedTiming split3("ComputeUnevacFromSpaceLiveRatio", GetTimings());
+    ComputeUnevacFromSpaceLiveRatio();
+  }
+
+  {
+    TimingLogger::ScopedTiming split4("ClearFromSpace", GetTimings());
+    region_space_->ClearFromSpace();
+  }
+
+  {
+    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    if (kUseBakerReadBarrier) {
+      ClearBlackPtrs();
+    }
+    Sweep(false);
+    SwapBitmaps();
+    heap_->UnBindBitmaps();
+
+    // Remove bitmaps for the immune spaces.
+    while (!cc_bitmaps_.empty()) {
+      accounting::ContinuousSpaceBitmap* cc_bitmap = cc_bitmaps_.back();
+      cc_heap_bitmap_->RemoveContinuousSpaceBitmap(cc_bitmap);
+      delete cc_bitmap;
+      cc_bitmaps_.pop_back();
+    }
+    region_space_bitmap_ = nullptr;
+  }
+
+  if (kVerboseMode) {
+    LOG(INFO) << "GC end of ReclaimPhase";
+  }
+}
+
+class ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor {
+ public:
+  explicit ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor(ConcurrentCopying* cc)
+      : collector_(cc) {}
+  void operator()(mirror::Object* ref) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+    DCHECK(ref != nullptr);
+    DCHECK(collector_->region_space_bitmap_->Test(ref)) << ref;
+    DCHECK(collector_->region_space_->IsInUnevacFromSpace(ref)) << ref;
+    if (kUseBakerReadBarrier) {
+      DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << ref;
+      // Clear the black ptr.
+      ref->SetReadBarrierPointer(ReadBarrier::WhitePtr());
+    }
+    size_t obj_size = ref->SizeOf();
+    size_t alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+    collector_->region_space_->AddLiveBytes(ref, alloc_size);
+  }
+
+ private:
+  ConcurrentCopying* collector_;
+};
+
+// Compute how much live objects are left in regions.
+void ConcurrentCopying::ComputeUnevacFromSpaceLiveRatio() {
+  region_space_->AssertAllRegionLiveBytesZeroOrCleared();
+  ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor visitor(this);
+  region_space_bitmap_->VisitMarkedRange(reinterpret_cast<uintptr_t>(region_space_->Begin()),
+                                         reinterpret_cast<uintptr_t>(region_space_->Limit()),
+                                         visitor);
+}
+
+// Assert the to-space invariant.
+void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+                                               mirror::Object* ref) {
+  CHECK(heap_->collector_type_ == kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
+  if (is_asserting_to_space_invariant_) {
+    if (region_space_->IsInToSpace(ref)) {
+      // OK.
+      return;
+    } else if (region_space_->IsInUnevacFromSpace(ref)) {
+      CHECK(region_space_bitmap_->Test(ref)) << ref;
+    } else if (region_space_->IsInFromSpace(ref)) {
+      // Not OK. Do extra logging.
+      if (obj != nullptr) {
+        if (kUseBakerReadBarrier) {
+          LOG(INFO) << "holder=" << obj << " " << PrettyTypeOf(obj)
+                    << " holder rb_ptr=" << obj->GetReadBarrierPointer();
+        } else {
+          LOG(INFO) << "holder=" << obj << " " << PrettyTypeOf(obj);
+        }
+        if (region_space_->IsInFromSpace(obj)) {
+          LOG(INFO) << "holder is in the from-space.";
+        } else if (region_space_->IsInToSpace(obj)) {
+          LOG(INFO) << "holder is in the to-space.";
+        } else if (region_space_->IsInUnevacFromSpace(obj)) {
+          LOG(INFO) << "holder is in the unevac from-space.";
+          if (region_space_bitmap_->Test(obj)) {
+            LOG(INFO) << "holder is marked in the region space bitmap.";
+          } else {
+            LOG(INFO) << "holder is not marked in the region space bitmap.";
+          }
+        } else {
+          // In a non-moving space.
+          if (immune_region_.ContainsObject(obj)) {
+            LOG(INFO) << "holder is in the image or the zygote space.";
+            accounting::ContinuousSpaceBitmap* cc_bitmap =
+                cc_heap_bitmap_->GetContinuousSpaceBitmap(obj);
+            CHECK(cc_bitmap != nullptr)
+                << "An immune space object must have a bitmap.";
+            if (cc_bitmap->Test(obj)) {
+              LOG(INFO) << "holder is marked in the bit map.";
+            } else {
+              LOG(INFO) << "holder is NOT marked in the bit map.";
+            }
+          } else {
+            LOG(INFO) << "holder is in a non-moving (or main) space.";
+            accounting::ContinuousSpaceBitmap* mark_bitmap =
+                heap_mark_bitmap_->GetContinuousSpaceBitmap(obj);
+            accounting::LargeObjectBitmap* los_bitmap =
+                heap_mark_bitmap_->GetLargeObjectBitmap(obj);
+            CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+            bool is_los = mark_bitmap == nullptr;
+            if (!is_los && mark_bitmap->Test(obj)) {
+              LOG(INFO) << "holder is marked in the mark bit map.";
+            } else if (is_los && los_bitmap->Test(obj)) {
+              LOG(INFO) << "holder is marked in the los bit map.";
+            } else {
+              // If ref is on the allocation stack, then it is considered
+              // mark/alive (but not necessarily on the live stack.)
+              if (IsOnAllocStack(obj)) {
+                LOG(INFO) << "holder is on the alloc stack.";
+              } else {
+                LOG(INFO) << "holder is not marked or on the alloc stack.";
+              }
+            }
+          }
+        }
+        LOG(INFO) << "offset=" << offset.SizeValue();
+      }
+      CHECK(false) << "Found from-space ref " << ref << " " << PrettyTypeOf(ref);
+    } else {
+      // In a non-moving spaces. Check that the ref is marked.
+      if (immune_region_.ContainsObject(ref)) {
+        accounting::ContinuousSpaceBitmap* cc_bitmap =
+            cc_heap_bitmap_->GetContinuousSpaceBitmap(ref);
+        CHECK(cc_bitmap != nullptr)
+            << "An immune space ref must have a bitmap. " << ref;
+        if (kUseBakerReadBarrier) {
+          CHECK(cc_bitmap->Test(ref))
+              << "Unmarked immune space ref. obj=" << obj << " rb_ptr="
+              << obj->GetReadBarrierPointer() << " ref=" << ref;
+        } else {
+          CHECK(cc_bitmap->Test(ref))
+              << "Unmarked immune space ref. obj=" << obj << " ref=" << ref;
+        }
+      } else {
+        accounting::ContinuousSpaceBitmap* mark_bitmap =
+            heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
+        accounting::LargeObjectBitmap* los_bitmap =
+            heap_mark_bitmap_->GetLargeObjectBitmap(ref);
+        CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+        bool is_los = mark_bitmap == nullptr;
+        if ((!is_los && mark_bitmap->Test(ref)) ||
+            (is_los && los_bitmap->Test(ref))) {
+          // OK.
+        } else {
+          // If ref is on the allocation stack, then it may not be
+          // marked live, but considered marked/alive (but not
+          // necessarily on the live stack).
+          CHECK(IsOnAllocStack(ref)) << "Unmarked ref that's not on the allocation stack. "
+                                     << "obj=" << obj << " ref=" << ref;
+        }
+      }
+    }
+  }
+}
+
+void ConcurrentCopying::ProcessRootCallback(mirror::Object** root, void* arg,
+                                            const RootInfo& /*root_info*/) {
+  reinterpret_cast<ConcurrentCopying*>(arg)->Process(root);
+}
+
+// Used to scan ref fields of an object.
+class ConcurrentCopyingRefFieldsVisitor {
+ public:
+  explicit ConcurrentCopyingRefFieldsVisitor(ConcurrentCopying* collector)
+      : collector_(collector) {}
+
+  void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
+      const ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+    collector_->Process(obj, offset);
+  }
+
+  void operator()(mirror::Class* klass, mirror::Reference* ref) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+    CHECK(klass->IsTypeOfReferenceClass());
+    collector_->DelayReferenceReferent(klass, ref);
+  }
+
+ private:
+  ConcurrentCopying* const collector_;
+};
+
+// Scan ref fields of an object.
+void ConcurrentCopying::Scan(mirror::Object* to_ref) {
+  DCHECK(!region_space_->IsInFromSpace(to_ref));
+  ConcurrentCopyingRefFieldsVisitor visitor(this);
+  to_ref->VisitReferences<true>(visitor, visitor);
+}
+
+// Process a field.
+inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
+  mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
+  if (ref == nullptr || region_space_->IsInToSpace(ref)) {
+    return;
+  }
+  mirror::Object* to_ref = Mark(ref);
+  if (to_ref == ref) {
+    return;
+  }
+  // This may fail if the mutator writes to the field at the same time. But it's ok.
+  mirror::Object* expected_ref = ref;
+  mirror::Object* new_ref = to_ref;
+  do {
+    if (expected_ref !=
+        obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset)) {
+      // It was updated by the mutator.
+      break;
+    }
+  } while (!obj->CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<false, false, kVerifyNone>(
+      offset, expected_ref, new_ref));
+}
+
+// Process a root.
+void ConcurrentCopying::Process(mirror::Object** root) {
+  mirror::Object* ref = *root;
+  if (ref == nullptr || region_space_->IsInToSpace(ref)) {
+    return;
+  }
+  mirror::Object* to_ref = Mark(ref);
+  if (to_ref == ref) {
+    return;
+  }
+  Atomic<mirror::Object*>* addr = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+  mirror::Object* expected_ref = ref;
+  mirror::Object* new_ref = to_ref;
+  do {
+    if (expected_ref != addr->LoadRelaxed()) {
+      // It was updated by the mutator.
+      break;
+    }
+  } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+}
+
+// Fill the given memory block with a dummy object. Used to fill in a
+// copy of objects that was lost in race.
+void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) {
+  CHECK(IsAligned<kObjectAlignment>(byte_size));
+  memset(dummy_obj, 0, byte_size);
+  mirror::Class* int_array_class = mirror::IntArray::GetArrayClass();
+  CHECK(int_array_class != nullptr);
+  AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class);
+  size_t component_size = int_array_class->GetComponentSize();
+  CHECK_EQ(component_size, sizeof(int32_t));
+  size_t data_offset = mirror::Array::DataOffset(component_size).SizeValue();
+  if (data_offset > byte_size) {
+    // An int array is too big. Use java.lang.Object.
+    mirror::Class* java_lang_Object = WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object);
+    AssertToSpaceInvariant(nullptr, MemberOffset(0), java_lang_Object);
+    CHECK_EQ(byte_size, java_lang_Object->GetObjectSize());
+    dummy_obj->SetClass(java_lang_Object);
+    CHECK_EQ(byte_size, dummy_obj->SizeOf());
+  } else {
+    // Use an int array.
+    dummy_obj->SetClass(int_array_class);
+    CHECK(dummy_obj->IsArrayInstance());
+    int32_t length = (byte_size - data_offset) / component_size;
+    dummy_obj->AsArray()->SetLength(length);
+    CHECK_EQ(dummy_obj->AsArray()->GetLength(), length)
+        << "byte_size=" << byte_size << " length=" << length
+        << " component_size=" << component_size << " data_offset=" << data_offset;
+    CHECK_EQ(byte_size, dummy_obj->SizeOf())
+        << "byte_size=" << byte_size << " length=" << length
+        << " component_size=" << component_size << " data_offset=" << data_offset;
+  }
+}
+
+// Reuse the memory blocks that were copy of objects that were lost in race.
+mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) {
+  // Try to reuse the blocks that were unused due to CAS failures.
+  CHECK(IsAligned<space::RegionSpace::kAlignment>(alloc_size));
+  Thread* self = Thread::Current();
+  size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment);
+  MutexLock mu(self, skipped_blocks_lock_);
+  auto it = skipped_blocks_map_.lower_bound(alloc_size);
+  if (it == skipped_blocks_map_.end()) {
+    // Not found.
+    return nullptr;
+  }
+  {
+    size_t byte_size = it->first;
+    CHECK_GE(byte_size, alloc_size);
+    if (byte_size > alloc_size && byte_size - alloc_size < min_object_size) {
+      // If remainder would be too small for a dummy object, retry with a larger request size.
+      it = skipped_blocks_map_.lower_bound(alloc_size + min_object_size);
+      if (it == skipped_blocks_map_.end()) {
+        // Not found.
+        return nullptr;
+      }
+      CHECK(IsAligned<space::RegionSpace::kAlignment>(it->first - alloc_size));
+      CHECK_GE(it->first - alloc_size, min_object_size)
+          << "byte_size=" << byte_size << " it->first=" << it->first << " alloc_size=" << alloc_size;
+    }
+  }
+  // Found a block.
+  CHECK(it != skipped_blocks_map_.end());
+  size_t byte_size = it->first;
+  uint8_t* addr = it->second;
+  CHECK_GE(byte_size, alloc_size);
+  CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr)));
+  CHECK(IsAligned<space::RegionSpace::kAlignment>(byte_size));
+  if (kVerboseMode) {
+    LOG(INFO) << "Reusing skipped bytes : " << reinterpret_cast<void*>(addr) << ", " << byte_size;
+  }
+  skipped_blocks_map_.erase(it);
+  memset(addr, 0, byte_size);
+  if (byte_size > alloc_size) {
+    // Return the remainder to the map.
+    CHECK(IsAligned<space::RegionSpace::kAlignment>(byte_size - alloc_size));
+    CHECK_GE(byte_size - alloc_size, min_object_size);
+    FillWithDummyObject(reinterpret_cast<mirror::Object*>(addr + alloc_size),
+                        byte_size - alloc_size);
+    CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr + alloc_size)));
+    skipped_blocks_map_.insert(std::make_pair(byte_size - alloc_size, addr + alloc_size));
+  }
+  return reinterpret_cast<mirror::Object*>(addr);
+}
+
+mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) {
+  DCHECK(region_space_->IsInFromSpace(from_ref));
+  // No read barrier to avoid nested RB that might violate the to-space
+  // invariant. Note that from_ref is a from space ref so the SizeOf()
+  // call will access the from-space meta objects, but it's ok and necessary.
+  size_t obj_size = from_ref->SizeOf<kDefaultVerifyFlags, kWithoutReadBarrier>();
+  size_t region_space_alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+  size_t region_space_bytes_allocated = 0U;
+  size_t non_moving_space_bytes_allocated = 0U;
+  size_t bytes_allocated = 0U;
+  mirror::Object* to_ref = region_space_->AllocNonvirtual<true>(
+      region_space_alloc_size, &region_space_bytes_allocated, nullptr);
+  bytes_allocated = region_space_bytes_allocated;
+  if (to_ref != nullptr) {
+    DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated);
+  }
+  bool fall_back_to_non_moving = false;
+  if (UNLIKELY(to_ref == nullptr)) {
+    // Failed to allocate in the region space. Try the skipped blocks.
+    to_ref = AllocateInSkippedBlock(region_space_alloc_size);
+    if (to_ref != nullptr) {
+      // Succeeded to allocate in a skipped block.
+      if (heap_->use_tlab_) {
+        // This is necessary for the tlab case as it's not accounted in the space.
+        region_space_->RecordAlloc(to_ref);
+      }
+      bytes_allocated = region_space_alloc_size;
+    } else {
+      // Fall back to the non-moving space.
+      fall_back_to_non_moving = true;
+      if (kVerboseMode) {
+        LOG(INFO) << "Out of memory in the to-space. Fall back to non-moving. skipped_bytes="
+                  << to_space_bytes_skipped_.LoadSequentiallyConsistent()
+                  << " skipped_objects=" << to_space_objects_skipped_.LoadSequentiallyConsistent();
+      }
+      fall_back_to_non_moving = true;
+      to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,
+                                               &non_moving_space_bytes_allocated, nullptr);
+      CHECK(to_ref != nullptr) << "Fall-back non-moving space allocation failed";
+      bytes_allocated = non_moving_space_bytes_allocated;
+      // Mark it in the mark bitmap.
+      accounting::ContinuousSpaceBitmap* mark_bitmap =
+          heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
+      CHECK(mark_bitmap != nullptr);
+      CHECK(!mark_bitmap->AtomicTestAndSet(to_ref));
+    }
+  }
+  DCHECK(to_ref != nullptr);
+
+  // Attempt to install the forward pointer. This is in a loop as the
+  // lock word atomic write can fail.
+  while (true) {
+    // Copy the object. TODO: copy only the lockword in the second iteration and on?
+    memcpy(to_ref, from_ref, obj_size);
+    // Set the gray ptr.
+    if (kUseBakerReadBarrier) {
+      to_ref->SetReadBarrierPointer(ReadBarrier::GrayPtr());
+    }
+
+    LockWord old_lock_word = to_ref->GetLockWord(false);
+
+    if (old_lock_word.GetState() == LockWord::kForwardingAddress) {
+      // Lost the race. Another thread (either GC or mutator) stored
+      // the forwarding pointer first. Make the lost copy (to_ref)
+      // look like a valid but dead (dummy) object and keep it for
+      // future reuse.
+      FillWithDummyObject(to_ref, bytes_allocated);
+      if (!fall_back_to_non_moving) {
+        DCHECK(region_space_->IsInToSpace(to_ref));
+        if (bytes_allocated > space::RegionSpace::kRegionSize) {
+          // Free the large alloc.
+          region_space_->FreeLarge(to_ref, bytes_allocated);
+        } else {
+          // Record the lost copy for later reuse.
+          heap_->num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated);
+          to_space_bytes_skipped_.FetchAndAddSequentiallyConsistent(bytes_allocated);
+          to_space_objects_skipped_.FetchAndAddSequentiallyConsistent(1);
+          MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+          skipped_blocks_map_.insert(std::make_pair(bytes_allocated,
+                                                    reinterpret_cast<uint8_t*>(to_ref)));
+        }
+      } else {
+        DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
+        DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
+        // Free the non-moving-space chunk.
+        accounting::ContinuousSpaceBitmap* mark_bitmap =
+            heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
+        CHECK(mark_bitmap != nullptr);
+        CHECK(mark_bitmap->Clear(to_ref));
+        heap_->non_moving_space_->Free(Thread::Current(), to_ref);
+      }
+
+      // Get the winner's forward ptr.
+      mirror::Object* lost_fwd_ptr = to_ref;
+      to_ref = reinterpret_cast<mirror::Object*>(old_lock_word.ForwardingAddress());
+      CHECK(to_ref != nullptr);
+      CHECK_NE(to_ref, lost_fwd_ptr);
+      CHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref));
+      CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress);
+      return to_ref;
+    }
+
+    LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref));
+
+    // Try to atomically write the fwd ptr.
+    bool success = from_ref->CasLockWordWeakSequentiallyConsistent(old_lock_word, new_lock_word);
+    if (LIKELY(success)) {
+      // The CAS succeeded.
+      objects_moved_.FetchAndAddSequentiallyConsistent(1);
+      bytes_moved_.FetchAndAddSequentiallyConsistent(region_space_alloc_size);
+      if (LIKELY(!fall_back_to_non_moving)) {
+        DCHECK(region_space_->IsInToSpace(to_ref));
+      } else {
+        DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
+        DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
+      }
+      if (kUseBakerReadBarrier) {
+        DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+      }
+      DCHECK(GetFwdPtr(from_ref) == to_ref);
+      CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress);
+      PushOntoMarkStack<true>(to_ref);
+      return to_ref;
+    } else {
+      // The CAS failed. It may have lost the race or may have failed
+      // due to monitor/hashcode ops. Either way, retry.
+    }
+  }
+}
+
+mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
+  DCHECK(from_ref != nullptr);
+  space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+    // It's already marked.
+    return from_ref;
+  }
+  mirror::Object* to_ref;
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
+    to_ref = GetFwdPtr(from_ref);
+    DCHECK(to_ref == nullptr || region_space_->IsInToSpace(to_ref) ||
+           heap_->non_moving_space_->HasAddress(to_ref))
+        << "from_ref=" << from_ref << " to_ref=" << to_ref;
+  } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
+    if (region_space_bitmap_->Test(from_ref)) {
+      to_ref = from_ref;
+    } else {
+      to_ref = nullptr;
+    }
+  } else {
+    // from_ref is in a non-moving space.
+    if (immune_region_.ContainsObject(from_ref)) {
+      accounting::ContinuousSpaceBitmap* cc_bitmap =
+          cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
+      DCHECK(cc_bitmap != nullptr)
+          << "An immune space object must have a bitmap";
+      if (kIsDebugBuild) {
+        DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
+            << "Immune space object must be already marked";
+      }
+      if (cc_bitmap->Test(from_ref)) {
+        // Already marked.
+        to_ref = from_ref;
+      } else {
+        // Newly marked.
+        to_ref = nullptr;
+      }
+    } else {
+      // Non-immune non-moving space. Use the mark bitmap.
+      accounting::ContinuousSpaceBitmap* mark_bitmap =
+          heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
+      accounting::LargeObjectBitmap* los_bitmap =
+          heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
+      CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+      bool is_los = mark_bitmap == nullptr;
+      if (!is_los && mark_bitmap->Test(from_ref)) {
+        // Already marked.
+        to_ref = from_ref;
+      } else if (is_los && los_bitmap->Test(from_ref)) {
+        // Already marked in LOS.
+        to_ref = from_ref;
+      } else {
+        // Not marked.
+        if (IsOnAllocStack(from_ref)) {
+          // If on the allocation stack, it's considered marked.
+          to_ref = from_ref;
+        } else {
+          // Not marked.
+          to_ref = nullptr;
+        }
+      }
+    }
+  }
+  return to_ref;
+}
+
+bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {
+  QuasiAtomic::ThreadFenceAcquire();
+  accounting::ObjectStack* alloc_stack = GetAllocationStack();
+  return alloc_stack->Contains(ref);
+}
+
+mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
+  if (from_ref == nullptr) {
+    return nullptr;
+  }
+  DCHECK(from_ref != nullptr);
+  DCHECK(heap_->collector_type_ == kCollectorTypeCC);
+  space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+    // It's already marked.
+    return from_ref;
+  }
+  mirror::Object* to_ref;
+  if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
+    to_ref = GetFwdPtr(from_ref);
+    if (kUseBakerReadBarrier) {
+      DCHECK(to_ref != ReadBarrier::GrayPtr()) << "from_ref=" << from_ref << " to_ref=" << to_ref;
+    }
+    if (to_ref == nullptr) {
+      // It isn't marked yet. Mark it by copying it to the to-space.
+      to_ref = Copy(from_ref);
+    }
+    DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
+        << "from_ref=" << from_ref << " to_ref=" << to_ref;
+  } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
+    // This may or may not succeed, which is ok.
+    if (kUseBakerReadBarrier) {
+      from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+    }
+    if (region_space_bitmap_->AtomicTestAndSet(from_ref)) {
+      // Already marked.
+      to_ref = from_ref;
+    } else {
+      // Newly marked.
+      to_ref = from_ref;
+      if (kUseBakerReadBarrier) {
+        DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+      }
+      PushOntoMarkStack<true>(to_ref);
+    }
+  } else {
+    // from_ref is in a non-moving space.
+    DCHECK(!region_space_->HasAddress(from_ref)) << from_ref;
+    if (immune_region_.ContainsObject(from_ref)) {
+      accounting::ContinuousSpaceBitmap* cc_bitmap =
+          cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
+      DCHECK(cc_bitmap != nullptr)
+          << "An immune space object must have a bitmap";
+      if (kIsDebugBuild) {
+        DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
+            << "Immune space object must be already marked";
+      }
+      // This may or may not succeed, which is ok.
+      if (kUseBakerReadBarrier) {
+        from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+      }
+      if (cc_bitmap->AtomicTestAndSet(from_ref)) {
+        // Already marked.
+        to_ref = from_ref;
+      } else {
+        // Newly marked.
+        to_ref = from_ref;
+        if (kUseBakerReadBarrier) {
+          DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+        }
+        PushOntoMarkStack<true>(to_ref);
+      }
+    } else {
+      // Use the mark bitmap.
+      accounting::ContinuousSpaceBitmap* mark_bitmap =
+          heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
+      accounting::LargeObjectBitmap* los_bitmap =
+          heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
+      CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+      bool is_los = mark_bitmap == nullptr;
+      if (!is_los && mark_bitmap->Test(from_ref)) {
+        // Already marked.
+        to_ref = from_ref;
+        if (kUseBakerReadBarrier) {
+          DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+                 to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+        }
+      } else if (is_los && los_bitmap->Test(from_ref)) {
+        // Already marked in LOS.
+        to_ref = from_ref;
+        if (kUseBakerReadBarrier) {
+          DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+                 to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+        }
+      } else {
+        // Not marked.
+        if (IsOnAllocStack(from_ref)) {
+          // If it's on the allocation stack, it's considered marked. Keep it white.
+          to_ref = from_ref;
+          // Objects on the allocation stack need not be marked.
+          if (!is_los) {
+            DCHECK(!mark_bitmap->Test(to_ref));
+          } else {
+            DCHECK(!los_bitmap->Test(to_ref));
+          }
+          if (kUseBakerReadBarrier) {
+            DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+          }
+        } else {
+          // Not marked or on the allocation stack. Try to mark it.
+          // This may or may not succeed, which is ok.
+          if (kUseBakerReadBarrier) {
+            from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+          }
+          if (!is_los && mark_bitmap->AtomicTestAndSet(from_ref)) {
+            // Already marked.
+            to_ref = from_ref;
+          } else if (is_los && los_bitmap->AtomicTestAndSet(from_ref)) {
+            // Already marked in LOS.
+            to_ref = from_ref;
+          } else {
+            // Newly marked.
+            to_ref = from_ref;
+            if (kUseBakerReadBarrier) {
+              DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+            }
+            PushOntoMarkStack<true>(to_ref);
+          }
+        }
+      }
+    }
+  }
+  return to_ref;
+}
+
+void ConcurrentCopying::FinishPhase() {
+  region_space_ = nullptr;
+  CHECK(mark_queue_.IsEmpty());
+  mark_queue_.Clear();
+  {
+    MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+    skipped_blocks_map_.clear();
+  }
+  WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+  heap_->ClearMarkedObjects();
+}
+
+mirror::Object* ConcurrentCopying::IsMarkedCallback(mirror::Object* from_ref, void* arg) {
+  return reinterpret_cast<ConcurrentCopying*>(arg)->IsMarked(from_ref);
+}
+
+bool ConcurrentCopying::IsHeapReferenceMarkedCallback(
+    mirror::HeapReference<mirror::Object>* field, void* arg) {
+  mirror::Object* from_ref = field->AsMirrorPtr();
+  mirror::Object* to_ref = reinterpret_cast<ConcurrentCopying*>(arg)->IsMarked(from_ref);
+  if (to_ref == nullptr) {
+    return false;
+  }
+  if (from_ref != to_ref) {
+    QuasiAtomic::ThreadFenceRelease();
+    field->Assign(to_ref);
+    QuasiAtomic::ThreadFenceSequentiallyConsistent();
+  }
+  return true;
+}
+
+mirror::Object* ConcurrentCopying::MarkCallback(mirror::Object* from_ref, void* arg) {
+  return reinterpret_cast<ConcurrentCopying*>(arg)->Mark(from_ref);
+}
+
+void ConcurrentCopying::ProcessMarkStackCallback(void* arg) {
+  reinterpret_cast<ConcurrentCopying*>(arg)->ProcessMarkStack();
+}
+
+void ConcurrentCopying::DelayReferenceReferent(mirror::Class* klass, mirror::Reference* reference) {
+  heap_->GetReferenceProcessor()->DelayReferenceReferent(
+      klass, reference, &IsHeapReferenceMarkedCallback, this);
+}
+
+void ConcurrentCopying::ProcessReferences(Thread* self, bool concurrent) {
+  TimingLogger::ScopedTiming split("ProcessReferences", GetTimings());
+  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+  GetHeap()->GetReferenceProcessor()->ProcessReferences(
+      concurrent, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(),
+      &IsHeapReferenceMarkedCallback, &MarkCallback, &ProcessMarkStackCallback, this);
+}
+
+void ConcurrentCopying::RevokeAllThreadLocalBuffers() {
+  TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+  region_space_->RevokeAllThreadLocalBuffers();
+}
+
 }  // namespace collector
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index ee5a785..d0e0446 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -17,34 +17,268 @@
 #ifndef ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_H_
 #define ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_H_
 
+#include "barrier.h"
 #include "garbage_collector.h"
+#include "immune_region.h"
+#include "jni.h"
+#include "object_callbacks.h"
+#include "offsets.h"
+#include "gc/accounting/atomic_stack.h"
+#include "gc/accounting/read_barrier_table.h"
+#include "gc/accounting/space_bitmap.h"
+#include "mirror/object.h"
+#include "mirror/object_reference.h"
+#include "safe_map.h"
+
+#include <unordered_map>
+#include <vector>
 
 namespace art {
+class RootInfo;
+
 namespace gc {
+
+namespace accounting {
+  typedef SpaceBitmap<kObjectAlignment> ContinuousSpaceBitmap;
+  class HeapBitmap;
+}  // namespace accounting
+
+namespace space {
+  class RegionSpace;
+}  // namespace space
+
 namespace collector {
 
+// Concurrent queue. Used as the mark stack. TODO: use a concurrent
+// stack for locality.
+class MarkQueue {
+ public:
+  explicit MarkQueue(size_t size) : size_(size) {
+    CHECK(IsPowerOfTwo(size_));
+    buf_.reset(new Atomic<mirror::Object*>[size_]);
+    CHECK(buf_.get() != nullptr);
+    Clear();
+  }
+
+  ALWAYS_INLINE Atomic<mirror::Object*>* GetSlotAddr(size_t index) {
+    return &(buf_.get()[index & (size_ - 1)]);
+  }
+
+  // Multiple-proceducer enqueue.
+  bool Enqueue(mirror::Object* to_ref) {
+    size_t t;
+    do {
+      t = tail_.LoadRelaxed();
+      size_t h = head_.LoadSequentiallyConsistent();
+      if (t + size_ == h) {
+        // It's full.
+        return false;
+      }
+    } while (!tail_.CompareExchangeWeakSequentiallyConsistent(t, t + 1));
+    // We got a slot but its content has not been filled yet at this point.
+    GetSlotAddr(t)->StoreSequentiallyConsistent(to_ref);
+    return true;
+  }
+
+  // Thread-unsafe.
+  bool EnqueueThreadUnsafe(mirror::Object* to_ref) {
+    size_t t = tail_.LoadRelaxed();
+    size_t h = head_.LoadRelaxed();
+    if (t + size_ == h) {
+      // It's full.
+      return false;
+    }
+    GetSlotAddr(t)->StoreRelaxed(to_ref);
+    tail_.StoreRelaxed(t + 1);
+    return true;
+  }
+
+  // Single-consumer dequeue.
+  mirror::Object* Dequeue() {
+    size_t h = head_.LoadRelaxed();
+    size_t t = tail_.LoadSequentiallyConsistent();
+    if (h == t) {
+      // it's empty.
+      return nullptr;
+    }
+    Atomic<mirror::Object*>* slot = GetSlotAddr(h);
+    mirror::Object* ref = slot->LoadSequentiallyConsistent();
+    while (ref == nullptr) {
+      // Wait until the slot content becomes visible.
+      ref = slot->LoadSequentiallyConsistent();
+    }
+    slot->StoreRelaxed(nullptr);
+    head_.StoreSequentiallyConsistent(h + 1);
+    return ref;
+  }
+
+  bool IsEmpty() {
+    size_t h = head_.LoadSequentiallyConsistent();
+    size_t t = tail_.LoadSequentiallyConsistent();
+    return h == t;
+  }
+
+  void Clear() {
+    head_.StoreRelaxed(0);
+    tail_.StoreRelaxed(0);
+    memset(buf_.get(), 0, size_ * sizeof(Atomic<mirror::Object*>));
+  }
+
+ private:
+  Atomic<size_t> head_;
+  Atomic<size_t> tail_;
+
+  size_t size_;
+  std::unique_ptr<Atomic<mirror::Object*>[]> buf_;
+};
+
 class ConcurrentCopying : public GarbageCollector {
  public:
-  explicit ConcurrentCopying(Heap* heap, bool generational = false,
-                             const std::string& name_prefix = "")
-      : GarbageCollector(heap,
-                         name_prefix + (name_prefix.empty() ? "" : " ") +
-                         "concurrent copying + mark sweep") {
-    UNUSED(generational);
-  }
+  // TODO: disable thse flags for production use.
+  // Enable the no-from-space-refs verification at the pause.
+  static constexpr bool kEnableNoFromSpaceRefsVerification = true;
+  // Enable the from-space bytes/objects check.
+  static constexpr bool kEnableFromSpaceAccountingCheck = true;
+  // Enable verbose mode.
+  static constexpr bool kVerboseMode = true;
 
-  ~ConcurrentCopying() {}
+  ConcurrentCopying(Heap* heap, const std::string& name_prefix = "");
+  ~ConcurrentCopying();
 
-  virtual void RunPhases() OVERRIDE {}
+  virtual void RunPhases() OVERRIDE;
+  void InitializePhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void MarkingPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void ReclaimPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void FinishPhase();
+
+  void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
   virtual GcType GetGcType() const OVERRIDE {
     return kGcTypePartial;
   }
   virtual CollectorType GetCollectorType() const OVERRIDE {
     return kCollectorTypeCC;
   }
-  virtual void RevokeAllThreadLocalBuffers() OVERRIDE {}
+  virtual void RevokeAllThreadLocalBuffers() OVERRIDE;
+  void SetRegionSpace(space::RegionSpace* region_space) {
+    DCHECK(region_space != nullptr);
+    region_space_ = region_space;
+  }
+  space::RegionSpace* RegionSpace() {
+    return region_space_;
+  }
+  void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset, mirror::Object* ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsInToSpace(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    DCHECK(ref != nullptr);
+    return IsMarked(ref) == ref;
+  }
+  mirror::Object* Mark(mirror::Object* from_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsMarking() const {
+    return is_marking_;
+  }
+  bool IsActive() const {
+    return is_active_;
+  }
+  Barrier& GetBarrier() {
+    return *gc_barrier_;
+  }
 
  private:
+  mirror::Object* PopOffMarkStack();
+  template<bool kThreadSafe>
+  void PushOntoMarkStack(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::Object* Copy(mirror::Object* from_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void Scan(mirror::Object* to_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void Process(mirror::Object* obj, MemberOffset offset)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void Process(mirror::Object** root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static void ProcessRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void VerifyNoFromSpaceReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  accounting::ObjectStack* GetAllocationStack();
+  accounting::ObjectStack* GetLiveStack();
+  bool ProcessMarkStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DelayReferenceReferent(mirror::Class* klass, mirror::Reference* reference)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void ProcessReferences(Thread* self, bool concurrent)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::Object* IsMarked(mirror::Object* from_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static mirror::Object* MarkCallback(mirror::Object* from_ref, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static mirror::Object* IsMarkedCallback(mirror::Object* from_ref, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static bool IsHeapReferenceMarkedCallback(
+      mirror::HeapReference<mirror::Object>* field, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  static void ProcessMarkStackCallback(void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SweepSystemWeaks(Thread* self)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+  void Sweep(bool swap_bitmaps)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+  void SweepLargeObjects(bool swap_bitmaps)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+  void ClearBlackPtrs()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+  void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::Object* AllocateInSkippedBlock(size_t alloc_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void CheckEmptyMarkQueue() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void IssueEmptyCheckpoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsOnAllocStack(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::Object* GetFwdPtr(mirror::Object* from_ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetFwdPtr(mirror::Object* from_ref, mirror::Object* to_ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void FlipThreadRoots() LOCKS_EXCLUDED(Locks::mutator_lock_);;
+  void SwapStacks(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void RecordLiveStackFreezeSize(Thread* self);
+  void ComputeUnevacFromSpaceLiveRatio();
+
+  space::RegionSpace* region_space_;      // The underlying region space.
+  std::unique_ptr<Barrier> gc_barrier_;
+  MarkQueue mark_queue_;
+  bool is_marking_;                       // True while marking is ongoing.
+  bool is_active_;                        // True while the collection is ongoing.
+  bool is_asserting_to_space_invariant_;  // True while asserting the to-space invariant.
+  ImmuneRegion immune_region_;
+  std::unique_ptr<accounting::HeapBitmap> cc_heap_bitmap_;
+  std::vector<accounting::SpaceBitmap<kObjectAlignment>*> cc_bitmaps_;
+  accounting::SpaceBitmap<kObjectAlignment>* region_space_bitmap_;
+  // A cache of Heap::GetMarkBitmap().
+  accounting::HeapBitmap* heap_mark_bitmap_;
+  size_t live_stack_freeze_size_;
+  size_t from_space_num_objects_at_first_pause_;
+  size_t from_space_num_bytes_at_first_pause_;
+  Atomic<int> is_mark_queue_push_disallowed_;
+
+  // How many objects and bytes we moved. Used for accounting.
+  Atomic<size_t> bytes_moved_;
+  Atomic<size_t> objects_moved_;
+
+  // The skipped blocks are memory blocks/chucks that were copies of
+  // objects that were unused due to lost races (cas failures) at
+  // object copy/forward pointer install. They are reused.
+  Mutex skipped_blocks_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  std::multimap<size_t, uint8_t*> skipped_blocks_map_ GUARDED_BY(skipped_blocks_lock_);
+  Atomic<size_t> to_space_bytes_skipped_;
+  Atomic<size_t> to_space_objects_skipped_;
+
+  accounting::ReadBarrierTable* rb_table_;
+  bool force_evacuate_all_;  // True if all regions are evacuated.
+
+  friend class ConcurrentCopyingRefFieldsVisitor;
+  friend class ConcurrentCopyingImmuneSpaceObjVisitor;
+  friend class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor;
+  friend class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor;
+  friend class ConcurrentCopyingClearBlackPtrsVisitor;
+  friend class ConcurrentCopyingLostCopyVisitor;
+  friend class ThreadFlipVisitor;
+  friend class FlipCallback;
+  friend class ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor;
+
   DISALLOW_COPY_AND_ASSIGN(ConcurrentCopying);
 };
 
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 9e6a800..8be18be 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -102,7 +102,7 @@
   total_time_ns_ += current_iteration->GetDurationNs();
   for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
     MutexLock mu(self, pause_histogram_lock_);
-    pause_histogram_.AddValue(pause_time / 1000);
+    pause_histogram_.AdjustAndAddValue(pause_time);
   }
   ATRACE_END();
 }
diff --git a/runtime/gc/collector/immune_region.h b/runtime/gc/collector/immune_region.h
index 277525e..30144f0 100644
--- a/runtime/gc/collector/immune_region.h
+++ b/runtime/gc/collector/immune_region.h
@@ -57,6 +57,13 @@
     UpdateSize();
   }
 
+  mirror::Object* Begin() {
+    return begin_;
+  }
+  mirror::Object* End() {
+    return end_;
+  }
+
  private:
   bool IsEmpty() const {
     return size_ == 0;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index b2482ac..234bce5 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -197,7 +197,7 @@
   BindBitmaps();
   t.NewTiming("ProcessCards");
   // Process dirty cards and add dirty cards to mod-union tables.
-  heap_->ProcessCards(GetTimings(), false);
+  heap_->ProcessCards(GetTimings(), false, false, true);
   // Clear the whole card table since we can not Get any additional dirty cards during the
   // paused GC. This saves memory but only works for pause the world collectors.
   t.NewTiming("ClearCardTable");
@@ -274,11 +274,11 @@
 }
 
 void MarkCompact::ResizeMarkStack(size_t new_size) {
-  std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End());
+  std::vector<StackReference<Object>> temp(mark_stack_->Begin(), mark_stack_->End());
   CHECK_LE(mark_stack_->Size(), new_size);
   mark_stack_->Resize(new_size);
-  for (const auto& obj : temp) {
-    mark_stack_->PushBack(obj);
+  for (auto& obj : temp) {
+    mark_stack_->PushBack(obj.AsMirrorPtr());
   }
 }
 
@@ -300,22 +300,20 @@
 }
 
 void MarkCompact::MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr,
-                                          void* arg) {
+                                            void* arg) {
   reinterpret_cast<MarkCompact*>(arg)->MarkObject(obj_ptr->AsMirrorPtr());
 }
 
 void MarkCompact::DelayReferenceReferentCallback(mirror::Class* klass, mirror::Reference* ref,
-                                               void* arg) {
+                                                 void* arg) {
   reinterpret_cast<MarkCompact*>(arg)->DelayReferenceReferent(klass, ref);
 }
 
-void MarkCompact::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                   RootType /*root_type*/) {
+void MarkCompact::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root);
 }
 
-void MarkCompact::UpdateRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                     RootType /*root_type*/) {
+void MarkCompact::UpdateRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   mirror::Object* obj = *root;
   mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
   if (obj != new_obj) {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index f40e870..06304bf 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "lock_word.h"
@@ -45,7 +46,7 @@
 
 namespace accounting {
   template <typename T> class AtomicStack;
-  typedef AtomicStack<mirror::Object*> ObjectStack;
+  typedef AtomicStack<mirror::Object> ObjectStack;
 }  // namespace accounting
 
 namespace space {
@@ -113,8 +114,7 @@
   void SweepSystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
-                               RootType /*root_type*/)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
@@ -156,13 +156,13 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Expand mark stack to 2x its current size.
-  void ResizeMarkStack(size_t new_size);
+  void ResizeMarkStack(size_t new_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns true if we should sweep the space.
   bool ShouldSweepSpace(space::ContinuousSpace* space) const;
 
   // Push an object onto the mark stack.
-  void MarkStackPush(mirror::Object* obj);
+  void MarkStackPush(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void UpdateAndMarkModUnion()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
@@ -180,8 +180,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
   // Update the references of objects by using the forwarding addresses.
   void UpdateReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
-  static void UpdateRootCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/)
+  static void UpdateRootCallback(mirror::Object** root, void* arg, const RootInfo& /*root_info*/)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
   // Move objects and restore lock words.
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6ad44e6..cd63d26 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -217,7 +217,7 @@
     Thread* self = Thread::Current();
     CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self));
     // Process dirty cards and add dirty cards to mod union tables, also ages cards.
-    heap_->ProcessCards(GetTimings(), false);
+    heap_->ProcessCards(GetTimings(), false, true, false);
     // The checkpoint root marking is required to avoid a race condition which occurs if the
     // following happens during a reference write:
     // 1. mutator dirties the card (write barrier)
@@ -255,7 +255,8 @@
   BindBitmaps();
   FindDefaultSpaceBitmap();
   // Process dirty cards and add dirty cards to mod union tables.
-  heap_->ProcessCards(GetTimings(), false);
+  // If the GC type is non sticky, then we just clear the cards instead of ageing them.
+  heap_->ProcessCards(GetTimings(), false, true, GetGcType() != kGcTypeSticky);
   WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
   MarkRoots(self);
   MarkReachableObjects();
@@ -330,11 +331,11 @@
     // Someone else acquired the lock and expanded the mark stack before us.
     return;
   }
-  std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End());
+  std::vector<StackReference<Object>> temp(mark_stack_->Begin(), mark_stack_->End());
   CHECK_LE(mark_stack_->Size(), new_size);
   mark_stack_->Resize(new_size);
-  for (const auto& obj : temp) {
-    mark_stack_->PushBack(obj);
+  for (auto& obj : temp) {
+    mark_stack_->PushBack(obj.AsMirrorPtr());
   }
 }
 
@@ -460,42 +461,35 @@
   }
 }
 
-void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                         RootType /*root_type*/) {
+void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
 }
 
-void MarkSweep::VerifyRootMarked(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void MarkSweep::VerifyRootMarked(Object** root, void* arg, const RootInfo& /*root_info*/) {
   CHECK(reinterpret_cast<MarkSweep*>(arg)->IsMarked(*root));
 }
 
-void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void MarkSweep::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
 }
 
-void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
-                                   const StackVisitor* visitor, RootType root_type) {
-  reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor, root_type);
+void MarkSweep::VerifyRootCallback(Object** root, void* arg, const RootInfo& root_info) {
+  reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(*root, root_info);
 }
 
-void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const StackVisitor* visitor,
-                           RootType root_type) {
+void MarkSweep::VerifyRoot(const Object* root, const RootInfo& root_info) {
   // See if the root is on any space bitmap.
   if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
     space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
     if (large_object_space != nullptr && !large_object_space->Contains(root)) {
-      LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type;
-      if (visitor != NULL) {
-        LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg;
-      }
+      LOG(ERROR) << "Found invalid root: " << root << " ";
+      root_info.Describe(LOG(ERROR));
     }
   }
 }
 
 void MarkSweep::VerifyRoots() {
-  Runtime::Current()->GetThreadList()->VerifyRoots(VerifyRootCallback, this);
+  Runtime::Current()->GetThreadList()->VisitRoots(VerifyRootCallback, this);
 }
 
 void MarkSweep::MarkRoots(Thread* self) {
@@ -561,7 +555,7 @@
 class MarkStackTask : public Task {
  public:
   MarkStackTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, size_t mark_stack_size,
-                Object** mark_stack)
+                StackReference<Object>* mark_stack)
       : mark_sweep_(mark_sweep),
         thread_pool_(thread_pool),
         mark_stack_pos_(mark_stack_size) {
@@ -634,11 +628,11 @@
   MarkSweep* const mark_sweep_;
   ThreadPool* const thread_pool_;
   // Thread local mark stack for this task.
-  Object* mark_stack_[kMaxSize];
+  StackReference<Object> mark_stack_[kMaxSize];
   // Mark stack position.
   size_t mark_stack_pos_;
 
-  void MarkStackPush(Object* obj) ALWAYS_INLINE {
+  ALWAYS_INLINE void MarkStackPush(Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     if (UNLIKELY(mark_stack_pos_ == kMaxSize)) {
       // Mark stack overflow, give 1/2 the stack to the thread pool as a new work task.
       mark_stack_pos_ /= 2;
@@ -648,7 +642,7 @@
     }
     DCHECK(obj != nullptr);
     DCHECK_LT(mark_stack_pos_, kMaxSize);
-    mark_stack_[mark_stack_pos_++] = obj;
+    mark_stack_[mark_stack_pos_++].Assign(obj);
   }
 
   virtual void Finalize() {
@@ -667,7 +661,7 @@
       Object* obj = nullptr;
       if (kUseMarkStackPrefetch) {
         while (mark_stack_pos_ != 0 && prefetch_fifo.size() < kFifoSize) {
-          Object* mark_stack_obj = mark_stack_[--mark_stack_pos_];
+          Object* const mark_stack_obj = mark_stack_[--mark_stack_pos_].AsMirrorPtr();
           DCHECK(mark_stack_obj != nullptr);
           __builtin_prefetch(mark_stack_obj);
           prefetch_fifo.push_back(mark_stack_obj);
@@ -681,7 +675,7 @@
         if (UNLIKELY(mark_stack_pos_ == 0)) {
           break;
         }
-        obj = mark_stack_[--mark_stack_pos_];
+        obj = mark_stack_[--mark_stack_pos_].AsMirrorPtr();
       }
       DCHECK(obj != nullptr);
       visitor(obj);
@@ -694,12 +688,12 @@
   CardScanTask(ThreadPool* thread_pool, MarkSweep* mark_sweep,
                accounting::ContinuousSpaceBitmap* bitmap,
                uint8_t* begin, uint8_t* end, uint8_t minimum_age, size_t mark_stack_size,
-               Object** mark_stack_obj)
+               StackReference<Object>* mark_stack_obj, bool clear_card)
       : MarkStackTask<false>(thread_pool, mark_sweep, mark_stack_size, mark_stack_obj),
         bitmap_(bitmap),
         begin_(begin),
         end_(end),
-        minimum_age_(minimum_age) {
+        minimum_age_(minimum_age), clear_card_(clear_card) {
   }
 
  protected:
@@ -707,6 +701,7 @@
   uint8_t* const begin_;
   uint8_t* const end_;
   const uint8_t minimum_age_;
+  const bool clear_card_;
 
   virtual void Finalize() {
     delete this;
@@ -715,7 +710,9 @@
   virtual void Run(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
     ScanObjectParallelVisitor visitor(this);
     accounting::CardTable* card_table = mark_sweep_->GetHeap()->GetCardTable();
-    size_t cards_scanned = card_table->Scan(bitmap_, begin_, end_, visitor, minimum_age_);
+    size_t cards_scanned = clear_card_ ?
+                           card_table->Scan<true>(bitmap_, begin_, end_, visitor, minimum_age_) :
+                           card_table->Scan<false>(bitmap_, begin_, end_, visitor, minimum_age_);
     VLOG(heap) << "Parallel scanning cards " << reinterpret_cast<void*>(begin_) << " - "
         << reinterpret_cast<void*>(end_) << " = " << cards_scanned;
     // Finish by emptying our local mark stack.
@@ -746,8 +743,8 @@
     TimingLogger::ScopedTiming t(paused ? "(Paused)ScanGrayObjects" : __FUNCTION__,
         GetTimings());
     // Try to take some of the mark stack since we can pass this off to the worker tasks.
-    Object** mark_stack_begin = mark_stack_->Begin();
-    Object** mark_stack_end = mark_stack_->End();
+    StackReference<Object>* mark_stack_begin = mark_stack_->Begin();
+    StackReference<Object>* mark_stack_end = mark_stack_->End();
     const size_t mark_stack_size = mark_stack_end - mark_stack_begin;
     // Estimated number of work tasks we will create.
     const size_t mark_stack_tasks = GetHeap()->GetContinuousSpaces().size() * thread_count;
@@ -770,6 +767,11 @@
       // Calculate how much address range each task gets.
       const size_t card_delta = RoundUp(address_range / thread_count + 1,
                                         accounting::CardTable::kCardSize);
+      // If paused and the space is neither zygote nor image space, we could clear the dirty
+      // cards to avoid accumulating them to increase card scanning load in the following GC
+      // cycles. We need to keep dirty cards of image space and zygote space in order to track
+      // references to the other spaces.
+      bool clear_card = paused && !space->IsZygoteSpace() && !space->IsImageSpace();
       // Create the worker tasks for this space.
       while (card_begin != card_end) {
         // Add a range of cards.
@@ -784,7 +786,7 @@
         // Add the new task to the thread pool.
         auto* task = new CardScanTask(thread_pool, this, space->GetMarkBitmap(), card_begin,
                                       card_begin + card_increment, minimum_age,
-                                      mark_stack_increment, mark_stack_end);
+                                      mark_stack_increment, mark_stack_end, clear_card);
         thread_pool->AddTask(self, task);
         card_begin += card_increment;
       }
@@ -818,8 +820,14 @@
         }
         TimingLogger::ScopedTiming t(name, GetTimings());
         ScanObjectVisitor visitor(this);
-        card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor,
-                         minimum_age);
+        bool clear_card = paused && !space->IsZygoteSpace() && !space->IsImageSpace();
+        if (clear_card) {
+          card_table->Scan<true>(space->GetMarkBitmap(), space->Begin(), space->End(), visitor,
+                                 minimum_age);
+        } else {
+          card_table->Scan<false>(space->GetMarkBitmap(), space->Begin(), space->End(), visitor,
+                                  minimum_age);
+        }
       }
     }
   }
@@ -947,9 +955,9 @@
 
 void MarkSweep::VerifyIsLive(const Object* obj) {
   if (!heap_->GetLiveBitmap()->Test(obj)) {
-    accounting::ObjectStack* allocation_stack = heap_->allocation_stack_.get();
-    CHECK(std::find(allocation_stack->Begin(), allocation_stack->End(), obj) !=
-        allocation_stack->End()) << "Found dead object " << obj << "\n" << heap_->DumpSpaces();
+    // TODO: Consider live stack? Has this code bitrotted?
+    CHECK(!heap_->allocation_stack_->Contains(obj))
+        << "Found dead object " << obj << "\n" << heap_->DumpSpaces();
   }
 }
 
@@ -981,7 +989,11 @@
       mark_sweep_->GetHeap()->RevokeRosAllocThreadLocalBuffers(thread);
       ATRACE_END();
     }
-    mark_sweep_->GetBarrier().Pass(self);
+    // If thread is a running mutator, then act on behalf of the garbage collector.
+    // See the code in ThreadList::RunCheckpoint.
+    if (thread->GetState() == kRunnable) {
+      mark_sweep_->GetBarrier().Pass(self);
+    }
   }
 
  private:
@@ -998,7 +1010,11 @@
   // run through the barrier including self.
   size_t barrier_count = thread_list->RunCheckpoint(&check_point);
   // Release locks then wait for all mutator threads to pass the barrier.
-  // TODO: optimize to not release locks when there are no threads to wait for.
+  // If there are no threads to wait which implys that all the checkpoint functions are finished,
+  // then no need to release locks.
+  if (barrier_count == 0) {
+    return;
+  }
   Locks::heap_bitmap_lock_->ExclusiveUnlock(self);
   Locks::mutator_lock_->SharedUnlock(self);
   {
@@ -1018,7 +1034,7 @@
   ObjectBytePair freed;
   ObjectBytePair freed_los;
   // How many objects are left in the array, modified after each space is swept.
-  Object** objects = allocations->Begin();
+  StackReference<Object>* objects = allocations->Begin();
   size_t count = allocations->Size();
   // Change the order to ensure that the non-moving space last swept as an optimization.
   std::vector<space::ContinuousSpace*> sweep_spaces;
@@ -1046,9 +1062,9 @@
     if (swap_bitmaps) {
       std::swap(live_bitmap, mark_bitmap);
     }
-    Object** out = objects;
+    StackReference<Object>* out = objects;
     for (size_t i = 0; i < count; ++i) {
-      Object* obj = objects[i];
+      Object* const obj = objects[i].AsMirrorPtr();
       if (kUseThreadLocalAllocationStack && obj == nullptr) {
         continue;
       }
@@ -1065,7 +1081,7 @@
           chunk_free_buffer[chunk_free_pos++] = obj;
         }
       } else {
-        *(out++) = obj;
+        (out++)->Assign(obj);
       }
     }
     if (chunk_free_pos > 0) {
@@ -1087,7 +1103,7 @@
       std::swap(large_live_objects, large_mark_objects);
     }
     for (size_t i = 0; i < count; ++i) {
-      Object* obj = objects[i];
+      Object* const obj = objects[i].AsMirrorPtr();
       // Handle large objects.
       if (kUseThreadLocalAllocationStack && obj == nullptr) {
         continue;
@@ -1188,7 +1204,7 @@
                                      static_cast<size_t>(MarkStackTask<false>::kMaxSize));
   CHECK_GT(chunk_size, 0U);
   // Split the current mark stack up into work tasks.
-  for (mirror::Object **it = mark_stack_->Begin(), **end = mark_stack_->End(); it < end; ) {
+  for (auto* it = mark_stack_->Begin(), *end = mark_stack_->End(); it < end; ) {
     const size_t delta = std::min(static_cast<size_t>(end - it), chunk_size);
     thread_pool->AddTask(self, new MarkStackTask<false>(thread_pool, this, delta, it));
     it += delta;
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 9ac110d..3f99e21 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "object_callbacks.h"
@@ -46,7 +47,7 @@
 
 namespace accounting {
   template<typename T> class AtomicStack;
-  typedef AtomicStack<mirror::Object*> ObjectStack;
+  typedef AtomicStack<mirror::Object> ObjectStack;
 }  // namespace accounting
 
 namespace collector {
@@ -135,7 +136,8 @@
 
   // Sweeps unmarked objects to complete the garbage collection. Virtual as by default it sweeps
   // all allocation spaces. Partial and sticky GCs want to just sweep a subset of the heap.
-  virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+  virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Sweeps unmarked objects to complete the garbage collection.
   void SweepLargeObjects(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -161,13 +163,14 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
 
   static mirror::Object* VerifySystemWeakIsLiveCallback(mirror::Object* obj, void* arg)
-      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   void VerifySystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
 
   // Verify that an object is live, either in a live bitmap or in the allocation stack.
   void VerifyIsLive(const mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* obj, void* arg)
@@ -182,13 +185,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                               RootType root_type)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static void VerifyRootMarked(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                               RootType /*root_type*/)
+  static void VerifyRootMarked(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
@@ -196,8 +197,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                                       RootType root_type)
+  static void MarkRootParallelCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Marks an object.
@@ -225,11 +225,12 @@
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   void MarkObjectNonNull(mirror::Object* obj)
-        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-        EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   // Marks an object atomically, safe to use from multiple threads.
-  void MarkObjectNonNullParallel(mirror::Object* obj);
+  void MarkObjectNonNullParallel(mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns true if we need to add obj to a mark stack.
   bool MarkObjectParallel(const mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
@@ -240,21 +241,21 @@
       NO_THREAD_SAFETY_ANALYSIS;
 
   // Expand mark stack to 2x its current size.
-  void ExpandMarkStack() EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_);
-  void ResizeMarkStack(size_t new_size) EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_);
+  void ExpandMarkStack() EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void ResizeMarkStack(size_t new_size) EXCLUSIVE_LOCKS_REQUIRED(mark_stack_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns how many threads we should use for the current GC phase based on if we are paused,
   // whether or not we care about pauses.
   size_t GetThreadCount(bool paused) const;
 
-  static void VerifyRootCallback(const mirror::Object* root, void* arg, size_t vreg,
-                                 const StackVisitor *visitor, RootType root_type);
+  static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info);
 
-  void VerifyRoot(const mirror::Object* root, size_t vreg, const StackVisitor* visitor,
-                  RootType root_type) NO_THREAD_SAFETY_ANALYSIS;
+  void VerifyRoot(const mirror::Object* root, const RootInfo& root_info) NO_THREAD_SAFETY_ANALYSIS;
 
   // Push a single reference on a mark stack.
-  void PushOnMarkStack(mirror::Object* obj);
+  void PushOnMarkStack(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Blackens objects grayed during a garbage collection.
   void ScanGrayObjects(bool paused, uint8_t minimum_age)
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index cb9f111..c1ba5e3 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -216,7 +216,7 @@
   // Assume the cleared space is already empty.
   BindBitmaps();
   // Process dirty cards and add dirty cards to mod-union tables.
-  heap_->ProcessCards(GetTimings(), kUseRememberedSet && generational_);
+  heap_->ProcessCards(GetTimings(), kUseRememberedSet && generational_, false, true);
   // Clear the whole card table since we can not Get any additional dirty cards during the
   // paused GC. This saves memory but only works for pause the world collectors.
   t.NewTiming("ClearCardTable");
@@ -253,8 +253,17 @@
   RecordFree(ObjectBytePair(from_objects - to_objects, from_bytes - to_bytes));
   // Clear and protect the from space.
   from_space_->Clear();
-  VLOG(heap) << "Protecting from_space_: " << *from_space_;
-  from_space_->GetMemMap()->Protect(kProtectFromSpace ? PROT_NONE : PROT_READ);
+  if (kProtectFromSpace && !from_space_->IsRosAllocSpace()) {
+    // Protect with PROT_NONE.
+    VLOG(heap) << "Protecting from_space_ : " << *from_space_;
+    from_space_->GetMemMap()->Protect(PROT_NONE);
+  } else {
+    // If RosAllocSpace, we'll leave it as PROT_READ here so the
+    // rosaloc verification can read the metadata magic number and
+    // protect it with PROT_NONE later in FinishPhase().
+    VLOG(heap) << "Protecting from_space_ with PROT_READ : " << *from_space_;
+    from_space_->GetMemMap()->Protect(PROT_READ);
+  }
   heap_->PreSweepingGcVerification(this);
   if (swap_semi_spaces_) {
     heap_->SwapSemiSpaces();
@@ -412,11 +421,11 @@
 }
 
 void SemiSpace::ResizeMarkStack(size_t new_size) {
-  std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End());
+  std::vector<StackReference<Object>> temp(mark_stack_->Begin(), mark_stack_->End());
   CHECK_LE(mark_stack_->Size(), new_size);
   mark_stack_->Resize(new_size);
-  for (const auto& obj : temp) {
-    mark_stack_->PushBack(obj);
+  for (auto& obj : temp) {
+    mark_stack_->PushBack(obj.AsMirrorPtr());
   }
 }
 
@@ -591,8 +600,7 @@
   reinterpret_cast<SemiSpace*>(arg)->DelayReferenceReferent(klass, ref);
 }
 
-void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void SemiSpace::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   auto ref = StackReference<mirror::Object>::FromMirrorPtr(*root);
   reinterpret_cast<SemiSpace*>(arg)->MarkObject(&ref);
   if (*root != ref.AsMirrorPtr()) {
@@ -749,6 +757,10 @@
 
 void SemiSpace::FinishPhase() {
   TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+  if (kProtectFromSpace && from_space_->IsRosAllocSpace()) {
+    VLOG(heap) << "Protecting from_space_ with PROT_NONE : " << *from_space_;
+    from_space_->GetMemMap()->Protect(PROT_NONE);
+  }
   // Null the "to" and "from" spaces since compacting from one to the other isn't valid until
   // further action is done by the heap.
   to_space_ = nullptr;
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 1c4f1e4..192fb14 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "mirror/object_reference.h"
@@ -44,7 +45,7 @@
 
 namespace accounting {
   template <typename T> class AtomicStack;
-  typedef AtomicStack<mirror::Object*> ObjectStack;
+  typedef AtomicStack<mirror::Object> ObjectStack;
 }  // namespace accounting
 
 namespace space {
@@ -132,8 +133,7 @@
   void SweepSystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
-                               RootType /*root_type*/)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
@@ -178,13 +178,13 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Expand mark stack to 2x its current size.
-  void ResizeMarkStack(size_t new_size);
+  void ResizeMarkStack(size_t new_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns true if we should sweep the space.
   virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const;
 
   // Push an object onto the mark stack.
-  void MarkStackPush(mirror::Object* obj);
+  void MarkStackPush(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void UpdateAndMarkModUnion()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index ef5d56e..9275e6d 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -46,6 +46,19 @@
 };
 std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type);
 
+static constexpr CollectorType kCollectorTypeDefault =
+#if ART_DEFAULT_GC_TYPE_IS_CMS
+    kCollectorTypeCMS
+#elif ART_DEFAULT_GC_TYPE_IS_SS
+    kCollectorTypeSS
+#elif ART_DEFAULT_GC_TYPE_IS_GSS
+    kCollectorTypeGSS
+#else
+    kCollectorTypeCMS
+#error "ART default GC type must be set"
+#endif
+    ;  // NOLINT [whitespace/semicolon] [5]
+
 }  // namespace gc
 }  // namespace art
 
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 9d2f6d1..b8c2452 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -25,6 +25,7 @@
 #include "gc/space/bump_pointer_space-inl.h"
 #include "gc/space/dlmalloc_space-inl.h"
 #include "gc/space/large_object_space.h"
+#include "gc/space/region_space-inl.h"
 #include "gc/space/rosalloc_space-inl.h"
 #include "runtime.h"
 #include "handle_scope-inl.h"
@@ -66,11 +67,12 @@
   size_t bytes_allocated;
   size_t usable_size;
   size_t new_num_bytes_allocated = 0;
-  if (allocator == kAllocatorTypeTLAB) {
+  if (allocator == kAllocatorTypeTLAB || allocator == kAllocatorTypeRegionTLAB) {
     byte_count = RoundUp(byte_count, space::BumpPointerSpace::kAlignment);
   }
   // If we have a thread local allocation we don't need to update bytes allocated.
-  if (allocator == kAllocatorTypeTLAB && byte_count <= self->TlabSize()) {
+  if ((allocator == kAllocatorTypeTLAB || allocator == kAllocatorTypeRegionTLAB) &&
+      byte_count <= self->TlabSize()) {
     obj = self->AllocTlab(byte_count);
     DCHECK(obj != nullptr) << "AllocTlab can't fail";
     obj->SetClass(klass);
@@ -195,7 +197,7 @@
 inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type,
                                            size_t alloc_size, size_t* bytes_allocated,
                                            size_t* usable_size) {
-  if (allocator_type != kAllocatorTypeTLAB &&
+  if (allocator_type != kAllocatorTypeTLAB && allocator_type != kAllocatorTypeRegionTLAB &&
       UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
     return nullptr;
   }
@@ -265,6 +267,55 @@
       *usable_size = alloc_size;
       break;
     }
+    case kAllocatorTypeRegion: {
+      DCHECK(region_space_ != nullptr);
+      alloc_size = RoundUp(alloc_size, space::RegionSpace::kAlignment);
+      ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+      break;
+    }
+    case kAllocatorTypeRegionTLAB: {
+      DCHECK(region_space_ != nullptr);
+      DCHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment);
+      if (UNLIKELY(self->TlabSize() < alloc_size)) {
+        if (space::RegionSpace::kRegionSize >= alloc_size) {
+          // Non-large. Check OOME for a tlab.
+          if (LIKELY(!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, space::RegionSpace::kRegionSize))) {
+            // Try to allocate a tlab.
+            if (!region_space_->AllocNewTlab(self)) {
+              // Failed to allocate a tlab. Try non-tlab.
+              ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+              return ret;
+            }
+            *bytes_allocated = space::RegionSpace::kRegionSize;
+            // Fall-through.
+          } else {
+            // Check OOME for a non-tlab allocation.
+            if (!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size)) {
+              ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+              return ret;
+            } else {
+              // Neither tlab or non-tlab works. Give up.
+              return nullptr;
+            }
+          }
+        } else {
+          // Large. Check OOME.
+          if (LIKELY(!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
+            ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size);
+            return ret;
+          } else {
+            return nullptr;
+          }
+        }
+      } else {
+        *bytes_allocated = 0;
+      }
+      // The allocation can't fail.
+      ret = self->AllocTlab(alloc_size);
+      DCHECK(ret != nullptr);
+      *usable_size = alloc_size;
+      break;
+    }
     default: {
       LOG(FATAL) << "Invalid allocator type";
       ret = nullptr;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 0fd0a9f..419d555 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -30,6 +30,7 @@
 #include "common_throws.h"
 #include "cutils/sched_policy.h"
 #include "debugger.h"
+#include "dex_file-inl.h"
 #include "gc/accounting/atomic_stack.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap-inl.h"
@@ -48,9 +49,11 @@
 #include "gc/space/dlmalloc_space-inl.h"
 #include "gc/space/image_space.h"
 #include "gc/space/large_object_space.h"
+#include "gc/space/region_space.h"
 #include "gc/space/rosalloc_space-inl.h"
 #include "gc/space/space-inl.h"
 #include "gc/space/zygote_space.h"
+#include "gc/task_processor.h"
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "heap-inl.h"
 #include "image.h"
@@ -76,8 +79,6 @@
 
 static constexpr size_t kCollectorTransitionStressIterations = 0;
 static constexpr size_t kCollectorTransitionStressWait = 10 * 1000;  // Microseconds
-static constexpr bool kGCALotMode = false;
-static constexpr size_t kGcAlotInterval = KB;
 // Minimum amount of remaining bytes before a concurrent GC is triggered.
 static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
 static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB;
@@ -99,6 +100,15 @@
 static const char* kNonMovingSpaceName = "non moving space";
 static const char* kZygoteSpaceName = "zygote space";
 static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB;
+static constexpr bool kGCALotMode = false;
+// GC alot mode uses a small allocation stack to stress test a lot of GC.
+static constexpr size_t kGcAlotAllocationStackSize = 4 * KB /
+    sizeof(mirror::HeapReference<mirror::Object>);
+// Verify objet has a small allocation stack size since searching the allocation stack is slow.
+static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB /
+    sizeof(mirror::HeapReference<mirror::Object>);
+static constexpr size_t kDefaultAllocationStackSize = 8 * MB /
+    sizeof(mirror::HeapReference<mirror::Object>);
 
 Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
            double target_utilization, double foreground_heap_growth_multiplier,
@@ -121,10 +131,7 @@
       foreground_collector_type_(foreground_collector_type),
       background_collector_type_(background_collector_type),
       desired_collector_type_(foreground_collector_type_),
-      heap_trim_request_lock_(nullptr),
-      last_trim_time_(0),
-      heap_transition_or_trim_target_time_(0),
-      heap_trim_request_pending_(false),
+      pending_task_lock_(nullptr),
       parallel_gc_threads_(parallel_gc_threads),
       conc_gc_threads_(conc_gc_threads),
       low_memory_mode_(low_memory_mode),
@@ -158,19 +165,19 @@
       verify_pre_gc_rosalloc_(verify_pre_gc_rosalloc),
       verify_pre_sweeping_rosalloc_(verify_pre_sweeping_rosalloc),
       verify_post_gc_rosalloc_(verify_post_gc_rosalloc),
-      last_gc_time_ns_(NanoTime()),
-      allocation_rate_(0),
       /* For GC a lot mode, we limit the allocations stacks to be kGcAlotInterval allocations. This
        * causes a lot of GC since we do a GC for alloc whenever the stack is full. When heap
        * verification is enabled, we limit the size of allocation stacks to speed up their
        * searching.
        */
-      max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval
-          : (kVerifyObjectSupport > kVerifyObjectModeFast) ? KB : MB),
+      max_allocation_stack_size_(kGCALotMode ? kGcAlotAllocationStackSize
+          : (kVerifyObjectSupport > kVerifyObjectModeFast) ? kVerifyObjectAllocationStackSize :
+          kDefaultAllocationStackSize),
       current_allocator_(kAllocatorTypeDlMalloc),
       current_non_moving_allocator_(kAllocatorTypeNonMoving),
       bump_pointer_space_(nullptr),
       temp_space_(nullptr),
+      region_space_(nullptr),
       min_free_(min_free),
       max_free_(max_free),
       target_utilization_(target_utilization),
@@ -185,6 +192,8 @@
       min_interval_homogeneous_space_compaction_by_oom_(
           min_interval_homogeneous_space_compaction_by_oom),
       last_time_homogeneous_space_compaction_by_oom_(NanoTime()),
+      pending_collector_transition_(nullptr),
+      pending_heap_trim_(nullptr),
       use_homogeneous_space_compaction_for_oom_(use_homogeneous_space_compaction_for_oom) {
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
     LOG(INFO) << "Heap() entering";
@@ -204,6 +213,12 @@
   mark_bitmap_.reset(new accounting::HeapBitmap(this));
   // Requested begin for the alloc space, to follow the mapped image and oat files
   uint8_t* requested_alloc_space_begin = nullptr;
+  if (foreground_collector_type_ == kCollectorTypeCC) {
+    // Need to use a low address so that we can allocate a contiguous
+    // 2 * Xmx space when there's no image (dex2oat for target).
+    CHECK_GE(300 * MB, non_moving_space_capacity);
+    requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity;
+  }
   if (!image_file_name.empty()) {
     std::string error_msg;
     space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str(),
@@ -234,8 +249,9 @@
                                      +-main alloc space2 / bump space 2 (capacity_)+-
                                      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
   */
-  // We don't have hspace compaction enabled with GSS.
-  if (foreground_collector_type_ == kCollectorTypeGSS) {
+  // We don't have hspace compaction enabled with GSS or CC.
+  if (foreground_collector_type_ == kCollectorTypeGSS ||
+      foreground_collector_type_ == kCollectorTypeCC) {
     use_homogeneous_space_compaction_for_oom_ = false;
   }
   bool support_homogeneous_space_compaction =
@@ -273,10 +289,12 @@
     // Try to reserve virtual memory at a lower address if we have a separate non moving space.
     request_begin = reinterpret_cast<uint8_t*>(300 * MB);
   }
-  // Attempt to create 2 mem maps at or after the requested begin.
-  main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
-                                                    &error_str));
-  CHECK(main_mem_map_1.get() != nullptr) << error_str;
+  if (foreground_collector_type_ != kCollectorTypeCC) {
+    // Attempt to create 2 mem maps at or after the requested begin.
+    main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
+                                                      &error_str));
+    CHECK(main_mem_map_1.get() != nullptr) << error_str;
+  }
   if (support_homogeneous_space_compaction ||
       background_collector_type_ == kCollectorTypeSS ||
       foreground_collector_type_ == kCollectorTypeSS) {
@@ -298,7 +316,10 @@
     AddSpace(non_moving_space_);
   }
   // Create other spaces based on whether or not we have a moving GC.
-  if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
+  if (foreground_collector_type_ == kCollectorTypeCC) {
+    region_space_ = space::RegionSpace::Create("Region space", capacity_ * 2, request_begin);
+    AddSpace(region_space_);
+  } else if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
     // Create bump pointer spaces.
     // We only to create the bump pointer if the foreground collector is a compacting GC.
     // TODO: Place bump-pointer spaces somewhere to minimize size of card table.
@@ -343,11 +364,11 @@
   CHECK(non_moving_space_ != nullptr);
   CHECK(!non_moving_space_->CanMoveObjects());
   // Allocate the large object space.
-  if (large_object_space_type == space::kLargeObjectSpaceTypeFreeList) {
+  if (large_object_space_type == space::LargeObjectSpaceType::kFreeList) {
     large_object_space_ = space::FreeListSpace::Create("free list large object space", nullptr,
                                                        capacity_);
     CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
-  } else if (large_object_space_type == space::kLargeObjectSpaceTypeMap) {
+  } else if (large_object_space_type == space::LargeObjectSpaceType::kMap) {
     large_object_space_ = space::LargeObjectMapSpace::Create("mem map large object space");
     CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
   } else {
@@ -372,6 +393,12 @@
   // Allocate the card table.
   card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
   CHECK(card_table_.get() != NULL) << "Failed to create card table";
+
+  if (foreground_collector_type_ == kCollectorTypeCC && kUseTableLookupReadBarrier) {
+    rb_table_.reset(new accounting::ReadBarrierTable());
+    DCHECK(rb_table_->IsAllCleared());
+  }
+
   // Card cache for now since it makes it easier for us to update the references to the copying
   // spaces.
   accounting::ModUnionTable* mod_union_table =
@@ -400,8 +427,8 @@
   gc_complete_lock_ = new Mutex("GC complete lock");
   gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
                                                 *gc_complete_lock_));
-  heap_trim_request_lock_ = new Mutex("Heap trim request lock");
-  last_gc_size_ = GetBytesAllocated();
+  task_processor_.reset(new TaskProcessor());
+  pending_task_lock_ = new Mutex("Pending task lock");
   if (ignore_max_footprint_) {
     SetIdealFootprint(std::numeric_limits<size_t>::max());
     concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
@@ -696,25 +723,83 @@
   }
 }
 
+// Visit objects when threads aren't suspended. If concurrent moving
+// GC, disable moving GC and suspend threads and then visit objects.
 void Heap::VisitObjects(ObjectCallback callback, void* arg) {
-  // GCs can move objects, so don't allow this.
-  ScopedAssertNoThreadSuspension ants(Thread::Current(), "Visiting objects");
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertSharedHeld(self);
+  DCHECK(!Locks::mutator_lock_->IsExclusiveHeld(self)) << "Call VisitObjectsPaused() instead";
+  if (IsGcConcurrentAndMoving()) {
+    // Concurrent moving GC. Just suspending threads isn't sufficient
+    // because a collection isn't one big pause and we could suspend
+    // threads in the middle (between phases) of a concurrent moving
+    // collection where it's not easily known which objects are alive
+    // (both the region space and the non-moving space) or which
+    // copies of objects to visit, and the to-space invariant could be
+    // easily broken. Visit objects while GC isn't running by using
+    // IncrementDisableMovingGC() and threads are suspended.
+    IncrementDisableMovingGC(self);
+    self->TransitionFromRunnableToSuspended(kWaitingForVisitObjects);
+    ThreadList* tl = Runtime::Current()->GetThreadList();
+    tl->SuspendAll();
+    VisitObjectsInternalRegionSpace(callback, arg);
+    VisitObjectsInternal(callback, arg);
+    tl->ResumeAll();
+    self->TransitionFromSuspendedToRunnable();
+    DecrementDisableMovingGC(self);
+  } else {
+    // GCs can move objects, so don't allow this.
+    ScopedAssertNoThreadSuspension ants(self, "Visiting objects");
+    DCHECK(region_space_ == nullptr);
+    VisitObjectsInternal(callback, arg);
+  }
+}
+
+// Visit objects when threads are already suspended.
+void Heap::VisitObjectsPaused(ObjectCallback callback, void* arg) {
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertExclusiveHeld(self);
+  VisitObjectsInternalRegionSpace(callback, arg);
+  VisitObjectsInternal(callback, arg);
+}
+
+// Visit objects in the region spaces.
+void Heap::VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg) {
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertExclusiveHeld(self);
+  if (region_space_ != nullptr) {
+    DCHECK(IsGcConcurrentAndMoving());
+    if (!zygote_creation_lock_.IsExclusiveHeld(self)) {
+      // Exclude the pre-zygote fork time where the semi-space collector
+      // calls VerifyHeapReferences() as part of the zygote compaction
+      // which then would call here without the moving GC disabled,
+      // which is fine.
+      DCHECK(IsMovingGCDisabled(self));
+    }
+    region_space_->Walk(callback, arg);
+  }
+}
+
+// Visit objects in the other spaces.
+void Heap::VisitObjectsInternal(ObjectCallback callback, void* arg) {
   if (bump_pointer_space_ != nullptr) {
     // Visit objects in bump pointer space.
     bump_pointer_space_->Walk(callback, arg);
   }
   // TODO: Switch to standard begin and end to use ranged a based loop.
-  for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End();
-      it < end; ++it) {
-    mirror::Object* obj = *it;
+  for (auto* it = allocation_stack_->Begin(), *end = allocation_stack_->End(); it < end; ++it) {
+    mirror::Object* const obj = it->AsMirrorPtr();
     if (obj != nullptr && obj->GetClass() != nullptr) {
       // Avoid the race condition caused by the object not yet being written into the allocation
-      // stack or the class not yet being written in the object. Or, if kUseThreadLocalAllocationStack,
-      // there can be nulls on the allocation stack.
+      // stack or the class not yet being written in the object. Or, if
+      // kUseThreadLocalAllocationStack, there can be nulls on the allocation stack.
       callback(obj, arg);
     }
   }
-  GetLiveBitmap()->Walk(callback, arg);
+  {
+    ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+    GetLiveBitmap()->Walk(callback, arg);
+  }
 }
 
 void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -847,7 +932,7 @@
     os << "Zygote space size " << PrettySize(zygote_space_->Size()) << "\n";
   }
   os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
-  os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
+  os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_);
   BaseMutex::DumpAll(os);
 }
 
@@ -862,7 +947,7 @@
   STLDeleteElements(&continuous_spaces_);
   STLDeleteElements(&discontinuous_spaces_);
   delete gc_complete_lock_;
-  delete heap_trim_request_lock_;
+  delete pending_task_lock_;
   VLOG(heap) << "Finished ~Heap()";
 }
 
@@ -925,6 +1010,9 @@
     } else if (allocator_type == kAllocatorTypeBumpPointer ||
                allocator_type == kAllocatorTypeTLAB) {
       space = bump_pointer_space_;
+    } else if (allocator_type == kAllocatorTypeRegion ||
+               allocator_type == kAllocatorTypeRegionTLAB) {
+      space = region_space_;
     }
     if (space != nullptr) {
       space->LogFragmentationAllocFailure(oss, byte_count);
@@ -933,37 +1021,23 @@
   self->ThrowOutOfMemoryError(oss.str().c_str());
 }
 
-void Heap::DoPendingTransitionOrTrim() {
-  Thread* self = Thread::Current();
-  CollectorType desired_collector_type;
-  // Wait until we reach the desired transition time.
-  while (true) {
-    uint64_t wait_time;
-    {
-      MutexLock mu(self, *heap_trim_request_lock_);
-      desired_collector_type = desired_collector_type_;
-      uint64_t current_time = NanoTime();
-      if (current_time >= heap_transition_or_trim_target_time_) {
-        break;
-      }
-      wait_time = heap_transition_or_trim_target_time_ - current_time;
-    }
-    ScopedThreadStateChange tsc(self, kSleeping);
-    usleep(wait_time / 1000);  // Usleep takes microseconds.
-  }
+void Heap::DoPendingCollectorTransition() {
+  CollectorType desired_collector_type = desired_collector_type_;
   // Launch homogeneous space compaction if it is desired.
   if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
     if (!CareAboutPauseTimes()) {
       PerformHomogeneousSpaceCompact();
+    } else {
+      VLOG(gc) << "Homogeneous compaction ignored due to jank perceptible process state";
     }
-    // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
-    desired_collector_type = collector_type_;
-    return;
+  } else {
+    TransitionCollector(desired_collector_type);
   }
-  // Transition the collector if the desired collector type is not the same as the current
-  // collector type.
-  TransitionCollector(desired_collector_type);
+}
+
+void Heap::Trim(Thread* self) {
   if (!CareAboutPauseTimes()) {
+    ATRACE_BEGIN("Deflating monitors");
     // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
     // about pauses.
     Runtime* runtime = Runtime::Current();
@@ -973,9 +1047,10 @@
     VLOG(heap) << "Deflating " << count << " monitors took "
         << PrettyDuration(NanoTime() - start_time);
     runtime->GetThreadList()->ResumeAll();
+    ATRACE_END();
   }
-  // Do a heap trim if it is needed.
-  Trim();
+  TrimIndirectReferenceTables(self);
+  TrimSpaces(self);
 }
 
 class TrimIndirectReferenceTableClosure : public Closure {
@@ -986,24 +1061,35 @@
     ATRACE_BEGIN("Trimming reference table");
     thread->GetJniEnv()->locals.Trim();
     ATRACE_END();
-    barrier_->Pass(Thread::Current());
+    // If thread is a running mutator, then act on behalf of the trim thread.
+    // See the code in ThreadList::RunCheckpoint.
+    if (thread->GetState() == kRunnable) {
+      barrier_->Pass(Thread::Current());
+    }
   }
 
  private:
   Barrier* const barrier_;
 };
 
-
-void Heap::Trim() {
-  Thread* self = Thread::Current();
-  {
-    MutexLock mu(self, *heap_trim_request_lock_);
-    if (!heap_trim_request_pending_ || last_trim_time_ + kHeapTrimWait >= NanoTime()) {
-      return;
-    }
-    last_trim_time_ = NanoTime();
-    heap_trim_request_pending_ = false;
+void Heap::TrimIndirectReferenceTables(Thread* self) {
+  ScopedObjectAccess soa(self);
+  ATRACE_BEGIN(__FUNCTION__);
+  JavaVMExt* vm = soa.Vm();
+  // Trim globals indirect reference table.
+  vm->TrimGlobals();
+  // Trim locals indirect reference tables.
+  Barrier barrier(0);
+  TrimIndirectReferenceTableClosure closure(&barrier);
+  ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+  size_t barrier_count = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
+  if (barrier_count != 0) {
+    barrier.Increment(self, barrier_count);
   }
+  ATRACE_END();
+}
+
+void Heap::TrimSpaces(Thread* self) {
   {
     // Need to do this before acquiring the locks since we don't want to get suspended while
     // holding any locks.
@@ -1015,20 +1101,8 @@
     WaitForGcToCompleteLocked(kGcCauseTrim, self);
     collector_type_running_ = kCollectorTypeHeapTrim;
   }
-  // Trim reference tables.
-  {
-    ScopedObjectAccess soa(self);
-    JavaVMExt* vm = soa.Vm();
-    // Trim globals indirect reference table.
-    vm->TrimGlobals();
-    // Trim locals indirect reference tables.
-    Barrier barrier(0);
-    TrimIndirectReferenceTableClosure closure(&barrier);
-    ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
-    size_t barrier_count = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
-    barrier.Increment(self, barrier_count);
-  }
-  uint64_t start_ns = NanoTime();
+  ATRACE_BEGIN(__FUNCTION__);
+  const uint64_t start_ns = NanoTime();
   // Trim the managed spaces.
   uint64_t total_alloc_space_allocated = 0;
   uint64_t total_alloc_space_size = 0;
@@ -1051,6 +1125,9 @@
   if (bump_pointer_space_ != nullptr) {
     total_alloc_space_allocated -= bump_pointer_space_->Size();
   }
+  if (region_space_ != nullptr) {
+    total_alloc_space_allocated -= region_space_->GetBytesAllocated();
+  }
   const float managed_utilization = static_cast<float>(total_alloc_space_allocated) /
       static_cast<float>(total_alloc_space_size);
   uint64_t gc_heap_end_ns = NanoTime();
@@ -1078,6 +1155,7 @@
       << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed)
       << ") heaps. Managed heap utilization of " << static_cast<int>(100 * managed_utilization)
       << "%.";
+  ATRACE_END();
 }
 
 bool Heap::IsValidObjectAddress(const mirror::Object* obj) const {
@@ -1122,6 +1200,9 @@
     // a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained.
     return temp_space_->Contains(obj);
   }
+  if (region_space_ != nullptr && region_space_->HasAddress(obj)) {
+    return true;
+  }
   space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true);
   space::DiscontinuousSpace* d_space = nullptr;
   if (c_space != nullptr) {
@@ -1471,10 +1552,7 @@
 
 void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from,
                           uint64_t* counts) {
-  // Can't do any GC in this function since this may move classes.
-  ScopedAssertNoThreadSuspension ants(Thread::Current(), "CountInstances");
   InstanceCounter counter(classes, use_is_assignable_from, counts);
-  ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   VisitObjects(InstanceCounter::Callback, &counter);
 }
 
@@ -1505,10 +1583,7 @@
 
 void Heap::GetInstances(mirror::Class* c, int32_t max_count,
                         std::vector<mirror::Object*>& instances) {
-  // Can't do any GC in this function since this may move classes.
-  ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetInstances");
   InstanceCollector collector(c, max_count, instances);
-  ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   VisitObjects(&InstanceCollector::Callback, &collector);
 }
 
@@ -1550,10 +1625,7 @@
 
 void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count,
                                std::vector<mirror::Object*>& referring_objects) {
-  // Can't do any GC in this function since this may move the object o.
-  ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetReferringObjects");
   ReferringObjectsFinder finder(o, max_count, referring_objects);
-  ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
   VisitObjects(&ReferringObjectsFinder::Callback, &finder);
 }
 
@@ -1604,8 +1676,6 @@
   // Make sure that we will have enough room to copy.
   CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
   Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
-  // Leave as prot read so that we can still run ROSAlloc verification on this space.
-  from_space->GetMemMap()->Protect(PROT_READ);
   const uint64_t space_size_after_compaction = to_space->Size();
   main_space_ = to_space;
   main_space_backup_.reset(from_space);
@@ -1628,7 +1698,6 @@
   return HomogeneousSpaceCompactResult::kSuccess;
 }
 
-
 void Heap::TransitionCollector(CollectorType collector_type) {
   if (collector_type == collector_type_) {
     return;
@@ -1780,7 +1849,15 @@
     collector_type_ = collector_type;
     gc_plan_.clear();
     switch (collector_type_) {
-      case kCollectorTypeCC:  // Fall-through.
+      case kCollectorTypeCC: {
+        gc_plan_.push_back(collector::kGcTypeFull);
+        if (use_tlab_) {
+          ChangeAllocator(kAllocatorTypeRegionTLAB);
+        } else {
+          ChangeAllocator(kAllocatorTypeRegion);
+        }
+        break;
+      }
       case kCollectorTypeMC:  // Fall-through.
       case kCollectorTypeSS:  // Fall-through.
       case kCollectorTypeGSS: {
@@ -1963,7 +2040,11 @@
     // Compact the bump pointer space to a new zygote bump pointer space.
     bool reset_main_space = false;
     if (IsMovingGc(collector_type_)) {
-      zygote_collector.SetFromSpace(bump_pointer_space_);
+      if (collector_type_ == kCollectorTypeCC) {
+        zygote_collector.SetFromSpace(region_space_);
+      } else {
+        zygote_collector.SetFromSpace(bump_pointer_space_);
+      }
     } else {
       CHECK(main_space_ != nullptr);
       // Copy from the main space.
@@ -1984,7 +2065,11 @@
       delete old_main_space;
       AddSpace(main_space_);
     } else {
-      bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+      if (collector_type_ == kCollectorTypeCC) {
+        region_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+      } else {
+        bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+      }
     }
     if (temp_space_ != nullptr) {
       CHECK(temp_space_->IsEmpty());
@@ -2059,9 +2144,9 @@
                           accounting::ObjectStack* stack) {
   DCHECK(bitmap1 != nullptr);
   DCHECK(bitmap2 != nullptr);
-  mirror::Object** limit = stack->End();
-  for (mirror::Object** it = stack->Begin(); it != limit; ++it) {
-    const mirror::Object* obj = *it;
+  const auto* limit = stack->End();
+  for (auto* it = stack->Begin(); it != limit; ++it) {
+    const mirror::Object* obj = it->AsMirrorPtr();
     if (!kUseThreadLocalAllocationStack || obj != nullptr) {
       if (bitmap1->HasAddress(obj)) {
         bitmap1->Set(obj);
@@ -2119,7 +2204,9 @@
   ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
   Locks::mutator_lock_->AssertNotHeld(self);
   if (self->IsHandlingStackOverflow()) {
-    LOG(WARNING) << "Performing GC on a thread that is handling a stack overflow.";
+    // If we are throwing a stack overflow error we probably don't have enough remaining stack
+    // space to run the GC.
+    return collector::kGcTypeNone;
   }
   bool compacting_gc;
   {
@@ -2141,16 +2228,9 @@
     ++runtime->GetStats()->gc_for_alloc_count;
     ++self->GetStats()->gc_for_alloc_count;
   }
-  uint64_t gc_start_time_ns = NanoTime();
-  uint64_t gc_start_size = GetBytesAllocated();
-  // Approximate allocation rate in bytes / second.
-  uint64_t ms_delta = NsToMs(gc_start_time_ns - last_gc_time_ns_);
-  // Back to back GCs can cause 0 ms of wait time in between GC invocations.
-  if (LIKELY(ms_delta != 0)) {
-    allocation_rate_ = ((gc_start_size - last_gc_size_) * 1000) / ms_delta;
-    ATRACE_INT("Allocation rate KB/s", allocation_rate_ / KB);
-    VLOG(heap) << "Allocation rate: " << PrettySize(allocation_rate_) << "/s";
-  }
+  const uint64_t bytes_allocated_before_gc = GetBytesAllocated();
+  // Approximate heap size.
+  ATRACE_INT("Heap size (KB)", bytes_allocated_before_gc / KB);
 
   DCHECK_LT(gc_type, collector::kGcTypeMax);
   DCHECK_NE(gc_type, collector::kGcTypeNone);
@@ -2159,7 +2239,9 @@
   // TODO: Clean this up.
   if (compacting_gc) {
     DCHECK(current_allocator_ == kAllocatorTypeBumpPointer ||
-           current_allocator_ == kAllocatorTypeTLAB);
+           current_allocator_ == kAllocatorTypeTLAB ||
+           current_allocator_ == kAllocatorTypeRegion ||
+           current_allocator_ == kAllocatorTypeRegionTLAB);
     switch (collector_type_) {
       case kCollectorTypeSS:
         // Fall-through.
@@ -2170,6 +2252,7 @@
         collector = semi_space_collector_;
         break;
       case kCollectorTypeCC:
+        concurrent_copying_collector_->SetRegionSpace(region_space_);
         collector = concurrent_copying_collector_;
         break;
       case kCollectorTypeMC:
@@ -2179,7 +2262,7 @@
       default:
         LOG(FATAL) << "Invalid collector type " << static_cast<size_t>(collector_type_);
     }
-    if (collector != mark_compact_collector_) {
+    if (collector != mark_compact_collector_ && collector != concurrent_copying_collector_) {
       temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
       CHECK(temp_space_->IsEmpty());
     }
@@ -2203,11 +2286,11 @@
   collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());
   total_objects_freed_ever_ += GetCurrentGcIteration()->GetFreedObjects();
   total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes();
-  RequestHeapTrim();
+  RequestTrim(self);
   // Enqueue cleared references.
   reference_processor_.EnqueueClearedReferences(self);
   // Grow the heap so that we know when to perform the next GC.
-  GrowForUtilization(collector);
+  GrowForUtilization(collector, bytes_allocated_before_gc);
   const size_t duration = GetCurrentGcIteration()->GetDurationNs();
   const std::vector<uint64_t>& pause_times = GetCurrentGcIteration()->GetPauseTimes();
   // Print the GC if it is an explicit GC (e.g. Runtime.gc()) or a slow GC
@@ -2256,8 +2339,8 @@
   gc_complete_cond_->Broadcast(self);
 }
 
-static void RootMatchesObjectVisitor(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                     RootType /*root_type*/) {
+static void RootMatchesObjectVisitor(mirror::Object** root, void* arg,
+                                     const RootInfo& /*root_info*/) {
   mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
   if (*root == obj) {
     LOG(INFO) << "Object " << obj << " is a root";
@@ -2299,12 +2382,12 @@
     return heap_->IsLiveObjectLocked(obj, true, false, true);
   }
 
-  static void VerifyRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                                 RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
     if (!visitor->VerifyReference(nullptr, *root, MemberOffset(0))) {
       LOG(ERROR) << "Root " << *root << " is dead with type " << PrettyTypeOf(*root)
-          << " thread_id= " << thread_id << " root_type= " << root_type;
+          << " thread_id= " << root_info.GetThreadId() << " root_type= " << root_info.GetType();
     }
   }
 
@@ -2390,8 +2473,8 @@
         // Attempt to see if the card table missed the reference.
         ScanVisitor scan_visitor;
         uint8_t* byte_cover_begin = reinterpret_cast<uint8_t*>(card_table->AddrFromCard(card_addr));
-        card_table->Scan(bitmap, byte_cover_begin,
-                         byte_cover_begin + accounting::CardTable::kCardSize, scan_visitor);
+        card_table->Scan<false>(bitmap, byte_cover_begin,
+                                byte_cover_begin + accounting::CardTable::kCardSize, scan_visitor);
       }
 
       // Search to see if any of the roots reference our object.
@@ -2460,8 +2543,8 @@
 void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) {
   // Slow path, the allocation stack push back must have already failed.
   DCHECK(!self->PushOnThreadLocalAllocationStack(*obj));
-  mirror::Object** start_address;
-  mirror::Object** end_address;
+  StackReference<mirror::Object>* start_address;
+  StackReference<mirror::Object>* end_address;
   while (!allocation_stack_->AtomicBumpBack(kThreadLocalAllocationStackSize, &start_address,
                                             &end_address)) {
     // TODO: Add handle VerifyObject.
@@ -2496,7 +2579,7 @@
   // 2. Allocated during the GC (pre sweep GC verification).
   // We don't want to verify the objects in the live stack since they themselves may be
   // pointing to dead objects if they are not reachable.
-  VisitObjects(VerifyObjectVisitor::VisitCallback, &visitor);
+  VisitObjectsPaused(VerifyObjectVisitor::VisitCallback, &visitor);
   // Verify the roots:
   Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRootCallback, &visitor);
   if (visitor.GetFailureCount() > 0) {
@@ -2620,9 +2703,9 @@
   VerifyLiveStackReferences visitor(this);
   GetLiveBitmap()->Visit(visitor);
   // We can verify objects in the live stack since none of these should reference dead objects.
-  for (mirror::Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
-    if (!kUseThreadLocalAllocationStack || *it != nullptr) {
-      visitor(*it);
+  for (auto* it = live_stack_->Begin(); it != live_stack_->End(); ++it) {
+    if (!kUseThreadLocalAllocationStack || it->AsMirrorPtr() != nullptr) {
+      visitor(it->AsMirrorPtr());
     }
   }
   return !visitor.Failed();
@@ -2638,7 +2721,7 @@
 
 void Heap::RevokeAllThreadLocalAllocationStacks(Thread* self) {
   // This must be called only during the pause.
-  CHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
+  DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
   MutexLock mu2(self, *Locks::thread_list_lock_);
   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
@@ -2682,7 +2765,8 @@
   return it->second;
 }
 
-void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets) {
+void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards,
+                        bool clear_alloc_space_cards) {
   TimingLogger::ScopedTiming t(__FUNCTION__, timings);
   // Clear cards and keep track of cards cleared in the mod-union table.
   for (const auto& space : continuous_spaces_) {
@@ -2698,17 +2782,21 @@
           << static_cast<int>(collector_type_);
       TimingLogger::ScopedTiming t2("AllocSpaceRemSetClearCards", timings);
       rem_set->ClearCards();
-    } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) {
+    } else if (process_alloc_space_cards) {
       TimingLogger::ScopedTiming t2("AllocSpaceClearCards", timings);
-      // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards
-      // were dirty before the GC started.
-      // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread)
-      // -> clean(cleaning thread).
-      // The races are we either end up with: Aged card, unaged card. Since we have the checkpoint
-      // roots and then we scan / update mod union tables after. We will always scan either card.
-      // If we end up with the non aged card, we scan it it in the pause.
-      card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(),
-                                     VoidFunctor());
+      if (clear_alloc_space_cards) {
+        card_table_->ClearCardRange(space->Begin(), space->End());
+      } else {
+        // No mod union table for the AllocSpace. Age the cards so that the GC knows that these
+        // cards were dirty before the GC started.
+        // TODO: Need to use atomic for the case where aged(cleaning thread) -> dirty(other thread)
+        // -> clean(cleaning thread).
+        // The races are we either end up with: Aged card, unaged card. Since we have the
+        // checkpoint roots and then we scan / update mod union tables after. We will always
+        // scan either card. If we end up with the non aged card, we scan it it in the pause.
+        card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(),
+                                       VoidFunctor());
+      }
     }
   }
 }
@@ -2722,7 +2810,6 @@
   TimingLogger::ScopedTiming t(__FUNCTION__, timings);
   if (verify_pre_gc_heap_) {
     TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyHeapReferences", timings);
-    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     size_t failures = VerifyHeapReferences();
     if (failures > 0) {
       LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed with " << failures
@@ -2774,9 +2861,11 @@
   if (verify_pre_sweeping_heap_) {
     TimingLogger::ScopedTiming t2("(Paused)PostSweepingVerifyHeapReferences", timings);
     CHECK_NE(self->GetState(), kRunnable);
-    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    // Swapping bound bitmaps does nothing.
-    gc->SwapBitmaps();
+    {
+      WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+      // Swapping bound bitmaps does nothing.
+      gc->SwapBitmaps();
+    }
     // Pass in false since concurrent reference processing can mean that the reference referents
     // may point to dead objects at the point which PreSweepingGcVerification is called.
     size_t failures = VerifyHeapReferences(false);
@@ -2784,7 +2873,10 @@
       LOG(FATAL) << "Pre sweeping " << gc->GetName() << " GC verification failed with " << failures
           << " failures";
     }
-    gc->SwapBitmaps();
+    {
+      WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+      gc->SwapBitmaps();
+    }
   }
   if (verify_pre_sweeping_rosalloc_) {
     RosAllocVerification(timings, "PreSweepingRosAllocVerification");
@@ -2806,7 +2898,6 @@
   }
   if (verify_post_gc_heap_) {
     TimingLogger::ScopedTiming t2("(Paused)PostGcVerifyHeapReferences", timings);
-    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     size_t failures = VerifyHeapReferences();
     if (failures > 0) {
       LOG(FATAL) << "Pre " << gc->GetName() << " heap verification failed with " << failures
@@ -2917,25 +3008,24 @@
   return foreground_heap_growth_multiplier_;
 }
 
-void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) {
+void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
+                              uint64_t bytes_allocated_before_gc) {
   // We know what our utilization is at this moment.
   // This doesn't actually resize any memory. It just lets the heap grow more when necessary.
   const uint64_t bytes_allocated = GetBytesAllocated();
-  last_gc_size_ = bytes_allocated;
-  last_gc_time_ns_ = NanoTime();
   uint64_t target_size;
   collector::GcType gc_type = collector_ran->GetGcType();
+  const double multiplier = HeapGrowthMultiplier();  // Use the multiplier to grow more for
+  // foreground.
+  const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier);
+  const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier);
   if (gc_type != collector::kGcTypeSticky) {
     // Grow the heap for non sticky GC.
-    const float multiplier = HeapGrowthMultiplier();  // Use the multiplier to grow more for
-    // foreground.
-    intptr_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
+    ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
     CHECK_GE(delta, 0);
     target_size = bytes_allocated + delta * multiplier;
-    target_size = std::min(target_size,
-                           bytes_allocated + static_cast<uint64_t>(max_free_ * multiplier));
-    target_size = std::max(target_size,
-                           bytes_allocated + static_cast<uint64_t>(min_free_ * multiplier));
+    target_size = std::min(target_size, bytes_allocated + adjusted_max_free);
+    target_size = std::max(target_size, bytes_allocated + adjusted_min_free);
     native_need_to_run_finalization_ = true;
     next_gc_type_ = collector::kGcTypeSticky;
   } else {
@@ -2957,8 +3047,8 @@
       next_gc_type_ = non_sticky_gc_type;
     }
     // If we have freed enough memory, shrink the heap back down.
-    if (bytes_allocated + max_free_ < max_allowed_footprint_) {
-      target_size = bytes_allocated + max_free_;
+    if (bytes_allocated + adjusted_max_free < max_allowed_footprint_) {
+      target_size = bytes_allocated + adjusted_max_free;
     } else {
       target_size = std::max(bytes_allocated, static_cast<uint64_t>(max_allowed_footprint_));
     }
@@ -2966,11 +3056,18 @@
   if (!ignore_max_footprint_) {
     SetIdealFootprint(target_size);
     if (IsGcConcurrent()) {
+      const uint64_t freed_bytes = current_gc_iteration_.GetFreedBytes() +
+          current_gc_iteration_.GetFreedLargeObjectBytes();
+      // Bytes allocated will shrink by freed_bytes after the GC runs, so if we want to figure out
+      // how many bytes were allocated during the GC we need to add freed_bytes back on.
+      CHECK_GE(bytes_allocated + freed_bytes, bytes_allocated_before_gc);
+      const uint64_t bytes_allocated_during_gc = bytes_allocated + freed_bytes -
+          bytes_allocated_before_gc;
       // Calculate when to perform the next ConcurrentGC.
       // Calculate the estimated GC duration.
       const double gc_duration_seconds = NsToMs(current_gc_iteration_.GetDurationNs()) / 1000.0;
       // Estimate how many remaining bytes we will have when we need to start the next GC.
-      size_t remaining_bytes = allocation_rate_ * gc_duration_seconds;
+      size_t remaining_bytes = bytes_allocated_during_gc * gc_duration_seconds;
       remaining_bytes = std::min(remaining_bytes, kMaxConcurrentRemainingBytes);
       remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes);
       if (UNLIKELY(remaining_bytes > max_allowed_footprint_)) {
@@ -2990,6 +3087,20 @@
   }
 }
 
+void Heap::ClampGrowthLimit() {
+  capacity_ = growth_limit_;
+  for (const auto& space : continuous_spaces_) {
+    if (space->IsMallocSpace()) {
+      gc::space::MallocSpace* malloc_space = space->AsMallocSpace();
+      malloc_space->ClampGrowthLimit();
+    }
+  }
+  // This space isn't added for performance reasons.
+  if (main_space_backup_.get() != nullptr) {
+    main_space_backup_->ClampGrowthLimit();
+  }
+}
+
 void Heap::ClearGrowthLimit() {
   growth_limit_ = capacity_;
   for (const auto& space : continuous_spaces_) {
@@ -3022,57 +3133,109 @@
   RequestConcurrentGC(self);
 }
 
-void Heap::RequestConcurrentGC(Thread* self) {
-  // Make sure that we can do a concurrent GC.
-  Runtime* runtime = Runtime::Current();
-  if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
-      self->IsHandlingStackOverflow()) {
-    return;
+class Heap::ConcurrentGCTask : public HeapTask {
+ public:
+  explicit ConcurrentGCTask(uint64_t target_time) : HeapTask(target_time) { }
+  virtual void Run(Thread* self) OVERRIDE {
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    heap->ConcurrentGC(self);
+    heap->ClearConcurrentGCRequest();
   }
-  JNIEnv* env = self->GetJniEnv();
-  DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
-  DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr);
-  env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
-                            WellKnownClasses::java_lang_Daemons_requestGC);
-  CHECK(!env->ExceptionCheck());
+};
+
+static bool CanAddHeapTask(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_) {
+  Runtime* runtime = Runtime::Current();
+  return runtime != nullptr && runtime->IsFinishedStarting() && !runtime->IsShuttingDown(self) &&
+      !self->IsHandlingStackOverflow();
+}
+
+void Heap::ClearConcurrentGCRequest() {
+  concurrent_gc_pending_.StoreRelaxed(false);
+}
+
+void Heap::RequestConcurrentGC(Thread* self) {
+  if (CanAddHeapTask(self) &&
+      concurrent_gc_pending_.CompareExchangeStrongSequentiallyConsistent(false, true)) {
+    task_processor_->AddTask(self, new ConcurrentGCTask(NanoTime()));  // Start straight away.
+  }
 }
 
 void Heap::ConcurrentGC(Thread* self) {
-  if (Runtime::Current()->IsShuttingDown(self)) {
-    return;
-  }
-  // Wait for any GCs currently running to finish.
-  if (WaitForGcToComplete(kGcCauseBackground, self) == collector::kGcTypeNone) {
-    // If the we can't run the GC type we wanted to run, find the next appropriate one and try that
-    // instead. E.g. can't do partial, so do full instead.
-    if (CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false) ==
-        collector::kGcTypeNone) {
-      for (collector::GcType gc_type : gc_plan_) {
-        // Attempt to run the collector, if we succeed, we are done.
-        if (gc_type > next_gc_type_ &&
-            CollectGarbageInternal(gc_type, kGcCauseBackground, false) != collector::kGcTypeNone) {
-          break;
+  if (!Runtime::Current()->IsShuttingDown(self)) {
+    // Wait for any GCs currently running to finish.
+    if (WaitForGcToComplete(kGcCauseBackground, self) == collector::kGcTypeNone) {
+      // If the we can't run the GC type we wanted to run, find the next appropriate one and try that
+      // instead. E.g. can't do partial, so do full instead.
+      if (CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false) ==
+          collector::kGcTypeNone) {
+        for (collector::GcType gc_type : gc_plan_) {
+          // Attempt to run the collector, if we succeed, we are done.
+          if (gc_type > next_gc_type_ &&
+              CollectGarbageInternal(gc_type, kGcCauseBackground, false) !=
+                  collector::kGcTypeNone) {
+            break;
+          }
         }
       }
     }
   }
 }
 
-void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) {
-  Thread* self = Thread::Current();
-  {
-    MutexLock mu(self, *heap_trim_request_lock_);
-    if (desired_collector_type_ == desired_collector_type) {
-      return;
-    }
-    heap_transition_or_trim_target_time_ =
-        std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
-    desired_collector_type_ = desired_collector_type;
+class Heap::CollectorTransitionTask : public HeapTask {
+ public:
+  explicit CollectorTransitionTask(uint64_t target_time) : HeapTask(target_time) { }
+  virtual void Run(Thread* self) OVERRIDE {
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    heap->DoPendingCollectorTransition();
+    heap->ClearPendingCollectorTransition(self);
   }
-  SignalHeapTrimDaemon(self);
+};
+
+void Heap::ClearPendingCollectorTransition(Thread* self) {
+  MutexLock mu(self, *pending_task_lock_);
+  pending_collector_transition_ = nullptr;
 }
 
-void Heap::RequestHeapTrim() {
+void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) {
+  Thread* self = Thread::Current();
+  desired_collector_type_ = desired_collector_type;
+  if (desired_collector_type_ == collector_type_ || !CanAddHeapTask(self)) {
+    return;
+  }
+  CollectorTransitionTask* added_task = nullptr;
+  const uint64_t target_time = NanoTime() + delta_time;
+  {
+    MutexLock mu(self, *pending_task_lock_);
+    // If we have an existing collector transition, update the targe time to be the new target.
+    if (pending_collector_transition_ != nullptr) {
+      task_processor_->UpdateTargetRunTime(self, pending_collector_transition_, target_time);
+      return;
+    }
+    added_task = new CollectorTransitionTask(target_time);
+    pending_collector_transition_ = added_task;
+  }
+  task_processor_->AddTask(self, added_task);
+}
+
+class Heap::HeapTrimTask : public HeapTask {
+ public:
+  explicit HeapTrimTask(uint64_t delta_time) : HeapTask(NanoTime() + delta_time) { }
+  virtual void Run(Thread* self) OVERRIDE {
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    heap->Trim(self);
+    heap->ClearPendingTrim(self);
+  }
+};
+
+void Heap::ClearPendingTrim(Thread* self) {
+  MutexLock mu(self, *pending_task_lock_);
+  pending_heap_trim_ = nullptr;
+}
+
+void Heap::RequestTrim(Thread* self) {
+  if (!CanAddHeapTask(self)) {
+    return;
+  }
   // GC completed and now we must decide whether to request a heap trim (advising pages back to the
   // kernel) or not. Issuing a request will also cause trimming of the libc heap. As a trim scans
   // a space it will hold its lock and can become a cause of jank.
@@ -3085,42 +3248,17 @@
   // to utilization (which is probably inversely proportional to how much benefit we can expect).
   // We could try mincore(2) but that's only a measure of how many pages we haven't given away,
   // not how much use we're making of those pages.
-
-  Thread* self = Thread::Current();
-  Runtime* runtime = Runtime::Current();
-  if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) ||
-      runtime->IsZygote()) {
-    // Ignore the request if we are the zygote to prevent app launching lag due to sleep in heap
-    // trimmer daemon. b/17310019
-    // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time)
-    // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check
-    // as we don't hold the lock while requesting the trim).
-    return;
-  }
+  HeapTrimTask* added_task = nullptr;
   {
-    MutexLock mu(self, *heap_trim_request_lock_);
-    if (last_trim_time_ + kHeapTrimWait >= NanoTime()) {
-      // We have done a heap trim in the last kHeapTrimWait nanosecs, don't request another one
-      // just yet.
+    MutexLock mu(self, *pending_task_lock_);
+    if (pending_heap_trim_ != nullptr) {
+      // Already have a heap trim request in task processor, ignore this request.
       return;
     }
-    heap_trim_request_pending_ = true;
-    uint64_t current_time = NanoTime();
-    if (heap_transition_or_trim_target_time_ < current_time) {
-      heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait;
-    }
+    added_task = new HeapTrimTask(kHeapTrimWait);
+    pending_heap_trim_ = added_task;
   }
-  // Notify the daemon thread which will actually do the heap trim.
-  SignalHeapTrimDaemon(self);
-}
-
-void Heap::SignalHeapTrimDaemon(Thread* self) {
-  JNIEnv* env = self->GetJniEnv();
-  DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
-  DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != nullptr);
-  env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
-                            WellKnownClasses::java_lang_Daemons_requestHeapTrim);
-  CHECK(!env->ExceptionCheck());
+  task_processor_->AddTask(self, added_task);
 }
 
 void Heap::RevokeThreadLocalBuffers(Thread* thread) {
@@ -3130,6 +3268,9 @@
   if (bump_pointer_space_ != nullptr) {
     bump_pointer_space_->RevokeThreadLocalBuffers(thread);
   }
+  if (region_space_ != nullptr) {
+    region_space_->RevokeThreadLocalBuffers(thread);
+  }
 }
 
 void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) {
@@ -3145,10 +3286,13 @@
   if (bump_pointer_space_ != nullptr) {
     bump_pointer_space_->RevokeAllThreadLocalBuffers();
   }
+  if (region_space_ != nullptr) {
+    region_space_->RevokeAllThreadLocalBuffers();
+  }
 }
 
 bool Heap::IsGCRequestPending() const {
-  return concurrent_start_bytes_ != std::numeric_limits<size_t>::max();
+  return concurrent_gc_pending_.LoadRelaxed();
 }
 
 void Heap::RunFinalization(JNIEnv* env) {
@@ -3230,7 +3374,7 @@
 }
 
 void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) {
-  CHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
+  CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
         (c->IsVariableSize() || c->GetObjectSize() == byte_count));
   CHECK_GE(byte_count, sizeof(mirror::Object));
 }
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 4e1a0ff..57c1460 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -27,6 +27,7 @@
 #include "base/timing_logger.h"
 #include "gc/accounting/atomic_stack.h"
 #include "gc/accounting/card_table.h"
+#include "gc/accounting/read_barrier_table.h"
 #include "gc/gc_cause.h"
 #include "gc/collector/garbage_collector.h"
 #include "gc/collector/gc_type.h"
@@ -57,6 +58,7 @@
 namespace gc {
 
 class ReferenceProcessor;
+class TaskProcessor;
 
 namespace accounting {
   class HeapBitmap;
@@ -85,6 +87,7 @@
   class ImageSpace;
   class LargeObjectSpace;
   class MallocSpace;
+  class RegionSpace;
   class RosAllocSpace;
   class Space;
   class SpaceTest;
@@ -142,15 +145,14 @@
   static constexpr double kDefaultHeapGrowthMultiplier = 2.0;
   // Primitive arrays larger than this size are put in the large object space.
   static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
+
   // Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
   // since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
-#if USE_ART_LOW_4G_ALLOCATOR
   static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
-      space::kLargeObjectSpaceTypeFreeList;
-#else
-  static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
-      space::kLargeObjectSpaceTypeMap;
-#endif
+      USE_ART_LOW_4G_ALLOCATOR ?
+          space::LargeObjectSpaceType::kFreeList
+        : space::LargeObjectSpaceType::kMap;
+
   // Used so that we don't overflow the allocation time atomic integer.
   static constexpr size_t kTimeAdjust = 1024;
 
@@ -215,7 +217,11 @@
 
   // Visit all of the live objects in the heap.
   void VisitObjects(ObjectCallback callback, void* arg)
-      SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+  void VisitObjectsPaused(ObjectCallback callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
 
   void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -244,7 +250,7 @@
   void VerifyHeap() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
   // Returns how many failures occured.
   size_t VerifyHeapReferences(bool verify_referents = true)
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool VerifyMissingCardMarks()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
@@ -301,6 +307,10 @@
   // implement dalvik.system.VMRuntime.clearGrowthLimit.
   void ClearGrowthLimit();
 
+  // Make the current growth limit the new maximum capacity, unmaps pages at the end of spaces
+  // which will never be used. Used to implement dalvik.system.VMRuntime.clampGrowthLimit.
+  void ClampGrowthLimit();
+
   // Target ideal heap utilization ratio, implements
   // dalvik.system.VMRuntime.getTargetHeapUtilization.
   double GetTargetHeapUtilization() const {
@@ -401,6 +411,10 @@
     return card_table_.get();
   }
 
+  accounting::ReadBarrierTable* GetReadBarrierTable() const {
+    return rb_table_.get();
+  }
+
   void AddFinalizerReference(Thread* self, mirror::Object** object);
 
   // Returns the number of bytes currently allocated.
@@ -470,11 +484,11 @@
 
   void DumpForSigQuit(std::ostream& os);
 
-  // Do a pending heap transition or trim.
-  void DoPendingTransitionOrTrim() LOCKS_EXCLUDED(heap_trim_request_lock_);
+  // Do a pending collector transition.
+  void DoPendingCollectorTransition();
 
-  // Trim the managed and native heaps by releasing unused memory back to the OS.
-  void Trim() LOCKS_EXCLUDED(heap_trim_request_lock_);
+  // Deflate monitors, ... and trim the spaces.
+  void Trim(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_);
 
   void RevokeThreadLocalBuffers(Thread* thread);
   void RevokeRosAllocThreadLocalBuffers(Thread* thread);
@@ -500,6 +514,7 @@
 
   // Mark and empty stack.
   void FlushAllocStack()
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   // Revoke all the thread-local allocation stacks.
@@ -513,10 +528,12 @@
                       accounting::SpaceBitmap<kObjectAlignment>* bitmap2,
                       accounting::SpaceBitmap<kLargeObjectAlignment>* large_objects,
                       accounting::ObjectStack* stack)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   // Mark the specified allocation stack as live.
   void MarkAllocStackAsLive(accounting::ObjectStack* stack)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   // Unbind any bound bitmaps.
@@ -606,12 +623,49 @@
   ReferenceProcessor* GetReferenceProcessor() {
     return &reference_processor_;
   }
+  TaskProcessor* GetTaskProcessor() {
+    return task_processor_.get();
+  }
 
   bool HasZygoteSpace() const {
     return zygote_space_ != nullptr;
   }
 
+  collector::ConcurrentCopying* ConcurrentCopyingCollector() {
+    return concurrent_copying_collector_;
+  }
+
+  CollectorType CurrentCollectorType() {
+    return collector_type_;
+  }
+
+  bool IsGcConcurrentAndMoving() const {
+    if (IsGcConcurrent() && IsMovingGc(collector_type_)) {
+      // Assume no transition when a concurrent moving collector is used.
+      DCHECK_EQ(collector_type_, foreground_collector_type_);
+      DCHECK_EQ(foreground_collector_type_, background_collector_type_)
+          << "Assume no transition such that collector_type_ won't change";
+      return true;
+    }
+    return false;
+  }
+
+  bool IsMovingGCDisabled(Thread* self) {
+    MutexLock mu(self, *gc_complete_lock_);
+    return disable_moving_gc_count_ > 0;
+  }
+
+  // Request an asynchronous trim.
+  void RequestTrim(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+
+  // Request asynchronous GC.
+  void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+
  private:
+  class ConcurrentGCTask;
+  class CollectorTransitionTask;
+  class HeapTrimTask;
+
   // Compact source space to target space.
   void Compact(space::ContinuousMemMapAllocSpace* target_space,
                space::ContinuousMemMapAllocSpace* source_space,
@@ -632,10 +686,14 @@
   static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
     return
         allocator_type != kAllocatorTypeBumpPointer &&
-        allocator_type != kAllocatorTypeTLAB;
+        allocator_type != kAllocatorTypeTLAB &&
+        allocator_type != kAllocatorTypeRegion &&
+        allocator_type != kAllocatorTypeRegionTLAB;
   }
   static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) {
-    return AllocatorHasAllocationStack(allocator_type);
+    return
+        allocator_type != kAllocatorTypeBumpPointer &&
+        allocator_type != kAllocatorTypeTLAB;
   }
   static bool IsMovingGc(CollectorType collector_type) {
     return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS ||
@@ -702,12 +760,10 @@
       EXCLUSIVE_LOCKS_REQUIRED(gc_complete_lock_);
 
   void RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time)
-      LOCKS_EXCLUDED(heap_trim_request_lock_);
-  void RequestHeapTrim() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
+      LOCKS_EXCLUDED(pending_task_lock_);
+
   void RequestConcurrentGCAndSaveObject(Thread* self, mirror::Object** obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void RequestConcurrentGC(Thread* self)
-      LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
   bool IsGCRequestPending() const;
 
   // Sometimes CollectGarbageInternal decides to run a different Gc than you requested. Returns
@@ -725,7 +781,8 @@
   void PrePauseRosAllocVerification(collector::GarbageCollector* gc)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
   void PreSweepingGcVerification(collector::GarbageCollector* gc)
-      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
   void PostGcVerification(collector::GarbageCollector* gc)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
   void PostGcVerificationPaused(collector::GarbageCollector* gc)
@@ -752,8 +809,10 @@
 
   // Given the current contents of the alloc space, increase the allowed heap footprint to match
   // the target utilization ratio.  This should only be called immediately after a full garbage
-  // collection.
-  void GrowForUtilization(collector::GarbageCollector* collector_ran);
+  // collection. bytes_allocated_before_gc is used to measure bytes / second for the period which
+  // the GC was run.
+  void GrowForUtilization(collector::GarbageCollector* collector_ran,
+                          uint64_t bytes_allocated_before_gc = 0);
 
   size_t GetPercentFree();
 
@@ -761,14 +820,13 @@
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
   // Swap the allocation stack with the live stack.
-  void SwapStacks(Thread* self);
+  void SwapStacks(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Clear cards and update the mod union table.
-  void ProcessCards(TimingLogger* timings, bool use_rem_sets);
-
-  // Signal the heap trim daemon that there is something to do, either a heap transition or heap
-  // trim.
-  void SignalHeapTrimDaemon(Thread* self);
+  // Clear cards and update the mod union table. When process_alloc_space_cards is true,
+  // if clear_alloc_space_cards is true, then we clear cards instead of ageing them. We do
+  // not process the alloc space if process_alloc_space_cards is false.
+  void ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards,
+                    bool clear_alloc_space_cards);
 
   // Push an object onto the allocation stack.
   void PushOnAllocationStack(Thread* self, mirror::Object** obj)
@@ -778,12 +836,29 @@
   void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, mirror::Object** obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void ClearConcurrentGCRequest();
+  void ClearPendingTrim(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+  void ClearPendingCollectorTransition(Thread* self) LOCKS_EXCLUDED(pending_task_lock_);
+
   // What kind of concurrency behavior is the runtime after? Currently true for concurrent mark
   // sweep GC, false for other GC types.
   bool IsGcConcurrent() const ALWAYS_INLINE {
     return collector_type_ == kCollectorTypeCMS || collector_type_ == kCollectorTypeCC;
   }
 
+  // Trim the managed and native spaces by releasing unused memory back to the OS.
+  void TrimSpaces(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_);
+
+  // Trim 0 pages at the end of reference tables.
+  void TrimIndirectReferenceTables(Thread* self);
+
+  void VisitObjectsInternal(ObjectCallback callback, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+  void VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+      LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+
   // All-known continuous spaces, where objects lie within fixed bounds.
   std::vector<space::ContinuousSpace*> continuous_spaces_;
 
@@ -813,6 +888,8 @@
   // The card table, dirtied by the write barrier.
   std::unique_ptr<accounting::CardTable> card_table_;
 
+  std::unique_ptr<accounting::ReadBarrierTable> rb_table_;
+
   // A mod-union table remembers all of the references from the it's space to other spaces.
   AllocationTrackingSafeMap<space::Space*, accounting::ModUnionTable*, kAllocatorTagHeap>
       mod_union_tables_;
@@ -830,14 +907,8 @@
   // Desired collector type, heap trimming daemon transitions the heap if it is != collector_type_.
   CollectorType desired_collector_type_;
 
-  // Lock which guards heap trim requests.
-  Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  // When we want to perform the next heap trim (nano seconds).
-  uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_);
-  // When we want to perform the next heap transition (nano seconds) or heap trim.
-  uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_);
-  // If we have a heap trim request pending.
-  bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_);
+  // Lock which guards pending tasks.
+  Mutex* pending_task_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 
   // How many GC threads we may use for paused parts of garbage collection.
   const size_t parallel_gc_threads_;
@@ -877,6 +948,9 @@
   // Reference processor;
   ReferenceProcessor reference_processor_;
 
+  // Task processor, proxies heap trim requests to the daemon threads.
+  std::unique_ptr<TaskProcessor> task_processor_;
+
   // True while the garbage collector is running.
   volatile CollectorType collector_type_running_ GUARDED_BY(gc_complete_lock_);
 
@@ -885,7 +959,7 @@
   collector::GcType next_gc_type_;
 
   // Maximum size that the heap can reach.
-  const size_t capacity_;
+  size_t capacity_;
 
   // The size the heap is limited to. This is initially smaller than capacity, but for largeHeap
   // programs it is "cleared" making it the same as capacity.
@@ -963,12 +1037,6 @@
   // Parallel GC data structures.
   std::unique_ptr<ThreadPool> thread_pool_;
 
-  // The nanosecond time at which the last GC ended.
-  uint64_t last_gc_time_ns_;
-
-  // How many bytes were allocated at the end of the last GC.
-  uint64_t last_gc_size_;
-
   // Estimated allocation rate (bytes / second). Computed between the time of the last GC cycle
   // and the start of the current one.
   uint64_t allocation_rate_;
@@ -1000,6 +1068,8 @@
   // Temp space is the space which the semispace collector copies to.
   space::BumpPointerSpace* temp_space_;
 
+  space::RegionSpace* region_space_;
+
   // Minimum free guarantees that you always have at least min_free_ free bytes after growing for
   // utilization, regardless of target utilization ratio.
   size_t min_free_;
@@ -1055,11 +1125,20 @@
   // Count for performed homogeneous space compaction.
   Atomic<size_t> count_performed_homogeneous_space_compaction_;
 
+  // Whether or not a concurrent GC is pending.
+  Atomic<bool> concurrent_gc_pending_;
+
+  // Active tasks which we can modify (change target time, desired collector type, etc..).
+  CollectorTransitionTask* pending_collector_transition_ GUARDED_BY(pending_task_lock_);
+  HeapTrimTask* pending_heap_trim_ GUARDED_BY(pending_task_lock_);
+
   // Whether or not we use homogeneous space compaction to avoid OOM errors.
   bool use_homogeneous_space_compaction_for_oom_;
 
+  friend class CollectorTransitionTask;
   friend class collector::GarbageCollector;
   friend class collector::MarkCompact;
+  friend class collector::ConcurrentCopying;
   friend class collector::MarkSweep;
   friend class collector::SemiSpace;
   friend class ReferenceQueue;
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 012f9f9..01e8795 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -23,11 +23,14 @@
 #include "reflection.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
+#include "task_processor.h"
 #include "well_known_classes.h"
 
 namespace art {
 namespace gc {
 
+static constexpr bool kAsyncReferenceQueueAdd = false;
+
 ReferenceProcessor::ReferenceProcessor()
     : process_references_args_(nullptr, nullptr, nullptr),
       preserving_references_(false),
@@ -213,17 +216,43 @@
   cleared_references_.UpdateRoots(callback, arg);
 }
 
+class ClearedReferenceTask : public HeapTask {
+ public:
+  explicit ClearedReferenceTask(jobject cleared_references)
+      : HeapTask(NanoTime()), cleared_references_(cleared_references) {
+  }
+  virtual void Run(Thread* thread) {
+    ScopedObjectAccess soa(thread);
+    jvalue args[1];
+    args[0].l = cleared_references_;
+    InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
+    soa.Env()->DeleteGlobalRef(cleared_references_);
+  }
+
+ private:
+  const jobject cleared_references_;
+};
+
 void ReferenceProcessor::EnqueueClearedReferences(Thread* self) {
   Locks::mutator_lock_->AssertNotHeld(self);
+  // When a runtime isn't started there are no reference queues to care about so ignore.
   if (!cleared_references_.IsEmpty()) {
-    // When a runtime isn't started there are no reference queues to care about so ignore.
     if (LIKELY(Runtime::Current()->IsStarted())) {
-      ScopedObjectAccess soa(self);
-      ScopedLocalRef<jobject> arg(self->GetJniEnv(),
-                                  soa.AddLocalReference<jobject>(cleared_references_.GetList()));
-      jvalue args[1];
-      args[0].l = arg.get();
-      InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
+      jobject cleared_references;
+      {
+        ReaderMutexLock mu(self, *Locks::mutator_lock_);
+        cleared_references = self->GetJniEnv()->vm->AddGlobalRef(
+            self, cleared_references_.GetList());
+      }
+      if (kAsyncReferenceQueueAdd) {
+        // TODO: This can cause RunFinalization to terminate before newly freed objects are
+        // finalized since they may not be enqueued by the time RunFinalization starts.
+        Runtime::Current()->GetHeap()->GetTaskProcessor()->AddTask(
+            self, new ClearedReferenceTask(cleared_references));
+      } else {
+        ClearedReferenceTask task(cleared_references);
+        task.Run(self);
+      }
     }
     cleared_references_.Clear();
   }
@@ -234,7 +263,7 @@
   MutexLock mu(self, *Locks::reference_processor_lock_);
   // Wait untul we are done processing reference.
   while (SlowPathEnabled()) {
-    condition_.Wait(self);
+    condition_.WaitHoldingLocks(self);
   }
   // At this point, since the sentinel of the reference is live, it is guaranteed to not be
   // enqueued if we just finished processing references. Otherwise, we may be doing the main GC
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index 5eb095b..c67fd98 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -53,7 +53,7 @@
   // The slow path bool is contained in the reference class object, can only be set once
   // Only allow setting this with mutators suspended so that we can avoid using a lock in the
   // GetReferent fast path as an optimization.
-  void EnableSlowPath() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void EnableSlowPath() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // Decode the referent, may block if references are being processed.
   mirror::Object* GetReferent(Thread* self, mirror::Reference* reference)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::reference_processor_lock_);
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index 4003524..7be0704 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -17,6 +17,7 @@
 #include "reference_queue.h"
 
 #include "accounting/card_table-inl.h"
+#include "collector/concurrent_copying.h"
 #include "heap.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
@@ -85,21 +86,54 @@
   } else {
     ref->SetPendingNext<false>(nullptr);
   }
+  Heap* heap = Runtime::Current()->GetHeap();
+  if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
+      heap->ConcurrentCopyingCollector()->IsActive()) {
+    // Clear the gray ptr we left in ConcurrentCopying::ProcessMarkStack().
+    // We don't want to do this when the zygote compaction collector (SemiSpace) is running.
+    CHECK(ref != nullptr);
+    CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr())
+        << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer();
+    if (heap->ConcurrentCopyingCollector()->RegionSpace()->IsInToSpace(ref)) {
+      // Moving objects.
+      ref->SetReadBarrierPointer(ReadBarrier::WhitePtr());
+      CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
+    } else {
+      // Non-moving objects.
+      ref->SetReadBarrierPointer(ReadBarrier::BlackPtr());
+      CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr());
+    }
+  }
   return ref;
 }
 
 void ReferenceQueue::Dump(std::ostream& os) const {
   mirror::Reference* cur = list_;
   os << "Reference starting at list_=" << list_ << "\n";
-  while (cur != nullptr) {
+  if (cur == nullptr) {
+    return;
+  }
+  do {
     mirror::Reference* pending_next = cur->GetPendingNext();
-    os << "PendingNext=" << pending_next;
+    os << "Reference= " << cur << " PendingNext=" << pending_next;
     if (cur->IsFinalizerReferenceInstance()) {
       os << " Zombie=" << cur->AsFinalizerReference()->GetZombie();
     }
     os << "\n";
     cur = pending_next;
+  } while (cur != list_);
+}
+
+size_t ReferenceQueue::GetLength() const {
+  size_t count = 0;
+  mirror::Reference* cur = list_;
+  if (cur != nullptr) {
+    do {
+      ++count;
+      cur = cur->GetPendingNext();
+    } while (cur != list_);
   }
+  return count;
 }
 
 void ReferenceQueue::ClearWhiteReferences(ReferenceQueue* cleared_references,
diff --git a/runtime/gc/reference_queue.h b/runtime/gc/reference_queue.h
index 4ef8478..f7d89d0 100644
--- a/runtime/gc/reference_queue.h
+++ b/runtime/gc/reference_queue.h
@@ -56,12 +56,14 @@
   // overhead.
   void EnqueueReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Enqueue a reference without checking that it is enqueable.
   void EnqueuePendingReference(mirror::Reference* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Dequeue the first reference (returns list_).
   mirror::Reference* DequeuePendingReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Enqueues finalizer references with white referents.  White referents are blackened, moved to the
-  // zombie field, and the referent field is cleared.
+  // Enqueues finalizer references with white referents.  White referents are blackened, moved to
+  // the zombie field, and the referent field is cleared.
   void EnqueueFinalizerReferences(ReferenceQueue* cleared_references,
                                   IsHeapReferenceMarkedCallback* is_marked_callback,
                                   MarkObjectCallback* mark_object_callback, void* arg)
@@ -73,24 +75,22 @@
   void ForwardSoftReferences(IsHeapReferenceMarkedCallback* preserve_callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Unlink the reference list clearing references objects with white referents.  Cleared references
+  // Unlink the reference list clearing references objects with white referents. Cleared references
   // registered to a reference queue are scheduled for appending by the heap worker thread.
   void ClearWhiteReferences(ReferenceQueue* cleared_references,
                             IsHeapReferenceMarkedCallback* is_marked_callback, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void Dump(std::ostream& os) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void Dump(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  size_t GetLength() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool IsEmpty() const {
     return list_ == nullptr;
   }
-
   void Clear() {
     list_ = nullptr;
   }
-
-  mirror::Reference* GetList() {
+  mirror::Reference* GetList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return list_;
   }
 
@@ -102,7 +102,6 @@
   // Lock, used for parallel GC reference enqueuing. It allows for multiple threads simultaneously
   // calling AtomicEnqueueIfNotEnqueued.
   Mutex* const lock_;
-
   // The actual reference list. Only a root for the mark compact GC since it will be null for other
   // GC types.
   mirror::Reference* list_;
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
new file mode 100644
index 0000000..888c0d2
--- /dev/null
+++ b/runtime/gc/reference_queue_test.cc
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include "common_runtime_test.h"
+#include "reference_queue.h"
+#include "handle_scope-inl.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+namespace gc {
+
+class ReferenceQueueTest : public CommonRuntimeTest {};
+
+TEST_F(ReferenceQueueTest, EnqueueDequeue) {
+  Thread* self = Thread::Current();
+  StackHandleScope<20> hs(self);
+  Mutex lock("Reference queue lock");
+  ReferenceQueue queue(&lock);
+  ASSERT_TRUE(queue.IsEmpty());
+  ScopedObjectAccess soa(self);
+  ASSERT_EQ(queue.GetLength(), 0U);
+  auto ref_class = hs.NewHandle(
+      Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
+                                                      NullHandle<mirror::ClassLoader>()));
+  ASSERT_TRUE(ref_class.Get() != nullptr);
+  auto ref1(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
+  ASSERT_TRUE(ref1.Get() != nullptr);
+  auto ref2(hs.NewHandle(ref_class->AllocObject(self)->AsReference()));
+  ASSERT_TRUE(ref2.Get() != nullptr);
+  // FIFO ordering.
+  queue.EnqueuePendingReference(ref1.Get());
+  ASSERT_TRUE(!queue.IsEmpty());
+  ASSERT_EQ(queue.GetLength(), 1U);
+  queue.EnqueuePendingReference(ref2.Get());
+  ASSERT_TRUE(!queue.IsEmpty());
+  ASSERT_EQ(queue.GetLength(), 2U);
+  ASSERT_EQ(queue.DequeuePendingReference(), ref2.Get());
+  ASSERT_TRUE(!queue.IsEmpty());
+  ASSERT_EQ(queue.GetLength(), 1U);
+  ASSERT_EQ(queue.DequeuePendingReference(), ref1.Get());
+  ASSERT_EQ(queue.GetLength(), 0U);
+  ASSERT_TRUE(queue.IsEmpty());
+}
+
+TEST_F(ReferenceQueueTest, Dump) {
+  Thread* self = Thread::Current();
+  StackHandleScope<20> hs(self);
+  Mutex lock("Reference queue lock");
+  ReferenceQueue queue(&lock);
+  ScopedObjectAccess soa(self);
+  queue.Dump(LOG(INFO));
+  auto weak_ref_class = hs.NewHandle(
+      Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;",
+                                                      NullHandle<mirror::ClassLoader>()));
+  ASSERT_TRUE(weak_ref_class.Get() != nullptr);
+  auto finalizer_ref_class = hs.NewHandle(
+      Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/FinalizerReference;",
+                                                      NullHandle<mirror::ClassLoader>()));
+  ASSERT_TRUE(finalizer_ref_class.Get() != nullptr);
+  auto ref1(hs.NewHandle(weak_ref_class->AllocObject(self)->AsReference()));
+  ASSERT_TRUE(ref1.Get() != nullptr);
+  auto ref2(hs.NewHandle(finalizer_ref_class->AllocObject(self)->AsReference()));
+  ASSERT_TRUE(ref2.Get() != nullptr);
+  queue.EnqueuePendingReference(ref1.Get());
+  queue.Dump(LOG(INFO));
+  queue.EnqueuePendingReference(ref2.Get());
+  queue.Dump(LOG(INFO));
+}
+
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 04b09e9..9675ba6 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -57,7 +57,7 @@
                                  kGcRetentionPolicyAlwaysCollect),
       growth_end_(mem_map->End()),
       objects_allocated_(0), bytes_allocated_(0),
-      block_lock_("Block lock"),
+      block_lock_("Block lock", kBumpPointerSpaceBlockLock),
       main_block_size_(0),
       num_blocks_(0) {
 }
@@ -172,7 +172,8 @@
   // Walk all of the objects in the main block first.
   while (pos < main_end) {
     mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
-    if (obj->GetClass() == nullptr) {
+    // No read barrier because obj may not be a valid object.
+    if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr) {
       // There is a race condition where a thread has just allocated an object but not set the
       // class. We can't know the size of this object, so we don't visit it and exit the function
       // since there is guaranteed to be not other blocks.
@@ -192,7 +193,8 @@
     CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
     // We don't know how many objects are allocated in the current block. When we hit a null class
     // assume its the end. TODO: Have a thread update the header when it flushes the block?
-    while (obj < end_obj && obj->GetClass() != nullptr) {
+    // No read barrier because obj may not be a valid object.
+    while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
       callback(obj, arg);
       obj = GetNextObject(obj);
     }
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 850a006..847f575 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -31,10 +31,10 @@
 
 class AllocationInfo;
 
-enum LargeObjectSpaceType {
-  kLargeObjectSpaceTypeDisabled,
-  kLargeObjectSpaceTypeMap,
-  kLargeObjectSpaceTypeFreeList,
+enum class LargeObjectSpaceType {
+  kDisabled,
+  kMap,
+  kFreeList,
 };
 
 // Abstraction implemented by all large object spaces.
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 43a2c59..9bbbb3c 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -173,7 +173,8 @@
   // stored in between objects.
   // Remaining size is for the new alloc space.
   const size_t growth_limit = growth_limit_ - size;
-  const size_t capacity = Capacity() - size;
+  // Use mem map limit in case error for clear growth limit.
+  const size_t capacity = NonGrowthLimitCapacity() - size;
   VLOG(heap) << "Begin " << reinterpret_cast<const void*>(begin_) << "\n"
              << "End " << reinterpret_cast<const void*>(End()) << "\n"
              << "Size " << size << "\n"
@@ -247,6 +248,16 @@
   context->freed.bytes += space->FreeList(self, num_ptrs, ptrs);
 }
 
+void MallocSpace::ClampGrowthLimit() {
+  size_t new_capacity = Capacity();
+  CHECK_LE(new_capacity, NonGrowthLimitCapacity());
+  GetLiveBitmap()->SetHeapSize(new_capacity);
+  GetMarkBitmap()->SetHeapSize(new_capacity);
+  GetMemMap()->SetSize(new_capacity);
+  limit_ = Begin() + new_capacity;
+  CHECK(temp_bitmap_.get() == nullptr);
+}
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 2fbd5f0..06239e5 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -110,6 +110,10 @@
     return GetMemMap()->Size();
   }
 
+  // Change the non growth limit capacity by shrinking or expanding the map. Currently, only
+  // shrinking is supported.
+  void ClampGrowthLimit();
+
   void Dump(std::ostream& os) const;
 
   void SetGrowthLimit(size_t growth_limit);
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
new file mode 100644
index 0000000..a4ed718
--- /dev/null
+++ b/runtime/gc/space/region_space-inl.h
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_INL_H_
+#define ART_RUNTIME_GC_SPACE_REGION_SPACE_INL_H_
+
+#include "region_space.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+inline mirror::Object* RegionSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated,
+                                          size_t* usable_size) {
+  num_bytes = RoundUp(num_bytes, kAlignment);
+  return AllocNonvirtual<false>(num_bytes, bytes_allocated, usable_size);
+}
+
+inline mirror::Object* RegionSpace::AllocThreadUnsafe(Thread* self, size_t num_bytes,
+                                                      size_t* bytes_allocated,
+                                                      size_t* usable_size) {
+  Locks::mutator_lock_->AssertExclusiveHeld(self);
+  return Alloc(self, num_bytes, bytes_allocated, usable_size);
+}
+
+template<bool kForEvac>
+inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated,
+                                                    size_t* usable_size) {
+  DCHECK(IsAligned<kAlignment>(num_bytes));
+  mirror::Object* obj;
+  if (LIKELY(num_bytes <= kRegionSize)) {
+    // Non-large object.
+    if (!kForEvac) {
+      obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+    } else {
+      DCHECK(evac_region_ != nullptr);
+      obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+    }
+    if (LIKELY(obj != nullptr)) {
+      return obj;
+    }
+    MutexLock mu(Thread::Current(), region_lock_);
+    // Retry with current region since another thread may have updated it.
+    if (!kForEvac) {
+      obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+    } else {
+      obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size);
+    }
+    if (LIKELY(obj != nullptr)) {
+      return obj;
+    }
+    if (!kForEvac) {
+      // Retain sufficient free regions for full evacuation.
+      if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
+        return nullptr;
+      }
+      for (size_t i = 0; i < num_regions_; ++i) {
+        Region* r = &regions_[i];
+        if (r->IsFree()) {
+          r->Unfree(time_);
+          r->SetNewlyAllocated();
+          ++num_non_free_regions_;
+          obj = r->Alloc(num_bytes, bytes_allocated, usable_size);
+          CHECK(obj != nullptr);
+          current_region_ = r;
+          return obj;
+        }
+      }
+    } else {
+      for (size_t i = 0; i < num_regions_; ++i) {
+        Region* r = &regions_[i];
+        if (r->IsFree()) {
+          r->Unfree(time_);
+          ++num_non_free_regions_;
+          obj = r->Alloc(num_bytes, bytes_allocated, usable_size);
+          CHECK(obj != nullptr);
+          evac_region_ = r;
+          return obj;
+        }
+      }
+    }
+  } else {
+    // Large object.
+    obj = AllocLarge<kForEvac>(num_bytes, bytes_allocated, usable_size);
+    if (LIKELY(obj != nullptr)) {
+      return obj;
+    }
+  }
+  return nullptr;
+}
+
+inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* bytes_allocated,
+                                                  size_t* usable_size) {
+  DCHECK(IsAllocated() && IsInToSpace());
+  DCHECK(IsAligned<kAlignment>(num_bytes));
+  Atomic<uint8_t*>* atomic_top = reinterpret_cast<Atomic<uint8_t*>*>(&top_);
+  uint8_t* old_top;
+  uint8_t* new_top;
+  do {
+    old_top = atomic_top->LoadRelaxed();
+    new_top = old_top + num_bytes;
+    if (UNLIKELY(new_top > end_)) {
+      return nullptr;
+    }
+  } while (!atomic_top->CompareExchangeWeakSequentiallyConsistent(old_top, new_top));
+  reinterpret_cast<Atomic<uint64_t>*>(&objects_allocated_)->FetchAndAddSequentiallyConsistent(1);
+  DCHECK_LE(atomic_top->LoadRelaxed(), end_);
+  DCHECK_LT(old_top, end_);
+  DCHECK_LE(new_top, end_);
+  *bytes_allocated = num_bytes;
+  if (usable_size != nullptr) {
+    *usable_size = num_bytes;
+  }
+  return reinterpret_cast<mirror::Object*>(old_top);
+}
+
+inline size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  size_t num_bytes = obj->SizeOf();
+  if (usable_size != nullptr) {
+    if (LIKELY(num_bytes <= kRegionSize)) {
+      DCHECK(RefToRegion(obj)->IsAllocated());
+      *usable_size = RoundUp(num_bytes, kAlignment);
+    } else {
+      DCHECK(RefToRegion(obj)->IsLarge());
+      *usable_size = RoundUp(num_bytes, kRegionSize);
+    }
+  }
+  return num_bytes;
+}
+
+template<RegionSpace::RegionType kRegionType>
+uint64_t RegionSpace::GetBytesAllocatedInternal() {
+  uint64_t bytes = 0;
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsFree()) {
+      continue;
+    }
+    switch (kRegionType) {
+      case RegionType::kRegionTypeAll:
+        bytes += r->BytesAllocated();
+        break;
+      case RegionType::kRegionTypeFromSpace:
+        if (r->IsInFromSpace()) {
+          bytes += r->BytesAllocated();
+        }
+        break;
+      case RegionType::kRegionTypeUnevacFromSpace:
+        if (r->IsInUnevacFromSpace()) {
+          bytes += r->BytesAllocated();
+        }
+        break;
+      case RegionType::kRegionTypeToSpace:
+        if (r->IsInToSpace()) {
+          bytes += r->BytesAllocated();
+        }
+        break;
+      default:
+        LOG(FATAL) << "Unexpected space type : " << kRegionType;
+    }
+  }
+  return bytes;
+}
+
+template<RegionSpace::RegionType kRegionType>
+uint64_t RegionSpace::GetObjectsAllocatedInternal() {
+  uint64_t bytes = 0;
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsFree()) {
+      continue;
+    }
+    switch (kRegionType) {
+      case RegionType::kRegionTypeAll:
+        bytes += r->ObjectsAllocated();
+        break;
+      case RegionType::kRegionTypeFromSpace:
+        if (r->IsInFromSpace()) {
+          bytes += r->ObjectsAllocated();
+        }
+        break;
+      case RegionType::kRegionTypeUnevacFromSpace:
+        if (r->IsInUnevacFromSpace()) {
+          bytes += r->ObjectsAllocated();
+        }
+        break;
+      case RegionType::kRegionTypeToSpace:
+        if (r->IsInToSpace()) {
+          bytes += r->ObjectsAllocated();
+        }
+        break;
+      default:
+        LOG(FATAL) << "Unexpected space type : " << kRegionType;
+    }
+  }
+  return bytes;
+}
+
+template<bool kToSpaceOnly>
+void RegionSpace::WalkInternal(ObjectCallback* callback, void* arg) {
+  // TODO: MutexLock on region_lock_ won't work due to lock order
+  // issues (the classloader classes lock and the monitor lock). We
+  // call this with threads suspended.
+  Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsFree() || (kToSpaceOnly && !r->IsInToSpace())) {
+      continue;
+    }
+    if (r->IsLarge()) {
+      mirror::Object* obj = reinterpret_cast<mirror::Object*>(r->Begin());
+      if (obj->GetClass() != nullptr) {
+        callback(obj, arg);
+      }
+    } else if (r->IsLargeTail()) {
+      // Do nothing.
+    } else {
+      uint8_t* pos = r->Begin();
+      uint8_t* top = r->Top();
+      while (pos < top) {
+        mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+        if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+          callback(obj, arg);
+          pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
+        } else {
+          break;
+        }
+      }
+    }
+  }
+}
+
+inline mirror::Object* RegionSpace::GetNextObject(mirror::Object* obj) {
+  const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
+  return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
+}
+
+template<bool kForEvac>
+mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, size_t* bytes_allocated,
+                                        size_t* usable_size) {
+  DCHECK(IsAligned<kAlignment>(num_bytes));
+  DCHECK_GT(num_bytes, kRegionSize);
+  size_t num_regs = RoundUp(num_bytes, kRegionSize) / kRegionSize;
+  DCHECK_GT(num_regs, 0U);
+  DCHECK_LT((num_regs - 1) * kRegionSize, num_bytes);
+  DCHECK_LE(num_bytes, num_regs * kRegionSize);
+  MutexLock mu(Thread::Current(), region_lock_);
+  if (!kForEvac) {
+    // Retain sufficient free regions for full evacuation.
+    if ((num_non_free_regions_ + num_regs) * 2 > num_regions_) {
+      return nullptr;
+    }
+  }
+  // Find a large enough contiguous free regions.
+  size_t left = 0;
+  while (left + num_regs - 1 < num_regions_) {
+    bool found = true;
+    size_t right = left;
+    DCHECK_LT(right, left + num_regs)
+        << "The inner loop Should iterate at least once";
+    while (right < left + num_regs) {
+      if (regions_[right].IsFree()) {
+        ++right;
+      } else {
+        found = false;
+        break;
+      }
+    }
+    if (found) {
+      // right points to the one region past the last free region.
+      DCHECK_EQ(left + num_regs, right);
+      Region* first_reg = &regions_[left];
+      DCHECK(first_reg->IsFree());
+      first_reg->UnfreeLarge(time_);
+      ++num_non_free_regions_;
+      first_reg->SetTop(first_reg->Begin() + num_bytes);
+      for (size_t p = left + 1; p < right; ++p) {
+        DCHECK_LT(p, num_regions_);
+        DCHECK(regions_[p].IsFree());
+        regions_[p].UnfreeLargeTail(time_);
+        ++num_non_free_regions_;
+      }
+      *bytes_allocated = num_bytes;
+      if (usable_size != nullptr) {
+        *usable_size = num_regs * kRegionSize;
+      }
+      return reinterpret_cast<mirror::Object*>(first_reg->Begin());
+    } else {
+      // right points to the non-free region. Start with the one after it.
+      left = right + 1;
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SPACE_REGION_SPACE_INL_H_
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
new file mode 100644
index 0000000..2c556d9
--- /dev/null
+++ b/runtime/gc/space/region_space.cc
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+#include "bump_pointer_space.h"
+#include "bump_pointer_space-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/class-inl.h"
+#include "thread_list.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+// If a region has live objects whose size is less than this percent
+// value of the region size, evaculate the region.
+static constexpr uint kEvaculateLivePercentThreshold = 75U;
+
+RegionSpace* RegionSpace::Create(const std::string& name, size_t capacity,
+                                 uint8_t* requested_begin) {
+  capacity = RoundUp(capacity, kRegionSize);
+  std::string error_msg;
+  std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
+                                                       PROT_READ | PROT_WRITE, true, &error_msg));
+  if (mem_map.get() == nullptr) {
+    LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
+        << PrettySize(capacity) << " with message " << error_msg;
+    MemMap::DumpMaps(LOG(ERROR));
+    return nullptr;
+  }
+  return new RegionSpace(name, mem_map.release());
+}
+
+RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map)
+    : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(),
+                                 kGcRetentionPolicyAlwaysCollect),
+      region_lock_("Region lock", kRegionSpaceRegionLock), time_(1U) {
+  size_t mem_map_size = mem_map->Size();
+  CHECK_ALIGNED(mem_map_size, kRegionSize);
+  CHECK_ALIGNED(mem_map->Begin(), kRegionSize);
+  num_regions_ = mem_map_size / kRegionSize;
+  num_non_free_regions_ = 0U;
+  DCHECK_GT(num_regions_, 0U);
+  regions_.reset(new Region[num_regions_]);
+  uint8_t* region_addr = mem_map->Begin();
+  for (size_t i = 0; i < num_regions_; ++i, region_addr += kRegionSize) {
+    regions_[i] = Region(i, region_addr, region_addr + kRegionSize);
+  }
+  if (kIsDebugBuild) {
+    CHECK_EQ(regions_[0].Begin(), Begin());
+    for (size_t i = 0; i < num_regions_; ++i) {
+      CHECK(regions_[i].IsFree());
+      CHECK_EQ(static_cast<size_t>(regions_[i].End() - regions_[i].Begin()), kRegionSize);
+      if (i + 1 < num_regions_) {
+        CHECK_EQ(regions_[i].End(), regions_[i + 1].Begin());
+      }
+    }
+    CHECK_EQ(regions_[num_regions_ - 1].End(), Limit());
+  }
+  full_region_ = Region();
+  DCHECK(!full_region_.IsFree());
+  DCHECK(full_region_.IsAllocated());
+  current_region_ = &full_region_;
+  evac_region_ = nullptr;
+  size_t ignored;
+  DCHECK(full_region_.Alloc(kAlignment, &ignored, nullptr) == nullptr);
+}
+
+size_t RegionSpace::FromSpaceSize() {
+  uint64_t num_regions = 0;
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsInFromSpace()) {
+      ++num_regions;
+    }
+  }
+  return num_regions * kRegionSize;
+}
+
+size_t RegionSpace::UnevacFromSpaceSize() {
+  uint64_t num_regions = 0;
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsInUnevacFromSpace()) {
+      ++num_regions;
+    }
+  }
+  return num_regions * kRegionSize;
+}
+
+size_t RegionSpace::ToSpaceSize() {
+  uint64_t num_regions = 0;
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsInToSpace()) {
+      ++num_regions;
+    }
+  }
+  return num_regions * kRegionSize;
+}
+
+inline bool RegionSpace::Region::ShouldBeEvacuated() {
+  DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
+  // if the region was allocated after the start of the
+  // previous GC or the live ratio is below threshold, evacuate
+  // it.
+  bool result;
+  if (is_newly_allocated_) {
+    result = true;
+  } else {
+    bool is_live_percent_valid = live_bytes_ != static_cast<size_t>(-1);
+    if (is_live_percent_valid) {
+      uint live_percent = GetLivePercent();
+      if (IsAllocated()) {
+        // Side node: live_percent == 0 does not necessarily mean
+        // there's no live objects due to rounding (there may be a
+        // few).
+        result = live_percent < kEvaculateLivePercentThreshold;
+      } else {
+        DCHECK(IsLarge());
+        result = live_percent == 0U;
+      }
+    } else {
+      result = false;
+    }
+  }
+  return result;
+}
+
+// Determine which regions to evacuate and mark them as
+// from-space. Mark the rest as unevacuated from-space.
+void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all) {
+  ++time_;
+  if (kUseTableLookupReadBarrier) {
+    DCHECK(rb_table->IsAllCleared());
+    rb_table->SetAll();
+  }
+  MutexLock mu(Thread::Current(), region_lock_);
+  size_t num_expected_large_tails = 0;
+  bool prev_large_evacuated = false;
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    RegionState state = r->State();
+    RegionType type = r->Type();
+    if (!r->IsFree()) {
+      DCHECK(r->IsInToSpace());
+      if (LIKELY(num_expected_large_tails == 0U)) {
+        DCHECK((state == RegionState::kRegionStateAllocated ||
+                state == RegionState::kRegionStateLarge) &&
+               type == RegionType::kRegionTypeToSpace);
+        bool should_evacuate = force_evacuate_all || r->ShouldBeEvacuated();
+        if (should_evacuate) {
+          r->SetAsFromSpace();
+          DCHECK(r->IsInFromSpace());
+        } else {
+          r->SetAsUnevacFromSpace();
+          DCHECK(r->IsInUnevacFromSpace());
+        }
+        if (UNLIKELY(state == RegionState::kRegionStateLarge &&
+                     type == RegionType::kRegionTypeToSpace)) {
+          prev_large_evacuated = should_evacuate;
+          num_expected_large_tails = RoundUp(r->BytesAllocated(), kRegionSize) / kRegionSize - 1;
+          DCHECK_GT(num_expected_large_tails, 0U);
+        }
+      } else {
+        DCHECK(state == RegionState::kRegionStateLargeTail &&
+               type == RegionType::kRegionTypeToSpace);
+        if (prev_large_evacuated) {
+          r->SetAsFromSpace();
+          DCHECK(r->IsInFromSpace());
+        } else {
+          r->SetAsUnevacFromSpace();
+          DCHECK(r->IsInUnevacFromSpace());
+        }
+        --num_expected_large_tails;
+      }
+    } else {
+      DCHECK_EQ(num_expected_large_tails, 0U);
+      if (kUseTableLookupReadBarrier) {
+        // Clear the rb table for to-space regions.
+        rb_table->Clear(r->Begin(), r->End());
+      }
+    }
+  }
+  current_region_ = &full_region_;
+  evac_region_ = &full_region_;
+}
+
+void RegionSpace::ClearFromSpace() {
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsInFromSpace()) {
+      r->Clear();
+      --num_non_free_regions_;
+    } else if (r->IsInUnevacFromSpace()) {
+      r->SetUnevacFromSpaceAsToSpace();
+    }
+  }
+  evac_region_ = nullptr;
+}
+
+void RegionSpace::AssertAllRegionLiveBytesZeroOrCleared() {
+  if (kIsDebugBuild) {
+    MutexLock mu(Thread::Current(), region_lock_);
+    for (size_t i = 0; i < num_regions_; ++i) {
+      Region* r = &regions_[i];
+      size_t live_bytes = r->LiveBytes();
+      CHECK(live_bytes == 0U || live_bytes == static_cast<size_t>(-1)) << live_bytes;
+    }
+  }
+}
+
+void RegionSpace::LogFragmentationAllocFailure(std::ostream& os,
+                                               size_t /* failed_alloc_bytes */) {
+  size_t max_contiguous_allocation = 0;
+  MutexLock mu(Thread::Current(), region_lock_);
+  if (current_region_->End() - current_region_->Top() > 0) {
+    max_contiguous_allocation = current_region_->End() - current_region_->Top();
+  }
+  if (num_non_free_regions_ * 2 < num_regions_) {
+    // We reserve half of the regions for evaluation only. If we
+    // occupy more than half the regions, do not report the free
+    // regions as available.
+    size_t max_contiguous_free_regions = 0;
+    size_t num_contiguous_free_regions = 0;
+    bool prev_free_region = false;
+    for (size_t i = 0; i < num_regions_; ++i) {
+      Region* r = &regions_[i];
+      if (r->IsFree()) {
+        if (!prev_free_region) {
+          CHECK_EQ(num_contiguous_free_regions, 0U);
+          prev_free_region = true;
+        }
+        ++num_contiguous_free_regions;
+      } else {
+        if (prev_free_region) {
+          CHECK_NE(num_contiguous_free_regions, 0U);
+          max_contiguous_free_regions = std::max(max_contiguous_free_regions,
+                                                 num_contiguous_free_regions);
+          num_contiguous_free_regions = 0U;
+          prev_free_region = false;
+        }
+      }
+    }
+    max_contiguous_allocation = std::max(max_contiguous_allocation,
+                                         max_contiguous_free_regions * kRegionSize);
+  }
+  os << "; failed due to fragmentation (largest possible contiguous allocation "
+     <<  max_contiguous_allocation << " bytes)";
+  // Caller's job to print failed_alloc_bytes.
+}
+
+void RegionSpace::Clear() {
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (!r->IsFree()) {
+      --num_non_free_regions_;
+    }
+    r->Clear();
+  }
+  current_region_ = &full_region_;
+  evac_region_ = &full_region_;
+}
+
+void RegionSpace::Dump(std::ostream& os) const {
+  os << GetName() << " "
+      << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit());
+}
+
+void RegionSpace::FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) {
+  DCHECK(Contains(large_obj));
+  DCHECK(IsAligned<kRegionSize>(large_obj));
+  MutexLock mu(Thread::Current(), region_lock_);
+  uint8_t* begin_addr = reinterpret_cast<uint8_t*>(large_obj);
+  uint8_t* end_addr = AlignUp(reinterpret_cast<uint8_t*>(large_obj) + bytes_allocated, kRegionSize);
+  CHECK_LT(begin_addr, end_addr);
+  for (uint8_t* addr = begin_addr; addr < end_addr; addr += kRegionSize) {
+    Region* reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(addr));
+    if (addr == begin_addr) {
+      DCHECK(reg->IsLarge());
+    } else {
+      DCHECK(reg->IsLargeTail());
+    }
+    reg->Clear();
+    --num_non_free_regions_;
+  }
+  if (end_addr < Limit()) {
+    // If we aren't at the end of the space, check that the next region is not a large tail.
+    Region* following_reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(end_addr));
+    DCHECK(!following_reg->IsLargeTail());
+  }
+}
+
+void RegionSpace::DumpRegions(std::ostream& os) {
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    regions_[i].Dump(os);
+  }
+}
+
+void RegionSpace::DumpNonFreeRegions(std::ostream& os) {
+  MutexLock mu(Thread::Current(), region_lock_);
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* reg = &regions_[i];
+    if (!reg->IsFree()) {
+      reg->Dump(os);
+    }
+  }
+}
+
+void RegionSpace::RecordAlloc(mirror::Object* ref) {
+  CHECK(ref != nullptr);
+  Region* r = RefToRegion(ref);
+  reinterpret_cast<Atomic<uint64_t>*>(&r->objects_allocated_)->FetchAndAddSequentiallyConsistent(1);
+}
+
+bool RegionSpace::AllocNewTlab(Thread* self) {
+  MutexLock mu(self, region_lock_);
+  RevokeThreadLocalBuffersLocked(self);
+  // Retain sufficient free regions for full evacuation.
+  if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
+    return false;
+  }
+  for (size_t i = 0; i < num_regions_; ++i) {
+    Region* r = &regions_[i];
+    if (r->IsFree()) {
+      r->Unfree(time_);
+      ++num_non_free_regions_;
+      // TODO: this is buggy. Debug it.
+      // r->SetNewlyAllocated();
+      r->SetTop(r->End());
+      r->is_a_tlab_ = true;
+      r->thread_ = self;
+      self->SetTlab(r->Begin(), r->End());
+      return true;
+    }
+  }
+  return false;
+}
+
+void RegionSpace::RevokeThreadLocalBuffers(Thread* thread) {
+  MutexLock mu(Thread::Current(), region_lock_);
+  RevokeThreadLocalBuffersLocked(thread);
+}
+
+void RegionSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
+  uint8_t* tlab_start = thread->GetTlabStart();
+  DCHECK_EQ(thread->HasTlab(), tlab_start != nullptr);
+  if (tlab_start != nullptr) {
+    DCHECK(IsAligned<kRegionSize>(tlab_start));
+    Region* r = RefToRegionLocked(reinterpret_cast<mirror::Object*>(tlab_start));
+    DCHECK(r->IsAllocated());
+    DCHECK_EQ(thread->GetThreadLocalBytesAllocated(), kRegionSize);
+    r->RecordThreadLocalAllocations(thread->GetThreadLocalObjectsAllocated(),
+                                    thread->GetThreadLocalBytesAllocated());
+    r->is_a_tlab_ = false;
+    r->thread_ = nullptr;
+  }
+  thread->SetTlab(nullptr, nullptr);
+}
+
+void RegionSpace::RevokeAllThreadLocalBuffers() {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+  MutexLock mu2(self, *Locks::thread_list_lock_);
+  std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+  for (Thread* thread : thread_list) {
+    RevokeThreadLocalBuffers(thread);
+  }
+}
+
+void RegionSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+  if (kIsDebugBuild) {
+    DCHECK(!thread->HasTlab());
+  }
+}
+
+void RegionSpace::AssertAllThreadLocalBuffersAreRevoked() {
+  if (kIsDebugBuild) {
+    Thread* self = Thread::Current();
+    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+    MutexLock mu2(self, *Locks::thread_list_lock_);
+    std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+    for (Thread* thread : thread_list) {
+      AssertThreadLocalBuffersAreRevoked(thread);
+    }
+  }
+}
+
+void RegionSpace::Region::Dump(std::ostream& os) const {
+  os << "Region[" << idx_ << "]=" << reinterpret_cast<void*>(begin_) << "-" << reinterpret_cast<void*>(top_)
+     << "-" << reinterpret_cast<void*>(end_)
+     << " state=" << static_cast<uint>(state_) << " type=" << static_cast<uint>(type_)
+     << " objects_allocated=" << objects_allocated_
+     << " alloc_time=" << alloc_time_ << " live_bytes=" << live_bytes_
+     << " is_newly_allocated=" << is_newly_allocated_ << " is_a_tlab=" << is_a_tlab_ << " thread=" << thread_ << "\n";
+}
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
new file mode 100644
index 0000000..4160547
--- /dev/null
+++ b/runtime/gc/space/region_space.h
@@ -0,0 +1,527 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
+#define ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
+
+#include "gc/accounting/read_barrier_table.h"
+#include "object_callbacks.h"
+#include "space.h"
+#include "thread.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+// A space that consists of equal-sized regions.
+class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
+ public:
+  typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+
+  SpaceType GetType() const OVERRIDE {
+    return kSpaceTypeRegionSpace;
+  }
+
+  // Create a region space with the requested sizes. The requested base address is not
+  // guaranteed to be granted, if it is required, the caller should call Begin on the returned
+  // space to confirm the request was granted.
+  static RegionSpace* Create(const std::string& name, size_t capacity, uint8_t* requested_begin);
+
+  // Allocate num_bytes, returns nullptr if the space is full.
+  mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
+                        size_t* usable_size) OVERRIDE;
+  // Thread-unsafe allocation for when mutators are suspended, used by the semispace collector.
+  mirror::Object* AllocThreadUnsafe(Thread* self, size_t num_bytes, size_t* bytes_allocated,
+                                    size_t* usable_size)
+      OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  // The main allocation routine.
+  template<bool kForEvac>
+  ALWAYS_INLINE mirror::Object* AllocNonvirtual(size_t num_bytes, size_t* bytes_allocated,
+                                                size_t* usable_size);
+  // Allocate/free large objects (objects that are larger than the region size.)
+  template<bool kForEvac>
+  mirror::Object* AllocLarge(size_t num_bytes, size_t* bytes_allocated, size_t* usable_size);
+  void FreeLarge(mirror::Object* large_obj, size_t bytes_allocated);
+
+  // Return the storage space required by obj.
+  size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return AllocationSizeNonvirtual(obj, usable_size);
+  }
+  size_t AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  size_t Free(Thread*, mirror::Object*) OVERRIDE {
+    UNIMPLEMENTED(FATAL);
+    return 0;
+  }
+  size_t FreeList(Thread*, size_t, mirror::Object**) OVERRIDE {
+    UNIMPLEMENTED(FATAL);
+    return 0;
+  }
+  accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE {
+    // No live bitmap.
+    return nullptr;
+  }
+  accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE {
+    // No mark bitmap.
+    return nullptr;
+  }
+
+  void Clear() OVERRIDE LOCKS_EXCLUDED(region_lock_);
+
+  void Dump(std::ostream& os) const;
+  void DumpRegions(std::ostream& os);
+  void DumpNonFreeRegions(std::ostream& os);
+
+  void RevokeThreadLocalBuffers(Thread* thread) LOCKS_EXCLUDED(region_lock_);
+  void RevokeThreadLocalBuffersLocked(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(region_lock_);
+  void RevokeAllThreadLocalBuffers() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_,
+                                                    Locks::thread_list_lock_);
+  void AssertThreadLocalBuffersAreRevoked(Thread* thread) LOCKS_EXCLUDED(region_lock_);
+  void AssertAllThreadLocalBuffersAreRevoked() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_,
+                                                              Locks::thread_list_lock_);
+
+  enum class RegionType : uint8_t {
+    kRegionTypeAll,              // All types.
+    kRegionTypeFromSpace,        // From-space. To be evacuated.
+    kRegionTypeUnevacFromSpace,  // Unevacuated from-space. Not to be evacuated.
+    kRegionTypeToSpace,          // To-space.
+    kRegionTypeNone,             // None.
+  };
+
+  enum class RegionState : uint8_t {
+    kRegionStateFree,            // Free region.
+    kRegionStateAllocated,       // Allocated region.
+    kRegionStateLarge,           // Large allocated (allocation larger than the region size).
+    kRegionStateLargeTail,       // Large tail (non-first regions of a large allocation).
+  };
+
+  template<RegionType kRegionType> uint64_t GetBytesAllocatedInternal();
+  template<RegionType kRegionType> uint64_t GetObjectsAllocatedInternal();
+  uint64_t GetBytesAllocated() {
+    return GetBytesAllocatedInternal<RegionType::kRegionTypeAll>();
+  }
+  uint64_t GetObjectsAllocated() {
+    return GetObjectsAllocatedInternal<RegionType::kRegionTypeAll>();
+  }
+  uint64_t GetBytesAllocatedInFromSpace() {
+    return GetBytesAllocatedInternal<RegionType::kRegionTypeFromSpace>();
+  }
+  uint64_t GetObjectsAllocatedInFromSpace() {
+    return GetObjectsAllocatedInternal<RegionType::kRegionTypeFromSpace>();
+  }
+  uint64_t GetBytesAllocatedInUnevacFromSpace() {
+    return GetBytesAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
+  }
+  uint64_t GetObjectsAllocatedInUnevacFromSpace() {
+    return GetObjectsAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
+  }
+
+  bool CanMoveObjects() const OVERRIDE {
+    return true;
+  }
+
+  bool Contains(const mirror::Object* obj) const {
+    const uint8_t* byte_obj = reinterpret_cast<const uint8_t*>(obj);
+    return byte_obj >= Begin() && byte_obj < Limit();
+  }
+
+  RegionSpace* AsRegionSpace() OVERRIDE {
+    return this;
+  }
+
+  // Go through all of the blocks and visit the continuous objects.
+  void Walk(ObjectCallback* callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    WalkInternal<false>(callback, arg);
+  }
+
+  void WalkToSpace(ObjectCallback* callback, void* arg)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    WalkInternal<true>(callback, arg);
+  }
+
+  accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() OVERRIDE {
+    return nullptr;
+  }
+  void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Object alignment within the space.
+  static constexpr size_t kAlignment = kObjectAlignment;
+  // The region size.
+  static constexpr size_t kRegionSize = 1 * MB;
+
+  bool IsInFromSpace(mirror::Object* ref) {
+    if (HasAddress(ref)) {
+      Region* r = RefToRegionUnlocked(ref);
+      return r->IsInFromSpace();
+    }
+    return false;
+  }
+
+  bool IsInUnevacFromSpace(mirror::Object* ref) {
+    if (HasAddress(ref)) {
+      Region* r = RefToRegionUnlocked(ref);
+      return r->IsInUnevacFromSpace();
+    }
+    return false;
+  }
+
+  bool IsInToSpace(mirror::Object* ref) {
+    if (HasAddress(ref)) {
+      Region* r = RefToRegionUnlocked(ref);
+      return r->IsInToSpace();
+    }
+    return false;
+  }
+
+  RegionType GetRegionType(mirror::Object* ref) {
+    if (HasAddress(ref)) {
+      Region* r = RefToRegionUnlocked(ref);
+      return r->Type();
+    }
+    return RegionType::kRegionTypeNone;
+  }
+
+  void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all)
+      LOCKS_EXCLUDED(region_lock_);
+
+  size_t FromSpaceSize();
+  size_t UnevacFromSpaceSize();
+  size_t ToSpaceSize();
+  void ClearFromSpace();
+
+  void AddLiveBytes(mirror::Object* ref, size_t alloc_size) {
+    Region* reg = RefToRegionUnlocked(ref);
+    reg->AddLiveBytes(alloc_size);
+  }
+
+  void AssertAllRegionLiveBytesZeroOrCleared();
+
+  void RecordAlloc(mirror::Object* ref);
+  bool AllocNewTlab(Thread* self);
+
+  uint32_t Time() {
+    return time_;
+  }
+
+ private:
+  RegionSpace(const std::string& name, MemMap* mem_map);
+
+  template<bool kToSpaceOnly>
+  void WalkInternal(ObjectCallback* callback, void* arg) NO_THREAD_SAFETY_ANALYSIS;
+
+  class Region {
+   public:
+    Region()
+        : idx_(static_cast<size_t>(-1)),
+          begin_(nullptr), top_(nullptr), end_(nullptr),
+          state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace),
+          objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
+          is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {}
+
+    Region(size_t idx, uint8_t* begin, uint8_t* end)
+        : idx_(idx), begin_(begin), top_(begin), end_(end),
+          state_(RegionState::kRegionStateFree), type_(RegionType::kRegionTypeNone),
+          objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
+          is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {
+      DCHECK_LT(begin, end);
+      DCHECK_EQ(static_cast<size_t>(end - begin), kRegionSize);
+    }
+
+    RegionState State() const {
+      return state_;
+    }
+
+    RegionType Type() const {
+      return type_;
+    }
+
+    void Clear() {
+      top_ = begin_;
+      state_ = RegionState::kRegionStateFree;
+      type_ = RegionType::kRegionTypeNone;
+      objects_allocated_ = 0;
+      alloc_time_ = 0;
+      live_bytes_ = static_cast<size_t>(-1);
+      if (!kMadviseZeroes) {
+        memset(begin_, 0, end_ - begin_);
+      }
+      madvise(begin_, end_ - begin_, MADV_DONTNEED);
+      is_newly_allocated_ = false;
+      is_a_tlab_ = false;
+      thread_ = nullptr;
+    }
+
+    ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated,
+                                        size_t* usable_size);
+
+    bool IsFree() const {
+      bool is_free = state_ == RegionState::kRegionStateFree;
+      if (is_free) {
+        DCHECK(IsInNoSpace());
+        DCHECK_EQ(begin_, top_);
+        DCHECK_EQ(objects_allocated_, 0U);
+      }
+      return is_free;
+    }
+
+    // Given a free region, declare it non-free (allocated).
+    void Unfree(uint32_t alloc_time) {
+      DCHECK(IsFree());
+      state_ = RegionState::kRegionStateAllocated;
+      type_ = RegionType::kRegionTypeToSpace;
+      alloc_time_ = alloc_time;
+    }
+
+    void UnfreeLarge(uint32_t alloc_time) {
+      DCHECK(IsFree());
+      state_ = RegionState::kRegionStateLarge;
+      type_ = RegionType::kRegionTypeToSpace;
+      alloc_time_ = alloc_time;
+    }
+
+    void UnfreeLargeTail(uint32_t alloc_time) {
+      DCHECK(IsFree());
+      state_ = RegionState::kRegionStateLargeTail;
+      type_ = RegionType::kRegionTypeToSpace;
+      alloc_time_ = alloc_time;
+    }
+
+    void SetNewlyAllocated() {
+      is_newly_allocated_ = true;
+    }
+
+    // Non-large, non-large-tail allocated.
+    bool IsAllocated() const {
+      return state_ == RegionState::kRegionStateAllocated;
+    }
+
+    // Large allocated.
+    bool IsLarge() const {
+      bool is_large = state_ == RegionState::kRegionStateLarge;
+      if (is_large) {
+        DCHECK_LT(begin_ + 1 * MB, top_);
+      }
+      return is_large;
+    }
+
+    // Large-tail allocated.
+    bool IsLargeTail() const {
+      bool is_large_tail = state_ == RegionState::kRegionStateLargeTail;
+      if (is_large_tail) {
+        DCHECK_EQ(begin_, top_);
+      }
+      return is_large_tail;
+    }
+
+    size_t Idx() const {
+      return idx_;
+    }
+
+    bool IsInFromSpace() const {
+      return type_ == RegionType::kRegionTypeFromSpace;
+    }
+
+    bool IsInToSpace() const {
+      return type_ == RegionType::kRegionTypeToSpace;
+    }
+
+    bool IsInUnevacFromSpace() const {
+      return type_ == RegionType::kRegionTypeUnevacFromSpace;
+    }
+
+    bool IsInNoSpace() const {
+      return type_ == RegionType::kRegionTypeNone;
+    }
+
+    void SetAsFromSpace() {
+      DCHECK(!IsFree() && IsInToSpace());
+      type_ = RegionType::kRegionTypeFromSpace;
+      live_bytes_ = static_cast<size_t>(-1);
+    }
+
+    void SetAsUnevacFromSpace() {
+      DCHECK(!IsFree() && IsInToSpace());
+      type_ = RegionType::kRegionTypeUnevacFromSpace;
+      live_bytes_ = 0U;
+    }
+
+    void SetUnevacFromSpaceAsToSpace() {
+      DCHECK(!IsFree() && IsInUnevacFromSpace());
+      type_ = RegionType::kRegionTypeToSpace;
+    }
+
+    ALWAYS_INLINE bool ShouldBeEvacuated();
+
+    void AddLiveBytes(size_t live_bytes) {
+      DCHECK(IsInUnevacFromSpace());
+      DCHECK(!IsLargeTail());
+      DCHECK_NE(live_bytes_, static_cast<size_t>(-1));
+      live_bytes_ += live_bytes;
+      DCHECK_LE(live_bytes_, BytesAllocated());
+    }
+
+    size_t LiveBytes() const {
+      return live_bytes_;
+    }
+
+    uint GetLivePercent() const {
+      DCHECK(IsInToSpace());
+      DCHECK(!IsLargeTail());
+      DCHECK_NE(live_bytes_, static_cast<size_t>(-1));
+      DCHECK_LE(live_bytes_, BytesAllocated());
+      size_t bytes_allocated = RoundUp(BytesAllocated(), kRegionSize);
+      DCHECK_GE(bytes_allocated, 0U);
+      uint result = (live_bytes_ * 100U) / bytes_allocated;
+      DCHECK_LE(result, 100U);
+      return result;
+    }
+
+    size_t BytesAllocated() const {
+      if (IsLarge()) {
+        DCHECK_LT(begin_ + kRegionSize, top_);
+        return static_cast<size_t>(top_ - begin_);
+      } else if (IsLargeTail()) {
+        DCHECK_EQ(begin_, top_);
+        return 0;
+      } else {
+        DCHECK(IsAllocated()) << static_cast<uint>(state_);
+        DCHECK_LE(begin_, top_);
+        size_t bytes = static_cast<size_t>(top_ - begin_);
+        DCHECK_LE(bytes, kRegionSize);
+        return bytes;
+      }
+    }
+
+    size_t ObjectsAllocated() const {
+      if (IsLarge()) {
+        DCHECK_LT(begin_ + 1 * MB, top_);
+        DCHECK_EQ(objects_allocated_, 0U);
+        return 1;
+      } else if (IsLargeTail()) {
+        DCHECK_EQ(begin_, top_);
+        DCHECK_EQ(objects_allocated_, 0U);
+        return 0;
+      } else {
+        DCHECK(IsAllocated()) << static_cast<uint>(state_);
+        return objects_allocated_;
+      }
+    }
+
+    uint8_t* Begin() const {
+      return begin_;
+    }
+
+    uint8_t* Top() const {
+      return top_;
+    }
+
+    void SetTop(uint8_t* new_top) {
+      top_ = new_top;
+    }
+
+    uint8_t* End() const {
+      return end_;
+    }
+
+    bool Contains(mirror::Object* ref) const {
+      return begin_ <= reinterpret_cast<uint8_t*>(ref) && reinterpret_cast<uint8_t*>(ref) < end_;
+    }
+
+    void Dump(std::ostream& os) const;
+
+    void RecordThreadLocalAllocations(size_t num_objects, size_t num_bytes) {
+      DCHECK(IsAllocated());
+      DCHECK_EQ(objects_allocated_, 0U);
+      DCHECK_EQ(top_, end_);
+      objects_allocated_ = num_objects;
+      top_ = begin_ + num_bytes;
+      DCHECK_EQ(top_, end_);
+    }
+
+   private:
+    size_t idx_;                   // The region's index in the region space.
+    uint8_t* begin_;               // The begin address of the region.
+    // Can't use Atomic<uint8_t*> as Atomic's copy operator is implicitly deleted.
+    uint8_t* top_;                 // The current position of the allocation.
+    uint8_t* end_;                 // The end address of the region.
+    RegionState state_;            // The region state (see RegionState).
+    RegionType type_;              // The region type (see RegionType).
+    uint64_t objects_allocated_;   // The number of objects allocated.
+    uint32_t alloc_time_;          // The allocation time of the region.
+    size_t live_bytes_;            // The live bytes. Used to compute the live percent.
+    bool is_newly_allocated_;      // True if it's allocated after the last collection.
+    bool is_a_tlab_;               // True if it's a tlab.
+    Thread* thread_;               // The owning thread if it's a tlab.
+
+    friend class RegionSpace;
+  };
+
+  Region* RefToRegion(mirror::Object* ref) LOCKS_EXCLUDED(region_lock_) {
+    MutexLock mu(Thread::Current(), region_lock_);
+    return RefToRegionLocked(ref);
+  }
+
+  Region* RefToRegionUnlocked(mirror::Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+    // For a performance reason (this is frequently called via
+    // IsInFromSpace() etc.) we avoid taking a lock here. Note that
+    // since we only change a region from to-space to from-space only
+    // during a pause (SetFromSpace()) and from from-space to free
+    // (after GC is done) as long as ref is a valid reference into an
+    // allocated region, it's safe to access the region state without
+    // the lock.
+    return RefToRegionLocked(ref);
+  }
+
+  Region* RefToRegionLocked(mirror::Object* ref) EXCLUSIVE_LOCKS_REQUIRED(region_lock_) {
+    DCHECK(HasAddress(ref));
+    uintptr_t offset = reinterpret_cast<uintptr_t>(ref) - reinterpret_cast<uintptr_t>(Begin());
+    size_t reg_idx = offset / kRegionSize;
+    DCHECK_LT(reg_idx, num_regions_);
+    Region* reg = &regions_[reg_idx];
+    DCHECK_EQ(reg->Idx(), reg_idx);
+    DCHECK(reg->Contains(ref));
+    return reg;
+  }
+
+  mirror::Object* GetNextObject(mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+
+  uint32_t time_;                  // The time as the number of collections since the startup.
+  size_t num_regions_;             // The number of regions in this space.
+  size_t num_non_free_regions_;    // The number of non-free regions in this space.
+  std::unique_ptr<Region[]> regions_ GUARDED_BY(region_lock_);
+                                   // The pointer to the region array.
+  Region* current_region_;         // The region that's being allocated currently.
+  Region* evac_region_;            // The region that's being evacuated to currently.
+  Region full_region_;             // The dummy/sentinel region that looks full.
+
+  DISALLOW_COPY_AND_ASSIGN(RegionSpace);
+};
+
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionState& value);
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionType& value);
+
+}  // namespace space
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 74d1a2b..ced25a4 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -365,8 +365,9 @@
   mark_bitmap_->Clear();
   SetEnd(begin_ + starting_size_);
   delete rosalloc_;
-  rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_, Capacity(),
-                             low_memory_mode_, Runtime::Current()->RunningOnValgrind());
+  rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_,
+                             NonGrowthLimitCapacity(), low_memory_mode_,
+                             Runtime::Current()->RunningOnValgrind());
   SetFootprintLimit(footprint_limit);
 }
 
diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc
index 486d79a..a2e2c1c 100644
--- a/runtime/gc/space/space.cc
+++ b/runtime/gc/space/space.cc
@@ -58,6 +58,11 @@
   UNREACHABLE();
 }
 
+RegionSpace* Space::AsRegionSpace() {
+  LOG(FATAL) << "Unreachable";
+  return nullptr;
+}
+
 AllocSpace* Space::AsAllocSpace() {
   UNIMPLEMENTED(FATAL) << "Unreachable";
   UNREACHABLE();
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 860a4c9..d24650b 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -50,6 +50,7 @@
 class RosAllocSpace;
 class ImageSpace;
 class LargeObjectSpace;
+class RegionSpace;
 class ZygoteSpace;
 
 static constexpr bool kDebugSpaces = kIsDebugBuild;
@@ -72,6 +73,7 @@
   kSpaceTypeZygoteSpace,
   kSpaceTypeBumpPointerSpace,
   kSpaceTypeLargeObjectSpace,
+  kSpaceTypeRegionSpace,
 };
 std::ostream& operator<<(std::ostream& os, const SpaceType& space_type);
 
@@ -132,6 +134,11 @@
   }
   virtual BumpPointerSpace* AsBumpPointerSpace();
 
+  bool IsRegionSpace() const {
+    return GetType() == kSpaceTypeRegionSpace;
+  }
+  virtual RegionSpace* AsRegionSpace();
+
   // Does this space hold large objects and implement the large object space abstraction?
   bool IsLargeObjectSpace() const {
     return GetType() == kSpaceTypeLargeObjectSpace;
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 9f39b80..09d10dd 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -127,6 +127,9 @@
 }
 
 void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
+  // This will lead to error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   {
     // Init < max == growth
     std::unique_ptr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, nullptr));
diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc
new file mode 100644
index 0000000..1a3c6f5
--- /dev/null
+++ b/runtime/gc/task_processor.cc
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#include "task_processor.h"
+
+#include "scoped_thread_state_change.h"
+
+namespace art {
+namespace gc {
+
+TaskProcessor::TaskProcessor()
+    : lock_(new Mutex("Task processor lock", kReferenceProcessorLock)), is_running_(false) {
+  // Piggyback off the reference processor lock level.
+  cond_.reset(new ConditionVariable("Task processor condition", *lock_));
+}
+
+TaskProcessor::~TaskProcessor() {
+  delete lock_;
+}
+
+void TaskProcessor::AddTask(Thread* self, HeapTask* task) {
+  ScopedThreadStateChange tsc(self, kBlocked);
+  MutexLock mu(self, *lock_);
+  tasks_.insert(task);
+  cond_->Signal(self);
+}
+
+HeapTask* TaskProcessor::GetTask(Thread* self) {
+  ScopedThreadStateChange tsc(self, kBlocked);
+  MutexLock mu(self, *lock_);
+  while (true) {
+    if (tasks_.empty()) {
+      if (!is_running_) {
+        return nullptr;
+      }
+      cond_->Wait(self);  // Empty queue, wait until we are signalled.
+    } else {
+      // Non empty queue, look at the top element and see if we are ready to run it.
+      const uint64_t current_time = NanoTime();
+      HeapTask* task = *tasks_.begin();
+      // If we are shutting down, return the task right away without waiting. Otherwise return the
+      // task if it is late enough.
+      uint64_t target_time = task->GetTargetRunTime();
+      if (!is_running_ || target_time <= current_time) {
+        tasks_.erase(tasks_.begin());
+        return task;
+      }
+      DCHECK_GT(target_time, current_time);
+      // Wait untl we hit the target run time.
+      const uint64_t delta_time = target_time - current_time;
+      const uint64_t ms_delta = NsToMs(delta_time);
+      const uint64_t ns_delta = delta_time - MsToNs(ms_delta);
+      cond_->TimedWait(self, static_cast<int64_t>(ms_delta), static_cast<int32_t>(ns_delta));
+    }
+  }
+  UNREACHABLE();
+  return nullptr;
+}
+
+void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t new_target_time) {
+  MutexLock mu(self, *lock_);
+  // Find the task.
+  auto range = tasks_.equal_range(task);
+  for (auto it = range.first; it != range.second; ++it) {
+    if (*it == task) {
+      // Check if the target time was updated, if so re-insert then wait.
+      if (new_target_time != task->GetTargetRunTime()) {
+        tasks_.erase(it);
+        task->SetTargetRunTime(new_target_time);
+        tasks_.insert(task);
+        // If we became the first task then we may need to signal since we changed the task that we
+        // are sleeping on.
+        if (*tasks_.begin() == task) {
+          cond_->Signal(self);
+        }
+        return;
+      }
+    }
+  }
+}
+
+bool TaskProcessor::IsRunning() const {
+  MutexLock mu(Thread::Current(), *lock_);
+  return is_running_;
+}
+
+void TaskProcessor::Stop(Thread* self) {
+  MutexLock mu(self, *lock_);
+  is_running_ = false;
+  cond_->Broadcast(self);
+}
+
+void TaskProcessor::Start(Thread* self) {
+  MutexLock mu(self, *lock_);
+  is_running_ = true;
+}
+
+void TaskProcessor::RunAllTasks(Thread* self) {
+  while (true) {
+    // Wait and get a task, may be interrupted.
+    HeapTask* task = GetTask(self);
+    if (task != nullptr) {
+      task->Run(self);
+      task->Finalize();
+    } else if (!IsRunning()) {
+      break;
+    }
+  }
+}
+
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h
new file mode 100644
index 0000000..765f035
--- /dev/null
+++ b/runtime/gc/task_processor.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_GC_TASK_PROCESSOR_H_
+#define ART_RUNTIME_GC_TASK_PROCESSOR_H_
+
+#include <memory>
+#include <set>
+
+#include "base/mutex.h"
+#include "globals.h"
+#include "thread_pool.h"
+
+namespace art {
+namespace gc {
+
+class HeapTask : public SelfDeletingTask {
+ public:
+  explicit HeapTask(uint64_t target_run_time) : target_run_time_(target_run_time) {
+  }
+  uint64_t GetTargetRunTime() const {
+    return target_run_time_;
+  }
+
+ private:
+  // Update the updated_target_run_time_, the task processor will re-insert the task when it is
+  // popped and update the target_run_time_.
+  void SetTargetRunTime(uint64_t new_target_run_time) {
+    target_run_time_ = new_target_run_time;
+  }
+
+  // Time in ns at which we want the task to run.
+  uint64_t target_run_time_;
+
+  friend class TaskProcessor;
+};
+
+// Used to process GC tasks (heap trim, heap transitions, concurrent GC).
+class TaskProcessor {
+ public:
+  TaskProcessor();
+  virtual ~TaskProcessor();
+  void AddTask(Thread* self, HeapTask* task) LOCKS_EXCLUDED(lock_);
+  HeapTask* GetTask(Thread* self) LOCKS_EXCLUDED(lock_);
+  void Start(Thread* self) LOCKS_EXCLUDED(lock_);
+  // Stop tells the RunAllTasks to finish up the remaining tasks as soon as
+  // possible then return.
+  void Stop(Thread* self) LOCKS_EXCLUDED(lock_);
+  void RunAllTasks(Thread* self) LOCKS_EXCLUDED(lock_);
+  bool IsRunning() const LOCKS_EXCLUDED(lock_);
+  void UpdateTargetRunTime(Thread* self, HeapTask* target_time, uint64_t new_target_time)
+      LOCKS_EXCLUDED(lock_);
+
+ private:
+  class CompareByTargetRunTime {
+   public:
+    bool operator()(const HeapTask* a, const HeapTask* b) const {
+      return a->GetTargetRunTime() < b->GetTargetRunTime();
+    }
+  };
+
+  mutable Mutex* lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  bool is_running_ GUARDED_BY(lock_);
+  std::unique_ptr<ConditionVariable> cond_ GUARDED_BY(lock_);
+  std::multiset<HeapTask*, CompareByTargetRunTime> tasks_ GUARDED_BY(lock_);
+};
+
+}  // namespace gc
+}  // namespace art
+
+#endif  // ART_RUNTIME_GC_TASK_PROCESSOR_H_
diff --git a/runtime/gc/task_processor_test.cc b/runtime/gc/task_processor_test.cc
new file mode 100644
index 0000000..5dd6d8f
--- /dev/null
+++ b/runtime/gc/task_processor_test.cc
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+#include "common_runtime_test.h"
+#include "task_processor.h"
+#include "thread_pool.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+namespace gc {
+
+class TaskProcessorTest : public CommonRuntimeTest {
+ public:
+};
+
+class RecursiveTask : public HeapTask {
+ public:
+  RecursiveTask(TaskProcessor* task_processor, Atomic<size_t>* counter, size_t max_recursion)
+     : HeapTask(NanoTime() + MsToNs(10)), task_processor_(task_processor), counter_(counter),
+       max_recursion_(max_recursion) {
+  }
+  virtual void Run(Thread* self) OVERRIDE {
+    if (max_recursion_ > 0) {
+      task_processor_->AddTask(self,
+                               new RecursiveTask(task_processor_, counter_, max_recursion_ - 1));
+      counter_->FetchAndAddSequentiallyConsistent(1U);
+    }
+  }
+
+ private:
+  TaskProcessor* const task_processor_;
+  Atomic<size_t>* const counter_;
+  const size_t max_recursion_;
+};
+
+class WorkUntilDoneTask : public SelfDeletingTask {
+ public:
+  WorkUntilDoneTask(TaskProcessor* task_processor, Atomic<bool>* done_running)
+      : task_processor_(task_processor), done_running_(done_running) {
+  }
+  virtual void Run(Thread* self) OVERRIDE {
+    task_processor_->RunAllTasks(self);
+    done_running_->StoreSequentiallyConsistent(true);
+  }
+
+ private:
+  TaskProcessor* const task_processor_;
+  Atomic<bool>* done_running_;
+};
+
+TEST_F(TaskProcessorTest, Interrupt) {
+  ThreadPool thread_pool("task processor test", 1U);
+  Thread* const self = Thread::Current();
+  TaskProcessor task_processor;
+  static constexpr size_t kRecursion = 10;
+  Atomic<bool> done_running(false);
+  Atomic<size_t> counter(0);
+  task_processor.AddTask(self, new RecursiveTask(&task_processor, &counter, kRecursion));
+  task_processor.Start(self);
+  // Add a task which will wait until interrupted to the thread pool.
+  thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running));
+  thread_pool.StartWorkers(self);
+  ASSERT_FALSE(done_running);
+  // Wait until all the tasks are done, but since we didn't interrupt, done_running should be 0.
+  while (counter.LoadSequentiallyConsistent() != kRecursion) {
+    usleep(10);
+  }
+  ASSERT_FALSE(done_running);
+  task_processor.Stop(self);
+  thread_pool.Wait(self, true, false);
+  // After the interrupt and wait, the WorkUntilInterruptedTasktask should have terminated and
+  // set done_running_ to true.
+  ASSERT_TRUE(done_running.LoadSequentiallyConsistent());
+
+  // Test that we finish remaining tasks before returning from RunTasksUntilInterrupted.
+  counter.StoreSequentiallyConsistent(0);
+  done_running.StoreSequentiallyConsistent(false);
+  // Self interrupt before any of the other tasks run, but since we added them we should keep on
+  // working until all the tasks are completed.
+  task_processor.Stop(self);
+  task_processor.AddTask(self, new RecursiveTask(&task_processor, &counter, kRecursion));
+  thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running));
+  thread_pool.StartWorkers(self);
+  thread_pool.Wait(self, true, false);
+  ASSERT_TRUE(done_running.LoadSequentiallyConsistent());
+  ASSERT_EQ(counter.LoadSequentiallyConsistent(), kRecursion);
+}
+
+class TestOrderTask : public HeapTask {
+ public:
+  explicit TestOrderTask(uint64_t expected_time, size_t expected_counter, size_t* counter)
+     : HeapTask(expected_time), expected_counter_(expected_counter), counter_(counter) {
+  }
+  virtual void Run(Thread* thread) OVERRIDE {
+    UNUSED(thread);  // Fix cppling bug.
+    ASSERT_EQ(*counter_, expected_counter_);
+    ++*counter_;
+  }
+
+ private:
+  const size_t expected_counter_;
+  size_t* const counter_;
+};
+
+TEST_F(TaskProcessorTest, Ordering) {
+  static const size_t kNumTasks = 25;
+  const uint64_t current_time = NanoTime();
+  Thread* const self = Thread::Current();
+  TaskProcessor task_processor;
+  task_processor.Stop(self);
+  size_t counter = 0;
+  std::vector<std::pair<uint64_t, size_t>> orderings;
+  for (size_t i = 0; i < kNumTasks; ++i) {
+    orderings.push_back(std::make_pair(current_time + MsToNs(10U * i), i));
+  }
+  for (size_t i = 0; i < kNumTasks; ++i) {
+    std::swap(orderings[i], orderings[(i * 87654231 + 12345) % orderings.size()]);
+  }
+  for (const auto& pair : orderings) {
+    auto* task = new TestOrderTask(pair.first, pair.second, &counter);
+    task_processor.AddTask(self, task);
+  }
+  ThreadPool thread_pool("task processor test", 1U);
+  Atomic<bool> done_running(false);
+  // Add a task which will wait until interrupted to the thread pool.
+  thread_pool.AddTask(self, new WorkUntilDoneTask(&task_processor, &done_running));
+  ASSERT_FALSE(done_running.LoadSequentiallyConsistent());
+  thread_pool.StartWorkers(self);
+  thread_pool.Wait(self, true, false);
+  ASSERT_TRUE(done_running.LoadSequentiallyConsistent());
+  ASSERT_EQ(counter, kNumTasks);
+}
+
+}  // namespace gc
+}  // namespace art
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index aee5586..7e0be64 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -19,22 +19,75 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"       // For Locks::mutator_lock_.
-#include "object_callbacks.h"
 
 namespace art {
 
+namespace mirror {
+class Object;
+}  // namespace mirror
+
+enum RootType {
+  kRootUnknown = 0,
+  kRootJNIGlobal,
+  kRootJNILocal,
+  kRootJavaFrame,
+  kRootNativeStack,
+  kRootStickyClass,
+  kRootThreadBlock,
+  kRootMonitorUsed,
+  kRootThreadObject,
+  kRootInternedString,
+  kRootDebugger,
+  kRootVMInternal,
+  kRootJNIMonitor,
+};
+std::ostream& operator<<(std::ostream& os, const RootType& root_type);
+
+class RootInfo {
+ public:
+  // Thread id 0 is for non thread roots.
+  explicit RootInfo(RootType type, uint32_t thread_id = 0)
+     : type_(type), thread_id_(thread_id) {
+  }
+  virtual ~RootInfo() {
+  }
+  RootType GetType() const {
+    return type_;
+  }
+  uint32_t GetThreadId() const {
+    return thread_id_;
+  }
+  virtual void Describe(std::ostream& os) const {
+    os << "Type=" << type_ << " thread_id=" << thread_id_;
+  }
+
+ private:
+  const RootType type_;
+  const uint32_t thread_id_;
+};
+
+// Returns the new address of the object, returns root if it has not moved. tid and root_type are
+// only used by hprof.
+typedef void (RootCallback)(mirror::Object** root, void* arg, const RootInfo& root_info);
+
 template<class MirrorType>
 class PACKED(4) GcRoot {
  public:
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) const {
+  void VisitRoot(RootCallback* callback, void* arg, const RootInfo& info) const {
     DCHECK(!IsNull());
-    callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&root_), arg, info);
     DCHECK(!IsNull());
   }
 
+  void VisitRootIfNonNull(RootCallback* callback, void* arg, const RootInfo& info) const {
+    if (!IsNull()) {
+      VisitRoot(callback, arg, info);
+    }
+  }
+
   // This is only used by IrtIterator.
   ALWAYS_INLINE MirrorType** AddressWithoutBarrier() {
     return &root_;
diff --git a/runtime/globals.h b/runtime/globals.h
index 3104229..0845475 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -58,12 +58,6 @@
 static constexpr bool kIsTargetBuild = false;
 #endif
 
-#if defined(ART_USE_PORTABLE_COMPILER)
-static constexpr bool kUsePortableCompiler = true;
-#else
-static constexpr bool kUsePortableCompiler = false;
-#endif
-
 #if defined(ART_USE_OPTIMIZING_COMPILER)
 static constexpr bool kUseOptimizingCompiler = true;
 #else
@@ -71,7 +65,7 @@
 #endif
 
 // Garbage collector constants.
-static constexpr bool kMovingCollector = true && !kUsePortableCompiler;
+static constexpr bool kMovingCollector = true;
 static constexpr bool kMarkCompactSupport = false && kMovingCollector;
 // True if we allow moving field arrays, this can cause complication with mark compact.
 static constexpr bool kMoveFieldArrays = !kMarkCompactSupport;
@@ -98,22 +92,34 @@
 static constexpr bool kUseBrooksReadBarrier = false;
 #endif
 
+#ifdef USE_TABLE_LOOKUP_READ_BARRIER
+static constexpr bool kUseTableLookupReadBarrier = true;
+#else
+static constexpr bool kUseTableLookupReadBarrier = false;
+#endif
+
 static constexpr bool kUseBakerOrBrooksReadBarrier = kUseBakerReadBarrier || kUseBrooksReadBarrier;
+static constexpr bool kUseReadBarrier = kUseBakerReadBarrier || kUseBrooksReadBarrier ||
+    kUseTableLookupReadBarrier;
 
 // If true, references within the heap are poisoned (negated).
+#ifdef ART_HEAP_POISONING
+static constexpr bool kPoisonHeapReferences = true;
+#else
 static constexpr bool kPoisonHeapReferences = false;
+#endif
 
 // Kinds of tracing clocks.
-enum TraceClockSource {
-  kTraceClockSourceThreadCpu,
-  kTraceClockSourceWall,
-  kTraceClockSourceDual,  // Both wall and thread CPU clocks.
+enum class TraceClockSource {
+  kThreadCpu,
+  kWall,
+  kDual,  // Both wall and thread CPU clocks.
 };
 
-#if defined(HAVE_POSIX_CLOCKS)
-static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceDual;
+#if defined(__linux__)
+static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kDual;
 #else
-static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceWall;
+static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kWall;
 #endif
 
 static constexpr bool kDefaultMustRelocate = true;
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index 9ddaf61..222083b 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -21,12 +21,14 @@
 
 #include "handle.h"
 #include "thread.h"
+#include "verify_object-inl.h"
 
 namespace art {
 
 template<size_t kNumReferences>
 inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
     : HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) {
+  DCHECK_EQ(self, Thread::Current());
   static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference");
   // TODO: Figure out how to use a compile assert.
   CHECK_EQ(&storage_[0], GetReferences());
@@ -42,6 +44,71 @@
   DCHECK_EQ(top_handle_scope, this);
 }
 
+inline size_t HandleScope::SizeOf(uint32_t num_references) {
+  size_t header_size = sizeof(HandleScope);
+  size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
+  return header_size + data_size;
+}
+
+inline size_t HandleScope::SizeOf(size_t pointer_size, uint32_t num_references) {
+  // Assume that the layout is packed.
+  size_t header_size = pointer_size + sizeof(number_of_references_);
+  size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
+  return header_size + data_size;
+}
+
+inline mirror::Object* HandleScope::GetReference(size_t i) const {
+  DCHECK_LT(i, number_of_references_);
+  return GetReferences()[i].AsMirrorPtr();
+}
+
+inline Handle<mirror::Object> HandleScope::GetHandle(size_t i) {
+  DCHECK_LT(i, number_of_references_);
+  return Handle<mirror::Object>(&GetReferences()[i]);
+}
+
+inline MutableHandle<mirror::Object> HandleScope::GetMutableHandle(size_t i) {
+  DCHECK_LT(i, number_of_references_);
+  return MutableHandle<mirror::Object>(&GetReferences()[i]);
+}
+
+inline void HandleScope::SetReference(size_t i, mirror::Object* object) {
+  DCHECK_LT(i, number_of_references_);
+  GetReferences()[i].Assign(object);
+}
+
+inline bool HandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) const {
+  // A HandleScope should always contain something. One created by the
+  // jni_compiler should have a jobject/jclass as a native method is
+  // passed in a this pointer or a class
+  DCHECK_GT(number_of_references_, 0U);
+  return &GetReferences()[0] <= handle_scope_entry &&
+      handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
+}
+
+template<size_t kNumReferences> template<class T>
+inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) {
+  SetReference(pos_, object);
+  MutableHandle<T> h(GetHandle<T>(pos_));
+  pos_++;
+  return h;
+}
+
+template<size_t kNumReferences> template<class T>
+inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
+  SetReference(pos_, *object);
+  MutableHandle<T> h(GetHandle<T>(pos_));
+  pos_++;
+  return HandleWrapper<T>(object, h);
+}
+
+template<size_t kNumReferences>
+inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
+  DCHECK_LT(i, kNumReferences);
+  VerifyObject(object);
+  GetReferences()[i].Assign(object);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_HANDLE_SCOPE_INL_H_
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 2c4f0f9..782bbea 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -22,6 +22,7 @@
 #include "handle.h"
 #include "stack.h"
 #include "utils.h"
+#include "verify_object.h"
 
 namespace art {
 namespace mirror {
@@ -47,19 +48,10 @@
   // takes the pointer size explicitly so that at compile time we can cross-compile correctly.
 
   // Returns the size of a HandleScope containing num_references handles.
-  static size_t SizeOf(uint32_t num_references) {
-    size_t header_size = sizeof(HandleScope);
-    size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
-    return header_size + data_size;
-  }
+  static size_t SizeOf(uint32_t num_references);
 
   // Returns the size of a HandleScope containing num_references handles.
-  static size_t SizeOf(size_t pointer_size, uint32_t num_references) {
-    // Assume that the layout is packed.
-    size_t header_size = pointer_size + sizeof(number_of_references_);
-    size_t data_size = sizeof(StackReference<mirror::Object>) * num_references;
-    return header_size + data_size;
-  }
+  static size_t SizeOf(size_t pointer_size, uint32_t num_references);
 
   // Link to previous HandleScope or null.
   HandleScope* GetLink() const {
@@ -67,37 +59,18 @@
   }
 
   ALWAYS_INLINE mirror::Object* GetReference(size_t i) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_LT(i, number_of_references_);
-    return GetReferences()[i].AsMirrorPtr();
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ALWAYS_INLINE Handle<mirror::Object> GetHandle(size_t i)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_LT(i, number_of_references_);
-    return Handle<mirror::Object>(&GetReferences()[i]);
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ALWAYS_INLINE MutableHandle<mirror::Object> GetMutableHandle(size_t i)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_LT(i, number_of_references_);
-    return MutableHandle<mirror::Object>(&GetReferences()[i]);
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_LT(i, number_of_references_);
-    GetReferences()[i].Assign(object);
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool Contains(StackReference<mirror::Object>* handle_scope_entry) const {
-    // A HandleScope should always contain something. One created by the
-    // jni_compiler should have a jobject/jclass as a native method is
-    // passed in a this pointer or a class
-    DCHECK_GT(number_of_references_, 0U);
-    return &GetReferences()[0] <= handle_scope_entry &&
-        handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
-  }
+  ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const;
 
   // Offset of link within HandleScope, used by generated code.
   static size_t LinkOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
@@ -174,27 +147,14 @@
   ALWAYS_INLINE ~StackHandleScope();
 
   template<class T>
-  ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetReference(pos_, object);
-    MutableHandle<T> h(GetHandle<T>(pos_));
-    pos_++;
-    return h;
-  }
+  ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template<class T>
   ALWAYS_INLINE HandleWrapper<T> NewHandleWrapper(T** object)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetReference(pos_, *object);
-    MutableHandle<T> h(GetHandle<T>(pos_));
-    pos_++;
-    return HandleWrapper<T>(object, h);
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_LT(i, kNumReferences);
-    GetReferences()[i].Assign(object);
-  }
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
   template<class T>
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 3069581..d2e93bc 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -44,10 +44,13 @@
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "gc/heap.h"
 #include "gc/space/space.h"
 #include "globals.h"
+#include "jdwp/jdwp.h"
+#include "jdwp/jdwp_priv.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
@@ -61,46 +64,17 @@
 
 namespace hprof {
 
-#define UNIQUE_ERROR -((((uintptr_t)__func__) << 16 | __LINE__) & (0x7fffffff))
+static constexpr bool kDirectStream = true;
 
-#define HPROF_TIME 0
-#define HPROF_NULL_STACK_TRACE   0
-#define HPROF_NULL_THREAD        0
+static constexpr uint32_t kHprofTime = 0;
+static constexpr uint32_t kHprofNullStackTrace = 0;
+static constexpr uint32_t kHprofNullThread = 0;
 
-#define U2_TO_BUF_BE(buf, offset, value) \
-    do { \
-      unsigned char* buf_ = (unsigned char*)(buf); \
-      int offset_ = static_cast<int>(offset); \
-      uint16_t value_ = (uint16_t)(value); \
-      buf_[offset_ + 0] = (unsigned char)(value_ >>  8); \
-      buf_[offset_ + 1] = (unsigned char)(value_      ); \
-    } while (0)
+static constexpr size_t kMaxObjectsPerSegment = 128;
+static constexpr size_t kMaxBytesPerSegment = 4096;
 
-#define U4_TO_BUF_BE(buf, offset, value) \
-    do { \
-      unsigned char* buf_ = (unsigned char*)(buf); \
-      int offset_ = static_cast<int>(offset); \
-      uint32_t value_ = (uint32_t)(value); \
-      buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
-      buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
-      buf_[offset_ + 2] = (unsigned char)(value_ >>  8); \
-      buf_[offset_ + 3] = (unsigned char)(value_      ); \
-    } while (0)
-
-#define U8_TO_BUF_BE(buf, offset, value) \
-    do { \
-      unsigned char* buf_ = (unsigned char*)(buf); \
-      int offset_ = static_cast<int>(offset); \
-      uint64_t value_ = (uint64_t)(value); \
-      buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
-      buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
-      buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
-      buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
-      buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
-      buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
-      buf_[offset_ + 6] = (unsigned char)(value_ >>  8); \
-      buf_[offset_ + 7] = (unsigned char)(value_      ); \
-    } while (0)
+// The static field-name for the synthetic object generated to account for class static overhead.
+static constexpr const char* kStaticOverheadName = "$staticOverhead";
 
 enum HprofTag {
   HPROF_TAG_STRING = 0x01,
@@ -170,216 +144,267 @@
 typedef uint32_t HprofStringId;
 typedef uint32_t HprofClassObjectId;
 
-// Represents a top-level hprof record, whose serialized format is:
-// U1  TAG: denoting the type of the record
-// U4  TIME: number of microseconds since the time stamp in the header
-// U4  LENGTH: number of bytes that follow this uint32_t field and belong to this record
-// U1* BODY: as many bytes as specified in the above uint32_t field
-class HprofRecord {
+class EndianOutput {
  public:
-  HprofRecord() : alloc_length_(128), fp_(nullptr), tag_(0), time_(0), length_(0), dirty_(false) {
-    body_ = reinterpret_cast<unsigned char*>(malloc(alloc_length_));
+  EndianOutput() : length_(0), sum_length_(0), max_length_(0), started_(false) {}
+  virtual ~EndianOutput() {}
+
+  void StartNewRecord(uint8_t tag, uint32_t time) {
+    if (length_ > 0) {
+      EndRecord();
+    }
+    DCHECK_EQ(length_, 0U);
+    AddU1(tag);
+    AddU4(time);
+    AddU4(0xdeaddead);  // Length, replaced on flush.
+    started_ = true;
   }
 
-  ~HprofRecord() {
-    free(body_);
-  }
-
-  int StartNewRecord(FILE* fp, uint8_t tag, uint32_t time) {
-    int rc = Flush();
-    if (rc != 0) {
-      return rc;
+  void EndRecord() {
+    // Replace length in header.
+    if (started_) {
+      UpdateU4(sizeof(uint8_t) + sizeof(uint32_t),
+               length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
     }
 
-    fp_ = fp;
-    tag_ = tag;
-    time_ = time;
+    HandleEndRecord();
+
+    sum_length_ += length_;
+    max_length_ = std::max(max_length_, length_);
     length_ = 0;
-    dirty_ = true;
-    return 0;
+    started_ = false;
   }
 
-  int Flush() {
-    if (dirty_) {
-      unsigned char headBuf[sizeof(uint8_t) + 2 * sizeof(uint32_t)];
-
-      headBuf[0] = tag_;
-      U4_TO_BUF_BE(headBuf, 1, time_);
-      U4_TO_BUF_BE(headBuf, 5, length_);
-
-      int nb = fwrite(headBuf, 1, sizeof(headBuf), fp_);
-      if (nb != sizeof(headBuf)) {
-        return UNIQUE_ERROR;
-      }
-      nb = fwrite(body_, 1, length_, fp_);
-      if (nb != static_cast<int>(length_)) {
-        return UNIQUE_ERROR;
-      }
-
-      dirty_ = false;
-    }
-    // TODO if we used less than half (or whatever) of allocLen, shrink the buffer.
-    return 0;
+  void AddU1(uint8_t value) {
+    AddU1List(&value, 1);
+  }
+  void AddU2(uint16_t value) {
+    AddU2List(&value, 1);
+  }
+  void AddU4(uint32_t value) {
+    AddU4List(&value, 1);
   }
 
-  int AddU1(uint8_t value) {
-    int err = GuaranteeRecordAppend(1);
-    if (UNLIKELY(err != 0)) {
-      return err;
-    }
-
-    body_[length_++] = value;
-    return 0;
+  void AddU8(uint64_t value) {
+    AddU8List(&value, 1);
   }
 
-  int AddU2(uint16_t value) {
-    return AddU2List(&value, 1);
-  }
-
-  int AddU4(uint32_t value) {
-    return AddU4List(&value, 1);
-  }
-
-  int AddU8(uint64_t value) {
-    return AddU8List(&value, 1);
-  }
-
-  int AddObjectId(const mirror::Object* value) {
-    return AddU4(PointerToLowMemUInt32(value));
+  void AddObjectId(const mirror::Object* value) {
+    AddU4(PointerToLowMemUInt32(value));
   }
 
   // The ID for the synthetic object generated to account for class static overhead.
-  int AddClassStaticsId(const mirror::Class* value) {
-    return AddU4(1 | PointerToLowMemUInt32(value));
+  void AddClassStaticsId(const mirror::Class* value) {
+    AddU4(1 | PointerToLowMemUInt32(value));
   }
 
-  int AddJniGlobalRefId(jobject value) {
-    return AddU4(PointerToLowMemUInt32(value));
+  void AddJniGlobalRefId(jobject value) {
+    AddU4(PointerToLowMemUInt32(value));
   }
 
-  int AddClassId(HprofClassObjectId value) {
-    return AddU4(value);
+  void AddClassId(HprofClassObjectId value) {
+    AddU4(value);
   }
 
-  int AddStringId(HprofStringId value) {
-    return AddU4(value);
+  void AddStringId(HprofStringId value) {
+    AddU4(value);
   }
 
-  int AddU1List(const uint8_t* values, size_t numValues) {
-    int err = GuaranteeRecordAppend(numValues);
-    if (UNLIKELY(err != 0)) {
-      return err;
-    }
-
-    memcpy(body_ + length_, values, numValues);
-    length_ += numValues;
-    return 0;
+  void AddU1List(const uint8_t* values, size_t count) {
+    HandleU1List(values, count);
+    length_ += count;
+  }
+  void AddU2List(const uint16_t* values, size_t count) {
+    HandleU2List(values, count);
+    length_ += count * sizeof(uint16_t);
+  }
+  void AddU4List(const uint32_t* values, size_t count) {
+    HandleU4List(values, count);
+    length_ += count * sizeof(uint32_t);
+  }
+  virtual void UpdateU4(size_t offset ATTRIBUTE_UNUSED, uint32_t new_value ATTRIBUTE_UNUSED) {
+    DCHECK_LE(offset, length_ - 4);
+  }
+  void AddU8List(const uint64_t* values, size_t count) {
+    HandleU8List(values, count);
+    length_ += count * sizeof(uint64_t);
   }
 
-  int AddU2List(const uint16_t* values, size_t numValues) {
-    int err = GuaranteeRecordAppend(numValues * 2);
-    if (UNLIKELY(err != 0)) {
-      return err;
-    }
-
-    unsigned char* insert = body_ + length_;
-    for (size_t i = 0; i < numValues; ++i) {
-      U2_TO_BUF_BE(insert, 0, *values++);
-      insert += sizeof(*values);
-    }
-    length_ += numValues * 2;
-    return 0;
-  }
-
-  int AddU4List(const uint32_t* values, size_t numValues) {
-    int err = GuaranteeRecordAppend(numValues * 4);
-    if (UNLIKELY(err != 0)) {
-      return err;
-    }
-
-    unsigned char* insert = body_ + length_;
-    for (size_t i = 0; i < numValues; ++i) {
-      U4_TO_BUF_BE(insert, 0, *values++);
-      insert += sizeof(*values);
-    }
-    length_ += numValues * 4;
-    return 0;
-  }
-
-  void UpdateU4(size_t offset, uint32_t new_value) {
-    U4_TO_BUF_BE(body_, offset, new_value);
-  }
-
-  int AddU8List(const uint64_t* values, size_t numValues) {
-    int err = GuaranteeRecordAppend(numValues * 8);
-    if (err != 0) {
-      return err;
-    }
-
-    unsigned char* insert = body_ + length_;
-    for (size_t i = 0; i < numValues; ++i) {
-      U8_TO_BUF_BE(insert, 0, *values++);
-      insert += sizeof(*values);
-    }
-    length_ += numValues * 8;
-    return 0;
-  }
-
-  int AddIdList(mirror::ObjectArray<mirror::Object>* values)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    int32_t length = values->GetLength();
+  void AddIdList(mirror::ObjectArray<mirror::Object>* values)
+  SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    const int32_t length = values->GetLength();
     for (int32_t i = 0; i < length; ++i) {
-      int err = AddObjectId(values->GetWithoutChecks(i));
-      if (UNLIKELY(err != 0)) {
-        return err;
-      }
+      AddObjectId(values->GetWithoutChecks(i));
     }
-    return 0;
   }
 
-  int AddUtf8String(const char* str) {
+  void AddUtf8String(const char* str) {
     // The terminating NUL character is NOT written.
-    return AddU1List((const uint8_t*)str, strlen(str));
+    AddU1List((const uint8_t*)str, strlen(str));
   }
 
-  size_t Size() const {
+  size_t Length() const {
     return length_;
   }
 
- private:
-  int GuaranteeRecordAppend(size_t nmore) {
-    size_t minSize = length_ + nmore;
-    if (minSize > alloc_length_) {
-      size_t newAllocLen = alloc_length_ * 2;
-      if (newAllocLen < minSize) {
-        newAllocLen = alloc_length_ + nmore + nmore/2;
-      }
-      unsigned char* newBody = (unsigned char*)realloc(body_, newAllocLen);
-      if (newBody != NULL) {
-        body_ = newBody;
-        alloc_length_ = newAllocLen;
-      } else {
-        // TODO: set an error flag so future ops will fail
-        return UNIQUE_ERROR;
-      }
-    }
-
-    CHECK_LE(length_ + nmore, alloc_length_);
-    return 0;
+  size_t SumLength() const {
+    return sum_length_;
   }
 
-  size_t alloc_length_;
-  unsigned char* body_;
+  size_t MaxLength() const {
+    return max_length_;
+  }
 
-  FILE* fp_;
-  uint8_t tag_;
-  uint32_t time_;
-  size_t length_;
-  bool dirty_;
+ protected:
+  virtual void HandleU1List(const uint8_t* values ATTRIBUTE_UNUSED,
+                            size_t count ATTRIBUTE_UNUSED) {
+  }
+  virtual void HandleU2List(const uint16_t* values ATTRIBUTE_UNUSED,
+                            size_t count ATTRIBUTE_UNUSED) {
+  }
+  virtual void HandleU4List(const uint32_t* values ATTRIBUTE_UNUSED,
+                            size_t count ATTRIBUTE_UNUSED) {
+  }
+  virtual void HandleU8List(const uint64_t* values ATTRIBUTE_UNUSED,
+                            size_t count ATTRIBUTE_UNUSED) {
+  }
+  virtual void HandleEndRecord() {
+  }
 
-  DISALLOW_COPY_AND_ASSIGN(HprofRecord);
+  size_t length_;      // Current record size.
+  size_t sum_length_;  // Size of all data.
+  size_t max_length_;  // Maximum seen length.
+  bool started_;       // Was StartRecord called?
 };
 
+// This keeps things buffered until flushed.
+class EndianOutputBuffered : public EndianOutput {
+ public:
+  explicit EndianOutputBuffered(size_t reserve_size) {
+    buffer_.reserve(reserve_size);
+  }
+  virtual ~EndianOutputBuffered() {}
+
+  void UpdateU4(size_t offset, uint32_t new_value) OVERRIDE {
+    DCHECK_LE(offset, length_ - 4);
+    buffer_[offset + 0] = static_cast<uint8_t>((new_value >> 24) & 0xFF);
+    buffer_[offset + 1] = static_cast<uint8_t>((new_value >> 16) & 0xFF);
+    buffer_[offset + 2] = static_cast<uint8_t>((new_value >> 8)  & 0xFF);
+    buffer_[offset + 3] = static_cast<uint8_t>((new_value >> 0)  & 0xFF);
+  }
+
+ protected:
+  void HandleU1List(const uint8_t* values, size_t count) OVERRIDE {
+    DCHECK_EQ(length_, buffer_.size());
+    buffer_.insert(buffer_.end(), values, values + count);
+  }
+
+  void HandleU2List(const uint16_t* values, size_t count) OVERRIDE {
+    DCHECK_EQ(length_, buffer_.size());
+    for (size_t i = 0; i < count; ++i) {
+      uint16_t value = *values;
+      buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
+      values++;
+    }
+  }
+
+  void HandleU4List(const uint32_t* values, size_t count) OVERRIDE {
+    DCHECK_EQ(length_, buffer_.size());
+    for (size_t i = 0; i < count; ++i) {
+      uint32_t value = *values;
+      buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 8)  & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 0)  & 0xFF));
+      values++;
+    }
+  }
+
+  void HandleU8List(const uint64_t* values, size_t count) OVERRIDE {
+    DCHECK_EQ(length_, buffer_.size());
+    for (size_t i = 0; i < count; ++i) {
+      uint64_t value = *values;
+      buffer_.push_back(static_cast<uint8_t>((value >> 56) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 48) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 40) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 32) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 8)  & 0xFF));
+      buffer_.push_back(static_cast<uint8_t>((value >> 0)  & 0xFF));
+      values++;
+    }
+  }
+
+  void HandleEndRecord() OVERRIDE {
+    DCHECK_EQ(buffer_.size(), length_);
+    if (kIsDebugBuild && started_) {
+      uint32_t stored_length =
+          static_cast<uint32_t>(buffer_[5]) << 24 |
+          static_cast<uint32_t>(buffer_[6]) << 16 |
+          static_cast<uint32_t>(buffer_[7]) << 8 |
+          static_cast<uint32_t>(buffer_[8]);
+      DCHECK_EQ(stored_length, length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
+    }
+    HandleFlush(buffer_.data(), length_);
+    buffer_.clear();
+  }
+
+  virtual void HandleFlush(const uint8_t* buffer ATTRIBUTE_UNUSED, size_t length ATTRIBUTE_UNUSED) {
+  }
+
+  std::vector<uint8_t> buffer_;
+};
+
+class FileEndianOutput FINAL : public EndianOutputBuffered {
+ public:
+  FileEndianOutput(File* fp, size_t reserved_size)
+      : EndianOutputBuffered(reserved_size), fp_(fp), errors_(false) {
+    DCHECK(fp != nullptr);
+  }
+  ~FileEndianOutput() {
+  }
+
+  bool Errors() {
+    return errors_;
+  }
+
+ protected:
+  void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE {
+    if (!errors_) {
+      errors_ = !fp_->WriteFully(buffer, length);
+    }
+  }
+
+ private:
+  File* fp_;
+  bool errors_;
+};
+
+class NetStateEndianOutput FINAL : public EndianOutputBuffered {
+ public:
+  NetStateEndianOutput(JDWP::JdwpNetStateBase* net_state, size_t reserved_size)
+      : EndianOutputBuffered(reserved_size), net_state_(net_state) {
+    DCHECK(net_state != nullptr);
+  }
+  ~NetStateEndianOutput() {}
+
+ protected:
+  void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE {
+    std::vector<iovec> iov;
+    iov.push_back(iovec());
+    iov[0].iov_base = const_cast<void*>(reinterpret_cast<const void*>(buffer));
+    iov[0].iov_len = length;
+    net_state_->WriteBufferedPacketLocked(iov);
+  }
+
+ private:
+  JDWP::JdwpNetStateBase* net_state_;
+};
+
+#define __ output->
+
 class Hprof {
  public:
   Hprof(const char* output_filename, int fd, bool direct_to_ddms)
@@ -387,226 +412,180 @@
         fd_(fd),
         direct_to_ddms_(direct_to_ddms),
         start_ns_(NanoTime()),
-        current_record_(),
-        gc_thread_serial_number_(0),
-        gc_scan_state_(0),
         current_heap_(HPROF_HEAP_DEFAULT),
         objects_in_segment_(0),
-        header_fp_(NULL),
-        header_data_ptr_(NULL),
-        header_data_size_(0),
-        body_fp_(NULL),
-        body_data_ptr_(NULL),
-        body_data_size_(0),
         next_string_id_(0x400000) {
     LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting...";
-
-    header_fp_ = open_memstream(&header_data_ptr_, &header_data_size_);
-    if (header_fp_ == NULL) {
-      PLOG(FATAL) << "header open_memstream failed";
-    }
-
-    body_fp_ = open_memstream(&body_data_ptr_, &body_data_size_);
-    if (body_fp_ == NULL) {
-      PLOG(FATAL) << "body open_memstream failed";
-    }
-  }
-
-  ~Hprof() {
-    if (header_fp_ != NULL) {
-      fclose(header_fp_);
-    }
-    if (body_fp_ != NULL) {
-      fclose(body_fp_);
-    }
-    free(header_data_ptr_);
-    free(body_data_ptr_);
   }
 
   void Dump()
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) {
-    // Walk the roots and the heap.
-    current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
-    Runtime::Current()->VisitRoots(RootVisitor, this);
-    Thread* self = Thread::Current();
+    // First pass to measure the size of the dump.
+    size_t overall_size;
+    size_t max_length;
     {
-      ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
-      Runtime::Current()->GetHeap()->VisitObjects(VisitObjectCallback, this);
+      EndianOutput count_output;
+      ProcessHeap(&count_output, false);
+      overall_size = count_output.SumLength();
+      max_length = count_output.MaxLength();
     }
-    current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
-    current_record_.Flush();
-    fflush(body_fp_);
 
-    // Write the header.
-    WriteFixedHeader();
-    // Write the string and class tables, and any stack traces, to the header.
-    // (jhat requires that these appear before any of the data in the body that refers to them.)
-    WriteStringTable();
-    WriteClassTable();
-    WriteStackTraces();
-    current_record_.Flush();
-    fflush(header_fp_);
-
-    bool okay = true;
+    bool okay;
     if (direct_to_ddms_) {
-      // Send the data off to DDMS.
-      iovec iov[2];
-      iov[0].iov_base = header_data_ptr_;
-      iov[0].iov_len = header_data_size_;
-      iov[1].iov_base = body_data_ptr_;
-      iov[1].iov_len = body_data_size_;
-      Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+      if (kDirectStream) {
+        okay = DumpToDdmsDirect(overall_size, max_length, CHUNK_TYPE("HPDS"));
+      } else {
+        okay = DumpToDdmsBuffered(overall_size, max_length);
+      }
     } else {
-      // Where exactly are we writing to?
-      int out_fd;
-      if (fd_ >= 0) {
-        out_fd = dup(fd_);
-        if (out_fd < 0) {
-          ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
-          return;
-        }
-      } else {
-        out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
-        if (out_fd < 0) {
-          ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
-                                strerror(errno));
-          return;
-        }
-      }
-
-      std::unique_ptr<File> file(new File(out_fd, filename_, true));
-      okay = file->WriteFully(header_data_ptr_, header_data_size_) &&
-             file->WriteFully(body_data_ptr_, body_data_size_);
-      if (okay) {
-        okay = file->FlushCloseOrErase() == 0;
-      } else {
-        file->Erase();
-      }
-      if (!okay) {
-        std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
-                                     filename_.c_str(), strerror(errno)));
-        ThrowRuntimeException("%s", msg.c_str());
-        LOG(ERROR) << msg;
-      }
+      okay = DumpToFile(overall_size, max_length);
     }
 
-    // Throw out a log message for the benefit of "runhat".
     if (okay) {
       uint64_t duration = NanoTime() - start_ns_;
       LOG(INFO) << "hprof: heap dump completed ("
-          << PrettySize(header_data_size_ + body_data_size_ + 1023)
+          << PrettySize(RoundUp(overall_size, 1024))
           << ") in " << PrettyDuration(duration);
     }
   }
 
  private:
-  static void RootVisitor(mirror::Object** obj, void* arg, uint32_t thread_id, RootType root_type)
+  struct Env {
+    Hprof* hprof;
+    EndianOutput* output;
+  };
+
+  static void RootVisitor(mirror::Object** obj, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(arg != nullptr);
     DCHECK(obj != nullptr);
     DCHECK(*obj != nullptr);
-    reinterpret_cast<Hprof*>(arg)->VisitRoot(*obj, thread_id, root_type);
+    Env* env = reinterpret_cast<Env*>(arg);
+    env->hprof->VisitRoot(*obj, root_info, env->output);
   }
 
   static void VisitObjectCallback(mirror::Object* obj, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK(obj != NULL);
-    DCHECK(arg != NULL);
-    reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj);
+    DCHECK(obj != nullptr);
+    DCHECK(arg != nullptr);
+    Env* env = reinterpret_cast<Env*>(arg);
+    env->hprof->DumpHeapObject(obj, env->output);
   }
 
-  void VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type)
+  void DumpHeapObject(mirror::Object* obj, EndianOutput* output)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  int DumpHeapObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DumpHeapClass(mirror::Class* klass, EndianOutput* output)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void Finish() {
+  void DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass, EndianOutput* output)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void ProcessHeap(EndianOutput* output, bool header_first)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Reset current heap and object count.
+    current_heap_ = HPROF_HEAP_DEFAULT;
+    objects_in_segment_ = 0;
+
+    if (header_first) {
+      ProcessHeader(output);
+      ProcessBody(output);
+    } else {
+      ProcessBody(output);
+      ProcessHeader(output);
+    }
   }
 
-  int WriteClassTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    HprofRecord* rec = &current_record_;
+  void ProcessBody(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Runtime* runtime = Runtime::Current();
+    // Walk the roots and the heap.
+    output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
+
+    Env env = { this, output };
+    runtime->VisitRoots(RootVisitor, &env);
+    runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, &env);
+
+    output->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
+    output->EndRecord();
+  }
+
+  void ProcessHeader(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Write the header.
+    WriteFixedHeader(output);
+    // Write the string and class tables, and any stack traces, to the header.
+    // (jhat requires that these appear before any of the data in the body that refers to them.)
+    WriteStringTable(output);
+    WriteClassTable(output);
+    WriteStackTraces(output);
+    output->EndRecord();
+  }
+
+  void WriteClassTable(EndianOutput* output) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     uint32_t nextSerialNumber = 1;
 
     for (mirror::Class* c : classes_) {
       CHECK(c != nullptr);
-
-      int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
-      if (UNLIKELY(err != 0)) {
-        return err;
-      }
-
+      output->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime);
       // LOAD CLASS format:
       // U4: class serial number (always > 0)
       // ID: class object ID. We use the address of the class object structure as its ID.
       // U4: stack trace serial number
       // ID: class name string ID
-      rec->AddU4(nextSerialNumber++);
-      rec->AddObjectId(c);
-      rec->AddU4(HPROF_NULL_STACK_TRACE);
-      rec->AddStringId(LookupClassNameId(c));
+      __ AddU4(nextSerialNumber++);
+      __ AddObjectId(c);
+      __ AddU4(kHprofNullStackTrace);
+      __ AddStringId(LookupClassNameId(c));
     }
-
-    return 0;
   }
 
-  int WriteStringTable() {
-    HprofRecord* rec = &current_record_;
-
-    for (std::pair<std::string, HprofStringId> p : strings_) {
+  void WriteStringTable(EndianOutput* output) {
+    for (const std::pair<std::string, HprofStringId>& p : strings_) {
       const std::string& string = p.first;
-      size_t id = p.second;
+      const size_t id = p.second;
 
-      int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_STRING, HPROF_TIME);
-      if (err != 0) {
-        return err;
-      }
+      output->StartNewRecord(HPROF_TAG_STRING, kHprofTime);
 
       // STRING format:
       // ID:  ID for this string
       // U1*: UTF8 characters for string (NOT NULL terminated)
       //      (the record format encodes the length)
-      err = rec->AddU4(id);
-      if (err != 0) {
-        return err;
-      }
-      err = rec->AddUtf8String(string.c_str());
-      if (err != 0) {
-        return err;
-      }
+      __ AddU4(id);
+      __ AddUtf8String(string.c_str());
     }
-
-    return 0;
   }
 
-  void StartNewHeapDumpSegment() {
+  void StartNewHeapDumpSegment(EndianOutput* output) {
     // This flushes the old segment and starts a new one.
-    current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+    output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
     objects_in_segment_ = 0;
-
     // Starting a new HEAP_DUMP resets the heap to default.
     current_heap_ = HPROF_HEAP_DEFAULT;
   }
 
-  int MarkRootObject(const mirror::Object* obj, jobject jniObj);
+  void CheckHeapSegmentConstraints(EndianOutput* output) {
+    if (objects_in_segment_ >= kMaxObjectsPerSegment || output->Length() >= kMaxBytesPerSegment) {
+      StartNewHeapDumpSegment(output);
+    }
+  }
+
+  void VisitRoot(const mirror::Object* obj, const RootInfo& root_info, EndianOutput* output)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
+                      uint32_t thread_serial, EndianOutput* output);
 
   HprofClassObjectId LookupClassId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    if (c == nullptr) {
-      // c is the superclass of java.lang.Object or a primitive.
-      return 0;
-    }
-
-    {
+    if (c != nullptr) {
       auto result = classes_.insert(c);
       const mirror::Class* present = *result.first;
       CHECK_EQ(present, c);
+      // Make sure that we've assigned a string ID for this class' name
+      LookupClassNameId(c);
     }
-
-    // Make sure that we've assigned a string ID for this class' name
-    LookupClassNameId(c);
-
-    HprofClassObjectId result = PointerToLowMemUInt32(c);
-    return result;
+    return PointerToLowMemUInt32(c);
   }
 
   HprofStringId LookupStringId(mirror::String* string) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -631,46 +610,125 @@
     return LookupStringId(PrettyDescriptor(c));
   }
 
-  void WriteFixedHeader() {
-    char magic[] = "JAVA PROFILE 1.0.3";
-    unsigned char buf[4];
-
+  void WriteFixedHeader(EndianOutput* output) {
     // Write the file header.
     // U1: NUL-terminated magic string.
-    fwrite(magic, 1, sizeof(magic), header_fp_);
+    const char magic[] = "JAVA PROFILE 1.0.3";
+    __ AddU1List(reinterpret_cast<const uint8_t*>(magic), sizeof(magic));
 
     // U4: size of identifiers.  We're using addresses as IDs and our heap references are stored
     // as uint32_t.
     // Note of warning: hprof-conv hard-codes the size of identifiers to 4.
     static_assert(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t),
                   "Unexpected HeapReference size");
-    U4_TO_BUF_BE(buf, 0, sizeof(uint32_t));
-    fwrite(buf, 1, sizeof(uint32_t), header_fp_);
+    __ AddU4(sizeof(uint32_t));
 
     // The current time, in milliseconds since 0:00 GMT, 1/1/70.
     timeval now;
-    uint64_t nowMs;
-    if (gettimeofday(&now, NULL) < 0) {
-      nowMs = 0;
-    } else {
-      nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
-    }
-
+    const uint64_t nowMs = (gettimeofday(&now, nullptr) < 0) ? 0 :
+        (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+    // TODO: It seems it would be correct to use U8.
     // U4: high word of the 64-bit time.
-    U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32));
-    fwrite(buf, 1, sizeof(uint32_t), header_fp_);
-
+    __ AddU4(static_cast<uint32_t>(nowMs >> 32));
     // U4: low word of the 64-bit time.
-    U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL));
-    fwrite(buf, 1, sizeof(uint32_t), header_fp_);  // xxx fix the time
+    __ AddU4(static_cast<uint32_t>(nowMs & 0xFFFFFFFF));
   }
 
-  void WriteStackTraces() {
+  void WriteStackTraces(EndianOutput* output) {
     // Write a dummy stack trace record so the analysis tools don't freak out.
-    current_record_.StartNewRecord(header_fp_, HPROF_TAG_STACK_TRACE, HPROF_TIME);
-    current_record_.AddU4(HPROF_NULL_STACK_TRACE);
-    current_record_.AddU4(HPROF_NULL_THREAD);
-    current_record_.AddU4(0);    // no frames
+    output->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
+    __ AddU4(kHprofNullStackTrace);
+    __ AddU4(kHprofNullThread);
+    __ AddU4(0);    // no frames
+  }
+
+  bool DumpToDdmsBuffered(size_t overall_size ATTRIBUTE_UNUSED, size_t max_length ATTRIBUTE_UNUSED)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    LOG(FATAL) << "Unimplemented";
+    UNREACHABLE();
+    //        // Send the data off to DDMS.
+    //        iovec iov[2];
+    //        iov[0].iov_base = header_data_ptr_;
+    //        iov[0].iov_len = header_data_size_;
+    //        iov[1].iov_base = body_data_ptr_;
+    //        iov[1].iov_len = body_data_size_;
+    //        Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+  }
+
+  bool DumpToFile(size_t overall_size, size_t max_length)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    // Where exactly are we writing to?
+    int out_fd;
+    if (fd_ >= 0) {
+      out_fd = dup(fd_);
+      if (out_fd < 0) {
+        ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
+        return false;
+      }
+    } else {
+      out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
+      if (out_fd < 0) {
+        ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
+                              strerror(errno));
+        return false;
+      }
+    }
+
+    std::unique_ptr<File> file(new File(out_fd, filename_, true));
+    bool okay;
+    {
+      FileEndianOutput file_output(file.get(), max_length);
+      ProcessHeap(&file_output, true);
+      okay = !file_output.Errors();
+
+      if (okay) {
+        // Check for expected size.
+        CHECK_EQ(file_output.SumLength(), overall_size);
+      }
+    }
+
+    if (okay) {
+      okay = file->FlushCloseOrErase() == 0;
+    } else {
+      file->Erase();
+    }
+    if (!okay) {
+      std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
+                                   filename_.c_str(), strerror(errno)));
+      ThrowRuntimeException("%s", msg.c_str());
+      LOG(ERROR) << msg;
+    }
+
+    return okay;
+  }
+
+  bool DumpToDdmsDirect(size_t overall_size, size_t max_length, uint32_t chunk_type)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    CHECK(direct_to_ddms_);
+    JDWP::JdwpState* state = Dbg::GetJdwpState();
+    CHECK(state != nullptr);
+    JDWP::JdwpNetStateBase* net_state = state->netState;
+    CHECK(net_state != nullptr);
+
+    // Hold the socket lock for the whole time since we want this to be atomic.
+    MutexLock mu(Thread::Current(), *net_state->GetSocketLock());
+
+    // Prepare the Ddms chunk.
+    constexpr size_t kChunkHeaderSize = kJDWPHeaderLen + 8;
+    uint8_t chunk_header[kChunkHeaderSize] = { 0 };
+    state->SetupChunkHeader(chunk_type, overall_size, kChunkHeaderSize, chunk_header);
+
+    // Prepare the output and send the chunk header.
+    NetStateEndianOutput net_output(net_state, max_length);
+    net_output.AddU1List(chunk_header, kChunkHeaderSize);
+
+    // Write the dump.
+    ProcessHeap(&net_output, true);
+
+    // Check for expected size.
+    CHECK_EQ(net_output.SumLength(), overall_size + kChunkHeaderSize);
+
+    return true;
   }
 
   // If direct_to_ddms_ is set, "filename_" and "fd" will be ignored.
@@ -682,21 +740,9 @@
 
   uint64_t start_ns_;
 
-  HprofRecord current_record_;
-
-  uint32_t gc_thread_serial_number_;
-  uint8_t gc_scan_state_;
   HprofHeapId current_heap_;  // Which heap we're currently dumping.
   size_t objects_in_segment_;
 
-  FILE* header_fp_;
-  char* header_data_ptr_;
-  size_t header_data_size_;
-
-  FILE* body_fp_;
-  char* body_data_ptr_;
-  size_t body_data_size_;
-
   std::set<mirror::Class*> classes_;
   HprofStringId next_string_id_;
   SafeMap<std::string, HprofStringId> strings_;
@@ -704,56 +750,56 @@
   DISALLOW_COPY_AND_ASSIGN(Hprof);
 };
 
-#define OBJECTS_PER_SEGMENT     ((size_t)128)
-#define BYTES_PER_SEGMENT       ((size_t)4096)
-
-// The static field-name for the synthetic object generated to account for class static overhead.
-#define STATIC_OVERHEAD_NAME    "$staticOverhead"
-
-static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) {
+static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* size_out) {
   char c = sig[0];
   HprofBasicType ret;
   size_t size;
 
   switch (c) {
-  case '[':
-  case 'L': ret = hprof_basic_object;  size = 4; break;
-  case 'Z': ret = hprof_basic_boolean; size = 1; break;
-  case 'C': ret = hprof_basic_char;    size = 2; break;
-  case 'F': ret = hprof_basic_float;   size = 4; break;
-  case 'D': ret = hprof_basic_double;  size = 8; break;
-  case 'B': ret = hprof_basic_byte;    size = 1; break;
-  case 'S': ret = hprof_basic_short;   size = 2; break;
-  case 'I': ret = hprof_basic_int;     size = 4; break;
-  case 'J': ret = hprof_basic_long;    size = 8; break;
-  default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE();
+    case '[':
+    case 'L':
+      ret = hprof_basic_object;
+      size = 4;
+      break;
+    case 'Z':
+      ret = hprof_basic_boolean;
+      size = 1;
+      break;
+    case 'C':
+      ret = hprof_basic_char;
+      size = 2;
+      break;
+    case 'F':
+      ret = hprof_basic_float;
+      size = 4;
+      break;
+    case 'D':
+      ret = hprof_basic_double;
+      size = 8;
+      break;
+    case 'B':
+      ret = hprof_basic_byte;
+      size = 1;
+      break;
+    case 'S':
+      ret = hprof_basic_short;
+      size = 2;
+      break;
+    case 'I':
+      ret = hprof_basic_int;
+      size = 4;
+      break;
+    case 'J':
+      ret = hprof_basic_long;
+      size = 8;
+      break;
+    default:
+      LOG(FATAL) << "UNREACHABLE";
+      UNREACHABLE();
   }
 
-  if (sizeOut != NULL) {
-    *sizeOut = size;
-  }
-
-  return ret;
-}
-
-static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t* sizeOut) {
-  HprofBasicType ret;
-  size_t size;
-
-  switch (prim) {
-  case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
-  case Primitive::kPrimChar:    ret = hprof_basic_char;    size = 2; break;
-  case Primitive::kPrimFloat:   ret = hprof_basic_float;   size = 4; break;
-  case Primitive::kPrimDouble:  ret = hprof_basic_double;  size = 8; break;
-  case Primitive::kPrimByte:    ret = hprof_basic_byte;    size = 1; break;
-  case Primitive::kPrimShort:   ret = hprof_basic_short;   size = 2; break;
-  case Primitive::kPrimInt:     ret = hprof_basic_int;     size = 4; break;
-  case Primitive::kPrimLong:    ret = hprof_basic_long;    size = 8; break;
-  default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE();
-  }
-
-  if (sizeOut != NULL) {
-    *sizeOut = size;
+  if (size_out != nullptr) {
+    *size_out = size;
   }
 
   return ret;
@@ -763,95 +809,94 @@
 // something when ctx->gc_scan_state_ is non-zero, which is usually
 // only true when marking the root set or unreachable
 // objects.  Used to add rootset references to obj.
-int Hprof::MarkRootObject(const mirror::Object* obj, jobject jniObj) {
-  HprofRecord* rec = &current_record_;
-  HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
-
-  if (heapTag == 0) {
-    return 0;
+void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
+                           uint32_t thread_serial, EndianOutput* output) {
+  if (heap_tag == 0) {
+    return;
   }
 
-  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
-    StartNewHeapDumpSegment();
-  }
+  CheckHeapSegmentConstraints(output);
 
-  switch (heapTag) {
-  // ID: object ID
-  case HPROF_ROOT_UNKNOWN:
-  case HPROF_ROOT_STICKY_CLASS:
-  case HPROF_ROOT_MONITOR_USED:
-  case HPROF_ROOT_INTERNED_STRING:
-  case HPROF_ROOT_DEBUGGER:
-  case HPROF_ROOT_VM_INTERNAL:
-    rec->AddU1(heapTag);
-    rec->AddObjectId(obj);
-    break;
+  switch (heap_tag) {
+    // ID: object ID
+    case HPROF_ROOT_UNKNOWN:
+    case HPROF_ROOT_STICKY_CLASS:
+    case HPROF_ROOT_MONITOR_USED:
+    case HPROF_ROOT_INTERNED_STRING:
+    case HPROF_ROOT_DEBUGGER:
+    case HPROF_ROOT_VM_INTERNAL:
+      __ AddU1(heap_tag);
+      __ AddObjectId(obj);
+      break;
 
-  // ID: object ID
-  // ID: JNI global ref ID
-  case HPROF_ROOT_JNI_GLOBAL:
-    rec->AddU1(heapTag);
-    rec->AddObjectId(obj);
-    rec->AddJniGlobalRefId(jniObj);
-    break;
+      // ID: object ID
+      // ID: JNI global ref ID
+    case HPROF_ROOT_JNI_GLOBAL:
+      __ AddU1(heap_tag);
+      __ AddObjectId(obj);
+      __ AddJniGlobalRefId(jni_obj);
+      break;
 
-  // ID: object ID
-  // U4: thread serial number
-  // U4: frame number in stack trace (-1 for empty)
-  case HPROF_ROOT_JNI_LOCAL:
-  case HPROF_ROOT_JNI_MONITOR:
-  case HPROF_ROOT_JAVA_FRAME:
-    rec->AddU1(heapTag);
-    rec->AddObjectId(obj);
-    rec->AddU4(gc_thread_serial_number_);
-    rec->AddU4((uint32_t)-1);
-    break;
+      // ID: object ID
+      // U4: thread serial number
+      // U4: frame number in stack trace (-1 for empty)
+    case HPROF_ROOT_JNI_LOCAL:
+    case HPROF_ROOT_JNI_MONITOR:
+    case HPROF_ROOT_JAVA_FRAME:
+      __ AddU1(heap_tag);
+      __ AddObjectId(obj);
+      __ AddU4(thread_serial);
+      __ AddU4((uint32_t)-1);
+      break;
 
-  // ID: object ID
-  // U4: thread serial number
-  case HPROF_ROOT_NATIVE_STACK:
-  case HPROF_ROOT_THREAD_BLOCK:
-    rec->AddU1(heapTag);
-    rec->AddObjectId(obj);
-    rec->AddU4(gc_thread_serial_number_);
-    break;
+      // ID: object ID
+      // U4: thread serial number
+    case HPROF_ROOT_NATIVE_STACK:
+    case HPROF_ROOT_THREAD_BLOCK:
+      __ AddU1(heap_tag);
+      __ AddObjectId(obj);
+      __ AddU4(thread_serial);
+      break;
 
-  // ID: thread object ID
-  // U4: thread serial number
-  // U4: stack trace serial number
-  case HPROF_ROOT_THREAD_OBJECT:
-    rec->AddU1(heapTag);
-    rec->AddObjectId(obj);
-    rec->AddU4(gc_thread_serial_number_);
-    rec->AddU4((uint32_t)-1);    // xxx
-    break;
+      // ID: thread object ID
+      // U4: thread serial number
+      // U4: stack trace serial number
+    case HPROF_ROOT_THREAD_OBJECT:
+      __ AddU1(heap_tag);
+      __ AddObjectId(obj);
+      __ AddU4(thread_serial);
+      __ AddU4((uint32_t)-1);    // xxx
+      break;
 
-  case HPROF_CLASS_DUMP:
-  case HPROF_INSTANCE_DUMP:
-  case HPROF_OBJECT_ARRAY_DUMP:
-  case HPROF_PRIMITIVE_ARRAY_DUMP:
-  case HPROF_HEAP_DUMP_INFO:
-  case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
-    // Ignored.
-    break;
+    case HPROF_CLASS_DUMP:
+    case HPROF_INSTANCE_DUMP:
+    case HPROF_OBJECT_ARRAY_DUMP:
+    case HPROF_PRIMITIVE_ARRAY_DUMP:
+    case HPROF_HEAP_DUMP_INFO:
+    case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
+      // Ignored.
+      break;
 
-  case HPROF_ROOT_FINALIZING:
-  case HPROF_ROOT_REFERENCE_CLEANUP:
-  case HPROF_UNREACHABLE:
-    LOG(FATAL) << "obsolete tag " << static_cast<int>(heapTag);
-    break;
+    case HPROF_ROOT_FINALIZING:
+    case HPROF_ROOT_REFERENCE_CLEANUP:
+    case HPROF_UNREACHABLE:
+      LOG(FATAL) << "obsolete tag " << static_cast<int>(heap_tag);
+      break;
   }
 
   ++objects_in_segment_;
-  return 0;
 }
 
 static int StackTraceSerialNumber(const mirror::Object* /*obj*/) {
-  return HPROF_NULL_STACK_TRACE;
+  return kHprofNullStackTrace;
 }
 
-int Hprof::DumpHeapObject(mirror::Object* obj) {
-  HprofRecord* rec = &current_record_;
+void Hprof::DumpHeapObject(mirror::Object* obj, EndianOutput* output) {
+  // Ignore classes that are retired.
+  if (obj->IsClass() && obj->AsClass()->IsRetired()) {
+    return;
+  }
+
   gc::space::ContinuousSpace* space =
       Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true);
   HprofHeapId heap_type = HPROF_HEAP_APP;
@@ -862,17 +907,15 @@
       heap_type = HPROF_HEAP_IMAGE;
     }
   }
-  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
-    StartNewHeapDumpSegment();
-  }
+  CheckHeapSegmentConstraints(output);
 
   if (heap_type != current_heap_) {
     HprofStringId nameId;
 
     // This object is in a different heap than the current one.
     // Emit a HEAP_DUMP_INFO tag to change heaps.
-    rec->AddU1(HPROF_HEAP_DUMP_INFO);
-    rec->AddU4(static_cast<uint32_t>(heap_type));   // uint32_t: heap type
+    __ AddU1(HPROF_HEAP_DUMP_INFO);
+    __ AddU4(static_cast<uint32_t>(heap_type));   // uint32_t: heap type
     switch (heap_type) {
     case HPROF_HEAP_APP:
       nameId = LookupStringId("app");
@@ -889,179 +932,194 @@
       nameId = LookupStringId("<ILLEGAL>");
       break;
     }
-    rec->AddStringId(nameId);
+    __ AddStringId(nameId);
     current_heap_ = heap_type;
   }
 
   mirror::Class* c = obj->GetClass();
-  if (c == NULL) {
+  if (c == nullptr) {
     // This object will bother HprofReader, because it has a NULL
     // class, so just don't dump it. It could be
     // gDvm.unlinkedJavaLangClass or it could be an object just
     // allocated which hasn't been initialized yet.
   } else {
     if (obj->IsClass()) {
-      mirror::Class* thisClass = obj->AsClass();
-      // obj is a ClassObject.
-      size_t sFieldCount = thisClass->NumStaticFields();
-      if (sFieldCount != 0) {
-        int byteLength = sFieldCount * sizeof(JValue);  // TODO bogus; fields are packed
-        // Create a byte array to reflect the allocation of the
-        // StaticField array at the end of this class.
-        rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
-        rec->AddClassStaticsId(thisClass);
-        rec->AddU4(StackTraceSerialNumber(obj));
-        rec->AddU4(byteLength);
-        rec->AddU1(hprof_basic_byte);
-        for (int i = 0; i < byteLength; ++i) {
-          rec->AddU1(0);
-        }
-      }
-
-      rec->AddU1(HPROF_CLASS_DUMP);
-      rec->AddClassId(LookupClassId(thisClass));
-      rec->AddU4(StackTraceSerialNumber(thisClass));
-      rec->AddClassId(LookupClassId(thisClass->GetSuperClass()));
-      rec->AddObjectId(thisClass->GetClassLoader());
-      rec->AddObjectId(nullptr);    // no signer
-      rec->AddObjectId(nullptr);    // no prot domain
-      rec->AddObjectId(nullptr);    // reserved
-      rec->AddObjectId(nullptr);    // reserved
-      if (thisClass->IsClassClass()) {
-        // ClassObjects have their static fields appended, so aren't all the same size.
-        // But they're at least this size.
-        rec->AddU4(sizeof(mirror::Class));  // instance size
-      } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
-        rec->AddU4(0);
-      } else {
-        rec->AddU4(thisClass->GetObjectSize());  // instance size
-      }
-
-      rec->AddU2(0);  // empty const pool
-
-      // Static fields
-      if (sFieldCount == 0) {
-        rec->AddU2((uint16_t)0);
-      } else {
-        rec->AddU2((uint16_t)(sFieldCount+1));
-        rec->AddStringId(LookupStringId(STATIC_OVERHEAD_NAME));
-        rec->AddU1(hprof_basic_object);
-        rec->AddClassStaticsId(thisClass);
-
-        for (size_t i = 0; i < sFieldCount; ++i) {
-          mirror::ArtField* f = thisClass->GetStaticField(i);
-
-          size_t size;
-          HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
-          rec->AddStringId(LookupStringId(f->GetName()));
-          rec->AddU1(t);
-          if (size == 1) {
-            rec->AddU1(static_cast<uint8_t>(f->Get32(thisClass)));
-          } else if (size == 2) {
-            rec->AddU2(static_cast<uint16_t>(f->Get32(thisClass)));
-          } else if (size == 4) {
-            rec->AddU4(f->Get32(thisClass));
-          } else if (size == 8) {
-            rec->AddU8(f->Get64(thisClass));
-          } else {
-            CHECK(false);
-          }
-        }
-      }
-
-      // Instance fields for this class (no superclass fields)
-      int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
-      rec->AddU2((uint16_t)iFieldCount);
-      for (int i = 0; i < iFieldCount; ++i) {
-        mirror::ArtField* f = thisClass->GetInstanceField(i);
-        HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
-        rec->AddStringId(LookupStringId(f->GetName()));
-        rec->AddU1(t);
-      }
+      DumpHeapClass(obj->AsClass(), output);
     } else if (c->IsArrayClass()) {
-      mirror::Array* aobj = obj->AsArray();
-      uint32_t length = aobj->GetLength();
-
-      if (obj->IsObjectArray()) {
-        // obj is an object array.
-        rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
-
-        rec->AddObjectId(obj);
-        rec->AddU4(StackTraceSerialNumber(obj));
-        rec->AddU4(length);
-        rec->AddClassId(LookupClassId(c));
-
-        // Dump the elements, which are always objects or NULL.
-        rec->AddIdList(aobj->AsObjectArray<mirror::Object>());
-      } else {
-        size_t size;
-        HprofBasicType t = PrimitiveToBasicTypeAndSize(c->GetComponentType()->GetPrimitiveType(), &size);
-
-        // obj is a primitive array.
-        rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
-
-        rec->AddObjectId(obj);
-        rec->AddU4(StackTraceSerialNumber(obj));
-        rec->AddU4(length);
-        rec->AddU1(t);
-
-        // Dump the raw, packed element values.
-        if (size == 1) {
-          rec->AddU1List((const uint8_t*)aobj->GetRawData(sizeof(uint8_t), 0), length);
-        } else if (size == 2) {
-          rec->AddU2List((const uint16_t*)aobj->GetRawData(sizeof(uint16_t), 0), length);
-        } else if (size == 4) {
-          rec->AddU4List((const uint32_t*)aobj->GetRawData(sizeof(uint32_t), 0), length);
-        } else if (size == 8) {
-          rec->AddU8List((const uint64_t*)aobj->GetRawData(sizeof(uint64_t), 0), length);
-        }
-      }
+      DumpHeapArray(obj->AsArray(), c, output);
     } else {
-      // obj is an instance object.
-      rec->AddU1(HPROF_INSTANCE_DUMP);
-      rec->AddObjectId(obj);
-      rec->AddU4(StackTraceSerialNumber(obj));
-      rec->AddClassId(LookupClassId(c));
-
-      // Reserve some space for the length of the instance data, which we won't
-      // know until we're done writing it.
-      size_t size_patch_offset = rec->Size();
-      rec->AddU4(0x77777777);
-
-      // Write the instance data;  fields for this class, followed by super class fields,
-      // and so on. Don't write the klass or monitor fields of Object.class.
-      mirror::Class* sclass = c;
-      while (!sclass->IsObjectClass()) {
-        int ifieldCount = sclass->NumInstanceFields();
-        for (int i = 0; i < ifieldCount; ++i) {
-          mirror::ArtField* f = sclass->GetInstanceField(i);
-          size_t size;
-          SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
-          if (size == 1) {
-            rec->AddU1(f->Get32(obj));
-          } else if (size == 2) {
-            rec->AddU2(f->Get32(obj));
-          } else if (size == 4) {
-            rec->AddU4(f->Get32(obj));
-          } else {
-            CHECK_EQ(size, 8U);
-            rec->AddU8(f->Get64(obj));
-          }
-        }
-
-        sclass = sclass->GetSuperClass();
-      }
-
-      // Patch the instance field length.
-      rec->UpdateU4(size_patch_offset, rec->Size() - (size_patch_offset + 4));
+      DumpHeapInstanceObject(obj, c, output);
     }
   }
 
   ++objects_in_segment_;
-  return 0;
 }
 
-void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type) {
+void Hprof::DumpHeapClass(mirror::Class* klass, EndianOutput* output) {
+  size_t sFieldCount = klass->NumStaticFields();
+  if (sFieldCount != 0) {
+    int byteLength = sFieldCount * sizeof(JValue);  // TODO bogus; fields are packed
+    // Create a byte array to reflect the allocation of the
+    // StaticField array at the end of this class.
+    __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+    __ AddClassStaticsId(klass);
+    __ AddU4(StackTraceSerialNumber(klass));
+    __ AddU4(byteLength);
+    __ AddU1(hprof_basic_byte);
+    for (int i = 0; i < byteLength; ++i) {
+      __ AddU1(0);
+    }
+  }
+
+  __ AddU1(HPROF_CLASS_DUMP);
+  __ AddClassId(LookupClassId(klass));
+  __ AddU4(StackTraceSerialNumber(klass));
+  __ AddClassId(LookupClassId(klass->GetSuperClass()));
+  __ AddObjectId(klass->GetClassLoader());
+  __ AddObjectId(nullptr);    // no signer
+  __ AddObjectId(nullptr);    // no prot domain
+  __ AddObjectId(nullptr);    // reserved
+  __ AddObjectId(nullptr);    // reserved
+  if (klass->IsClassClass()) {
+    // ClassObjects have their static fields appended, so aren't all the same size.
+    // But they're at least this size.
+    __ AddU4(sizeof(mirror::Class));  // instance size
+  } else if (klass->IsArrayClass() || klass->IsPrimitive()) {
+    __ AddU4(0);
+  } else {
+    __ AddU4(klass->GetObjectSize());  // instance size
+  }
+
+  __ AddU2(0);  // empty const pool
+
+  // Static fields
+  if (sFieldCount == 0) {
+    __ AddU2((uint16_t)0);
+  } else {
+    __ AddU2((uint16_t)(sFieldCount+1));
+    __ AddStringId(LookupStringId(kStaticOverheadName));
+    __ AddU1(hprof_basic_object);
+    __ AddClassStaticsId(klass);
+
+    for (size_t i = 0; i < sFieldCount; ++i) {
+      mirror::ArtField* f = klass->GetStaticField(i);
+
+      size_t size;
+      HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+      __ AddStringId(LookupStringId(f->GetName()));
+      __ AddU1(t);
+      switch (size) {
+        case 1:
+          __ AddU1(static_cast<uint8_t>(f->Get32(klass)));
+          break;
+        case 2:
+          __ AddU2(static_cast<uint16_t>(f->Get32(klass)));
+          break;
+        case 4:
+          __ AddU4(f->Get32(klass));
+          break;
+        case 8:
+          __ AddU8(f->Get64(klass));
+          break;
+        default:
+          LOG(FATAL) << "Unexpected size " << size;
+          UNREACHABLE();
+      }
+    }
+  }
+
+  // Instance fields for this class (no superclass fields)
+  int iFieldCount = klass->IsObjectClass() ? 0 : klass->NumInstanceFields();
+  __ AddU2((uint16_t)iFieldCount);
+  for (int i = 0; i < iFieldCount; ++i) {
+    mirror::ArtField* f = klass->GetInstanceField(i);
+    __ AddStringId(LookupStringId(f->GetName()));
+    HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), nullptr);
+    __ AddU1(t);
+  }
+}
+
+void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output) {
+  uint32_t length = obj->GetLength();
+
+  if (obj->IsObjectArray()) {
+    // obj is an object array.
+    __ AddU1(HPROF_OBJECT_ARRAY_DUMP);
+
+    __ AddObjectId(obj);
+    __ AddU4(StackTraceSerialNumber(obj));
+    __ AddU4(length);
+    __ AddClassId(LookupClassId(klass));
+
+    // Dump the elements, which are always objects or NULL.
+    __ AddIdList(obj->AsObjectArray<mirror::Object>());
+  } else {
+    size_t size;
+    HprofBasicType t = SignatureToBasicTypeAndSize(
+        Primitive::Descriptor(klass->GetComponentType()->GetPrimitiveType()), &size);
+
+    // obj is a primitive array.
+    __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+
+    __ AddObjectId(obj);
+    __ AddU4(StackTraceSerialNumber(obj));
+    __ AddU4(length);
+    __ AddU1(t);
+
+    // Dump the raw, packed element values.
+    if (size == 1) {
+      __ AddU1List(reinterpret_cast<const uint8_t*>(obj->GetRawData(sizeof(uint8_t), 0)), length);
+    } else if (size == 2) {
+      __ AddU2List(reinterpret_cast<const uint16_t*>(obj->GetRawData(sizeof(uint16_t), 0)), length);
+    } else if (size == 4) {
+      __ AddU4List(reinterpret_cast<const uint32_t*>(obj->GetRawData(sizeof(uint32_t), 0)), length);
+    } else if (size == 8) {
+      __ AddU8List(reinterpret_cast<const uint64_t*>(obj->GetRawData(sizeof(uint64_t), 0)), length);
+    }
+  }
+}
+
+void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass,
+                                   EndianOutput* output) {
+  // obj is an instance object.
+  __ AddU1(HPROF_INSTANCE_DUMP);
+  __ AddObjectId(obj);
+  __ AddU4(StackTraceSerialNumber(obj));
+  __ AddClassId(LookupClassId(klass));
+
+  // Reserve some space for the length of the instance data, which we won't
+  // know until we're done writing it.
+  size_t size_patch_offset = output->Length();
+  __ AddU4(0x77777777);
+
+  // Write the instance data;  fields for this class, followed by super class fields,
+  // and so on. Don't write the klass or monitor fields of Object.class.
+  while (!klass->IsObjectClass()) {
+    int ifieldCount = klass->NumInstanceFields();
+    for (int i = 0; i < ifieldCount; ++i) {
+      mirror::ArtField* f = klass->GetInstanceField(i);
+      size_t size;
+      SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+      if (size == 1) {
+        __ AddU1(f->Get32(obj));
+      } else if (size == 2) {
+        __ AddU2(f->Get32(obj));
+      } else if (size == 4) {
+        __ AddU4(f->Get32(obj));
+      } else {
+        CHECK_EQ(size, 8U);
+        __ AddU8(f->Get64(obj));
+      }
+    }
+
+    klass = klass->GetSuperClass();
+  }
+
+  // Patch the instance field length.
+  __ UpdateU4(size_patch_offset, output->Length() - (size_patch_offset + 4));
+}
+
+void Hprof::VisitRoot(const mirror::Object* obj, const RootInfo& info, EndianOutput* output) {
   static const HprofHeapTag xlate[] = {
     HPROF_ROOT_UNKNOWN,
     HPROF_ROOT_JNI_GLOBAL,
@@ -1079,15 +1137,11 @@
     HPROF_ROOT_VM_INTERNAL,
     HPROF_ROOT_JNI_MONITOR,
   };
-  CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
-  if (obj == NULL) {
+  CHECK_LT(info.GetType(), sizeof(xlate) / sizeof(HprofHeapTag));
+  if (obj == nullptr) {
     return;
   }
-  gc_scan_state_ = xlate[type];
-  gc_thread_serial_number_ = thread_id;
-  MarkRootObject(obj, 0);
-  gc_scan_state_ = 0;
-  gc_thread_serial_number_ = 0;
+  MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId(), output);
 }
 
 // If "direct_to_ddms" is true, the other arguments are ignored, and data is
@@ -1095,14 +1149,23 @@
 // If "fd" is >= 0, the output will be written to that file descriptor.
 // Otherwise, "filename" is used to create an output file.
 void DumpHeap(const char* filename, int fd, bool direct_to_ddms) {
-  CHECK(filename != NULL);
+  CHECK(filename != nullptr);
 
+  Thread* self = Thread::Current();
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  if (heap->IsGcConcurrentAndMoving()) {
+    // Need to take a heap dump while GC isn't running. See the
+    // comment in Heap::VisitObjects().
+    heap->IncrementDisableMovingGC(self);
+  }
   Runtime::Current()->GetThreadList()->SuspendAll();
   Hprof hprof(filename, fd, direct_to_ddms);
   hprof.Dump();
   Runtime::Current()->GetThreadList()->ResumeAll();
+  if (heap->IsGcConcurrentAndMoving()) {
+    heap->DecrementDisableMovingGC(self);
+  }
 }
 
 }  // namespace hprof
-
 }  // namespace art
diff --git a/runtime/image.cc b/runtime/image.cc
index b83eeb1..269a07d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -111,7 +111,17 @@
 }
 
 mirror::ObjectArray<mirror::Object>* ImageHeader::GetImageRoots() const {
-  return reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_);
+  // Need a read barrier as it's not visited during root scan.
+  // Pass in the address of the local variable to the read barrier
+  // rather than image_roots_ because it won't move (asserted below)
+  // and it's a const member.
+  mirror::ObjectArray<mirror::Object>* image_roots =
+      reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_);
+  mirror::ObjectArray<mirror::Object>* result =
+      ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Object>, kWithReadBarrier, true>(
+          &image_roots);
+  DCHECK_EQ(image_roots, result);
+  return result;
 }
 
 }  // namespace art
diff --git a/runtime/image.h b/runtime/image.h
index 7e2b847..3c527b8 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -118,7 +118,8 @@
 
   mirror::Object* GetImageRoot(ImageRoot image_root) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  mirror::ObjectArray<mirror::Object>* GetImageRoots() const;
+  mirror::ObjectArray<mirror::Object>* GetImageRoots() const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void RelocateImage(off_t delta);
 
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 0d84a1e..aa2a6b5 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -242,15 +242,15 @@
   madvise(release_start, release_end - release_start, MADV_DONTNEED);
 }
 
-void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                        RootType root_type) {
+void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg,
+                                        const RootInfo& root_info) {
   for (auto ref : *this) {
     if (*ref == nullptr) {
       // Need to skip null entries to make it possible to do the
       // non-null check after the call back.
       continue;
     }
-    callback(ref, arg, tid, root_type);
+    callback(ref, arg, root_info);
     DCHECK(*ref != nullptr);
   }
 }
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fbd5714..7f7870a 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -31,6 +31,8 @@
 
 namespace art {
 
+class RootInfo;
+
 namespace mirror {
 class Object;
 }  // namespace mirror
@@ -316,7 +318,7 @@
     return IrtIterator(table_, Capacity(), Capacity());
   }
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint32_t GetSegmentState() const {
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 99ee597..1156cf5 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -43,6 +43,9 @@
 }
 
 TEST_F(IndirectReferenceTableTest, BasicTest) {
+  // This will lead to error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   ScopedObjectAccess soa(Thread::Current());
   static const size_t kTableInitial = 10;
   static const size_t kTableMax = 20;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 639b0f0..90115c3 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -54,9 +54,10 @@
 static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = true;
 
 static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
-  return instrumentation->InstallStubsForClass(klass);
+  instrumentation->InstallStubsForClass(klass);
+  return true;  // we visit all classes.
 }
 
 Instrumentation::Instrumentation()
@@ -73,40 +74,31 @@
       quick_alloc_entry_points_instrumentation_counter_(0) {
 }
 
-bool Instrumentation::InstallStubsForClass(mirror::Class* klass) {
-  for (size_t i = 0, e = klass->NumDirectMethods(); i < e; i++) {
-    InstallStubsForMethod(klass->GetDirectMethod(i));
+void Instrumentation::InstallStubsForClass(mirror::Class* klass) {
+  if (klass->IsErroneous()) {
+    // We can't execute code in a erroneous class: do nothing.
+  } else if (!klass->IsResolved()) {
+    // We need the class to be resolved to install/uninstall stubs. Otherwise its methods
+    // could not be initialized or linked with regards to class inheritance.
+  } else {
+    for (size_t i = 0, e = klass->NumDirectMethods(); i < e; i++) {
+      InstallStubsForMethod(klass->GetDirectMethod(i));
+    }
+    for (size_t i = 0, e = klass->NumVirtualMethods(); i < e; i++) {
+      InstallStubsForMethod(klass->GetVirtualMethod(i));
+    }
   }
-  for (size_t i = 0, e = klass->NumVirtualMethods(); i < e; i++) {
-    InstallStubsForMethod(klass->GetVirtualMethod(i));
-  }
-  return true;
 }
 
-static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code,
-                              const void* portable_code, bool have_portable_code)
+static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  method->SetEntryPointFromPortableCompiledCode(portable_code);
   method->SetEntryPointFromQuickCompiledCode(quick_code);
-  bool portable_enabled = method->IsPortableCompiled();
-  if (have_portable_code && !portable_enabled) {
-    method->SetIsPortableCompiled();
-  } else if (portable_enabled) {
-    method->ClearIsPortableCompiled();
-  }
   if (!method->IsResolutionMethod()) {
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     if (class_linker->IsQuickToInterpreterBridge(quick_code) ||
         (class_linker->IsQuickResolutionStub(quick_code) &&
          Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() &&
          !method->IsNative() && !method->IsProxyMethod())) {
-      if (kIsDebugBuild) {
-        if (quick_code == GetQuickToInterpreterBridge()) {
-          DCHECK(portable_code == GetPortableToInterpreterBridge());
-        } else if (class_linker->IsQuickResolutionStub(quick_code)) {
-          DCHECK(class_linker->IsPortableResolutionStub(portable_code));
-        }
-      }
       DCHECK(!method->IsNative()) << PrettyMethod(method);
       DCHECK(!method->IsProxyMethod()) << PrettyMethod(method);
       method->SetEntryPointFromInterpreter(art::artInterpreterToInterpreterBridge);
@@ -126,27 +118,21 @@
       method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;")) {
     return;
   }
-  const void* new_portable_code;
   const void* new_quick_code;
   bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_;
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   bool is_class_initialized = method->GetDeclaringClass()->IsInitialized();
-  bool have_portable_code = false;
   if (uninstall) {
     if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) {
-      new_portable_code = GetPortableToInterpreterBridge();
       new_quick_code = GetQuickToInterpreterBridge();
     } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
-      new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
       new_quick_code = class_linker->GetQuickOatCodeFor(method);
     } else {
-      new_portable_code = GetPortableResolutionStub();
       new_quick_code = GetQuickResolutionStub();
     }
   } else {  // !uninstall
     if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) &&
         !method->IsNative()) {
-      new_portable_code = GetPortableToInterpreterBridge();
       new_quick_code = GetQuickToInterpreterBridge();
     } else {
       // Do not overwrite resolution trampoline. When the trampoline initializes the method's
@@ -154,20 +140,17 @@
       // For more details, see ClassLinker::FixupStaticTrampolines.
       if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
         if (entry_exit_stubs_installed_) {
-          new_portable_code = GetPortableToInterpreterBridge();
           new_quick_code = GetQuickInstrumentationEntryPoint();
         } else {
-          new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
           new_quick_code = class_linker->GetQuickOatCodeFor(method);
           DCHECK(!class_linker->IsQuickToInterpreterBridge(new_quick_code));
         }
       } else {
-        new_portable_code = GetPortableResolutionStub();
         new_quick_code = GetQuickResolutionStub();
       }
     }
   }
-  UpdateEntrypoints(method, new_quick_code, new_portable_code, have_portable_code);
+  UpdateEntrypoints(method, new_quick_code);
 }
 
 // Places the instrumentation exit pc as the return PC for every quick frame. This also allows
@@ -195,7 +178,7 @@
         return true;  // Ignore upcalls.
       }
       if (GetCurrentQuickFrame() == NULL) {
-        bool interpreter_frame = !m->IsPortableCompiled();
+        bool interpreter_frame = true;
         InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, 0, GetFrameId(),
                                                         interpreter_frame);
         if (kVerboseInstrumentation) {
@@ -565,6 +548,7 @@
   }
   Thread* const self = Thread::Current();
   Runtime* runtime = Runtime::Current();
+  Locks::mutator_lock_->AssertExclusiveHeld(self);
   Locks::thread_list_lock_->AssertNotHeld(self);
   if (desired_level > 0) {
     if (require_interpreter) {
@@ -654,41 +638,27 @@
   }
 }
 
-void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
-                                        const void* portable_code, bool have_portable_code) {
-  const void* new_portable_code;
+void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code) {
+  DCHECK(method->GetDeclaringClass()->IsResolved());
   const void* new_quick_code;
-  bool new_have_portable_code;
   if (LIKELY(!instrumentation_stubs_installed_)) {
-    new_portable_code = portable_code;
     new_quick_code = quick_code;
-    new_have_portable_code = have_portable_code;
   } else {
     if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
-      new_portable_code = GetPortableToInterpreterBridge();
       new_quick_code = GetQuickToInterpreterBridge();
-      new_have_portable_code = false;
     } else {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       if (class_linker->IsQuickResolutionStub(quick_code) ||
           class_linker->IsQuickToInterpreterBridge(quick_code)) {
-        DCHECK(class_linker->IsPortableResolutionStub(portable_code) ||
-               class_linker->IsPortableToInterpreterBridge(portable_code));
-        new_portable_code = portable_code;
         new_quick_code = quick_code;
-        new_have_portable_code = have_portable_code;
       } else if (entry_exit_stubs_installed_) {
         new_quick_code = GetQuickInstrumentationEntryPoint();
-        new_portable_code = GetPortableToInterpreterBridge();
-        new_have_portable_code = false;
       } else {
-        new_portable_code = portable_code;
         new_quick_code = quick_code;
-        new_have_portable_code = have_portable_code;
       }
     }
   }
-  UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
+  UpdateEntrypoints(method, new_quick_code);
 }
 
 bool Instrumentation::AddDeoptimizedMethod(mirror::ArtMethod* method) {
@@ -701,12 +671,14 @@
     return false;
   }
   // Not found. Add it.
+  static_assert(!kMovingMethods, "Not safe if methods can move");
   int32_t hash_code = method->IdentityHashCode();
   deoptimized_methods_.insert(std::make_pair(hash_code, GcRoot<mirror::ArtMethod>(method)));
   return true;
 }
 
 bool Instrumentation::FindDeoptimizedMethod(mirror::ArtMethod* method) {
+  static_assert(!kMovingMethods, "Not safe if methods can move");
   int32_t hash_code = method->IdentityHashCode();
   auto range = deoptimized_methods_.equal_range(hash_code);
   for (auto it = range.first; it != range.second; ++it) {
@@ -730,6 +702,7 @@
 }
 
 bool Instrumentation::RemoveDeoptimizedMethod(mirror::ArtMethod* method) {
+  static_assert(!kMovingMethods, "Not safe if methods can move");
   int32_t hash_code = method->IdentityHashCode();
   auto range = deoptimized_methods_.equal_range(hash_code);
   for (auto it = range.first; it != range.second; ++it) {
@@ -761,8 +734,7 @@
         << " is already deoptimized";
   }
   if (!interpreter_stubs_installed_) {
-    UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(),
-                      false);
+    UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint());
 
     // Install instrumentation exit stub and instrumentation frames. We may already have installed
     // these previously so it will only cover the newly created frames.
@@ -793,12 +765,10 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     if (method->IsStatic() && !method->IsConstructor() &&
         !method->GetDeclaringClass()->IsInitialized()) {
-      UpdateEntrypoints(method, GetQuickResolutionStub(), GetPortableResolutionStub(), false);
+      UpdateEntrypoints(method, GetQuickResolutionStub());
     } else {
-      bool have_portable_code = false;
       const void* quick_code = class_linker->GetQuickOatCodeFor(method);
-      const void* portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-      UpdateEntrypoints(method, quick_code, portable_code, have_portable_code);
+      UpdateEntrypoints(method, quick_code);
     }
 
     // If there is no deoptimized method left, we can restore the stack of each thread.
@@ -1041,15 +1011,14 @@
   // back to an upcall.
   NthCallerVisitor visitor(self, 1, true);
   visitor.WalkStack(true);
-  bool deoptimize = (visitor.caller != NULL) &&
+  bool deoptimize = (visitor.caller != nullptr) &&
                     (interpreter_stubs_installed_ || IsDeoptimized(visitor.caller));
-  if (deoptimize && kVerboseInstrumentation) {
-    LOG(INFO) << "Deoptimizing into " << PrettyMethod(visitor.caller);
-  }
   if (deoptimize) {
     if (kVerboseInstrumentation) {
-      LOG(INFO) << "Deoptimizing from " << PrettyMethod(method)
-                << " result is " << std::hex << return_value.GetJ();
+      LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ",
+                                PrettyMethod(visitor.caller).c_str(),
+                                PrettyMethod(method).c_str(),
+                                return_value.GetJ()) << *self;
     }
     self->SetDeoptimizationReturnValue(return_value);
     return GetTwoWordSuccessValue(*return_pc,
@@ -1095,7 +1064,7 @@
     return;
   }
   for (auto pair : deoptimized_methods_) {
-    pair.second.VisitRoot(callback, arg, 0, kRootVMInternal);
+    pair.second.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   }
 }
 
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index effa9f7..cea0388 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -193,8 +193,7 @@
   void ResetQuickAllocEntryPoints() EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
 
   // Update the code of a method respecting any installed stubs.
-  void UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
-                         const void* portable_code, bool have_portable_code)
+  void UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Get the quick code for the given method. More efficient than asking the class linker as it
@@ -217,10 +216,6 @@
     return forced_interpret_only_;
   }
 
-  bool ShouldPortableCodeDeoptimize() const {
-    return instrumentation_stubs_installed_;
-  }
-
   bool AreExitStubsInstalled() const {
     return instrumentation_stubs_installed_;
   }
@@ -333,7 +328,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Call back for configure stubs.
-  bool InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void InstallStubsForMethod(mirror::ArtMethod* method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 7ecb58e..19bfc4e 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -60,7 +60,7 @@
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_strong_intern_roots_) {
       mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
-      root.VisitRoot(callback, arg, 0, kRootInternedString);
+      root.VisitRoot(callback, arg, RootInfo(kRootInternedString));
       mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
       if (new_ref != old_ref) {
         // The GC moved a root in the log. Need to search the strong interns and update the
@@ -192,6 +192,7 @@
     const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
     if (string_id != nullptr) {
       uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
+      // GetResolvedString() contains a RB.
       mirror::String* image_string = dex_cache->GetResolvedString(string_idx);
       if (image_string != NULL) {
         return image_string;
@@ -214,6 +215,13 @@
   allow_new_interns_ = false;
 }
 
+void InternTable::EnsureNewInternsDisallowed() {
+  // Lock and unlock once to ensure that no threads are still in the
+  // middle of adding new interns.
+  MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
+  CHECK(!allow_new_interns_);
+}
+
 mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
   if (s == nullptr) {
     return nullptr;
@@ -329,10 +337,10 @@
 
 void InternTable::Table::VisitRoots(RootCallback* callback, void* arg) {
   for (auto& intern : pre_zygote_table_) {
-    intern.VisitRoot(callback, arg, 0, kRootInternedString);
+    intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
   }
   for (auto& intern : post_zygote_table_) {
-    intern.VisitRoot(callback, arg, 0, kRootInternedString);
+    intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
   }
 }
 
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 371d3f7..2e31b7e 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -85,8 +85,9 @@
 
   void DumpForSigQuit(std::ostream& os) const;
 
-  void DisallowNewInterns() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DisallowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void AllowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void EnsureNewInternsDisallowed() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Adds all of the resolved image strings from the image space into the intern table. The
   // advantage of doing this is preventing expensive DexFile::FindStringId calls.
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index b04a18b..9d988e9 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -493,7 +493,23 @@
   while (shadow_frame != NULL) {
     self->SetTopOfShadowStack(shadow_frame);
     const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
-    value = Execute(self, code_item, *shadow_frame, value);
+    const uint32_t dex_pc = shadow_frame->GetDexPC();
+    uint32_t new_dex_pc;
+    if (UNLIKELY(self->IsExceptionPending())) {
+      const instrumentation::Instrumentation* const instrumentation =
+          Runtime::Current()->GetInstrumentation();
+      uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc,
+                                                                    instrumentation);
+      new_dex_pc = found_dex_pc;  // the dex pc of a matching catch handler
+                                  // or DexFile::kDexNoIndex if there is none.
+    } else {
+      const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+      new_dex_pc = dex_pc + instr->SizeInCodeUnits();  // the dex pc of the next instruction.
+    }
+    if (new_dex_pc != DexFile::kDexNoIndex) {
+      shadow_frame->SetDexPC(new_dex_pc);
+      value = Execute(self, code_item, *shadow_frame, value);
+    }
     ShadowFrame* old_frame = shadow_frame;
     shadow_frame = shadow_frame->GetLink();
     delete old_frame;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 3c7db85..a29558e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -145,6 +145,18 @@
     case Primitive::kPrimInt:
       shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset)));
       break;
+    case Primitive::kPrimBoolean:
+      shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldBoolean(field_offset)));
+      break;
+    case Primitive::kPrimByte:
+      shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldByte(field_offset)));
+      break;
+    case Primitive::kPrimChar:
+      shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldChar(field_offset)));
+      break;
+    case Primitive::kPrimShort:
+      shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetFieldShort(field_offset)));
+      break;
     case Primitive::kPrimLong:
       shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset)));
       break;
@@ -163,9 +175,13 @@
   template bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \
                                          uint16_t inst_data)
 
-EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt);    // iget-quick.
-EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong);   // iget-wide-quick.
-EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot);    // iget-object-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt);      // iget-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimBoolean);  // iget-boolean-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimByte);     // iget-byte-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimChar);     // iget-char-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimShort);    // iget-short-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong);     // iget-wide-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot);      // iget-object-quick.
 #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
 
 template<Primitive::Type field_type>
@@ -530,11 +546,13 @@
 
 void AbortTransaction(Thread* self, const char* fmt, ...) {
   CHECK(Runtime::Current()->IsActiveTransaction());
-  // Throw an exception so we can abort the transaction and undo every change.
+  // Constructs abort message.
   va_list args;
   va_start(args, fmt);
-  self->ThrowNewExceptionV(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;", fmt,
-                           args);
+  std::string abort_msg;
+  StringAppendV(&abort_msg, fmt, args);
+  // Throws an exception so we can abort the transaction and rollback every change.
+  Runtime::Current()->AbortTransactionAndThrowInternalError(self, abort_msg);
   va_end(args);
 }
 
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index c332a7b..e4b3247 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -148,7 +148,10 @@
   const void* const* currentHandlersTable;
   bool notified_method_entry_event = false;
   UPDATE_HANDLER_TABLE();
-  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing..
+  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing.
+    if (kIsDebugBuild) {
+      self->AssertNoPendingException();
+    }
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
       instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -236,6 +239,7 @@
 
   HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) {
     Throwable* exception = self->GetException(nullptr);
+    DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
     shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
     self->ClearException();
     ADVANCE(1);
@@ -543,7 +547,7 @@
   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(), shadow_frame.GetMethod(), length, self,
+        inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
         Runtime::Current()->GetHeap()->GetCurrentAllocator());
     if (UNLIKELY(obj == NULL)) {
       HANDLE_PENDING_EXCEPTION();
@@ -1249,6 +1253,30 @@
   }
   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);
@@ -2310,22 +2338,6 @@
     UnexpectedOpcode(inst, shadow_frame);
   HANDLE_INSTRUCTION_END();
 
-  HANDLE_INSTRUCTION_START(UNUSED_EF)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F0)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F1)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
-  HANDLE_INSTRUCTION_START(UNUSED_F2)
-    UnexpectedOpcode(inst, shadow_frame);
-  HANDLE_INSTRUCTION_END();
-
   HANDLE_INSTRUCTION_START(UNUSED_F3)
     UnexpectedOpcode(inst, shadow_frame);
   HANDLE_INSTRUCTION_END();
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index f9bbfa1..2f85587 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -69,7 +69,10 @@
   uint32_t dex_pc = shadow_frame.GetDexPC();
   bool notified_method_entry_event = false;
   const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
-  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing..
+  if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing.
+    if (kIsDebugBuild) {
+        self->AssertNoPendingException();
+    }
     if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
       instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
                                         shadow_frame.GetMethod(), 0);
@@ -161,6 +164,7 @@
       case Instruction::MOVE_EXCEPTION: {
         PREAMBLE();
         Throwable* exception = self->GetException(nullptr);
+        DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
         shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
         self->ClearException();
         inst = inst->Next_1xx();
@@ -455,7 +459,7 @@
         PREAMBLE();
         int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
         Object* obj = AllocArrayFromCode<do_access_check, true>(
-            inst->VRegC_22c(), shadow_frame.GetMethod(), length, self,
+            inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
             Runtime::Current()->GetHeap()->GetCurrentAllocator());
         if (UNLIKELY(obj == NULL)) {
           HANDLE_PENDING_EXCEPTION();
@@ -1128,6 +1132,30 @@
         POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
         break;
       }
+      case Instruction::IGET_BOOLEAN_QUICK: {
+        PREAMBLE();
+        bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
+      case Instruction::IGET_BYTE_QUICK: {
+        PREAMBLE();
+        bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
+      case Instruction::IGET_CHAR_QUICK: {
+        PREAMBLE();
+        bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
+      case Instruction::IGET_SHORT_QUICK: {
+        PREAMBLE();
+        bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+        break;
+      }
       case Instruction::SGET_BOOLEAN: {
         PREAMBLE();
         bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
@@ -2137,7 +2165,7 @@
         inst = inst->Next_2xx();
         break;
       case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
-      case Instruction::UNUSED_EF ... Instruction::UNUSED_FF:
+      case Instruction::UNUSED_F3 ... Instruction::UNUSED_FF:
       case Instruction::UNUSED_79:
       case Instruction::UNUSED_7A:
         UnexpectedOpcode(inst, shadow_frame);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 5d04fac..ea7c192 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -22,6 +22,7 @@
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "check_jni.h"
+#include "dex_file-inl.h"
 #include "fault_handler.h"
 #include "indirect_reference_table-inl.h"
 #include "mirror/art_method.h"
@@ -31,6 +32,7 @@
 #include "java_vm_ext.h"
 #include "parsed_options.h"
 #include "runtime-inl.h"
+#include "runtime_options.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
@@ -247,7 +249,7 @@
   }
 
  private:
-  AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibrarires> libraries_;
+  AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibraries> libraries_;
 };
 
 
@@ -356,14 +358,15 @@
   JII::AttachCurrentThreadAsDaemon
 };
 
-JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
+JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options)
     : runtime_(runtime),
       check_jni_abort_hook_(nullptr),
       check_jni_abort_hook_data_(nullptr),
       check_jni_(false),  // Initialized properly in the constructor body below.
-      force_copy_(options->force_copy_),
-      tracing_enabled_(!options->jni_trace_.empty() || VLOG_IS_ON(third_party_jni)),
-      trace_(options->jni_trace_),
+      force_copy_(runtime_options.Exists(RuntimeArgumentMap::JniOptsForceCopy)),
+      tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
+                       || VLOG_IS_ON(third_party_jni)),
+      trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
       globals_lock_("JNI global reference table lock"),
       globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
       libraries_(new Libraries),
@@ -373,9 +376,7 @@
       allow_new_weak_globals_(true),
       weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
   functions = unchecked_functions_;
-  if (options->check_jni_) {
-    SetCheckJniEnabled(true);
-  }
+  SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
 }
 
 JavaVMExt::~JavaVMExt() {
@@ -549,6 +550,13 @@
   weak_globals_add_condition_.Broadcast(self);
 }
 
+void JavaVMExt::EnsureNewWeakGlobalsDisallowed() {
+  // Lock and unlock once to ensure that no threads are still in the
+  // middle of adding new weak globals.
+  MutexLock mu(Thread::Current(), weak_globals_lock_);
+  CHECK(!allow_new_weak_globals_);
+}
+
 mirror::Object* JavaVMExt::DecodeGlobal(Thread* self, IndirectRef ref) {
   return globals_.SynchronizedGet(self, &globals_lock_, ref);
 }
@@ -763,10 +771,8 @@
 
 void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
   Thread* self = Thread::Current();
-  {
-    ReaderMutexLock mu(self, globals_lock_);
-    globals_.VisitRoots(callback, arg, 0, kRootJNIGlobal);
-  }
+  ReaderMutexLock mu(self, globals_lock_);
+  globals_.VisitRoots(callback, arg, RootInfo(kRootJNIGlobal));
   // The weak_globals table is visited by the GC itself (because it mutates the table).
 }
 
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 749b9fb..037fbe5 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -34,10 +34,11 @@
 class Libraries;
 class ParsedOptions;
 class Runtime;
+struct RuntimeArgumentMap;
 
 class JavaVMExt : public JavaVM {
  public:
-  JavaVMExt(Runtime* runtime, ParsedOptions* options);
+  JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
   ~JavaVMExt();
 
   bool ForceCopy() const {
@@ -104,9 +105,9 @@
 
   void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+  void DisallowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void EnsureNewWeakGlobalsDisallowed() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   jobject AddGlobalRef(Thread* self, mirror::Object* obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/java_vm_ext_test.cc b/runtime/java_vm_ext_test.cc
index 60c6a5c..2cbfa81 100644
--- a/runtime/java_vm_ext_test.cc
+++ b/runtime/java_vm_ext_test.cc
@@ -69,7 +69,12 @@
     } else {
       ok = vms_buf[0]->AttachCurrentThreadAsDaemon(&env, nullptr);
     }
-    EXPECT_EQ(gSmallStack ? JNI_ERR : JNI_OK, ok);
+    // TODO: Find a way to test with exact SMALL_STACK value, for which we would bail. The pthreads
+    //       spec says that the stack size argument is a lower bound, and bionic currently gives us
+    //       a chunk more on arm64.
+    if (!gSmallStack) {
+      EXPECT_EQ(JNI_OK, ok);
+    }
     if (ok == JNI_OK) {
       ok = vms_buf[0]->DetachCurrentThread();
       EXPECT_EQ(JNI_OK, ok);
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index 0c9451c..9f37998 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -27,6 +27,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
+#include <vector>
 
 struct iovec;
 
@@ -100,13 +101,15 @@
 std::ostream& operator<<(std::ostream& os, const JdwpTransportType& rhs);
 
 struct JdwpOptions {
-  JdwpTransportType transport;
-  bool server;
-  bool suspend;
-  std::string host;
-  uint16_t port;
+  JdwpTransportType transport = kJdwpTransportUnknown;
+  bool server = false;
+  bool suspend = false;
+  std::string host = "";
+  uint16_t port = static_cast<uint16_t>(-1);
 };
 
+bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs);
+
 struct JdwpEvent;
 class JdwpNetStateBase;
 struct ModBasket;
@@ -188,7 +191,7 @@
    * The VM has finished initializing.  Only called when the debugger is
    * connected at the time initialization completes.
    */
-  bool PostVMStart() LOCKS_EXCLUDED(event_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void PostVMStart() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
    * A location of interest has been reached.  This is used for breakpoints,
@@ -202,7 +205,7 @@
    *
    * "returnValue" is non-null for MethodExit events only.
    */
-  bool PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr, int eventFlags,
+  void PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr, int eventFlags,
                          const JValue* returnValue)
      LOCKS_EXCLUDED(event_list_lock_)
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -214,7 +217,7 @@
    * "fieldValue" is non-null for field modification events only.
    * "is_modification" is true for field modification, false for field access.
    */
-  bool PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field, mirror::Object* thisPtr,
+  void PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field, mirror::Object* thisPtr,
                       const JValue* fieldValue, bool is_modification)
       LOCKS_EXCLUDED(event_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -224,7 +227,7 @@
    *
    * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
    */
-  bool PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
+  void PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
                      const EventLocation* pCatchLoc, mirror::Object* thisPtr)
       LOCKS_EXCLUDED(event_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -232,14 +235,14 @@
   /*
    * A thread has started or stopped.
    */
-  bool PostThreadChange(Thread* thread, bool start)
+  void PostThreadChange(Thread* thread, bool start)
       LOCKS_EXCLUDED(event_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /*
    * Class has been prepared.
    */
-  bool PostClassPrepare(mirror::Class* klass)
+  void PostClassPrepare(mirror::Class* klass)
       LOCKS_EXCLUDED(event_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -251,6 +254,9 @@
   // Called if/when we realize we're talking to DDMS.
   void NotifyDdmsActive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+
+  void SetupChunkHeader(uint32_t type, size_t data_len, size_t header_size, uint8_t* out_header);
+
   /*
    * Send up a chunk of DDM data.
    */
@@ -307,15 +313,16 @@
   void SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy suspend_policy,
                                      ObjectId threadId)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void CleanupMatchList(JdwpEvent** match_list,
-                        size_t match_count)
+  void CleanupMatchList(const std::vector<JdwpEvent*>& match_list)
       EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void EventFinish(ExpandBuf* pReq);
-  void FindMatchingEvents(JdwpEventKind eventKind,
-                          const ModBasket& basket,
-                          JdwpEvent** match_list,
-                          size_t* pMatchCount)
+  bool FindMatchingEvents(JdwpEventKind eventKind, const ModBasket& basket,
+                          std::vector<JdwpEvent*>* match_list)
+      LOCKS_EXCLUDED(event_list_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void FindMatchingEventsLocked(JdwpEventKind eventKind, const ModBasket& basket,
+                                std::vector<JdwpEvent*>* match_list)
       EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void UnregisterEvent(JdwpEvent* pEvent)
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index df7d068..adc2912 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -123,7 +123,7 @@
 bool InitAdbTransport(JdwpState* state, const JdwpOptions*) {
   VLOG(jdwp) << "ADB transport startup";
   state->netState = new JdwpAdbState(state);
-  return (state->netState != NULL);
+  return (state->netState != nullptr);
 }
 
 /*
@@ -145,7 +145,7 @@
   iov.iov_len        = 1;
 
   msghdr msg;
-  msg.msg_name       = NULL;
+  msg.msg_name       = nullptr;
   msg.msg_namelen    = 0;
   msg.msg_iov        = &iov;
   msg.msg_iovlen     = 1;
@@ -352,7 +352,7 @@
        * re-issue the select.  We're currently using #2, as it's more
        * reliable than #1 and generally better than #3.  Wastes two fds.
        */
-      selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+      selCount = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr);
       if (selCount < 0) {
         if (errno == EINTR) {
           continue;
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 1e0a2d2..a8eaa26 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -172,9 +172,9 @@
  * not be added to the list, and an appropriate error will be returned.
  */
 JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) {
-  CHECK(pEvent != NULL);
-  CHECK(pEvent->prev == NULL);
-  CHECK(pEvent->next == NULL);
+  CHECK(pEvent != nullptr);
+  CHECK(pEvent->prev == nullptr);
+  CHECK(pEvent->next == nullptr);
 
   {
     /*
@@ -223,7 +223,7 @@
      * Add to list.
      */
     MutexLock mu(Thread::Current(), event_list_lock_);
-    if (event_list_ != NULL) {
+    if (event_list_ != nullptr) {
       pEvent->next = event_list_;
       event_list_->prev = pEvent;
     }
@@ -245,7 +245,7 @@
  * Grab the eventLock before calling here.
  */
 void JdwpState::UnregisterEvent(JdwpEvent* pEvent) {
-  if (pEvent->prev == NULL) {
+  if (pEvent->prev == nullptr) {
     /* head of the list */
     CHECK(event_list_ == pEvent);
 
@@ -254,11 +254,11 @@
     pEvent->prev->next = pEvent->next;
   }
 
-  if (pEvent->next != NULL) {
+  if (pEvent->next != nullptr) {
     pEvent->next->prev = pEvent->prev;
-    pEvent->next = NULL;
+    pEvent->next = nullptr;
   }
-  pEvent->prev = NULL;
+  pEvent->prev = nullptr;
 
   {
     /*
@@ -303,7 +303,7 @@
   }
 
   --event_list_size_;
-  CHECK(event_list_size_ != 0 || event_list_ == NULL);
+  CHECK(event_list_size_ != 0 || event_list_ == nullptr);
 }
 
 /*
@@ -343,7 +343,7 @@
   MutexLock mu(Thread::Current(), event_list_lock_);
 
   JdwpEvent* pEvent = event_list_;
-  while (pEvent != NULL) {
+  while (pEvent != nullptr) {
     JdwpEvent* pNextEvent = pEvent->next;
 
     UnregisterEvent(pEvent);
@@ -351,7 +351,7 @@
     pEvent = pNextEvent;
   }
 
-  event_list_ = NULL;
+  event_list_ = nullptr;
 }
 
 /*
@@ -372,13 +372,13 @@
  * Do not call this until the event has been removed from the list.
  */
 void EventFree(JdwpEvent* pEvent) {
-  if (pEvent == NULL) {
+  if (pEvent == nullptr) {
     return;
   }
 
   /* make sure it was removed from the list */
-  CHECK(pEvent->prev == NULL);
-  CHECK(pEvent->next == NULL);
+  CHECK(pEvent->prev == nullptr);
+  CHECK(pEvent->next == nullptr);
   /* want to check state->event_list_ != pEvent */
 
   /*
@@ -387,11 +387,11 @@
   for (int i = 0; i < pEvent->modCount; i++) {
     if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
       free(pEvent->mods[i].classMatch.classPattern);
-      pEvent->mods[i].classMatch.classPattern = NULL;
+      pEvent->mods[i].classMatch.classPattern = nullptr;
     }
     if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
       free(pEvent->mods[i].classExclude.classPattern);
-      pEvent->mods[i].classExclude.classPattern = NULL;
+      pEvent->mods[i].classExclude.classPattern = nullptr;
     }
   }
 
@@ -399,26 +399,12 @@
 }
 
 /*
- * Allocate storage for matching events.  To keep things simple we
- * use an array with enough storage for the entire list.
- *
- * The state->eventLock should be held before calling.
- */
-static JdwpEvent** AllocMatchList(size_t event_count) {
-  return new JdwpEvent*[event_count];
-}
-
-/*
  * Run through the list and remove any entries with an expired "count" mod
- * from the event list, then free the match list.
+ * from the event list.
  */
-void JdwpState::CleanupMatchList(JdwpEvent** match_list, size_t match_count) {
-  JdwpEvent** ppEvent = match_list;
-
-  while (match_count--) {
-    JdwpEvent* pEvent = *ppEvent;
-
-    for (int i = 0; i < pEvent->modCount; i++) {
+void JdwpState::CleanupMatchList(const std::vector<JdwpEvent*>& match_list) {
+  for (JdwpEvent* pEvent : match_list) {
+    for (int i = 0; i < pEvent->modCount; ++i) {
       if (pEvent->mods[i].modKind == MK_COUNT && pEvent->mods[i].count.count == 0) {
         VLOG(jdwp) << StringPrintf("##### Removing expired event (requestId=%#" PRIx32 ")",
                                    pEvent->requestId);
@@ -427,11 +413,7 @@
         break;
       }
     }
-
-    ppEvent++;
   }
-
-  delete[] match_list;
 }
 
 /*
@@ -536,40 +518,55 @@
 }
 
 /*
- * Find all events of type "eventKind" with mods that match up with the
- * rest of the arguments.
+ * Find all events of type "event_kind" with mods that match up with the
+ * rest of the arguments while holding the event list lock. This method
+ * is used by FindMatchingEvents below.
  *
- * Found events are appended to "match_list", and "*pMatchCount" is advanced,
- * so this may be called multiple times for grouped events.
+ * Found events are appended to "match_list" so this may be called multiple times for grouped
+ * events.
  *
  * DO NOT call this multiple times for the same eventKind, as Count mods are
  * decremented during the scan.
  */
-void JdwpState::FindMatchingEvents(JdwpEventKind eventKind, const ModBasket& basket,
-                                   JdwpEvent** match_list, size_t* pMatchCount) {
-  /* start after the existing entries */
-  match_list += *pMatchCount;
-
+void JdwpState::FindMatchingEventsLocked(JdwpEventKind event_kind, const ModBasket& basket,
+                                         std::vector<JdwpEvent*>* match_list) {
   for (JdwpEvent* pEvent = event_list_; pEvent != nullptr; pEvent = pEvent->next) {
-    if (pEvent->eventKind == eventKind && ModsMatch(pEvent, basket)) {
-      *match_list++ = pEvent;
-      (*pMatchCount)++;
+    if (pEvent->eventKind == event_kind && ModsMatch(pEvent, basket)) {
+      match_list->push_back(pEvent);
     }
   }
 }
 
 /*
+ * Find all events of type "event_kind" with mods that match up with the
+ * rest of the arguments and return true if at least one event matches,
+ * false otherwise.
+ *
+ * Found events are appended to "match_list" so this may be called multiple
+ * times for grouped events.
+ *
+ * DO NOT call this multiple times for the same eventKind, as Count mods are
+ * decremented during the scan.
+ */
+bool JdwpState::FindMatchingEvents(JdwpEventKind event_kind, const ModBasket& basket,
+                                   std::vector<JdwpEvent*>* match_list) {
+  MutexLock mu(Thread::Current(), event_list_lock_);
+  match_list->reserve(event_list_size_);
+  FindMatchingEventsLocked(event_kind, basket, match_list);
+  return !match_list->empty();
+}
+
+/*
  * Scan through the list of matches and determine the most severe
  * suspension policy.
  */
-static JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** match_list, int match_count) {
+static JdwpSuspendPolicy ScanSuspendPolicy(const std::vector<JdwpEvent*>& match_list) {
   JdwpSuspendPolicy policy = SP_NONE;
 
-  while (match_count--) {
-    if ((*match_list)->suspend_policy > policy) {
-      policy = (*match_list)->suspend_policy;
+  for (JdwpEvent* pEvent : match_list) {
+    if (pEvent->suspend_policy > policy) {
+      policy = pEvent->suspend_policy;
     }
-    match_list++;
   }
 
   return policy;
@@ -626,19 +623,18 @@
 
 void JdwpState::SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy suspend_policy,
                                               ObjectId threadId) {
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
   self->AssertThreadSuspensionIsAllowable();
+  CHECK(pReq != nullptr);
   /* send request and possibly suspend ourselves */
-  if (pReq != NULL) {
-    JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
-    self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend);
-    if (suspend_policy != SP_NONE) {
-      SetWaitForEventThread(threadId);
-    }
-    EventFinish(pReq);
-    SuspendByPolicy(suspend_policy, thread_self_id);
-    self->TransitionFromSuspendedToRunnable();
+  JDWP::ObjectId thread_self_id = Dbg::GetThreadSelfId();
+  self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSend);
+  if (suspend_policy != SP_NONE) {
+    SetWaitForEventThread(threadId);
   }
+  EventFinish(pReq);
+  SuspendByPolicy(suspend_policy, thread_self_id);
+  self->TransitionFromSuspendedToRunnable();
 }
 
 /*
@@ -729,10 +725,10 @@
   uint8_t* buf = expandBufGetBuffer(pReq);
 
   Set4BE(buf, expandBufGetLength(pReq));
-  Set4BE(buf+4, NextRequestSerial());
-  Set1(buf+8, 0);     /* flags */
-  Set1(buf+9, kJdwpEventCommandSet);
-  Set1(buf+10, kJdwpCompositeCommand);
+  Set4BE(buf + 4, NextRequestSerial());
+  Set1(buf + 8, 0);     /* flags */
+  Set1(buf + 9, kJdwpEventCommandSet);
+  Set1(buf + 10, kJdwpCompositeCommand);
 
   // Prevents from interleaving commands and events. Otherwise we could end up in sending an event
   // before sending the reply of the command being processed and would lead to bad synchronization
@@ -753,43 +749,30 @@
  * any application code has been executed".  The thread ID in the message
  * must be for the main thread.
  */
-bool JdwpState::PostVMStart() {
-  JdwpSuspendPolicy suspend_policy;
+void JdwpState::PostVMStart() {
+  JdwpSuspendPolicy suspend_policy = (options_->suspend) ? SP_ALL : SP_NONE;
   ObjectId threadId = Dbg::GetThreadSelfId();
 
-  if (options_->suspend) {
-    suspend_policy = SP_ALL;
-  } else {
-    suspend_policy = SP_NONE;
-  }
+  VLOG(jdwp) << "EVENT: " << EK_VM_START;
+  VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
 
   ExpandBuf* pReq = eventPrep();
-  {
-    MutexLock mu(Thread::Current(), event_list_lock_);  // probably don't need this here
-
-    VLOG(jdwp) << "EVENT: " << EK_VM_START;
-    VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
-
-    expandBufAdd1(pReq, suspend_policy);
-    expandBufAdd4BE(pReq, 1);
-
-    expandBufAdd1(pReq, EK_VM_START);
-    expandBufAdd4BE(pReq, 0);       /* requestId */
-    expandBufAdd8BE(pReq, threadId);
-  }
+  expandBufAdd1(pReq, suspend_policy);
+  expandBufAdd4BE(pReq, 1);
+  expandBufAdd1(pReq, EK_VM_START);
+  expandBufAdd4BE(pReq, 0);       /* requestId */
+  expandBufAddObjectId(pReq, threadId);
 
   Dbg::ManageDeoptimization();
 
   /* send request and possibly suspend ourselves */
   SendRequestAndPossiblySuspend(pReq, suspend_policy, threadId);
-
-  return true;
 }
 
-static void LogMatchingEventsAndThread(JdwpEvent** match_list, size_t match_count,
+static void LogMatchingEventsAndThread(const std::vector<JdwpEvent*> match_list,
                                        ObjectId thread_id)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  for (size_t i = 0; i < match_count; ++i) {
+  for (size_t i = 0, e = match_list.size(); i < e; ++i) {
     JdwpEvent* pEvent = match_list[i];
     VLOG(jdwp) << "EVENT #" << i << ": " << pEvent->eventKind
                << StringPrintf(" (requestId=%#" PRIx32 ")", pEvent->requestId);
@@ -831,7 +814,7 @@
  *  - Single-step to a line with a breakpoint.  Should get a single
  *    event message with both events in it.
  */
-bool JdwpState::PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr,
+void JdwpState::PostLocationEvent(const EventLocation* pLoc, mirror::Object* thisPtr,
                                   int eventFlags, const JValue* returnValue) {
   DCHECK(pLoc != nullptr);
   DCHECK(pLoc->method != nullptr);
@@ -852,7 +835,7 @@
    */
   if (basket.thread == GetDebugThread()) {
     VLOG(jdwp) << "Ignoring location event in JDWP thread";
-    return false;
+    return;
   }
 
   /*
@@ -866,73 +849,69 @@
    */
   if (InvokeInProgress()) {
     VLOG(jdwp) << "Not checking breakpoints during invoke (" << basket.className << ")";
-    return false;
+    return;
   }
 
-  size_t match_count = 0;
-  ExpandBuf* pReq = NULL;
-  JdwpSuspendPolicy suspend_policy = SP_NONE;
-  JdwpEvent** match_list = nullptr;
-  ObjectId thread_id = 0;
+  std::vector<JdwpEvent*> match_list;
   {
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      match_list = AllocMatchList(event_list_size_);
-      if ((eventFlags & Dbg::kBreakpoint) != 0) {
-        FindMatchingEvents(EK_BREAKPOINT, basket, match_list, &match_count);
-      }
-      if ((eventFlags & Dbg::kSingleStep) != 0) {
-        FindMatchingEvents(EK_SINGLE_STEP, basket, match_list, &match_count);
-      }
-      if ((eventFlags & Dbg::kMethodEntry) != 0) {
-        FindMatchingEvents(EK_METHOD_ENTRY, basket, match_list, &match_count);
-      }
-      if ((eventFlags & Dbg::kMethodExit) != 0) {
-        FindMatchingEvents(EK_METHOD_EXIT, basket, match_list, &match_count);
-        FindMatchingEvents(EK_METHOD_EXIT_WITH_RETURN_VALUE, basket, match_list, &match_count);
-      }
+    // We use the locked version because we have multiple possible match events.
+    MutexLock mu(Thread::Current(), event_list_lock_);
+    match_list.reserve(event_list_size_);
+    if ((eventFlags & Dbg::kBreakpoint) != 0) {
+      FindMatchingEventsLocked(EK_BREAKPOINT, basket, &match_list);
     }
-    if (match_count != 0) {
-      suspend_policy = scanSuspendPolicy(match_list, match_count);
-
-      thread_id = Dbg::GetThreadId(basket.thread);
-      JDWP::JdwpLocation jdwp_location;
-      SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
-
-      if (VLOG_IS_ON(jdwp)) {
-        LogMatchingEventsAndThread(match_list, match_count, thread_id);
-        VLOG(jdwp) << "  location=" << jdwp_location;
-        VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
-      }
-
-      pReq = eventPrep();
-      expandBufAdd1(pReq, suspend_policy);
-      expandBufAdd4BE(pReq, match_count);
-
-      for (size_t i = 0; i < match_count; i++) {
-        expandBufAdd1(pReq, match_list[i]->eventKind);
-        expandBufAdd4BE(pReq, match_list[i]->requestId);
-        expandBufAdd8BE(pReq, thread_id);
-        expandBufAddLocation(pReq, jdwp_location);
-        if (match_list[i]->eventKind == EK_METHOD_EXIT_WITH_RETURN_VALUE) {
-          Dbg::OutputMethodReturnValue(jdwp_location.method_id, returnValue, pReq);
-        }
-      }
+    if ((eventFlags & Dbg::kSingleStep) != 0) {
+      FindMatchingEventsLocked(EK_SINGLE_STEP, basket, &match_list);
     }
-
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      CleanupMatchList(match_list, match_count);
+    if ((eventFlags & Dbg::kMethodEntry) != 0) {
+      FindMatchingEventsLocked(EK_METHOD_ENTRY, basket, &match_list);
     }
+    if ((eventFlags & Dbg::kMethodExit) != 0) {
+      FindMatchingEventsLocked(EK_METHOD_EXIT, basket, &match_list);
+      FindMatchingEventsLocked(EK_METHOD_EXIT_WITH_RETURN_VALUE, basket, &match_list);
+    }
+  }
+  if (match_list.empty()) {
+    // No matching event.
+    return;
+  }
+  JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+
+  ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+  JDWP::JdwpLocation jdwp_location;
+  SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
+
+  if (VLOG_IS_ON(jdwp)) {
+    LogMatchingEventsAndThread(match_list, thread_id);
+    VLOG(jdwp) << "  location=" << jdwp_location;
+    VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
+  }
+
+  ExpandBuf* pReq = eventPrep();
+  expandBufAdd1(pReq, suspend_policy);
+  expandBufAdd4BE(pReq, match_list.size());
+
+  for (const JdwpEvent* pEvent : match_list) {
+    expandBufAdd1(pReq, pEvent->eventKind);
+    expandBufAdd4BE(pReq, pEvent->requestId);
+    expandBufAddObjectId(pReq, thread_id);
+    expandBufAddLocation(pReq, jdwp_location);
+    if (pEvent->eventKind == EK_METHOD_EXIT_WITH_RETURN_VALUE) {
+      Dbg::OutputMethodReturnValue(jdwp_location.method_id, returnValue, pReq);
+    }
+  }
+
+  {
+    MutexLock mu(Thread::Current(), event_list_lock_);
+    CleanupMatchList(match_list);
   }
 
   Dbg::ManageDeoptimization();
 
   SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
-  return match_count != 0;
 }
 
-bool JdwpState::PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field,
+void JdwpState::PostFieldEvent(const EventLocation* pLoc, mirror::ArtField* field,
                                mirror::Object* this_object, const JValue* fieldValue,
                                bool is_modification) {
   DCHECK(pLoc != nullptr);
@@ -950,86 +929,73 @@
 
   if (InvokeInProgress()) {
     VLOG(jdwp) << "Not posting field event during invoke";
-    return false;
+    return;
   }
 
-  size_t match_count = 0;
-  ExpandBuf* pReq = NULL;
-  JdwpSuspendPolicy suspend_policy = SP_NONE;
-  JdwpEvent** match_list = nullptr;
-  ObjectId thread_id = 0;
+  std::vector<JdwpEvent*> match_list;
+  const JdwpEventKind match_kind = (is_modification) ? EK_FIELD_MODIFICATION : EK_FIELD_ACCESS;
+  if (!FindMatchingEvents(match_kind, basket, &match_list)) {
+    // No matching event.
+    return;
+  }
+
+  JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+  ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+  ObjectRegistry* registry = Dbg::GetObjectRegistry();
+  ObjectId instance_id = registry->Add(basket.thisPtr);
+  RefTypeId field_type_id = registry->AddRefType(field->GetDeclaringClass());
+  FieldId field_id = Dbg::ToFieldId(field);
+  JDWP::JdwpLocation jdwp_location;
+  SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
+
+  if (VLOG_IS_ON(jdwp)) {
+    LogMatchingEventsAndThread(match_list, thread_id);
+    VLOG(jdwp) << "  location=" << jdwp_location;
+    VLOG(jdwp) << StringPrintf("  this=%#" PRIx64, instance_id);
+    VLOG(jdwp) << StringPrintf("  type=%#" PRIx64, field_type_id) << " "
+        << Dbg::GetClassName(field_id);
+    VLOG(jdwp) << StringPrintf("  field=%#" PRIx32, field_id) << " "
+        << Dbg::GetFieldName(field_id);
+    VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
+  }
+
+  ExpandBuf* pReq = eventPrep();
+  expandBufAdd1(pReq, suspend_policy);
+  expandBufAdd4BE(pReq, match_list.size());
+
+  // Get field's reference type tag.
+  JDWP::JdwpTypeTag type_tag = Dbg::GetTypeTag(field->GetDeclaringClass());
+
+  // Get instance type tag.
+  uint8_t tag;
   {
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      match_list = AllocMatchList(event_list_size_);
-      if (is_modification) {
-        FindMatchingEvents(EK_FIELD_MODIFICATION, basket, match_list, &match_count);
-      } else {
-        FindMatchingEvents(EK_FIELD_ACCESS, basket, match_list, &match_count);
-      }
+    ScopedObjectAccessUnchecked soa(Thread::Current());
+    tag = Dbg::TagFromObject(soa, basket.thisPtr);
+  }
+
+  for (const JdwpEvent* pEvent : match_list) {
+    expandBufAdd1(pReq, pEvent->eventKind);
+    expandBufAdd4BE(pReq, pEvent->requestId);
+    expandBufAddObjectId(pReq, thread_id);
+    expandBufAddLocation(pReq, jdwp_location);
+    expandBufAdd1(pReq, type_tag);
+    expandBufAddRefTypeId(pReq, field_type_id);
+    expandBufAddFieldId(pReq, field_id);
+    expandBufAdd1(pReq, tag);
+    expandBufAddObjectId(pReq, instance_id);
+    if (is_modification) {
+      Dbg::OutputFieldValue(field_id, fieldValue, pReq);
     }
-    if (match_count != 0) {
-      suspend_policy = scanSuspendPolicy(match_list, match_count);
+  }
 
-      thread_id = Dbg::GetThreadId(basket.thread);
-      ObjectRegistry* registry = Dbg::GetObjectRegistry();
-      ObjectId instance_id = registry->Add(basket.thisPtr);
-      RefTypeId field_type_id = registry->AddRefType(field->GetDeclaringClass());
-      FieldId field_id = Dbg::ToFieldId(field);
-      JDWP::JdwpLocation jdwp_location;
-      SetJdwpLocationFromEventLocation(pLoc, &jdwp_location);
-
-      if (VLOG_IS_ON(jdwp)) {
-        LogMatchingEventsAndThread(match_list, match_count, thread_id);
-        VLOG(jdwp) << "  location=" << jdwp_location;
-        VLOG(jdwp) << StringPrintf("  this=%#" PRIx64, instance_id);
-        VLOG(jdwp) << StringPrintf("  type=%#" PRIx64, field_type_id) << " "
-                   << Dbg::GetClassName(field_id);
-        VLOG(jdwp) << StringPrintf("  field=%#" PRIx32, field_id) << " "
-                   << Dbg::GetFieldName(field_id);
-        VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
-      }
-
-      pReq = eventPrep();
-      expandBufAdd1(pReq, suspend_policy);
-      expandBufAdd4BE(pReq, match_count);
-
-      // Get field's reference type tag.
-      JDWP::JdwpTypeTag type_tag = Dbg::GetTypeTag(field->GetDeclaringClass());
-
-      // Get instance type tag.
-      uint8_t tag;
-      {
-        ScopedObjectAccessUnchecked soa(Thread::Current());
-        tag = Dbg::TagFromObject(soa, basket.thisPtr);
-      }
-
-      for (size_t i = 0; i < match_count; i++) {
-        expandBufAdd1(pReq, match_list[i]->eventKind);
-        expandBufAdd4BE(pReq, match_list[i]->requestId);
-        expandBufAdd8BE(pReq, thread_id);
-        expandBufAddLocation(pReq, jdwp_location);
-        expandBufAdd1(pReq, type_tag);
-        expandBufAddRefTypeId(pReq, field_type_id);
-        expandBufAddFieldId(pReq, field_id);
-        expandBufAdd1(pReq, tag);
-        expandBufAddObjectId(pReq, instance_id);
-        if (is_modification) {
-          Dbg::OutputFieldValue(field_id, fieldValue, pReq);
-        }
-      }
-    }
-
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      CleanupMatchList(match_list, match_count);
-    }
+  {
+    MutexLock mu(Thread::Current(), event_list_lock_);
+    CleanupMatchList(match_list);
   }
 
   Dbg::ManageDeoptimization();
 
   SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
-  return match_count != 0;
 }
 
 /*
@@ -1038,7 +1004,7 @@
  * Valid mods:
  *  Count, ThreadOnly
  */
-bool JdwpState::PostThreadChange(Thread* thread, bool start) {
+void JdwpState::PostThreadChange(Thread* thread, bool start) {
   CHECK_EQ(thread, Thread::Current());
 
   /*
@@ -1046,61 +1012,53 @@
    */
   if (InvokeInProgress()) {
     LOG(WARNING) << "Not posting thread change during invoke";
-    return false;
+    return;
+  }
+
+  // We need the java.lang.Thread object associated to the starting/ending
+  // thread to get its JDWP id. Therefore we can't report event if there
+  // is no Java peer. This happens when the runtime shuts down and re-attaches
+  // the current thread without creating a Java peer.
+  if (thread->GetPeer() == nullptr) {
+    return;
   }
 
   ModBasket basket;
   basket.thread = thread;
 
-  ExpandBuf* pReq = NULL;
-  JdwpSuspendPolicy suspend_policy = SP_NONE;
-  JdwpEvent** match_list = nullptr;
-  size_t match_count = 0;
-  ObjectId thread_id = 0;
+  std::vector<JdwpEvent*> match_list;
+  const JdwpEventKind match_kind = (start) ? EK_THREAD_START : EK_THREAD_DEATH;
+  if (!FindMatchingEvents(match_kind, basket, &match_list)) {
+    // No matching event.
+    return;
+  }
+
+  JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+  ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+
+  if (VLOG_IS_ON(jdwp)) {
+    LogMatchingEventsAndThread(match_list, thread_id);
+    VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
+  }
+
+  ExpandBuf* pReq = eventPrep();
+  expandBufAdd1(pReq, suspend_policy);
+  expandBufAdd4BE(pReq, match_list.size());
+
+  for (const JdwpEvent* pEvent : match_list) {
+    expandBufAdd1(pReq, pEvent->eventKind);
+    expandBufAdd4BE(pReq, pEvent->requestId);
+    expandBufAdd8BE(pReq, thread_id);
+  }
+
   {
-    {
-      // Don't allow the list to be updated while we scan it.
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      match_list = AllocMatchList(event_list_size_);
-      if (start) {
-        FindMatchingEvents(EK_THREAD_START, basket, match_list, &match_count);
-      } else {
-        FindMatchingEvents(EK_THREAD_DEATH, basket, match_list, &match_count);
-      }
-    }
-
-    if (match_count != 0) {
-      suspend_policy = scanSuspendPolicy(match_list, match_count);
-
-      thread_id = Dbg::GetThreadId(basket.thread);
-
-      if (VLOG_IS_ON(jdwp)) {
-        LogMatchingEventsAndThread(match_list, match_count, thread_id);
-        VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
-      }
-
-      pReq = eventPrep();
-      expandBufAdd1(pReq, suspend_policy);
-      expandBufAdd4BE(pReq, match_count);
-
-      for (size_t i = 0; i < match_count; i++) {
-        expandBufAdd1(pReq, match_list[i]->eventKind);
-        expandBufAdd4BE(pReq, match_list[i]->requestId);
-        expandBufAdd8BE(pReq, thread_id);
-      }
-    }
-
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      CleanupMatchList(match_list, match_count);
-    }
+    MutexLock mu(Thread::Current(), event_list_lock_);
+    CleanupMatchList(match_list);
   }
 
   Dbg::ManageDeoptimization();
 
   SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
-
-  return match_count != 0;
 }
 
 /*
@@ -1132,7 +1090,7 @@
  * because there's a pretty good chance that we're not going to send it
  * up the debugger.
  */
-bool JdwpState::PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
+void JdwpState::PostException(const EventLocation* pThrowLoc, mirror::Throwable* exception_object,
                               const EventLocation* pCatchLoc, mirror::Object* thisPtr) {
   DCHECK(exception_object != nullptr);
   DCHECK(pThrowLoc != nullptr);
@@ -1159,72 +1117,61 @@
   /* don't try to post an exception caused by the debugger */
   if (InvokeInProgress()) {
     VLOG(jdwp) << "Not posting exception hit during invoke (" << basket.className << ")";
-    return false;
+    return;
   }
 
-  size_t match_count = 0;
-  ExpandBuf* pReq = NULL;
-  JdwpSuspendPolicy suspend_policy = SP_NONE;
-  JdwpEvent** match_list = nullptr;
-  ObjectId thread_id = 0;
+  std::vector<JdwpEvent*> match_list;
+  if (!FindMatchingEvents(EK_EXCEPTION, basket, &match_list)) {
+    // No matching event.
+    return;
+  }
+
+  JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+  ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+  ObjectRegistry* registry = Dbg::GetObjectRegistry();
+  ObjectId exceptionId = registry->Add(exception_object);
+  JDWP::JdwpLocation jdwp_throw_location;
+  JDWP::JdwpLocation jdwp_catch_location;
+  SetJdwpLocationFromEventLocation(pThrowLoc, &jdwp_throw_location);
+  SetJdwpLocationFromEventLocation(pCatchLoc, &jdwp_catch_location);
+
+  if (VLOG_IS_ON(jdwp)) {
+    std::string exceptionClassName(PrettyDescriptor(exception_object->GetClass()));
+
+    LogMatchingEventsAndThread(match_list, thread_id);
+    VLOG(jdwp) << "  throwLocation=" << jdwp_throw_location;
+    if (jdwp_catch_location.class_id == 0) {
+      VLOG(jdwp) << "  catchLocation=uncaught";
+    } else {
+      VLOG(jdwp) << "  catchLocation=" << jdwp_catch_location;
+    }
+    VLOG(jdwp) << StringPrintf("  exception=%#" PRIx64, exceptionId) << " "
+        << exceptionClassName;
+    VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
+  }
+
+  ExpandBuf* pReq = eventPrep();
+  expandBufAdd1(pReq, suspend_policy);
+  expandBufAdd4BE(pReq, match_list.size());
+
+  for (const JdwpEvent* pEvent : match_list) {
+    expandBufAdd1(pReq, pEvent->eventKind);
+    expandBufAdd4BE(pReq, pEvent->requestId);
+    expandBufAddObjectId(pReq, thread_id);
+    expandBufAddLocation(pReq, jdwp_throw_location);
+    expandBufAdd1(pReq, JT_OBJECT);
+    expandBufAddObjectId(pReq, exceptionId);
+    expandBufAddLocation(pReq, jdwp_catch_location);
+  }
+
   {
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      match_list = AllocMatchList(event_list_size_);
-      FindMatchingEvents(EK_EXCEPTION, basket, match_list, &match_count);
-    }
-    if (match_count != 0) {
-      suspend_policy = scanSuspendPolicy(match_list, match_count);
-
-      thread_id = Dbg::GetThreadId(basket.thread);
-      ObjectRegistry* registry = Dbg::GetObjectRegistry();
-      ObjectId exceptionId = registry->Add(exception_object);
-      JDWP::JdwpLocation jdwp_throw_location;
-      JDWP::JdwpLocation jdwp_catch_location;
-      SetJdwpLocationFromEventLocation(pThrowLoc, &jdwp_throw_location);
-      SetJdwpLocationFromEventLocation(pCatchLoc, &jdwp_catch_location);
-
-      if (VLOG_IS_ON(jdwp)) {
-        std::string exceptionClassName(PrettyDescriptor(exception_object->GetClass()));
-
-        LogMatchingEventsAndThread(match_list, match_count, thread_id);
-        VLOG(jdwp) << "  throwLocation=" << jdwp_throw_location;
-        if (jdwp_catch_location.class_id == 0) {
-          VLOG(jdwp) << "  catchLocation=uncaught";
-        } else {
-          VLOG(jdwp) << "  catchLocation=" << jdwp_catch_location;
-        }
-        VLOG(jdwp) << StringPrintf("  exception=%#" PRIx64, exceptionId) << " "
-                   << exceptionClassName;
-        VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
-      }
-
-      pReq = eventPrep();
-      expandBufAdd1(pReq, suspend_policy);
-      expandBufAdd4BE(pReq, match_count);
-
-      for (size_t i = 0; i < match_count; i++) {
-        expandBufAdd1(pReq, match_list[i]->eventKind);
-        expandBufAdd4BE(pReq, match_list[i]->requestId);
-        expandBufAdd8BE(pReq, thread_id);
-        expandBufAddLocation(pReq, jdwp_throw_location);
-        expandBufAdd1(pReq, JT_OBJECT);
-        expandBufAdd8BE(pReq, exceptionId);
-        expandBufAddLocation(pReq, jdwp_catch_location);
-      }
-    }
-
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      CleanupMatchList(match_list, match_count);
-    }
+    MutexLock mu(Thread::Current(), event_list_lock_);
+    CleanupMatchList(match_list);
   }
 
   Dbg::ManageDeoptimization();
 
   SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
-
-  return match_count != 0;
 }
 
 /*
@@ -1233,7 +1180,7 @@
  * Valid mods:
  *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
  */
-bool JdwpState::PostClassPrepare(mirror::Class* klass) {
+void JdwpState::PostClassPrepare(mirror::Class* klass) {
   DCHECK(klass != nullptr);
 
   ModBasket basket;
@@ -1244,80 +1191,85 @@
   /* suppress class prep caused by debugger */
   if (InvokeInProgress()) {
     VLOG(jdwp) << "Not posting class prep caused by invoke (" << basket.className << ")";
-    return false;
+    return;
   }
 
-  ExpandBuf* pReq = NULL;
-  JdwpSuspendPolicy suspend_policy = SP_NONE;
-  JdwpEvent** match_list = nullptr;
-  size_t match_count = 0;
-  ObjectId thread_id = 0;
+  std::vector<JdwpEvent*> match_list;
+  if (!FindMatchingEvents(EK_CLASS_PREPARE, basket, &match_list)) {
+    // No matching event.
+    return;
+  }
+
+  JdwpSuspendPolicy suspend_policy = ScanSuspendPolicy(match_list);
+  ObjectId thread_id = Dbg::GetThreadId(basket.thread);
+  ObjectRegistry* registry = Dbg::GetObjectRegistry();
+  RefTypeId class_id = registry->AddRefType(basket.locationClass);
+
+  // OLD-TODO - we currently always send both "verified" and "prepared" since
+  // debuggers seem to like that.  There might be some advantage to honesty,
+  // since the class may not yet be verified.
+  int status = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
+  JDWP::JdwpTypeTag tag = Dbg::GetTypeTag(basket.locationClass);
+  std::string temp;
+  std::string signature(basket.locationClass->GetDescriptor(&temp));
+
+  if (VLOG_IS_ON(jdwp)) {
+    LogMatchingEventsAndThread(match_list, thread_id);
+    VLOG(jdwp) << StringPrintf("  type=%#" PRIx64, class_id) << " " << signature;
+    VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
+  }
+
+  if (thread_id == debug_thread_id_) {
+    /*
+     * JDWP says that, for a class prep in the debugger thread, we
+     * should set thread to null and if any threads were supposed
+     * to be suspended then we suspend all other threads.
+     */
+    VLOG(jdwp) << "  NOTE: class prepare in debugger thread!";
+    thread_id = 0;
+    if (suspend_policy == SP_EVENT_THREAD) {
+      suspend_policy = SP_ALL;
+    }
+  }
+
+  ExpandBuf* pReq = eventPrep();
+  expandBufAdd1(pReq, suspend_policy);
+  expandBufAdd4BE(pReq, match_list.size());
+
+  for (const JdwpEvent* pEvent : match_list) {
+    expandBufAdd1(pReq, pEvent->eventKind);
+    expandBufAdd4BE(pReq, pEvent->requestId);
+    expandBufAddObjectId(pReq, thread_id);
+    expandBufAdd1(pReq, tag);
+    expandBufAddRefTypeId(pReq, class_id);
+    expandBufAddUtf8String(pReq, signature);
+    expandBufAdd4BE(pReq, status);
+  }
+
   {
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      match_list = AllocMatchList(event_list_size_);
-      FindMatchingEvents(EK_CLASS_PREPARE, basket, match_list, &match_count);
-    }
-    if (match_count != 0) {
-      suspend_policy = scanSuspendPolicy(match_list, match_count);
-
-      thread_id = Dbg::GetThreadId(basket.thread);
-      ObjectRegistry* registry = Dbg::GetObjectRegistry();
-      RefTypeId class_id = registry->AddRefType(basket.locationClass);
-
-      // OLD-TODO - we currently always send both "verified" and "prepared" since
-      // debuggers seem to like that.  There might be some advantage to honesty,
-      // since the class may not yet be verified.
-      int status = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
-      JDWP::JdwpTypeTag tag = Dbg::GetTypeTag(basket.locationClass);
-      std::string temp;
-      std::string signature(basket.locationClass->GetDescriptor(&temp));
-
-      if (VLOG_IS_ON(jdwp)) {
-        LogMatchingEventsAndThread(match_list, match_count, thread_id);
-        VLOG(jdwp) << StringPrintf("  type=%#" PRIx64, class_id) << " " << signature;
-        VLOG(jdwp) << "  suspend_policy=" << suspend_policy;
-      }
-
-      if (thread_id == debug_thread_id_) {
-        /*
-         * JDWP says that, for a class prep in the debugger thread, we
-         * should set thread to null and if any threads were supposed
-         * to be suspended then we suspend all other threads.
-         */
-        VLOG(jdwp) << "  NOTE: class prepare in debugger thread!";
-        thread_id = 0;
-        if (suspend_policy == SP_EVENT_THREAD) {
-          suspend_policy = SP_ALL;
-        }
-      }
-
-      pReq = eventPrep();
-      expandBufAdd1(pReq, suspend_policy);
-      expandBufAdd4BE(pReq, match_count);
-
-      for (size_t i = 0; i < match_count; i++) {
-        expandBufAdd1(pReq, match_list[i]->eventKind);
-        expandBufAdd4BE(pReq, match_list[i]->requestId);
-        expandBufAdd8BE(pReq, thread_id);
-        expandBufAdd1(pReq, tag);
-        expandBufAdd8BE(pReq, class_id);
-        expandBufAddUtf8String(pReq, signature);
-        expandBufAdd4BE(pReq, status);
-      }
-    }
-
-    {
-      MutexLock mu(Thread::Current(), event_list_lock_);
-      CleanupMatchList(match_list, match_count);
-    }
+    MutexLock mu(Thread::Current(), event_list_lock_);
+    CleanupMatchList(match_list);
   }
 
   Dbg::ManageDeoptimization();
 
   SendRequestAndPossiblySuspend(pReq, suspend_policy, thread_id);
+}
 
-  return match_count != 0;
+/*
+ * Setup the header for a chunk of DDM data.
+ */
+void JdwpState::SetupChunkHeader(uint32_t type, size_t data_len, size_t header_size,
+                                 uint8_t* out_header) {
+  CHECK_EQ(header_size, static_cast<size_t>(kJDWPHeaderLen + 8));
+  /* form the header (JDWP plus DDMS) */
+  Set4BE(out_header, header_size + data_len);
+  Set4BE(out_header + 4, NextRequestSerial());
+  Set1(out_header + 8, 0);     /* flags */
+  Set1(out_header + 9, kJDWPDdmCmdSet);
+  Set1(out_header + 10, kJDWPDdmCmd);
+  Set4BE(out_header + 11, type);
+  Set4BE(out_header + 15, data_len);
 }
 
 /*
@@ -1328,10 +1280,10 @@
  * the fun event token gymnastics.
  */
 void JdwpState::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) {
-  uint8_t header[kJDWPHeaderLen + 8];
+  uint8_t header[kJDWPHeaderLen + 8] = { 0 };
   size_t dataLen = 0;
 
-  CHECK(iov != NULL);
+  CHECK(iov != nullptr);
   CHECK_GT(iov_count, 0);
   CHECK_LT(iov_count, 10);
 
@@ -1346,14 +1298,7 @@
     dataLen += iov[i].iov_len;
   }
 
-  /* form the header (JDWP plus DDMS) */
-  Set4BE(header, sizeof(header) + dataLen);
-  Set4BE(header+4, NextRequestSerial());
-  Set1(header+8, 0);     /* flags */
-  Set1(header+9, kJDWPDdmCmdSet);
-  Set1(header+10, kJDWPDdmCmd);
-  Set4BE(header+11, type);
-  Set4BE(header+15, dataLen);
+  SetupChunkHeader(type, dataLen, sizeof(header), header);
 
   wrapiov[0].iov_base = header;
   wrapiov[0].iov_len = sizeof(header);
@@ -1364,7 +1309,7 @@
   bool safe_to_release_mutator_lock_over_send = !Locks::mutator_lock_->IsExclusiveHeld(self);
   if (safe_to_release_mutator_lock_over_send) {
     for (size_t i = 0; i < kMutatorLock; ++i) {
-      if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) {
+      if (self->GetHeldMutex(static_cast<LockLevel>(i)) != nullptr) {
         safe_to_release_mutator_lock_over_send = false;
         break;
       }
diff --git a/runtime/jdwp/jdwp_expand_buf.cc b/runtime/jdwp/jdwp_expand_buf.cc
index 0a64f28..cc85cdd 100644
--- a/runtime/jdwp/jdwp_expand_buf.cc
+++ b/runtime/jdwp/jdwp_expand_buf.cc
@@ -57,7 +57,7 @@
  * Free a JdwpBuf and associated storage.
  */
 void expandBufFree(ExpandBuf* pBuf) {
-  if (pBuf == NULL) {
+  if (pBuf == nullptr) {
     return;
   }
 
@@ -93,7 +93,7 @@
   }
 
   uint8_t* newPtr = reinterpret_cast<uint8_t*>(realloc(pBuf->storage, pBuf->maxLen));
-  if (newPtr == NULL) {
+  if (newPtr == nullptr) {
     LOG(FATAL) << "realloc(" << pBuf->maxLen << ") failed";
   }
 
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index be34bd3..a1d2a6c 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -106,8 +106,8 @@
                              Dbg::GetMethodName(method_id).c_str());
   VLOG(jdwp) << StringPrintf("        %d args:", arg_count);
 
-  std::unique_ptr<JdwpTag[]> argTypes(arg_count > 0 ? new JdwpTag[arg_count] : NULL);
-  std::unique_ptr<uint64_t[]> argValues(arg_count > 0 ? new uint64_t[arg_count] : NULL);
+  std::unique_ptr<JdwpTag[]> argTypes(arg_count > 0 ? new JdwpTag[arg_count] : nullptr);
+  std::unique_ptr<uint64_t[]> argValues(arg_count > 0 ? new uint64_t[arg_count] : nullptr);
   for (int32_t i = 0; i < arg_count; ++i) {
     argTypes[i] = request->ReadTag();
     size_t width = Dbg::GetTagWidth(argTypes[i]);
@@ -124,7 +124,9 @@
   JdwpTag resultTag;
   uint64_t resultValue;
   ObjectId exceptObjId;
-  JdwpError err = Dbg::InvokeMethod(thread_id, object_id, class_id, method_id, arg_count, argValues.get(), argTypes.get(), options, &resultTag, &resultValue, &exceptObjId);
+  JdwpError err = Dbg::InvokeMethod(thread_id, object_id, class_id, method_id, arg_count,
+                                    argValues.get(), argTypes.get(), options, &resultTag,
+                                    &resultValue, &exceptObjId);
   if (err != ERR_NONE) {
     return err;
   }
@@ -203,7 +205,7 @@
     // Get class vs. interface and status flags.
     JDWP::JdwpTypeTag type_tag;
     uint32_t class_status;
-    JDWP::JdwpError status = Dbg::GetClassInfo(ids[i], &type_tag, &class_status, NULL);
+    JDWP::JdwpError status = Dbg::GetClassInfo(ids[i], &type_tag, &class_status, nullptr);
     if (status != ERR_NONE) {
       return status;
     }
@@ -331,15 +333,15 @@
   std::vector<std::string> class_path;
   Split(Runtime::Current()->GetClassPathString(), ':', &class_path);
   expandBufAdd4BE(pReply, class_path.size());
-  for (size_t i = 0; i < class_path.size(); ++i) {
-    expandBufAddUtf8String(pReply, class_path[i]);
+  for (const std::string& str : class_path) {
+    expandBufAddUtf8String(pReply, str);
   }
 
   std::vector<std::string> boot_class_path;
   Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path);
   expandBufAdd4BE(pReply, boot_class_path.size());
-  for (size_t i = 0; i < boot_class_path.size(); ++i) {
-    expandBufAddUtf8String(pReply, boot_class_path[i]);
+  for (const std::string& str : boot_class_path) {
+    expandBufAddUtf8String(pReply, str);
   }
 
   return ERR_NONE;
@@ -371,7 +373,7 @@
 static JdwpError VM_CapabilitiesNew(JdwpState*, Request* request, ExpandBuf* reply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // The first few capabilities are the same as those reported by the older call.
-  VM_Capabilities(NULL, request, reply);
+  VM_Capabilities(nullptr, request, reply);
 
   expandBufAdd1(reply, false);   // canRedefineClasses
   expandBufAdd1(reply, false);   // canAddMethod
@@ -507,7 +509,7 @@
   RefTypeId refTypeId = request->ReadRefTypeId();
   JDWP::JdwpTypeTag type_tag;
   uint32_t class_status;
-  JDWP::JdwpError status = Dbg::GetClassInfo(refTypeId, &type_tag, &class_status, NULL);
+  JDWP::JdwpError status = Dbg::GetClassInfo(refTypeId, &type_tag, &class_status, nullptr);
   if (status != ERR_NONE) {
     return status;
   }
@@ -1345,8 +1347,10 @@
       }
       break;
     default:
-      LOG(WARNING) << "GLITCH: unsupported modKind=" << mod.modKind;
-      break;
+      LOG(WARNING) << "Unsupported modifier " << mod.modKind << " for event " << pEvent->eventKind;
+      // Free allocated event to avoid leak before leaving.
+      EventFree(pEvent);
+      return JDWP::ERR_NOT_IMPLEMENTED;
     }
   }
 
@@ -1430,7 +1434,7 @@
 static JdwpError DDM_Chunk(JdwpState* state, Request* request, ExpandBuf* pReply)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   state->NotifyDdmsActive();
-  uint8_t* replyBuf = NULL;
+  uint8_t* replyBuf = nullptr;
   int replyLen = -1;
   if (Dbg::DdmHandlePacket(request, &replyBuf, &replyLen)) {
     // If they want to send something back, we copy it into the buffer.
@@ -1479,11 +1483,11 @@
   { 1,    12, VM_Capabilities,          "VirtualMachine.Capabilities" },
   { 1,    13, VM_ClassPaths,            "VirtualMachine.ClassPaths" },
   { 1,    14, VM_DisposeObjects,        "VirtualMachine.DisposeObjects" },
-  { 1,    15, NULL,                     "VirtualMachine.HoldEvents" },
-  { 1,    16, NULL,                     "VirtualMachine.ReleaseEvents" },
+  { 1,    15, nullptr,                  "VirtualMachine.HoldEvents" },
+  { 1,    16, nullptr,                  "VirtualMachine.ReleaseEvents" },
   { 1,    17, VM_CapabilitiesNew,       "VirtualMachine.CapabilitiesNew" },
-  { 1,    18, NULL,                     "VirtualMachine.RedefineClasses" },
-  { 1,    19, NULL,                     "VirtualMachine.SetDefaultStratum" },
+  { 1,    18, nullptr,                  "VirtualMachine.RedefineClasses" },
+  { 1,    19, nullptr,                  "VirtualMachine.SetDefaultStratum" },
   { 1,    20, VM_AllClassesWithGeneric, "VirtualMachine.AllClassesWithGeneric" },
   { 1,    21, VM_InstanceCounts,        "VirtualMachine.InstanceCounts" },
 
@@ -1495,7 +1499,7 @@
   { 2,    5,  RT_Methods,              "ReferenceType.Methods" },
   { 2,    6,  RT_GetValues,            "ReferenceType.GetValues" },
   { 2,    7,  RT_SourceFile,           "ReferenceType.SourceFile" },
-  { 2,    8,  NULL,                    "ReferenceType.NestedTypes" },
+  { 2,    8,  nullptr,                 "ReferenceType.NestedTypes" },
   { 2,    9,  RT_Status,               "ReferenceType.Status" },
   { 2,    10, RT_Interfaces,           "ReferenceType.Interfaces" },
   { 2,    11, RT_ClassObject,          "ReferenceType.ClassObject" },
@@ -1504,8 +1508,8 @@
   { 2,    14, RT_FieldsWithGeneric,    "ReferenceType.FieldsWithGeneric" },
   { 2,    15, RT_MethodsWithGeneric,   "ReferenceType.MethodsWithGeneric" },
   { 2,    16, RT_Instances,            "ReferenceType.Instances" },
-  { 2,    17, NULL,                    "ReferenceType.ClassFileVersion" },
-  { 2,    18, NULL,                    "ReferenceType.ConstantPool" },
+  { 2,    17, nullptr,                 "ReferenceType.ClassFileVersion" },
+  { 2,    18, nullptr,                 "ReferenceType.ConstantPool" },
 
   /* ClassType command set (3) */
   { 3,    1,  CT_Superclass,    "ClassType.Superclass" },
@@ -1522,7 +1526,7 @@
   { 6,    1,  M_LineTable,                "Method.LineTable" },
   { 6,    2,  M_VariableTable,            "Method.VariableTable" },
   { 6,    3,  M_Bytecodes,                "Method.Bytecodes" },
-  { 6,    4,  NULL,                       "Method.IsObsolete" },
+  { 6,    4,  nullptr,                    "Method.IsObsolete" },
   { 6,    5,  M_VariableTableWithGeneric, "Method.VariableTableWithGeneric" },
 
   /* Field command set (8) */
@@ -1531,7 +1535,7 @@
   { 9,    1,  OR_ReferenceType,     "ObjectReference.ReferenceType" },
   { 9,    2,  OR_GetValues,         "ObjectReference.GetValues" },
   { 9,    3,  OR_SetValues,         "ObjectReference.SetValues" },
-  { 9,    4,  NULL,                 "ObjectReference.UNUSED" },
+  { 9,    4,  nullptr,              "ObjectReference.UNUSED" },
   { 9,    5,  OR_MonitorInfo,       "ObjectReference.MonitorInfo" },
   { 9,    6,  OR_InvokeMethod,      "ObjectReference.InvokeMethod" },
   { 9,    7,  OR_DisableCollection, "ObjectReference.DisableCollection" },
@@ -1552,11 +1556,11 @@
   { 11,   7,  TR_FrameCount,                  "ThreadReference.FrameCount" },
   { 11,   8,  TR_OwnedMonitors,               "ThreadReference.OwnedMonitors" },
   { 11,   9,  TR_CurrentContendedMonitor,     "ThreadReference.CurrentContendedMonitor" },
-  { 11,   10, NULL,                           "ThreadReference.Stop" },
+  { 11,   10, nullptr,                        "ThreadReference.Stop" },
   { 11,   11, TR_Interrupt,                   "ThreadReference.Interrupt" },
   { 11,   12, TR_DebugSuspendCount,           "ThreadReference.SuspendCount" },
   { 11,   13, TR_OwnedMonitorsStackDepthInfo, "ThreadReference.OwnedMonitorsStackDepthInfo" },
-  { 11,   14, NULL,                           "ThreadReference.ForceEarlyReturn" },
+  { 11,   14, nullptr,                        "ThreadReference.ForceEarlyReturn" },
 
   /* ThreadGroupReference command set (12) */
   { 12,   1,  TGR_Name,         "ThreadGroupReference.Name" },
@@ -1574,26 +1578,27 @@
   /* EventRequest command set (15) */
   { 15,   1,  ER_Set,           "EventRequest.Set" },
   { 15,   2,  ER_Clear,         "EventRequest.Clear" },
-  { 15,   3,  NULL,             "EventRequest.ClearAllBreakpoints" },
+  { 15,   3,  nullptr,          "EventRequest.ClearAllBreakpoints" },
 
   /* StackFrame command set (16) */
   { 16,   1,  SF_GetValues,     "StackFrame.GetValues" },
   { 16,   2,  SF_SetValues,     "StackFrame.SetValues" },
   { 16,   3,  SF_ThisObject,    "StackFrame.ThisObject" },
-  { 16,   4,  NULL,             "StackFrame.PopFrames" },
+  { 16,   4,  nullptr,          "StackFrame.PopFrames" },
 
   /* ClassObjectReference command set (17) */
   { 17,   1,  COR_ReflectedType, "ClassObjectReference.ReflectedType" },
 
   /* Event command set (64) */
-  { 64, 100,  NULL, "Event.Composite" },  // sent from VM to debugger, never received by VM
+  { 64, 100,  nullptr, "Event.Composite" },  // sent from VM to debugger, never received by VM
 
   { 199,  1,  DDM_Chunk,        "DDM.Chunk" },
 };
 
 static const char* GetCommandName(Request* request) {
   for (size_t i = 0; i < arraysize(gHandlers); ++i) {
-    if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand()) {
+    if (gHandlers[i].cmdSet == request->GetCommandSet() &&
+        gHandlers[i].cmd == request->GetCommand()) {
       return gHandlers[i].name;
     }
   }
@@ -1661,7 +1666,9 @@
 
   size_t i;
   for (i = 0; i < arraysize(gHandlers); ++i) {
-    if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand() && gHandlers[i].func != NULL) {
+    if (gHandlers[i].cmdSet == request->GetCommandSet() &&
+        gHandlers[i].cmd == request->GetCommand() &&
+        gHandlers[i].func != nullptr) {
       VLOG(jdwp) << DescribeCommand(request);
       result = (*gHandlers[i].func)(this, request, pReply);
       if (result == ERR_NONE) {
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index c500ef5..b04aa6e 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -135,11 +135,16 @@
  */
 ssize_t JdwpNetStateBase::WriteBufferedPacket(const std::vector<iovec>& iov) {
   MutexLock mu(Thread::Current(), socket_lock_);
+  return WriteBufferedPacketLocked(iov);
+}
+
+ssize_t JdwpNetStateBase::WriteBufferedPacketLocked(const std::vector<iovec>& iov) {
+  socket_lock_.AssertHeld(Thread::Current());
   return TEMP_FAILURE_RETRY(writev(clientSock, &iov[0], iov.size()));
 }
 
 bool JdwpState::IsConnected() {
-  return netState != NULL && netState->IsConnected();
+  return netState != nullptr && netState->IsConnected();
 }
 
 void JdwpState::SendBufferedRequest(uint32_t type, const std::vector<iovec>& iov) {
@@ -202,18 +207,18 @@
       thread_start_lock_("JDWP thread start lock", kJdwpStartLock),
       thread_start_cond_("JDWP thread start condition variable", thread_start_lock_),
       pthread_(0),
-      thread_(NULL),
+      thread_(nullptr),
       debug_thread_started_(false),
       debug_thread_id_(0),
       run(false),
-      netState(NULL),
+      netState(nullptr),
       attach_lock_("JDWP attach lock", kJdwpAttachLock),
       attach_cond_("JDWP attach condition variable", attach_lock_),
       last_activity_time_ms_(0),
       request_serial_(0x10000000),
       event_serial_(0x20000000),
       event_list_lock_("JDWP event list lock", kJdwpEventListLock),
-      event_list_(NULL),
+      event_list_(nullptr),
       event_list_size_(0),
       event_thread_lock_("JDWP event thread lock"),
       event_thread_cond_("JDWP event thread condition variable", event_thread_lock_),
@@ -289,7 +294,7 @@
     }
     if (!state->IsActive()) {
       LOG(ERROR) << "JDWP connection failed";
-      return NULL;
+      return nullptr;
     }
 
     LOG(INFO) << "JDWP connected";
@@ -317,7 +322,7 @@
   UnregisterAll();
   {
     MutexLock mu(Thread::Current(), event_list_lock_);
-    CHECK(event_list_ == NULL);
+    CHECK(event_list_ == nullptr);
   }
 
   Dbg::ProcessDelayedFullUndeoptimizations();
@@ -336,7 +341,7 @@
  * Tell the JDWP thread to shut down.  Frees "state".
  */
 JdwpState::~JdwpState() {
-  if (netState != NULL) {
+  if (netState != nullptr) {
     /*
      * Close down the network to inspire the thread to halt.
      */
@@ -353,9 +358,9 @@
 
     VLOG(jdwp) << "JDWP freeing netstate...";
     delete netState;
-    netState = NULL;
+    netState = nullptr;
   }
-  CHECK(netState == NULL);
+  CHECK(netState == nullptr);
 
   ResetState();
 }
@@ -398,10 +403,10 @@
  */
 static void* StartJdwpThread(void* arg) {
   JdwpState* state = reinterpret_cast<JdwpState*>(arg);
-  CHECK(state != NULL);
+  CHECK(state != nullptr);
 
   state->Run();
-  return NULL;
+  return nullptr;
 }
 
 void JdwpState::Run() {
@@ -614,6 +619,18 @@
   return !(lhs == rhs);
 }
 
+bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs) {
+  if (&lhs == &rhs) {
+    return true;
+  }
+
+  return lhs.transport == rhs.transport &&
+      lhs.server == rhs.server &&
+      lhs.suspend == rhs.suspend &&
+      lhs.host == rhs.host &&
+      lhs.port == rhs.port;
+}
+
 }  // namespace JDWP
 
 }  // namespace art
diff --git a/runtime/jdwp/jdwp_priv.h b/runtime/jdwp/jdwp_priv.h
index 29ad185..f290be0 100644
--- a/runtime/jdwp/jdwp_priv.h
+++ b/runtime/jdwp/jdwp_priv.h
@@ -71,6 +71,10 @@
 
   ssize_t WritePacket(ExpandBuf* pReply, size_t length) LOCKS_EXCLUDED(socket_lock_);
   ssize_t WriteBufferedPacket(const std::vector<iovec>& iov) LOCKS_EXCLUDED(socket_lock_);
+  Mutex* GetSocketLock() {
+    return &socket_lock_;
+  }
+  ssize_t WriteBufferedPacketLocked(const std::vector<iovec>& iov);
 
   int clientSock;  // Active connection to debugger.
 
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 7119ce5..cbdde24 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -77,12 +77,12 @@
       /* scan through a range of ports, binding to the first available */
       for (port = kBasePort; port <= kMaxPort; port++) {
         state->netState = SocketStartup(state, port, true);
-        if (state->netState != NULL) {
+        if (state->netState != nullptr) {
           break;
         }
       }
     }
-    if (state->netState == NULL) {
+    if (state->netState == nullptr) {
       LOG(ERROR) << "JDWP net startup failed (req port=" << options->port << ")";
       return false;
     }
@@ -157,7 +157,7 @@
  fail:
   netState->Shutdown();
   delete netState;
-  return NULL;
+  return nullptr;
 }
 
 /*
@@ -284,7 +284,7 @@
 #else
   h_errno = 0;
   pEntry = gethostbyname(options->host.c_str());
-  if (pEntry == NULL) {
+  if (pEntry == nullptr) {
     PLOG(WARNING) << "gethostbyname('" << options->host << "') failed";
     return false;
   }
@@ -403,7 +403,7 @@
        * re-issue the select.  We're currently using #2, as it's more
        * reliable than #1 and generally better than #3.  Wastes two fds.
        */
-      selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+      selCount = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr);
       if (selCount < 0) {
         if (errno == EINTR) {
           continue;
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index 9123994..e415c3d 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -104,7 +104,20 @@
 }
 
 void ObjectRegistry::Clear() {
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
+
+  // We must not hold the mutator lock exclusively if we want to delete weak global
+  // references. Otherwise this can lead to a deadlock with a running GC:
+  // 1. GC thread disables access to weak global references, then releases
+  //    mutator lock.
+  // 2. JDWP thread takes mutator lock exclusively after suspending all
+  //    threads.
+  // 3. GC thread waits for shared mutator lock which is held by JDWP
+  //    thread.
+  // 4. JDWP thread clears weak global references but need to wait for GC
+  //    thread to re-enable access to them.
+  Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+
   MutexLock mu(self, lock_);
   VLOG(jdwp) << "Object registry contained " << object_to_entry_.size() << " entries";
   // Delete all the JNI references.
@@ -138,7 +151,7 @@
 
 jobject ObjectRegistry::GetJObject(JDWP::ObjectId id) {
   if (id == 0) {
-    return NULL;
+    return nullptr;
   }
   Thread* self = Thread::Current();
   MutexLock mu(self, lock_);
@@ -194,7 +207,7 @@
   ObjectRegistryEntry& entry = *it->second;
   if (entry.jni_reference_type == JNIWeakGlobalRefType) {
     JNIEnv* env = self->GetJniEnv();
-    return env->IsSameObject(entry.jni_reference, NULL);  // Has the jweak been collected?
+    return env->IsSameObject(entry.jni_reference, nullptr);  // Has the jweak been collected?
   } else {
     return false;  // We hold a strong reference, so we know this is live.
   }
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 4797e69..37ad46e 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2256,8 +2256,10 @@
         java_buffer, WellKnownClasses::java_nio_DirectByteBuffer_capacity));
   }
 
-  static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) {
-    CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNIInvalidRefType);
+  static jobjectRefType GetObjectRefType(JNIEnv* env ATTRIBUTE_UNUSED, jobject java_object) {
+    if (java_object == nullptr) {
+      return JNIInvalidRefType;
+    }
 
     // Do we definitely know what kind of reference this is?
     IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
@@ -2274,7 +2276,7 @@
       return JNILocalRefType;
     }
     LOG(FATAL) << "IndirectRefKind[" << kind << "]";
-    return JNIInvalidRefType;
+    UNREACHABLE();
   }
 
  private:
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 62b6b34..906aa4c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -634,6 +634,9 @@
 }
 
 TEST_F(JniInternalTest, FindClass) {
+  // This tests leads to warnings in the log.
+  ScopedLogSeverity sls(LogSeverity::ERROR);
+
   FindClassTest(false);
   FindClassTest(true);
 }
@@ -890,40 +893,44 @@
   // Sanity check that no exceptions are pending.
   ASSERT_FALSE(env_->ExceptionCheck());
 
-  // Check that registering method without name causes a NoSuchMethodError.
+  // The following can print errors to the log we'd like to ignore.
   {
-    JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
-    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
-  }
-  ExpectException(jlnsme);
+    ScopedLogSeverity sls(LogSeverity::FATAL);
+    // Check that registering method without name causes a NoSuchMethodError.
+    {
+      JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
+      EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+    }
+    ExpectException(jlnsme);
 
-  // Check that registering method without signature causes a NoSuchMethodError.
-  {
-    JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
-    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
-  }
-  ExpectException(jlnsme);
+    // Check that registering method without signature causes a NoSuchMethodError.
+    {
+      JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
+      EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+    }
+    ExpectException(jlnsme);
 
-  // Check that registering method without function causes a NoSuchMethodError.
-  {
-    JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
-    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
-  }
-  ExpectException(jlnsme);
+    // Check that registering method without function causes a NoSuchMethodError.
+    {
+      JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
+      EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+    }
+    ExpectException(jlnsme);
 
-  // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
-  {
-    JNINativeMethod methods[] = { { "foo", "()V", native_function } };
-    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
-  }
-  ExpectException(jlnsme);
+    // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
+    {
+      JNINativeMethod methods[] = { { "foo", "()V", native_function } };
+      EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+    }
+    ExpectException(jlnsme);
 
-  // Check that registering non-native methods causes a NoSuchMethodError.
-  {
-    JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
-    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+    // Check that registering non-native methods causes a NoSuchMethodError.
+    {
+      JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
+      EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+    }
+    ExpectException(jlnsme);
   }
-  ExpectException(jlnsme);
 
   // Check that registering native methods is successful.
   {
@@ -1300,16 +1307,20 @@
   jweak weak_global = env_->NewWeakGlobalRef(local);
   EXPECT_EQ(JNIWeakGlobalRefType, env_->GetObjectRefType(weak_global));
 
-  CheckJniAbortCatcher jni_abort_catcher;
-  jobject invalid = reinterpret_cast<jobject>(this);
-  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
-  jni_abort_catcher.Check("use of invalid jobject");
+  {
+    CheckJniAbortCatcher jni_abort_catcher;
+    jobject invalid = reinterpret_cast<jobject>(this);
+    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
+    jni_abort_catcher.Check("use of invalid jobject");
+  }
 
   // TODO: invoke a native method and test that its arguments are considered local references.
 
-  // Null as object should fail.
+  // Null as pointer should not fail and return invalid-ref. b/18820997
   EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(nullptr));
-  jni_abort_catcher.Check("java_object == null");
+
+  // TODO: Null as reference should return the original type.
+  // This requires running a GC so a non-null object gets freed.
 }
 
 TEST_F(JniInternalTest, StaleWeakGlobal) {
@@ -1624,7 +1635,6 @@
 
 
 TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) {
-  TEST_DISABLED_FOR_PORTABLE();
   Thread::Current()->TransitionFromSuspendedToRunnable();
   LoadDex("AllFields");
   bool started = runtime_->Start();
@@ -1655,7 +1665,6 @@
 }
 
 TEST_F(JniInternalTest, GetObjectField_SetObjectField) {
-  TEST_DISABLED_FOR_PORTABLE();
   Thread::Current()->TransitionFromSuspendedToRunnable();
   LoadDex("AllFields");
   runtime_->Start();
@@ -1705,6 +1714,9 @@
 }
 
 TEST_F(JniInternalTest, DeleteLocalRef) {
+  // This tests leads to warnings and errors in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   jstring s = env_->NewStringUTF("");
   ASSERT_NE(s, nullptr);
   env_->DeleteLocalRef(s);
@@ -1741,6 +1753,9 @@
   ASSERT_EQ(JNI_OK, env_->PushLocalFrame(0));
   env_->PopLocalFrame(nullptr);
 
+  // The following two tests will print errors to the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   // Negative capacities are not allowed.
   ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(-1));
 
@@ -1749,6 +1764,9 @@
 }
 
 TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) {
+  // This tests leads to errors in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   jobject original = env_->NewStringUTF("");
   ASSERT_NE(original, nullptr);
 
@@ -1813,6 +1831,9 @@
 }
 
 TEST_F(JniInternalTest, DeleteGlobalRef) {
+  // This tests leads to warnings and errors in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   jstring s = env_->NewStringUTF("");
   ASSERT_NE(s, nullptr);
 
@@ -1863,6 +1884,9 @@
 }
 
 TEST_F(JniInternalTest, DeleteWeakGlobalRef) {
+  // This tests leads to warnings and errors in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   jstring s = env_->NewStringUTF("");
   ASSERT_NE(s, nullptr);
 
@@ -1987,6 +2011,9 @@
 }
 
 TEST_F(JniInternalTest, MonitorEnterExit) {
+  // This will print some error messages. Suppress.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   // Create an object to torture.
   jclass object_class = env_->FindClass("java/lang/Object");
   ASSERT_NE(object_class, nullptr);
diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc
index e22d75f..1f424b3 100644
--- a/runtime/jobject_comparator.cc
+++ b/runtime/jobject_comparator.cc
@@ -25,33 +25,32 @@
 
 bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const {
   // Ensure null references and cleared jweaks appear at the end.
-  if (jobj1 == NULL) {
+  if (jobj1 == nullptr) {
     return true;
-  } else if (jobj2 == NULL) {
+  } else if (jobj2 == nullptr) {
     return false;
   }
   ScopedObjectAccess soa(Thread::Current());
-  mirror::Object* obj1 = soa.Decode<mirror::Object*>(jobj1);
-  mirror::Object* obj2 = soa.Decode<mirror::Object*>(jobj2);
-  if (obj1 == NULL) {
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object*>(jobj1)));
+  Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object*>(jobj2)));
+  if (obj1.Get() == nullptr) {
     return true;
-  } else if (obj2 == NULL) {
+  } else if (obj2.Get() == nullptr) {
     return false;
   }
   // Sort by class...
   if (obj1->GetClass() != obj2->GetClass()) {
-    return obj1->GetClass()->IdentityHashCode() < obj2->IdentityHashCode();
-  } else {
-    // ...then by size...
-    size_t count1 = obj1->SizeOf();
-    size_t count2 = obj2->SizeOf();
-    if (count1 != count2) {
-      return count1 < count2;
-    } else {
-      // ...and finally by identity hash code.
-      return obj1->IdentityHashCode() < obj2->IdentityHashCode();
-    }
+    return obj1->GetClass()->IdentityHashCode() < obj2->GetClass()->IdentityHashCode();
   }
+  // ...then by size...
+  const size_t count1 = obj1->SizeOf();
+  const size_t count2 = obj2->SizeOf();
+  if (count1 != count2) {
+    return count1 < count2;
+  }
+  // ...and finally by identity hash code.
+  return obj1->IdentityHashCode() < obj2->IdentityHashCode();
 }
 
 }  // namespace art
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 8303f84..a722813 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -665,6 +665,19 @@
   maps_ = nullptr;
 }
 
+void MemMap::SetSize(size_t new_size) {
+  if (new_size == base_size_) {
+    return;
+  }
+  CHECK_ALIGNED(new_size, kPageSize);
+  CHECK_EQ(base_size_, size_) << "Unsupported";
+  CHECK_LE(new_size, base_size_);
+  CHECK_EQ(munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(BaseBegin()) + new_size),
+                  base_size_ - new_size), 0) << new_size << " " << base_size_;
+  base_size_ = new_size;
+  size_ = new_size;
+}
+
 std::ostream& operator<<(std::ostream& os, const MemMap& mem_map) {
   os << StringPrintf("[MemMap: %p-%p prot=0x%x %s]",
                      mem_map.BaseBegin(), mem_map.BaseEnd(), mem_map.GetProtect(),
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 9b003aa..dc337e0 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -107,6 +107,9 @@
     return size_;
   }
 
+  // Resize the mem-map by unmapping pages at the end. Currently only supports shrinking.
+  void SetSize(size_t new_size);
+
   uint8_t* End() const {
     return Begin() + Size();
   }
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 13f881d..048d8ba 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -19,6 +19,7 @@
 
 #include "array.h"
 
+#include "base/stringprintf.h"
 #include "class.h"
 #include "gc/heap-inl.h"
 #include "thread.h"
@@ -199,9 +200,7 @@
 
 template<class T>
 inline void PrimitiveArray<T>::VisitRoots(RootCallback* callback, void* arg) {
-  if (!array_class_.IsNull()) {
-    array_class_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 template<typename T>
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 7e20076..5a4ebd1 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -56,9 +56,7 @@
 }
 
 void ArtField::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_reflect_ArtField_.IsNull()) {
-    java_lang_reflect_ArtField_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_reflect_ArtField_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 // TODO: we could speed up the search if fields are ordered by offsets.
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index c29276a..7d31148 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -24,6 +24,7 @@
 #include "class_linker.h"
 #include "dex_cache.h"
 #include "dex_file.h"
+#include "dex_file-inl.h"
 #include "object-inl.h"
 #include "object_array.h"
 #include "oat.h"
@@ -72,12 +73,7 @@
 }
 
 inline uint32_t ArtMethod::GetDexMethodIndex() {
-#ifdef ART_SEA_IR_MODE
-  // TODO: Re-add this check for (PORTABLE + SMALL + ) SEA IR when PORTABLE IS fixed!
-  // DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
-#else
   DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
-#endif
   return GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_));
 }
 
@@ -187,21 +183,11 @@
   return PointerToLowMemUInt32(GetEntryPointFromQuickCompiledCode());
 }
 
-inline uint32_t ArtMethod::GetPortableOatCodeOffset() {
-  DCHECK(!Runtime::Current()->IsStarted());
-  return PointerToLowMemUInt32(GetEntryPointFromPortableCompiledCode());
-}
-
 inline void ArtMethod::SetQuickOatCodeOffset(uint32_t code_offset) {
   DCHECK(!Runtime::Current()->IsStarted());
   SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(code_offset));
 }
 
-inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) {
-  DCHECK(!Runtime::Current()->IsStarted());
-  SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
-}
-
 inline const uint8_t* ArtMethod::GetMappingTable(size_t pointer_size) {
   const void* code_pointer = GetQuickOatCodePointer(pointer_size);
   if (code_pointer == nullptr) {
@@ -380,8 +366,7 @@
 }
 
 inline const DexFile::CodeItem* ArtMethod::GetCodeItem() {
-  mirror::ArtMethod* method = GetInterfaceMethodIfProxy();
-  return method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset());
+  return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
 }
 
 inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx) {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 1729686..b2016dc 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -39,10 +39,9 @@
 namespace art {
 namespace mirror {
 
-extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char);
 extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                       const char*);
-#if defined(__LP64__) || defined(__arm__)
+#if defined(__LP64__) || defined(__arm__) || defined(__i386__)
 extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
                                              const char*);
 #endif
@@ -61,9 +60,7 @@
 
 
 void ArtMethod::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_reflect_ArtMethod_.IsNull()) {
-    java_lang_reflect_ArtMethod_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_reflect_ArtMethod_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 mirror::String* ArtMethod::GetNameAsString(Thread* self) {
@@ -200,11 +197,13 @@
 }
 
 uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) {
-  if (IsPortableCompiled()) {
-    // Portable doesn't use the machine pc, we just use dex pc instead.
-    return static_cast<uint32_t>(pc);
-  }
   const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
+  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
+  if (IsOptimized(sizeof(void*))) {
+    uint32_t ret = GetStackMap(sought_offset).GetDexPc();
+    return ret;
+  }
+
   MappingTable table(entry_point != nullptr ?
       GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
   if (table.TotalSize() == 0) {
@@ -213,7 +212,6 @@
     DCHECK(IsNative() || IsCalleeSaveMethod() || IsProxyMethod()) << PrettyMethod(this);
     return DexFile::kDexNoIndex;   // Special no mapping case
   }
-  uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
   // Assume the caller wants a pc-to-dex mapping so check here first.
   typedef MappingTable::PcToDexIterator It;
   for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
@@ -347,19 +345,12 @@
 
 bool ArtMethod::IsEntrypointInterpreter() {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  if (!IsPortableCompiled()) {  // Quick.
-    const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
-    return oat_quick_code == nullptr ||
-        oat_quick_code != GetEntryPointFromQuickCompiledCode();
-  } else {  // Portable.
-    const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this);
-    return oat_portable_code == nullptr ||
-        oat_portable_code != GetEntryPointFromPortableCompiledCode();
-  }
+  const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
+  return oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode();
 }
 
 const void* ArtMethod::GetQuickOatEntryPoint(size_t pointer_size) {
-  if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
+  if (IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
     return nullptr;
   }
   Runtime* runtime = Runtime::Current();
@@ -412,34 +403,27 @@
   } else {
     const bool kLogInvocationStartAndReturn = false;
     bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
-    bool have_portable_code = GetEntryPointFromPortableCompiledCode() != nullptr;
-    if (LIKELY(have_quick_code || have_portable_code)) {
+    if (LIKELY(have_quick_code)) {
       if (kLogInvocationStartAndReturn) {
-        LOG(INFO) << StringPrintf("Invoking '%s' %s code=%p", PrettyMethod(this).c_str(),
-                                  have_quick_code ? "quick" : "portable",
-                                  have_quick_code ? GetEntryPointFromQuickCompiledCode()
-                                                  : GetEntryPointFromPortableCompiledCode());
+        LOG(INFO) << StringPrintf("Invoking '%s' quick code=%p", PrettyMethod(this).c_str(),
+                                  GetEntryPointFromQuickCompiledCode());
       }
 
-      // Ensure that we won't be accidentally calling quick/portable compiled code when -Xint.
+      // Ensure that we won't be accidentally calling quick compiled code when -Xint.
       if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()) {
         CHECK(IsEntrypointInterpreter())
             << "Don't call compiled code when -Xint " << PrettyMethod(this);
       }
 
-      if (!IsPortableCompiled()) {
-#if defined(__LP64__) || defined(__arm__)
-        if (!IsStatic()) {
-          (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
-        } else {
-          (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
-        }
-#else
+#if defined(__LP64__) || defined(__arm__) || defined(__i386__)
+      if (!IsStatic()) {
         (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
-#endif
       } else {
-        (*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]);
+        (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
       }
+#else
+      (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
+#endif
       if (UNLIKELY(self->GetException(nullptr) == Thread::GetDeoptimizationException())) {
         // Unusual case where we were running generated code and an
         // exception was thrown to force the activations to be removed from the
@@ -451,10 +435,8 @@
         interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
       }
       if (kLogInvocationStartAndReturn) {
-        LOG(INFO) << StringPrintf("Returned '%s' %s code=%p", PrettyMethod(this).c_str(),
-                                  have_quick_code ? "quick" : "portable",
-                                  have_quick_code ? GetEntryPointFromQuickCompiledCode()
-                                                  : GetEntryPointFromPortableCompiledCode());
+        LOG(INFO) << StringPrintf("Returned '%s' quick code=%p", PrettyMethod(this).c_str(),
+                                  GetEntryPointFromQuickCompiledCode());
       }
     } else {
       LOG(INFO) << "Not invoking '" << PrettyMethod(this) << "' code=null";
@@ -484,10 +466,6 @@
 }
 
 QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() {
-  if (UNLIKELY(IsPortableCompiled())) {
-    // Portable compiled dex bytecode or jni stub.
-    return QuickMethodFrameInfo(kStackAlignment, 0u, 0u);
-  }
   Runtime* runtime = Runtime::Current();
 
   if (UNLIKELY(IsAbstract())) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 2107944..f33ca94 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -20,11 +20,13 @@
 #include "dex_file.h"
 #include "gc_root.h"
 #include "invoke_type.h"
+#include "method_reference.h"
 #include "modifiers.h"
 #include "object.h"
 #include "object_callbacks.h"
 #include "quick/quick_method_frame_info.h"
 #include "read_barrier_option.h"
+#include "stack.h"
 #include "stack_map.h"
 
 namespace art {
@@ -151,25 +153,12 @@
     // Temporary solution for detecting if a method has been optimized: the compiler
     // does not create a GC map. Instead, the vmap table contains the stack map
     // (as in stack_map.h).
-    return GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) != nullptr
+    return !IsNative()
+        && GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) != nullptr
         && GetQuickOatCodePointer(pointer_size) != nullptr
         && GetNativeGcMap(pointer_size) == nullptr;
   }
 
-  bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return (GetAccessFlags() & kAccPortableCompiled) != 0;
-  }
-
-  void SetIsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK(!IsPortableCompiled());
-    SetAccessFlags(GetAccessFlags() | kAccPortableCompiled);
-  }
-
-  void ClearIsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK(IsPortableCompiled());
-    SetAccessFlags(GetAccessFlags() & ~kAccPortableCompiled);
-  }
-
   bool CheckIncompatibleClassChange(InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint16_t GetMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -282,42 +271,6 @@
         EntryPointFromInterpreterOffset(pointer_size), entry_point_from_interpreter, pointer_size);
   }
 
-  ALWAYS_INLINE static MemberOffset EntryPointFromPortableCompiledCodeOffset(size_t pointer_size) {
-    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
-        PtrSizedFields, entry_point_from_portable_compiled_code_) / sizeof(void*) * pointer_size);
-  }
-
-  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  const void* GetEntryPointFromPortableCompiledCode()
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    CheckObjectSizeEqualsMirrorSize();
-    return GetEntryPointFromPortableCompiledCodePtrSize(sizeof(void*));
-  }
-
-  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  ALWAYS_INLINE const void* GetEntryPointFromPortableCompiledCodePtrSize(size_t pointer_size)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return GetFieldPtrWithSize<const void*, kVerifyFlags>(
-        EntryPointFromPortableCompiledCodeOffset(pointer_size), pointer_size);
-  }
-
-  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    CheckObjectSizeEqualsMirrorSize();
-    return SetEntryPointFromPortableCompiledCodePtrSize(entry_point_from_portable_compiled_code,
-                                                        sizeof(void*));
-  }
-
-  template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  void SetEntryPointFromPortableCompiledCodePtrSize(
-      const void* entry_point_from_portable_compiled_code, size_t pointer_size)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    SetFieldPtrWithSize<false, true, kVerifyFlags>(
-        EntryPointFromPortableCompiledCodeOffset(pointer_size),
-        entry_point_from_portable_compiled_code, pointer_size);
-  }
-
   template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   const void* GetEntryPointFromQuickCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     CheckObjectSizeEqualsMirrorSize();
@@ -374,9 +327,7 @@
   bool IsEntrypointInterpreter() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint32_t GetQuickOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  uint32_t GetPortableOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   ALWAYS_INLINE static const void* EntryPointToCodePointer(const void* entry_point) {
     uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
@@ -440,8 +391,9 @@
   }
 
   FrameOffset GetHandleScopeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    DCHECK_LT(sizeof(void*), GetFrameSizeInBytes());
-    return FrameOffset(sizeof(void*));
+    constexpr size_t handle_scope_offset = sizeof(StackReference<mirror::ArtMethod>);
+    DCHECK_LT(handle_scope_offset, GetFrameSizeInBytes());
+    return FrameOffset(handle_scope_offset);
   }
 
   void RegisterNative(const void* native_method, bool is_fast)
@@ -521,6 +473,10 @@
   uintptr_t ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure = true)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  MethodReference ToMethodReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return MethodReference(GetDexFile(), GetDexMethodIndex());
+  }
+
   // Find the catch block for the given exception type and dex_pc. When a catch block is found,
   // indicates whether the found catch block is responsible for clearing the exception or whether
   // a move-exception instruction is present.
@@ -642,12 +598,8 @@
     void* entry_point_from_jni_;
 
     // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
-    // portable compiled code or the interpreter.
+    // the interpreter.
     void* entry_point_from_quick_compiled_code_;
-
-    // Method dispatch from portable compiled code invokes this pointer which may cause bridging
-    // into quick compiled code or the interpreter. Last to simplify entrypoint logic.
-    void* entry_point_from_portable_compiled_code_;
   } ptr_sized_fields_;
 
   static GcRoot<Class> java_lang_reflect_ArtMethod_;
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 1662ebf..495f753 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -514,7 +514,15 @@
          IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
          this == String::GetJavaLangString() ||
          this == ArtField::GetJavaLangReflectArtField() ||
-         this == ArtMethod::GetJavaLangReflectArtMethod());
+         this == ArtMethod::GetJavaLangReflectArtMethod())
+      << "IsIdxLoaded=" << IsIdxLoaded<kVerifyFlags>()
+      << " IsRetired=" << IsRetired<kVerifyFlags>()
+      << " IsErroneous=" <<
+          IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()
+      << " IsString=" << (this == String::GetJavaLangString())
+      << " IsArtField=" << (this == ArtField::GetJavaLangReflectArtField())
+      << " IsArtMethod=" << (this == ArtMethod::GetJavaLangReflectArtMethod())
+      << " descriptor=" << PrettyDescriptor(this);
   return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
 }
 
@@ -642,7 +650,14 @@
 template <bool kVisitClass, typename Visitor>
 inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
   VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
-  if (!IsTemp() && IsResolved()) {
+  // Right after a class is allocated, but not yet loaded
+  // (kStatusNotReady, see ClassLinkder::LoadClass()), GC may find it
+  // and scan it. IsTemp() may call Class::GetAccessFlags() but may
+  // fail in the DCHECK in Class::GetAccessFlags() because the class
+  // status is kStatusNotReady. To avoid it, rely on IsResolved()
+  // only. This is fine because a temp class never goes into the
+  // kStatusResolved state.
+  if (IsResolved()) {
     // Temp classes don't ever populate imt/vtable or static fields and they are not even
     // allocated with the right size for those. Also, unresolved classes don't have fields
     // linked yet.
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index bd3bfbf..ae684b1 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -52,9 +52,7 @@
 }
 
 void Class::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_Class_.IsNull()) {
-    java_lang_Class_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_Class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 void Class::SetStatus(Status new_status, Thread* self) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 812cfd3..bd49754 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1048,6 +1048,11 @@
     DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor);
   };
 
+  // Returns true if the class loader is null, ie the class loader is the boot strap class loader.
+  bool IsBootStrapClassLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return GetClassLoader() == nullptr;
+  }
+
  private:
   void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index ef6fc67..53e5534 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -34,6 +34,7 @@
 TEST_F(DexCacheTest, Open) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
+  ASSERT_TRUE(java_lang_dex_file_ != NULL);
   Handle<DexCache> dex_cache(
       hs.NewHandle(class_linker_->AllocDexCache(soa.Self(), *java_lang_dex_file_)));
   ASSERT_TRUE(dex_cache.Get() != NULL);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 121947d..d690163 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -154,7 +154,6 @@
     }
   } while (!atomic_rb_ptr->CompareExchangeWeakSequentiallyConsistent(expected_ref.reference_,
                                                                      new_ref.reference_));
-  DCHECK_EQ(new_ref.reference_, atomic_rb_ptr->LoadRelaxed());
   return true;
 #else
   UNUSED(expected_rb_ptr, rb_ptr);
@@ -826,6 +825,17 @@
 template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
 inline bool Object::CasFieldWeakSequentiallyConsistentObject(MemberOffset field_offset,
                                                              Object* old_value, Object* new_value) {
+  bool success = CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<
+      kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, old_value, new_value);
+  if (success) {
+    Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
+  }
+  return success;
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier(
+    MemberOffset field_offset, Object* old_value, Object* new_value) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
   }
@@ -848,7 +858,14 @@
 
   bool success = atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_ref.reference_,
                                                                         new_ref.reference_);
+  return success;
+}
 
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset,
+                                                               Object* old_value, Object* new_value) {
+  bool success = CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<
+      kTransactionActive, kCheckTransaction, kVerifyFlags>(field_offset, old_value, new_value);
   if (success) {
     Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
   }
@@ -856,8 +873,8 @@
 }
 
 template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
-inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset,
-                                                             Object* old_value, Object* new_value) {
+inline bool Object::CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier(
+    MemberOffset field_offset, Object* old_value, Object* new_value) {
   if (kCheckTransaction) {
     DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
   }
@@ -880,10 +897,6 @@
 
   bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref.reference_,
                                                                           new_ref.reference_);
-
-  if (success) {
-    Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
-  }
   return success;
 }
 
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 65d6ade..9262a3e 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -24,6 +24,7 @@
 #include "class.h"
 #include "class-inl.h"
 #include "class_linker-inl.h"
+#include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/heap.h"
 #include "iftable-inl.h"
@@ -38,6 +39,8 @@
 namespace art {
 namespace mirror {
 
+Atomic<uint32_t> Object::hash_code_seed(987654321U + std::time(nullptr));
+
 class CopyReferenceFieldsWithReadBarrierVisitor {
  public:
   explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj)
@@ -72,7 +75,7 @@
   uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest);
   size_t offset = sizeof(Object);
   memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset);
-  if (kUseBakerOrBrooksReadBarrier) {
+  if (kUseReadBarrier) {
     // We need a RB here. After the memcpy that covers the whole
     // object above, copy references fields one by one again with a
     // RB. TODO: Optimize this later?
@@ -135,16 +138,19 @@
 }
 
 uint32_t Object::GenerateIdentityHashCode() {
-  static Atomic<uint32_t> seed(987654321U + std::time(nullptr));
   uint32_t expected_value, new_value;
   do {
-    expected_value = seed.LoadRelaxed();
+    expected_value = hash_code_seed.LoadRelaxed();
     new_value = expected_value * 1103515245 + 12345;
-  } while ((expected_value & LockWord::kHashMask) == 0 ||
-      !seed.CompareExchangeWeakRelaxed(expected_value, new_value));
+  } while (!hash_code_seed.CompareExchangeWeakRelaxed(expected_value, new_value) ||
+      (expected_value & LockWord::kHashMask) == 0);
   return expected_value & LockWord::kHashMask;
 }
 
+void Object::SetHashCodeSeed(uint32_t new_seed) {
+  hash_code_seed.StoreRelaxed(new_seed);
+}
+
 int32_t Object::IdentityHashCode() const {
   mirror::Object* current_this = const_cast<mirror::Object*>(this);
   while (true) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 221feca..780c5ae 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -240,12 +240,24 @@
   bool CasFieldWeakSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value,
                                                 Object* new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier(MemberOffset field_offset,
+                                                                   Object* old_value,
+                                                                   Object* new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template<bool kTransactionActive, bool kCheckTransaction = true,
       VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value,
                                                   Object* new_value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  template<bool kTransactionActive, bool kCheckTransaction = true,
+      VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  bool CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier(MemberOffset field_offset,
+                                                                     Object* old_value,
+                                                                     Object* new_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
@@ -419,6 +431,11 @@
   void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor)
       NO_THREAD_SAFETY_ANALYSIS;
 
+  // Used by object_test.
+  static void SetHashCodeSeed(uint32_t new_seed);
+  // Generate an identity hash code. Public for object test.
+  static uint32_t GenerateIdentityHashCode();
+
  protected:
   // Accessors for non-Java type fields
   template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
@@ -471,9 +488,6 @@
     }
   }
 
-  // Generate an identity hash code.
-  static uint32_t GenerateIdentityHashCode();
-
   // A utility function that copies an object in a read barrier and
   // write barrier-aware way. This is internally used by Clone() and
   // Class::CopyOf().
@@ -481,6 +495,8 @@
                             size_t num_bytes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static Atomic<uint32_t> hash_code_seed;
+
   // The Class representing the type of the object.
   HeapReference<Class> klass_;
   // Monitor and hash code information.
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index fbc4f4a..96d426b 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -131,7 +131,7 @@
   CHECK_EQ(sizeof(HeapReference<T>), sizeof(uint32_t));
   IntArray* dstAsIntArray = reinterpret_cast<IntArray*>(this);
   IntArray* srcAsIntArray = reinterpret_cast<IntArray*>(src);
-  if (kUseBakerOrBrooksReadBarrier) {
+  if (kUseReadBarrier) {
     // TODO: Optimize this later?
     const bool copy_forward = (src != this) || (dst_pos < src_pos) || (dst_pos - src_pos >= count);
     if (copy_forward) {
@@ -174,7 +174,7 @@
   CHECK_EQ(sizeof(HeapReference<T>), sizeof(uint32_t));
   IntArray* dstAsIntArray = reinterpret_cast<IntArray*>(this);
   IntArray* srcAsIntArray = reinterpret_cast<IntArray*>(src);
-  if (kUseBakerOrBrooksReadBarrier) {
+  if (kUseReadBarrier) {
     // TODO: Optimize this later?
     for (int i = 0; i < count; ++i) {
       // We need a RB here. ObjectArray::GetWithoutChecks() contains a RB.
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 9d789cd..fb42d28 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -77,10 +77,6 @@
 TEST_F(ObjectTest, Constants) {
   EXPECT_EQ(kObjectReferenceSize, sizeof(HeapReference<Object>));
   EXPECT_EQ(kObjectHeaderSize, sizeof(Object));
-  EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32,
-            ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value());
-  EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64,
-            ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value());
   EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32,
             ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value());
   EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64,
@@ -317,7 +313,7 @@
       java_lang_dex_file_->GetIndexForStringId(*string_id));
   ASSERT_TRUE(type_id != NULL);
   uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
-  Object* array = CheckAndAllocArrayFromCodeInstrumented(type_idx, sort, 3, Thread::Current(), false,
+  Object* array = CheckAndAllocArrayFromCodeInstrumented(type_idx, 3, sort, Thread::Current(), false,
                                                          Runtime::Current()->GetHeap()->GetCurrentAllocator());
   EXPECT_TRUE(array->IsArrayInstance());
   EXPECT_EQ(3, array->AsArray()->GetLength());
@@ -735,5 +731,14 @@
   // TODO: test that interfaces trump superclasses.
 }
 
+TEST_F(ObjectTest, IdentityHashCode) {
+  // Regression test for b/19046417 which had an infinite loop if the
+  // (seed & LockWord::kHashMask) == 0. seed 0 triggered the infinite loop since we did the check
+  // before the CAS which resulted in the same seed the next loop iteration.
+  mirror::Object::SetHashCodeSeed(0);
+  int32_t hash_code = mirror::Object::GenerateIdentityHashCode();
+  EXPECT_NE(hash_code, 0);
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc
index c36bd98..35130e8 100644
--- a/runtime/mirror/reference.cc
+++ b/runtime/mirror/reference.cc
@@ -33,9 +33,7 @@
 }
 
 void Reference::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_ref_Reference_.IsNull()) {
-    java_lang_ref_Reference_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_ref_Reference_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 1eb20f7..c2a67e8 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -68,9 +68,7 @@
 }
 
 void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_StackTraceElement_.IsNull()) {
-    java_lang_StackTraceElement_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_StackTraceElement_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01599ae..e199d0e 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -224,9 +224,7 @@
 }
 
 void String::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_String_.IsNull()) {
-    java_lang_String_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_String_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index 93ed4d4..61d85e2 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -138,9 +138,7 @@
 }
 
 void Throwable::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_Throwable_.IsNull()) {
-    java_lang_Throwable_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_Throwable_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 23c18f8..09dc78a 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -46,7 +46,6 @@
 static constexpr uint32_t kAccPreverified =          0x00080000;  // class (runtime),
                                                                   // method (dex only)
 static constexpr uint32_t kAccFastNative =           0x00080000;  // method (dex only)
-static constexpr uint32_t kAccPortableCompiled =     0x00100000;  // method (dex only)
 static constexpr uint32_t kAccMiranda =              0x00200000;  // method (dex only)
 
 // Special runtime-only flags.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 233267b..5ed8c7d 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -16,6 +16,9 @@
 
 #include "monitor.h"
 
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+#include <cutils/trace.h>
 #include <vector>
 
 #include "base/mutex.h"
@@ -251,7 +254,12 @@
     {
       ScopedThreadStateChange tsc(self, kBlocked);  // Change to blocked and give up mutator_lock_.
       MutexLock mu2(self, monitor_lock_);  // Reacquire monitor_lock_ without mutator_lock_ for Wait.
-      if (owner_ != NULL) {  // Did the owner_ give the lock up?
+      if (owner_ != nullptr) {  // Did the owner_ give the lock up?
+        if (ATRACE_ENABLED()) {
+          std::string name;
+          owner_->GetThreadName(name);
+          ATRACE_BEGIN(("Contended on monitor with owner " + name).c_str());
+        }
         monitor_contenders_.Wait(self);  // Still contended so wait.
         // Woken from contention.
         if (log_contention) {
@@ -275,6 +283,7 @@
             LogContentionEvent(self, wait_ms, sample_percent, owners_filename, owners_line_number);
           }
         }
+        ATRACE_END();
       }
     }
     self->SetMonitorEnterObject(nullptr);
@@ -924,8 +933,11 @@
                                            PrettyTypeOf(pretty_object).c_str());
       } else {
         // - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
+        // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread
+        // suspension and move pretty_object.
+        const std::string pretty_type(PrettyTypeOf(pretty_object));
         os << wait_message << StringPrintf("<0x%08x> (a %s)", pretty_object->IdentityHashCode(),
-                                           PrettyTypeOf(pretty_object).c_str());
+                                           pretty_type.c_str());
       }
     }
     // - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5
@@ -992,14 +1004,9 @@
   // the locks held in this stack frame.
   std::vector<uint32_t> monitor_enter_dex_pcs;
   verifier::MethodVerifier::FindLocksAtDexPc(m, dex_pc, &monitor_enter_dex_pcs);
-  if (monitor_enter_dex_pcs.empty()) {
-    return;
-  }
-
-  for (size_t i = 0; i < monitor_enter_dex_pcs.size(); ++i) {
+  for (uint32_t monitor_dex_pc : monitor_enter_dex_pcs) {
     // The verifier works in terms of the dex pcs of the monitor-enter instructions.
     // We want the registers used by those instructions (so we can read the values out of them).
-    uint32_t monitor_dex_pc = monitor_enter_dex_pcs[i];
     uint16_t monitor_enter_instruction = code_item->insns_[monitor_dex_pc];
 
     // Quick sanity check.
@@ -1009,8 +1016,8 @@
     }
 
     uint16_t monitor_register = ((monitor_enter_instruction >> 8) & 0xff);
-    mirror::Object* o = reinterpret_cast<mirror::Object*>(stack_visitor->GetVReg(m, monitor_register,
-                                                                                 kReferenceVReg));
+    mirror::Object* o = reinterpret_cast<mirror::Object*>(
+        stack_visitor->GetVReg(m, monitor_register, kReferenceVReg));
     callback(o, callback_context);
   }
 }
@@ -1099,6 +1106,13 @@
   monitor_add_condition_.Broadcast(self);
 }
 
+void MonitorList::EnsureNewMonitorsDisallowed() {
+  // Lock and unlock once to ensure that no threads are still in the
+  // middle of adding new monitors.
+  MutexLock mu(Thread::Current(), monitor_list_lock_);
+  CHECK(!allow_new_monitors_);
+}
+
 void MonitorList::Add(Monitor* m) {
   Thread* self = Thread::Current();
   MutexLock mu(self, monitor_list_lock_);
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 8f97a40..0c5f8a4 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -266,12 +266,13 @@
   MonitorList();
   ~MonitorList();
 
-  void Add(Monitor* m);
+  void Add(Monitor* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void SweepMonitorList(IsMarkedCallback* callback, void* arg)
       LOCKS_EXCLUDED(monitor_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void DisallowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_);
   void AllowNewMonitors() LOCKS_EXCLUDED(monitor_list_lock_);
+  void EnsureNewMonitorsDisallowed() LOCKS_EXCLUDED(monitor_list_lock_);
   // Returns how many monitors were deflated.
   size_t DeflateMonitors() LOCKS_EXCLUDED(monitor_list_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index adc7848..6d1e721 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -356,6 +356,8 @@
 TEST_F(MonitorTest, CheckExceptionsWait1) {
   // Make the CreateTask wait 10ms, the UseTask wait 10ms.
   // => The use task will get the lock first and get to self == owner check.
+  // This will lead to OOM and monitor error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
   CommonWaitSetup(this, class_linker_, 10, 50, false, false, 2, 50, true,
                   "Monitor test thread pool 1");
 }
@@ -364,6 +366,8 @@
 TEST_F(MonitorTest, CheckExceptionsWait2) {
   // Make the CreateTask wait 0ms, the UseTask wait 10ms.
   // => The create task will get the lock first and get to ms >= 0
+  // This will lead to OOM and monitor error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
   CommonWaitSetup(this, class_linker_, 0, -1, true, false, 10, 50, true,
                   "Monitor test thread pool 2");
 }
@@ -373,6 +377,8 @@
   // Make the CreateTask wait 0ms, then Wait for a long time. Make the InterruptTask wait 10ms,
   // after which it will interrupt the create task and then wait another 10ms.
   // => The create task will get to the interrupted-exception throw.
+  // This will lead to OOM and monitor error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
   CommonWaitSetup(this, class_linker_, 0, 500, true, true, 10, 50, true,
                   "Monitor test thread pool 3");
 }
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index f37312e..037072d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -115,7 +115,8 @@
   }
 
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
-  std::unique_ptr<std::vector<const DexFile*>> dex_files(new std::vector<const DexFile*>());
+  std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> dex_files(
+      new std::vector<std::unique_ptr<const DexFile>>());
   std::vector<std::string> error_msgs;
 
   bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
@@ -143,9 +144,11 @@
   }
 }
 
-static std::vector<const DexFile*>* toDexFiles(jlong dex_file_address, JNIEnv* env) {
-  std::vector<const DexFile*>* dex_files = reinterpret_cast<std::vector<const DexFile*>*>(
-      static_cast<uintptr_t>(dex_file_address));
+static std::vector<std::unique_ptr<const DexFile>>*
+toDexFiles(jlong dex_file_address, JNIEnv* env) {
+  std::vector<std::unique_ptr<const DexFile>>* dex_files
+    = reinterpret_cast<std::vector<std::unique_ptr<const DexFile>>*>(
+        static_cast<uintptr_t>(dex_file_address));
   if (UNLIKELY(dex_files == nullptr)) {
     ScopedObjectAccess soa(env);
     ThrowNullPointerException(NULL, "dex_file == null");
@@ -154,27 +157,29 @@
 }
 
 static void DexFile_closeDexFile(JNIEnv* env, jclass, jlong cookie) {
-  std::unique_ptr<std::vector<const DexFile*>> dex_files(toDexFiles(cookie, env));
+  std::unique_ptr<std::vector<std::unique_ptr<const DexFile>>> dex_files(toDexFiles(cookie, env));
   if (dex_files.get() == nullptr) {
     return;
   }
   ScopedObjectAccess soa(env);
 
-  size_t index = 0;
-  for (const DexFile* dex_file : *dex_files) {
+  // The Runtime currently never unloads classes, which means any registered
+  // dex files must be kept around forever in case they are used. We
+  // accomplish this here by explicitly leaking those dex files that are
+  // registered.
+  //
+  // TODO: The Runtime should support unloading of classes and freeing of the
+  // dex files for those unloaded classes rather than leaking dex files here.
+  for (auto& dex_file : *dex_files) {
     if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
-      (*dex_files)[index] = nullptr;
+      dex_file.release();
     }
-    index++;
   }
-
-  STLDeleteElements(dex_files.get());
-  // Unique_ptr will delete the vector itself.
 }
 
 static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
                                         jlong cookie) {
-  std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env);
+  std::vector<std::unique_ptr<const DexFile>>* dex_files = toDexFiles(cookie, env);
   if (dex_files == NULL) {
     VLOG(class_linker) << "Failed to find dex_file";
     return NULL;
@@ -186,7 +191,7 @@
   }
   const std::string descriptor(DotToDescriptor(class_name.c_str()));
   const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
-  for (const DexFile* dex_file : *dex_files) {
+  for (auto& dex_file : *dex_files) {
     const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
     if (dex_class_def != nullptr) {
       ScopedObjectAccess soa(env);
@@ -218,13 +223,13 @@
 // Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
 static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jlong cookie) {
   jobjectArray result = nullptr;
-  std::vector<const DexFile*>* dex_files = toDexFiles(cookie, env);
+  std::vector<std::unique_ptr<const DexFile>>* dex_files = toDexFiles(cookie, env);
 
   if (dex_files != nullptr) {
     // Push all class descriptors into a set. Use set instead of unordered_set as we want to
     // retrieve all in the end.
     std::set<const char*, CharPointerComparator> descriptors;
-    for (const DexFile* dex_file : *dex_files) {
+    for (auto& dex_file : *dex_files) {
       for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
         const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
         const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -301,7 +306,10 @@
                                                         nullptr,
                                                         false, &error_msg));
   if (oat_file.get() == nullptr) {
-    if (kReasonLogging) {
+    // Note that even though this is kDexoptNeeded, we use
+    // kVerboseLogging instead of the usual kReasonLogging since it is
+    // the common case on first boot and very spammy.
+    if (kVerboseLogging) {
       LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename
           << "' for file location '" << filename << "': " << error_msg;
     }
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index d40d64b..599d97f 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -34,6 +34,7 @@
 #include "gc/heap.h"
 #include "gc/space/dlmalloc_space.h"
 #include "gc/space/image_space.h"
+#include "gc/task_processor.h"
 #include "intern_table.h"
 #include "jni_internal.h"
 #include "mirror/art_method-inl.h"
@@ -133,6 +134,10 @@
   Runtime::Current()->GetHeap()->ClearGrowthLimit();
 }
 
+static void VMRuntime_clampGrowthLimit(JNIEnv*, jobject) {
+  Runtime::Current()->GetHeap()->ClampGrowthLimit();
+}
+
 static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) {
   return Dbg::IsDebuggerActive();
 }
@@ -213,18 +218,38 @@
   runtime->UpdateProfilerState(process_state);
 }
 
-static void VMRuntime_trimHeap(JNIEnv*, jobject) {
-  Runtime::Current()->GetHeap()->DoPendingTransitionOrTrim();
+static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
+  Runtime::Current()->GetHeap()->Trim(ThreadForEnv(env));
 }
 
 static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
   Runtime::Current()->GetHeap()->ConcurrentGC(ThreadForEnv(env));
 }
 
+static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) {
+  Runtime::Current()->GetHeap()->RequestTrim(ThreadForEnv(env));
+}
+
+static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) {
+  Runtime::Current()->GetHeap()->RequestConcurrentGC(ThreadForEnv(env));
+}
+
+static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) {
+  Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(ThreadForEnv(env));
+}
+
+static void VMRuntime_stopHeapTaskProcessor(JNIEnv* env, jobject) {
+  Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(ThreadForEnv(env));
+}
+
+static void VMRuntime_runHeapTasks(JNIEnv* env, jobject) {
+  Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
+}
+
 typedef std::map<std::string, mirror::String*> StringTable;
 
 static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg,
-                                            uint32_t /*thread_id*/, RootType /*root_type*/)
+                                            const RootInfo& /*root_info*/)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   StringTable& table = *reinterpret_cast<StringTable*>(arg);
   mirror::String* string = const_cast<mirror::Object*>(*root)->AsString();
@@ -556,6 +581,7 @@
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
+  NATIVE_METHOD(VMRuntime, clampGrowthLimit, "()V"),
   NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
   NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),
@@ -569,8 +595,13 @@
   NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
   NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
   NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
+  NATIVE_METHOD(VMRuntime, requestConcurrentGC, "()V"),
+  NATIVE_METHOD(VMRuntime, requestHeapTrim, "()V"),
+  NATIVE_METHOD(VMRuntime, runHeapTasks, "()V"),
   NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"),
+  NATIVE_METHOD(VMRuntime, startHeapTaskProcessor, "()V"),
   NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"),
+  NATIVE_METHOD(VMRuntime, stopHeapTaskProcessor, "()V"),
   NATIVE_METHOD(VMRuntime, trimHeap, "()V"),
   NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index f1a04cb..c056adc 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -26,7 +26,7 @@
 #include "ScopedUtfChars.h"
 #include "thread-inl.h"
 
-#if defined(HAVE_PRCTL)
+#if defined(__linux__)
 #include <sys/prctl.h>
 #endif
 
@@ -35,9 +35,9 @@
 namespace art {
 
 static void EnableDebugger() {
+#if defined(__linux__)
   // To let a non-privileged gdbserver attach to this
   // process, we must set our dumpable flag.
-#if defined(HAVE_PRCTL)
   if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
     PLOG(ERROR) << "prctl(PR_SET_DUMPABLE) failed for pid " << getpid();
   }
@@ -86,9 +86,15 @@
   }
   debug_flags &= ~DEBUG_ENABLE_DEBUGGER;
 
-  // These two are for backwards compatibility with Dalvik.
+  if ((debug_flags & DEBUG_ENABLE_SAFEMODE) != 0) {
+    // Ensure that any (secondary) oat files will be interpreted.
+    Runtime* runtime = Runtime::Current();
+    runtime->AddCompilerOption("--compiler-filter=interpret-only");
+    debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
+  }
+
+  // This is for backwards compatibility with Dalvik.
   debug_flags &= ~DEBUG_ENABLE_ASSERT;
-  debug_flags &= ~DEBUG_ENABLE_SAFEMODE;
 
   if (debug_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags);
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 760eb9b..e4b8db1 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -88,6 +88,7 @@
     case kWaitingForSignalCatcherOutput:  return kJavaWaiting;
     case kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
     case kWaitingForMethodTracingStart:   return kJavaWaiting;
+    case kWaitingForVisitObjects:         return kJavaWaiting;
     case kSuspended:                      return kJavaRunnable;
     // Don't add a 'default' here so the compiler can spot incompatible enum changes.
   }
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index 1775468..b7f31f2 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -20,6 +20,7 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "dex_file-inl.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
 #include "scoped_thread_state_change.h"
diff --git a/runtime/oat.cc b/runtime/oat.cc
index eab34f7..c223e2e 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -20,12 +20,13 @@
 #include <zlib.h>
 
 #include "arch/instruction_set_features.h"
+#include "base/stringprintf.h"
 #include "utils.h"
 
 namespace art {
 
-const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '5', '1', '\0' };
+constexpr uint8_t OatHeader::kOatMagic[4];
+constexpr uint8_t OatHeader::kOatVersion[4];
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -67,6 +68,13 @@
                      uint32_t image_file_location_oat_checksum,
                      uint32_t image_file_location_oat_data_begin,
                      const SafeMap<std::string, std::string>* variable_data) {
+  // Don't want asserts in header as they would be checked in each file that includes it. But the
+  // fields are private, so we check inside a method.
+  static_assert(sizeof(magic_) == sizeof(kOatMagic),
+                "Oat magic and magic_ have different lengths.");
+  static_assert(sizeof(version_) == sizeof(kOatVersion),
+                "Oat version and version_ have different lengths.");
+
   memcpy(magic_, kOatMagic, sizeof(kOatMagic));
   memcpy(version_, kOatVersion, sizeof(kOatVersion));
   executable_offset_ = 0;
@@ -105,9 +113,6 @@
   interpreter_to_interpreter_bridge_offset_ = 0;
   interpreter_to_compiled_code_bridge_offset_ = 0;
   jni_dlsym_lookup_offset_ = 0;
-  portable_imt_conflict_trampoline_offset_ = 0;
-  portable_resolution_trampoline_offset_ = 0;
-  portable_to_interpreter_bridge_offset_ = 0;
   quick_generic_jni_trampoline_offset_ = 0;
   quick_imt_conflict_trampoline_offset_ = 0;
   quick_resolution_trampoline_offset_ = 0;
@@ -130,6 +135,28 @@
   return true;
 }
 
+std::string OatHeader::GetValidationErrorMessage() const {
+  if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
+    static_assert(sizeof(kOatMagic) == 4, "kOatMagic has unexpected length");
+    return StringPrintf("Invalid oat magic, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
+                        kOatMagic[0], kOatMagic[1], kOatMagic[2], kOatMagic[3],
+                        magic_[0], magic_[1], magic_[2], magic_[3]);
+  }
+  if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
+    static_assert(sizeof(kOatVersion) == 4, "kOatVersion has unexpected length");
+    return StringPrintf("Invalid oat version, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
+                        kOatVersion[0], kOatVersion[1], kOatVersion[2], kOatVersion[3],
+                        version_[0], version_[1], version_[2], version_[3]);
+  }
+  if (!IsAligned<kPageSize>(executable_offset_)) {
+    return "Executable offset not page-aligned.";
+  }
+  if (!IsAligned<kPageSize>(image_patch_delta_)) {
+    return "Image patch delta not page-aligned.";
+  }
+  return "";
+}
+
 const char* OatHeader::GetMagic() const {
   CHECK(IsValid());
   return reinterpret_cast<const char*>(magic_);
@@ -231,75 +258,18 @@
   UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset));
 }
 
-const void* OatHeader::GetPortableImtConflictTrampoline() const {
-  return reinterpret_cast<const uint8_t*>(this) + GetPortableImtConflictTrampolineOffset();
-}
-
-uint32_t OatHeader::GetPortableImtConflictTrampolineOffset() const {
-  DCHECK(IsValid());
-  CHECK_GE(portable_imt_conflict_trampoline_offset_, jni_dlsym_lookup_offset_);
-  return portable_imt_conflict_trampoline_offset_;
-}
-
-void OatHeader::SetPortableImtConflictTrampolineOffset(uint32_t offset) {
-  CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
-  DCHECK(IsValid());
-  DCHECK_EQ(portable_imt_conflict_trampoline_offset_, 0U) << offset;
-
-  portable_imt_conflict_trampoline_offset_ = offset;
-  UpdateChecksum(&portable_imt_conflict_trampoline_offset_, sizeof(offset));
-}
-
-const void* OatHeader::GetPortableResolutionTrampoline() const {
-  return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset();
-}
-
-uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const {
-  DCHECK(IsValid());
-  CHECK_GE(portable_resolution_trampoline_offset_, portable_imt_conflict_trampoline_offset_);
-  return portable_resolution_trampoline_offset_;
-}
-
-void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) {
-  CHECK(offset == 0 || offset >= portable_imt_conflict_trampoline_offset_);
-  DCHECK(IsValid());
-  DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset;
-
-  portable_resolution_trampoline_offset_ = offset;
-  UpdateChecksum(&portable_resolution_trampoline_offset_, sizeof(offset));
-}
-
-const void* OatHeader::GetPortableToInterpreterBridge() const {
-  return reinterpret_cast<const uint8_t*>(this) + GetPortableToInterpreterBridgeOffset();
-}
-
-uint32_t OatHeader::GetPortableToInterpreterBridgeOffset() const {
-  DCHECK(IsValid());
-  CHECK_GE(portable_to_interpreter_bridge_offset_, portable_resolution_trampoline_offset_);
-  return portable_to_interpreter_bridge_offset_;
-}
-
-void OatHeader::SetPortableToInterpreterBridgeOffset(uint32_t offset) {
-  CHECK(offset == 0 || offset >= portable_resolution_trampoline_offset_);
-  DCHECK(IsValid());
-  DCHECK_EQ(portable_to_interpreter_bridge_offset_, 0U) << offset;
-
-  portable_to_interpreter_bridge_offset_ = offset;
-  UpdateChecksum(&portable_to_interpreter_bridge_offset_, sizeof(offset));
-}
-
 const void* OatHeader::GetQuickGenericJniTrampoline() const {
   return reinterpret_cast<const uint8_t*>(this) + GetQuickGenericJniTrampolineOffset();
 }
 
 uint32_t OatHeader::GetQuickGenericJniTrampolineOffset() const {
   DCHECK(IsValid());
-  CHECK_GE(quick_generic_jni_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+  CHECK_GE(quick_generic_jni_trampoline_offset_, jni_dlsym_lookup_offset_);
   return quick_generic_jni_trampoline_offset_;
 }
 
 void OatHeader::SetQuickGenericJniTrampolineOffset(uint32_t offset) {
-  CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+  CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
   DCHECK(IsValid());
   DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
 
diff --git a/runtime/oat.h b/runtime/oat.h
index 11ed4fb..7faf33b 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
 
 class PACKED(4) OatHeader {
  public:
-  static const uint8_t kOatMagic[4];
-  static const uint8_t kOatVersion[4];
+  static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
+  static constexpr uint8_t kOatVersion[] = { '0', '5', '5', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -47,6 +47,7 @@
                            const SafeMap<std::string, std::string>* variable_data);
 
   bool IsValid() const;
+  std::string GetValidationErrorMessage() const;
   const char* GetMagic() const;
   uint32_t GetChecksum() const;
   void UpdateChecksum(const void* data, size_t length);
@@ -68,16 +69,6 @@
   uint32_t GetJniDlsymLookupOffset() const;
   void SetJniDlsymLookupOffset(uint32_t offset);
 
-  const void* GetPortableResolutionTrampoline() const;
-  uint32_t GetPortableResolutionTrampolineOffset() const;
-  void SetPortableResolutionTrampolineOffset(uint32_t offset);
-  const void* GetPortableImtConflictTrampoline() const;
-  uint32_t GetPortableImtConflictTrampolineOffset() const;
-  void SetPortableImtConflictTrampolineOffset(uint32_t offset);
-  const void* GetPortableToInterpreterBridge() const;
-  uint32_t GetPortableToInterpreterBridgeOffset() const;
-  void SetPortableToInterpreterBridgeOffset(uint32_t offset);
-
   const void* GetQuickGenericJniTrampoline() const;
   uint32_t GetQuickGenericJniTrampolineOffset() const;
   void SetQuickGenericJniTrampolineOffset(uint32_t offset);
@@ -129,9 +120,6 @@
   uint32_t interpreter_to_interpreter_bridge_offset_;
   uint32_t interpreter_to_compiled_code_bridge_offset_;
   uint32_t jni_dlsym_lookup_offset_;
-  uint32_t portable_imt_conflict_trampoline_offset_;
-  uint32_t portable_resolution_trampoline_offset_;
-  uint32_t portable_to_interpreter_bridge_offset_;
   uint32_t quick_generic_jni_trampoline_offset_;
   uint32_t quick_imt_conflict_trampoline_offset_;
   uint32_t quick_resolution_trampoline_offset_;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 91e571b..9061bb3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -75,32 +75,26 @@
   CHECK(!filename.empty()) << location;
   CheckLocation(location);
   std::unique_ptr<OatFile> ret;
-  if (kUsePortableCompiler && executable) {
-    // If we are using PORTABLE, use dlopen to deal with relocations.
-    //
-    // We use our own ELF loader for Quick to deal with legacy apps that
-    // open a generated dex file by name, remove the file, then open
-    // another generated dex file with the same name. http://b/10614658
-    ret.reset(OpenDlopen(filename, location, requested_base, error_msg));
-  } else {
-    // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
-    //
-    // On target, dlopen may fail when compiling due to selinux restrictions on installd.
-    //
-    // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile.
-    // This won't work for portable runtime execution because it doesn't process relocations.
-    std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
-    if (file.get() == NULL) {
-      *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
-      return nullptr;
-    }
-    ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
-                          error_msg));
-
-    // It would be nice to unlink here. But we might have opened the file created by the
-    // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
-    // to allow removal when we know the ELF must be borked.
+  // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
+  //
+  // On target, dlopen may fail when compiling due to selinux restrictions on installd.
+  //
+  // We use our own ELF loader for Quick to deal with legacy apps that
+  // open a generated dex file by name, remove the file, then open
+  // another generated dex file with the same name. http://b/10614658
+  //
+  // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile.
+  std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
+  if (file.get() == NULL) {
+    *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
+    return nullptr;
   }
+  ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+                        error_msg));
+
+  // It would be nice to unlink here. But we might have opened the file created by the
+  // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
+  // to allow removal when we know the ELF must be borked.
   return ret.release();
 }
 
@@ -233,7 +227,9 @@
 
 bool OatFile::Setup(std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
-    *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str());
+    std::string cause = GetOatHeader().GetValidationErrorMessage();
+    *error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
+                              cause.c_str());
     return false;
   }
   const uint8_t* oat = Begin();
@@ -458,9 +454,9 @@
   return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_;
 }
 
-const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
+std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
   return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
-                       dex_file_location_checksum_, error_msg);
+                       dex_file_location_checksum_, GetOatFile(), error_msg);
 }
 
 uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
@@ -591,7 +587,6 @@
 
 void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
   CHECK(method != NULL);
-  method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
   method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
 }
 
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index a335c94..6ae3c3e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -97,29 +97,8 @@
       return code_offset_;
     }
 
-    const void* GetPortableCode() const {
-      // TODO: encode whether code is portable/quick in flags within OatMethod.
-      if (kUsePortableCompiler) {
-        return GetOatPointer<const void*>(code_offset_);
-      } else {
-        return nullptr;
-      }
-    }
-
     const void* GetQuickCode() const {
-      if (kUsePortableCompiler) {
-        return nullptr;
-      } else {
-        return GetOatPointer<const void*>(code_offset_);
-      }
-    }
-
-    // Returns 0.
-    uint32_t GetPortableCodeSize() const {
-      // TODO: With Quick, we store the size before the code. With Portable, the code is in a .o
-      // file we don't manage ourselves. ELF symbols do have a concept of size, so we could capture
-      // that and store it somewhere, such as the OatMethod.
-      return 0;
+      return GetOatPointer<const void*>(code_offset_);
     }
 
     // Returns size of quick code.
@@ -231,7 +210,7 @@
   class OatDexFile {
    public:
     // Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
-    const DexFile* OpenDexFile(std::string* error_msg) const;
+    std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const;
 
     const OatFile* GetOatFile() const {
       return oat_file_;
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 592deed..cf81cc5 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -35,34 +35,10 @@
 }  // namespace mirror
 class StackVisitor;
 
-enum RootType {
-  kRootUnknown = 0,
-  kRootJNIGlobal,
-  kRootJNILocal,
-  kRootJavaFrame,
-  kRootNativeStack,
-  kRootStickyClass,
-  kRootThreadBlock,
-  kRootMonitorUsed,
-  kRootThreadObject,
-  kRootInternedString,
-  kRootDebugger,
-  kRootVMInternal,
-  kRootJNIMonitor,
-};
-std::ostream& operator<<(std::ostream& os, const RootType& root_type);
-
-// Returns the new address of the object, returns root if it has not moved. tid and root_type are
-// only used by hprof.
-typedef void (RootCallback)(mirror::Object** root, void* arg, uint32_t thread_id,
-    RootType root_type);
 // A callback for visiting an object in the heap.
 typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
 // A callback used for marking an object, returns the new address of the object if the object moved.
 typedef mirror::Object* (MarkObjectCallback)(mirror::Object* obj, void* arg) WARN_UNUSED;
-// A callback for verifying roots.
-typedef void (VerifyRootCallback)(const mirror::Object* root, void* arg, size_t vreg,
-    const StackVisitor* visitor, RootType root_type);
 
 typedef void (MarkHeapReferenceCallback)(mirror::HeapReference<mirror::Object>* ref, void* arg);
 typedef void (DelayReferenceReferentCallback)(mirror::Class* klass, mirror::Reference* ref, void* arg);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 3e6c86b..a5df892 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -18,10 +18,6 @@
 
 #include <sstream>
 
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
 #include "base/stringpiece.h"
 #include "debugger.h"
 #include "gc/heap.h"
@@ -30,234 +26,327 @@
 #include "trace.h"
 #include "utils.h"
 
+#include "cmdline_parser.h"
+#include "runtime_options.h"
+
 namespace art {
 
+using MemoryKiB = Memory<1024>;
+
 ParsedOptions::ParsedOptions()
-    :
-    boot_class_path_(nullptr),
-    check_jni_(kIsDebugBuild),                      // -Xcheck:jni is off by default for regular
-                                                    // builds but on by default in debug builds.
-    force_copy_(false),
-    compiler_callbacks_(nullptr),
-    is_zygote_(false),
-    must_relocate_(kDefaultMustRelocate),
-    dex2oat_enabled_(true),
-    image_dex2oat_enabled_(true),
-    interpreter_only_(kPoisonHeapReferences),       // kPoisonHeapReferences currently works with
-                                                    // the interpreter only.
-                                                    // TODO: make it work with the compiler.
-    is_explicit_gc_disabled_(false),
-    use_tlab_(false),
-    verify_pre_gc_heap_(false),
-    verify_pre_sweeping_heap_(kIsDebugBuild),       // Pre sweeping is the one that usually fails
-                                                    // if the GC corrupted the heap.
-    verify_post_gc_heap_(false),
-    verify_pre_gc_rosalloc_(kIsDebugBuild),
-    verify_pre_sweeping_rosalloc_(false),
-    verify_post_gc_rosalloc_(false),
-    long_pause_log_threshold_(gc::Heap::kDefaultLongPauseLogThreshold),
-    long_gc_log_threshold_(gc::Heap::kDefaultLongGCLogThreshold),
-    dump_gc_performance_on_shutdown_(false),
-    ignore_max_footprint_(false),
-    heap_initial_size_(gc::Heap::kDefaultInitialSize),
-    heap_maximum_size_(gc::Heap::kDefaultMaximumSize),
-    heap_growth_limit_(0),                          // 0 means no growth limit.
-    heap_min_free_(gc::Heap::kDefaultMinFree),
-    heap_max_free_(gc::Heap::kDefaultMaxFree),
-    heap_non_moving_space_capacity_(gc::Heap::kDefaultNonMovingSpaceCapacity),
-    large_object_space_type_(gc::Heap::kDefaultLargeObjectSpaceType),
-    large_object_threshold_(gc::Heap::kDefaultLargeObjectThreshold),
-    heap_target_utilization_(gc::Heap::kDefaultTargetUtilization),
-    foreground_heap_growth_multiplier_(gc::Heap::kDefaultHeapGrowthMultiplier),
-    parallel_gc_threads_(1),
-    conc_gc_threads_(0),                            // Only the main GC thread, no workers.
-    collector_type_(                                // The default GC type is set in makefiles.
-#if ART_DEFAULT_GC_TYPE_IS_CMS
-        gc::kCollectorTypeCMS),
-#elif ART_DEFAULT_GC_TYPE_IS_SS
-        gc::kCollectorTypeSS),
-#elif ART_DEFAULT_GC_TYPE_IS_GSS
-    gc::kCollectorTypeGSS),
-#else
-    gc::kCollectorTypeCMS),
-#error "ART default GC type must be set"
-#endif
-    background_collector_type_(gc::kCollectorTypeNone),
-                                                    // If background_collector_type_ is
-                                                    // kCollectorTypeNone, it defaults to the
-                                                    // collector_type_ after parsing options. If
-                                                    // you set this to kCollectorTypeHSpaceCompact
-                                                    // then we will do an hspace compaction when
-                                                    // we transition to background instead of a
-                                                    // normal collector transition.
-    stack_size_(0),                                 // 0 means default.
-    max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
-    low_memory_mode_(false),
-    lock_profiling_threshold_(0),
-    method_trace_(false),
-    method_trace_file_("/data/method-trace-file.bin"),
-    method_trace_file_size_(10 * MB),
-    hook_is_sensitive_thread_(nullptr),
+  : hook_is_sensitive_thread_(nullptr),
     hook_vfprintf_(vfprintf),
     hook_exit_(exit),
-    hook_abort_(nullptr),                           // We don't call abort(3) by default; see
-                                                    // Runtime::Abort.
-    profile_clock_source_(kDefaultTraceClockSource),
-    verify_(true),
-    image_isa_(kRuntimeISA),
-    use_homogeneous_space_compaction_for_oom_(true),  // Enable hspace compaction on OOM by default.
-    min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000))  // 100s.
-    {}
+    hook_abort_(nullptr) {                          // We don't call abort(3) by default; see
+                                                    // Runtime::Abort
+}
 
-ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized) {
+ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized,
+                                     RuntimeArgumentMap* runtime_options) {
+  CHECK(runtime_options != nullptr);
+
   std::unique_ptr<ParsedOptions> parsed(new ParsedOptions());
-  if (parsed->Parse(options, ignore_unrecognized)) {
+  if (parsed->Parse(options, ignore_unrecognized, runtime_options)) {
     return parsed.release();
   }
   return nullptr;
 }
 
-// Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
-// memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
-// [gG] gigabytes.
-//
-// "s" should point just past the "-Xm?" part of the string.
-// "div" specifies a divisor, e.g. 1024 if the value must be a multiple
-// of 1024.
-//
-// The spec says the -Xmx and -Xms options must be multiples of 1024.  It
-// doesn't say anything about -Xss.
-//
-// Returns 0 (a useless size) if "s" is malformed or specifies a low or
-// non-evenly-divisible value.
-//
-size_t ParseMemoryOption(const char* s, size_t div) {
-  // strtoul accepts a leading [+-], which we don't want,
-  // so make sure our string starts with a decimal digit.
-  if (isdigit(*s)) {
-    char* s2;
-    size_t val = strtoul(s, &s2, 10);
-    if (s2 != s) {
-      // s2 should be pointing just after the number.
-      // If this is the end of the string, the user
-      // has specified a number of bytes.  Otherwise,
-      // there should be exactly one more character
-      // that specifies a multiplier.
-      if (*s2 != '\0') {
-        // The remainder of the string is either a single multiplier
-        // character, or nothing to indicate that the value is in
-        // bytes.
-        char c = *s2++;
-        if (*s2 == '\0') {
-          size_t mul;
-          if (c == '\0') {
-            mul = 1;
-          } else if (c == 'k' || c == 'K') {
-            mul = KB;
-          } else if (c == 'm' || c == 'M') {
-            mul = MB;
-          } else if (c == 'g' || c == 'G') {
-            mul = GB;
-          } else {
-            // Unknown multiplier character.
-            return 0;
-          }
+using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
 
-          if (val <= std::numeric_limits<size_t>::max() / mul) {
-            val *= mul;
-          } else {
-            // Clamp to a multiple of 1024.
-            val = std::numeric_limits<size_t>::max() & ~(1024-1);
-          }
-        } else {
-          // There's more than one character after the numeric part.
-          return 0;
-        }
-      }
-      // The man page says that a -Xm value must be a multiple of 1024.
-      if (val % div == 0) {
-        return val;
-      }
-    }
-  }
-  return 0;
+// Yes, the stack frame is huge. But we get called super early on (and just once)
+// to pass the command line arguments, so we'll probably be ok.
+// Ideas to avoid suppressing this diagnostic are welcome!
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognized) {
+  using M = RuntimeArgumentMap;
+
+  std::unique_ptr<RuntimeParser::Builder> parser_builder =
+      std::unique_ptr<RuntimeParser::Builder>(new RuntimeParser::Builder());
+
+  parser_builder->
+       Define("-Xzygote")
+          .IntoKey(M::Zygote)
+      .Define("-help")
+          .IntoKey(M::Help)
+      .Define("-showversion")
+          .IntoKey(M::ShowVersion)
+      .Define("-Xbootclasspath:_")
+          .WithType<std::string>()
+          .IntoKey(M::BootClassPath)
+      .Define("-Xbootclasspath-locations:_")
+          .WithType<ParseStringList<':'>>()  // std::vector<std::string>, split by :
+          .IntoKey(M::BootClassPathLocations)
+      .Define({"-classpath _", "-cp _"})
+          .WithType<std::string>()
+          .IntoKey(M::ClassPath)
+      .Define("-Ximage:_")
+          .WithType<std::string>()
+          .IntoKey(M::Image)
+      .Define("-Xcheck:jni")
+          .IntoKey(M::CheckJni)
+      .Define("-Xjniopts:forcecopy")
+          .IntoKey(M::JniOptsForceCopy)
+      .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
+          .WithType<JDWP::JdwpOptions>()
+          .IntoKey(M::JdwpOptions)
+      .Define("-Xms_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::MemoryInitialSize)
+      .Define("-Xmx_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::MemoryMaximumSize)
+      .Define("-XX:HeapGrowthLimit=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::HeapGrowthLimit)
+      .Define("-XX:HeapMinFree=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::HeapMinFree)
+      .Define("-XX:HeapMaxFree=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::HeapMaxFree)
+      .Define("-XX:NonMovingSpaceCapacity=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::NonMovingSpaceCapacity)
+      .Define("-XX:HeapTargetUtilization=_")
+          .WithType<double>().WithRange(0.1, 0.9)
+          .IntoKey(M::HeapTargetUtilization)
+      .Define("-XX:ForegroundHeapGrowthMultiplier=_")
+          .WithType<double>().WithRange(0.1, 1.0)
+          .IntoKey(M::ForegroundHeapGrowthMultiplier)
+      .Define("-XX:ParallelGCThreads=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::ParallelGCThreads)
+      .Define("-XX:ConcGCThreads=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::ConcGCThreads)
+      .Define("-Xss_")
+          .WithType<Memory<1>>()
+          .IntoKey(M::StackSize)
+      .Define("-XX:MaxSpinsBeforeThinLockInflation=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::MaxSpinsBeforeThinLockInflation)
+      .Define("-XX:LongPauseLogThreshold=_")  // in ms
+          .WithType<MillisecondsToNanoseconds>()  // store as ns
+          .IntoKey(M::LongPauseLogThreshold)
+      .Define("-XX:LongGCLogThreshold=_")  // in ms
+          .WithType<MillisecondsToNanoseconds>()  // store as ns
+          .IntoKey(M::LongGCLogThreshold)
+      .Define("-XX:DumpGCPerformanceOnShutdown")
+          .IntoKey(M::DumpGCPerformanceOnShutdown)
+      .Define("-XX:IgnoreMaxFootprint")
+          .IntoKey(M::IgnoreMaxFootprint)
+      .Define("-XX:LowMemoryMode")
+          .IntoKey(M::LowMemoryMode)
+      .Define("-XX:UseTLAB")
+          .IntoKey(M::UseTLAB)
+      .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
+          .WithValues({true, false})
+          .IntoKey(M::EnableHSpaceCompactForOOM)
+      .Define("-XX:HspaceCompactForOOMMinIntervalMs=_")  // in ms
+          .WithType<MillisecondsToNanoseconds>()  // store as ns
+          .IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
+      .Define("-D_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::PropertiesList)
+      .Define("-Xjnitrace:_")
+          .WithType<std::string>()
+          .IntoKey(M::JniTrace)
+      .Define("-Xpatchoat:_")
+          .WithType<std::string>()
+          .IntoKey(M::PatchOat)
+      .Define({"-Xrelocate", "-Xnorelocate"})
+          .WithValues({true, false})
+          .IntoKey(M::Relocate)
+      .Define({"-Xdex2oat", "-Xnodex2oat"})
+          .WithValues({true, false})
+          .IntoKey(M::Dex2Oat)
+      .Define({"-Ximage-dex2oat", "-Xnoimage-dex2oat"})
+          .WithValues({true, false})
+          .IntoKey(M::ImageDex2Oat)
+      .Define("-Xint")
+          .WithValue(true)
+          .IntoKey(M::Interpret)
+      .Define("-Xgc:_")
+          .WithType<XGcOption>()
+          .IntoKey(M::GcOption)
+      .Define("-XX:LargeObjectSpace=_")
+          .WithType<gc::space::LargeObjectSpaceType>()
+          .WithValueMap({{"disabled", gc::space::LargeObjectSpaceType::kDisabled},
+                         {"freelist", gc::space::LargeObjectSpaceType::kFreeList},
+                         {"map",      gc::space::LargeObjectSpaceType::kMap}})
+          .IntoKey(M::LargeObjectSpace)
+      .Define("-XX:LargeObjectThreshold=_")
+          .WithType<Memory<1>>()
+          .IntoKey(M::LargeObjectThreshold)
+      .Define("-XX:BackgroundGC=_")
+          .WithType<BackgroundGcOption>()
+          .IntoKey(M::BackgroundGc)
+      .Define("-XX:+DisableExplicitGC")
+          .IntoKey(M::DisableExplicitGC)
+      .Define("-verbose:_")
+          .WithType<LogVerbosity>()
+          .IntoKey(M::Verbose)
+      .Define("-Xlockprofthreshold:_")
+          .WithType<unsigned int>()
+          .IntoKey(M::LockProfThreshold)
+      .Define("-Xstacktracefile:_")
+          .WithType<std::string>()
+          .IntoKey(M::StackTraceFile)
+      .Define("-Xmethod-trace")
+          .IntoKey(M::MethodTrace)
+      .Define("-Xmethod-trace-file:_")
+          .WithType<std::string>()
+          .IntoKey(M::MethodTraceFile)
+      .Define("-Xmethod-trace-file-size:_")
+          .WithType<unsigned int>()
+          .IntoKey(M::MethodTraceFileSize)
+      .Define("-Xprofile:_")
+          .WithType<TraceClockSource>()
+          .WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu},
+                         {"wallclock",      TraceClockSource::kWall},
+                         {"dualclock",      TraceClockSource::kDual}})
+          .IntoKey(M::ProfileClock)
+      .Define("-Xenable-profiler")
+          .WithType<TestProfilerOptions>()
+          .AppendValues()
+          .IntoKey(M::ProfilerOpts)  // NOTE: Appends into same key as -Xprofile-*
+      .Define("-Xprofile-_")  // -Xprofile-<key>:<value>
+          .WithType<TestProfilerOptions>()
+          .AppendValues()
+          .IntoKey(M::ProfilerOpts)  // NOTE: Appends into same key as -Xenable-profiler
+      .Define("-Xcompiler:_")
+          .WithType<std::string>()
+          .IntoKey(M::Compiler)
+      .Define("-Xcompiler-option _")
+          .WithType<std::vector<std::string>>()
+          .AppendValues()
+          .IntoKey(M::CompilerOptions)
+      .Define("-Ximage-compiler-option _")
+          .WithType<std::vector<std::string>>()
+          .AppendValues()
+          .IntoKey(M::ImageCompilerOptions)
+      .Define("-Xverify:_")
+          .WithType<bool>()
+          .WithValueMap({{"none", false},
+                         {"remote", true},
+                         {"all", true}})
+          .IntoKey(M::Verify)
+      .Define("-XX:NativeBridge=_")
+          .WithType<std::string>()
+          .IntoKey(M::NativeBridge)
+      .Ignore({
+          "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
+          "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
+          "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
+          "-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_",
+          "-Xincludeselectedmethod", "-Xjitthreshold:_", "-Xjitcodecachesize:_",
+          "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_",
+          "-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
+          "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
+      .IgnoreUnrecognized(ignore_unrecognized);
+
+  // TODO: Move Usage information into this DSL.
+
+  return std::unique_ptr<RuntimeParser>(new RuntimeParser(parser_builder->Build()));
 }
 
-static gc::CollectorType ParseCollectorType(const std::string& option) {
-  if (option == "MS" || option == "nonconcurrent") {
-    return gc::kCollectorTypeMS;
-  } else if (option == "CMS" || option == "concurrent") {
-    return gc::kCollectorTypeCMS;
-  } else if (option == "SS") {
-    return gc::kCollectorTypeSS;
-  } else if (option == "GSS") {
-    return gc::kCollectorTypeGSS;
-  } else if (option == "CC") {
-    return gc::kCollectorTypeCC;
-  } else if (option == "MC") {
-    return gc::kCollectorTypeMC;
-  } else {
-    return gc::kCollectorTypeNone;
-  }
-}
+#pragma GCC diagnostic pop
 
-bool ParsedOptions::ParseXGcOption(const std::string& option) {
-  std::vector<std::string> gc_options;
-  Split(option.substr(strlen("-Xgc:")), ',', &gc_options);
-  for (const std::string& gc_option : gc_options) {
-    gc::CollectorType collector_type = ParseCollectorType(gc_option);
-    if (collector_type != gc::kCollectorTypeNone) {
-      collector_type_ = collector_type;
-    } else if (gc_option == "preverify") {
-      verify_pre_gc_heap_ = true;
-    } else if (gc_option == "nopreverify") {
-      verify_pre_gc_heap_ = false;
-    }  else if (gc_option == "presweepingverify") {
-      verify_pre_sweeping_heap_ = true;
-    } else if (gc_option == "nopresweepingverify") {
-      verify_pre_sweeping_heap_ = false;
-    } else if (gc_option == "postverify") {
-      verify_post_gc_heap_ = true;
-    } else if (gc_option == "nopostverify") {
-      verify_post_gc_heap_ = false;
-    } else if (gc_option == "preverify_rosalloc") {
-      verify_pre_gc_rosalloc_ = true;
-    } else if (gc_option == "nopreverify_rosalloc") {
-      verify_pre_gc_rosalloc_ = false;
-    } else if (gc_option == "presweepingverify_rosalloc") {
-      verify_pre_sweeping_rosalloc_ = true;
-    } else if (gc_option == "nopresweepingverify_rosalloc") {
-      verify_pre_sweeping_rosalloc_ = false;
-    } else if (gc_option == "postverify_rosalloc") {
-      verify_post_gc_rosalloc_ = true;
-    } else if (gc_option == "nopostverify_rosalloc") {
-      verify_post_gc_rosalloc_ = false;
-    } else if ((gc_option == "precise") ||
-               (gc_option == "noprecise") ||
-               (gc_option == "verifycardtable") ||
-               (gc_option == "noverifycardtable")) {
-      // Ignored for backwards compatibility.
+// Remove all the special options that have something in the void* part of the option.
+// If runtime_options is not null, put the options in there.
+// As a side-effect, populate the hooks from options.
+bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options,
+                                          RuntimeArgumentMap* runtime_options,
+                                          std::vector<std::string>* out_options) {
+  using M = RuntimeArgumentMap;
+
+  // TODO: Move the below loop into JNI
+  // Handle special options that set up hooks
+  for (size_t i = 0; i < options.size(); ++i) {
+    const std::string option(options[i].first);
+      // TODO: support -Djava.class.path
+    if (option == "bootclasspath") {
+      auto boot_class_path
+          = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second);
+
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::BootClassPathDexList, boot_class_path);
+      }
+    } else if (option == "compilercallbacks") {
+      CompilerCallbacks* compiler_callbacks =
+          reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::CompilerCallbacksPtr, compiler_callbacks);
+      }
+    } else if (option == "imageinstructionset") {
+      const char* isa_str = reinterpret_cast<const char*>(options[i].second);
+      auto&& image_isa = GetInstructionSetFromString(isa_str);
+      if (image_isa == kNone) {
+        Usage("%s is not a valid instruction set.", isa_str);
+        return false;
+      }
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::ImageInstructionSet, image_isa);
+      }
+    } else if (option == "sensitiveThread") {
+      const void* hook = options[i].second;
+      bool (*hook_is_sensitive_thread)() = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));
+
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookIsSensitiveThread, hook_is_sensitive_thread);
+      }
+    } else if (option == "vfprintf") {
+      const void* hook = options[i].second;
+      if (hook == nullptr) {
+        Usage("vfprintf argument was NULL");
+        return false;
+      }
+      int (*hook_vfprintf)(FILE *, const char*, va_list) =
+          reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));
+
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookVfprintf, hook_vfprintf);
+      }
+      hook_vfprintf_ = hook_vfprintf;
+    } else if (option == "exit") {
+      const void* hook = options[i].second;
+      if (hook == nullptr) {
+        Usage("exit argument was NULL");
+        return false;
+      }
+      void(*hook_exit)(jint) = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookExit, hook_exit);
+      }
+      hook_exit_ = hook_exit;
+    } else if (option == "abort") {
+      const void* hook = options[i].second;
+      if (hook == nullptr) {
+        Usage("abort was NULL\n");
+        return false;
+      }
+      void(*hook_abort)() = reinterpret_cast<void(*)()>(const_cast<void*>(hook));
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookAbort, hook_abort);
+      }
+      hook_abort_ = hook_abort;
     } else {
-      Usage("Unknown -Xgc option %s\n", gc_option.c_str());
-      return false;
+      // It is a regular option, that doesn't have a known 'second' value.
+      // Push it on to the regular options which will be parsed by our parser.
+      if (out_options != nullptr) {
+        out_options->push_back(option);
+      }
     }
   }
+
   return true;
 }
 
-bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized) {
-  const char* boot_class_path_string = getenv("BOOTCLASSPATH");
-  if (boot_class_path_string != NULL) {
-    boot_class_path_string_ = boot_class_path_string;
-  }
-  const char* class_path_string = getenv("CLASSPATH");
-  if (class_path_string != NULL) {
-    class_path_string_ = class_path_string;
-  }
-
-  // Default to number of processors minus one since the main GC thread also does work.
-  parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
-
+bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized,
+                          RuntimeArgumentMap* runtime_options) {
 //  gLogVerbosity.class_linker = true;  // TODO: don't check this in!
 //  gLogVerbosity.compiler = true;  // TODO: don't check this in!
 //  gLogVerbosity.gc = true;  // TODO: don't check this in!
@@ -277,425 +366,104 @@
       LOG(INFO) << "option[" << i << "]=" << options[i].first;
     }
   }
-  for (size_t i = 0; i < options.size(); ++i) {
-    const std::string option(options[i].first);
-    if (StartsWith(option, "-help")) {
-      Usage(nullptr);
-      return false;
-    } else if (StartsWith(option, "-showversion")) {
-      UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
+
+  auto parser = MakeParser(ignore_unrecognized);
+
+  // Convert to a simple string list (without the magic pointer options)
+  std::vector<std::string> argv_list;
+  if (!ProcessSpecialOptions(options, nullptr, &argv_list)) {
+    return false;
+  }
+
+  CmdlineResult parse_result = parser->Parse(argv_list);
+
+  // Handle parse errors by displaying the usage and potentially exiting.
+  if (parse_result.IsError()) {
+    if (parse_result.GetStatus() == CmdlineResult::kUsage) {
+      UsageMessage(stdout, "%s\n", parse_result.GetMessage().c_str());
       Exit(0);
-    } else if (StartsWith(option, "-Xbootclasspath:")) {
-      boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data();
-      LOG(INFO) << "setting boot class path to " << boot_class_path_string_;
-    } else if (option == "-classpath" || option == "-cp") {
-      // TODO: support -Djava.class.path
-      i++;
-      if (i == options.size()) {
-        Usage("Missing required class path value for %s\n", option.c_str());
-        return false;
-      }
-      const StringPiece& value = options[i].first;
-      class_path_string_ = value.data();
-    } else if (option == "bootclasspath") {
-      boot_class_path_
-          = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second);
-    } else if (StartsWith(option, "-Ximage:")) {
-      if (!ParseStringAfterChar(option, ':', &image_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xcheck:jni")) {
-      check_jni_ = true;
-    } else if (StartsWith(option, "-Xjniopts:forcecopy")) {
-      force_copy_ = true;
-    } else if (StartsWith(option, "-Xrunjdwp:") || StartsWith(option, "-agentlib:jdwp=")) {
-      std::string tail(option.substr(option[1] == 'X' ? 10 : 15));
-      // TODO: move parsing logic out of Dbg
-      if (tail == "help" || !Dbg::ParseJdwpOptions(tail)) {
-        if (tail != "help") {
-          UsageMessage(stderr, "Failed to parse JDWP option %s\n", tail.c_str());
-        }
-        Usage("Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
-              "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
-        return false;
-      }
-    } else if (StartsWith(option, "-Xms")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_initial_size_ = size;
-    } else if (StartsWith(option, "-Xmx")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-Xmx")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_maximum_size_ = size;
-    } else if (StartsWith(option, "-XX:HeapGrowthLimit=")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapGrowthLimit=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_growth_limit_ = size;
-    } else if (StartsWith(option, "-XX:HeapMinFree=")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapMinFree=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_min_free_ = size;
-    } else if (StartsWith(option, "-XX:HeapMaxFree=")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapMaxFree=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_max_free_ = size;
-    } else if (StartsWith(option, "-XX:NonMovingSpaceCapacity=")) {
-      size_t size = ParseMemoryOption(
-          option.substr(strlen("-XX:NonMovingSpaceCapacity=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_non_moving_space_capacity_ = size;
-    } else if (StartsWith(option, "-XX:HeapTargetUtilization=")) {
-      if (!ParseDouble(option, '=', 0.1, 0.9, &heap_target_utilization_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:ForegroundHeapGrowthMultiplier=")) {
-      if (!ParseDouble(option, '=', 0.1, 10.0, &foreground_heap_growth_multiplier_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:ParallelGCThreads=")) {
-      if (!ParseUnsignedInteger(option, '=', &parallel_gc_threads_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:ConcGCThreads=")) {
-      if (!ParseUnsignedInteger(option, '=', &conc_gc_threads_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xss")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).c_str(), 1);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      stack_size_ = size;
-    } else if (StartsWith(option, "-XX:MaxSpinsBeforeThinLockInflation=")) {
-      if (!ParseUnsignedInteger(option, '=', &max_spins_before_thin_lock_inflation_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:LongPauseLogThreshold=")) {
-      unsigned int value;
-      if (!ParseUnsignedInteger(option, '=', &value)) {
-        return false;
-      }
-      long_pause_log_threshold_ = MsToNs(value);
-    } else if (StartsWith(option, "-XX:LongGCLogThreshold=")) {
-      unsigned int value;
-      if (!ParseUnsignedInteger(option, '=', &value)) {
-        return false;
-      }
-      long_gc_log_threshold_ = MsToNs(value);
-    } else if (option == "-XX:DumpGCPerformanceOnShutdown") {
-      dump_gc_performance_on_shutdown_ = true;
-    } else if (option == "-XX:IgnoreMaxFootprint") {
-      ignore_max_footprint_ = true;
-    } else if (option == "-XX:LowMemoryMode") {
-      low_memory_mode_ = true;
-      // TODO Might want to turn off must_relocate here.
-    } else if (option == "-XX:UseTLAB") {
-      use_tlab_ = true;
-    } else if (option == "-XX:EnableHSpaceCompactForOOM") {
-      use_homogeneous_space_compaction_for_oom_ = true;
-    } else if (option == "-XX:DisableHSpaceCompactForOOM") {
-      use_homogeneous_space_compaction_for_oom_ = false;
-    } else if (StartsWith(option, "-D")) {
-      properties_.push_back(option.substr(strlen("-D")));
-    } else if (StartsWith(option, "-Xjnitrace:")) {
-      jni_trace_ = option.substr(strlen("-Xjnitrace:"));
-    } else if (option == "compilercallbacks") {
-      compiler_callbacks_ =
-          reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
-    } else if (option == "imageinstructionset") {
-      const char* isa_str = reinterpret_cast<const char*>(options[i].second);
-      image_isa_ = GetInstructionSetFromString(isa_str);
-      if (image_isa_ == kNone) {
-        Usage("%s is not a valid instruction set.", isa_str);
-        return false;
-      }
-    } else if (option == "-Xzygote") {
-      is_zygote_ = true;
-    } else if (StartsWith(option, "-Xpatchoat:")) {
-      if (!ParseStringAfterChar(option, ':', &patchoat_executable_)) {
-        return false;
-      }
-    } else if (option == "-Xrelocate") {
-      must_relocate_ = true;
-    } else if (option == "-Xnorelocate") {
-      must_relocate_ = false;
-    } else if (option == "-Xnodex2oat") {
-      dex2oat_enabled_ = false;
-    } else if (option == "-Xdex2oat") {
-      dex2oat_enabled_ = true;
-    } else if (option == "-Xnoimage-dex2oat") {
-      image_dex2oat_enabled_ = false;
-    } else if (option == "-Ximage-dex2oat") {
-      image_dex2oat_enabled_ = true;
-    } else if (option == "-Xint") {
-      interpreter_only_ = true;
-    } else if (StartsWith(option, "-Xgc:")) {
-      if (!ParseXGcOption(option)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:LargeObjectSpace=")) {
-      std::string substring;
-      if (!ParseStringAfterChar(option, '=', &substring)) {
-        return false;
-      }
-      if (substring == "disabled") {
-        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeDisabled;
-      } else if (substring == "freelist") {
-        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeFreeList;
-      } else if (substring == "map") {
-        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeMap;
-      } else {
-        Usage("Unknown -XX:LargeObjectSpace= option %s\n", substring.c_str());
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:LargeObjectThreshold=")) {
-      std::string substring;
-      if (!ParseStringAfterChar(option, '=', &substring)) {
-        return false;
-      }
-      size_t size = ParseMemoryOption(substring.c_str(), 1);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      large_object_threshold_ = size;
-    } else if (StartsWith(option, "-XX:BackgroundGC=")) {
-      std::string substring;
-      if (!ParseStringAfterChar(option, '=', &substring)) {
-        return false;
-      }
-      // Special handling for HSpaceCompact since this is only valid as a background GC type.
-      if (substring == "HSpaceCompact") {
-        background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
-      } else {
-        gc::CollectorType collector_type = ParseCollectorType(substring);
-        if (collector_type != gc::kCollectorTypeNone) {
-          background_collector_type_ = collector_type;
-        } else {
-          Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str());
-          return false;
-        }
-      }
-    } else if (option == "-XX:+DisableExplicitGC") {
-      is_explicit_gc_disabled_ = true;
-    } else if (StartsWith(option, "-verbose:")) {
-      std::vector<std::string> verbose_options;
-      Split(option.substr(strlen("-verbose:")), ',', &verbose_options);
-      for (size_t j = 0; j < verbose_options.size(); ++j) {
-        if (verbose_options[j] == "class") {
-          gLogVerbosity.class_linker = true;
-        } else if (verbose_options[j] == "compiler") {
-          gLogVerbosity.compiler = true;
-        } else if (verbose_options[j] == "gc") {
-          gLogVerbosity.gc = true;
-        } else if (verbose_options[j] == "heap") {
-          gLogVerbosity.heap = true;
-        } else if (verbose_options[j] == "jdwp") {
-          gLogVerbosity.jdwp = true;
-        } else if (verbose_options[j] == "jni") {
-          gLogVerbosity.jni = true;
-        } else if (verbose_options[j] == "monitor") {
-          gLogVerbosity.monitor = true;
-        } else if (verbose_options[j] == "profiler") {
-          gLogVerbosity.profiler = true;
-        } else if (verbose_options[j] == "signals") {
-          gLogVerbosity.signals = true;
-        } else if (verbose_options[j] == "startup") {
-          gLogVerbosity.startup = true;
-        } else if (verbose_options[j] == "third-party-jni") {
-          gLogVerbosity.third_party_jni = true;
-        } else if (verbose_options[j] == "threads") {
-          gLogVerbosity.threads = true;
-        } else if (verbose_options[j] == "verifier") {
-          gLogVerbosity.verifier = true;
-        } else {
-          Usage("Unknown -verbose option %s\n", verbose_options[j].c_str());
-          return false;
-        }
-      }
-    } else if (StartsWith(option, "-Xlockprofthreshold:")) {
-      if (!ParseUnsignedInteger(option, ':', &lock_profiling_threshold_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xstacktracefile:")) {
-      if (!ParseStringAfterChar(option, ':', &stack_trace_file_)) {
-        return false;
-      }
-    } else if (option == "sensitiveThread") {
-      const void* hook = options[i].second;
-      hook_is_sensitive_thread_ = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));
-    } else if (option == "vfprintf") {
-      const void* hook = options[i].second;
-      if (hook == nullptr) {
-        Usage("vfprintf argument was NULL");
-        return false;
-      }
-      hook_vfprintf_ =
-          reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));
-    } else if (option == "exit") {
-      const void* hook = options[i].second;
-      if (hook == nullptr) {
-        Usage("exit argument was NULL");
-        return false;
-      }
-      hook_exit_ = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
-    } else if (option == "abort") {
-      const void* hook = options[i].second;
-      if (hook == nullptr) {
-        Usage("abort was NULL\n");
-        return false;
-      }
-      hook_abort_ = reinterpret_cast<void(*)()>(const_cast<void*>(hook));
-    } else if (option == "-Xmethod-trace") {
-      method_trace_ = true;
-    } else if (StartsWith(option, "-Xmethod-trace-file:")) {
-      method_trace_file_ = option.substr(strlen("-Xmethod-trace-file:"));
-    } else if (StartsWith(option, "-Xmethod-trace-file-size:")) {
-      if (!ParseUnsignedInteger(option, ':', &method_trace_file_size_)) {
-        return false;
-      }
-    } else if (option == "-Xprofile:threadcpuclock") {
-      Trace::SetDefaultClockSource(kTraceClockSourceThreadCpu);
-    } else if (option == "-Xprofile:wallclock") {
-      Trace::SetDefaultClockSource(kTraceClockSourceWall);
-    } else if (option == "-Xprofile:dualclock") {
-      Trace::SetDefaultClockSource(kTraceClockSourceDual);
-    } else if (option == "-Xenable-profiler") {
-      profiler_options_.enabled_ = true;
-    } else if (StartsWith(option, "-Xprofile-filename:")) {
-      if (!ParseStringAfterChar(option, ':', &profile_output_filename_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-period:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.period_s_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-duration:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.duration_s_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-interval:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.interval_us_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-backoff:")) {
-      if (!ParseDouble(option, ':', 1.0, 10.0, &profiler_options_.backoff_coefficient_)) {
-        return false;
-      }
-    } else if (option == "-Xprofile-start-immediately") {
-      profiler_options_.start_immediately_ = true;
-    } else if (StartsWith(option, "-Xprofile-top-k-threshold:")) {
-      if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_threshold_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-top-k-change-threshold:")) {
-      if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_change_threshold_)) {
-        return false;
-      }
-    } else if (option == "-Xprofile-type:method") {
-      profiler_options_.profile_type_ = kProfilerMethod;
-    } else if (option == "-Xprofile-type:stack") {
-      profiler_options_.profile_type_ = kProfilerBoundedStack;
-    } else if (StartsWith(option, "-Xprofile-max-stack-depth:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.max_stack_depth_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xcompiler:")) {
-      if (!ParseStringAfterChar(option, ':', &compiler_executable_)) {
-        return false;
-      }
-    } else if (option == "-Xcompiler-option") {
-      i++;
-      if (i == options.size()) {
-        Usage("Missing required compiler option for %s\n", option.c_str());
-        return false;
-      }
-      compiler_options_.push_back(options[i].first);
-    } else if (option == "-Ximage-compiler-option") {
-      i++;
-      if (i == options.size()) {
-        Usage("Missing required compiler option for %s\n", option.c_str());
-        return false;
-      }
-      image_compiler_options_.push_back(options[i].first);
-    } else if (StartsWith(option, "-Xverify:")) {
-      std::string verify_mode = option.substr(strlen("-Xverify:"));
-      if (verify_mode == "none") {
-        verify_ = false;
-      } else if (verify_mode == "remote" || verify_mode == "all") {
-        verify_ = true;
-      } else {
-        Usage("Unknown -Xverify option %s\n", verify_mode.c_str());
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:NativeBridge=")) {
-      if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-ea") ||
-               StartsWith(option, "-da") ||
-               StartsWith(option, "-enableassertions") ||
-               StartsWith(option, "-disableassertions") ||
-               (option == "--runtime-arg") ||
-               (option == "-esa") ||
-               (option == "-dsa") ||
-               (option == "-enablesystemassertions") ||
-               (option == "-disablesystemassertions") ||
-               (option == "-Xrs") ||
-               StartsWith(option, "-Xint:") ||
-               StartsWith(option, "-Xdexopt:") ||
-               (option == "-Xnoquithandler") ||
-               StartsWith(option, "-Xjnigreflimit:") ||
-               (option == "-Xgenregmap") ||
-               (option == "-Xnogenregmap") ||
-               StartsWith(option, "-Xverifyopt:") ||
-               (option == "-Xcheckdexsum") ||
-               (option == "-Xincludeselectedop") ||
-               StartsWith(option, "-Xjitop:") ||
-               (option == "-Xincludeselectedmethod") ||
-               StartsWith(option, "-Xjitthreshold:") ||
-               StartsWith(option, "-Xjitcodecachesize:") ||
-               (option == "-Xjitblocking") ||
-               StartsWith(option, "-Xjitmethod:") ||
-               StartsWith(option, "-Xjitclass:") ||
-               StartsWith(option, "-Xjitoffset:") ||
-               StartsWith(option, "-Xjitconfig:") ||
-               (option == "-Xjitcheckcg") ||
-               (option == "-Xjitverbose") ||
-               (option == "-Xjitprofile") ||
-               (option == "-Xjitdisableopt") ||
-               (option == "-Xjitsuspendpoll") ||
-               StartsWith(option, "-XX:mainThreadStackSize=")) {
-      // Ignored for backwards compatibility.
-    } else if (!ignore_unrecognized) {
-      Usage("Unrecognized option %s\n", option.c_str());
+    } else if (parse_result.GetStatus() == CmdlineResult::kUnknown && !ignore_unrecognized) {
+      Usage("%s\n", parse_result.GetMessage().c_str());
       return false;
+    } else {
+      Usage("%s\n", parse_result.GetMessage().c_str());
+      Exit(0);
+    }
+
+    UNREACHABLE();
+    return false;
+  }
+
+  using M = RuntimeArgumentMap;
+  RuntimeArgumentMap args = parser->ReleaseArgumentsMap();
+
+  // -help, -showversion, etc.
+  if (args.Exists(M::Help)) {
+    Usage(nullptr);
+    return false;
+  } else if (args.Exists(M::ShowVersion)) {
+    UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
+    Exit(0);
+  } else if (args.Exists(M::BootClassPath)) {
+    LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
+  }
+
+  // Set a default boot class path if we didn't get an explicit one via command line.
+  if (getenv("BOOTCLASSPATH") != nullptr) {
+    args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
+  }
+
+  // Set a default class path if we didn't get an explicit one via command line.
+  if (getenv("CLASSPATH") != nullptr) {
+    args.SetIfMissing(M::ClassPath, std::string(getenv("CLASSPATH")));
+  }
+
+  // Default to number of processors minus one since the main GC thread also does work.
+  args.SetIfMissing(M::ParallelGCThreads,
+                    static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_CONF) - 1u));
+
+  // -Xverbose:
+  {
+    LogVerbosity *log_verbosity = args.Get(M::Verbose);
+    if (log_verbosity != nullptr) {
+      gLogVerbosity = *log_verbosity;
     }
   }
-  // If not set, background collector type defaults to homogeneous compaction
-  // if not low memory mode, semispace otherwise.
-  if (background_collector_type_ == gc::kCollectorTypeNone) {
-    background_collector_type_ = low_memory_mode_ ?
-        gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
+
+  // -Xprofile:
+  Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));
+
+  if (!ProcessSpecialOptions(options, &args, nullptr)) {
+      return false;
+  }
+
+  {
+    // If not set, background collector type defaults to homogeneous compaction.
+    // If foreground is GSS, use GSS as background collector.
+    // If not low memory mode, semispace otherwise.
+
+    gc::CollectorType background_collector_type_;
+    gc::CollectorType collector_type_ = (XGcOption{}).collector_type_;  // NOLINT [whitespace/braces] [5]
+    bool low_memory_mode_ = args.Exists(M::LowMemoryMode);
+
+    background_collector_type_ = args.GetOrDefault(M::BackgroundGc);
+    {
+      XGcOption* xgc = args.Get(M::GcOption);
+      if (xgc != nullptr && xgc->collector_type_ != gc::kCollectorTypeNone) {
+        collector_type_ = xgc->collector_type_;
+      }
+    }
+
+    if (background_collector_type_ == gc::kCollectorTypeNone) {
+      if (collector_type_ != gc::kCollectorTypeGSS) {
+        background_collector_type_ = low_memory_mode_ ?
+            gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
+      } else {
+        background_collector_type_ = collector_type_;
+      }
+    }
+
+    args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
   }
 
   // If a reference to the dalvik core.jar snuck in, replace it with
@@ -710,23 +478,45 @@
   std::string core_jar("/core-hostdex.jar");
   std::string core_libart_jar("/core-libart-hostdex.jar");
 #endif
-  size_t core_jar_pos = boot_class_path_string_.find(core_jar);
+  auto boot_class_path_string = args.GetOrDefault(M::BootClassPath);
+
+  size_t core_jar_pos = boot_class_path_string.find(core_jar);
   if (core_jar_pos != std::string::npos) {
-    boot_class_path_string_.replace(core_jar_pos, core_jar.size(), core_libart_jar);
+    boot_class_path_string.replace(core_jar_pos, core_jar.size(), core_libart_jar);
+    args.Set(M::BootClassPath, boot_class_path_string);
   }
 
-  if (compiler_callbacks_ == nullptr && image_.empty()) {
-    image_ += GetAndroidRoot();
-    image_ += "/framework/boot.art";
+  {
+    auto&& boot_class_path = args.GetOrDefault(M::BootClassPath);
+    auto&& boot_class_path_locations = args.GetOrDefault(M::BootClassPathLocations);
+    if (args.Exists(M::BootClassPathLocations)) {
+      size_t boot_class_path_count = ParseStringList<':'>::Split(boot_class_path).Size();
+
+      if (boot_class_path_count != boot_class_path_locations.Size()) {
+        Usage("The number of boot class path files does not match"
+            " the number of boot class path locations given\n"
+            "  boot class path files     (%zu): %s\n"
+            "  boot class path locations (%zu): %s\n",
+            boot_class_path.size(), boot_class_path_string.c_str(),
+            boot_class_path_locations.Size(), boot_class_path_locations.Join().c_str());
+        return false;
+      }
+    }
   }
-  if (heap_growth_limit_ == 0) {
-    heap_growth_limit_ = heap_maximum_size_;
+
+  if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
+    std::string image = GetAndroidRoot();
+    image += "/framework/boot.art";
+    args.Set(M::Image, image);
   }
-  if (background_collector_type_ == gc::kCollectorTypeNone) {
-    background_collector_type_ = collector_type_;
+
+  if (args.GetOrDefault(M::HeapGrowthLimit) == 0u) {  // 0 means no growth limit
+    args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
   }
+
+  *runtime_options = std::move(args);
   return true;
-}  // NOLINT(readability/fn_size)
+}
 
 void ParsedOptions::Exit(int status) {
   hook_exit_(status);
@@ -802,6 +592,8 @@
   UsageMessage(stream, "  -Xgc:[no]postverify_rosalloc\n");
   UsageMessage(stream, "  -Xgc:[no]presweepingverify\n");
   UsageMessage(stream, "  -Ximage:filename\n");
+  UsageMessage(stream, "  -Xbootclasspath-locations:bootclasspath\n"
+                       "     (override the dex locations of the -Xbootclasspath files)\n");
   UsageMessage(stream, "  -XX:+DisableExplicitGC\n");
   UsageMessage(stream, "  -XX:ParallelGCThreads=integervalue\n");
   UsageMessage(stream, "  -XX:ConcGCThreads=integervalue\n");
@@ -877,73 +669,4 @@
   Exit((error) ? 1 : 0);
 }
 
-bool ParsedOptions::ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
-  std::string::size_type colon = s.find(c);
-  if (colon == std::string::npos) {
-    Usage("Missing char %c in option %s\n", c, s.c_str());
-    return false;
-  }
-  // Add one to remove the char we were trimming until.
-  *parsed_value = s.substr(colon + 1);
-  return true;
-}
-
-bool ParsedOptions::ParseInteger(const std::string& s, char after_char, int* parsed_value) {
-  std::string::size_type colon = s.find(after_char);
-  if (colon == std::string::npos) {
-    Usage("Missing char %c in option %s\n", after_char, s.c_str());
-    return false;
-  }
-  const char* begin = &s[colon + 1];
-  char* end;
-  size_t result = strtoul(begin, &end, 10);
-  if (begin == end || *end != '\0') {
-    Usage("Failed to parse integer from %s\n", s.c_str());
-    return false;
-  }
-  *parsed_value = result;
-  return true;
-}
-
-bool ParsedOptions::ParseUnsignedInteger(const std::string& s, char after_char,
-                                         unsigned int* parsed_value) {
-  int i;
-  if (!ParseInteger(s, after_char, &i)) {
-    return false;
-  }
-  if (i < 0) {
-    Usage("Negative value %d passed for unsigned option %s\n", i, s.c_str());
-    return false;
-  }
-  *parsed_value = i;
-  return true;
-}
-
-bool ParsedOptions::ParseDouble(const std::string& option, char after_char,
-                                double min, double max, double* parsed_value) {
-  std::string substring;
-  if (!ParseStringAfterChar(option, after_char, &substring)) {
-    return false;
-  }
-  bool sane_val = true;
-  double value;
-  if ((false)) {
-    // TODO: this doesn't seem to work on the emulator.  b/15114595
-    std::stringstream iss(substring);
-    iss >> value;
-    // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
-    sane_val = iss.eof() && (value >= min) && (value <= max);
-  } else {
-    char* end = nullptr;
-    value = strtod(substring.c_str(), &end);
-    sane_val = *end == '\0' && value >= min && value <= max;
-  }
-  if (!sane_val) {
-    Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
-    return false;
-  }
-  *parsed_value = value;
-  return true;
-}
-
 }  // namespace art
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 9294868..529dd5c 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -27,92 +27,44 @@
 #include "gc/space/large_object_space.h"
 #include "arch/instruction_set.h"
 #include "profiler_options.h"
+#include "runtime_options.h"
 
 namespace art {
 
 class CompilerCallbacks;
 class DexFile;
+struct RuntimeArgumentMap;
 
 typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
 
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser;
+
 class ParsedOptions {
  public:
-  // returns null if problem parsing and ignore_unrecognized is false
-  static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized);
+  using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
+  // Create a parser that can turn user-defined input into a RuntimeArgumentMap.
+  // This visibility is effectively for testing-only, and normal code does not
+  // need to create its own parser.
+  static std::unique_ptr<RuntimeParser> MakeParser(bool ignore_unrecognized);
 
-  const std::vector<const DexFile*>* boot_class_path_;
-  std::string boot_class_path_string_;
-  std::string class_path_string_;
-  std::string image_;
-  bool check_jni_;
-  bool force_copy_;
-  std::string jni_trace_;
-  std::string native_bridge_library_filename_;
-  CompilerCallbacks* compiler_callbacks_;
-  bool is_zygote_;
-  bool must_relocate_;
-  bool dex2oat_enabled_;
-  bool image_dex2oat_enabled_;
-  std::string patchoat_executable_;
-  bool interpreter_only_;
-  bool is_explicit_gc_disabled_;
-  bool use_tlab_;
-  bool verify_pre_gc_heap_;
-  bool verify_pre_sweeping_heap_;
-  bool verify_post_gc_heap_;
-  bool verify_pre_gc_rosalloc_;
-  bool verify_pre_sweeping_rosalloc_;
-  bool verify_post_gc_rosalloc_;
-  unsigned int long_pause_log_threshold_;
-  unsigned int long_gc_log_threshold_;
-  bool dump_gc_performance_on_shutdown_;
-  bool ignore_max_footprint_;
-  size_t heap_initial_size_;
-  size_t heap_maximum_size_;
-  size_t heap_growth_limit_;
-  size_t heap_min_free_;
-  size_t heap_max_free_;
-  size_t heap_non_moving_space_capacity_;
-  gc::space::LargeObjectSpaceType large_object_space_type_;
-  size_t large_object_threshold_;
-  double heap_target_utilization_;
-  double foreground_heap_growth_multiplier_;
-  unsigned int parallel_gc_threads_;
-  unsigned int conc_gc_threads_;
-  gc::CollectorType collector_type_;
-  gc::CollectorType background_collector_type_;
-  size_t stack_size_;
-  unsigned int max_spins_before_thin_lock_inflation_;
-  bool low_memory_mode_;
-  unsigned int lock_profiling_threshold_;
-  std::string stack_trace_file_;
-  bool method_trace_;
-  std::string method_trace_file_;
-  unsigned int method_trace_file_size_;
+  // returns true if parsing succeeds, and stores the resulting options into runtime_options
+  static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized,
+                               RuntimeArgumentMap* runtime_options);
+
   bool (*hook_is_sensitive_thread_)();
   jint (*hook_vfprintf_)(FILE* stream, const char* format, va_list ap);
   void (*hook_exit_)(jint status);
   void (*hook_abort_)();
-  std::vector<std::string> properties_;
-  std::string compiler_executable_;
-  std::vector<std::string> compiler_options_;
-  std::vector<std::string> image_compiler_options_;
-  ProfilerOptions profiler_options_;
-  std::string profile_output_filename_;
-  TraceClockSource profile_clock_source_;
-  bool verify_;
-  InstructionSet image_isa_;
-
-  // Whether or not we use homogeneous space compaction to avoid OOM errors. If enabled,
-  // the heap will attempt to create an extra space which enables compacting from a malloc space to
-  // another malloc space when we are about to throw OOM.
-  bool use_homogeneous_space_compaction_for_oom_;
-  // Minimal interval allowed between two homogeneous space compactions caused by OOM.
-  uint64_t min_interval_homogeneous_space_compaction_by_oom_;
 
  private:
   ParsedOptions();
 
+  bool ProcessSpecialOptions(const RuntimeOptions& options,
+                             RuntimeArgumentMap* runtime_options,
+                             std::vector<std::string>* out_options);
+
   void Usage(const char* fmt, ...);
   void UsageMessage(FILE* stream, const char* fmt, ...);
   void UsageMessageV(FILE* stream, const char* fmt, va_list ap);
@@ -120,13 +72,8 @@
   void Exit(int status);
   void Abort();
 
-  bool Parse(const RuntimeOptions& options,  bool ignore_unrecognized);
-  bool ParseXGcOption(const std::string& option);
-  bool ParseStringAfterChar(const std::string& option, char after_char, std::string* parsed_value);
-  bool ParseInteger(const std::string& option, char after_char, int* parsed_value);
-  bool ParseUnsignedInteger(const std::string& option, char after_char, unsigned int* parsed_value);
-  bool ParseDouble(const std::string& option, char after_char, double min, double max,
-                   double* parsed_value);
+  bool Parse(const RuntimeOptions& options,  bool ignore_unrecognized,
+             RuntimeArgumentMap* runtime_options);
 };
 
 }  // namespace art
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 61481b1..79dc2fa 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -22,7 +22,12 @@
 
 namespace art {
 
-class ParsedOptionsTest : public CommonRuntimeTest {};
+class ParsedOptionsTest : public ::testing::Test {
+ public:
+  static void SetUpTestCase() {
+    CommonRuntimeTest::SetUpAndroidRoot();
+  }
+};
 
 TEST_F(ParsedOptionsTest, ParsedOptions) {
   void* test_vfprintf = reinterpret_cast<void*>(0xa);
@@ -30,7 +35,7 @@
   void* test_exit = reinterpret_cast<void*>(0xc);
   void* null = reinterpret_cast<void*>(NULL);
 
-  std::string lib_core(GetLibCoreDexFileName());
+  std::string lib_core(CommonRuntimeTest::GetLibCoreDexFileName());
 
   std::string boot_class_path;
   boot_class_path += "-Xbootclasspath:";
@@ -54,20 +59,28 @@
   options.push_back(std::make_pair("vfprintf", test_vfprintf));
   options.push_back(std::make_pair("abort", test_abort));
   options.push_back(std::make_pair("exit", test_exit));
-  std::unique_ptr<ParsedOptions> parsed(ParsedOptions::Create(options, false));
-  ASSERT_TRUE(parsed.get() != NULL);
 
-  EXPECT_EQ(lib_core, parsed->boot_class_path_string_);
-  EXPECT_EQ(lib_core, parsed->class_path_string_);
-  EXPECT_EQ(std::string("boot_image"), parsed->image_);
-  EXPECT_EQ(true, parsed->check_jni_);
-  EXPECT_EQ(2048U, parsed->heap_initial_size_);
-  EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
-  EXPECT_EQ(1 * MB, parsed->stack_size_);
-  EXPECT_DOUBLE_EQ(0.75, parsed->heap_target_utilization_);
-  EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_);
-  EXPECT_TRUE(test_exit == parsed->hook_exit_);
-  EXPECT_TRUE(test_abort == parsed->hook_abort_);
+  RuntimeArgumentMap map;
+  std::unique_ptr<ParsedOptions> parsed(ParsedOptions::Create(options, false, &map));
+  ASSERT_TRUE(parsed.get() != NULL);
+  ASSERT_NE(0u, map.Size());
+
+  using Opt = RuntimeArgumentMap;
+
+#define EXPECT_PARSED_EQ(expected, actual_key) EXPECT_EQ(expected, map.GetOrDefault(actual_key))
+#define EXPECT_PARSED_EXISTS(actual_key) EXPECT_TRUE(map.Exists(actual_key))
+
+  EXPECT_PARSED_EQ(lib_core, Opt::BootClassPath);
+  EXPECT_PARSED_EQ(lib_core, Opt::ClassPath);
+  EXPECT_PARSED_EQ(std::string("boot_image"), Opt::Image);
+  EXPECT_PARSED_EXISTS(Opt::CheckJni);
+  EXPECT_PARSED_EQ(2048U, Opt::MemoryInitialSize);
+  EXPECT_PARSED_EQ(4 * KB, Opt::MemoryMaximumSize);
+  EXPECT_PARSED_EQ(1 * MB, Opt::StackSize);
+  EXPECT_DOUBLE_EQ(0.75, map.GetOrDefault(Opt::HeapTargetUtilization));
+  EXPECT_TRUE(test_vfprintf == map.GetOrDefault(Opt::HookVfprintf));
+  EXPECT_TRUE(test_exit == map.GetOrDefault(Opt::HookExit));
+  EXPECT_TRUE(test_abort == map.GetOrDefault(Opt::HookAbort));
   EXPECT_TRUE(VLOG_IS_ON(class_linker));
   EXPECT_FALSE(VLOG_IS_ON(compiler));
   EXPECT_FALSE(VLOG_IS_ON(heap));
@@ -78,9 +91,11 @@
   EXPECT_FALSE(VLOG_IS_ON(startup));
   EXPECT_FALSE(VLOG_IS_ON(third_party_jni));
   EXPECT_FALSE(VLOG_IS_ON(threads));
-  ASSERT_EQ(2U, parsed->properties_.size());
-  EXPECT_EQ("foo=bar", parsed->properties_[0]);
-  EXPECT_EQ("baz=qux", parsed->properties_[1]);
+
+  auto&& properties_list = map.GetOrDefault(Opt::PropertiesList);
+  ASSERT_EQ(2U, properties_list.size());
+  EXPECT_EQ("foo=bar", properties_list[0]);
+  EXPECT_EQ("baz=qux", properties_list[1]);
 }
 
 }  // namespace art
diff --git a/runtime/primitive.cc b/runtime/primitive.cc
index a639f93..d29a060 100644
--- a/runtime/primitive.cc
+++ b/runtime/primitive.cc
@@ -31,6 +31,11 @@
   "PrimVoid",
 };
 
+const char* Primitive::PrettyDescriptor(Primitive::Type type) {
+  CHECK(Primitive::kPrimNot <= type && type <= Primitive::kPrimVoid) << static_cast<int>(type);
+  return kTypeNames[type];
+}
+
 std::ostream& operator<<(std::ostream& os, const Primitive::Type& type) {
   int32_t int_type = static_cast<int32_t>(type);
   if (type >= Primitive::kPrimNot && type <= Primitive::kPrimVoid) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index afcc64d..9dda144 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -146,6 +146,29 @@
     }
   }
 
+  static const char* PrettyDescriptor(Type type);
+
+  static bool IsFloatingPointType(Type type) {
+    return type == kPrimFloat || type == kPrimDouble;
+  }
+
+  static bool IsIntegralType(Type type) {
+    switch (type) {
+      case kPrimByte:
+      case kPrimChar:
+      case kPrimShort:
+      case kPrimInt:
+      case kPrimLong:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  static bool Is64BitType(Type type) {
+    return type == kPrimLong || type == kPrimDouble;
+  }
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(Primitive);
 };
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index e399195..c3bdcb1 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -40,13 +40,7 @@
 #include "thread.h"
 #include "thread_list.h"
 
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
-#if !defined(ART_USE_PORTABLE_COMPILER)
 #include "entrypoints/quick/quick_entrypoints.h"
-#endif
 
 namespace art {
 
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 3415e8f..d65b2d5 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -15,6 +15,7 @@
  */
 
 #include "inline_method_analyser.h"
+#include "dex_file-inl.h"
 #include "dex_instruction.h"
 #include "dex_instruction-inl.h"
 #include "mirror/art_field.h"
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 72b696b..3463025 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -103,6 +103,9 @@
   kIntrinsicFlagIsObject   = 4,
   // kIntrinsicUnsafePut
   kIntrinsicFlagIsOrdered  = 8,
+
+  // kIntrinsicDoubleCvt, kIntrinsicFloatCvt.
+  kIntrinsicFlagToFloatingPoint = kIntrinsicFlagMin,
 };
 
 struct InlineIGetIPutData {
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 90c9fe7..34f6713 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -204,9 +204,7 @@
     CHECK(code_item != nullptr);
     uint16_t num_regs = code_item->registers_size_;
     uint32_t dex_pc = GetDexPc();
-    const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
-    uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
-    ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
+    ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, dex_pc);
     StackHandleScope<3> hs(self_);
     mirror::Class* declaring_class = m->GetDeclaringClass();
     Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
@@ -214,7 +212,7 @@
     Handle<mirror::ArtMethod> h_method(hs.NewHandle(m));
     verifier::MethodVerifier verifier(self_, h_dex_cache->GetDexFile(), h_dex_cache, h_class_loader,
                                       &m->GetClassDef(), code_item, m->GetDexMethodIndex(),
-                                      h_method, m->GetAccessFlags(), false, true, true);
+                                      h_method, m->GetAccessFlags(), false, true, true, true);
     verifier.Verify();
     const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
     for (uint16_t reg = 0; reg < num_regs; ++reg) {
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index cf1ecbf..31622de 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -32,7 +32,7 @@
 class ThrowLocation;
 class ShadowFrame;
 
-// Manages exception delivery for Quick backend. Not used by Portable backend.
+// Manages exception delivery for Quick backend.
 class QuickExceptionHandler {
  public:
   QuickExceptionHandler(Thread* self, bool is_deoptimization)
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 0dc31e7..c74fded 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -19,43 +19,147 @@
 
 #include "read_barrier.h"
 
+#include "gc/collector/concurrent_copying.h"
+#include "gc/heap.h"
 #include "mirror/object_reference.h"
+#include "mirror/reference.h"
+#include "runtime.h"
 
 namespace art {
 
-template <typename MirrorType, ReadBarrierOption kReadBarrierOption>
+template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kMaybeDuringStartup>
 inline MirrorType* ReadBarrier::Barrier(
     mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
-  // Unused for now.
-  UNUSED(obj, offset, ref_addr);
   const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
   if (with_read_barrier && kUseBakerReadBarrier) {
-    // To be implemented.
-    return ref_addr->AsMirrorPtr();
+    // The higher bits of the rb ptr, rb_ptr_high_bits (must be zero)
+    // is used to create artificial data dependency from the is_gray
+    // load to the ref field (ptr) load to avoid needing a load-load
+    // barrier between the two.
+    uintptr_t rb_ptr_high_bits;
+    bool is_gray = HasGrayReadBarrierPointer(obj, &rb_ptr_high_bits);
+    ref_addr = reinterpret_cast<mirror::HeapReference<MirrorType>*>(
+        rb_ptr_high_bits | reinterpret_cast<uintptr_t>(ref_addr));
+    MirrorType* ref = ref_addr->AsMirrorPtr();
+    if (is_gray) {
+      // Slow-path.
+      ref = reinterpret_cast<MirrorType*>(Mark(ref));
+    }
+    if (kEnableReadBarrierInvariantChecks) {
+      CHECK_EQ(rb_ptr_high_bits, 0U) << obj << " rb_ptr=" << obj->GetReadBarrierPointer();
+    }
+    AssertToSpaceInvariant(obj, offset, ref);
+    return ref;
   } else if (with_read_barrier && kUseBrooksReadBarrier) {
     // To be implemented.
     return ref_addr->AsMirrorPtr();
+  } else if (with_read_barrier && kUseTableLookupReadBarrier) {
+    MirrorType* ref = ref_addr->AsMirrorPtr();
+    MirrorType* old_ref = ref;
+    // The heap or the collector can be null at startup. TODO: avoid the need for this null check.
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    if (heap != nullptr && heap->GetReadBarrierTable()->IsSet(old_ref)) {
+      ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
+      // Update the field atomically. This may fail if mutator updates before us, but it's ok.
+      obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
+          offset, old_ref, ref);
+    }
+    AssertToSpaceInvariant(obj, offset, ref);
+    return ref;
   } else {
     // No read barrier.
     return ref_addr->AsMirrorPtr();
   }
 }
 
-template <typename MirrorType, ReadBarrierOption kReadBarrierOption>
+template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kMaybeDuringStartup>
 inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root) {
   MirrorType* ref = *root;
   const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
   if (with_read_barrier && kUseBakerReadBarrier) {
-    // To be implemented.
+    if (kMaybeDuringStartup && IsDuringStartup()) {
+      // During startup, the heap may not be initialized yet. Just
+      // return the given ref.
+      return ref;
+    }
+    // TODO: separate the read barrier code from the collector code more.
+    if (Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->IsMarking()) {
+      ref = reinterpret_cast<MirrorType*>(Mark(ref));
+    }
+    AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
     return ref;
   } else if (with_read_barrier && kUseBrooksReadBarrier) {
     // To be implemented.
     return ref;
+  } else if (with_read_barrier && kUseTableLookupReadBarrier) {
+    if (kMaybeDuringStartup && IsDuringStartup()) {
+      // During startup, the heap may not be initialized yet. Just
+      // return the given ref.
+      return ref;
+    }
+    if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+      MirrorType* old_ref = ref;
+      ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
+      // Update the field atomically. This may fail if mutator updates before us, but it's ok.
+      Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+      atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+    }
+    AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+    return ref;
   } else {
     return ref;
   }
 }
 
+inline bool ReadBarrier::IsDuringStartup() {
+  gc::Heap* heap = Runtime::Current()->GetHeap();
+  if (heap == nullptr) {
+    // During startup, the heap can be null.
+    return true;
+  }
+  if (heap->CurrentCollectorType() != gc::kCollectorTypeCC) {
+    // CC isn't running.
+    return true;
+  }
+  gc::collector::ConcurrentCopying* collector = heap->ConcurrentCopyingCollector();
+  if (collector == nullptr) {
+    // During startup, the collector can be null.
+    return true;
+  }
+  return false;
+}
+
+inline void ReadBarrier::AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+                                                mirror::Object* ref) {
+  if (kEnableToSpaceInvariantChecks || kIsDebugBuild) {
+    if (ref == nullptr || IsDuringStartup()) {
+      return;
+    }
+    Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->
+        AssertToSpaceInvariant(obj, offset, ref);
+  }
+}
+
+inline mirror::Object* ReadBarrier::Mark(mirror::Object* obj) {
+  return Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->Mark(obj);
+}
+
+inline bool ReadBarrier::HasGrayReadBarrierPointer(mirror::Object* obj,
+                                                   uintptr_t* out_rb_ptr_high_bits) {
+  mirror::Object* rb_ptr = obj->GetReadBarrierPointer();
+  uintptr_t rb_ptr_bits = reinterpret_cast<uintptr_t>(rb_ptr);
+  uintptr_t rb_ptr_low_bits = rb_ptr_bits & rb_ptr_mask_;
+  if (kEnableReadBarrierInvariantChecks) {
+    CHECK(rb_ptr_low_bits == white_ptr_ || rb_ptr_low_bits == gray_ptr_ ||
+          rb_ptr_low_bits == black_ptr_)
+        << "obj=" << obj << " rb_ptr=" << rb_ptr << " " << PrettyTypeOf(obj);
+  }
+  bool is_gray = rb_ptr_low_bits == gray_ptr_;
+  // The high bits are supposed to be zero. We check this on the caller side.
+  *out_rb_ptr_high_bits = rb_ptr_bits & ~rb_ptr_mask_;
+  return is_gray;
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_READ_BARRIER_INL_H_
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index ed5db4e..474b46f 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -19,6 +19,7 @@
 
 #include "base/mutex.h"
 #include "base/macros.h"
+#include "jni.h"
 #include "offsets.h"
 #include "read_barrier_c.h"
 
@@ -26,25 +27,70 @@
 // which needs to be a C header file for asm_support.h.
 
 namespace art {
+
 namespace mirror {
+  class ArtField;
+  class ArtMethod;
   class Object;
   template<typename MirrorType> class HeapReference;
 }  // namespace mirror
 
 class ReadBarrier {
  public:
+  // TODO: disable thse flags for production use.
+  // Enable the to-space invariant checks.
+  static constexpr bool kEnableToSpaceInvariantChecks = true;
+  // Enable the read barrier checks.
+  static constexpr bool kEnableReadBarrierInvariantChecks = true;
+
   // It's up to the implementation whether the given field gets
   // updated whereas the return value must be an updated reference.
-  template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+            bool kMaybeDuringStartup = false>
   ALWAYS_INLINE static MirrorType* Barrier(
       mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // It's up to the implementation whether the given root gets updated
   // whereas the return value must be an updated reference.
-  template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+  template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+            bool kMaybeDuringStartup = false>
   ALWAYS_INLINE static MirrorType* BarrierForRoot(MirrorType** root)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static bool IsDuringStartup();
+
+  // Without the holder object.
+  static void AssertToSpaceInvariant(mirror::Object* ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+  }
+  // With the holder object.
+  static void AssertToSpaceInvariant(mirror::Object* obj, MemberOffset offset,
+                                     mirror::Object* ref)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static mirror::Object* Mark(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static mirror::Object* WhitePtr() {
+    return reinterpret_cast<mirror::Object*>(white_ptr_);
+  }
+  static mirror::Object* GrayPtr() {
+    return reinterpret_cast<mirror::Object*>(gray_ptr_);
+  }
+  static mirror::Object* BlackPtr() {
+    return reinterpret_cast<mirror::Object*>(black_ptr_);
+  }
+
+  ALWAYS_INLINE static bool HasGrayReadBarrierPointer(mirror::Object* obj,
+                                                      uintptr_t* out_rb_ptr_high_bits)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Note: These couldn't be constexpr pointers as reinterpret_cast isn't compatible with them.
+  static constexpr uintptr_t white_ptr_ = 0x0;    // Not marked.
+  static constexpr uintptr_t gray_ptr_ = 0x1;     // Marked, but not marked through. On mark stack.
+  static constexpr uintptr_t black_ptr_ = 0x2;    // Marked through. Used for non-moving objects.
+  static constexpr uintptr_t rb_ptr_mask_ = 0x3;  // The low 2 bits for white|gray|black.
 };
 
 }  // namespace art
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 1385c60..49efaa2 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -22,10 +22,14 @@
 // include globals.h.
 
 // Uncomment one of the following two and the two fields in
-// Object.java (libcore) to enable baker or brooks pointers.
+// Object.java (libcore) to enable baker, brooks (unimplemented), or
+// table-lookup read barriers.
 
+#ifdef ART_USE_READ_BARRIER
 // #define USE_BAKER_READ_BARRIER
 // #define USE_BROOKS_READ_BARRIER
+#define USE_TABLE_LOOKUP_READ_BARRIER
+#endif
 
 #if defined(USE_BAKER_READ_BARRIER) || defined(USE_BROOKS_READ_BARRIER)
 #define USE_BAKER_OR_BROOKS_READ_BARRIER
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 01c5070..357d454 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -71,43 +71,6 @@
   return obj->AsArray()->GetLength();
 }
 
-struct ObjectComparator {
-  bool operator()(GcRoot<mirror::Object> root1, GcRoot<mirror::Object> root2)
-    // TODO: enable analysis when analysis can work with the STL.
-      NO_THREAD_SAFETY_ANALYSIS {
-    Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
-    mirror::Object* obj1 = root1.Read<kWithoutReadBarrier>();
-    mirror::Object* obj2 = root2.Read<kWithoutReadBarrier>();
-    // Ensure null references and cleared jweaks appear at the end.
-    if (obj1 == NULL) {
-      return true;
-    } else if (obj2 == NULL) {
-      return false;
-    }
-    Runtime* runtime = Runtime::Current();
-    if (runtime->IsClearedJniWeakGlobal(obj1)) {
-      return true;
-    } else if (runtime->IsClearedJniWeakGlobal(obj2)) {
-      return false;
-    }
-
-    // Sort by class...
-    if (obj1->GetClass() != obj2->GetClass()) {
-      return obj1->GetClass()->IdentityHashCode() < obj2->IdentityHashCode();
-    } else {
-      // ...then by size...
-      size_t count1 = obj1->SizeOf();
-      size_t count2 = obj2->SizeOf();
-      if (count1 != count2) {
-        return count1 < count2;
-      } else {
-        // ...and finally by identity hash code.
-        return obj1->IdentityHashCode() < obj2->IdentityHashCode();
-      }
-    }
-  }
-};
-
 // Log an object with some additional info.
 //
 // Pass in the number of elements in the array (or 0 if this is not an
@@ -153,6 +116,38 @@
 }
 
 void ReferenceTable::Dump(std::ostream& os, Table& entries) {
+  // Compare GC roots, first by class, then size, then address.
+  struct GcRootComparator {
+    bool operator()(GcRoot<mirror::Object> root1, GcRoot<mirror::Object> root2) const
+      // TODO: enable analysis when analysis can work with the STL.
+        NO_THREAD_SAFETY_ANALYSIS {
+      Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+      // These GC roots are already forwarded in ReferenceTable::Dump. We sort by class since there
+      // are no suspend points which can happen during the sorting process. This works since
+      // we are guaranteed that the addresses of obj1, obj2, obj1->GetClass, obj2->GetClass wont
+      // change during the sorting process. The classes are forwarded by ref->GetClass().
+      mirror::Object* obj1 = root1.Read<kWithoutReadBarrier>();
+      mirror::Object* obj2 = root2.Read<kWithoutReadBarrier>();
+      DCHECK(obj1 != nullptr);
+      DCHECK(obj2 != nullptr);
+      Runtime* runtime = Runtime::Current();
+      DCHECK(!runtime->IsClearedJniWeakGlobal(obj1));
+      DCHECK(!runtime->IsClearedJniWeakGlobal(obj2));
+      // Sort by class...
+      if (obj1->GetClass() != obj2->GetClass()) {
+        return obj1->GetClass() < obj2->GetClass();
+      }
+      // ...then by size...
+      const size_t size1 = obj1->SizeOf();
+      const size_t size2 = obj2->SizeOf();
+      if (size1 != size2) {
+        return size1 < size2;
+      }
+      // ...and finally by address.
+      return obj1 < obj2;
+    }
+  };
+
   if (entries.empty()) {
     os << "  (empty)\n";
     return;
@@ -166,16 +161,17 @@
     first = 0;
   }
   os << "  Last " << (count - first) << " entries (of " << count << "):\n";
+  Runtime* runtime = Runtime::Current();
   for (int idx = count - 1; idx >= first; --idx) {
     mirror::Object* ref = entries[idx].Read();
-    if (ref == NULL) {
+    if (ref == nullptr) {
       continue;
     }
-    if (Runtime::Current()->IsClearedJniWeakGlobal(ref)) {
+    if (runtime->IsClearedJniWeakGlobal(ref)) {
       os << StringPrintf("    %5d: cleared jweak\n", idx);
       continue;
     }
-    if (ref->GetClass() == NULL) {
+    if (ref->GetClass() == nullptr) {
       // should only be possible right after a plain dvmMalloc().
       size_t size = ref->SizeOf();
       os << StringPrintf("    %5d: %p (raw) (%zd bytes)\n", idx, ref, size);
@@ -189,7 +185,7 @@
     if (element_count != 0) {
       StringAppendF(&extras, " (%zd elements)", element_count);
     } else if (ref->GetClass()->IsStringClass()) {
-      mirror::String* s = const_cast<mirror::Object*>(ref)->AsString();
+      mirror::String* s = ref->AsString();
       std::string utf8(s->ToModifiedUtf8());
       if (s->GetLength() <= 16) {
         StringAppendF(&extras, " \"%s\"", utf8.c_str());
@@ -200,57 +196,50 @@
     os << StringPrintf("    %5d: ", idx) << ref << " " << className << extras << "\n";
   }
 
-  // Make a copy of the table and sort it.
+  // Make a copy of the table and sort it, only adding non null and not cleared elements.
   Table sorted_entries;
-  for (size_t i = 0; i < entries.size(); ++i) {
-    mirror::Object* entry = entries[i].Read();
-    sorted_entries.push_back(GcRoot<mirror::Object>(entry));
-  }
-  std::sort(sorted_entries.begin(), sorted_entries.end(), ObjectComparator());
-
-  // Remove any uninteresting stuff from the list. The sort moved them all to the end.
-  while (!sorted_entries.empty() && sorted_entries.back().IsNull()) {
-    sorted_entries.pop_back();
-  }
-  while (!sorted_entries.empty() &&
-         Runtime::Current()->IsClearedJniWeakGlobal(
-             sorted_entries.back().Read<kWithoutReadBarrier>())) {
-    sorted_entries.pop_back();
+  for (GcRoot<mirror::Object>& root : entries) {
+    if (!root.IsNull() && !runtime->IsClearedJniWeakGlobal(root.Read())) {
+      sorted_entries.push_back(root);
+    }
   }
   if (sorted_entries.empty()) {
     return;
   }
+  std::sort(sorted_entries.begin(), sorted_entries.end(), GcRootComparator());
 
   // Dump a summary of the whole table.
   os << "  Summary:\n";
   size_t equiv = 0;
   size_t identical = 0;
-  for (size_t idx = 1; idx < count; idx++) {
-    mirror::Object* prev = sorted_entries[idx-1].Read<kWithoutReadBarrier>();
-    mirror::Object* current = sorted_entries[idx].Read<kWithoutReadBarrier>();
-    size_t element_count = GetElementCount(prev);
-    if (current == prev) {
-      // Same reference, added more than once.
-      identical++;
-    } else if (current->GetClass() == prev->GetClass() && GetElementCount(current) == element_count) {
-      // Same class / element count, different object.
-      equiv++;
-    } else {
-      // Different class.
-      DumpSummaryLine(os, prev, element_count, identical, equiv);
-      equiv = identical = 0;
+  mirror::Object* prev = nullptr;
+  for (GcRoot<mirror::Object>& root : sorted_entries) {
+    mirror::Object* current = root.Read<kWithoutReadBarrier>();
+    if (prev != nullptr) {
+      const size_t element_count = GetElementCount(prev);
+      if (current == prev) {
+        // Same reference, added more than once.
+        ++identical;
+      } else if (current->GetClass() == prev->GetClass() &&
+          GetElementCount(current) == element_count) {
+        // Same class / element count, different object.
+        ++equiv;
+      } else {
+        // Different class.
+        DumpSummaryLine(os, prev, element_count, identical, equiv);
+        equiv = 0;
+        identical = 0;
+      }
     }
+    prev = current;
   }
   // Handle the last entry.
-  DumpSummaryLine(os, sorted_entries.back().Read<kWithoutReadBarrier>(),
-                  GetElementCount(sorted_entries.back().Read<kWithoutReadBarrier>()),
-                  identical, equiv);
+  DumpSummaryLine(os, prev, GetElementCount(prev), identical, equiv);
 }
 
-void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, uint32_t tid,
-                                RootType root_type) {
+void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info) {
   for (GcRoot<mirror::Object>& root : entries_) {
-    root.VisitRoot(visitor, arg, tid, root_type);
+    root.VisitRoot(visitor, arg, root_info);
   }
 }
 
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 6cffa85..22cf1cd 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -49,7 +49,7 @@
 
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VisitRoots(RootCallback* visitor, void* arg, uint32_t tid, RootType root_type);
+  void VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info);
 
  private:
   typedef std::vector<GcRoot<mirror::Object>,
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 85f9938..2aeb92d 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -77,12 +77,6 @@
   }
 
   void AppendWide(uint64_t value) {
-    // For ARM and MIPS portable, align wide values to 8 bytes (ArgArray starts at offset of 4).
-#if defined(ART_USE_PORTABLE_COMPILER) && (defined(__arm__) || defined(__mips__))
-    if (num_bytes_ % 8 == 0) {
-      num_bytes_ += 4;
-    }
-#endif
     arg_array_[num_bytes_ / 4] = value;
     arg_array_[(num_bytes_ / 4) + 1] = value >> 32;
     num_bytes_ += 8;
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index eca1800..7aefdaa 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -493,7 +493,6 @@
 };
 
 TEST_F(ReflectionTest, StaticMainMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   ScopedObjectAccess soa(Thread::Current());
   jobject jclass_loader = LoadDex("Main");
   StackHandleScope<1> hs(soa.Self());
@@ -518,122 +517,98 @@
 }
 
 TEST_F(ReflectionTest, StaticNopMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeNopMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticNopMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeNopMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticIdentityByteMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeIdentityByteMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticIdentityByteMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeIdentityByteMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticIdentityIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeIdentityIntMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticIdentityIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeIdentityIntMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticIdentityDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeIdentityDoubleMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticIdentityDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeIdentityDoubleMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntIntMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntIntMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntIntIntMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntIntIntMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumIntIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntIntIntIntMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumIntIntIntIntIntMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumIntIntIntIntIntMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleDoubleMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleDoubleMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleDoubleDoubleMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleDoubleDoubleMethod(false);
 }
 
 TEST_F(ReflectionTest, StaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(true);
 }
 
 TEST_F(ReflectionTest, NonStaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
-  TEST_DISABLED_FOR_PORTABLE();
   InvokeSumDoubleDoubleDoubleDoubleDoubleMethod(false);
 }
 
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index cdf8d54..a82bc85 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -19,6 +19,7 @@
 
 #include "runtime.h"
 
+#include "mirror/art_method.h"
 #include "read_barrier-inl.h"
 
 namespace art {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 078e7d2..6704963 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -29,10 +29,12 @@
 #include <cstdio>
 #include <cstdlib>
 #include <limits>
-#include <memory>
+#include <memory_representation.h>
 #include <vector>
 #include <fcntl.h>
 
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
 #include "arch/arm/quick_method_frame_info_arm.h"
 #include "arch/arm/registers_arm.h"
 #include "arch/arm64/quick_method_frame_info_arm64.h"
@@ -40,6 +42,8 @@
 #include "arch/instruction_set_features.h"
 #include "arch/mips/quick_method_frame_info_mips.h"
 #include "arch/mips/registers_mips.h"
+#include "arch/mips64/quick_method_frame_info_mips64.h"
+#include "arch/mips64/registers_mips64.h"
 #include "arch/x86/quick_method_frame_info_x86.h"
 #include "arch/x86/registers_x86.h"
 #include "arch/x86_64/quick_method_frame_info_x86_64.h"
@@ -57,19 +61,19 @@
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space.h"
+#include "handle_scope-inl.h"
 #include "image.h"
 #include "instrumentation.h"
 #include "intern_table.h"
 #include "jni_internal.h"
+#include "mirror/array.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
-#include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/throwable.h"
 #include "monitor.h"
-#include "native_bridge_art_interface.h"
 #include "native/dalvik_system_DexFile.h"
 #include "native/dalvik_system_VMDebug.h"
 #include "native/dalvik_system_VMRuntime.h"
@@ -78,48 +82,43 @@
 #include "native/java_lang_Class.h"
 #include "native/java_lang_DexCache.h"
 #include "native/java_lang_Object.h"
-#include "native/java_lang_ref_FinalizerReference.h"
-#include "native/java_lang_reflect_Array.h"
-#include "native/java_lang_reflect_Constructor.h"
-#include "native/java_lang_reflect_Field.h"
-#include "native/java_lang_reflect_Method.h"
-#include "native/java_lang_reflect_Proxy.h"
-#include "native/java_lang_ref_Reference.h"
 #include "native/java_lang_Runtime.h"
 #include "native/java_lang_String.h"
 #include "native/java_lang_System.h"
 #include "native/java_lang_Thread.h"
 #include "native/java_lang_Throwable.h"
 #include "native/java_lang_VMClassLoader.h"
+#include "native/java_lang_ref_FinalizerReference.h"
+#include "native/java_lang_ref_Reference.h"
+#include "native/java_lang_reflect_Array.h"
+#include "native/java_lang_reflect_Constructor.h"
+#include "native/java_lang_reflect_Field.h"
+#include "native/java_lang_reflect_Method.h"
+#include "native/java_lang_reflect_Proxy.h"
 #include "native/java_util_concurrent_atomic_AtomicLong.h"
 #include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
 #include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
 #include "native/sun_misc_Unsafe.h"
-#include "parsed_options.h"
+#include "native_bridge_art_interface.h"
 #include "oat_file.h"
 #include "os.h"
+#include "parsed_options.h"
+#include "profiler.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
+#include "runtime_options.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "sigchain.h"
 #include "signal_catcher.h"
 #include "signal_set.h"
-#include "handle_scope-inl.h"
 #include "thread.h"
 #include "thread_list.h"
 #include "trace.h"
 #include "transaction.h"
-#include "profiler.h"
 #include "verifier/method_verifier.h"
 #include "well_known_classes.h"
 
-#include "JniConstants.h"  // Last to avoid LOG redefinition in ics-mr1-plus-art.
-
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
 namespace art {
 
 // If a signal isn't handled properly, enable a handler that attempts to dump the Java stack.
@@ -174,7 +173,8 @@
       implicit_null_checks_(false),
       implicit_so_checks_(false),
       implicit_suspend_checks_(false),
-      is_native_bridge_loaded_(false) {
+      is_native_bridge_loaded_(false),
+      jdwp_options_(nullptr) {
   CheckAsmSupportOffsetsAndSizes();
 }
 
@@ -190,6 +190,13 @@
   }
 
   Thread* self = Thread::Current();
+  if (self == nullptr) {
+    CHECK(AttachCurrentThread("Shutdown thread", false, nullptr, false));
+    self = Thread::Current();
+  } else {
+    LOG(WARNING) << "Current thread not detached in Runtime shutdown";
+  }
+
   {
     MutexLock mu(self, *Locks::runtime_shutdown_lock_);
     shutting_down_started_ = true;
@@ -198,6 +205,16 @@
     }
     shutting_down_ = true;
   }
+  // Shutdown and wait for the daemons.
+  CHECK(self != nullptr);
+  if (IsFinishedStarting()) {
+    self->ClearException();
+    self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
+                                            WellKnownClasses::java_lang_Daemons_stop);
+  }
+  DetachCurrentThread();
+  self = nullptr;
+
   // Shut down background profiler before the runtime exits.
   if (profiler_started_) {
     BackgroundMethodSamplingProfiler::Shutdown();
@@ -211,6 +228,10 @@
 
   // Make sure our internal threads are dead before we start tearing down things they're using.
   Dbg::StopJdwp();
+  if (jdwp_options_ != nullptr) {
+    delete jdwp_options_;
+  }
+
   delete signal_catcher_;
 
   // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
@@ -574,7 +595,7 @@
 
   // Start the JDWP thread. If the command-line debugger flags specified "suspend=y",
   // this will pause the runtime, so we probably want this to come last.
-  Dbg::StartJdwp();
+  Dbg::StartJdwp(jdwp_options_);
 }
 
 void Runtime::StartSignalCatcher() {
@@ -608,8 +629,9 @@
 }
 
 static bool OpenDexFilesFromImage(const std::string& image_location,
-                                  std::vector<const DexFile*>& dex_files,
+                                  std::vector<std::unique_ptr<const DexFile>>* dex_files,
                                   size_t* failures) {
+  DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is NULL";
   std::string system_filename;
   bool has_system = false;
   std::string cache_filename_unused;
@@ -653,11 +675,11 @@
       *failures += 1;
       continue;
     }
-    const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
-    if (dex_file == nullptr) {
+    std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+    if (dex_file.get() == nullptr) {
       *failures += 1;
     } else {
-      dex_files.push_back(dex_file);
+      dex_files->push_back(std::move(dex_file));
     }
   }
   Runtime::Current()->GetClassLinker()->RegisterOatFile(oat_file.release());
@@ -666,8 +688,10 @@
 
 
 static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames,
+                           const std::vector<std::string>& dex_locations,
                            const std::string& image_location,
-                           std::vector<const DexFile*>& dex_files) {
+                           std::vector<std::unique_ptr<const DexFile>>* dex_files) {
+  DCHECK(dex_files != nullptr) << "OpenDexFiles: out-param is NULL";
   size_t failure_count = 0;
   if (!image_location.empty() && OpenDexFilesFromImage(image_location, dex_files, &failure_count)) {
     return failure_count;
@@ -675,12 +699,13 @@
   failure_count = 0;
   for (size_t i = 0; i < dex_filenames.size(); i++) {
     const char* dex_filename = dex_filenames[i].c_str();
+    const char* dex_location = dex_locations[i].c_str();
     std::string error_msg;
     if (!OS::FileExists(dex_filename)) {
       LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
       continue;
     }
-    if (!DexFile::Open(dex_filename, dex_filename, &error_msg, &dex_files)) {
+    if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
       LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
       ++failure_count;
     }
@@ -693,8 +718,11 @@
 
   MemMap::Init();
 
-  std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
-  if (options.get() == nullptr) {
+  using Opt = RuntimeArgumentMap;
+  RuntimeArgumentMap runtime_options;
+  std::unique_ptr<ParsedOptions> parsed_options(
+      ParsedOptions::Create(raw_options, ignore_unrecognized, &runtime_options));
+  if (parsed_options.get() == nullptr) {
     LOG(ERROR) << "Failed to parse options";
     return false;
   }
@@ -702,76 +730,84 @@
 
   QuasiAtomic::Startup();
 
-  Monitor::Init(options->lock_profiling_threshold_, options->hook_is_sensitive_thread_);
+  Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
+                runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
 
-  boot_class_path_string_ = options->boot_class_path_string_;
-  class_path_string_ = options->class_path_string_;
-  properties_ = options->properties_;
+  boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+  class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
+  properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);
 
-  compiler_callbacks_ = options->compiler_callbacks_;
-  patchoat_executable_ = options->patchoat_executable_;
-  must_relocate_ = options->must_relocate_;
-  is_zygote_ = options->is_zygote_;
-  is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
-  dex2oat_enabled_ = options->dex2oat_enabled_;
-  image_dex2oat_enabled_ = options->image_dex2oat_enabled_;
+  compiler_callbacks_ = runtime_options.GetOrDefault(Opt::CompilerCallbacksPtr);
+  patchoat_executable_ = runtime_options.ReleaseOrDefault(Opt::PatchOat);
+  must_relocate_ = runtime_options.GetOrDefault(Opt::Relocate);
+  is_zygote_ = runtime_options.Exists(Opt::Zygote);
+  is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
+  dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);
+  image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
 
-  vfprintf_ = options->hook_vfprintf_;
-  exit_ = options->hook_exit_;
-  abort_ = options->hook_abort_;
+  vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);
+  exit_ = runtime_options.GetOrDefault(Opt::HookExit);
+  abort_ = runtime_options.GetOrDefault(Opt::HookAbort);
 
-  default_stack_size_ = options->stack_size_;
-  stack_trace_file_ = options->stack_trace_file_;
+  default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize);
+  stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile);
 
-  compiler_executable_ = options->compiler_executable_;
-  compiler_options_ = options->compiler_options_;
-  image_compiler_options_ = options->image_compiler_options_;
-  image_location_ = options->image_;
+  compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
+  compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
+  image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);
+  image_location_ = runtime_options.GetOrDefault(Opt::Image);
 
-  max_spins_before_thin_lock_inflation_ = options->max_spins_before_thin_lock_inflation_;
+  max_spins_before_thin_lock_inflation_ =
+      runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);
 
   monitor_list_ = new MonitorList;
   monitor_pool_ = MonitorPool::Create();
   thread_list_ = new ThreadList;
   intern_table_ = new InternTable;
 
-  verify_ = options->verify_;
+  verify_ = runtime_options.GetOrDefault(Opt::Verify);
 
-  if (options->interpreter_only_) {
+  if (runtime_options.GetOrDefault(Opt::Interpret)) {
     GetInstrumentation()->ForceInterpretOnly();
   }
 
-  heap_ = new gc::Heap(options->heap_initial_size_,
-                       options->heap_growth_limit_,
-                       options->heap_min_free_,
-                       options->heap_max_free_,
-                       options->heap_target_utilization_,
-                       options->foreground_heap_growth_multiplier_,
-                       options->heap_maximum_size_,
-                       options->heap_non_moving_space_capacity_,
-                       options->image_,
-                       options->image_isa_,
-                       options->collector_type_,
-                       options->background_collector_type_,
-                       options->large_object_space_type_,
-                       options->large_object_threshold_,
-                       options->parallel_gc_threads_,
-                       options->conc_gc_threads_,
-                       options->low_memory_mode_,
-                       options->long_pause_log_threshold_,
-                       options->long_gc_log_threshold_,
-                       options->ignore_max_footprint_,
-                       options->use_tlab_,
-                       options->verify_pre_gc_heap_,
-                       options->verify_pre_sweeping_heap_,
-                       options->verify_post_gc_heap_,
-                       options->verify_pre_gc_rosalloc_,
-                       options->verify_pre_sweeping_rosalloc_,
-                       options->verify_post_gc_rosalloc_,
-                       options->use_homogeneous_space_compaction_for_oom_,
-                       options->min_interval_homogeneous_space_compaction_by_oom_);
+  XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
+  heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
+                       runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
+                       runtime_options.GetOrDefault(Opt::HeapMinFree),
+                       runtime_options.GetOrDefault(Opt::HeapMaxFree),
+                       runtime_options.GetOrDefault(Opt::HeapTargetUtilization),
+                       runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier),
+                       runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
+                       runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
+                       runtime_options.GetOrDefault(Opt::Image),
+                       runtime_options.GetOrDefault(Opt::ImageInstructionSet),
+                       xgc_option.collector_type_,
+                       runtime_options.GetOrDefault(Opt::BackgroundGc),
+                       runtime_options.GetOrDefault(Opt::LargeObjectSpace),
+                       runtime_options.GetOrDefault(Opt::LargeObjectThreshold),
+                       runtime_options.GetOrDefault(Opt::ParallelGCThreads),
+                       runtime_options.GetOrDefault(Opt::ConcGCThreads),
+                       runtime_options.Exists(Opt::LowMemoryMode),
+                       runtime_options.GetOrDefault(Opt::LongPauseLogThreshold),
+                       runtime_options.GetOrDefault(Opt::LongGCLogThreshold),
+                       runtime_options.Exists(Opt::IgnoreMaxFootprint),
+                       runtime_options.Exists(Opt::UseTLAB),
+                       xgc_option.verify_pre_gc_heap_,
+                       xgc_option.verify_pre_sweeping_heap_,
+                       xgc_option.verify_post_gc_heap_,
+                       xgc_option.verify_pre_gc_rosalloc_,
+                       xgc_option.verify_pre_sweeping_rosalloc_,
+                       xgc_option.verify_post_gc_rosalloc_,
+                       runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),
+                       runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
 
-  dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_;
+  dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown);
+
+  if (runtime_options.Exists(Opt::JdwpOptions)) {
+    JDWP::JdwpOptions options = runtime_options.GetOrDefault(Opt::JdwpOptions);
+    jdwp_options_ = new JDWP::JdwpOptions(options);
+  }
 
   BlockSignals();
   InitPlatformSignalHandlers();
@@ -822,7 +858,7 @@
     }
   }
 
-  java_vm_ = new JavaVMExt(this, options.get());
+  java_vm_ = new JavaVMExt(this, runtime_options);
 
   Thread::Startup();
 
@@ -846,25 +882,46 @@
     if (kIsDebugBuild) {
       GetHeap()->GetImageSpace()->VerifyImageAllocations();
     }
-  } else if (!IsCompiler() || !image_dex2oat_enabled_) {
+    if (boot_class_path_string_.empty()) {
+      // The bootclasspath is not explicitly specified: construct it from the loaded dex files.
+      const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();
+      std::vector<std::string> dex_locations;
+      dex_locations.reserve(boot_class_path.size());
+      for (const DexFile* dex_file : boot_class_path) {
+        dex_locations.push_back(dex_file->GetLocation());
+      }
+      boot_class_path_string_ = Join(dex_locations, ':');
+    }
+  } else {
     std::vector<std::string> dex_filenames;
     Split(boot_class_path_string_, ':', &dex_filenames);
-    std::vector<const DexFile*> boot_class_path;
-    OpenDexFiles(dex_filenames, options->image_, boot_class_path);
-    class_linker_->InitWithoutImage(boot_class_path);
+
+    std::vector<std::string> dex_locations;
+    if (!runtime_options.Exists(Opt::BootClassPathLocations)) {
+      dex_locations = dex_filenames;
+    } else {
+      dex_locations = runtime_options.GetOrDefault(Opt::BootClassPathLocations);
+      CHECK_EQ(dex_filenames.size(), dex_locations.size());
+    }
+
+    std::vector<std::unique_ptr<const DexFile>> boot_class_path;
+    OpenDexFiles(dex_filenames,
+                 dex_locations,
+                 runtime_options.GetOrDefault(Opt::Image),
+                 &boot_class_path);
+    instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet);
+    class_linker_->InitWithoutImage(std::move(boot_class_path));
+
     // TODO: Should we move the following to InitWithoutImage?
-    SetInstructionSet(kRuntimeISA);
+    SetInstructionSet(instruction_set_);
     for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
       Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
       if (!HasCalleeSaveMethod(type)) {
         SetCalleeSaveMethod(CreateCalleeSaveMethod(), type);
       }
     }
-  } else {
-    CHECK(options->boot_class_path_ != nullptr);
-    CHECK_NE(options->boot_class_path_->size(), 0U);
-    class_linker_->InitWithoutImage(*options->boot_class_path_);
   }
+
   CHECK(class_linker_ != nullptr);
 
   // Initialize the special sentinel_ value early.
@@ -873,20 +930,42 @@
 
   verifier::MethodVerifier::Init();
 
-  method_trace_ = options->method_trace_;
-  method_trace_file_ = options->method_trace_file_;
-  method_trace_file_size_ = options->method_trace_file_size_;
+  method_trace_ = runtime_options.Exists(Opt::MethodTrace);
+  method_trace_file_ = runtime_options.ReleaseOrDefault(Opt::MethodTraceFile);
+  method_trace_file_size_ = runtime_options.ReleaseOrDefault(Opt::MethodTraceFileSize);
 
-  profile_output_filename_ = options->profile_output_filename_;
-  profiler_options_ = options->profiler_options_;
+  {
+    auto&& profiler_options = runtime_options.ReleaseOrDefault(Opt::ProfilerOpts);
+    profile_output_filename_ = profiler_options.output_file_name_;
+
+    // TODO: Don't do this, just change ProfilerOptions to include the output file name?
+    ProfilerOptions other_options(
+        profiler_options.enabled_,
+        profiler_options.period_s_,
+        profiler_options.duration_s_,
+        profiler_options.interval_us_,
+        profiler_options.backoff_coefficient_,
+        profiler_options.start_immediately_,
+        profiler_options.top_k_threshold_,
+        profiler_options.top_k_change_threshold_,
+        profiler_options.profile_type_,
+        profiler_options.max_stack_depth_);
+
+    profiler_options_ = other_options;
+  }
 
   // TODO: move this to just be an Trace::Start argument
-  Trace::SetDefaultClockSource(options->profile_clock_source_);
+  Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
 
-  if (options->method_trace_) {
+  if (method_trace_) {
     ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
-    Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0,
-                 false, false, 0);
+    Trace::Start(method_trace_file_.c_str(),
+                 -1,
+                 static_cast<int>(method_trace_file_size_),
+                 0,
+                 false,
+                 false,
+                 0);
   }
 
   // Pre-allocate an OutOfMemoryError for the double-OOME case.
@@ -930,7 +1009,10 @@
   // Runtime::Start():
   //   DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
   //   No-op wrt native bridge.
-  is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_);
+  {
+    std::string native_bridge_file_name = runtime_options.ReleaseOrDefault(Opt::NativeBridge);
+    is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
+  }
 
   VLOG(startup) << "Runtime::Init exiting";
   return true;
@@ -956,10 +1038,9 @@
   // Most JNI libraries can just use System.loadLibrary, but libcore can't because it's
   // the library that implements System.loadLibrary!
   {
-    std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
     std::string reason;
-    if (!java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) {
-      LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
+    if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, &reason)) {
+      LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << reason;
     }
   }
 
@@ -1181,35 +1262,23 @@
   }
 }
 
+void Runtime::VisitTransactionRoots(RootCallback* callback, void* arg) {
+  if (preinitialization_transaction_ != nullptr) {
+    preinitialization_transaction_->VisitRoots(callback, arg);
+  }
+}
+
 void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
   java_vm_->VisitRoots(callback, arg);
-  if (!sentinel_.IsNull()) {
-    sentinel_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!sentinel_.IsNull());
-  }
-  if (!pre_allocated_OutOfMemoryError_.IsNull()) {
-    pre_allocated_OutOfMemoryError_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!pre_allocated_OutOfMemoryError_.IsNull());
-  }
-  resolution_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  DCHECK(!resolution_method_.IsNull());
-  if (!pre_allocated_NoClassDefFoundError_.IsNull()) {
-    pre_allocated_NoClassDefFoundError_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!pre_allocated_NoClassDefFoundError_.IsNull());
-  }
-  if (HasImtConflictMethod()) {
-    imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
-  if (!imt_unimplemented_method_.IsNull()) {
-    imt_unimplemented_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
-  if (HasDefaultImt()) {
-    default_imt_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
+  sentinel_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  resolution_method_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+  pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  imt_conflict_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  imt_unimplemented_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  default_imt_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
-    if (!callee_save_methods_[i].IsNull()) {
-      callee_save_methods_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
-    }
+    callee_save_methods_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   }
   verifier::MethodVerifier::VisitStaticRoots(callback, arg);
   {
@@ -1218,9 +1287,7 @@
       verifier->VisitRoots(callback, arg);
     }
   }
-  if (preinitialization_transaction_ != nullptr) {
-    preinitialization_transaction_->VisitRoots(callback, arg);
-  }
+  VisitTransactionRoots(callback, arg);
   instrumentation_.VisitRoots(callback, arg);
 }
 
@@ -1229,6 +1296,15 @@
   VisitNonThreadRoots(callback, arg);
 }
 
+void Runtime::VisitThreadRoots(RootCallback* callback, void* arg) {
+  thread_list_->VisitRoots(callback, arg);
+}
+
+size_t Runtime::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+                                gc::collector::GarbageCollector* collector) {
+  return thread_list_->FlipThreadRoots(thread_flip_visitor, flip_callback, collector);
+}
+
 void Runtime::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
   VisitNonConcurrentRoots(callback, arg);
   VisitConcurrentRoots(callback, arg, flags);
@@ -1257,10 +1333,9 @@
   method->SetDexMethodIndex(DexFile::kDexNoIndex);
   // When compiling, the code pointer will get set later when the image is loaded.
   if (runtime->IsCompiler()) {
-    method->SetEntryPointFromPortableCompiledCode(nullptr);
-    method->SetEntryPointFromQuickCompiledCode(nullptr);
+    size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
+    method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
   } else {
-    method->SetEntryPointFromPortableCompiledCode(GetPortableImtConflictStub());
     method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
   }
   return method.Get();
@@ -1277,10 +1352,9 @@
   method->SetDexMethodIndex(DexFile::kDexNoIndex);
   // When compiling, the code pointer will get set later when the image is loaded.
   if (runtime->IsCompiler()) {
-    method->SetEntryPointFromPortableCompiledCode(nullptr);
-    method->SetEntryPointFromQuickCompiledCode(nullptr);
+    size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
+    method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
   } else {
-    method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionStub());
     method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
   }
   return method.Get();
@@ -1295,8 +1369,8 @@
   method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod());
   // TODO: use a special method for callee saves
   method->SetDexMethodIndex(DexFile::kDexNoIndex);
-  method->SetEntryPointFromPortableCompiledCode(nullptr);
-  method->SetEntryPointFromQuickCompiledCode(nullptr);
+  size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
+  method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
   DCHECK_NE(instruction_set_, kNone);
   return method.Get();
 }
@@ -1313,6 +1387,14 @@
   java_vm_->AllowNewWeakGlobals();
 }
 
+void Runtime::EnsureNewSystemWeaksDisallowed() {
+  // Lock and unlock the system weak locks once to ensure that no
+  // threads are still in the middle of adding new system weaks.
+  monitor_list_->EnsureNewMonitorsDisallowed();
+  intern_table_->EnsureNewInternsDisallowed();
+  java_vm_->EnsureNewWeakGlobalsDisallowed();
+}
+
 void Runtime::SetInstructionSet(InstructionSet instruction_set) {
   instruction_set_ = instruction_set;
   if ((instruction_set_ == kThumb2) || (instruction_set_ == kArm)) {
@@ -1325,6 +1407,11 @@
       CalleeSaveType type = static_cast<CalleeSaveType>(i);
       callee_save_method_frame_infos_[i] = mips::MipsCalleeSaveMethodFrameInfo(type);
     }
+  } else if (instruction_set_ == kMips64) {
+    for (int i = 0; i != kLastCalleeSaveType; ++i) {
+      CalleeSaveType type = static_cast<CalleeSaveType>(i);
+      callee_save_method_frame_infos_[i] = mips64::Mips64CalleeSaveMethodFrameInfo(type);
+    }
   } else if (instruction_set_ == kX86) {
     for (int i = 0; i != kLastCalleeSaveType; ++i) {
       CalleeSaveType type = static_cast<CalleeSaveType>(i);
@@ -1369,12 +1456,18 @@
 
 void Runtime::AddMethodVerifier(verifier::MethodVerifier* verifier) {
   DCHECK(verifier != nullptr);
+  if (gAborting) {
+    return;
+  }
   MutexLock mu(Thread::Current(), method_verifier_lock_);
   method_verifiers_.insert(verifier);
 }
 
 void Runtime::RemoveMethodVerifier(verifier::MethodVerifier* verifier) {
   DCHECK(verifier != nullptr);
+  if (gAborting) {
+    return;
+  }
   MutexLock mu(Thread::Current(), method_verifier_lock_);
   auto it = method_verifiers_.find(verifier);
   CHECK(it != method_verifiers_.end());
@@ -1401,6 +1494,31 @@
   preinitialization_transaction_ = nullptr;
 }
 
+
+bool Runtime::IsTransactionAborted() const {
+  if (!IsActiveTransaction()) {
+    return false;
+  } else {
+    DCHECK(IsCompiler());
+    return preinitialization_transaction_->IsAborted();
+  }
+}
+
+void Runtime::AbortTransactionAndThrowInternalError(Thread* self,
+                                                    const std::string& abort_message) {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  preinitialization_transaction_->Abort(abort_message);
+  ThrowInternalErrorForAbortedTransaction(self);
+}
+
+void Runtime::ThrowInternalErrorForAbortedTransaction(Thread* self) {
+  DCHECK(IsCompiler());
+  DCHECK(IsActiveTransaction());
+  DCHECK(IsTransactionAborted());
+  preinitialization_transaction_->ThrowInternalError(self);
+}
+
 void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
                                       uint8_t value, bool is_volatile) const {
   DCHECK(IsCompiler());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 39fd910..d98eb8d 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -43,7 +43,13 @@
 
 namespace gc {
   class Heap;
+  namespace collector {
+    class GarbageCollector;
+  }  // namespace collector
 }  // namespace gc
+namespace JDWP {
+  struct JdwpOptions;
+}  // namespace JDWP
 namespace mirror {
   class ArtMethod;
   class ClassLoader;
@@ -58,6 +64,7 @@
   class MethodVerifier;
 }  // namespace verifier
 class ClassLinker;
+class Closure;
 class DexFile;
 class InternTable;
 class JavaVMExt;
@@ -133,6 +140,10 @@
     return compiler_options_;
   }
 
+  void AddCompilerOption(std::string option) {
+    compiler_options_.push_back(option);
+  }
+
   const std::vector<std::string>& GetImageCompilerOptions() const {
     return image_compiler_options_;
   }
@@ -266,8 +277,9 @@
     return "2.1.0";
   }
 
-  void DisallowNewSystemWeaks() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void DisallowNewSystemWeaks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void AllowNewSystemWeaks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void EnsureNewSystemWeaksDisallowed() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Visit all the roots. If only_dirty is true then non-dirty roots won't be visited. If
   // clean_dirty is true then dirty roots will be marked as non-dirty after visiting.
@@ -283,6 +295,17 @@
   void VisitNonThreadRoots(RootCallback* visitor, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void VisitTransactionRoots(RootCallback* visitor, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Visit all of the thread roots.
+  void VisitThreadRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Flip thread roots from from-space refs to to-space refs.
+  size_t FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+                         gc::collector::GarbageCollector* collector)
+      LOCKS_EXCLUDED(Locks::mutator_lock_);
+
   // Visit all other roots which must be done with mutators suspended.
   void VisitNonConcurrentRoots(RootCallback* visitor, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -421,6 +444,9 @@
       LOCKS_EXCLUDED(method_verifier_lock_);
 
   const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader);
+
+  // The caller is responsible for ensuring the class_path DexFiles remain
+  // valid as long as the Runtime object remains valid.
   void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path);
 
   void StartProfiler(const char* profile_output_filename);
@@ -432,6 +458,13 @@
   }
   void EnterTransactionMode(Transaction* transaction);
   void ExitTransactionMode();
+  bool IsTransactionAborted() const;
+
+  void AbortTransactionAndThrowInternalError(Thread* self, const std::string& abort_message)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void ThrowInternalErrorForAbortedTransaction(Thread* self)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
                                bool is_volatile) const;
   void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value,
@@ -652,6 +685,9 @@
   // that there's no native bridge.
   bool is_native_bridge_loaded_;
 
+  // JDWP options for debugging.
+  const JDWP::JdwpOptions* jdwp_options_;
+
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
 std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 1de035c..35d944f 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -25,19 +25,29 @@
 
 #include "base/dumpable.h"
 #include "base/logging.h"
+#include "base/macros.h"
 #include "base/mutex.h"
 #include "base/stringprintf.h"
 #include "thread-inl.h"
+#include "thread_list.h"
 #include "utils.h"
 
 namespace art {
 
 static constexpr bool kDumpHeapObjectOnSigsevg = false;
+static constexpr bool kUseSigRTTimeout = true;
 
 struct Backtrace {
+ public:
+  explicit Backtrace(void* raw_context) : raw_context_(raw_context) {}
   void Dump(std::ostream& os) const {
-    DumpNativeStack(os, GetTid(), "\t");
+    DumpNativeStack(os, GetTid(), "\t", nullptr, raw_context_);
   }
+ private:
+  // Stores the context of the signal that was unexpected and will terminate the runtime. The
+  // DumpNativeStack code will take care of casting it to the expected type. This is required
+  // as our signal handler runs on an alternate stack.
+  void* raw_context_;
 };
 
 struct OsInfo {
@@ -276,10 +286,29 @@
   mcontext_t& context;
 };
 
+// Return the signal number we recognize as timeout. -1 means not active/supported.
+static int GetTimeoutSignal() {
+#if defined(__APPLE__)
+  // Mac does not support realtime signals.
+  UNUSED(kUseSigRTTimeout);
+  return -1;
+#else
+  return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1;
+#endif
+}
+
+static bool IsTimeoutSignal(int signal_number) {
+  return signal_number == GetTimeoutSignal();
+}
+
 void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) {
   static bool handlingUnexpectedSignal = false;
   if (handlingUnexpectedSignal) {
     LogMessage::LogLine(__FILE__, __LINE__, INTERNAL_FATAL, "HandleUnexpectedSignal reentered\n");
+    if (IsTimeoutSignal(signal_number)) {
+      // Ignore a recursive timeout.
+      return;
+    }
     _exit(1);
   }
   handlingUnexpectedSignal = true;
@@ -298,7 +327,7 @@
   pid_t tid = GetTid();
   std::string thread_name(GetThreadName(tid));
   UContext thread_context(raw_context);
-  Backtrace thread_backtrace;
+  Backtrace thread_backtrace(raw_context);
 
   LOG(INTERNAL_FATAL) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
                       << StringPrintf("Fatal signal %d (%s), code %d (%s)",
@@ -313,6 +342,10 @@
                       << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
   Runtime* runtime = Runtime::Current();
   if (runtime != nullptr) {
+    if (IsTimeoutSignal(signal_number)) {
+      // Special timeout signal. Try to dump all threads.
+      runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL));
+    }
     gc::Heap* heap = runtime->GetHeap();
     LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
     if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
@@ -367,6 +400,10 @@
   rc += sigaction(SIGSTKFLT, &action, NULL);
 #endif
   rc += sigaction(SIGTRAP, &action, NULL);
+  // Special dump-all timeout.
+  if (GetTimeoutSignal() != -1) {
+    rc += sigaction(GetTimeoutSignal(), &action, NULL);
+  }
   CHECK_EQ(rc, 0);
 }
 
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
new file mode 100644
index 0000000..c54461e
--- /dev/null
+++ b/runtime/runtime_options.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "runtime_options.h"
+
+#include "gc/heap.h"
+#include "monitor.h"
+#include "runtime.h"
+#include "trace.h"
+#include "utils.h"
+#include "debugger.h"
+
+namespace art {
+
+// Specify storage for the RuntimeOptions keys.
+
+#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key<Type> RuntimeArgumentMap::Name {__VA_ARGS__};  // NOLINT [readability/braces] [4]
+#include "runtime_options.def"
+
+}  // namespace art
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
new file mode 100644
index 0000000..022a2a5
--- /dev/null
+++ b/runtime/runtime_options.def
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef RUNTIME_OPTIONS_KEY
+#error "Please #define RUNTIME_OPTIONS_KEY before #including this file"
+#define RUNTIME_OPTIONS_KEY(...)  // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for RuntimeOptions.
+// These can be used with RuntimeOptions.Get/Set/etc, for example:
+//         RuntimeOptions opt; bool* dex2oat_enabled = opt.Get(RuntimeOptions::Dex2Oat);
+//
+// Column Descriptions:
+//                   <<Type>>             <<Key Name>>                  <<Default Value>>
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+
+// Parse-able keys from the command line.
+RUNTIME_OPTIONS_KEY (Unit,                Zygote)
+RUNTIME_OPTIONS_KEY (Unit,                Help)
+RUNTIME_OPTIONS_KEY (Unit,                ShowVersion)
+RUNTIME_OPTIONS_KEY (std::string,         BootClassPath)
+RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations)  // std::vector<std::string>
+RUNTIME_OPTIONS_KEY (std::string,         ClassPath)
+RUNTIME_OPTIONS_KEY (std::string,         Image)
+RUNTIME_OPTIONS_KEY (Unit,                CheckJni)
+RUNTIME_OPTIONS_KEY (Unit,                JniOptsForceCopy)
+RUNTIME_OPTIONS_KEY (JDWP::JdwpOptions,   JdwpOptions)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryMaximumSize,              gc::Heap::kDefaultMaximumSize)  // -Xmx
+RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryInitialSize,              gc::Heap::kDefaultInitialSize)  // -Xms
+RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapGrowthLimit)                // Default is 0 for unlimited
+RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapMinFree,                    gc::Heap::kDefaultMinFree)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapMaxFree,                    gc::Heap::kDefaultMaxFree)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           NonMovingSpaceCapacity,         gc::Heap::kDefaultNonMovingSpaceCapacity)
+RUNTIME_OPTIONS_KEY (double,              HeapTargetUtilization,          gc::Heap::kDefaultTargetUtilization)
+RUNTIME_OPTIONS_KEY (double,              ForegroundHeapGrowthMultiplier, gc::Heap::kDefaultHeapGrowthMultiplier)
+RUNTIME_OPTIONS_KEY (unsigned int,        ParallelGCThreads,              1u)
+RUNTIME_OPTIONS_KEY (unsigned int,        ConcGCThreads)
+RUNTIME_OPTIONS_KEY (Memory<1>,           StackSize)  // -Xss
+RUNTIME_OPTIONS_KEY (unsigned int,        MaxSpinsBeforeThinLockInflation,Monitor::kDefaultMaxSpinsBeforeThinLockInflation)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+                                          LongPauseLogThreshold,          gc::Heap::kDefaultLongPauseLogThreshold)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+                                          LongGCLogThreshold,             gc::Heap::kDefaultLongGCLogThreshold)
+RUNTIME_OPTIONS_KEY (Unit,                DumpGCPerformanceOnShutdown)
+RUNTIME_OPTIONS_KEY (Unit,                IgnoreMaxFootprint)
+RUNTIME_OPTIONS_KEY (Unit,                LowMemoryMode)
+RUNTIME_OPTIONS_KEY (Unit,                UseTLAB)
+RUNTIME_OPTIONS_KEY (bool,                EnableHSpaceCompactForOOM,      true)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+                                          HSpaceCompactForOOMMinIntervalsMs,\
+                                                                          MsToNs(100 * 1000))  // 100s
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+                                          PropertiesList)  // -D<whatever> -D<whatever> ...
+RUNTIME_OPTIONS_KEY (std::string,         JniTrace)
+RUNTIME_OPTIONS_KEY (std::string,         PatchOat)
+RUNTIME_OPTIONS_KEY (bool,                Relocate,                       kDefaultMustRelocate)
+RUNTIME_OPTIONS_KEY (bool,                Dex2Oat,                        true)
+RUNTIME_OPTIONS_KEY (bool,                ImageDex2Oat,                   true)
+                                                        // kPoisonHeapReferences currently works with
+                                                        // the interpreter only.
+                                                        // TODO: make it work with the compiler.
+RUNTIME_OPTIONS_KEY (bool,                Interpret,                      (kPoisonHeapReferences || kUseReadBarrier)) // -Xint
+                                                        // Disable the compiler for CC (for now).
+RUNTIME_OPTIONS_KEY (XGcOption,           GcOption)  // -Xgc:
+RUNTIME_OPTIONS_KEY (gc::space::LargeObjectSpaceType, \
+                                          LargeObjectSpace,               gc::Heap::kDefaultLargeObjectSpaceType)
+RUNTIME_OPTIONS_KEY (Memory<1>,           LargeObjectThreshold,           gc::Heap::kDefaultLargeObjectThreshold)
+RUNTIME_OPTIONS_KEY (BackgroundGcOption,  BackgroundGc)
+
+RUNTIME_OPTIONS_KEY (Unit,                DisableExplicitGC)
+RUNTIME_OPTIONS_KEY (LogVerbosity,        Verbose)
+RUNTIME_OPTIONS_KEY (unsigned int,        LockProfThreshold)
+RUNTIME_OPTIONS_KEY (std::string,         StackTraceFile)
+RUNTIME_OPTIONS_KEY (Unit,                MethodTrace)
+RUNTIME_OPTIONS_KEY (std::string,         MethodTraceFile,                "/data/method-trace-file.bin")
+RUNTIME_OPTIONS_KEY (unsigned int,        MethodTraceFileSize,            10 * MB)
+RUNTIME_OPTIONS_KEY (TraceClockSource,    ProfileClock,                   kDefaultTraceClockSource)  // -Xprofile:
+RUNTIME_OPTIONS_KEY (TestProfilerOptions, ProfilerOpts)  // -Xenable-profiler, -Xprofile-*
+RUNTIME_OPTIONS_KEY (std::string,         Compiler)
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+                                          CompilerOptions)  // -Xcompiler-option ...
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+                                          ImageCompilerOptions)  // -Ximage-compiler-option ...
+RUNTIME_OPTIONS_KEY (bool,                Verify,                         true)
+RUNTIME_OPTIONS_KEY (std::string,         NativeBridge)
+
+// Not parse-able from command line, but can be provided explicitly.
+RUNTIME_OPTIONS_KEY (const std::vector<const DexFile*>*, \
+                                          BootClassPathDexList)  // TODO: make unique_ptr
+RUNTIME_OPTIONS_KEY (InstructionSet,      ImageInstructionSet,            kRuntimeISA)
+RUNTIME_OPTIONS_KEY (CompilerCallbacks*,  CompilerCallbacksPtr)  // TDOO: make unique_ptr
+RUNTIME_OPTIONS_KEY (bool (*)(),          HookIsSensitiveThread)
+RUNTIME_OPTIONS_KEY (int32_t (*)(FILE* stream, const char* format, va_list ap), \
+                                          HookVfprintf,                   vfprintf)
+RUNTIME_OPTIONS_KEY (void (*)(int32_t status), \
+                                          HookExit,                       exit)
+                                                                          // We don't call abort(3) by default; see
+                                                                          // Runtime::Abort.
+RUNTIME_OPTIONS_KEY (void (*)(),          HookAbort,                      nullptr)
+
+
+#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
new file mode 100644
index 0000000..ebd52d7
--- /dev/null
+++ b/runtime/runtime_options.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_OPTIONS_H_
+#define ART_RUNTIME_RUNTIME_OPTIONS_H_
+
+#include "runtime/base/variant_map.h"
+#include "cmdline/cmdline_types.h"  // TODO: don't need to include this file here
+
+// Map keys
+#include <vector>
+#include <string>
+#include "runtime/base/logging.h"
+#include "cmdline/unit.h"
+#include "jdwp/jdwp.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+#include "arch/instruction_set.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+namespace art {
+
+class CompilerCallbacks;
+class DexFile;
+struct XGcOption;
+struct BackgroundGcOption;
+struct TestProfilerOptions;
+
+#define DECLARE_KEY(Type, Name) static const Key<Type> Name
+
+  // Define a key that is usable with a RuntimeArgumentMap.
+  // This key will *not* work with other subtypes of VariantMap.
+  template <typename TValue>
+  struct RuntimeArgumentMapKey : VariantMapKey<TValue> {
+    RuntimeArgumentMapKey() {}
+    explicit RuntimeArgumentMapKey(TValue default_value)
+      : VariantMapKey<TValue>(std::move(default_value)) {}
+    // Don't ODR-use constexpr default values, which means that Struct::Fields
+    // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+  };
+
+  // Defines a type-safe heterogeneous key->value map.
+  // Use the VariantMap interface to look up or to store a RuntimeArgumentMapKey,Value pair.
+  //
+  // Example:
+  //    auto map = RuntimeArgumentMap();
+  //    map.Set(RuntimeArgumentMap::HeapTargetUtilization, 5.0);
+  //    double *target_utilization = map.Get(RuntimeArgumentMap);
+  //
+  struct RuntimeArgumentMap : VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey> {
+    // This 'using' line is necessary to inherit the variadic constructor.
+    using VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey>::VariantMap;
+
+    // Make the next many usages of Key slightly shorter to type.
+    template <typename TValue>
+    using Key = RuntimeArgumentMapKey<TValue>;
+
+    // List of key declarations, shorthand for 'static const Key<T> Name'
+#define RUNTIME_OPTIONS_KEY(Type, Name, ...) static const Key<Type> Name;
+#include "runtime_options.def"
+  };
+
+#undef DECLARE_KEY
+
+  // using RuntimeOptions = RuntimeArgumentMap;
+}  // namespace art
+
+#endif  // ART_RUNTIME_RUNTIME_OPTIONS_H_
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index ae3eaf2..adf3480 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -20,6 +20,7 @@
 #include "base/casts.h"
 #include "java_vm_ext.h"
 #include "jni_env_ext-inl.h"
+#include "mirror/art_field.h"
 #include "read_barrier.h"
 #include "thread-inl.h"
 #include "verify_object.h"
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 43714b9..b39aebf 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -113,6 +113,9 @@
   }
 }
 
+extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference<mirror::ArtMethod>* sp)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 mirror::Object* StackVisitor::GetThisObject() const {
   mirror::ArtMethod* m = GetMethod();
   if (m->IsStatic()) {
@@ -125,7 +128,14 @@
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
-  } else if (m->IsOptimized(sizeof(void*))) {
+  } else if (m->IsProxyMethod()) {
+    if (cur_quick_frame_ != nullptr) {
+      return artQuickGetProxyThisObject(cur_quick_frame_);
+    } else {
+      return cur_shadow_frame_->GetVRegReference(0);
+    }
+  } else if (m->IsOptimized(GetInstructionSetPointerSize(
+      Runtime::Current()->GetInstructionSet()))) {
     // TODO: Implement, currently only used for exceptions when jdwp is enabled.
     UNIMPLEMENTED(WARNING)
         << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
@@ -163,11 +173,10 @@
       bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
       uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
       uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
-      uintptr_t ptr_val;
-      bool success = is_float ? GetFPR(reg, &ptr_val) : GetGPR(reg, &ptr_val);
-      if (!success) {
+      if (!IsAccessibleRegister(reg, is_float)) {
         return false;
       }
+      uintptr_t ptr_val = GetRegister(reg, is_float);
       bool target64 = Is64BitInstructionSet(kRuntimeISA);
       if (target64) {
         bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
@@ -185,11 +194,14 @@
       const DexFile::CodeItem* code_item = m->GetCodeItem();
       DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
                                                         // its instructions?
-      *val = *GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
-                          frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+      uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
+                                   frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+      DCHECK(addr != nullptr);
+      *val = *addr;
       return true;
     }
   } else {
+    DCHECK(cur_shadow_frame_ != nullptr);
     *val = cur_shadow_frame_->GetVReg(vreg);
     return true;
   }
@@ -219,12 +231,11 @@
       uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
       uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
       uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
-      uintptr_t ptr_val_lo, ptr_val_hi;
-      bool success = is_float ? GetFPR(reg_lo, &ptr_val_lo) : GetGPR(reg_lo, &ptr_val_lo);
-      success &= is_float ? GetFPR(reg_hi, &ptr_val_hi) : GetGPR(reg_hi, &ptr_val_hi);
-      if (!success) {
+      if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
         return false;
       }
+      uintptr_t ptr_val_lo = GetRegister(reg_lo, is_float);
+      uintptr_t ptr_val_hi = GetRegister(reg_hi, is_float);
       bool target64 = Is64BitInstructionSet(kRuntimeISA);
       if (target64) {
         int64_t value_long_lo = static_cast<int64_t>(ptr_val_lo);
@@ -240,10 +251,12 @@
                                                         // its instructions?
       uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
                                    frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+      DCHECK(addr != nullptr);
       *val = *reinterpret_cast<uint64_t*>(addr);
       return true;
     }
   } else {
+    DCHECK(cur_shadow_frame_ != nullptr);
     *val = cur_shadow_frame_->GetVRegLong(vreg);
     return true;
   }
@@ -264,17 +277,16 @@
       bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
       uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
       const uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
+      if (!IsAccessibleRegister(reg, is_float)) {
+        return false;
+      }
       bool target64 = Is64BitInstructionSet(kRuntimeISA);
       // Deal with 32 or 64-bit wide registers in a way that builds on all targets.
       if (target64) {
         bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
         bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
         if (wide_lo || wide_hi) {
-          uintptr_t old_reg_val;
-          bool success = is_float ? GetFPR(reg, &old_reg_val) : GetGPR(reg, &old_reg_val);
-          if (!success) {
-            return false;
-          }
+          uintptr_t old_reg_val = GetRegister(reg, is_float);
           uint64_t new_vreg_portion = static_cast<uint64_t>(new_value);
           uint64_t old_reg_val_as_wide = static_cast<uint64_t>(old_reg_val);
           uint64_t mask = 0xffffffff;
@@ -286,21 +298,20 @@
           new_value = static_cast<uintptr_t>((old_reg_val_as_wide & mask) | new_vreg_portion);
         }
       }
-      if (is_float) {
-        return SetFPR(reg, new_value);
-      } else {
-        return SetGPR(reg, new_value);
-      }
+      SetRegister(reg, new_value, is_float);
+      return true;
     } else {
       const DexFile::CodeItem* code_item = m->GetCodeItem();
       DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
                                                         // its instructions?
       uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
                                    frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+      DCHECK(addr != nullptr);
       *addr = new_value;
       return true;
     }
   } else {
+    DCHECK(cur_shadow_frame_ != nullptr);
     cur_shadow_frame_->SetVReg(vreg, new_value);
     return true;
   }
@@ -330,17 +341,16 @@
       uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
       uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
       uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
+      if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
+        return false;
+      }
       uintptr_t new_value_lo = static_cast<uintptr_t>(new_value & 0xFFFFFFFF);
       uintptr_t new_value_hi = static_cast<uintptr_t>(new_value >> 32);
       bool target64 = Is64BitInstructionSet(kRuntimeISA);
       // Deal with 32 or 64-bit wide registers in a way that builds on all targets.
       if (target64) {
-        uintptr_t old_reg_val_lo, old_reg_val_hi;
-        bool success = is_float ? GetFPR(reg_lo, &old_reg_val_lo) : GetGPR(reg_lo, &old_reg_val_lo);
-        success &= is_float ? GetFPR(reg_hi, &old_reg_val_hi) : GetGPR(reg_hi, &old_reg_val_hi);
-        if (!success) {
-          return false;
-        }
+        uintptr_t old_reg_val_lo = GetRegister(reg_lo, is_float);
+        uintptr_t old_reg_val_hi = GetRegister(reg_hi, is_float);
         uint64_t new_vreg_portion_lo = static_cast<uint64_t>(new_value_lo);
         uint64_t new_vreg_portion_hi = static_cast<uint64_t>(new_value_hi) << 32;
         uint64_t old_reg_val_lo_as_wide = static_cast<uint64_t>(old_reg_val_lo);
@@ -350,47 +360,64 @@
         new_value_lo = static_cast<uintptr_t>((old_reg_val_lo_as_wide & mask_lo) | new_vreg_portion_lo);
         new_value_hi = static_cast<uintptr_t>((old_reg_val_hi_as_wide & mask_hi) | new_vreg_portion_hi);
       }
-      bool success = is_float ? SetFPR(reg_lo, new_value_lo) : SetGPR(reg_lo, new_value_lo);
-      success &= is_float ? SetFPR(reg_hi, new_value_hi) : SetGPR(reg_hi, new_value_hi);
-      return success;
+      SetRegister(reg_lo, new_value_lo, is_float);
+      SetRegister(reg_hi, new_value_hi, is_float);
+      return true;
     } else {
       const DexFile::CodeItem* code_item = m->GetCodeItem();
       DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
                                                         // its instructions?
       uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
                                    frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+      DCHECK(addr != nullptr);
       *reinterpret_cast<uint64_t*>(addr) = new_value;
       return true;
     }
   } else {
+    DCHECK(cur_shadow_frame_ != nullptr);
     cur_shadow_frame_->SetVRegLong(vreg, new_value);
     return true;
   }
 }
 
+bool StackVisitor::IsAccessibleGPR(uint32_t reg) const {
+  DCHECK(context_ != nullptr);
+  return context_->IsAccessibleGPR(reg);
+}
+
 uintptr_t* StackVisitor::GetGPRAddress(uint32_t reg) const {
-  DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
+  DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+  DCHECK(context_ != nullptr);
   return context_->GetGPRAddress(reg);
 }
 
-bool StackVisitor::GetGPR(uint32_t reg, uintptr_t* val) const {
-  DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
-  return context_->GetGPR(reg, val);
+uintptr_t StackVisitor::GetGPR(uint32_t reg) const {
+  DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+  DCHECK(context_ != nullptr);
+  return context_->GetGPR(reg);
 }
 
-bool StackVisitor::SetGPR(uint32_t reg, uintptr_t value) {
-  DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
-  return context_->SetGPR(reg, value);
+void StackVisitor::SetGPR(uint32_t reg, uintptr_t value) {
+  DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+  DCHECK(context_ != nullptr);
+  context_->SetGPR(reg, value);
 }
 
-bool StackVisitor::GetFPR(uint32_t reg, uintptr_t* val) const {
-  DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
-  return context_->GetFPR(reg, val);
+bool StackVisitor::IsAccessibleFPR(uint32_t reg) const {
+  DCHECK(context_ != nullptr);
+  return context_->IsAccessibleFPR(reg);
 }
 
-bool StackVisitor::SetFPR(uint32_t reg, uintptr_t value) {
-  DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine";
-  return context_->SetFPR(reg, value);
+uintptr_t StackVisitor::GetFPR(uint32_t reg) const {
+  DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+  DCHECK(context_ != nullptr);
+  return context_->GetFPR(reg);
+}
+
+void StackVisitor::SetFPR(uint32_t reg, uintptr_t value) {
+  DCHECK(cur_quick_frame_ != nullptr) << "This is a quick frame routine";
+  DCHECK(context_ != nullptr);
+  context_->SetFPR(reg, value);
 }
 
 uintptr_t StackVisitor::GetReturnPc() const {
@@ -605,4 +632,11 @@
   }
 }
 
+void JavaFrameRootInfo::Describe(std::ostream& os) const {
+  const StackVisitor* visitor = stack_visitor_;
+  CHECK(visitor != nullptr);
+  os << "Type=" << GetType() << " thread_id=" << GetThreadId() << " location=" <<
+      visitor->DescribeLocation() << " vreg=" << vreg_;
+}
+
 }  // namespace art
diff --git a/runtime/stack.h b/runtime/stack.h
index 1d772e6..5a86ca1 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -22,7 +22,9 @@
 
 #include "arch/instruction_set.h"
 #include "dex_file.h"
+#include "gc_root.h"
 #include "mirror/object_reference.h"
+#include "read_barrier.h"
 #include "throw_location.h"
 #include "utils.h"
 #include "verify_object.h"
@@ -72,8 +74,7 @@
       : mirror::ObjectReference<false, MirrorType>(p) {}
 };
 
-// ShadowFrame has 3 possible layouts:
-//  - portable - a unified array of VRegs and references. Precise references need GC maps.
+// ShadowFrame has 2 possible layouts:
 //  - interpreter - separate VRegs and reference arrays. References are in the reference array.
 //  - JNI - just VRegs, but where every VReg holds a reference.
 class ShadowFrame {
@@ -100,28 +101,11 @@
   ~ShadowFrame() {}
 
   bool HasReferenceArray() const {
-#if defined(ART_USE_PORTABLE_COMPILER)
-    return (number_of_vregs_ & kHasReferenceArray) != 0;
-#else
     return true;
-#endif
   }
 
   uint32_t NumberOfVRegs() const {
-#if defined(ART_USE_PORTABLE_COMPILER)
-    return number_of_vregs_ & ~kHasReferenceArray;
-#else
     return number_of_vregs_;
-#endif
-  }
-
-  void SetNumberOfVRegs(uint32_t number_of_vregs) {
-#if defined(ART_USE_PORTABLE_COMPILER)
-    number_of_vregs_ = number_of_vregs | (number_of_vregs_ & kHasReferenceArray);
-#else
-    UNUSED(number_of_vregs);
-    UNIMPLEMENTED(FATAL) << "Should only be called when portable is enabled";
-#endif
   }
 
   uint32_t GetDexPC() const {
@@ -180,6 +164,9 @@
       const uint32_t* vreg_ptr = &vregs_[i];
       ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
     }
+    if (kUseReadBarrier) {
+      ReadBarrier::AssertToSpaceInvariant(ref);
+    }
     if (kVerifyFlags & kVerifyReads) {
       VerifyObject(ref);
     }
@@ -247,6 +234,9 @@
     if (kVerifyFlags & kVerifyWrites) {
       VerifyObject(val);
     }
+    if (kUseReadBarrier) {
+      ReadBarrier::AssertToSpaceInvariant(val);
+    }
     uint32_t* vreg = &vregs_[i];
     reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
     if (HasReferenceArray()) {
@@ -270,16 +260,6 @@
 
   ThrowLocation GetCurrentLocationForThrow() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetMethod(mirror::ArtMethod* method) {
-#if defined(ART_USE_PORTABLE_COMPILER)
-    DCHECK(method != nullptr);
-    method_ = method;
-#else
-    UNUSED(method);
-    UNIMPLEMENTED(FATAL) << "Should only be called when portable is enabled";
-#endif
-  }
-
   bool Contains(StackReference<mirror::Object>* shadow_frame_entry_obj) const {
     if (HasReferenceArray()) {
       return ((&References()[0] <= shadow_frame_entry_obj) &&
@@ -316,10 +296,6 @@
               uint32_t dex_pc, bool has_reference_array)
       : number_of_vregs_(num_vregs), link_(link), method_(method), dex_pc_(dex_pc) {
     if (has_reference_array) {
-#if defined(ART_USE_PORTABLE_COMPILER)
-      CHECK_LT(num_vregs, static_cast<uint32_t>(kHasReferenceArray));
-      number_of_vregs_ |= kHasReferenceArray;
-#endif
       memset(vregs_, 0, num_vregs * (sizeof(uint32_t) + sizeof(StackReference<mirror::Object>)));
     } else {
       memset(vregs_, 0, num_vregs * sizeof(uint32_t));
@@ -336,13 +312,7 @@
     return const_cast<StackReference<mirror::Object>*>(const_cast<const ShadowFrame*>(this)->References());
   }
 
-#if defined(ART_USE_PORTABLE_COMPILER)
-  constexpr uint32_t kHasReferenceArray = 1ul << 31;
-  // TODO: make const in the portable case.
-  uint32_t number_of_vregs_;
-#else
   const uint32_t number_of_vregs_;
-#endif
   // Link to previous shadow frame or NULL.
   ShadowFrame* link_;
   mirror::ArtMethod* method_;
@@ -352,6 +322,19 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ShadowFrame);
 };
 
+class JavaFrameRootInfo : public RootInfo {
+ public:
+  JavaFrameRootInfo(uint32_t thread_id, const StackVisitor* stack_visitor, size_t vreg)
+     : RootInfo(kRootJavaFrame, thread_id), stack_visitor_(stack_visitor), vreg_(vreg) {
+  }
+  virtual void Describe(std::ostream& os) const OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  const StackVisitor* const stack_visitor_;
+  const size_t vreg_;
+};
+
 // The managed stack is used to record fragments of managed code stacks. Managed code stacks
 // may either be shadow frames or lists of frames using fixed frame sizes. Transition records are
 // necessary for transitions between code using different frame layouts and transitions into native
@@ -666,10 +649,29 @@
   StackVisitor(Thread* thread, Context* context, size_t num_frames)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool GetGPR(uint32_t reg, uintptr_t* val) const;
-  bool SetGPR(uint32_t reg, uintptr_t value);
-  bool GetFPR(uint32_t reg, uintptr_t* val) const;
-  bool SetFPR(uint32_t reg, uintptr_t value);
+  bool IsAccessibleRegister(uint32_t reg, bool is_float) const {
+    return is_float ? IsAccessibleFPR(reg) : IsAccessibleGPR(reg);
+  }
+  uintptr_t GetRegister(uint32_t reg, bool is_float) const {
+    DCHECK(IsAccessibleRegister(reg, is_float));
+    return is_float ? GetFPR(reg) : GetGPR(reg);
+  }
+  void SetRegister(uint32_t reg, uintptr_t value, bool is_float) {
+    DCHECK(IsAccessibleRegister(reg, is_float));
+    if (is_float) {
+      SetFPR(reg, value);
+    } else {
+      SetGPR(reg, value);
+    }
+  }
+
+  bool IsAccessibleGPR(uint32_t reg) const;
+  uintptr_t GetGPR(uint32_t reg) const;
+  void SetGPR(uint32_t reg, uintptr_t value);
+
+  bool IsAccessibleFPR(uint32_t reg) const;
+  uintptr_t GetFPR(uint32_t reg) const;
+  void SetFPR(uint32_t reg, uintptr_t value);
 
   void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index a58ecab..7cc3e57 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -19,6 +19,7 @@
 
 #include "base/bit_vector.h"
 #include "memory_region.h"
+#include "utils.h"
 
 namespace art {
 
@@ -199,6 +200,11 @@
        && region_.size() == other.region_.size();
   }
 
+  static size_t ComputeAlignedStackMapSize(size_t stack_mask_size) {
+    // On ARM, the stack maps must be 4-byte aligned.
+    return RoundUp(StackMap::kFixedSize + stack_mask_size, 4);
+  }
+
  private:
   static constexpr int kDexPcOffset = 0;
   static constexpr int kNativePcOffsetOffset = kDexPcOffset + sizeof(uint32_t);
@@ -262,7 +268,7 @@
   }
 
   size_t StackMapSize() const {
-    return StackMap::kFixedSize + GetStackMaskSize();
+    return StackMap::ComputeAlignedStackMapSize(GetStackMaskSize());
   }
 
   DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) {
diff --git a/runtime/strutil.h b/runtime/strutil.h
deleted file mode 100644
index c8d39e2..0000000
--- a/runtime/strutil.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_RUNTIME_STRUTIL_H_
-#define ART_RUNTIME_STRUTIL_H_
-
-#include <string.h>
-
-namespace art {
-
-// Key comparison function for C strings.
-struct CStringLt {
-  bool operator()(const char* s1, const char* s2) const {
-    return strcmp(s1, s2) < 0;
-  }
-};
-
-// Key equality function for C strings.
-struct CStringEq {
-  bool operator()(const char* s1, const char* s2) const {
-    return strcmp(s1, s2) == 0;
-  }
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_STRUTIL_H_
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 7aed8b0..16add79 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -178,6 +178,11 @@
       // Failed to transition to Runnable. Release shared mutator_lock_ access and try again.
       Locks::mutator_lock_->SharedUnlock(this);
     } else {
+      // Run the flip function, if set.
+      Closure* flip_func = GetFlipFunction();
+      if (flip_func != nullptr) {
+        flip_func->Run(this);
+      }
       return static_cast<ThreadState>(old_state);
     }
   } while (true);
@@ -208,22 +213,23 @@
   if (tlsPtr_.thread_local_alloc_stack_top < tlsPtr_.thread_local_alloc_stack_end) {
     // There's room.
     DCHECK_LE(reinterpret_cast<uint8_t*>(tlsPtr_.thread_local_alloc_stack_top) +
-                  sizeof(mirror::Object*),
+              sizeof(StackReference<mirror::Object>),
               reinterpret_cast<uint8_t*>(tlsPtr_.thread_local_alloc_stack_end));
-    DCHECK(*tlsPtr_.thread_local_alloc_stack_top == nullptr);
-    *tlsPtr_.thread_local_alloc_stack_top = obj;
+    DCHECK(tlsPtr_.thread_local_alloc_stack_top->AsMirrorPtr() == nullptr);
+    tlsPtr_.thread_local_alloc_stack_top->Assign(obj);
     ++tlsPtr_.thread_local_alloc_stack_top;
     return true;
   }
   return false;
 }
 
-inline void Thread::SetThreadLocalAllocationStack(mirror::Object** start, mirror::Object** end) {
+inline void Thread::SetThreadLocalAllocationStack(StackReference<mirror::Object>* start,
+                                                  StackReference<mirror::Object>* end) {
   DCHECK(Thread::Current() == this) << "Should be called by self";
   DCHECK(start != nullptr);
   DCHECK(end != nullptr);
-  DCHECK_ALIGNED(start, sizeof(mirror::Object*));
-  DCHECK_ALIGNED(end, sizeof(mirror::Object*));
+  DCHECK_ALIGNED(start, sizeof(StackReference<mirror::Object>));
+  DCHECK_ALIGNED(end, sizeof(StackReference<mirror::Object>));
   DCHECK_LT(start, end);
   tlsPtr_.thread_local_alloc_stack_end = end;
   tlsPtr_.thread_local_alloc_stack_top = start;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f7c7106..16edab3 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -33,6 +33,7 @@
 
 #include "arch/context.h"
 #include "base/mutex.h"
+#include "base/timing_logger.h"
 #include "base/to_str.h"
 #include "class_linker-inl.h"
 #include "class_linker.h"
@@ -91,7 +92,7 @@
 }
 
 void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
-                     PortableEntryPoints* ppoints, QuickEntryPoints* qpoints);
+                     QuickEntryPoints* qpoints);
 
 void Thread::InitTlsEntryPoints() {
   // Insert a placeholder so we can easily tell if we call an unimplemented entry point.
@@ -102,7 +103,7 @@
     *it = reinterpret_cast<uintptr_t>(UnimplementedEntryPoint);
   }
   InitEntryPoints(&tlsPtr_.interpreter_entrypoints, &tlsPtr_.jni_entrypoints,
-                  &tlsPtr_.portable_entrypoints, &tlsPtr_.quick_entrypoints);
+                  &tlsPtr_.quick_entrypoints);
 }
 
 void Thread::ResetQuickAllocEntryPointsForThread() {
@@ -515,12 +516,6 @@
   size_t read_guard_size;
   GetThreadStack(tlsPtr_.pthread_self, &read_stack_base, &read_stack_size, &read_guard_size);
 
-  // This is included in the SIGQUIT output, but it's useful here for thread debugging.
-  VLOG(threads) << StringPrintf("Native stack is at %p (%s with %s guard)",
-                                read_stack_base,
-                                PrettySize(read_stack_size).c_str(),
-                                PrettySize(read_guard_size).c_str());
-
   tlsPtr_.stack_begin = reinterpret_cast<uint8_t*>(read_stack_base);
   tlsPtr_.stack_size = read_stack_size;
 
@@ -537,6 +532,12 @@
     return false;
   }
 
+  // This is included in the SIGQUIT output, but it's useful here for thread debugging.
+  VLOG(threads) << StringPrintf("Native stack is at %p (%s with %s guard)",
+                                read_stack_base,
+                                PrettySize(read_stack_size).c_str(),
+                                PrettySize(read_guard_size).c_str());
+
   // Set stack_end_ to the bottom of the stack saving space of stack overflows
 
   Runtime* runtime = Runtime::Current();
@@ -592,13 +593,13 @@
 }
 
 uint64_t Thread::GetCpuMicroTime() const {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
   clockid_t cpu_clock_id;
   pthread_getcpuclockid(tlsPtr_.pthread_self, &cpu_clock_id);
   timespec now;
   clock_gettime(cpu_clock_id, &now);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_nsec / UINT64_C(1000);
-#else
+#else  // __APPLE__
   UNIMPLEMENTED(WARNING);
   return -1;
 #endif
@@ -723,13 +724,34 @@
   return success;
 }
 
+Closure* Thread::GetFlipFunction() {
+  Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
+  Closure* func;
+  do {
+    func = atomic_func->LoadRelaxed();
+    if (func == nullptr) {
+      return nullptr;
+    }
+  } while (!atomic_func->CompareExchangeWeakSequentiallyConsistent(func, nullptr));
+  DCHECK(func != nullptr);
+  return func;
+}
+
+void Thread::SetFlipFunction(Closure* function) {
+  CHECK(function != nullptr);
+  Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
+  atomic_func->StoreSequentiallyConsistent(function);
+}
+
 void Thread::FullSuspendCheck() {
   VLOG(threads) << this << " self-suspending";
   ATRACE_BEGIN("Full suspend check");
   // Make thread appear suspended to other threads, release mutator_lock_.
+  tls32_.suspended_at_suspend_check = true;
   TransitionFromRunnableToSuspended(kSuspended);
   // Transition back to runnable noting requests to suspend, re-acquire share on mutator_lock_.
   TransitionFromSuspendedToRunnable();
+  tls32_.suspended_at_suspend_check = false;
   ATRACE_END();
   VLOG(threads) << this << " self-reviving";
 }
@@ -740,6 +762,20 @@
   bool is_daemon = false;
   Thread* self = Thread::Current();
 
+  // If flip_function is not null, it means we have run a checkpoint
+  // before the thread wakes up to execute the flip function and the
+  // thread roots haven't been forwarded.  So the following access to
+  // the roots (opeer or methods in the frames) would be bad. Run it
+  // here. TODO: clean up.
+  if (thread != nullptr) {
+    ScopedObjectAccessUnchecked soa(self);
+    Thread* this_thread = const_cast<Thread*>(thread);
+    Closure* flip_func = this_thread->GetFlipFunction();
+    if (flip_func != nullptr) {
+      flip_func->Run(this_thread);
+    }
+  }
+
   // Don't do this if we are aborting since the GC may have all the threads suspended. This will
   // cause ScopedObjectAccessUnchecked to deadlock.
   if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) {
@@ -932,7 +968,10 @@
         os << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", reinterpret_cast<intptr_t>(o),
                            PrettyTypeOf(o).c_str());
       } else {
-        os << StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), PrettyTypeOf(o).c_str());
+        // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So
+        // we get the pretty type beofre we call IdentityHashCode.
+        const std::string pretty_type(PrettyTypeOf(o));
+        os << StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str());
       }
     }
     os << "\n";
@@ -977,10 +1016,24 @@
 }
 
 void Thread::DumpJavaStack(std::ostream& os) const {
+  // If flip_function is not null, it means we have run a checkpoint
+  // before the thread wakes up to execute the flip function and the
+  // thread roots haven't been forwarded.  So the following access to
+  // the roots (locks or methods in the frames) would be bad. Run it
+  // here. TODO: clean up.
+  {
+    Thread* this_thread = const_cast<Thread*>(this);
+    Closure* flip_func = this_thread->GetFlipFunction();
+    if (flip_func != nullptr) {
+      flip_func->Run(this_thread);
+    }
+  }
+
   // Dumping the Java stack involves the verifier for locks. The verifier operates under the
   // assumption that there is no exception pending on entry. Thus, stash any pending exception.
-  // TODO: Find a way to avoid const_cast.
-  StackHandleScope<3> scope(const_cast<Thread*>(this));
+  // Thread::Current() instead of this in case a thread is dumping the stack of another suspended
+  // thread.
+  StackHandleScope<3> scope(Thread::Current());
   Handle<mirror::Throwable> exc;
   Handle<mirror::Object> throw_location_this_object;
   Handle<mirror::ArtMethod> throw_location_method;
@@ -1096,7 +1149,7 @@
   wait_mutex_ = new Mutex("a thread wait mutex");
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
   tlsPtr_.debug_invoke_req = new DebugInvokeReq;
-  tlsPtr_.single_step_control = new SingleStepControl;
+  tlsPtr_.single_step_control = nullptr;
   tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
   tlsPtr_.name = new std::string(kThreadNameDuringStartup);
   tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
@@ -1111,6 +1164,8 @@
   for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
     tlsPtr_.checkpoint_functions[i] = nullptr;
   }
+  tlsPtr_.flip_function = nullptr;
+  tls32_.suspended_at_suspend_check = false;
 }
 
 bool Thread::IsStillStarting() const {
@@ -1147,8 +1202,7 @@
   }
 }
 
-static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/,
-                               RootType /*root_type*/)
+static void MonitorExitVisitor(mirror::Object** object, void* arg, const RootInfo& /*root_info*/)
     NO_THREAD_SAFETY_ANALYSIS {
   Thread* self = reinterpret_cast<Thread*>(arg);
   mirror::Object* entered_monitor = *object;
@@ -1167,7 +1221,7 @@
 
   if (tlsPtr_.jni_env != nullptr) {
     // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
-    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, RootInfo(kRootVMInternal));
     // Release locally held global references which releasing may require the mutator lock.
     if (tlsPtr_.jpeer != nullptr) {
       // If pthread_create fails we don't have a jni env here.
@@ -1210,7 +1264,10 @@
     tlsPtr_.opeer = nullptr;
   }
 
-  Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+  {
+    ScopedObjectAccess soa(self);
+    Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+  }
 }
 
 Thread::~Thread() {
@@ -1227,6 +1284,8 @@
   CHECK(tlsPtr_.checkpoint_functions[0] == nullptr);
   CHECK(tlsPtr_.checkpoint_functions[1] == nullptr);
   CHECK(tlsPtr_.checkpoint_functions[2] == nullptr);
+  CHECK(tlsPtr_.flip_function == nullptr);
+  CHECK_EQ(tls32_.suspended_at_suspend_check, false);
 
   // We may be deleting a still born thread.
   SetStateUnsafe(kTerminated);
@@ -1243,7 +1302,9 @@
   }
 
   delete tlsPtr_.debug_invoke_req;
-  delete tlsPtr_.single_step_control;
+  if (tlsPtr_.single_step_control != nullptr) {
+    delete tlsPtr_.single_step_control;
+  }
   delete tlsPtr_.instrumentation_stack;
   delete tlsPtr_.name;
   delete tlsPtr_.stack_trace_sample;
@@ -1326,7 +1387,7 @@
       mirror::Object* object = cur->GetReference(j);
       if (object != nullptr) {
         mirror::Object* old_obj = object;
-        visitor(&object, arg, thread_id, kRootNativeStack);
+        visitor(&object, arg, RootInfo(kRootNativeStack, thread_id));
         if (old_obj != object) {
           cur->SetReference(j, object);
         }
@@ -1336,7 +1397,6 @@
 }
 
 mirror::Object* Thread::DecodeJObject(jobject obj) const {
-  Locks::mutator_lock_->AssertSharedHeld(this);
   if (obj == nullptr) {
     return nullptr;
   }
@@ -1865,16 +1925,6 @@
   JNI_ENTRY_POINT_INFO(pDlsymLookup)
 #undef JNI_ENTRY_POINT_INFO
 
-#define PORTABLE_ENTRY_POINT_INFO(x) \
-    if (PORTABLE_ENTRYPOINT_OFFSET(ptr_size, x).Uint32Value() == offset) { \
-      os << #x; \
-      return; \
-    }
-  PORTABLE_ENTRY_POINT_INFO(pPortableImtConflictTrampoline)
-  PORTABLE_ENTRY_POINT_INFO(pPortableResolutionTrampoline)
-  PORTABLE_ENTRY_POINT_INFO(pPortableToInterpreterBridge)
-#undef PORTABLE_ENTRY_POINT_INFO
-
 #define QUICK_ENTRY_POINT_INFO(x) \
     if (QUICK_ENTRYPOINT_OFFSET(ptr_size, x).Uint32Value() == offset) { \
       os << #x; \
@@ -2142,6 +2192,7 @@
         uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
         StackMap map = m->GetStackMap(native_pc_offset);
         MemoryRegion mask = map.GetStackMask();
+        // Visit stack entries that hold pointers.
         for (size_t i = 0; i < mask.size_in_bits(); ++i) {
           if (mask.LoadBit(i)) {
             StackReference<mirror::Object>* ref_addr =
@@ -2156,6 +2207,16 @@
             }
           }
         }
+        // Visit callee-save registers that hold pointers.
+        uint32_t register_mask = map.GetRegisterMask();
+        for (size_t i = 0; i < BitSizeOf<uint32_t>(); ++i) {
+          if (register_mask & (1 << i)) {
+            mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(i));
+            if (*ref_addr != nullptr) {
+              visitor_(ref_addr, -1, this);
+            }
+          }
+        }
       } else {
         const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
         CHECK(native_gc_map != nullptr) << PrettyMethod(m);
@@ -2223,8 +2284,8 @@
   RootCallbackVisitor(RootCallback* callback, void* arg, uint32_t tid)
      : callback_(callback), arg_(arg), tid_(tid) {}
 
-  void operator()(mirror::Object** obj, size_t, const StackVisitor*) const {
-    callback_(obj, arg_, tid_, kRootJavaFrame);
+  void operator()(mirror::Object** obj, size_t vreg, const StackVisitor* stack_visitor) const {
+    callback_(obj, arg_, JavaFrameRootInfo(tid_, stack_visitor, vreg));
   }
 
  private:
@@ -2236,23 +2297,24 @@
 void Thread::VisitRoots(RootCallback* visitor, void* arg) {
   uint32_t thread_id = GetThreadId();
   if (tlsPtr_.opeer != nullptr) {
-    visitor(&tlsPtr_.opeer, arg, thread_id, kRootThreadObject);
+    visitor(&tlsPtr_.opeer, arg, RootInfo(kRootThreadObject, thread_id));
   }
   if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) {
-    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
+    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg,
+            RootInfo(kRootNativeStack, thread_id));
   }
   tlsPtr_.throw_location.VisitRoots(visitor, arg);
   if (tlsPtr_.monitor_enter_object != nullptr) {
-    visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
+    visitor(&tlsPtr_.monitor_enter_object, arg, RootInfo(kRootNativeStack, thread_id));
   }
-  tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, thread_id, kRootJNILocal);
-  tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, thread_id, kRootJNIMonitor);
+  tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, RootInfo(kRootJNILocal, thread_id));
+  tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, RootInfo(kRootJNIMonitor, thread_id));
   HandleScopeVisitRoots(visitor, arg, thread_id);
   if (tlsPtr_.debug_invoke_req != nullptr) {
-    tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+    tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
   }
   if (tlsPtr_.single_step_control != nullptr) {
-    tlsPtr_.single_step_control->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+    tlsPtr_.single_step_control->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
   }
   if (tlsPtr_.deoptimization_shadow_frame != nullptr) {
     RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
@@ -2263,8 +2325,8 @@
     }
   }
   if (tlsPtr_.shadow_frame_under_construction != nullptr) {
-    RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
-    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitorToCallback);
+    RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
     for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
         shadow_frame != nullptr;
         shadow_frame = shadow_frame->GetLink()) {
@@ -2273,21 +2335,22 @@
   }
   // Visit roots on this thread's stack
   Context* context = GetLongJumpContext();
-  RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
-  ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitorToCallback);
+  RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+  ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitor_to_callback);
   mapper.WalkStack();
   ReleaseLongJumpContext(context);
   for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
     if (frame.this_object_ != nullptr) {
-      visitor(&frame.this_object_, arg, thread_id, kRootJavaFrame);
+      visitor(&frame.this_object_, arg, RootInfo(kRootVMInternal, thread_id));
     }
     DCHECK(frame.method_ != nullptr);
-    visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg, thread_id, kRootJavaFrame);
+    visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg,
+            RootInfo(kRootVMInternal, thread_id));
   }
 }
 
-static void VerifyRoot(mirror::Object** root, void* /*arg*/, uint32_t /*thread_id*/,
-                       RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static void VerifyRoot(mirror::Object** root, void* /*arg*/, const RootInfo& /*root_info*/)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   VerifyObject(*root);
 }
 
@@ -2359,5 +2422,19 @@
   return mprotect(pregion, kStackOverflowProtectedSize, PROT_READ|PROT_WRITE) == 0;
 }
 
+void Thread::ActivateSingleStepControl(SingleStepControl* ssc) {
+  CHECK(Dbg::IsDebuggerActive());
+  CHECK(GetSingleStepControl() == nullptr) << "Single step already active in thread " << *this;
+  CHECK(ssc != nullptr);
+  tlsPtr_.single_step_control = ssc;
+}
+
+void Thread::DeactivateSingleStepControl() {
+  CHECK(Dbg::IsDebuggerActive());
+  CHECK(GetSingleStepControl() != nullptr) << "Single step not active in thread " << *this;
+  SingleStepControl* ssc = GetSingleStepControl();
+  tlsPtr_.single_step_control = nullptr;
+  delete ssc;
+}
 
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 5b3e746..26b7b6f 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -31,10 +31,10 @@
 #include "base/mutex.h"
 #include "entrypoints/interpreter/interpreter_entrypoints.h"
 #include "entrypoints/jni/jni_entrypoints.h"
-#include "entrypoints/portable/portable_entrypoints.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "globals.h"
 #include "handle_scope.h"
+#include "instrumentation.h"
 #include "jvalue.h"
 #include "object_callbacks.h"
 #include "offsets.h"
@@ -75,7 +75,7 @@
 class Runtime;
 class ScopedObjectAccessAlreadyRunnable;
 class ShadowFrame;
-struct SingleStepControl;
+class SingleStepControl;
 class Thread;
 class ThreadList;
 
@@ -216,6 +216,9 @@
   bool RequestCheckpoint(Closure* function)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_);
 
+  void SetFlipFunction(Closure* function);
+  Closure* GetFlipFunction();
+
   // Called when thread detected that the thread_suspend_count_ was non-zero. Gives up share of
   // mutator_lock_ and waits until it is resumed and thread_suspend_count_ is zero.
   void FullSuspendCheck()
@@ -549,12 +552,6 @@
   }
 
   template<size_t pointer_size>
-  static ThreadOffset<pointer_size> PortableEntryPointOffset(size_t port_entrypoint_offset) {
-    return ThreadOffsetFromTlsPtr<pointer_size>(
-        OFFSETOF_MEMBER(tls_ptr_sized_values, portable_entrypoints) + port_entrypoint_offset);
-  }
-
-  template<size_t pointer_size>
   static ThreadOffset<pointer_size> SelfOffset() {
     return ThreadOffsetFromTlsPtr<pointer_size>(OFFSETOF_MEMBER(tls_ptr_sized_values, self));
   }
@@ -712,6 +709,15 @@
     return tlsPtr_.single_step_control;
   }
 
+  // Activates single step control for debugging. The thread takes the
+  // ownership of the given SingleStepControl*. It is deleted by a call
+  // to DeactivateSingleStepControl or upon thread destruction.
+  void ActivateSingleStepControl(SingleStepControl* ssc);
+
+  // Deactivates single step control for debugging.
+  void DeactivateSingleStepControl();
+
+
   // Returns the fake exception used to activate deoptimization.
   static mirror::Throwable* GetDeoptimizationException() {
     return reinterpret_cast<mirror::Throwable*>(-1);
@@ -787,6 +793,12 @@
   mirror::Object* AllocTlab(size_t bytes);
   void SetTlab(uint8_t* start, uint8_t* end);
   bool HasTlab() const;
+  uint8_t* GetTlabStart() {
+    return tlsPtr_.thread_local_start;
+  }
+  uint8_t* GetTlabPos() {
+    return tlsPtr_.thread_local_pos;
+  }
 
   // Remove the suspend trigger for this thread by making the suspend_trigger_ TLS value
   // equal to a valid pointer.
@@ -804,10 +816,12 @@
 
 
   // Push an object onto the allocation stack.
-  bool PushOnThreadLocalAllocationStack(mirror::Object* obj);
+  bool PushOnThreadLocalAllocationStack(mirror::Object* obj)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Set the thread local allocation pointers to the given pointers.
-  void SetThreadLocalAllocationStack(mirror::Object** start, mirror::Object** end);
+  void SetThreadLocalAllocationStack(StackReference<mirror::Object>* start,
+                                     StackReference<mirror::Object>* end);
 
   // Resets the thread local allocation pointers.
   void RevokeThreadLocalAllocationStack();
@@ -854,6 +868,10 @@
     return tlsPtr_.nested_signal_state;
   }
 
+  bool IsSuspendedAtSuspendCheck() const {
+    return tls32_.suspended_at_suspend_check;
+  }
+
  private:
   explicit Thread(bool daemon);
   ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
@@ -959,7 +977,7 @@
       suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
       daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
       thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false),
-      handling_signal_(false), padding_(0) {
+      handling_signal_(false), suspended_at_suspend_check(false) {
     }
 
     union StateAndFlags state_and_flags;
@@ -1003,8 +1021,10 @@
     // True if signal is being handled by this thread.
     bool32_t handling_signal_;
 
-    // Padding to make the size aligned to 8.  Remove this if we add another 32 bit field.
-    int32_t padding_;
+    // True if the thread is suspended in FullSuspendCheck(). This is
+    // used to distinguish runnable threads that are suspended due to
+    // a normal suspend check from other threads.
+    bool32_t suspended_at_suspend_check;
   } tls32_;
 
   struct PACKED(8) tls_64bit_sized_values {
@@ -1031,7 +1051,7 @@
       pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
       thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
       thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
-      nested_signal_state(nullptr) {
+      nested_signal_state(nullptr), flip_function(nullptr) {
         for (size_t i = 0; i < kLockLevelCount; ++i) {
           held_mutexes[i] = nullptr;
         }
@@ -1128,7 +1148,6 @@
     // TODO: move this to more of a global offset table model to avoid per-thread duplication.
     InterpreterEntryPoints interpreter_entrypoints;
     JniEntryPoints jni_entrypoints;
-    PortableEntryPoints portable_entrypoints;
     QuickEntryPoints quick_entrypoints;
 
     // Thread-local allocation pointer.
@@ -1141,14 +1160,17 @@
     void* rosalloc_runs[kNumRosAllocThreadLocalSizeBrackets];
 
     // Thread-local allocation stack data/routines.
-    mirror::Object** thread_local_alloc_stack_top;
-    mirror::Object** thread_local_alloc_stack_end;
+    StackReference<mirror::Object>* thread_local_alloc_stack_top;
+    StackReference<mirror::Object>* thread_local_alloc_stack_end;
 
     // Support for Mutex lock hierarchy bug detection.
     BaseMutex* held_mutexes[kLockLevelCount];
 
     // Recorded thread state for nested signals.
     jmp_buf* nested_signal_state;
+
+    // The function used for thread flip.
+    Closure* flip_function;
   } tlsPtr_;
 
   // Guards the 'interrupted_' and 'wait_monitor_' members.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index beafcda..05a0bff 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -27,6 +27,7 @@
 
 #include <sstream>
 
+#include "base/histogram-inl.h"
 #include "base/mutex.h"
 #include "base/mutex-inl.h"
 #include "base/timing_logger.h"
@@ -43,10 +44,16 @@
 namespace art {
 
 static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5);
+static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000;  // 30s.
+// Use 0 since we want to yield to prevent blocking for an unpredictable amount of time.
+static constexpr useconds_t kThreadSuspendInitialSleepUs = 0;
+static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000;
+static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000;
 
 ThreadList::ThreadList()
     : suspend_all_count_(0), debug_suspend_all_count_(0),
-      thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_) {
+      thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_),
+      suspend_all_historam_("suspend all histogram", 16, 64) {
   CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1)));
 }
 
@@ -97,6 +104,15 @@
 }
 
 void ThreadList::DumpForSigQuit(std::ostream& os) {
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    // Only print if we have samples.
+    if (suspend_all_historam_.SampleSize() > 0) {
+      Histogram<uint64_t>::CumulativeData data;
+      suspend_all_historam_.CreateHistogram(&data);
+      suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data);  // Dump time to suspend.
+    }
+  }
   Dump(os);
   DumpUnattachedThreads(os);
 }
@@ -139,6 +155,10 @@
   closedir(d);
 }
 
+// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke
+// addr2line when available.
+static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000;
+
 // A closure used by Thread::Dump.
 class DumpCheckpoint FINAL : public Closure {
  public:
@@ -159,14 +179,15 @@
       MutexLock mu(self, *Locks::logging_lock_);
       *os_ << local_os.str();
     }
-    barrier_.Pass(self);
+    if (thread->GetState() == kRunnable) {
+      barrier_.Pass(self);
+    }
   }
 
   void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
     Thread* self = Thread::Current();
     ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
-    const uint32_t kWaitTimeoutMs = 10000;
-    bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kWaitTimeoutMs);
+    bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kDumpWaitTimeout);
     if (timed_out) {
       // Avoid a recursive abort.
       LOG((kIsDebugBuild && (gAborting == 0)) ? FATAL : ERROR)
@@ -188,7 +209,9 @@
   }
   DumpCheckpoint checkpoint(&os);
   size_t threads_running_checkpoint = RunCheckpoint(&checkpoint);
-  checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
+  if (threads_running_checkpoint != 0) {
+    checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
+  }
 }
 
 void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread* ignore2) {
@@ -219,22 +242,13 @@
 #endif
 
 // Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an
-// individual thread requires polling. delay_us is the requested sleep and total_delay_us
-// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield,
-// subsequently sleeps increase delay_us from 1ms to 500ms by doubling.
-static void ThreadSuspendSleep(useconds_t* delay_us, useconds_t* total_delay_us) {
-  useconds_t new_delay_us = (*delay_us) * 2;
-  CHECK_GE(new_delay_us, *delay_us);
-  if (new_delay_us < 500000) {  // Don't allow sleeping to be more than 0.5s.
-    *delay_us = new_delay_us;
-  }
-  if (*delay_us == 0) {
+// individual thread requires polling. delay_us is the requested sleep wait. If delay_us is 0 then
+// we use sched_yield instead of calling usleep.
+static void ThreadSuspendSleep(useconds_t delay_us) {
+  if (delay_us == 0) {
     sched_yield();
-    // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep).
-    *delay_us = 500;
   } else {
-    usleep(*delay_us);
-    *total_delay_us += *delay_us;
+    usleep(delay_us);
   }
 }
 
@@ -283,16 +297,23 @@
   // Run the checkpoint on the suspended threads.
   for (const auto& thread : suspended_count_modified_threads) {
     if (!thread->IsSuspended()) {
-      // Wait until the thread is suspended.
-      useconds_t total_delay_us = 0;
+      if (ATRACE_ENABLED()) {
+        std::ostringstream oss;
+        thread->ShortDump(oss);
+        ATRACE_BEGIN((std::string("Waiting for suspension of thread ") + oss.str()).c_str());
+      }
+      // Busy wait until the thread is suspended.
+      const uint64_t start_time = NanoTime();
       do {
-        useconds_t delay_us = 100;
-        ThreadSuspendSleep(&delay_us, &total_delay_us);
+        ThreadSuspendSleep(kThreadSuspendInitialSleepUs);
       } while (!thread->IsSuspended());
+      const uint64_t total_delay = NanoTime() - start_time;
       // Shouldn't need to wait for longer than 1000 microseconds.
-      constexpr useconds_t kLongWaitThresholdUS = 1000;
-      if (UNLIKELY(total_delay_us > kLongWaitThresholdUS)) {
-        LOG(WARNING) << "Waited " << total_delay_us << " us for thread suspend!";
+      constexpr uint64_t kLongWaitThreshold = MsToNs(1);
+      ATRACE_END();
+      if (UNLIKELY(total_delay > kLongWaitThreshold)) {
+        LOG(WARNING) << "Long wait of " << PrettyDuration(total_delay) << " for "
+            << *thread << " suspension!";
       }
     }
     // We know for sure that the thread is suspended at this point.
@@ -310,8 +331,7 @@
     Thread::resume_cond_->Broadcast(self);
   }
 
-  // Add one for self.
-  return count + suspended_count_modified_threads.size() + 1;
+  return count;
 }
 
 // Request that a checkpoint function be run on all active (non-suspended)
@@ -342,6 +362,95 @@
   return count;
 }
 
+// A checkpoint/suspend-all hybrid to switch thread roots from
+// from-space to to-space refs. Used to synchronize threads at a point
+// to mark the initiation of marking while maintaining the to-space
+// invariant.
+size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+                                   gc::collector::GarbageCollector* collector) {
+  TimingLogger::ScopedTiming split("ThreadListFlip", collector->GetTimings());
+  const uint64_t start_time = NanoTime();
+  Thread* self = Thread::Current();
+  Locks::mutator_lock_->AssertNotHeld(self);
+  Locks::thread_list_lock_->AssertNotHeld(self);
+  Locks::thread_suspend_count_lock_->AssertNotHeld(self);
+  CHECK_NE(self->GetState(), kRunnable);
+
+  std::vector<Thread*> runnable_threads;
+  std::vector<Thread*> other_threads;
+
+  // Suspend all threads once.
+  {
+    MutexLock mu(self, *Locks::thread_list_lock_);
+    MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+    // Update global suspend all state for attaching threads.
+    ++suspend_all_count_;
+    // Increment everybody's suspend count (except our own).
+    for (const auto& thread : list_) {
+      if (thread == self) {
+        continue;
+      }
+      thread->ModifySuspendCount(self, +1, false);
+    }
+  }
+
+  // Run the flip callback for the collector.
+  Locks::mutator_lock_->ExclusiveLock(self);
+  flip_callback->Run(self);
+  Locks::mutator_lock_->ExclusiveUnlock(self);
+  collector->RegisterPause(NanoTime() - start_time);
+
+  // Resume runnable threads.
+  {
+    MutexLock mu(self, *Locks::thread_list_lock_);
+    MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+    --suspend_all_count_;
+    for (const auto& thread : list_) {
+      if (thread == self) {
+        continue;
+      }
+      // Set the flip function for both runnable and suspended threads
+      // because Thread::DumpState/DumpJavaStack() (invoked by a
+      // checkpoint) may cause the flip function to be run for a
+      // runnable/suspended thread before a runnable threads runs it
+      // for itself or we run it for a suspended thread below.
+      thread->SetFlipFunction(thread_flip_visitor);
+      if (thread->IsSuspendedAtSuspendCheck()) {
+        // The thread will resume right after the broadcast.
+        thread->ModifySuspendCount(self, -1, false);
+        runnable_threads.push_back(thread);
+      } else {
+        other_threads.push_back(thread);
+      }
+    }
+    Thread::resume_cond_->Broadcast(self);
+  }
+
+  // Run the closure on the other threads and let them resume.
+  {
+    ReaderMutexLock mu(self, *Locks::mutator_lock_);
+    for (const auto& thread : other_threads) {
+      Closure* flip_func = thread->GetFlipFunction();
+      if (flip_func != nullptr) {
+        flip_func->Run(thread);
+      }
+    }
+    // Run it for self.
+    thread_flip_visitor->Run(self);
+  }
+
+  // Resume other threads.
+  {
+    MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+    for (const auto& thread : other_threads) {
+      thread->ModifySuspendCount(self, -1, false);
+    }
+    Thread::resume_cond_->Broadcast(self);
+  }
+
+  return runnable_threads.size() + other_threads.size() + 1;  // +1 for self.
+}
+
 void ThreadList::SuspendAll() {
   Thread* self = Thread::Current();
 
@@ -351,7 +460,7 @@
     VLOG(threads) << "Thread[null] SuspendAll starting...";
   }
   ATRACE_BEGIN("Suspending mutator threads");
-  uint64_t start_time = NanoTime();
+  const uint64_t start_time = NanoTime();
 
   Locks::mutator_lock_->AssertNotHeld(self);
   Locks::thread_list_lock_->AssertNotHeld(self);
@@ -377,16 +486,18 @@
   // Block on the mutator lock until all Runnable threads release their share of access.
 #if HAVE_TIMED_RWLOCK
   // Timeout if we wait more than 30 seconds.
-  if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, 30 * 1000, 0)) {
+  if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) {
     UnsafeLogFatalForThreadSuspendAllTimeout();
   }
 #else
   Locks::mutator_lock_->ExclusiveLock(self);
 #endif
 
-  uint64_t end_time = NanoTime();
-  if (end_time - start_time > kLongThreadSuspendThreshold) {
-    LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(end_time - start_time);
+  const uint64_t end_time = NanoTime();
+  const uint64_t suspend_time = end_time - start_time;
+  suspend_all_historam_.AdjustAndAddValue(suspend_time);
+  if (suspend_time > kLongThreadSuspendThreshold) {
+    LOG(WARNING) << "Suspending all threads took: " << PrettyDuration(suspend_time);
   }
 
   if (kDebugLocking) {
@@ -454,6 +565,9 @@
 }
 
 void ThreadList::Resume(Thread* thread, bool for_debugger) {
+  // This assumes there was an ATRACE_BEGIN when we suspended the thread.
+  ATRACE_END();
+
   Thread* self = Thread::Current();
   DCHECK_NE(thread, self);
   VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") starting..."
@@ -501,12 +615,11 @@
 
 Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
                                         bool debug_suspension, bool* timed_out) {
-  static const useconds_t kTimeoutUs = 30 * 1000000;  // 30s.
-  useconds_t total_delay_us = 0;
-  useconds_t delay_us = 0;
-  bool did_suspend_request = false;
+  const uint64_t start_time = NanoTime();
+  useconds_t sleep_us = kThreadSuspendInitialSleepUs;
   *timed_out = false;
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
+  Thread* suspended_thread = nullptr;
   VLOG(threads) << "SuspendThreadByPeer starting";
   while (true) {
     Thread* thread;
@@ -520,10 +633,18 @@
       MutexLock thread_list_mu(self, *Locks::thread_list_lock_);
       thread = Thread::FromManagedThread(soa, peer);
       if (thread == nullptr) {
+        if (suspended_thread != nullptr) {
+          MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
+          // If we incremented the suspend count but the thread reset its peer, we need to
+          // re-decrement it since it is shutting down and may deadlock the runtime in
+          // ThreadList::WaitForOtherNonDaemonThreadsToExit.
+          suspended_thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+        }
         ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer);
         return nullptr;
       }
       if (!Contains(thread)) {
+        CHECK(suspended_thread == nullptr);
         VLOG(threads) << "SuspendThreadByPeer failed for unattached thread: "
             << reinterpret_cast<void*>(thread);
         return nullptr;
@@ -538,9 +659,10 @@
             // which will allow this thread to be suspended.
             continue;
           }
-          thread->ModifySuspendCount(self, +1, debug_suspension);
+          CHECK(suspended_thread == nullptr);
+          suspended_thread = thread;
+          suspended_thread->ModifySuspendCount(self, +1, debug_suspension);
           request_suspension = false;
-          did_suspend_request = true;
         } else {
           // If the caller isn't requesting suspension, a suspension should have already occurred.
           CHECK_GT(thread->GetSuspendCount(), 0);
@@ -555,21 +677,37 @@
         // done.
         if (thread->IsSuspended()) {
           VLOG(threads) << "SuspendThreadByPeer thread suspended: " << *thread;
+          if (ATRACE_ENABLED()) {
+            std::string name;
+            thread->GetThreadName(name);
+            ATRACE_BEGIN(StringPrintf("SuspendThreadByPeer suspended %s for peer=%p", name.c_str(),
+                                      peer).c_str());
+          }
           return thread;
         }
-        if (total_delay_us >= kTimeoutUs) {
+        const uint64_t total_delay = NanoTime() - start_time;
+        if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
           ThreadSuspendByPeerWarning(self, FATAL, "Thread suspension timed out", peer);
-          if (did_suspend_request) {
-            thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+          if (suspended_thread != nullptr) {
+            CHECK_EQ(suspended_thread, thread);
+            suspended_thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
           }
           *timed_out = true;
           return nullptr;
+        } else if (sleep_us == 0 &&
+            total_delay > static_cast<uint64_t>(kThreadSuspendMaxYieldUs) * 1000) {
+          // We have spun for kThreadSuspendMaxYieldUs time, switch to sleeps to prevent
+          // excessive CPU usage.
+          sleep_us = kThreadSuspendMaxYieldUs / 2;
         }
       }
       // Release locks and come out of runnable state.
     }
-    VLOG(threads) << "SuspendThreadByPeer sleeping to allow thread chance to suspend";
-    ThreadSuspendSleep(&delay_us, &total_delay_us);
+    VLOG(threads) << "SuspendThreadByPeer waiting to allow thread chance to suspend";
+    ThreadSuspendSleep(sleep_us);
+    // This may stay at 0 if sleep_us == 0, but this is WAI since we want to avoid using usleep at
+    // all if possible. This shouldn't be an issue since time to suspend should always be small.
+    sleep_us = std::min(sleep_us * 2, kThreadSuspendMaxSleepUs);
   }
 }
 
@@ -580,12 +718,11 @@
 
 Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension,
                                             bool* timed_out) {
-  static const useconds_t kTimeoutUs = 30 * 1000000;  // 30s.
-  useconds_t total_delay_us = 0;
-  useconds_t delay_us = 0;
+  const uint64_t start_time = NanoTime();
+  useconds_t sleep_us = kThreadSuspendInitialSleepUs;
   *timed_out = false;
   Thread* suspended_thread = nullptr;
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
   CHECK_NE(thread_id, kInvalidThreadId);
   VLOG(threads) << "SuspendThreadByThreadId starting";
   while (true) {
@@ -638,22 +775,35 @@
         // count, or else we've waited and it has self suspended) or is the current thread, we're
         // done.
         if (thread->IsSuspended()) {
+          if (ATRACE_ENABLED()) {
+            std::string name;
+            thread->GetThreadName(name);
+            ATRACE_BEGIN(StringPrintf("SuspendThreadByThreadId suspended %s id=%d",
+                                      name.c_str(), thread_id).c_str());
+          }
           VLOG(threads) << "SuspendThreadByThreadId thread suspended: " << *thread;
           return thread;
         }
-        if (total_delay_us >= kTimeoutUs) {
+        const uint64_t total_delay = NanoTime() - start_time;
+        if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
           ThreadSuspendByThreadIdWarning(WARNING, "Thread suspension timed out", thread_id);
           if (suspended_thread != nullptr) {
             thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
           }
           *timed_out = true;
           return nullptr;
+        } else if (sleep_us == 0 &&
+            total_delay > static_cast<uint64_t>(kThreadSuspendMaxYieldUs) * 1000) {
+          // We have spun for kThreadSuspendMaxYieldUs time, switch to sleeps to prevent
+          // excessive CPU usage.
+          sleep_us = kThreadSuspendMaxYieldUs / 2;
         }
       }
       // Release locks and come out of runnable state.
     }
-    VLOG(threads) << "SuspendThreadByThreadId sleeping to allow thread chance to suspend";
-    ThreadSuspendSleep(&delay_us, &total_delay_us);
+    VLOG(threads) << "SuspendThreadByThreadId waiting to allow thread chance to suspend";
+    ThreadSuspendSleep(sleep_us);
+    sleep_us = std::min(sleep_us * 2, kThreadSuspendMaxSleepUs);
   }
 }
 
@@ -775,7 +925,6 @@
 void ThreadList::ResumeAllForDebugger() {
   Thread* self = Thread::Current();
   Thread* debug_thread = Dbg::GetDebugThread();
-  bool needs_resume = false;
 
   VLOG(threads) << *self << " ResumeAllForDebugger starting...";
 
@@ -788,32 +937,34 @@
       MutexLock suspend_count_mu(self, *Locks::thread_suspend_count_lock_);
       // Update global suspend all state for attaching threads.
       DCHECK_GE(suspend_all_count_, debug_suspend_all_count_);
-      needs_resume = (debug_suspend_all_count_ > 0);
-      if (needs_resume) {
+      if (debug_suspend_all_count_ > 0) {
         --suspend_all_count_;
         --debug_suspend_all_count_;
-        // Decrement everybody's suspend count (except our own).
-        for (const auto& thread : list_) {
-          if (thread == self || thread == debug_thread) {
-            continue;
-          }
-          if (thread->GetDebugSuspendCount() == 0) {
-            // This thread may have been individually resumed with ThreadReference.Resume.
-            continue;
-          }
-          VLOG(threads) << "requesting thread resume: " << *thread;
-          thread->ModifySuspendCount(self, -1, true);
-        }
       } else {
         // We've been asked to resume all threads without being asked to
-        // suspend them all before. Let's print a warning.
+        // suspend them all before. That may happen if a debugger tries
+        // to resume some suspended threads (with suspend count == 1)
+        // at once with a VirtualMachine.Resume command. Let's print a
+        // warning.
         LOG(WARNING) << "Debugger attempted to resume all threads without "
                      << "having suspended them all before.";
       }
+      // Decrement everybody's suspend count (except our own).
+      for (const auto& thread : list_) {
+        if (thread == self || thread == debug_thread) {
+          continue;
+        }
+        if (thread->GetDebugSuspendCount() == 0) {
+          // This thread may have been individually resumed with ThreadReference.Resume.
+          continue;
+        }
+        VLOG(threads) << "requesting thread resume: " << *thread;
+        thread->ModifySuspendCount(self, -1, true);
+      }
     }
   }
 
-  if (needs_resume) {
+  {
     MutexLock mu(self, *Locks::thread_suspend_count_lock_);
     Thread::resume_cond_->Broadcast(self);
   }
@@ -1003,28 +1154,6 @@
   }
 }
 
-class VerifyRootWrapperArg {
- public:
-  VerifyRootWrapperArg(VerifyRootCallback* callback, void* arg) : callback_(callback), arg_(arg) {
-  }
-  VerifyRootCallback* const callback_;
-  void* const arg_;
-};
-
-static void VerifyRootWrapperCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                      RootType root_type) {
-  VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
-  wrapperArg->callback_(*root, wrapperArg->arg_, 0, NULL, root_type);
-}
-
-void ThreadList::VerifyRoots(VerifyRootCallback* callback, void* arg) const {
-  VerifyRootWrapperArg wrapper(callback, arg);
-  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-  for (const auto& thread : list_) {
-    thread->VisitRoots(VerifyRootWrapperCallback, &wrapper);
-  }
-}
-
 uint32_t ThreadList::AllocThreadId(Thread* self) {
   MutexLock mu(self, *Locks::allocated_thread_ids_lock_);
   for (size_t i = 0; i < allocated_ids_.size(); ++i) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 13684c7..d18315a 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -17,7 +17,9 @@
 #ifndef ART_RUNTIME_THREAD_LIST_H_
 #define ART_RUNTIME_THREAD_LIST_H_
 
+#include "base/histogram.h"
 #include "base/mutex.h"
+#include "gc_root.h"
 #include "jni.h"
 #include "object_callbacks.h"
 
@@ -25,6 +27,11 @@
 #include <list>
 
 namespace art {
+namespace gc {
+  namespace collector {
+    class GarbageCollector;
+  }  // namespac collector
+}  // namespace gc
 class Closure;
 class Thread;
 class TimingLogger;
@@ -39,11 +46,10 @@
   ~ThreadList();
 
   void DumpForSigQuit(std::ostream& os)
-      LOCKS_EXCLUDED(Locks::thread_list_lock_);
+      LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::mutator_lock_);
   // For thread suspend timeout dumps.
   void Dump(std::ostream& os)
-      LOCKS_EXCLUDED(Locks::thread_list_lock_,
-                     Locks::thread_suspend_count_lock_);
+      LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_);
   pid_t GetLockOwner();  // For SignalCatcher.
 
   // Thread suspension support.
@@ -94,6 +100,14 @@
   LOCKS_EXCLUDED(Locks::thread_list_lock_,
                  Locks::thread_suspend_count_lock_);
 
+  // Flip thread roots from from-space refs to to-space refs. Used by
+  // the concurrent copying collector.
+  size_t FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+                         gc::collector::GarbageCollector* collector)
+      LOCKS_EXCLUDED(Locks::mutator_lock_,
+                     Locks::thread_list_lock_,
+                     Locks::thread_suspend_count_lock_);
+
   // Suspends all threads
   void SuspendAllForDebugger()
       LOCKS_EXCLUDED(Locks::mutator_lock_,
@@ -125,9 +139,6 @@
   void VisitRoots(RootCallback* callback, void* arg) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VerifyRoots(VerifyRootCallback* callback, void* arg) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Return a copy of the thread list.
   std::list<Thread*> GetList() EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_) {
     return list_;
@@ -169,6 +180,10 @@
   // Signaled when threads terminate. Used to determine when all non-daemons have terminated.
   ConditionVariable thread_exit_cond_ GUARDED_BY(Locks::thread_list_lock_);
 
+  // Thread suspend time histogram. Only modified when all the threads are suspended, so guarding
+  // by mutator lock ensures no thread can read when another thread is modifying it.
+  Histogram<uint64_t> suspend_all_historam_ GUARDED_BY(Locks::mutator_lock_);
+
   friend class Thread;
 
   DISALLOW_COPY_AND_ASSIGN(ThreadList);
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index d6330c8..79b57af 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -22,19 +22,32 @@
 
 #include "barrier.h"
 #include "base/mutex.h"
-#include "closure.h"
 #include "mem_map.h"
 
 namespace art {
 
 class ThreadPool;
 
+class Closure {
+ public:
+  virtual ~Closure() { }
+  virtual void Run(Thread* self) = 0;
+};
+
 class Task : public Closure {
  public:
-  // Called when references reaches 0.
+  // Called after Closure::Run has been called.
   virtual void Finalize() { }
 };
 
+class SelfDeletingTask : public Task {
+ public:
+  virtual ~SelfDeletingTask() { }
+  virtual void Finalize() {
+    delete this;
+  }
+};
+
 class ThreadPoolWorker {
  public:
   static const size_t kDefaultStackSize = 1 * MB;
diff --git a/runtime/thread_state.h b/runtime/thread_state.h
index 6e5deeb..b5479ed 100644
--- a/runtime/thread_state.h
+++ b/runtime/thread_state.h
@@ -41,6 +41,7 @@
   kWaitingInMainSignalCatcherLoop,  // WAITING        TS_WAIT      blocking/reading/processing signals
   kWaitingForDeoptimization,        // WAITING        TS_WAIT      waiting for deoptimization suspend all
   kWaitingForMethodTracingStart,    // WAITING        TS_WAIT      waiting for method tracing to start
+  kWaitingForVisitObjects,          // WAITING        TS_WAIT      waiting for visiting objects
   kStarting,                        // NEW            TS_WAIT      native thread started, not yet ready to run managed code
   kNative,                          // RUNNABLE       TS_RUNNING   running in a JNI native method
   kSuspended,                       // RUNNABLE       TS_RUNNING   suspended by GC or debugger
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
index 04abe64..4d2aec0 100644
--- a/runtime/throw_location.cc
+++ b/runtime/throw_location.cc
@@ -34,11 +34,11 @@
 
 void ThrowLocation::VisitRoots(RootCallback* visitor, void* arg) {
   if (this_object_ != nullptr) {
-    visitor(&this_object_, arg, 0, kRootVMInternal);
+    visitor(&this_object_, arg, RootInfo(kRootVMInternal));
     DCHECK(this_object_ != nullptr);
   }
   if (method_ != nullptr) {
-    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, 0, kRootVMInternal);
+    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, RootInfo(kRootVMInternal));
     DCHECK(method_ != nullptr);
   }
 }
diff --git a/runtime/throw_location.h b/runtime/throw_location.h
index b36eb67..bec0da4 100644
--- a/runtime/throw_location.h
+++ b/runtime/throw_location.h
@@ -20,6 +20,7 @@
 #include "object_callbacks.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "gc_root.h"
 
 #include <stdint.h>
 #include <string>
diff --git a/runtime/trace.cc b/runtime/trace.cc
index b510844..0950abeb 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -38,9 +38,7 @@
 #include "ScopedLocalRef.h"
 #include "thread.h"
 #include "thread_list.h"
-#if !defined(ART_USE_PORTABLE_COMPILER)
 #include "entrypoints/quick/quick_entrypoints.h"
-#endif
 
 namespace art {
 
@@ -152,33 +150,33 @@
 }
 
 void Trace::SetDefaultClockSource(TraceClockSource clock_source) {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
   default_clock_source_ = clock_source;
 #else
-  if (clock_source != kTraceClockSourceWall) {
+  if (clock_source != TraceClockSource::kWall) {
     LOG(WARNING) << "Ignoring tracing request to use CPU time.";
   }
 #endif
 }
 
 static uint16_t GetTraceVersion(TraceClockSource clock_source) {
-  return (clock_source == kTraceClockSourceDual) ? kTraceVersionDualClock
+  return (clock_source == TraceClockSource::kDual) ? kTraceVersionDualClock
                                                     : kTraceVersionSingleClock;
 }
 
 static uint16_t GetRecordSize(TraceClockSource clock_source) {
-  return (clock_source == kTraceClockSourceDual) ? kTraceRecordSizeDualClock
+  return (clock_source == TraceClockSource::kDual) ? kTraceRecordSizeDualClock
                                                     : kTraceRecordSizeSingleClock;
 }
 
 bool Trace::UseThreadCpuClock() {
-  return (clock_source_ == kTraceClockSourceThreadCpu) ||
-      (clock_source_ == kTraceClockSourceDual);
+  return (clock_source_ == TraceClockSource::kThreadCpu) ||
+      (clock_source_ == TraceClockSource::kDual);
 }
 
 bool Trace::UseWallClock() {
-  return (clock_source_ == kTraceClockSourceWall) ||
-      (clock_source_ == kTraceClockSourceDual);
+  return (clock_source_ == TraceClockSource::kWall) ||
+      (clock_source_ == TraceClockSource::kDual);
 }
 
 void Trace::MeasureClockOverhead() {
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 478066f..7e2e0a6 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -30,7 +30,8 @@
 // TODO: remove (only used for debugging purpose).
 static constexpr bool kEnableTransactionStats = false;
 
-Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) {
+Transaction::Transaction()
+  : log_lock_("transaction log lock", kTransactionLogLock), aborted_(false) {
   CHECK(Runtime::Current()->IsCompiler());
 }
 
@@ -57,6 +58,35 @@
   }
 }
 
+void Transaction::Abort(const std::string& abort_message) {
+  MutexLock mu(Thread::Current(), log_lock_);
+  // We may abort more than once if the java.lang.InternalError thrown at the
+  // time of the abort has been caught during execution of a class initializer.
+  // We just keep the message of the first abort because it will cause the
+  // transaction to be rolled back anyway.
+  if (!aborted_) {
+    aborted_ = true;
+    abort_message_ = abort_message;
+  }
+}
+
+void Transaction::ThrowInternalError(Thread* self) {
+  DCHECK(IsAborted());
+  std::string abort_msg(GetAbortMessage());
+  self->ThrowNewException(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
+                          abort_msg.c_str());
+}
+
+bool Transaction::IsAborted() {
+  MutexLock mu(Thread::Current(), log_lock_);
+  return aborted_;
+}
+
+const std::string& Transaction::GetAbortMessage() {
+  MutexLock mu(Thread::Current(), log_lock_);
+  return abort_message_;
+}
+
 void Transaction::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
                                           uint8_t value, bool is_volatile) {
   DCHECK(obj != nullptr);
@@ -150,7 +180,7 @@
   intern_string_logs_.push_front(log);
 }
 
-void Transaction::Abort() {
+void Transaction::Rollback() {
   CHECK(!Runtime::Current()->IsActiveTransaction());
   Thread* self = Thread::Current();
   self->AssertNoPendingException();
@@ -206,7 +236,7 @@
     it.second.VisitRoots(callback, arg);
     mirror::Object* old_root = it.first;
     mirror::Object* new_root = old_root;
-    callback(&new_root, arg, 0, kRootUnknown);
+    callback(&new_root, arg, RootInfo(kRootUnknown));
     if (new_root != old_root) {
       moving_roots.push_back(std::make_pair(old_root, new_root));
     }
@@ -233,7 +263,7 @@
     mirror::Array* old_root = it.first;
     CHECK(!old_root->IsObjectArray());
     mirror::Array* new_root = old_root;
-    callback(reinterpret_cast<mirror::Object**>(&new_root), arg, 0, kRootUnknown);
+    callback(reinterpret_cast<mirror::Object**>(&new_root), arg, RootInfo(kRootUnknown));
     if (new_root != old_root) {
       moving_roots.push_back(std::make_pair(old_root, new_root));
     }
@@ -396,7 +426,7 @@
       mirror::Object* obj =
           reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
       if (obj != nullptr) {
-        callback(&obj, arg, 0, kRootUnknown);
+        callback(&obj, arg, RootInfo(kRootUnknown));
         field_value.value = reinterpret_cast<uintptr_t>(obj);
       }
     }
@@ -441,7 +471,7 @@
 }
 
 void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
-  callback(reinterpret_cast<mirror::Object**>(&str_), arg, 0, kRootInternedString);
+  callback(reinterpret_cast<mirror::Object**>(&str_), arg, RootInfo(kRootInternedString));
 }
 
 void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 566f231..be614f9 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -20,6 +20,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/value_object.h"
+#include "gc_root.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "primitive.h"
@@ -41,6 +42,14 @@
   Transaction();
   ~Transaction();
 
+  void Abort(const std::string& abort_message)
+      LOCKS_EXCLUDED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void ThrowInternalError(Thread* self)
+      LOCKS_EXCLUDED(log_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsAborted() LOCKS_EXCLUDED(log_lock_);
+
   // Record object field changes.
   void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
                                bool is_volatile)
@@ -84,7 +93,7 @@
       LOCKS_EXCLUDED(log_lock_);
 
   // Abort transaction by undoing all recorded changes.
-  void Abort()
+  void Rollback()
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       LOCKS_EXCLUDED(log_lock_);
 
@@ -205,10 +214,14 @@
       EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  const std::string& GetAbortMessage() LOCKS_EXCLUDED(log_lock_);
+
   Mutex log_lock_ ACQUIRED_AFTER(Locks::intern_table_lock_);
   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_);
+  bool aborted_ GUARDED_BY(log_lock_);
+  std::string abort_message_ GUARDED_BY(log_lock_);
 
   DISALLOW_COPY_AND_ASSIGN(Transaction);
 };
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 8c4b90d..b80fe22 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -24,8 +24,68 @@
 
 namespace art {
 
-class TransactionTest : public CommonRuntimeTest {};
+class TransactionTest : public CommonRuntimeTest {
+ public:
+  // Tests failing class initialization due to native call with transaction rollback.
+  void testTransactionAbort(const char* tested_class_signature) {
+    ScopedObjectAccess soa(Thread::Current());
+    jobject jclass_loader = LoadDex("Transaction");
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::ClassLoader> class_loader(
+        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+    ASSERT_TRUE(class_loader.Get() != nullptr);
 
+    // Load and initialize java.lang.ExceptionInInitializerError and java.lang.InternalError
+    // classes so they can be thrown during class initialization if the transaction aborts.
+    MutableHandle<mirror::Class> h_klass(
+        hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
+                                                    "Ljava/lang/ExceptionInInitializerError;")));
+    ASSERT_TRUE(h_klass.Get() != nullptr);
+    class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+    ASSERT_TRUE(h_klass->IsInitialized());
+
+    h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
+    ASSERT_TRUE(h_klass.Get() != nullptr);
+    class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+    ASSERT_TRUE(h_klass->IsInitialized());
+
+    // Load and verify utility class.
+    h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$AbortHelperClass;",
+                                            class_loader));
+    ASSERT_TRUE(h_klass.Get() != nullptr);
+    class_linker_->VerifyClass(soa.Self(), h_klass);
+    ASSERT_TRUE(h_klass->IsVerified());
+
+    // Load and verify tested class.
+    h_klass.Assign(class_linker_->FindClass(soa.Self(), tested_class_signature, class_loader));
+    ASSERT_TRUE(h_klass.Get() != nullptr);
+    class_linker_->VerifyClass(soa.Self(), h_klass);
+    ASSERT_TRUE(h_klass->IsVerified());
+
+    mirror::Class::Status old_status = h_klass->GetStatus();
+    uint32_t old_lock_word = h_klass->GetLockWord(false).GetValue();
+
+    Transaction transaction;
+    Runtime::Current()->EnterTransactionMode(&transaction);
+    bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+    Runtime::Current()->ExitTransactionMode();
+    ASSERT_FALSE(success);
+    ASSERT_TRUE(h_klass->IsErroneous());
+    ASSERT_TRUE(soa.Self()->IsExceptionPending());
+    ASSERT_TRUE(transaction.IsAborted());
+
+    // Check class's monitor get back to its original state without rolling back changes.
+    uint32_t new_lock_word = h_klass->GetLockWord(false).GetValue();
+    EXPECT_EQ(old_lock_word, new_lock_word);
+
+    // Check class status is rolled back properly.
+    soa.Self()->ClearException();
+    transaction.Rollback();
+    ASSERT_EQ(old_status, h_klass->GetStatus());
+  }
+};
+
+// Tests object's class is preserved after transaction rollback.
 TEST_F(TransactionTest, Object_class) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
@@ -40,11 +100,12 @@
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
   Runtime::Current()->ExitTransactionMode();
 
-  // Aborting transaction must not clear the Object::class field.
-  transaction.Abort();
+  // Rolling back transaction's changes must not clear the Object::class field.
+  transaction.Rollback();
   EXPECT_EQ(h_obj->GetClass(), h_klass.Get());
 }
 
+// Tests object's monitor state is preserved after transaction rollback.
 TEST_F(TransactionTest, Object_monitor) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
@@ -66,13 +127,14 @@
   uint32_t new_lock_word = h_obj->GetLockWord(false).GetValue();
   Runtime::Current()->ExitTransactionMode();
 
-  // Aborting transaction must not clear the Object::class field.
-  transaction.Abort();
+  // Rolling back transaction's changes must not change monitor's state.
+  transaction.Rollback();
   uint32_t aborted_lock_word = h_obj->GetLockWord(false).GetValue();
   EXPECT_NE(old_lock_word, new_lock_word);
   EXPECT_EQ(aborted_lock_word, new_lock_word);
 }
 
+// Tests array's length is preserved after transaction rollback.
 TEST_F(TransactionTest, Array_length) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
@@ -95,11 +157,12 @@
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
   Runtime::Current()->ExitTransactionMode();
 
-  // Aborting transaction must not clear the Object::class field.
-  transaction.Abort();
+  // Rolling back transaction's changes must not reset array's length.
+  transaction.Rollback();
   EXPECT_EQ(h_obj->GetLength(), kArraySize);
 }
 
+// Tests static fields are reset to their default value after transaction rollback.
 TEST_F(TransactionTest, StaticFieldsTest) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<4> hs(soa.Self());
@@ -110,8 +173,10 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  ASSERT_TRUE(success);
   ASSERT_TRUE(h_klass->IsInitialized());
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
 
   // Lookup fields.
   mirror::ArtField* booleanField = h_klass->FindDeclaredStaticField("booleanField", "Z");
@@ -155,7 +220,7 @@
   ASSERT_DOUBLE_EQ(doubleField->GetDouble(h_klass.Get()), static_cast<double>(0.0));
 
   mirror::ArtField* objectField = h_klass->FindDeclaredStaticField("objectField",
-                                                                      "Ljava/lang/Object;");
+                                                                   "Ljava/lang/Object;");
   ASSERT_TRUE(objectField != nullptr);
   ASSERT_EQ(objectField->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
   ASSERT_EQ(objectField->GetObject(h_klass.Get()), nullptr);
@@ -168,7 +233,7 @@
   ASSERT_TRUE(h_obj.Get() != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
-  // Modify fields inside transaction and abort it.
+  // Modify fields inside transaction then rollback changes.
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
   booleanField->SetBoolean<true>(h_klass.Get(), true);
@@ -181,7 +246,7 @@
   doubleField->SetDouble<true>(h_klass.Get(), 1.0);
   objectField->SetObject<true>(h_klass.Get(), h_obj.Get());
   Runtime::Current()->ExitTransactionMode();
-  transaction.Abort();
+  transaction.Rollback();
 
   // Check values have properly been restored to their original (default) value.
   EXPECT_EQ(booleanField->GetBoolean(h_klass.Get()), false);
@@ -195,6 +260,7 @@
   EXPECT_EQ(objectField->GetObject(h_klass.Get()), nullptr);
 }
 
+// Tests instance fields are reset to their default value after transaction rollback.
 TEST_F(TransactionTest, InstanceFieldsTest) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<5> hs(soa.Self());
@@ -205,8 +271,10 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  ASSERT_TRUE(success);
   ASSERT_TRUE(h_klass->IsInitialized());
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
 
   // Allocate an InstanceFieldTest object.
   Handle<mirror::Object> h_instance(hs.NewHandle(h_klass->AllocObject(soa.Self())));
@@ -267,7 +335,7 @@
   ASSERT_TRUE(h_obj.Get() != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
-  // Modify fields inside transaction and abort it.
+  // Modify fields inside transaction then rollback changes.
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
   booleanField->SetBoolean<true>(h_instance.Get(), true);
@@ -280,7 +348,7 @@
   doubleField->SetDouble<true>(h_instance.Get(), 1.0);
   objectField->SetObject<true>(h_instance.Get(), h_obj.Get());
   Runtime::Current()->ExitTransactionMode();
-  transaction.Abort();
+  transaction.Rollback();
 
   // Check values have properly been restored to their original (default) value.
   EXPECT_EQ(booleanField->GetBoolean(h_instance.Get()), false);
@@ -294,7 +362,7 @@
   EXPECT_EQ(objectField->GetObject(h_instance.Get()), nullptr);
 }
 
-
+// Tests static array fields are reset to their default value after transaction rollback.
 TEST_F(TransactionTest, StaticArrayFieldsTest) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<4> hs(soa.Self());
@@ -305,8 +373,10 @@
   Handle<mirror::Class> h_klass(
       hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  ASSERT_TRUE(success);
   ASSERT_TRUE(h_klass->IsInitialized());
+  ASSERT_FALSE(soa.Self()->IsExceptionPending());
 
   // Lookup fields.
   mirror::ArtField* booleanArrayField = h_klass->FindDeclaredStaticField("booleanArrayField", "[Z");
@@ -382,7 +452,7 @@
   ASSERT_TRUE(h_obj.Get() != nullptr);
   ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
 
-  // Modify fields inside transaction and abort it.
+  // Modify fields inside transaction then rollback changes.
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
   booleanArray->SetWithoutChecks<true>(0, true);
@@ -395,7 +465,7 @@
   doubleArray->SetWithoutChecks<true>(0, 1.0);
   objectArray->SetWithoutChecks<true>(0, h_obj.Get());
   Runtime::Current()->ExitTransactionMode();
-  transaction.Abort();
+  transaction.Rollback();
 
   // Check values have properly been restored to their original (default) value.
   EXPECT_EQ(booleanArray->GetWithoutChecks(0), false);
@@ -409,6 +479,7 @@
   EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr);
 }
 
+// Tests successful class initialization without class initializer.
 TEST_F(TransactionTest, EmptyClass) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
@@ -417,18 +488,22 @@
   ASSERT_TRUE(class_loader.Get() != nullptr);
 
   Handle<mirror::Class> h_klass(
-      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;", class_loader)));
+      hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;",
+                                            class_loader)));
   ASSERT_TRUE(h_klass.Get() != nullptr);
   class_linker_->VerifyClass(soa.Self(), h_klass);
   ASSERT_TRUE(h_klass->IsVerified());
 
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
-  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   Runtime::Current()->ExitTransactionMode();
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(h_klass->IsInitialized());
   ASSERT_FALSE(soa.Self()->IsExceptionPending());
 }
 
+// Tests successful class initialization with class initializer.
 TEST_F(TransactionTest, StaticFieldClass) {
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
@@ -445,51 +520,37 @@
 
   Transaction transaction;
   Runtime::Current()->EnterTransactionMode(&transaction);
-  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
+  bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
   Runtime::Current()->ExitTransactionMode();
+  ASSERT_TRUE(success);
+  ASSERT_TRUE(h_klass->IsInitialized());
   ASSERT_FALSE(soa.Self()->IsExceptionPending());
 }
 
-TEST_F(TransactionTest, BlacklistedClass) {
-  ScopedObjectAccess soa(Thread::Current());
-  jobject jclass_loader = LoadDex("Transaction");
-  StackHandleScope<2> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(
-      hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
-  ASSERT_TRUE(class_loader.Get() != nullptr);
-
-  // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
-  // be thrown by class initialization due to native call.
-  MutableHandle<mirror::Class> h_klass(
-      hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
-                                                  "Ljava/lang/ExceptionInInitializerError;")));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(soa.Self(), h_klass);
-  ASSERT_TRUE(h_klass->IsVerified());
-  h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(soa.Self(), h_klass);
-  ASSERT_TRUE(h_klass->IsVerified());
-
-  // Load and verify Transaction$NativeSupport used in class initialization.
-  h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
-                                             class_loader));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(soa.Self(), h_klass);
-  ASSERT_TRUE(h_klass->IsVerified());
-
-  h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
-                                             class_loader));
-  ASSERT_TRUE(h_klass.Get() != nullptr);
-  class_linker_->VerifyClass(soa.Self(), h_klass);
-  ASSERT_TRUE(h_klass->IsVerified());
-
-  Transaction transaction;
-  Runtime::Current()->EnterTransactionMode(&transaction);
-  class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
-  Runtime::Current()->ExitTransactionMode();
-  ASSERT_TRUE(soa.Self()->IsExceptionPending());
+// Tests failing class initialization due to native call.
+TEST_F(TransactionTest, NativeCallAbortClass) {
+  testTransactionAbort("LTransaction$NativeCallAbortClass;");
 }
 
+// Tests failing class initialization due to native call in a "synchronized" statement
+// (which must catch any exception, do the monitor-exit then re-throw the caught exception).
+TEST_F(TransactionTest, SynchronizedNativeCallAbortClass) {
+  testTransactionAbort("LTransaction$SynchronizedNativeCallAbortClass;");
+}
 
+// Tests failing class initialization due to native call, even if an "all" catch handler
+// catches the exception thrown when aborting the transaction.
+TEST_F(TransactionTest, CatchNativeCallAbortClass) {
+  testTransactionAbort("LTransaction$CatchNativeCallAbortClass;");
+}
+
+// Tests failing class initialization with multiple transaction aborts.
+TEST_F(TransactionTest, MultipleNativeCallAbortClass) {
+  testTransactionAbort("LTransaction$MultipleNativeCallAbortClass;");
+}
+
+// Tests failing class initialization due to allocating instance of finalizable class.
+TEST_F(TransactionTest, FinalizableAbortClass) {
+  testTransactionAbort("LTransaction$FinalizableAbortClass;");
+}
 }  // namespace art
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 1211547..af16d7e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -39,17 +39,10 @@
 #include "scoped_thread_state_change.h"
 #include "utf-inl.h"
 
-#if !defined(HAVE_POSIX_CLOCKS)
-#include <sys/time.h>
-#endif
-
-#if defined(HAVE_PRCTL)
-#include <sys/prctl.h>
-#endif
-
 #if defined(__APPLE__)
 #include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
 #include <sys/syscall.h>
+#include <sys/time.h>
 #endif
 
 #include <backtrace/Backtrace.h>  // For DumpNativeStack.
@@ -60,6 +53,10 @@
 
 namespace art {
 
+#if defined(__linux__)
+static constexpr bool kUseAddr2line = !kIsTargetBuild;
+#endif
+
 pid_t GetTid() {
 #if defined(__APPLE__)
   uint64_t owner;
@@ -164,11 +161,11 @@
 }
 
 uint64_t MilliTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
   timespec now;
   clock_gettime(CLOCK_MONOTONIC, &now);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000) + now.tv_nsec / UINT64_C(1000000);
-#else
+#else  // __APPLE__
   timeval now;
   gettimeofday(&now, NULL);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000) + now.tv_usec / UINT64_C(1000);
@@ -176,11 +173,11 @@
 }
 
 uint64_t MicroTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
   timespec now;
   clock_gettime(CLOCK_MONOTONIC, &now);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_nsec / UINT64_C(1000);
-#else
+#else  // __APPLE__
   timeval now;
   gettimeofday(&now, NULL);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_usec;
@@ -188,11 +185,11 @@
 }
 
 uint64_t NanoTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
   timespec now;
   clock_gettime(CLOCK_MONOTONIC, &now);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
-#else
+#else  // __APPLE__
   timeval now;
   gettimeofday(&now, NULL);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_usec * UINT64_C(1000);
@@ -200,11 +197,11 @@
 }
 
 uint64_t ThreadCpuNanoTime() {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(__linux__)
   timespec now;
   clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
-#else
+#else  // __APPLE__
   UNIMPLEMENTED(WARNING);
   return -1;
 #endif
@@ -1057,21 +1054,17 @@
   } else {
     s = thread_name + len - 15;
   }
-#if defined(__BIONIC__)
+#if defined(__linux__)
   // pthread_setname_np fails rather than truncating long strings.
-  char buf[16];       // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
+  char buf[16];       // MAX_TASK_COMM_LEN=16 is hard-coded in the kernel.
   strncpy(buf, s, sizeof(buf)-1);
   buf[sizeof(buf)-1] = '\0';
   errno = pthread_setname_np(pthread_self(), buf);
   if (errno != 0) {
     PLOG(WARNING) << "Unable to set the name of current thread to '" << buf << "'";
   }
-#elif defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+#else  // __APPLE__
   pthread_setname_np(thread_name);
-#elif defined(HAVE_PRCTL)
-  prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);  // NOLINT (unsigned long)
-#else
-  UNIMPLEMENTED(WARNING) << thread_name;
 #endif
 }
 
@@ -1117,8 +1110,76 @@
   return "";
 }
 
+#if defined(__linux__)
+
+ALWAYS_INLINE
+static inline void WritePrefix(std::ostream* os, const char* prefix, bool odd) {
+  if (prefix != nullptr) {
+    *os << prefix;
+  }
+  *os << "  ";
+  if (!odd) {
+    *os << " ";
+  }
+}
+
+static bool RunCommand(std::string cmd, std::ostream* os, const char* prefix) {
+  FILE* stream = popen(cmd.c_str(), "r");
+  if (stream) {
+    if (os != nullptr) {
+      bool odd_line = true;               // We indent them differently.
+      bool wrote_prefix = false;          // Have we already written a prefix?
+      constexpr size_t kMaxBuffer = 128;  // Relatively small buffer. Should be OK as we're on an
+                                          // alt stack, but just to be sure...
+      char buffer[kMaxBuffer];
+      while (!feof(stream)) {
+        if (fgets(buffer, kMaxBuffer, stream) != nullptr) {
+          // Split on newlines.
+          char* tmp = buffer;
+          for (;;) {
+            char* new_line = strchr(tmp, '\n');
+            if (new_line == nullptr) {
+              // Print the rest.
+              if (*tmp != 0) {
+                if (!wrote_prefix) {
+                  WritePrefix(os, prefix, odd_line);
+                }
+                wrote_prefix = true;
+                *os << tmp;
+              }
+              break;
+            }
+            if (!wrote_prefix) {
+              WritePrefix(os, prefix, odd_line);
+            }
+            char saved = *(new_line + 1);
+            *(new_line + 1) = 0;
+            *os << tmp;
+            *(new_line + 1) = saved;
+            tmp = new_line + 1;
+            odd_line = !odd_line;
+            wrote_prefix = false;
+          }
+        }
+      }
+    }
+    pclose(stream);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+static void Addr2line(const std::string& map_src, uintptr_t offset, std::ostream& os,
+                      const char* prefix) {
+  std::string cmdline(StringPrintf("addr2line --functions --inlines --demangle -e %s %zx",
+                                   map_src.c_str(), offset));
+  RunCommand(cmdline.c_str(), &os, prefix);
+}
+#endif
+
 void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
-    mirror::ArtMethod* current_method) {
+    mirror::ArtMethod* current_method, void* ucontext_ptr) {
 #if __linux__
   // b/18119146
   if (RUNNING_ON_VALGRIND != 0) {
@@ -1134,7 +1195,7 @@
 #endif
 
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
-  if (!backtrace->Unwind(0)) {
+  if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
     os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
     return;
   } else if (backtrace->NumFrames() == 0) {
@@ -1142,6 +1203,16 @@
     return;
   }
 
+  // Check whether we have and should use addr2line.
+  bool use_addr2line;
+  if (kUseAddr2line) {
+    // Try to run it to see whether we have it. Push an argument so that it doesn't assume a.out
+    // and print to stderr.
+    use_addr2line = (gAborting > 0) && RunCommand("addr2line -h", nullptr, nullptr);
+  } else {
+    use_addr2line = false;
+  }
+
   for (Backtrace::const_iterator it = backtrace->begin();
        it != backtrace->end(); ++it) {
     // We produce output like this:
@@ -1153,16 +1224,19 @@
     // after the <RELATIVE_ADDR>. There can be any prefix data before the
     // #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix.
     os << prefix << StringPrintf("#%02zu pc ", it->num);
-    if (!it->map) {
+    bool try_addr2line = false;
+    if (!BacktraceMap::IsValid(it->map)) {
       os << StringPrintf("%08" PRIxPTR "  ???", it->pc);
     } else {
-      os << StringPrintf("%08" PRIxPTR "  ", it->pc - it->map->start)
-         << it->map->name << " (";
+      os << StringPrintf("%08" PRIxPTR "  ", it->pc - it->map.start);
+      os << it->map.name;
+      os << " (";
       if (!it->func_name.empty()) {
         os << it->func_name;
         if (it->func_offset != 0) {
           os << "+" << it->func_offset;
         }
+        try_addr2line = true;
       } else if (current_method != nullptr &&
                  Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
                  current_method->PcIsWithinQuickCode(it->pc)) {
@@ -1175,9 +1249,12 @@
       os << ")";
     }
     os << "\n";
+    if (try_addr2line && use_addr2line) {
+      Addr2line(it->map.name, it->pc - it->map.start, os, prefix);
+    }
   }
 #else
-  UNUSED(os, tid, prefix, current_method);
+  UNUSED(os, tid, prefix, current_method, ucontext_ptr);
 #endif
 }
 
diff --git a/runtime/utils.h b/runtime/utils.h
index 668c897..1c2576c 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -430,7 +430,8 @@
 // Sleep for the given number of nanoseconds, a bad way to handle contention.
 void NanoSleep(uint64_t ns);
 
-// Initialize a timespec to either an absolute or relative time.
+// Initialize a timespec to either a relative time (ms,ns), or to the absolute
+// time corresponding to the indicated clock value plus the supplied offset.
 void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts);
 
 // Splits a string using the given separator character into a vector of
@@ -464,7 +465,7 @@
 
 // Dumps the native stack for thread 'tid' to 'os'.
 void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "",
-    mirror::ArtMethod* current_method = nullptr)
+    mirror::ArtMethod* current_method = nullptr, void* ucontext = nullptr)
     NO_THREAD_SAFETY_ANALYSIS;
 
 // Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
@@ -548,6 +549,13 @@
 template <typename T>
 using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
 
+// C++14 from-the-future import (std::make_unique)
+// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr.
+template <typename T, typename ... Args>
+std::unique_ptr<T> MakeUnique(Args&& ... args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index a98bc90..a3dd13c6 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -392,6 +392,9 @@
 }
 
 TEST_F(UtilsTest, ExecError) {
+  // This will lead to error messages in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+
   std::vector<std::string> command;
   command.push_back("bogus");
   std::string error_msg;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1b3cc8f..474a066d 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -103,6 +103,14 @@
   return false;
 }
 
+static void SafelyMarkAllRegistersAsConflicts(MethodVerifier* verifier, RegisterLine* reg_line) {
+  if (verifier->IsConstructor()) {
+    // Before we mark all regs as conflicts, check that we don't have an uninitialized this.
+    reg_line->CheckConstructorReturn(verifier);
+  }
+  reg_line->MarkAllRegistersAsConflicts(verifier);
+}
+
 MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
                                                         mirror::Class* klass,
                                                         bool allow_soft_failures,
@@ -278,7 +286,7 @@
 
   MethodVerifier verifier(self, dex_file, dex_cache, class_loader, class_def, code_item,
                           method_idx, method, method_access_flags, true, allow_soft_failures,
-                          need_precise_constants);
+                          need_precise_constants, true);
   if (verifier.Verify()) {
     // Verification completed, however failures may be pending that didn't cause the verification
     // to hard fail.
@@ -344,7 +352,8 @@
                                const DexFile::CodeItem* code_item, uint32_t dex_method_idx,
                                Handle<mirror::ArtMethod> method, uint32_t method_access_flags,
                                bool can_load_classes, bool allow_soft_failures,
-                               bool need_precise_constants, bool verify_to_dump)
+                               bool need_precise_constants, bool verify_to_dump,
+                               bool allow_thread_suspension)
     : self_(self),
       reg_types_(can_load_classes),
       work_insn_idx_(-1),
@@ -369,7 +378,8 @@
       need_precise_constants_(need_precise_constants),
       has_check_casts_(false),
       has_virtual_or_interface_invokes_(false),
-      verify_to_dump_(verify_to_dump) {
+      verify_to_dump_(verify_to_dump),
+      allow_thread_suspension_(allow_thread_suspension) {
   Runtime::Current()->AddMethodVerifier(this);
   DCHECK(class_def != nullptr);
 }
@@ -388,7 +398,7 @@
   Handle<mirror::ArtMethod> method(hs.NewHandle(m));
   MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
                           m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
-                          false, true, false);
+                          false, true, false, false);
   verifier.interesting_dex_pc_ = dex_pc;
   verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs;
   verifier.FindLocksAtDexPc();
@@ -435,7 +445,7 @@
   Handle<mirror::ArtMethod> method(hs.NewHandle(m));
   MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
                           m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
-                          true, true, false);
+                          true, true, false, true);
   return verifier.FindAccessedFieldAtDexPc(dex_pc);
 }
 
@@ -467,7 +477,7 @@
   Handle<mirror::ArtMethod> method(hs.NewHandle(m));
   MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
                           m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
-                          true, true, false);
+                          true, true, false, true);
   return verifier.FindInvokedMethodAtDexPc(dex_pc);
 }
 
@@ -725,7 +735,16 @@
     /* Flag instructions that are garbage collection points */
     // All invoke points are marked as "Throw" points already.
     // We are relying on this to also count all the invokes as interesting.
-    if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow()) {
+    if (inst->IsBranch()) {
+      insn_flags_[dex_pc].SetCompileTimeInfoPoint();
+      // The compiler also needs safepoints for fall-through to loop heads.
+      // Such a loop head must be a target of a branch.
+      int32_t offset = 0;
+      bool cond, self_ok;
+      bool target_ok = GetBranchOffset(dex_pc, &offset, &cond, &self_ok);
+      DCHECK(target_ok);
+      insn_flags_[dex_pc + offset].SetCompileTimeInfoPoint();
+    } else if (inst->IsSwitch() || inst->IsThrow()) {
       insn_flags_[dex_pc].SetCompileTimeInfoPoint();
     } else if (inst->IsReturn()) {
       insn_flags_[dex_pc].SetCompileTimeInfoPointAndReturn();
@@ -1385,7 +1404,9 @@
 
   /* Continue until no instructions are marked "changed". */
   while (true) {
-    self_->AllowThreadSuspension();
+    if (allow_thread_suspension_) {
+      self_->AllowThreadSuspension();
+    }
     // Find the first marked one. Use "start_guess" as a way to find one quickly.
     uint32_t insn_idx = start_guess;
     for (; insn_idx < insns_size; insn_idx++) {
@@ -1550,6 +1571,16 @@
   std::unique_ptr<RegisterLine> branch_line;
   std::unique_ptr<RegisterLine> fallthrough_line;
 
+  /*
+   * If we are in a constructor, and we currently have an UninitializedThis type
+   * in a register somewhere, we need to make sure it isn't overwritten.
+   */
+  bool track_uninitialized_this = false;
+  size_t uninitialized_this_loc = 0;
+  if (IsConstructor()) {
+    track_uninitialized_this = work_line_->GetUninitializedThisLoc(this, &uninitialized_this_loc);
+  }
+
   switch (inst->Opcode()) {
     case Instruction::NOP:
       /*
@@ -1612,6 +1643,12 @@
       break;
 
     case Instruction::MOVE_EXCEPTION: {
+      // We do not allow MOVE_EXCEPTION as the first instruction in a method. This is a simple case
+      // where one entrypoint to the catch block is not actually an exception path.
+      if (work_insn_idx_ == 0) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "move-exception at pc 0x0";
+        break;
+      }
       /*
        * This statement can only appear as the first instruction in an exception handler. We verify
        * that as part of extracting the exception type from the catch block list.
@@ -2696,6 +2733,18 @@
     case Instruction::IGET_OBJECT_QUICK:
       VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.JavaLangObject(false), false);
       break;
+    case Instruction::IGET_BOOLEAN_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Boolean(), true);
+      break;
+    case Instruction::IGET_BYTE_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Byte(), true);
+      break;
+    case Instruction::IGET_CHAR_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Char(), true);
+      break;
+    case Instruction::IGET_SHORT_QUICK:
+      VerifyQuickFieldAccess<FieldAccessType::kAccGet>(inst, reg_types_.Short(), true);
+      break;
     case Instruction::IPUT_QUICK:
       VerifyQuickFieldAccess<FieldAccessType::kAccPut>(inst, reg_types_.Integer(), true);
       break;
@@ -2735,31 +2784,10 @@
     }
 
     /* These should never appear during verification. */
-    case Instruction::UNUSED_3E:
-    case Instruction::UNUSED_3F:
-    case Instruction::UNUSED_40:
-    case Instruction::UNUSED_41:
-    case Instruction::UNUSED_42:
-    case Instruction::UNUSED_43:
+    case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
+    case Instruction::UNUSED_F3 ... Instruction::UNUSED_FF:
     case Instruction::UNUSED_79:
     case Instruction::UNUSED_7A:
-    case Instruction::UNUSED_EF:
-    case Instruction::UNUSED_F0:
-    case Instruction::UNUSED_F1:
-    case Instruction::UNUSED_F2:
-    case Instruction::UNUSED_F3:
-    case Instruction::UNUSED_F4:
-    case Instruction::UNUSED_F5:
-    case Instruction::UNUSED_F6:
-    case Instruction::UNUSED_F7:
-    case Instruction::UNUSED_F8:
-    case Instruction::UNUSED_F9:
-    case Instruction::UNUSED_FA:
-    case Instruction::UNUSED_FB:
-    case Instruction::UNUSED_FC:
-    case Instruction::UNUSED_FD:
-    case Instruction::UNUSED_FE:
-    case Instruction::UNUSED_FF:
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
       break;
 
@@ -2769,6 +2797,20 @@
      */
   }  // end - switch (dec_insn.opcode)
 
+  /*
+   * If we are in a constructor, and we had an UninitializedThis type
+   * in a register somewhere, we need to make sure it wasn't overwritten.
+   */
+  if (track_uninitialized_this) {
+    bool was_invoke_direct = (inst->Opcode() == Instruction::INVOKE_DIRECT ||
+                              inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
+    if (work_line_->WasUninitializedThisOverwritten(this, uninitialized_this_loc,
+                                                    was_invoke_direct)) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+          << "Constructor failed to initialize this object";
+    }
+  }
+
   if (have_pending_hard_failure_) {
     if (Runtime::Current()->IsCompiler()) {
       /* When compiling, check that the last failure is a hard failure */
@@ -2950,7 +2992,7 @@
       const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx);
       Instruction::Code opcode = ret_inst->Opcode();
       if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
-        work_line_->MarkAllRegistersAsConflicts(this);
+        SafelyMarkAllRegistersAsConflicts(this, work_line_.get());
       } else {
         if (opcode == Instruction::RETURN_WIDE) {
           work_line_->MarkAllRegistersAsConflictsExceptWide(this, ret_inst->VRegA_11x());
@@ -3159,7 +3201,7 @@
   }
   // See if the method type implied by the invoke instruction matches the access flags for the
   // target method.
-  if ((method_type == METHOD_DIRECT && !res_method->IsDirect()) ||
+  if ((method_type == METHOD_DIRECT && (!res_method->IsDirect() || res_method->IsStatic())) ||
       (method_type == METHOD_STATIC && !res_method->IsStatic()) ||
       ((method_type == METHOD_VIRTUAL || method_type == METHOD_INTERFACE) && res_method->IsDirect())
       ) {
@@ -3408,7 +3450,7 @@
     return nullptr;
   }
   mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
-  if (FailOrAbort(this, !Thread::Current()->IsExceptionPending(),
+  if (FailOrAbort(this, !self_->IsExceptionPending(),
                   "Unexpected exception pending for quickened invoke at ",
                   work_insn_idx_)) {
     return nullptr;
@@ -3418,7 +3460,9 @@
 
 mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst,
                                                                 bool is_range) {
-  DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
+  DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_)
+      << PrettyMethod(dex_method_idx_, *dex_file_, true) << "@" << work_insn_idx_;
+
   mirror::ArtMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(),
                                                              is_range);
   if (res_method == nullptr) {
@@ -3900,6 +3944,10 @@
   DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
          inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
          inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
+         inst->Opcode() == Instruction::IGET_BOOLEAN_QUICK ||
+         inst->Opcode() == Instruction::IGET_BYTE_QUICK ||
+         inst->Opcode() == Instruction::IGET_CHAR_QUICK ||
+         inst->Opcode() == Instruction::IGET_SHORT_QUICK ||
          inst->Opcode() == Instruction::IPUT_QUICK ||
          inst->Opcode() == Instruction::IPUT_WIDE_QUICK ||
          inst->Opcode() == Instruction::IPUT_OBJECT_QUICK ||
@@ -4099,7 +4147,7 @@
       const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
       Instruction::Code opcode = ret_inst->Opcode();
       if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
-        target_line->MarkAllRegistersAsConflicts(this);
+        SafelyMarkAllRegistersAsConflicts(this, target_line);
       } else {
         target_line->CopyFromLine(merge_line);
         if (opcode == Instruction::RETURN_WIDE) {
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 0c4bf3c..b83e647 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -207,10 +207,11 @@
                  const DexFile::CodeItem* code_item, uint32_t method_idx,
                  Handle<mirror::ArtMethod> method,
                  uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
-                 bool need_precise_constants) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+                 bool need_precise_constants, bool allow_thread_suspension)
+          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       : MethodVerifier(self, dex_file, dex_cache, class_loader, class_def, code_item, method_idx,
                        method, access_flags, can_load_classes, allow_soft_failures,
-                       need_precise_constants, false) {}
+                       need_precise_constants, false, allow_thread_suspension) {}
 
   ~MethodVerifier();
 
@@ -238,6 +239,20 @@
   bool HasFailures() const;
   const RegType& ResolveCheckedClass(uint32_t class_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst,
+                                           RegisterLine* reg_line,
+                                           bool is_range)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Is the method being verified a constructor?
+  bool IsConstructor() const {
+    return (method_access_flags_ & kAccConstructor) != 0;
+  }
+
+  // Is the method verified static?
+  bool IsStatic() const {
+    return (method_access_flags_ & kAccStatic) != 0;
+  }
 
  private:
   // Private constructor for dumping.
@@ -246,7 +261,7 @@
                  const DexFile::CodeItem* code_item, uint32_t method_idx,
                  Handle<mirror::ArtMethod> method, uint32_t access_flags,
                  bool can_load_classes, bool allow_soft_failures, bool need_precise_constants,
-                 bool verify_to_dump)
+                 bool verify_to_dump, bool allow_thread_suspension)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Adds the given string to the beginning of the last failure message.
@@ -586,11 +601,6 @@
                                                       mirror::ArtMethod* res_method)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst,
-                                           RegisterLine* reg_line,
-                                           bool is_range)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   mirror::ArtMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst, bool is_range)
   SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -626,16 +636,6 @@
   bool UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line, bool update_merge_line)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Is the method being verified a constructor?
-  bool IsConstructor() const {
-    return (method_access_flags_ & kAccConstructor) != 0;
-  }
-
-  // Is the method verified static?
-  bool IsStatic() const {
-    return (method_access_flags_ & kAccStatic) != 0;
-  }
-
   // Return the register type for the method.
   const RegType& GetMethodReturnType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -730,6 +730,11 @@
   // VerifyMethodAndDump.
   const bool verify_to_dump_;
 
+  // Whether or not we call AllowThreadSuspension periodically, we want a way to disable this for
+  // thread dumping checkpoints since we may get thread suspension at an inopportune time due to
+  // FindLocksAtDexPC, resulting in deadlocks.
+  const bool allow_thread_suspension_;
+
   DISALLOW_COPY_AND_ASSIGN(MethodVerifier);
 };
 std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs);
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 770ca7e..f67adc1 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -41,14 +41,12 @@
         << error_msg;
   }
 
-  void VerifyDexFile(const DexFile* dex)
+  void VerifyDexFile(const DexFile& dex)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ASSERT_TRUE(dex != NULL);
-
     // Verify all the classes defined in this file
-    for (size_t i = 0; i < dex->NumClassDefs(); i++) {
-      const DexFile::ClassDef& class_def = dex->GetClassDef(i);
-      const char* descriptor = dex->GetClassDescriptor(class_def);
+    for (size_t i = 0; i < dex.NumClassDefs(); i++) {
+      const DexFile::ClassDef& class_def = dex.GetClassDef(i);
+      const char* descriptor = dex.GetClassDescriptor(class_def);
       VerifyClass(descriptor);
     }
   }
@@ -56,7 +54,8 @@
 
 TEST_F(MethodVerifierTest, LibCore) {
   ScopedObjectAccess soa(Thread::Current());
-  VerifyDexFile(java_lang_dex_file_);
+  ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+  VerifyDexFile(*java_lang_dex_file_);
 }
 
 }  // namespace verifier
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 41541b5..3510665 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -779,9 +779,7 @@
 }
 
 void RegType::VisitRoots(RootCallback* callback, void* arg) const {
-  if (!klass_.IsNull()) {
-    callback(reinterpret_cast<mirror::Object**>(&klass_), arg, 0, kRootUnknown);
-  }
+  klass_.VisitRootIfNonNull(callback, arg, RootInfo(kRootUnknown));
 }
 
 void UninitializedThisReferenceType::CheckInvariants() const {
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 72d7938..3b09871 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -25,6 +25,49 @@
 namespace art {
 namespace verifier {
 
+bool RegisterLine::WasUninitializedThisOverwritten(MethodVerifier* verifier,
+                                                   size_t this_loc,
+                                                   bool was_invoke_direct) const {
+  DCHECK(verifier->IsConstructor());
+
+  // Is the UnintializedThis type still there?
+  if (GetRegisterType(verifier, this_loc).IsUninitializedThisReference() ||
+      GetRegisterType(verifier, this_loc).IsUnresolvedAndUninitializedThisReference()) {
+    return false;
+  }
+
+  // If there is an initialized reference here now, did we just perform an invoke-direct? Note that
+  // this is the correct approach for dex bytecode: results of invoke-direct are stored in the
+  // result register. Overwriting "this_loc" can only be done by a constructor call.
+  if (GetRegisterType(verifier, this_loc).IsReferenceTypes() && was_invoke_direct) {
+    return false;
+    // Otherwise we could have just copied a different initialized reference to this location.
+  }
+
+  // The UnintializedThis in the register is gone, so check to see if it's somewhere else now.
+  for (size_t i = 0; i < num_regs_; i++) {
+    if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
+        GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) {
+      // We found it somewhere else...
+      return false;
+    }
+  }
+
+  // The UninitializedThis is gone from the original register, and now we can't find it.
+  return true;
+}
+
+bool RegisterLine::GetUninitializedThisLoc(MethodVerifier* verifier, size_t* vreg) const {
+  for (size_t i = 0; i < num_regs_; i++) {
+    if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
+        GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) {
+      *vreg = i;
+      return true;
+    }
+  }
+  return false;
+}
+
 bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
   for (size_t i = 0; i < num_regs_; i++) {
     if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 52b5c13..ca61a0b 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -157,6 +157,18 @@
    */
   bool CheckConstructorReturn(MethodVerifier* verifier) const;
 
+  /*
+   * Check if an UninitializedThis at the specified location has been overwritten before
+   * being correctly initialized.
+   */
+  bool WasUninitializedThisOverwritten(MethodVerifier* verifier, size_t this_loc,
+                                       bool was_invoke_direct) const;
+
+  /*
+   * Get the first location of an UninitializedThis type, or return kInvalidVreg if there are none.
+   */
+  bool GetUninitializedThisLoc(MethodVerifier* verifier, size_t* vreg) const;
+
   // Compare two register lines. Returns 0 if they match.
   // Using this for a sort is unwise, since the value can change based on machine endianness.
   int CompareLine(const RegisterLine* line2) const {
diff --git a/runtime/vmap_table.h b/runtime/vmap_table.h
index df5cd80..db9e1ea 100644
--- a/runtime/vmap_table.h
+++ b/runtime/vmap_table.h
@@ -65,7 +65,7 @@
     uint16_t adjusted_vreg = vreg + kEntryAdjustment;
     size_t end = DecodeUnsignedLeb128(&table);
     bool high_reg = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
-    bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64);
+    bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64) || (kRuntimeISA == kMips64);
     if (target64 && high_reg) {
       // Wide promoted registers are associated with the sreg of the low portion.
       adjusted_vreg--;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 16338c4..e368d2c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -66,9 +66,9 @@
 jmethodID WellKnownClasses::java_lang_Character_valueOf;
 jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass;
 jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init;
-jmethodID WellKnownClasses::java_lang_Daemons_requestGC;
 jmethodID WellKnownClasses::java_lang_Daemons_requestHeapTrim;
 jmethodID WellKnownClasses::java_lang_Daemons_start;
+jmethodID WellKnownClasses::java_lang_Daemons_stop;
 jmethodID WellKnownClasses::java_lang_Double_valueOf;
 jmethodID WellKnownClasses::java_lang_Float_valueOf;
 jmethodID WellKnownClasses::java_lang_Integer_valueOf;
@@ -204,9 +204,9 @@
   java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
   java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
 
-  java_lang_Daemons_requestGC = CacheMethod(env, java_lang_Daemons, true, "requestGC", "()V");
   java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V");
   java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
+  java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
 
   ScopedLocalRef<jclass> java_lang_ref_FinalizerReference(env, env->FindClass("java/lang/ref/FinalizerReference"));
   java_lang_ref_FinalizerReference_add = CacheMethod(env, java_lang_ref_FinalizerReference.get(), true, "add", "(Ljava/lang/Object;)V");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index d651b90..1a4f0f8 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -77,9 +77,9 @@
   static jmethodID java_lang_Character_valueOf;
   static jmethodID java_lang_ClassLoader_loadClass;
   static jmethodID java_lang_ClassNotFoundException_init;
-  static jmethodID java_lang_Daemons_requestGC;
   static jmethodID java_lang_Daemons_requestHeapTrim;
   static jmethodID java_lang_Daemons_start;
+  static jmethodID java_lang_Daemons_stop;
   static jmethodID java_lang_Double_valueOf;
   static jmethodID java_lang_Float_valueOf;
   static jmethodID java_lang_Integer_valueOf;
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index b7ff360..e1aae11 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -28,6 +28,8 @@
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+$(eval $(call set-target-local-clang-vars))
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -40,6 +42,7 @@
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+$(eval $(call set-target-local-clang-vars))
 include $(BUILD_STATIC_LIBRARY)
 
 # Build host library.
@@ -54,6 +57,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_LDLIBS = -ldl
 LOCAL_MULTILIB := both
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -67,5 +71,4 @@
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_LDLIBS = -ldl
 LOCAL_MULTILIB := both
-include external/libcxx/libcxx.mk
 include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 601e321..2eb518c 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -170,12 +170,13 @@
   // Note that we check that the signal number is in range here.  An out of range signal
   // number should behave exactly as the libc sigaction.
   if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
-    if (old_action != NULL) {
-      *old_action = user_sigactions[signal].GetAction();
-    }
+    struct sigaction saved_action = user_sigactions[signal].GetAction();
     if (new_action != NULL) {
       user_sigactions[signal].SetAction(*new_action, false);
     }
+    if (old_action != NULL) {
+      *old_action = saved_action;
+    }
     return 0;
   }
 
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index c2877be..544cbc5 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -294,20 +294,20 @@
     assert(!env->ExceptionCheck());
 
     // Create a string object.
-    jobject library_string = env->NewStringUTF("arttest");
+    jobject library_string = env->NewStringUTF("non_existing_library");
     assert(library_string != nullptr);
     assert(!env->ExceptionCheck());
 
     env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string);
-    if (env->ExceptionCheck()) {
-      // At most we expect UnsatisfiedLinkError.
-      jthrowable thrown = env->ExceptionOccurred();
-      env->ExceptionClear();
+    assert(env->ExceptionCheck());
 
-      jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
-      jclass thrown_class = env->GetObjectClass(thrown);
-      assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
-    }
+    // We expect UnsatisfiedLinkError.
+    jthrowable thrown = env->ExceptionOccurred();
+    env->ExceptionClear();
+
+    jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
+    jclass thrown_class = env->GetObjectClass(thrown);
+    assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
   }
 }
 
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 631c4be..40be56c 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -52,11 +52,11 @@
       // v2 is added because of the instruction at DexPC 0024. Object merges with 0 is Object. See:
       //   0024: move-object v3, v2
       //   0025: goto 0013
-      // Detaled dex instructions for ReferenceMap.java are at the end of this function.
+      // Detailed dex instructions for ReferenceMap.java are at the end of this function.
       // CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1);  // v8: this, v3: y, v2: y, v1: x
       // We eliminate the non-live registers at a return, so only v3 is live.
       // Note that it is OK for a compiler to not have a dex map at this dex PC because
-      // a return is not a safepoint.
+      // a return is not necessarily a safepoint.
       CHECK_REGS_CONTAIN_REFS(0x13U, false);  // v3: y
       CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1, 0);  // v8: this, v2: y, v1: x, v0: ex
       CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1, 0);  // v8: this, v5: x[1], v2: y, v1: x, v0: ex
@@ -68,8 +68,10 @@
       CHECK_REGS_CONTAIN_REFS(0x27U, true, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
       CHECK_REGS_CONTAIN_REFS(0x29U, true, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
       CHECK_REGS_CONTAIN_REFS(0x2cU, true, 8, 4, 2, 1);  // v8: this, v4: ex, v2: y, v1: x
-      CHECK_REGS_CONTAIN_REFS(0x2fU, true, 8, 4, 3, 2, 1);  // v8: this, v4: ex, v3: y, v2: y, v1: x
-      CHECK_REGS_CONTAIN_REFS(0x32U, true, 8, 3, 2, 1, 0);  // v8: this, v3: y, v2: y, v1: x, v0: ex
+      // Note that it is OK for a compiler to not have a dex map at these two dex PCs because
+      // a goto is not necessarily a safepoint.
+      CHECK_REGS_CONTAIN_REFS(0x2fU, false, 8, 4, 3, 2, 1);  // v8: this, v4: ex, v3: y, v2: y, v1: x
+      CHECK_REGS_CONTAIN_REFS(0x32U, false, 8, 3, 2, 1, 0);  // v8: this, v3: y, v2: y, v1: x, v0: ex
     }
 
     return true;
diff --git a/test/004-SignalTest/src/Main.java b/test/004-SignalTest/src/Main.java
index 0391592..8b1f49b 100644
--- a/test/004-SignalTest/src/Main.java
+++ b/test/004-SignalTest/src/Main.java
@@ -20,7 +20,7 @@
     private static native int testSignal();
 
     private static void stackOverflow() {
-       stackOverflow();
+        stackOverflow();
     }
 
     public static void main(String[] args) {
@@ -40,7 +40,6 @@
         }
         try {
             stackOverflow();
-
             // Should never get here.
             throw new AssertionError();
         } catch (StackOverflowError e) {
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index 743d62c..3d0f074 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -94,6 +94,16 @@
     unsafe.putLong(t, longOffset, longValue);
     check(t.longVar, longValue, "Unsafe.putLong(Object, long, long)");
     check(unsafe.getLong(t, longOffset), longValue, "Unsafe.getLong(Object, long)");
+
+    if (unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
+        System.out.println("Unexpectedly succeeding compareAndSwap...");
+    }
+    if (!unsafe.compareAndSwapInt(t, intOffset, intValue, 0)) {
+        System.out.println("Unexpectedly not succeeding compareAndSwap...");
+    }
+    if (!unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
+        System.out.println("Unexpectedly not succeeding compareAndSwap...");
+    }
   }
 
   private static class TestClass {
diff --git a/test/015-switch/expected.txt b/test/015-switch/expected.txt
index 91b4714..be6d2ca 100644
--- a/test/015-switch/expected.txt
+++ b/test/015-switch/expected.txt
@@ -1,3 +1,119 @@
+packed
+default
+default
+0
+1
+2
+default
+default
+packed2
+-2
+-1
+0
+1
+2
+default
+default
+packed3
+default
+default
+default
+default
+2
+3
+4
+5
+6
+default
+default
+packed4
+default
+2147483646
+2147483647
+default
+packed5
+-2147483648
+-2147483647
+default
+packed6
+-2147483648
+default
+packed7
+default
+default
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+default
+sparse
+default
+default
+0
+1
+default
+3
+default
+default
+sparse2
+-2
+-1
+0
+default
+2
+default
+default
+sparse3
+default
+default
+default
+default
+2
+default
+4
+5
+6
+default
+default
+sparse4
+2147483645
+default
+2147483647
+default
+sparse5
+-2147483648
+default
+default
+sparse7
+default
+default
+1
+2
+default
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+default
 CORRECT (one)
 CORRECT (not found)
 CORRECT (large)
diff --git a/test/015-switch/src/Main.java b/test/015-switch/src/Main.java
index dd97a8c..2a7995a 100644
--- a/test/015-switch/src/Main.java
+++ b/test/015-switch/src/Main.java
@@ -18,7 +18,338 @@
  * Test switch() blocks
  */
 public class Main {
+
+    // TODO: This should be translated to smali tests, so it is guaranteed we have the right kind
+    //       of switch.
+
+    // Simple packed-switch.
+    public static void packedSwitch(int value) {
+        switch (value) {
+            case 0:
+                System.out.println("0"); break;
+            case 1:
+                System.out.println("1"); break;
+            case 2:
+                System.out.println("2"); break;
+            case 3:
+                System.out.println("3"); break;
+            case 4:
+                System.out.println("4"); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple packed-switch starting at a negative index.
+    public static void packedSwitch2(int value) {
+        switch (value) {
+            case -3:
+                System.out.println("-3"); break;
+            case -2:
+                System.out.println("-2"); break;
+            case -1:
+                System.out.println("-1"); break;
+            case 0:
+                System.out.println("0"); break;
+            case 1:
+                System.out.println("1"); break;
+            case 2:
+                System.out.println("2"); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple packed-switch starting above 0.
+    public static void packedSwitch3(int value) {
+        switch (value) {
+            case 2:
+                System.out.println("2"); break;
+            case 3:
+                System.out.println("3"); break;
+            case 4:
+                System.out.println("4"); break;
+            case 5:
+                System.out.println("5"); break;
+            case 6:
+                System.out.println("6"); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple packed-switch going up to max_int.
+    public static void packedSwitch4(int value) {
+        switch (value) {
+            case Integer.MAX_VALUE - 1:
+                System.out.println(Integer.MAX_VALUE - 1); break;
+            case Integer.MAX_VALUE:
+                System.out.println(Integer.MAX_VALUE); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple packed-switch starting at min_int.
+    public static void packedSwitch5(int value) {
+        switch (value) {
+            case Integer.MIN_VALUE:
+                System.out.println(Integer.MIN_VALUE); break;
+            case Integer.MIN_VALUE + 1:
+                System.out.println(Integer.MIN_VALUE + 1); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple (packed-)switch with only min_int.
+    public static void packedSwitch6(int value) {
+        switch (value) {
+            case Integer.MIN_VALUE:
+                System.out.println(Integer.MIN_VALUE); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Long packed-switch that might lead to not creating chained-ifs.
+    public static void packedSwitch7(int value) {
+        switch (value) {
+            case 1:
+                System.out.println(1); break;
+            case 2:
+                System.out.println(2); break;
+            case 3:
+                System.out.println(3); break;
+            case 4:
+                System.out.println(4); break;
+            case 5:
+                System.out.println(5); break;
+            case 6:
+                System.out.println(6); break;
+            case 7:
+                System.out.println(7); break;
+            case 8:
+                System.out.println(8); break;
+            case 9:
+                System.out.println(9); break;
+            case 10:
+                System.out.println(10); break;
+            case 11:
+                System.out.println(11); break;
+            case 12:
+                System.out.println(12); break;
+            case 13:
+                System.out.println(13); break;
+            case 14:
+                System.out.println(14); break;
+            case 15:
+                System.out.println(15); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Sparse switch, just leave a gap.
+    public static void sparseSwitch(int value) {
+        switch (value) {
+            case 0:
+                System.out.println("0"); break;
+            case 1:
+                System.out.println("1"); break;
+            case 3:
+                System.out.println("3"); break;
+            case 4:
+                System.out.println("4"); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple sparse-switch starting at a negative index.
+    public static void sparseSwitch2(int value) {
+        switch (value) {
+            case -3:
+                System.out.println("-3"); break;
+            case -2:
+                System.out.println("-2"); break;
+            case -1:
+                System.out.println("-1"); break;
+            case 0:
+                System.out.println("0"); break;
+            case 2:
+                System.out.println("2"); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple sparse-switch starting above 0.
+    public static void sparseSwitch3(int value) {
+        switch (value) {
+            case 2:
+                System.out.println("2"); break;
+            case 4:
+                System.out.println("4"); break;
+            case 5:
+                System.out.println("5"); break;
+            case 6:
+                System.out.println("6"); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple sparse-switch going up to max_int.
+    public static void sparseSwitch4(int value) {
+        switch (value) {
+            case Integer.MAX_VALUE - 2:
+                System.out.println(Integer.MAX_VALUE - 2); break;
+            case Integer.MAX_VALUE:
+                System.out.println(Integer.MAX_VALUE); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Simple sparse-switch starting at min_int.
+    public static void sparseSwitch5(int value) {
+        switch (value) {
+            case Integer.MIN_VALUE:
+                System.out.println(Integer.MIN_VALUE); break;
+            case Integer.MIN_VALUE + 2:
+                System.out.println(Integer.MIN_VALUE + 2); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
+    // Long sparse-switch that might lead to not creating chained-ifs.
+    public static void sparseSwitch7(int value) {
+        switch (value) {
+            case 1:
+                System.out.println(1); break;
+            case 2:
+                System.out.println(2); break;
+            case 4:
+                System.out.println(4); break;
+            case 5:
+                System.out.println(5); break;
+            case 6:
+                System.out.println(6); break;
+            case 7:
+                System.out.println(7); break;
+            case 8:
+                System.out.println(8); break;
+            case 9:
+                System.out.println(9); break;
+            case 10:
+                System.out.println(10); break;
+            case 11:
+                System.out.println(11); break;
+            case 12:
+                System.out.println(12); break;
+            case 13:
+                System.out.println(13); break;
+            case 14:
+                System.out.println(14); break;
+            case 15:
+                System.out.println(15); break;
+            default:
+                System.out.println("default"); break;
+        }
+    }
+
     public static void main(String args[]) {
+        /*
+         * Note: We are using for loops and calls to hopefully avoid simplifying the switch
+         *       structure from constant propagation. When inlining is supported, this needs to
+         *       be revisited.
+         */
+
+        System.out.println("packed");
+        for (int i = -2; i < 3; i++) {
+            packedSwitch(i);
+        }
+        packedSwitch(Integer.MIN_VALUE);
+        packedSwitch(Integer.MAX_VALUE);
+
+        System.out.println("packed2");
+        for (int i = -2; i < 3; i++) {
+            packedSwitch2(i);
+        }
+        packedSwitch2(Integer.MIN_VALUE);
+        packedSwitch2(Integer.MAX_VALUE);
+
+        System.out.println("packed3");
+        for (int i = -2; i < 7; i++) {
+            packedSwitch3(i);
+        }
+        packedSwitch3(Integer.MIN_VALUE);
+        packedSwitch3(Integer.MAX_VALUE);
+
+        System.out.println("packed4");
+        for (int i = Integer.MAX_VALUE - 2; i > 0; i++) {
+            packedSwitch4(i);
+        }
+        packedSwitch4(Integer.MIN_VALUE);
+
+        System.out.println("packed5");
+        for (int i = Integer.MIN_VALUE; i < Integer.MIN_VALUE + 2; i++) {
+            packedSwitch5(i);
+        }
+        packedSwitch5(Integer.MAX_VALUE);
+
+        System.out.println("packed6");
+        packedSwitch6(Integer.MIN_VALUE);
+        packedSwitch6(Integer.MAX_VALUE);
+
+        System.out.println("packed7");
+        for (int i = -1; i < 17; i++) {
+            packedSwitch7(i);
+        }
+
+
+        System.out.println("sparse");
+        for (int i = -2; i < 4; i++) {
+            sparseSwitch(i);
+        }
+        sparseSwitch(Integer.MIN_VALUE);
+        sparseSwitch(Integer.MAX_VALUE);
+
+        System.out.println("sparse2");
+        for (int i = -2; i < 3; i++) {
+            sparseSwitch2(i);
+        }
+        sparseSwitch2(Integer.MIN_VALUE);
+        sparseSwitch2(Integer.MAX_VALUE);
+
+        System.out.println("sparse3");
+        for (int i = -2; i < 7; i++) {
+            sparseSwitch3(i);
+        }
+        sparseSwitch3(Integer.MIN_VALUE);
+        sparseSwitch3(Integer.MAX_VALUE);
+
+        System.out.println("sparse4");
+        for (int i = Integer.MAX_VALUE - 2; i > 0; i++) {
+            sparseSwitch4(i);
+        }
+        sparseSwitch4(Integer.MIN_VALUE);
+
+        System.out.println("sparse5");
+        for (int i = Integer.MIN_VALUE; i < Integer.MIN_VALUE + 2; i++) {
+            sparseSwitch5(i);
+        }
+        sparseSwitch5(Integer.MAX_VALUE);
+
+        System.out.println("sparse7");
+        for (int i = -1; i < 17; i++) {
+            sparseSwitch7(i);
+        }
+
+        // Older tests.
+
         int a = 1;
 
         switch (a) {
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 32fbf2d..238e73a 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -292,8 +292,8 @@
                     break;
             }
 
-            strong[depth] = funStr;
             weak[depth] = new WeakReference(funStr);
+            strong[depth] = funStr;
             if (depth+1 < MAX_DEPTH)
                 dive(depth+1, iteration+1);
             else
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 56972ff..0e90c4d 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -34,6 +34,7 @@
     test_Math_max_F();
     test_Math_min_D();
     test_Math_max_D();
+    test_Math_sqrt();
     test_Math_ceil();
     test_Math_floor();
     test_Math_rint();
@@ -54,6 +55,7 @@
     test_StrictMath_max_F();
     test_StrictMath_min_D();
     test_StrictMath_max_D();
+    test_StrictMath_sqrt();
     test_StrictMath_ceil();
     test_StrictMath_floor();
     test_StrictMath_rint();
@@ -119,14 +121,29 @@
     }
   }
 
+  // Break up the charAt tests. The optimizing compiler doesn't optimize methods with try-catch yet,
+  // so we need to separate out the tests that are expected to throw exception
+
   public static void test_String_charAt() {
-    String testStr = "Now is the time";
+    String testStr = "Now is the time to test some stuff";
 
-    Assert.assertEquals('N', testStr.charAt(0));
-    Assert.assertEquals('o', testStr.charAt(1));
-    Assert.assertEquals(' ', testStr.charAt(10));
-    Assert.assertEquals('e', testStr.charAt(testStr.length()-1));
+    Assert.assertEquals(testStr.length() - 1, 33);  // 33 = testStr.length()-1 as a constant.
+    Assert.assertEquals('f', testStr.charAt(33));
 
+    test_String_charAt(testStr, 'N', 'o', ' ', 'f');
+    test_String_charAt(testStr.substring(3,15), ' ', 'i', 'm', 'e');
+  }
+  public static void test_String_charAt(String testStr, char a, char b, char c, char d) {
+    Assert.assertEquals(a, testStr.charAt(0));
+    Assert.assertEquals(b, testStr.charAt(1));
+    Assert.assertEquals(c, testStr.charAt(10));
+    Assert.assertEquals(d, testStr.charAt(testStr.length()-1));
+
+    test_String_charAtExc(testStr);
+    test_String_charAtExc2(testStr);
+  }
+
+  private static void test_String_charAtExc(String testStr) {
     try {
       testStr.charAt(-1);
       Assert.fail();
@@ -137,6 +154,44 @@
       Assert.fail();
     } catch (StringIndexOutOfBoundsException expected) {
     }
+    try {
+      if (testStr.length() == 34) {
+          testStr.charAt(34);  // 34 = "Now is the time to test some stuff".length()
+      } else {
+          Assert.assertEquals(testStr.length(), 12);  // 12 = " is the time".length()
+          testStr.charAt(12);
+      }
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      test_String_charAt_inner(testStr, -1);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      test_String_charAt_inner(testStr, 80);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      if (testStr.length() == 34) {
+        // 34 = "Now is the time to test some stuff".length()
+        test_String_charAt_inner(testStr, 34);
+      } else {
+        Assert.assertEquals(testStr.length(), 12);  // 12 = " is the time".length()
+        test_String_charAt_inner(testStr, 12);
+      }
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+
+    String strEmpty = "";
+    try {
+      strEmpty.charAt(0);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
 
     String strNull = null;
     try {
@@ -146,6 +201,32 @@
     }
   }
 
+  private static char test_String_charAt_inner(String s, int index) {
+    // Using non-constant index here (assuming that this method wasn't inlined).
+    return s.charAt(index);
+  }
+
+  private static void test_String_charAtExc2(String testStr) {
+    try {
+      test_String_charAtExc3(testStr);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+    try {
+      test_String_charAtExc4(testStr);
+      Assert.fail();
+    } catch (StringIndexOutOfBoundsException expected) {
+    }
+  }
+
+  private static void test_String_charAtExc3(String testStr) {
+    Assert.assertEquals('N', testStr.charAt(-1));
+  }
+
+  private static void test_String_charAtExc4(String testStr) {
+    Assert.assertEquals('N', testStr.charAt(100));
+  }
+
   static int start;
   private static int[] negIndex = { -100000 };
   public static void test_String_indexOf() {
@@ -276,6 +357,7 @@
   }
 
   public static void test_Math_abs_I() {
+    Math.abs(-1);
     Assert.assertEquals(Math.abs(0), 0);
     Assert.assertEquals(Math.abs(123), 123);
     Assert.assertEquals(Math.abs(-123), 123);
@@ -286,15 +368,18 @@
   }
 
   public static void test_Math_abs_J() {
+    Math.abs(-1L);
     Assert.assertEquals(Math.abs(0L), 0L);
     Assert.assertEquals(Math.abs(123L), 123L);
     Assert.assertEquals(Math.abs(-123L), 123L);
     Assert.assertEquals(Math.abs(Long.MAX_VALUE), Long.MAX_VALUE);
     Assert.assertEquals(Math.abs(Long.MIN_VALUE), Long.MIN_VALUE);
     Assert.assertEquals(Math.abs(Long.MIN_VALUE - 1), Long.MAX_VALUE);
+    Assert.assertEquals(Math.abs(2147483648L), 2147483648L);
   }
 
   public static void test_Math_min_I() {
+    Math.min(1, 0);
     Assert.assertEquals(Math.min(0, 0), 0);
     Assert.assertEquals(Math.min(1, 0), 0);
     Assert.assertEquals(Math.min(0, 1), 0);
@@ -304,6 +389,7 @@
   }
 
   public static void test_Math_max_I() {
+    Math.max(1, 0);
     Assert.assertEquals(Math.max(0, 0), 0);
     Assert.assertEquals(Math.max(1, 0), 1);
     Assert.assertEquals(Math.max(0, 1), 1);
@@ -313,6 +399,7 @@
   }
 
   public static void test_Math_min_J() {
+    Math.min(1L, 0L);
     Assert.assertEquals(Math.min(0L, 0L), 0L);
     Assert.assertEquals(Math.min(1L, 0L), 0L);
     Assert.assertEquals(Math.min(0L, 1L), 0L);
@@ -322,6 +409,7 @@
   }
 
   public static void test_Math_max_J() {
+    Math.max(1L, 0L);
     Assert.assertEquals(Math.max(0L, 0L), 0L);
     Assert.assertEquals(Math.max(1L, 0L), 1L);
     Assert.assertEquals(Math.max(0L, 1L), 1L);
@@ -331,6 +419,7 @@
   }
 
   public static void test_Math_min_F() {
+    Math.min(1.0f, Float.NaN);
     Assert.assertTrue(Float.isNaN(Math.min(1.0f, Float.NaN)));
     Assert.assertTrue(Float.isNaN(Math.min(Float.NaN, 1.0f)));
     Assert.assertEquals(Math.min(-0.0f, 0.0f), -0.0f);
@@ -345,6 +434,7 @@
   }
 
   public static void test_Math_max_F() {
+    Math.max(1.0f, Float.NaN);
     Assert.assertTrue(Float.isNaN(Math.max(1.0f, Float.NaN)));
     Assert.assertTrue(Float.isNaN(Math.max(Float.NaN, 1.0f)));
     Assert.assertEquals(Math.max(-0.0f, 0.0f), 0.0f);
@@ -359,6 +449,7 @@
   }
 
   public static void test_Math_min_D() {
+    Math.min(1.0d, Double.NaN);
     Assert.assertTrue(Double.isNaN(Math.min(1.0d, Double.NaN)));
     Assert.assertTrue(Double.isNaN(Math.min(Double.NaN, 1.0d)));
     Assert.assertEquals(Math.min(-0.0d, 0.0d), -0.0d);
@@ -373,6 +464,7 @@
   }
 
   public static void test_Math_max_D() {
+    Math.max(1.0d, Double.NaN);
     Assert.assertTrue(Double.isNaN(Math.max(1.0d, Double.NaN)));
     Assert.assertTrue(Double.isNaN(Math.max(Double.NaN, 1.0d)));
     Assert.assertEquals(Math.max(-0.0d, 0.0d), 0.0d);
@@ -386,7 +478,15 @@
     Assert.assertEquals(Math.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE);
   }
 
+  public static void test_Math_sqrt() {
+    Math.sqrt(+4.0);
+    Assert.assertEquals(Math.sqrt(+4.0), +2.0d, 0.0);
+    Assert.assertEquals(Math.sqrt(+49.0), +7.0d, 0.0);
+    Assert.assertEquals(Math.sqrt(+1.44), +1.2d, 0.0);
+  }
+
   public static void test_Math_ceil() {
+    Math.ceil(-0.9);
     Assert.assertEquals(Math.ceil(+0.0), +0.0d, 0.0);
     Assert.assertEquals(Math.ceil(-0.0), -0.0d, 0.0);
     Assert.assertEquals(Math.ceil(-0.9), -0.0d, 0.0);
@@ -408,6 +508,7 @@
   }
 
   public static void test_Math_floor() {
+    Math.floor(+2.1);
     Assert.assertEquals(Math.floor(+0.0), +0.0d, 0.0);
     Assert.assertEquals(Math.floor(-0.0), -0.0d, 0.0);
     Assert.assertEquals(Math.floor(+2.0), +2.0d, 0.0);
@@ -426,6 +527,7 @@
   }
 
   public static void test_Math_rint() {
+    Math.rint(+2.1);
     Assert.assertEquals(Math.rint(+0.0), +0.0d, 0.0);
     Assert.assertEquals(Math.rint(-0.0), -0.0d, 0.0);
     Assert.assertEquals(Math.rint(+2.0), +2.0d, 0.0);
@@ -444,6 +546,7 @@
   }
 
   public static void test_Math_round_D() {
+    Math.round(2.1d);
     Assert.assertEquals(Math.round(+0.0d), (long)+0.0);
     Assert.assertEquals(Math.round(-0.0d), (long)+0.0);
     Assert.assertEquals(Math.round(2.0d), 2l);
@@ -465,6 +568,7 @@
   }
 
   public static void test_Math_round_F() {
+    Math.round(2.1f);
     Assert.assertEquals(Math.round(+0.0f), (int)+0.0);
     Assert.assertEquals(Math.round(-0.0f), (int)+0.0);
     Assert.assertEquals(Math.round(2.0f), 2);
@@ -485,6 +589,7 @@
   }
 
   public static void test_StrictMath_abs_I() {
+    StrictMath.abs(-1);
     Assert.assertEquals(StrictMath.abs(0), 0);
     Assert.assertEquals(StrictMath.abs(123), 123);
     Assert.assertEquals(StrictMath.abs(-123), 123);
@@ -495,6 +600,7 @@
   }
 
   public static void test_StrictMath_abs_J() {
+    StrictMath.abs(-1L);
     Assert.assertEquals(StrictMath.abs(0L), 0L);
     Assert.assertEquals(StrictMath.abs(123L), 123L);
     Assert.assertEquals(StrictMath.abs(-123L), 123L);
@@ -504,6 +610,7 @@
   }
 
   public static void test_StrictMath_min_I() {
+    StrictMath.min(1, 0);
     Assert.assertEquals(StrictMath.min(0, 0), 0);
     Assert.assertEquals(StrictMath.min(1, 0), 0);
     Assert.assertEquals(StrictMath.min(0, 1), 0);
@@ -513,6 +620,7 @@
   }
 
   public static void test_StrictMath_max_I() {
+    StrictMath.max(1, 0);
     Assert.assertEquals(StrictMath.max(0, 0), 0);
     Assert.assertEquals(StrictMath.max(1, 0), 1);
     Assert.assertEquals(StrictMath.max(0, 1), 1);
@@ -522,6 +630,7 @@
   }
 
   public static void test_StrictMath_min_J() {
+    StrictMath.min(1L, 0L);
     Assert.assertEquals(StrictMath.min(0L, 0L), 0L);
     Assert.assertEquals(StrictMath.min(1L, 0L), 0L);
     Assert.assertEquals(StrictMath.min(0L, 1L), 0L);
@@ -531,6 +640,7 @@
   }
 
   public static void test_StrictMath_max_J() {
+    StrictMath.max(1L, 0L);
     Assert.assertEquals(StrictMath.max(0L, 0L), 0L);
     Assert.assertEquals(StrictMath.max(1L, 0L), 1L);
     Assert.assertEquals(StrictMath.max(0L, 1L), 1L);
@@ -540,6 +650,7 @@
   }
 
   public static void test_StrictMath_min_F() {
+    StrictMath.min(1.0f, Float.NaN);
     Assert.assertTrue(Float.isNaN(StrictMath.min(1.0f, Float.NaN)));
     Assert.assertTrue(Float.isNaN(StrictMath.min(Float.NaN, 1.0f)));
     Assert.assertEquals(StrictMath.min(-0.0f, 0.0f), -0.0f);
@@ -554,6 +665,7 @@
   }
 
   public static void test_StrictMath_max_F() {
+    StrictMath.max(1.0f, Float.NaN);
     Assert.assertTrue(Float.isNaN(StrictMath.max(1.0f, Float.NaN)));
     Assert.assertTrue(Float.isNaN(StrictMath.max(Float.NaN, 1.0f)));
     Assert.assertEquals(StrictMath.max(-0.0f, 0.0f), 0.0f);
@@ -568,6 +680,7 @@
   }
 
   public static void test_StrictMath_min_D() {
+    StrictMath.min(1.0d, Double.NaN);
     Assert.assertTrue(Double.isNaN(StrictMath.min(1.0d, Double.NaN)));
     Assert.assertTrue(Double.isNaN(StrictMath.min(Double.NaN, 1.0d)));
     Assert.assertEquals(StrictMath.min(-0.0d, 0.0d), -0.0d);
@@ -582,6 +695,7 @@
   }
 
   public static void test_StrictMath_max_D() {
+    StrictMath.max(1.0d, Double.NaN);
     Assert.assertTrue(Double.isNaN(StrictMath.max(1.0d, Double.NaN)));
     Assert.assertTrue(Double.isNaN(StrictMath.max(Double.NaN, 1.0d)));
     Assert.assertEquals(StrictMath.max(-0.0d, 0.0d), 0.0d);
@@ -595,7 +709,15 @@
     Assert.assertEquals(StrictMath.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE);
   }
 
+  public static void test_StrictMath_sqrt() {
+    StrictMath.sqrt(+4.0);
+    Assert.assertEquals(StrictMath.sqrt(+4.0), +2.0d, 0.0);
+    Assert.assertEquals(StrictMath.sqrt(+49.0), +7.0d, 0.0);
+    Assert.assertEquals(StrictMath.sqrt(+1.44), +1.2d, 0.0);
+  }
+
   public static void test_StrictMath_ceil() {
+    StrictMath.ceil(-0.9);
     Assert.assertEquals(StrictMath.ceil(+0.0), +0.0d, 0.0);
     Assert.assertEquals(StrictMath.ceil(-0.0), -0.0d, 0.0);
     Assert.assertEquals(StrictMath.ceil(-0.9), -0.0d, 0.0);
@@ -617,6 +739,7 @@
   }
 
   public static void test_StrictMath_floor() {
+    StrictMath.floor(+2.1);
     Assert.assertEquals(StrictMath.floor(+0.0), +0.0d, 0.0);
     Assert.assertEquals(StrictMath.floor(-0.0), -0.0d, 0.0);
     Assert.assertEquals(StrictMath.floor(+2.0), +2.0d, 0.0);
@@ -635,6 +758,7 @@
   }
 
   public static void test_StrictMath_rint() {
+    StrictMath.rint(+2.1);
     Assert.assertEquals(StrictMath.rint(+0.0), +0.0d, 0.0);
     Assert.assertEquals(StrictMath.rint(-0.0), -0.0d, 0.0);
     Assert.assertEquals(StrictMath.rint(+2.0), +2.0d, 0.0);
@@ -653,6 +777,7 @@
   }
 
   public static void test_StrictMath_round_D() {
+    StrictMath.round(2.1d);
     Assert.assertEquals(StrictMath.round(+0.0d), (long)+0.0);
     Assert.assertEquals(StrictMath.round(-0.0d), (long)+0.0);
     Assert.assertEquals(StrictMath.round(2.0d), 2l);
@@ -674,6 +799,7 @@
   }
 
   public static void test_StrictMath_round_F() {
+    StrictMath.round(2.1f);
     Assert.assertEquals(StrictMath.round(+0.0f), (int)+0.0);
     Assert.assertEquals(StrictMath.round(-0.0f), (int)+0.0);
     Assert.assertEquals(StrictMath.round(2.0f), 2);
@@ -694,6 +820,7 @@
   }
 
   public static void test_Float_floatToRawIntBits() {
+    Float.floatToRawIntBits(-1.0f);
     Assert.assertEquals(Float.floatToRawIntBits(-1.0f), 0xbf800000);
     Assert.assertEquals(Float.floatToRawIntBits(0.0f), 0);
     Assert.assertEquals(Float.floatToRawIntBits(1.0f), 0x3f800000);
@@ -703,6 +830,7 @@
   }
 
   public static void test_Float_intBitsToFloat() {
+    Float.intBitsToFloat(0xbf800000);
     Assert.assertEquals(Float.intBitsToFloat(0xbf800000), -1.0f);
     Assert.assertEquals(Float.intBitsToFloat(0x00000000), 0.0f);
     Assert.assertEquals(Float.intBitsToFloat(0x3f800000), 1.0f);
@@ -712,6 +840,7 @@
   }
 
   public static void test_Double_doubleToRawLongBits() {
+    Double.doubleToRawLongBits(-1.0);
     Assert.assertEquals(Double.doubleToRawLongBits(-1.0), 0xbff0000000000000L);
     Assert.assertEquals(Double.doubleToRawLongBits(0.0), 0x0000000000000000L);
     Assert.assertEquals(Double.doubleToRawLongBits(1.0), 0x3ff0000000000000L);
@@ -721,6 +850,7 @@
   }
 
   public static void test_Double_longBitsToDouble() {
+    Double.longBitsToDouble(0xbff0000000000000L);
     Assert.assertEquals(Double.longBitsToDouble(0xbff0000000000000L), -1.0);
     Assert.assertEquals(Double.longBitsToDouble(0x0000000000000000L), 0.0);
     Assert.assertEquals(Double.longBitsToDouble(0x3ff0000000000000L), 1.0);
@@ -730,6 +860,7 @@
   }
 
   public static void test_Short_reverseBytes() {
+      Short.reverseBytes((short)0x1357);
       Assert.assertEquals(Short.reverseBytes((short)0x0000), (short)0x0000);
       Assert.assertEquals(Short.reverseBytes((short)0xffff), (short)0xffff);
       Assert.assertEquals(Short.reverseBytes((short)0x8000), (short)0x0080);
@@ -741,6 +872,7 @@
   }
 
   public static void test_Integer_reverseBytes() {
+      Integer.reverseBytes(0x13579bdf);
       Assert.assertEquals(Integer.reverseBytes(0x00000000), 0x00000000);
       Assert.assertEquals(Integer.reverseBytes(0xffffffff), 0xffffffff);
       Assert.assertEquals(Integer.reverseBytes(0x80000000), 0x00000080);
@@ -750,6 +882,7 @@
   }
 
   public static void test_Long_reverseBytes() {
+      Long.reverseBytes(0x13579bdf2468ace0L);
       Assert.assertEquals(Long.reverseBytes(0x0000000000000000L), 0x0000000000000000L);
       Assert.assertEquals(Long.reverseBytes(0xffffffffffffffffL), 0xffffffffffffffffL);
       Assert.assertEquals(Long.reverseBytes(0x8000000000000000L), 0x0000000000000080L);
@@ -758,6 +891,7 @@
   }
 
   public static void test_Integer_reverse() {
+    Integer.reverse(0x12345678);
     Assert.assertEquals(Integer.reverse(1), 0x80000000);
     Assert.assertEquals(Integer.reverse(-1), 0xffffffff);
     Assert.assertEquals(Integer.reverse(0), 0);
@@ -768,6 +902,7 @@
   }
 
   public static void test_Long_reverse() {
+    Long.reverse(0x1234567812345678L);
     Assert.assertEquals(Long.reverse(1L), 0x8000000000000000L);
     Assert.assertEquals(Long.reverse(-1L), 0xffffffffffffffffL);
     Assert.assertEquals(Long.reverse(0L), 0L);
@@ -822,6 +957,7 @@
     b[1] = 0x12;
     b[2] = 0x11;
     long address = (long)address_of.invoke(runtime, b);
+    peek_short.invoke(null, address, false);
     Assert.assertEquals((short)peek_short.invoke(null, address, false), 0x1213);  // Aligned read
     Assert.assertEquals((short)peek_short.invoke(null, address + 1, false), 0x1112);  // Unaligned read
   }
@@ -834,6 +970,7 @@
     b[3] = 0x12;
     b[4] = 0x11;
     long address = (long)address_of.invoke(runtime, b);
+    peek_int.invoke(null, address, false);
     Assert.assertEquals((int)peek_int.invoke(null, address, false), 0x12131415);
     Assert.assertEquals((int)peek_int.invoke(null, address + 1, false), 0x11121314);
   }
@@ -850,6 +987,7 @@
     b[7] = 0x12;
     b[8] = 0x11;
     long address = (long)address_of.invoke(runtime, b);
+    peek_long.invoke(null, address, false);
     Assert.assertEquals((long)peek_long.invoke(null, address, false), 0x1213141516171819L);
     Assert.assertEquals((long)peek_long.invoke(null, address + 1, false), 0x1112131415161718L);
   }
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index 7f24b1b..4d781c3 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -28,14 +28,22 @@
         testMethodTracing();
     }
 
-    private static void testMethodTracing() throws Exception {
-        File tempFile;
+    private static File createTempFile() throws Exception {
         try {
-            tempFile = File.createTempFile("test", ".trace");
+            return  File.createTempFile("test", ".trace");
         } catch (IOException e) {
-            System.setProperty("java.io.tmpdir", "/sdcard");
-            tempFile = File.createTempFile("test", ".trace");
+            System.setProperty("java.io.tmpdir", "/data/local/tmp");
+            try {
+                return File.createTempFile("test", ".trace");
+            } catch (IOException e2) {
+                System.setProperty("java.io.tmpdir", "/sdcard");
+                return File.createTempFile("test", ".trace");
+            }
         }
+    }
+
+    private static void testMethodTracing() throws Exception {
+        File tempFile = createTempFile();
         tempFile.deleteOnExit();
         String tempFileName = tempFile.getPath();
 
diff --git a/test/109-suspend-check/src/Main.java b/test/109-suspend-check/src/Main.java
index ae10576..cd5130d 100644
--- a/test/109-suspend-check/src/Main.java
+++ b/test/109-suspend-check/src/Main.java
@@ -21,10 +21,15 @@
         System.out.println("Running (" + TEST_TIME + " seconds) ...");
         InfiniteForLoop forLoop = new InfiniteForLoop();
         InfiniteWhileLoop whileLoop = new InfiniteWhileLoop();
+        InfiniteWhileLoopWithIntrinsic whileLoopWithIntrinsic =
+            new InfiniteWhileLoopWithIntrinsic();
+        InfiniteDoWhileLoopWithLong doWhileLoopWithLong = new InfiniteDoWhileLoopWithLong();
         InfiniteDoWhileLoop doWhileLoop = new InfiniteDoWhileLoop();
         MakeGarbage garbage = new MakeGarbage();
         forLoop.start();
         whileLoop.start();
+        whileLoopWithIntrinsic.start();
+        doWhileLoopWithLong.start();
         doWhileLoop.start();
         garbage.start();
         for (int i = 0; i < TEST_TIME; i++) {
@@ -34,6 +39,8 @@
         }
         forLoop.stopNow();
         whileLoop.stopNow();
+        whileLoopWithIntrinsic.stopNow();
+        doWhileLoopWithLong.stopNow();
         doWhileLoop.stopNow();
         garbage.stopNow();
         System.out.println("Done.");
@@ -48,6 +55,35 @@
     }
 }
 
+class InfiniteWhileLoopWithIntrinsic extends Thread {
+  volatile private boolean keepGoing = true;
+  private String[] strings = { "a", "b", "c", "d" };
+  private int sum = 0;
+  public void run() {
+    int i = 0;
+    while (keepGoing) {
+      i++;
+      sum += strings[i & 3].length();
+    }
+  }
+  public void stopNow() {
+    keepGoing = false;
+  }
+}
+
+class InfiniteDoWhileLoopWithLong extends Thread {
+  volatile private long keepGoing = 7L;
+  public void run() {
+    int i = 0;
+    do {
+      i++;
+    } while (keepGoing >= 4L);
+  }
+  public void stopNow() {
+    keepGoing = 1L;
+  }
+}
+
 class InfiniteWhileLoop extends Thread {
   volatile private boolean keepGoing = true;
   public void run() {
diff --git a/test/114-ParallelGC/src/Main.java b/test/114-ParallelGC/src/Main.java
index 8e2519d..46029cf 100644
--- a/test/114-ParallelGC/src/Main.java
+++ b/test/114-ParallelGC/src/Main.java
@@ -16,51 +16,36 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 public class Main implements Runnable {
 
-    public final static long TIMEOUT_VALUE = 5;  // Timeout in minutes.
-    public final static long MAX_SIZE = 1000;  // Maximum size of array-list to allocate.
+    // Timeout in minutes. Make it larger than the run-test timeout to get a native thread dump by
+    // ART on timeout when running on the host.
+    private final static long TIMEOUT_VALUE = 7;
+
+    private final static long MAX_SIZE = 1000;  // Maximum size of array-list to allocate.
+
+    private final static int THREAD_COUNT = 16;
+
+    // Use a couple of different forms of synchronizing to test some of these...
+    private final static AtomicInteger counter = new AtomicInteger();
+    private final static Object gate = new Object();
+    private volatile static int waitCount = 0;
 
     public static void main(String[] args) throws Exception {
-        Thread[] threads = new Thread[16];
+        Thread[] threads = new Thread[THREAD_COUNT];
 
-        // Use a cyclic system of synchronous queues to pass a boolean token around.
-        //
-        // The combinations are:
-        //
-        // Worker receives:    true     false    false    true
-        // Worker has OOM:     false    false    true     true
-        //    |
-        //    v
-        // Value to pass:      true     false    false    false
-        // Exit out of loop:   false    true     true     true
-        // Wait on in queue:   true     false    false    true
-        //
-        // Finally, the workers are supposed to wait on the barrier to synchronize the GC run.
-
-        CyclicBarrier barrier = new CyclicBarrier(threads.length);
-        List<SynchronousQueue<Boolean>> queues = new ArrayList<SynchronousQueue<Boolean>>(
-            threads.length);
-        for (int i = 0; i < threads.length; i++) {
-            queues.add(new SynchronousQueue<Boolean>());
-        }
+        // This barrier is used to synchronize the threads starting to allocate.
+        // Note: Even though a barrier is not allocation-free, this one is fine, as it will be used
+        //       before filling the heap.
+        CyclicBarrier startBarrier = new CyclicBarrier(threads.length);
 
         for (int i = 0; i < threads.length; i++) {
-            threads[i] = new Thread(new Main(i, queues.get(i), queues.get((i + 1) % threads.length),
-                                             barrier));
+            threads[i] = new Thread(new Main(startBarrier));
+            threads[i].start();
         }
-        for (Thread thread : threads) {
-            thread.start();
-        }
-
-        // Push off the cycle.
-        checkTimeout(queues.get(0).offer(Boolean.TRUE, TIMEOUT_VALUE, TimeUnit.MINUTES));
 
         // Wait for the threads to finish.
         for (Thread thread : threads) {
@@ -68,86 +53,89 @@
         }
 
         // Allocate objects to definitely run GC before quitting.
+        ArrayList<Object> l = new ArrayList<Object>();
         try {
-            for (int i = 0; i < 1000; i++) {
-                new ArrayList<Object>(i);
+            for (int i = 0; i < 100000; i++) {
+                l.add(new ArrayList<Object>(i));
             }
         } catch (OutOfMemoryError oom) {
         }
+        // Make the (outer) ArrayList unreachable. Note it may still
+        // be reachable under an interpreter or a compiler without a
+        // liveness analysis.
+        l = null;
+        new ArrayList<Object>(50);
     }
 
-    private static void checkTimeout(Object o) {
-        checkTimeout(o != null);
+    private Main(CyclicBarrier startBarrier) {
+        this.startBarrier = startBarrier;
     }
 
-    private static void checkTimeout(boolean b) {
-        if (!b) {
-            // Something went wrong.
-            System.out.println("Bad things happened, timeout.");
-            System.exit(1);
-        }
-    }
-
-    private final int id;
-    private final SynchronousQueue<Boolean> waitOn;
-    private final SynchronousQueue<Boolean> pushTo;
-    private final CyclicBarrier finalBarrier;
-
-    private Main(int id, SynchronousQueue<Boolean> waitOn, SynchronousQueue<Boolean> pushTo,
-        CyclicBarrier finalBarrier) {
-        this.id = id;
-        this.waitOn = waitOn;
-        this.pushTo = pushTo;
-        this.finalBarrier = finalBarrier;
-    }
+    private ArrayList<Object> store;
+    private CyclicBarrier startBarrier;
 
     public void run() {
         try {
             work();
-        } catch (Exception exc) {
-            // Any exception is bad.
-            exc.printStackTrace(System.err);
+        } catch (Throwable t) {
+            // Any exception or error getting here is bad.
+            try {
+                // May need allocations...
+                t.printStackTrace(System.err);
+            } catch (Throwable tInner) {
+            }
             System.exit(1);
         }
     }
 
-    public void work() throws BrokenBarrierException, InterruptedException, TimeoutException {
+    private void work() throws Exception {
+        // Any exceptions except an OOME in the allocation loop are bad and handed off to the
+        // caller which should abort the whole runtime.
+
         ArrayList<Object> l = new ArrayList<Object>();
+        store = l;  // Keep it alive.
 
-        // Main loop.
-        for (int i = 0; ; i++) {
-          Boolean receivedB = waitOn.poll(TIMEOUT_VALUE, TimeUnit.MINUTES);
-          checkTimeout(receivedB);
-          boolean received = receivedB;
+        // Wait for the start signal.
+        startBarrier.await(TIMEOUT_VALUE, java.util.concurrent.TimeUnit.MINUTES);
 
-          // This is the first stage, try to allocate up till MAX_SIZE.
-          boolean oom = i >= MAX_SIZE;
-          try {
-            l.add(new ArrayList<Object>(i));
-          } catch (OutOfMemoryError oome) {
-            oom = true;
-          }
-
-          if (!received || oom) {
-            // First stage, always push false.
-            checkTimeout(pushTo.offer(Boolean.FALSE, TIMEOUT_VALUE, TimeUnit.MINUTES));
-
-            // If we received true, wait for the false to come around.
-            if (received) {
-              checkTimeout(waitOn.poll(TIMEOUT_VALUE, TimeUnit.MINUTES));
+        // Allocate.
+        try {
+            for (int i = 0; i < MAX_SIZE; i++) {
+                l.add(new ArrayList<Object>(i));
             }
-
-            // Break out of the loop.
-            break;
-          } else {
-            // Pass on true.
-            checkTimeout(pushTo.offer(Boolean.TRUE, TIMEOUT_VALUE, TimeUnit.MINUTES));
-          }
+        } catch (OutOfMemoryError oome) {
+            // Fine, we're done.
         }
 
-        // We have reached the final point. Wait on the barrier, but at most a minute.
-        finalBarrier.await(TIMEOUT_VALUE, TimeUnit.MINUTES);
+        // Atomically increment the counter and check whether we were last.
+        int number = counter.incrementAndGet();
 
-        // Done.
+        if (number < THREAD_COUNT) {
+            // Not last.
+            synchronized (gate) {
+                // Increment the wait counter.
+                waitCount++;
+                gate.wait(TIMEOUT_VALUE * 1000 * 60);
+            }
+        } else {
+            // Last. Wait until waitCount == THREAD_COUNT - 1.
+            for (int loops = 0; ; loops++) {
+                synchronized (gate) {
+                    if (waitCount == THREAD_COUNT - 1) {
+                        // OK, everyone's waiting. Notify and break out.
+                        gate.notifyAll();
+                        break;
+                    } else if (loops > 40) {
+                        // 1s wait, too many tries.
+                        System.out.println("Waited too long for the last thread.");
+                        System.exit(1);
+                    }
+                }
+                // Wait a bit.
+                Thread.sleep(25);
+            }
+        }
+
+        store = null;  // Allow GC to reclaim it.
     }
 }
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
index 2df6705..9e5c7dd 100755
--- a/test/116-nodex2oat/run
+++ b/test/116-nodex2oat/run
@@ -14,11 +14,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Remove prebuild from the flags, this test is for testing not having oat files.
-flags="${@/--prebuild/}"
+flags="${@}"
 
-# Use the non-prebuild script.
-RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}"
+# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
+# flag isn't set, or complain.
+# Note: prebuild is the default.
+if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
+  echo "Test 116-nodex2oat cannot run in prebuild mode."
+  exit 1
+fi
 
 # Make sure we can run without an oat file,
 echo "Run -Xnodex2oat"
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
index a7c96a0..c749c74 100755
--- a/test/117-nopatchoat/run
+++ b/test/117-nopatchoat/run
@@ -16,10 +16,23 @@
 
 # ensure flags includes prebuild and relocate. It doesn't make sense unless we
 # have a oat file we want to relocate.
-# TODO Unfortunately we have no way to force prebuild on for both host and target (or skip if not on).
-flags="${@/--relocate/}"
-flags="${flags/--no-relocate/}"
-flags="${flags} --relocate"
+flags="$@"
+
+# This test is supposed to test with oat files. Make sure that the no-prebuild flag isn't set,
+# or complain.
+# Note: prebuild is the default.
+if [[ "${flags}" == *--no-prebuild* ]] ; then
+  echo "Test 117-nopatchoat is not intended to run in no-prebuild mode."
+  exit 1
+fi
+
+# This test is supposed to test relocation. Make sure that the no-relocate flag isn't set,
+# or complain.
+# Note: relocate is the default.
+if [[ "${flags}" == *--no-relocate* ]] ; then
+  echo "Test 117-nopatchoat is not intended to run in no-relocate mode."
+  exit 1
+fi
 
 # Make sure we can run without relocation
 echo "Run without dex2oat/patchoat"
diff --git a/test/118-noimage-dex2oat/expected.txt b/test/118-noimage-dex2oat/expected.txt
index 6825fae..bcb695d 100644
--- a/test/118-noimage-dex2oat/expected.txt
+++ b/test/118-noimage-dex2oat/expected.txt
@@ -1,6 +1,9 @@
 Run -Xnoimage-dex2oat
 Has image is false, is image dex2oat enabled is false, is BOOTCLASSPATH on disk is false.
+testB18485243 PASS
 Run -Ximage-dex2oat
 Has image is true, is image dex2oat enabled is true, is BOOTCLASSPATH on disk is true.
+testB18485243 PASS
 Run default
 Has image is true, is image dex2oat enabled is true, is BOOTCLASSPATH on disk is true.
+testB18485243 PASS
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 92a4ec2..2037797 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -14,17 +14,26 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+flags="$@"
+
+# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
+# flag isn't set, or complain.
+# Note: prebuild is the default.
+if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
+  echo "Test 118-noimage-dex2oat cannot run in prebuild mode."
+  exit 1
+fi
+
 # Force relocation otherwise we will just use the already created core.oat/art pair.
-flags="${@/--no-relocate/--relocate}"
+# Note: relocate is the default.
+if [[ "${flags}" == *--no-relocate* ]] ; then
+  echo "Test 118-noimage-dex2oat is not intended to run in no-relocate mode."
+  exit 1
+fi
 
-# Use the non-prebuild script.
-RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}"
-
-if [ $(basename $RUN) == 'host-run-test-jar' ]; then
+if [[ $@ == *--host* ]]; then
     framework="${ANDROID_HOST_OUT}/framework"
     bpath_suffix="-hostdex"
-    # Remove prebuild from the flags, this test is for testing not having oat files.
-    flags="${flags/--prebuild/}"
 else
     framework="/system/framework"
     bpath_suffix=""
diff --git a/test/118-noimage-dex2oat/smali/b_18485243.smali b/test/118-noimage-dex2oat/smali/b_18485243.smali
new file mode 100644
index 0000000..41fbc74
--- /dev/null
+++ b/test/118-noimage-dex2oat/smali/b_18485243.smali
@@ -0,0 +1,22 @@
+.class public LB18485243;
+.super Ljava/lang/Object;
+.source "b_18485243.smali"
+
+.method public constructor <init>()V
+    .registers 2
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
+
+.method private static toInt()I
+    .registers 1
+    const v0, 0
+    return v0
+.end method
+
+.method public run()I
+    .registers 3
+    invoke-direct {p0}, LB18485243;->toInt()I
+    move-result v0
+    return v0
+.end method
diff --git a/test/118-noimage-dex2oat/src/Main.java b/test/118-noimage-dex2oat/src/Main.java
index c83b84d..9bf5bb3 100644
--- a/test/118-noimage-dex2oat/src/Main.java
+++ b/test/118-noimage-dex2oat/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 public class Main {
@@ -36,6 +37,8 @@
     } else if (!hasImage && isBootClassPathOnDisk) {
       throw new Error("Image with dex2oat enabled runs without an image file");
     }
+
+    testB18485243();
   }
 
   static {
@@ -67,4 +70,19 @@
       return (boolean) isBootClassPathOnDiskMethod.invoke(null, instructionSet);
     }
   }
+
+  private static void testB18485243() throws Exception {
+    Class<?> k = Class.forName("B18485243");
+    Object o = k.newInstance();
+    Method m = k.getDeclaredMethod("run");
+    try {
+      m.invoke(o);
+    } catch (InvocationTargetException e) {
+      Throwable actual = e.getTargetException();
+      if (!(actual instanceof IncompatibleClassChangeError)) {
+        throw new AssertionError("Expected IncompatibleClassChangeError", actual);
+      }
+    }
+    System.out.println("testB18485243 PASS");
+  }
 }
diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run
index 745b0c9..c409cbb 100644
--- a/test/119-noimage-patchoat/run
+++ b/test/119-noimage-patchoat/run
@@ -14,10 +14,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Force relocation otherwise we will just use the already created core.oat/art pair.
-flags="${@/--no-relocate/--relocate}"
+flags="$@"
 
-if [ $(basename $RUN) == 'host-run-test-jar' ]; then
+# Force relocation otherwise we will just use the already created core.oat/art pair.
+# Note: relocate is the default.
+if [[ "${flags}" == *--no-relocate* ]] ; then
+  echo "Test 119-noimage-patchoat is not intended to run in no-relocate mode."
+  exit 1
+fi
+
+if [[ $@ == *--host* ]]; then
   false_bin="/bin/false"
 else
   false_bin="/system/bin/false"
diff --git a/test/122-npe/src/Main.java b/test/122-npe/src/Main.java
index 2fdcb9c..8f68205 100644
--- a/test/122-npe/src/Main.java
+++ b/test/122-npe/src/Main.java
@@ -191,6 +191,132 @@
     check(npe, thisLine += 7);
 
     try {
+      ((Value) null).volatileObjectField.toString();
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileObjectField = "Fisk";
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileIntField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileIntField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useFloat(((Value) null).volatileFloatField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileFloatField = 42.0F;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useLong(((Value) null).volatileLongField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileLongField = 42L;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useDouble(((Value) null).volatileDoubleField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileDoubleField = 42.0d;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileByteField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileByteField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      if (((Value) null).volatileBooleanField) { }
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileBooleanField = true;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileCharField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileCharField = '\u0042';
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      useInt(((Value) null).volatileShortField);
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
+      ((Value) null).volatileShortField = 42;
+    } catch (NullPointerException e) {
+      npe = e;
+    }
+    check(npe, thisLine += 7);
+
+    try {
       ((Object[]) null)[0].toString();
     } catch (NullPointerException e) {
       npe = e;
@@ -477,11 +603,22 @@
   static class Value {
     Object objectField;
     int intField;
-    float floatField; long longField;
+    float floatField;
+    long longField;
     double doubleField;
     byte byteField;
     boolean booleanField;
     char charField;
     short shortField;
+
+    volatile Object volatileObjectField;
+    volatile int volatileIntField;
+    volatile float volatileFloatField;
+    volatile long volatileLongField;
+    volatile double volatileDoubleField;
+    volatile byte volatileByteField;
+    volatile boolean volatileBooleanField;
+    volatile char volatileCharField;
+    volatile short volatileShortField;
   }
 }
diff --git a/test/129-ThreadGetId/expected.txt b/test/129-ThreadGetId/expected.txt
new file mode 100644
index 0000000..134d8d0
--- /dev/null
+++ b/test/129-ThreadGetId/expected.txt
@@ -0,0 +1 @@
+Finishing
diff --git a/test/129-ThreadGetId/info.txt b/test/129-ThreadGetId/info.txt
new file mode 100644
index 0000000..443062d
--- /dev/null
+++ b/test/129-ThreadGetId/info.txt
@@ -0,0 +1 @@
+Regression test for b/18661622
diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java
new file mode 100644
index 0000000..9934bba
--- /dev/null
+++ b/test/129-ThreadGetId/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+import java.util.Map;
+
+public class Main implements Runnable {
+    static final int numberOfThreads = 5;
+    static final int totalOperations = 1000;
+
+    public static void main(String[] args) throws Exception {
+        final Thread[] threads = new Thread[numberOfThreads];
+        for (int t = 0; t < threads.length; t++) {
+            threads[t] = new Thread(new Main());
+            threads[t].start();
+        }
+        for (Thread t : threads) {
+            t.join();
+        }
+        System.out.println("Finishing");
+    }
+
+    public void test_getId() {
+        if (Thread.currentThread().getId() <= 0) {
+            System.out.println("current thread's ID is not positive");
+        }
+        // Check all the current threads for positive IDs.
+        Map<Thread, StackTraceElement[]> stMap = Thread.getAllStackTraces();
+        for (Thread thread : stMap.keySet()) {
+            if (thread.getId() <= 0) {
+                System.out.println("thread's ID is not positive: " + thread.getName());
+            }
+        }
+    }
+
+    public void run() {
+        for (int i = 0; i < totalOperations; ++i) {
+            test_getId();
+        }
+    }
+}
diff --git a/test/130-hprof/expected.txt b/test/130-hprof/expected.txt
new file mode 100644
index 0000000..cc3d9f2
--- /dev/null
+++ b/test/130-hprof/expected.txt
@@ -0,0 +1 @@
+Generated data.
diff --git a/test/130-hprof/info.txt b/test/130-hprof/info.txt
new file mode 100644
index 0000000..64475ef
--- /dev/null
+++ b/test/130-hprof/info.txt
@@ -0,0 +1 @@
+Dump the heap for this test.
diff --git a/test/130-hprof/src/Main.java b/test/130-hprof/src/Main.java
new file mode 100644
index 0000000..67e5232
--- /dev/null
+++ b/test/130-hprof/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class Main {
+    private static final int TEST_LENGTH = 100;
+
+    private static boolean makeArray(int i) {
+        return i % 10 == 0;
+    }
+
+    private static void fillArray(Object global[], Object local[], int i) {
+        // Very stupid linking.
+        local[0] = global;
+        for (int j = 1; j < local.length; j++) {
+            local[j] = global[j];
+        }
+    }
+
+    public static void main(String[] args) {
+        // Create some data.
+        Object data[] = new Object[TEST_LENGTH];
+        for (int i = 0; i < data.length; i++) {
+            if (makeArray(i)) {
+                data[i] = new Object[TEST_LENGTH];
+            } else {
+                data[i] = String.valueOf(i);
+            }
+        }
+        for (int i = 0; i < data.length; i++) {
+            if (makeArray(i)) {
+                Object data2[] = (Object[]) data[i];
+                fillArray(data, data2, i);
+            }
+        }
+        System.out.println("Generated data.");
+
+        File dumpFile = null;
+        File convFile = null;
+
+        try {
+            // Now dump the heap.
+            dumpFile = createDump();
+
+            // Run hprof-conv on it.
+            convFile = getConvFile();
+
+            File hprof_conv = getHprofConf();
+            try {
+                ProcessBuilder pb = new ProcessBuilder(
+                        hprof_conv.getAbsoluteFile().toString(),
+                        dumpFile.getAbsoluteFile().toString(),
+                        convFile.getAbsoluteFile().toString());
+                pb.redirectErrorStream(true);
+                Process process = pb.start();
+                int ret = process.waitFor();
+                if (ret != 0) {
+                    throw new RuntimeException("Exited abnormally with " + ret);
+                }
+            } catch (Exception exc) {
+                throw new RuntimeException(exc);
+            }
+        } finally {
+            // Delete the files.
+            if (dumpFile != null) {
+                dumpFile.delete();
+            }
+            if (convFile != null) {
+                convFile.delete();
+            }
+        }
+    }
+
+    private static File getHprofConf() {
+        // Use the java.library.path. It points to the lib directory.
+        File libDir = new File(System.getProperty("java.library.path"));
+        return new File(new File(libDir.getParentFile(), "bin"), "hprof-conv");
+    }
+
+    private static File createDump() {
+        java.lang.reflect.Method dumpHprofDataMethod = getDumpHprofDataMethod();
+        if (dumpHprofDataMethod != null) {
+            File f = getDumpFile();
+            try {
+                dumpHprofDataMethod.invoke(null, f.getAbsoluteFile().toString());
+                return f;
+            } catch (Exception exc) {
+                exc.printStackTrace(System.out);
+            }
+        } else {
+            System.out.println("Could not find dump method!");
+        }
+        return null;
+    }
+
+    /**
+     * Finds VMDebug.dumpHprofData() through reflection.  In the reference
+     * implementation this will not be available.
+     *
+     * @return the reflection object, or null if the method can't be found
+     */
+    private static Method getDumpHprofDataMethod() {
+        ClassLoader myLoader = Main.class.getClassLoader();
+        Class vmdClass;
+        try {
+            vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
+        } catch (ClassNotFoundException cnfe) {
+            return null;
+        }
+
+        Method meth;
+        try {
+            meth = vmdClass.getMethod("dumpHprofData",
+                    new Class[] { String.class });
+        } catch (NoSuchMethodException nsme) {
+            System.err.println("Found VMDebug but not dumpHprofData method");
+            return null;
+        }
+
+        return meth;
+    }
+
+    private static File getDumpFile() {
+        try {
+            return File.createTempFile("test-130-hprof", "dump");
+        } catch (Exception exc) {
+            return null;
+        }
+    }
+
+    private static File getConvFile() {
+        try {
+            return File.createTempFile("test-130-hprof", "conv");
+        } catch (Exception exc) {
+            return null;
+        }
+    }
+}
diff --git a/test/131-structural-change/build b/test/131-structural-change/build
new file mode 100755
index 0000000..7ddc81d
--- /dev/null
+++ b/test/131-structural-change/build
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+${JAVAC} -d classes-ex `find src-ex -name '*.java'`
+
+if [ ${NEED_DEX} = "true" ]; then
+  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+  zip $TEST_NAME.jar classes.dex
+  ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+  zip ${TEST_NAME}-ex.jar classes.dex
+fi
diff --git a/test/131-structural-change/expected.txt b/test/131-structural-change/expected.txt
new file mode 100644
index 0000000..cc7713d
--- /dev/null
+++ b/test/131-structural-change/expected.txt
@@ -0,0 +1,2 @@
+Should really reach here.
+Done.
diff --git a/test/131-structural-change/info.txt b/test/131-structural-change/info.txt
new file mode 100644
index 0000000..6d5817b
--- /dev/null
+++ b/test/131-structural-change/info.txt
@@ -0,0 +1 @@
+Check whether a structural change in a (non-native) multi-dex scenario is detected.
diff --git a/test/131-structural-change/run b/test/131-structural-change/run
new file mode 100755
index 0000000..63fdb8c
--- /dev/null
+++ b/test/131-structural-change/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/runtime/closure.h b/test/131-structural-change/src-ex/A.java
similarity index 65%
copy from runtime/closure.h
copy to test/131-structural-change/src-ex/A.java
index 9bea28f..800347b 100644
--- a/runtime/closure.h
+++ b/test/131-structural-change/src-ex/A.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
-
-namespace art {
-
-class Thread;
-
-class Closure {
- public:
-  virtual ~Closure() { }
-  virtual void Run(Thread* self) = 0;
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_CLOSURE_H_
+public class A {
+    public void bar() {
+    }
+}
diff --git a/runtime/closure.h b/test/131-structural-change/src-ex/B.java
similarity index 65%
rename from runtime/closure.h
rename to test/131-structural-change/src-ex/B.java
index 9bea28f..61369db 100644
--- a/runtime/closure.h
+++ b/test/131-structural-change/src-ex/B.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
-
-namespace art {
-
-class Thread;
-
-class Closure {
- public:
-  virtual ~Closure() { }
-  virtual void Run(Thread* self) = 0;
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_CLOSURE_H_
+public class B extends A {
+    public void test() {
+        System.out.println("Should not reach this!");
+    }
+}
diff --git a/runtime/closure.h b/test/131-structural-change/src/A.java
similarity index 65%
copy from runtime/closure.h
copy to test/131-structural-change/src/A.java
index 9bea28f..b07de58 100644
--- a/runtime/closure.h
+++ b/test/131-structural-change/src/A.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
+public class A {
+    public void foo() {
+    }
 
-namespace art {
+    public void bar() {
+    }
 
-class Thread;
-
-class Closure {
- public:
-  virtual ~Closure() { }
-  virtual void Run(Thread* self) = 0;
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_CLOSURE_H_
+    public void baz() {
+    }
+}
diff --git a/test/131-structural-change/src/Main.java b/test/131-structural-change/src/Main.java
new file mode 100644
index 0000000..8dfa280
--- /dev/null
+++ b/test/131-structural-change/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Structural hazard test.
+ */
+public class Main {
+    public static void main(String[] args) {
+        new Main().run();
+    }
+
+    private void run() {
+        try {
+            Class<?> bClass = getClass().getClassLoader().loadClass("A");
+            System.out.println("Should really reach here.");
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+
+        boolean haveOatFile = hasOat();
+        boolean gotError = false;
+        try {
+            Class<?> bClass = getClass().getClassLoader().loadClass("B");
+        } catch (IncompatibleClassChangeError icce) {
+            gotError = true;
+        } catch (Exception e) {
+            e.printStackTrace(System.out);
+        }
+        if (haveOatFile ^ gotError) {
+            System.out.println("Did not get expected error.");
+        }
+        System.out.println("Done.");
+    }
+
+    static {
+        System.loadLibrary("arttest");
+    }
+
+    private native static boolean hasOat();
+}
diff --git a/test/132-daemon-locks-shutdown/expected.txt b/test/132-daemon-locks-shutdown/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/expected.txt
diff --git a/test/132-daemon-locks-shutdown/info.txt b/test/132-daemon-locks-shutdown/info.txt
new file mode 100644
index 0000000..f804064
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/info.txt
@@ -0,0 +1 @@
+Tests that we can shut down the runtime with daemons still looping over locks.
diff --git a/test/132-daemon-locks-shutdown/src/Main.java b/test/132-daemon-locks-shutdown/src/Main.java
new file mode 100644
index 0000000..b5bbc8c
--- /dev/null
+++ b/test/132-daemon-locks-shutdown/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test that daemon threads still contending for a lock don't make the runtime abort on shutdown.
+ */
+public class Main {
+
+    public final static int THREAD_COUNT = 32;
+
+    public static void main(String[] args) throws Exception {
+        Object sync = new Object();
+
+        for (int i = 0; i < THREAD_COUNT; i++) {
+            Thread t = new Thread(new Wait(sync));
+            t.setDaemon(true);
+            t.start();
+        }
+    }
+
+    private static class Wait implements Runnable {
+        private Object obj;
+
+        public Wait(Object obj) {
+            this.obj = obj;
+        }
+
+        public void run() {
+            for (;;) {
+                synchronized(obj) {
+                    try {
+                        obj.wait(1);
+                    } catch (Exception exc) {
+                        exc.printStackTrace(System.out);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/test/133-static-invoke-super/expected.txt b/test/133-static-invoke-super/expected.txt
new file mode 100644
index 0000000..089d8e8
--- /dev/null
+++ b/test/133-static-invoke-super/expected.txt
@@ -0,0 +1,3 @@
+basis: performed 50000000 iterations
+test1: performed 50000000 iterations
+Timing is acceptable.
diff --git a/test/133-static-invoke-super/info.txt b/test/133-static-invoke-super/info.txt
new file mode 100644
index 0000000..606331b
--- /dev/null
+++ b/test/133-static-invoke-super/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of invoking static methods in super class. To see the numbers, invoke
+this test with the "--timing" option.
diff --git a/test/133-static-invoke-super/run b/test/133-static-invoke-super/run
new file mode 100755
index 0000000..e27a622
--- /dev/null
+++ b/test/133-static-invoke-super/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# As this is a performance test we always use the non-debug build.
+exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/133-static-invoke-super/src/Main.java b/test/133-static-invoke-super/src/Main.java
new file mode 100644
index 0000000..7cfd099
--- /dev/null
+++ b/test/133-static-invoke-super/src/Main.java
@@ -0,0 +1,63 @@
+
+public class Main {
+    static class SuperClass {
+      protected static int getVar(int w) {
+          return w & 0xF;
+      }
+    }
+    static class SubClass extends SuperClass {
+      final int getVarDirect(int w) {
+        return w & 0xF;
+      }
+      public void testDirect(int max) {
+        for (int i = 0; i < max; ++i) {
+          getVarDirect(max);
+        }
+      }
+      public void testStatic(int max) {
+        for (int i = 0; i < max; ++i) {
+          getVar(max);
+        }
+      }
+    }
+
+    static public void main(String[] args) throws Exception {
+        boolean timing = (args.length >= 1) && args[0].equals("--timing");
+        run(timing);
+    }
+
+    static int testBasis(int interations) {
+      (new SubClass()).testDirect(interations);
+      return interations;
+    }
+
+    static int testStatic(int interations) {
+      (new SubClass()).testStatic(interations);
+      return interations;
+    }
+
+    static public void run(boolean timing) {
+        long time0 = System.nanoTime();
+        int count1 = testBasis(50000000);
+        long time1 = System.nanoTime();
+        int count2 = testStatic(50000000);
+        long time2 = System.nanoTime();
+
+        System.out.println("basis: performed " + count1 + " iterations");
+        System.out.println("test1: performed " + count2 + " iterations");
+
+        double basisMsec = (time1 - time0) / (double) count1 / 1000000;
+        double msec1 = (time2 - time1) / (double) count2 / 1000000;
+
+        if (msec1 < basisMsec * 5) {
+            System.out.println("Timing is acceptable.");
+        } else {
+            System.out.println("Iterations are taking too long!");
+            timing = true;
+        }
+        if (timing) {
+            System.out.printf("basis time: %.3g msec\n", basisMsec);
+            System.out.printf("test1: %.3g msec per iteration\n", msec1);
+        }
+    }
+}
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index e7dbe24..7ce2868 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -62,6 +62,18 @@
     }
   }
 
+  public static void assertFloatIsNaN(float result) {
+    if (!Float.isNaN(result)) {
+      throw new Error("Expected: NaN, found: " + result);
+    }
+  }
+
+  public static void assertDoubleIsNaN(double result) {
+    if (!Double.isNaN(result)) {
+      throw new Error("Expected: NaN, found: " + result);
+    }
+  }
+
 
   public static void main(String[] args) {
     // Generate, compile and check int-to-long Dex instructions.
@@ -94,6 +106,21 @@
     // Generate, compile and check float-to-int Dex instructions.
     floatToInt();
 
+    // Generate, compile and check float-to-long Dex instructions.
+    floatToLong();
+
+    // Generate, compile and check float-to-double Dex instructions.
+    floatToDouble();
+
+    // Generate, compile and check double-to-int Dex instructions.
+    doubleToInt();
+
+    // Generate, compile and check double-to-long Dex instructions.
+    doubleToLong();
+
+    // Generate, compile and check double-to-float Dex instructions.
+    doubleToFloat();
+
     // Generate, compile and check int-to-byte Dex instructions.
     shortToByte();
     intToByte();
@@ -342,6 +369,136 @@
     assertIntEquals(-2147483648, $opt$FloatToInt(Float.NEGATIVE_INFINITY));
   }
 
+  private static void floatToLong() {
+    assertLongEquals(1L, $opt$FloatToLong(1F));
+    assertLongEquals(0L, $opt$FloatToLong(0F));
+    assertLongEquals(0L, $opt$FloatToLong(-0F));
+    assertLongEquals(-1L, $opt$FloatToLong(-1F));
+    assertLongEquals(51L, $opt$FloatToLong(51F));
+    assertLongEquals(-51L, $opt$FloatToLong(-51F));
+    assertLongEquals(0L, $opt$FloatToLong(0.5F));
+    assertLongEquals(0L, $opt$FloatToLong(0.4999999F));
+    assertLongEquals(0L, $opt$FloatToLong(-0.4999999F));
+    assertLongEquals(0L, $opt$FloatToLong(-0.5F));
+    assertLongEquals(42L, $opt$FloatToLong(42.199F));
+    assertLongEquals(-42L, $opt$FloatToLong(-42.199F));
+    assertLongEquals(2147483648L, $opt$FloatToLong(2147483647F));  // 2^31 - 1
+    assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483647F));  // -(2^31 - 1)
+    assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483648F));  // -(2^31)
+    assertLongEquals(2147483648L, $opt$FloatToLong(2147483648F));  // (2^31)
+    assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483649F));  // -(2^31 + 1)
+    assertLongEquals(9223372036854775807L, $opt$FloatToLong(9223372036854775807F));  // 2^63 - 1
+    assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775807F));  // -(2^63 - 1)
+    assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775808F));  // -(2^63)
+    assertLongEquals(0L, $opt$FloatToLong(Float.NaN));
+    assertLongEquals(9223372036854775807L, $opt$FloatToLong(Float.POSITIVE_INFINITY));
+    assertLongEquals(-9223372036854775808L, $opt$FloatToLong(Float.NEGATIVE_INFINITY));
+  }
+
+  private static void floatToDouble() {
+    assertDoubleEquals(1D, $opt$FloatToDouble(1F));
+    assertDoubleEquals(0D, $opt$FloatToDouble(0F));
+    assertDoubleEquals(0D, $opt$FloatToDouble(-0F));
+    assertDoubleEquals(-1D, $opt$FloatToDouble(-1F));
+    assertDoubleEquals(51D, $opt$FloatToDouble(51F));
+    assertDoubleEquals(-51D, $opt$FloatToDouble(-51F));
+    assertDoubleEquals(0.5D, $opt$FloatToDouble(0.5F));
+    assertDoubleEquals(0.49999991059303284D, $opt$FloatToDouble(0.4999999F));
+    assertDoubleEquals(-0.49999991059303284D, $opt$FloatToDouble(-0.4999999F));
+    assertDoubleEquals(-0.5D, $opt$FloatToDouble(-0.5F));
+    assertDoubleEquals(42.19900131225586D, $opt$FloatToDouble(42.199F));
+    assertDoubleEquals(-42.19900131225586D, $opt$FloatToDouble(-42.199F));
+    assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483647F));  // 2^31 - 1
+    assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483647F));  // -(2^31 - 1)
+    assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483648F));  // -(2^31)
+    assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483648F));  // (2^31)
+    assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483649F));  // -(2^31 + 1)
+    assertDoubleEquals(9223372036854775807D, $opt$FloatToDouble(9223372036854775807F));  // 2^63 - 1
+    assertDoubleEquals(-9223372036854775807D, $opt$FloatToDouble(-9223372036854775807F));  // -(2^63 - 1)
+    assertDoubleEquals(-9223372036854775808D, $opt$FloatToDouble(-9223372036854775808F));  // -(2^63)
+    assertDoubleIsNaN($opt$FloatToDouble(Float.NaN));
+    assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$FloatToDouble(Float.POSITIVE_INFINITY));
+    assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY));
+  }
+
+  private static void doubleToInt() {
+    assertIntEquals(1, $opt$DoubleToInt(1D));
+    assertIntEquals(0, $opt$DoubleToInt(0D));
+    assertIntEquals(0, $opt$DoubleToInt(-0D));
+    assertIntEquals(-1, $opt$DoubleToInt(-1D));
+    assertIntEquals(51, $opt$DoubleToInt(51D));
+    assertIntEquals(-51, $opt$DoubleToInt(-51D));
+    assertIntEquals(0, $opt$DoubleToInt(0.5D));
+    assertIntEquals(0, $opt$DoubleToInt(0.4999999D));
+    assertIntEquals(0, $opt$DoubleToInt(-0.4999999D));
+    assertIntEquals(0, $opt$DoubleToInt(-0.5D));
+    assertIntEquals(42, $opt$DoubleToInt(42.199D));
+    assertIntEquals(-42, $opt$DoubleToInt(-42.199D));
+    assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D));  // 2^31 - 1
+    assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D));  // -(2^31 - 1)
+    assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D));  // -(2^31)
+    assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D));  // (2^31)
+    assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D));  // -(2^31 + 1)
+    assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D));  // 2^63 - 1
+    assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D));  // -(2^63 - 1)
+    assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D));  // -(2^63)
+    assertIntEquals(0, $opt$DoubleToInt(Double.NaN));
+    assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY));
+    assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY));
+  }
+
+  private static void doubleToLong() {
+    assertLongEquals(1L, $opt$DoubleToLong(1D));
+    assertLongEquals(0L, $opt$DoubleToLong(0D));
+    assertLongEquals(0L, $opt$DoubleToLong(-0D));
+    assertLongEquals(-1L, $opt$DoubleToLong(-1D));
+    assertLongEquals(51L, $opt$DoubleToLong(51D));
+    assertLongEquals(-51L, $opt$DoubleToLong(-51D));
+    assertLongEquals(0L, $opt$DoubleToLong(0.5D));
+    assertLongEquals(0L, $opt$DoubleToLong(0.4999999D));
+    assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D));
+    assertLongEquals(0L, $opt$DoubleToLong(-0.5D));
+    assertLongEquals(42L, $opt$DoubleToLong(42.199D));
+    assertLongEquals(-42L, $opt$DoubleToLong(-42.199D));
+    assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D));  // 2^31 - 1
+    assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D));  // -(2^31 - 1)
+    assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D));  // -(2^31)
+    assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D));  // (2^31)
+    assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D));  // -(2^31 + 1)
+    assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D));  // 2^63 - 1
+    assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D));  // -(2^63 - 1)
+    assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D));  // -(2^63)
+    assertLongEquals(0L, $opt$DoubleToLong(Double.NaN));
+    assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY));
+    assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY));
+  }
+
+  private static void doubleToFloat() {
+    assertFloatEquals(1F, $opt$DoubleToFloat(1D));
+    assertFloatEquals(0F, $opt$DoubleToFloat(0D));
+    assertFloatEquals(0F, $opt$DoubleToFloat(-0D));
+    assertFloatEquals(-1F, $opt$DoubleToFloat(-1D));
+    assertFloatEquals(51F, $opt$DoubleToFloat(51D));
+    assertFloatEquals(-51F, $opt$DoubleToFloat(-51D));
+    assertFloatEquals(0.5F, $opt$DoubleToFloat(0.5D));
+    assertFloatEquals(0.4999999F, $opt$DoubleToFloat(0.4999999D));
+    assertFloatEquals(-0.4999999F, $opt$DoubleToFloat(-0.4999999D));
+    assertFloatEquals(-0.5F, $opt$DoubleToFloat(-0.5D));
+    assertFloatEquals(42.199F, $opt$DoubleToFloat(42.199D));
+    assertFloatEquals(-42.199F, $opt$DoubleToFloat(-42.199D));
+    assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483647D));  // 2^31 - 1
+    assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483647D));  // -(2^31 - 1)
+    assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483648D));  // -(2^31)
+    assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483648D));  // (2^31)
+    assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483649D));  // -(2^31 + 1)
+    assertFloatEquals(9223372036854775807F, $opt$DoubleToFloat(9223372036854775807D));  // 2^63 - 1
+    assertFloatEquals(-9223372036854775807F, $opt$DoubleToFloat(-9223372036854775807D));  // -(2^63 - 1)
+    assertFloatEquals(-9223372036854775808F, $opt$DoubleToFloat(-9223372036854775808D));  // -(2^63)
+    assertFloatIsNaN($opt$DoubleToFloat(Float.NaN));
+    assertFloatEquals(Float.POSITIVE_INFINITY, $opt$DoubleToFloat(Double.POSITIVE_INFINITY));
+    assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$DoubleToFloat(Double.NEGATIVE_INFINITY));
+  }
+
   private static void shortToByte() {
     assertByteEquals((byte)1, $opt$ShortToByte((short)1));
     assertByteEquals((byte)0, $opt$ShortToByte((short)0));
@@ -470,48 +627,63 @@
 
 
   // These methods produce int-to-long Dex instructions.
-  static long $opt$ByteToLong(byte a) { return a; }
-  static long $opt$ShortToLong(short a) { return a; }
-  static long $opt$IntToLong(int a) { return a; }
-  static long $opt$CharToLong(int a) { return a; }
+  static long $opt$ByteToLong(byte a) { return (long)a; }
+  static long $opt$ShortToLong(short a) { return (long)a; }
+  static long $opt$IntToLong(int a) { return (long)a; }
+  static long $opt$CharToLong(int a) { return (long)a; }
 
   // These methods produce int-to-float Dex instructions.
-  static float $opt$ByteToFloat(byte a) { return a; }
-  static float $opt$ShortToFloat(short a) { return a; }
-  static float $opt$IntToFloat(int a) { return a; }
-  static float $opt$CharToFloat(char a) { return a; }
+  static float $opt$ByteToFloat(byte a) { return (float)a; }
+  static float $opt$ShortToFloat(short a) { return (float)a; }
+  static float $opt$IntToFloat(int a) { return (float)a; }
+  static float $opt$CharToFloat(char a) { return (float)a; }
 
   // These methods produce int-to-double Dex instructions.
-  static double $opt$ByteToDouble(byte a) { return a; }
-  static double $opt$ShortToDouble(short a) { return a; }
-  static double $opt$IntToDouble(int a) { return a; }
-  static double $opt$CharToDouble(int a) { return a; }
+  static double $opt$ByteToDouble(byte a) { return (double)a; }
+  static double $opt$ShortToDouble(short a) { return (double)a; }
+  static double $opt$IntToDouble(int a) { return (double)a; }
+  static double $opt$CharToDouble(int a) { return (double)a; }
 
   // These methods produce long-to-int Dex instructions.
-  static int $opt$LongToInt(long a){ return (int)a; }
-  static int $opt$LongLiteralToInt(){ return (int)42L; }
+  static int $opt$LongToInt(long a) { return (int)a; }
+  static int $opt$LongLiteralToInt() { return (int)42L; }
 
   // This method produces a long-to-float Dex instruction.
-  static float $opt$LongToFloat(long a){ return (float)a; }
+  static float $opt$LongToFloat(long a) { return (float)a; }
 
   // This method produces a long-to-double Dex instruction.
-  static double $opt$LongToDouble(long a){ return (double)a; }
+  static double $opt$LongToDouble(long a) { return (double)a; }
 
   // This method produces a float-to-int Dex instruction.
-  static int $opt$FloatToInt(float a){ return (int)a; }
+  static int $opt$FloatToInt(float a) { return (int)a; }
+
+  // This method produces a float-to-long Dex instruction.
+  static long $opt$FloatToLong(float a){ return (long)a; }
+
+  // This method produces a float-to-double Dex instruction.
+  static double $opt$FloatToDouble(float a) { return (double)a; }
+
+  // This method produces a double-to-int Dex instruction.
+  static int $opt$DoubleToInt(double a){ return (int)a; }
+
+  // This method produces a double-to-long Dex instruction.
+  static long $opt$DoubleToLong(double a){ return (long)a; }
+
+  // This method produces a double-to-float Dex instruction.
+  static float $opt$DoubleToFloat(double a) { return (float)a; }
 
   // These methods produce int-to-byte Dex instructions.
-  static byte $opt$ShortToByte(short a){ return (byte)a; }
-  static byte $opt$IntToByte(int a){ return (byte)a; }
-  static byte $opt$CharToByte(char a){ return (byte)a; }
+  static byte $opt$ShortToByte(short a) { return (byte)a; }
+  static byte $opt$IntToByte(int a) { return (byte)a; }
+  static byte $opt$CharToByte(char a) { return (byte)a; }
 
   // These methods produce int-to-short Dex instructions.
-  static short $opt$ByteToShort(byte a){ return (short)a; }
-  static short $opt$IntToShort(int a){ return (short)a; }
-  static short $opt$CharToShort(char a){ return (short)a; }
+  static short $opt$ByteToShort(byte a) { return (short)a; }
+  static short $opt$IntToShort(int a) { return (short)a; }
+  static short $opt$CharToShort(char a) { return (short)a; }
 
   // These methods produce int-to-char Dex instructions.
-  static char $opt$ByteToChar(byte a){ return (char)a; }
-  static char $opt$ShortToChar(short a){ return (char)a; }
-  static char $opt$IntToChar(int a){ return (char)a; }
+  static char $opt$ByteToChar(byte a) { return (char)a; }
+  static char $opt$ShortToChar(short a) { return (char)a; }
+  static char $opt$IntToChar(int a) { return (char)a; }
 }
diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/428-optimizing-arith-rem/src/Main.java
index 46bd3c6..3f77318 100644
--- a/test/428-optimizing-arith-rem/src/Main.java
+++ b/test/428-optimizing-arith-rem/src/Main.java
@@ -16,49 +16,7 @@
 
 public class Main {
 
-  public static void expectEquals(int expected, int result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-
-  public static void expectEquals(long expected, long result) {
-    if (expected != result) {
-      throw new Error("Expected: " + expected + ", found: " + result);
-    }
-  }
-
-  public static void expectDivisionByZero(int value) {
-    try {
-      $opt$Rem(value, 0);
-      throw new Error("Expected RuntimeException when modulo by 0");
-    } catch (java.lang.RuntimeException e) {
-    }
-    try {
-      $opt$RemZero(value);
-      throw new Error("Expected RuntimeException when modulo by 0");
-    } catch (java.lang.RuntimeException e) {
-    }
-  }
-
-  public static void expectDivisionByZero(long value) {
-    try {
-      $opt$Rem(value, 0L);
-      throw new Error("Expected RuntimeException when modulo by 0");
-    } catch (java.lang.RuntimeException e) {
-    }
-    try {
-      $opt$RemZero(value);
-      throw new Error("Expected RuntimeException when modulo by 0");
-    } catch (java.lang.RuntimeException e) {
-    }
-  }
-
   public static void main(String[] args) {
-    rem();
-  }
-
-  public static void rem() {
     remInt();
     remLong();
   }
@@ -115,22 +73,22 @@
     expectEquals(-7L, $opt$Rem(-7L, 9L));
     expectEquals(-7L, $opt$Rem(-7L, -9L));
 
-    expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, 1L));
-    expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, -1L));
-    expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, 1L));
-    expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, -1L)); // no overflow
-    expectEquals(-1L, $opt$Rem(Integer.MIN_VALUE, Integer.MAX_VALUE));
-    expectEquals(Integer.MAX_VALUE, $opt$Rem(Integer.MAX_VALUE, Integer.MIN_VALUE));
+    expectEquals(0L, $opt$Rem(Long.MAX_VALUE, 1L));
+    expectEquals(0L, $opt$Rem(Long.MAX_VALUE, -1L));
+    expectEquals(0L, $opt$Rem(Long.MIN_VALUE, 1L));
+    expectEquals(0L, $opt$Rem(Long.MIN_VALUE, -1L)); // no overflow
+    expectEquals(-1L, $opt$Rem(Long.MIN_VALUE, Long.MAX_VALUE));
+    expectEquals(Long.MAX_VALUE, $opt$Rem(Long.MAX_VALUE, Long.MIN_VALUE));
 
     expectEquals(0L, $opt$Rem(0L, 7L));
-    expectEquals(0L, $opt$Rem(0L, Integer.MAX_VALUE));
-    expectEquals(0L, $opt$Rem(0L, Integer.MIN_VALUE));
+    expectEquals(0L, $opt$Rem(0L, Long.MAX_VALUE));
+    expectEquals(0L, $opt$Rem(0L, Long.MIN_VALUE));
 
     expectDivisionByZero(0L);
     expectDivisionByZero(1L);
     expectDivisionByZero(5L);
-    expectDivisionByZero(Integer.MAX_VALUE);
-    expectDivisionByZero(Integer.MIN_VALUE);
+    expectDivisionByZero(Long.MAX_VALUE);
+    expectDivisionByZero(Long.MIN_VALUE);
   }
 
   static int $opt$Rem(int a, int b) {
@@ -157,4 +115,43 @@
   static long $opt$RemZero(long a) {
     return a % 0L;
   }
+
+  public static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  public static void expectDivisionByZero(int value) {
+    try {
+      $opt$Rem(value, 0);
+      throw new Error("Expected RuntimeException when modulo by 0");
+    } catch (java.lang.RuntimeException e) {
+    }
+    try {
+      $opt$RemZero(value);
+      throw new Error("Expected RuntimeException when modulo by 0");
+    } catch (java.lang.RuntimeException e) {
+    }
+  }
+
+  public static void expectDivisionByZero(long value) {
+    try {
+      $opt$Rem(value, 0L);
+      throw new Error("Expected RuntimeException when modulo by 0");
+    } catch (java.lang.RuntimeException e) {
+    }
+    try {
+      $opt$RemZero(value);
+      throw new Error("Expected RuntimeException when modulo by 0");
+    } catch (java.lang.RuntimeException e) {
+    }
+  }
+
 }
diff --git a/test/436-rem-float/expected.txt b/test/436-rem-float/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/436-rem-float/expected.txt
diff --git a/test/436-rem-float/info.txt b/test/436-rem-float/info.txt
new file mode 100644
index 0000000..b023f59
--- /dev/null
+++ b/test/436-rem-float/info.txt
@@ -0,0 +1 @@
+Tests for floating point modulo (rem) operation.
diff --git a/test/436-rem-float/src/Main.java b/test/436-rem-float/src/Main.java
new file mode 100644
index 0000000..cc6341a
--- /dev/null
+++ b/test/436-rem-float/src/Main.java
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+  public static void main(String[] args) {
+    remFloat();
+    remDouble();
+  }
+
+  private static void remFloat() {
+    expectApproxEquals(1.98F, $opt$Rem(1.98F, 2F));
+    expectApproxEquals(0F, $opt$Rem(2F, 0.5F));
+    expectApproxEquals(0.09999F, $opt$Rem(1.0F, 0.1F));
+    expectApproxEquals(1.9F, $opt$Rem(6.5F, 2.3F));
+    expectApproxEquals(0.48F, $opt$Rem(1.98F, 1.5F));
+    expectApproxEquals(0.9999F, $opt$Rem(0.9999F, 1.222F));
+    expectApproxEquals(0.9999F, $opt$Rem(0.9999F, 1.0001F));
+    expectApproxEquals(-1.98F, $opt$Rem(-1.98F, 2F));
+    expectApproxEquals(-0F, $opt$Rem(-2F, 0.5F));
+    expectApproxEquals(-0.09999F, $opt$Rem(-1.0F, 0.1F));
+    expectApproxEquals(-1.9F, $opt$Rem(-6.5F, 2.3F));
+    expectApproxEquals(-0.48F, $opt$Rem(-1.98F, 1.5F));
+    expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, 1.222F));
+    expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, 1.0001F));
+    expectApproxEquals(1.98F, $opt$Rem(1.98F, -2F));
+    expectApproxEquals(0F, $opt$Rem(2F, -0.5F));
+    expectApproxEquals(0.09999F, $opt$Rem(1.0F, -0.1F));
+    expectApproxEquals(1.9F, $opt$Rem(6.5F, -2.3F));
+    expectApproxEquals(0.48F, $opt$Rem(1.98F, -1.5F));
+    expectApproxEquals(0.9999F, $opt$Rem(0.9999F, -1.222F));
+    expectApproxEquals(0.9999F, $opt$Rem(0.9999F, -1.0001F));
+    expectApproxEquals(-1.98F, $opt$Rem(-1.98F, -2F));
+    expectApproxEquals(-0F, $opt$Rem(-2F, -0.5F));
+    expectApproxEquals(-0.09999F, $opt$Rem(-1.0F, -0.1F));
+    expectApproxEquals(-1.9F, $opt$Rem(-6.5F, -2.3F));
+    expectApproxEquals(-0.48F, $opt$Rem(-1.98F, -1.5F));
+    expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, -1.222F));
+    expectApproxEquals(-0.9999F, $opt$Rem(-0.9999F, -1.0001F));
+
+    expectApproxEquals(1.68267e-18F, $opt$Rem(61615.2F, -2.48699e-17F));
+    expectApproxEquals(-8.63819e-09F, $opt$Rem(-1.73479e+14F, 3.11154e-08F));
+    expectApproxEquals(1.10911e-12F, $opt$Rem(338122F, 4.57572e-12F));
+
+    expectApproxEquals(2F, $opt$RemConst(6F));
+    expectApproxEquals(2F, $opt$Rem(5.1F, 3.1F));
+    expectApproxEquals(2.1F, $opt$Rem(5.1F, 3F));
+    expectApproxEquals(-2F, $opt$Rem(-5.1F, 3.1F));
+    expectApproxEquals(-2.1F, $opt$Rem(-5.1F, -3F));
+    expectApproxEquals(2F, $opt$Rem(6F, 4F));
+    expectApproxEquals(2F, $opt$Rem(6F, -4F));
+    expectApproxEquals(0F, $opt$Rem(6F, 3F));
+    expectApproxEquals(0F, $opt$Rem(6F, -3F));
+    expectApproxEquals(0F, $opt$Rem(6F, 1F));
+    expectApproxEquals(0F, $opt$Rem(6F, -1F));
+    expectApproxEquals(-1F, $opt$Rem(-7F, 3F));
+    expectApproxEquals(-1F, $opt$Rem(-7F, -3F));
+    expectApproxEquals(0F, $opt$Rem(6F, 6F));
+    expectApproxEquals(0F, $opt$Rem(-6F, -6F));
+    expectApproxEquals(7F, $opt$Rem(7F, 9F));
+    expectApproxEquals(7F, $opt$Rem(7F, -9F));
+    expectApproxEquals(-7F, $opt$Rem(-7F, 9F));
+    expectApproxEquals(-7F, $opt$Rem(-7F, -9F));
+    expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, 1F));
+    expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, -1F));
+    expectApproxEquals(0F, $opt$Rem(Float.MIN_VALUE, 1F));
+    expectApproxEquals(0F, $opt$Rem(Float.MIN_VALUE, -1F));
+    expectApproxEquals(0F, $opt$Rem(0F, 7F));
+    expectApproxEquals(0F, $opt$Rem(0F, Float.MAX_VALUE));
+    expectApproxEquals(0F, $opt$Rem(0F, Float.MIN_VALUE));
+    expectApproxEquals(0F, $opt$Rem(0F, Float.POSITIVE_INFINITY));
+    expectApproxEquals(0F, $opt$Rem(0F, Float.NEGATIVE_INFINITY));
+    expectApproxEquals(4F, $opt$Rem(4F, Float.POSITIVE_INFINITY));
+    expectApproxEquals(4F, $opt$Rem(4F, Float.NEGATIVE_INFINITY));
+    expectApproxEquals(-4F, $opt$Rem(-4F, Float.POSITIVE_INFINITY));
+    expectApproxEquals(-4F, $opt$Rem(-4F, Float.NEGATIVE_INFINITY));
+    expectApproxEquals(0F, $opt$Rem(Float.MIN_NORMAL, Float.MIN_VALUE));
+    expectApproxEquals(0F, $opt$Rem(Float.MIN_NORMAL, Float.MIN_NORMAL));
+    expectApproxEquals(0F, $opt$Rem(Float.MIN_VALUE, Float.MIN_VALUE));
+    expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, Float.MIN_VALUE));
+    expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, Float.MAX_VALUE));
+    expectApproxEquals(0F, $opt$Rem(Float.MAX_VALUE, Float.MIN_NORMAL));
+    expectApproxEquals(Float.MIN_NORMAL, $opt$Rem(Float.MIN_NORMAL, Float.MAX_VALUE));
+    expectApproxEquals(Float.MIN_NORMAL, $opt$Rem(Float.MIN_NORMAL, Float.NEGATIVE_INFINITY));
+    expectApproxEquals(Float.MIN_NORMAL, $opt$Rem(Float.MIN_NORMAL, Float.POSITIVE_INFINITY));
+    expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.MAX_VALUE));
+    expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.MIN_NORMAL));
+    expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.NEGATIVE_INFINITY));
+    expectApproxEquals(Float.MIN_VALUE, $opt$Rem(Float.MIN_VALUE, Float.POSITIVE_INFINITY));
+    expectApproxEquals(Float.MAX_VALUE, $opt$Rem(Float.MAX_VALUE, Float.NEGATIVE_INFINITY));
+    expectApproxEquals(Float.MAX_VALUE, $opt$Rem(Float.MAX_VALUE, Float.POSITIVE_INFINITY));
+
+    expectNaN($opt$Rem(Float.NaN, 3F));
+    expectNaN($opt$Rem(3F, Float.NaN));
+    expectNaN($opt$Rem(3F, 0F));
+    expectNaN($opt$Rem(1F, 0F));
+    expectNaN($opt$Rem(-1F, 0F));
+    expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.MIN_VALUE));
+    expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.MAX_VALUE));
+    expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.MIN_NORMAL));
+    expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Rem(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY));
+    expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.MIN_VALUE));
+    expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.MAX_VALUE));
+    expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.MIN_NORMAL));
+    expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+    expectNaN($opt$Rem(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+  }
+
+  private static void remDouble() {
+    expectApproxEquals(1.98D, $opt$Rem(1.98D, 2D));
+    expectApproxEquals(0D, $opt$Rem(2D, 0.5D));
+    expectApproxEquals(0.09999D, $opt$Rem(1.0D, 0.1D));
+    expectApproxEquals(1.9D, $opt$Rem(6.5D, 2.3D));
+    expectApproxEquals(0.48D, $opt$Rem(1.98D, 1.5D));
+    expectApproxEquals(0.9999D, $opt$Rem(0.9999D, 1.222D));
+    expectApproxEquals(0.9999D, $opt$Rem(0.9999D, 1.0001D));
+    expectApproxEquals(-1.98D, $opt$Rem(-1.98D, 2D));
+    expectApproxEquals(-0D, $opt$Rem(-2D, 0.5D));
+    expectApproxEquals(-0.09999D, $opt$Rem(-1.0D, 0.1D));
+    expectApproxEquals(-1.9D, $opt$Rem(-6.5D, 2.3D));
+    expectApproxEquals(-0.48D, $opt$Rem(-1.98D, 1.5D));
+    expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, 1.222D));
+    expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, 1.0001D));
+    expectApproxEquals(1.98D, $opt$Rem(1.98D, -2D));
+    expectApproxEquals(0D, $opt$Rem(2D, -0.5D));
+    expectApproxEquals(0.09999D, $opt$Rem(1.0D, -0.1D));
+    expectApproxEquals(1.9D, $opt$Rem(6.5D, -2.3D));
+    expectApproxEquals(0.48D, $opt$Rem(1.98D, -1.5D));
+    expectApproxEquals(0.9999D, $opt$Rem(0.9999D, -1.222D));
+    expectApproxEquals(0.9999D, $opt$Rem(0.9999D, -1.0001D));
+    expectApproxEquals(-1.98D, $opt$Rem(-1.98D, -2D));
+    expectApproxEquals(-0D, $opt$Rem(-2D, -0.5D));
+    expectApproxEquals(-0.09999D, $opt$Rem(-1.0D, -0.1D));
+    expectApproxEquals(-1.9D, $opt$Rem(-6.5D, -2.3D));
+    expectApproxEquals(-0.48D, $opt$Rem(-1.98D, -1.5D));
+    expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, -1.222D));
+    expectApproxEquals(-0.9999D, $opt$Rem(-0.9999D, -1.0001D));
+
+    expectApproxEquals(2D, $opt$RemConst(6D));
+    expectApproxEquals(2D, $opt$Rem(5.1D, 3.1D));
+    expectApproxEquals(2.1D, $opt$Rem(5.1D, 3D));
+    expectApproxEquals(-2D, $opt$Rem(-5.1D, 3.1D));
+    expectApproxEquals(-2.1D, $opt$Rem(-5.1D, -3D));
+    expectApproxEquals(2D, $opt$Rem(6D, 4D));
+    expectApproxEquals(2D, $opt$Rem(6D, -4D));
+    expectApproxEquals(0D, $opt$Rem(6D, 3D));
+    expectApproxEquals(0D, $opt$Rem(6D, -3D));
+    expectApproxEquals(0D, $opt$Rem(6D, 1D));
+    expectApproxEquals(0D, $opt$Rem(6D, -1D));
+    expectApproxEquals(-1D, $opt$Rem(-7D, 3D));
+    expectApproxEquals(-1D, $opt$Rem(-7D, -3D));
+    expectApproxEquals(0D, $opt$Rem(6D, 6D));
+    expectApproxEquals(0D, $opt$Rem(-6D, -6D));
+    expectApproxEquals(7D, $opt$Rem(7D, 9D));
+    expectApproxEquals(7D, $opt$Rem(7D, -9D));
+    expectApproxEquals(-7D, $opt$Rem(-7D, 9D));
+    expectApproxEquals(-7D, $opt$Rem(-7D, -9D));
+    expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, 1D));
+    expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, -1D));
+    expectApproxEquals(0D, $opt$Rem(Double.MIN_VALUE, 1D));
+    expectApproxEquals(0D, $opt$Rem(Double.MIN_VALUE, -1D));
+    expectApproxEquals(0D, $opt$Rem(0D, 7D));
+    expectApproxEquals(0D, $opt$Rem(0D, Double.MAX_VALUE));
+    expectApproxEquals(0D, $opt$Rem(0D, Double.MIN_VALUE));
+    expectApproxEquals(0D, $opt$Rem(0D, Double.POSITIVE_INFINITY));
+    expectApproxEquals(0D, $opt$Rem(0D, Double.NEGATIVE_INFINITY));
+    expectApproxEquals(4D, $opt$Rem(4D, Double.POSITIVE_INFINITY));
+    expectApproxEquals(4D, $opt$Rem(4D, Double.NEGATIVE_INFINITY));
+    expectApproxEquals(-4D, $opt$Rem(-4D, Double.POSITIVE_INFINITY));
+    expectApproxEquals(-4D, $opt$Rem(-4D, Double.NEGATIVE_INFINITY));
+    expectApproxEquals(0D, $opt$Rem(Double.MIN_NORMAL, Double.MIN_VALUE));
+    expectApproxEquals(0D, $opt$Rem(Double.MIN_NORMAL, Double.MIN_NORMAL));
+    expectApproxEquals(0D, $opt$Rem(Double.MIN_VALUE, Double.MIN_VALUE));
+    expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, Double.MIN_VALUE));
+    expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, Double.MAX_VALUE));
+    expectApproxEquals(0D, $opt$Rem(Double.MAX_VALUE, Double.MIN_NORMAL));
+    expectApproxEquals(Double.MIN_NORMAL, $opt$Rem(Double.MIN_NORMAL, Double.MAX_VALUE));
+    expectApproxEquals(Double.MIN_NORMAL, $opt$Rem(Double.MIN_NORMAL, Double.NEGATIVE_INFINITY));
+    expectApproxEquals(Double.MIN_NORMAL, $opt$Rem(Double.MIN_NORMAL, Double.POSITIVE_INFINITY));
+    expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.MAX_VALUE));
+    expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.MIN_NORMAL));
+    expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.NEGATIVE_INFINITY));
+    expectApproxEquals(Double.MIN_VALUE, $opt$Rem(Double.MIN_VALUE, Double.POSITIVE_INFINITY));
+    expectApproxEquals(Double.MAX_VALUE, $opt$Rem(Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
+    expectApproxEquals(Double.MAX_VALUE, $opt$Rem(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
+
+    expectNaN($opt$Rem(Double.NaN, 3D));
+    expectNaN($opt$Rem(3D, Double.NaN));
+    expectNaN($opt$Rem(3D, 0D));
+    expectNaN($opt$Rem(1D, 0D));
+    expectNaN($opt$Rem(-1D, 0D));
+    expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.MIN_VALUE));
+    expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.MAX_VALUE));
+    expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.MIN_NORMAL));
+    expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+    expectNaN($opt$Rem(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+    expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.MIN_VALUE));
+    expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.MAX_VALUE));
+    expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.MIN_NORMAL));
+    expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+    expectNaN($opt$Rem(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+  }
+
+  static float $opt$Rem(float a, float b) {
+    return a % b;
+  }
+
+ static float $opt$RemConst(float a) {
+    return a % 4F;
+  }
+
+  static double $opt$Rem(double a, double b) {
+    return a % b;
+  }
+
+  static double $opt$RemConst(double a) {
+    return a % 4D;
+  }
+
+  public static void expectApproxEquals(float a, float b) {
+    float maxDelta = 0.00001F;
+    boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: " + b
+          + ", with delta: " + maxDelta + " " + (a - b));
+    }
+  }
+
+  public static void expectApproxEquals(double a, double b) {
+    double maxDelta = 0.00001D;
+    boolean aproxEquals = (a > b) ? ((a - b) < maxDelta) : ((b - a) < maxDelta);
+    if (!aproxEquals) {
+      throw new Error("Expected: " + a + ", found: "
+          + b + ", with delta: " + maxDelta + " " + (a - b));
+    }
+  }
+
+  public static void expectNaN(float a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+  public static void expectNaN(double a) {
+    if (a == a) {
+      throw new Error("Expected NaN: " + a);
+    }
+  }
+
+}
diff --git a/test/436-shift-constant/expected.txt b/test/436-shift-constant/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/436-shift-constant/expected.txt
diff --git a/test/436-shift-constant/info.txt b/test/436-shift-constant/info.txt
new file mode 100644
index 0000000..dc20646
--- /dev/null
+++ b/test/436-shift-constant/info.txt
@@ -0,0 +1 @@
+Regression tests for shift instructions and constants larger than 8bits.
diff --git a/test/436-shift-constant/src/Main.java b/test/436-shift-constant/src/Main.java
new file mode 100644
index 0000000..e69f64b
--- /dev/null
+++ b/test/436-shift-constant/src/Main.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    assertEquals(0x80000000, doShiftInt(1));
+    assertEquals(0x8000000000000000L, doShiftLong(1L));
+  }
+
+  public static int doShiftInt(int value) {
+    return value << 0xFFFF;
+  }
+
+  public static long doShiftLong(long value) {
+    return value << 0xFFFF;
+  }
+
+  public static void assertEquals(int a, int b) {
+    if (a != b) {
+      throw new Error("Expected " + a + ", got " + b);
+    }
+  }
+
+  public static void assertEquals(long a, long b) {
+    if (a != b) {
+      throw new Error("Expected " + a + ", got " + b);
+    }
+  }
+}
diff --git a/test/437-inline/expected.txt b/test/437-inline/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/437-inline/expected.txt
diff --git a/test/437-inline/info.txt b/test/437-inline/info.txt
new file mode 100644
index 0000000..6487a21
--- /dev/null
+++ b/test/437-inline/info.txt
@@ -0,0 +1 @@
+Tests inlining in the compiler.
diff --git a/test/437-inline/src/Main.java b/test/437-inline/src/Main.java
new file mode 100644
index 0000000..daabe4e
--- /dev/null
+++ b/test/437-inline/src/Main.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    if ($opt$inline$returnInt() != 4) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnParameter(42) != 42) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnWide() != 12L) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnWideParameter(0x100000001L) != 0x100000001L) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnReferenceParameter(Main.class) != Main.class) {
+      throw new Error();
+    }
+
+    $opt$inline$returnVoid();
+    $opt$inline$returnVoidWithOneParameter(32);
+
+    if ($opt$inline$returnAdd(42, 1) != 43) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnSub(42, 1) != 41) {
+      throw new Error();
+    }
+
+    // Some architectures used to not be able to allocate registers with
+    // floating point operations. This call is a regression test that we don't
+    // try inlining methods with floats in it on such architectures. The
+    // compiler used to crash after inlining a method it cannot allocate
+    // registers for.
+    tryInlineFloat();
+  }
+
+  public static int tryInlineFloat() {
+    return useFloatMethod();
+  }
+
+  public static float staticFloat = 42.0f;
+
+  public static int useFloatMethod() {
+    return (int)staticFloat;
+  }
+
+  public static int $opt$inline$returnParameter(int a) {
+    return a;
+  }
+
+  public static int $opt$inline$returnAdd(int a, int b) {
+    return a + b;
+  }
+
+  public static int $opt$inline$returnSub(int a, int b) {
+    return a - b;
+  }
+
+  public static int $opt$inline$returnInt() {
+    return 4;
+  }
+
+  public static long $opt$inline$returnWideParameter(long a) {
+    return a;
+  }
+
+  public static long $opt$inline$returnWide() {
+    return 12L;
+  }
+
+  public static Object $opt$inline$returnReferenceParameter(Object o) {
+    return o;
+  }
+
+  public static void $opt$inline$returnVoid() {
+    return;
+  }
+
+  public static void $opt$inline$returnVoidWithOneParameter(int a) {
+    return;
+  }
+}
diff --git a/test/438-volatile/expected.txt b/test/438-volatile/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/438-volatile/expected.txt
diff --git a/test/438-volatile/info.txt b/test/438-volatile/info.txt
new file mode 100644
index 0000000..7a4c81a
--- /dev/null
+++ b/test/438-volatile/info.txt
@@ -0,0 +1 @@
+Tests basic operations (set/get) on volatiles.
diff --git a/test/438-volatile/src/Main.java b/test/438-volatile/src/Main.java
new file mode 100644
index 0000000..a870e4c
--- /dev/null
+++ b/test/438-volatile/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  static volatile long long_volatile;
+  static volatile double double_volatile;
+
+  public static void main(String[] args) {
+    checkVolatileUpdate(0L);
+    checkVolatileUpdate(Long.MAX_VALUE);
+    checkVolatileUpdate(Long.MIN_VALUE);
+
+    checkVolatileUpdate(0.0);
+    checkVolatileUpdate(Double.MAX_VALUE);
+    checkVolatileUpdate(-Double.MAX_VALUE);
+  }
+
+  public static long $opt$update(long a) {
+     long_volatile = a;
+     return long_volatile;
+  }
+
+  public static double $opt$update(double a) {
+     double_volatile = a;
+     return double_volatile;
+  }
+
+  public static void checkVolatileUpdate(long value) {
+    if (value != $opt$update(value)) {
+      throw new RuntimeException("Volatile update failed for long:" + value);
+    }
+  }
+
+  public static void checkVolatileUpdate(double value) {
+    if (value != $opt$update(value)) {
+      throw new RuntimeException("Volatile update failed for double:" + value);
+    }
+  }
+
+}
diff --git a/test/439-npe/expected.txt b/test/439-npe/expected.txt
new file mode 100644
index 0000000..271d40d
--- /dev/null
+++ b/test/439-npe/expected.txt
@@ -0,0 +1,18 @@
+$opt$setObjectField
+$opt$setIntField
+$opt$setFloatField
+$opt$setLongField
+$opt$setDoubleField
+$opt$setByteField
+$opt$setBooleanField
+$opt$setCharField
+$opt$setShortField
+$opt$getObjectField
+$opt$getIntField
+$opt$getFloatField
+$opt$getLongField
+$opt$getDoubleField
+$opt$getByteField
+$opt$getBooleanField
+$opt$getCharField
+$opt$getShortField
diff --git a/test/439-npe/info.txt b/test/439-npe/info.txt
new file mode 100644
index 0000000..d15ab2c
--- /dev/null
+++ b/test/439-npe/info.txt
@@ -0,0 +1,2 @@
+More tests for NullPointerExceptions to complement 122-npe.
+They check set/get to volatile fields.
diff --git a/test/439-npe/src/Main.java b/test/439-npe/src/Main.java
new file mode 100644
index 0000000..40c2645
--- /dev/null
+++ b/test/439-npe/src/Main.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  private volatile Object objectField;
+  private volatile int intField;
+  private volatile float floatField;
+  private volatile long longField;
+  private volatile double doubleField;
+  private volatile byte byteField;
+  private volatile boolean booleanField;
+  private volatile char charField;
+  private volatile short shortField;
+
+  public static void $opt$setObjectField(Main m) {
+    m.objectField = null;
+  }
+
+  public static void $opt$setIntField(Main m) {
+    m.intField = 0;
+  }
+
+  public static void $opt$setFloatField(Main m) {
+    m.floatField = 0;
+  }
+
+  public static void $opt$setLongField(Main m) {
+    m.longField = 0;
+  }
+
+  public static void $opt$setDoubleField(Main m) {
+    m.doubleField = 0;
+  }
+
+  public static void $opt$setByteField(Main m) {
+    m.byteField = 0;
+  }
+
+  public static void $opt$setBooleanField(Main m) {
+    m.booleanField = false;
+  }
+
+  public static void $opt$setCharField(Main m) {
+    m.charField = 0;
+  }
+
+  public static void $opt$setShortField(Main m) {
+    m.shortField = 0;
+  }
+
+  public static Object $opt$getObjectField(Main m) {
+    return m.objectField;
+  }
+
+  public static int $opt$getIntField(Main m) {
+    return m.intField;
+  }
+
+  public static float $opt$getFloatField(Main m) {
+    return m.floatField;
+  }
+
+  public static long $opt$getLongField(Main m) {
+    return m.longField;
+  }
+
+  public static double $opt$getDoubleField(Main m) {
+    return m.doubleField;
+  }
+
+  public static byte $opt$getByteField(Main m) {
+    return m.byteField;
+  }
+
+  public static boolean $opt$getBooleanField(Main m) {
+    return m.booleanField;
+  }
+
+  public static char $opt$getCharField(Main m) {
+    return m.charField;
+  }
+
+  public static short $opt$getShortField(Main m) {
+    return m.shortField;
+  }
+
+  public static void main(String[] args) {
+    int methodLine = 30;
+    int thisLine = 103;
+    try {
+      $opt$setObjectField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 2, methodLine, "$opt$setObjectField");
+    }
+    try {
+      $opt$setIntField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setIntField");
+    }
+    try {
+      $opt$setFloatField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setFloatField");
+    }
+    try {
+      $opt$setLongField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setLongField");
+    }
+    try {
+      $opt$setDoubleField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setDoubleField");
+    }
+    try {
+      $opt$setByteField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setByteField");
+    }
+    try {
+      $opt$setBooleanField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setBooleanField");
+    }
+    try {
+      $opt$setCharField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setCharField");
+    }
+    try {
+      $opt$setShortField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$setShortField");
+    }
+    try {
+      $opt$getObjectField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getObjectField");
+    }
+    try {
+      $opt$getIntField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getIntField");
+    }
+    try {
+      $opt$getFloatField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getFloatField");
+    }
+    try {
+      $opt$getLongField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getLongField");
+    }
+    try {
+      $opt$getDoubleField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getDoubleField");
+    }
+    try {
+      $opt$getByteField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getByteField");
+    }
+    try {
+      $opt$getBooleanField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getBooleanField");
+    }
+    try {
+      $opt$getCharField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getCharField");
+    }
+    try {
+      $opt$getShortField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 4, "$opt$getShortField");
+    }
+  }
+
+  static void check(NullPointerException npe, int mainLine, int medthodLine, String methodName) {
+    System.out.println(methodName);
+    StackTraceElement[] trace = npe.getStackTrace();
+    checkElement(trace[0], "Main", methodName, "Main.java", medthodLine);
+    checkElement(trace[1], "Main", "main", "Main.java", mainLine);
+  }
+
+  static void checkElement(StackTraceElement element,
+                           String declaringClass, String methodName,
+                           String fileName, int lineNumber) {
+    assertEquals(declaringClass, element.getClassName());
+    assertEquals(methodName, element.getMethodName());
+    assertEquals(fileName, element.getFileName());
+    assertEquals(lineNumber, element.getLineNumber());
+  }
+
+  static void assertEquals(Object expected, Object actual) {
+    if (!expected.equals(actual)) {
+      String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+      throw new AssertionError(msg);
+    }
+  }
+
+  static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new AssertionError("Expected " + expected + " got " + actual);
+    }
+  }
+
+}
diff --git a/test/439-swap-double/expected.txt b/test/439-swap-double/expected.txt
new file mode 100644
index 0000000..019c901
--- /dev/null
+++ b/test/439-swap-double/expected.txt
@@ -0,0 +1,4 @@
+-26.0
+-24.0
+-22.0
+-20.0
diff --git a/test/439-swap-double/info.txt b/test/439-swap-double/info.txt
new file mode 100644
index 0000000..23447d2
--- /dev/null
+++ b/test/439-swap-double/info.txt
@@ -0,0 +1,2 @@
+Test for the optimizing compiler's parallel swap support in
+the presence of register pairs (in this case, doubles on ARM).
diff --git a/test/439-swap-double/src/Main.java b/test/439-swap-double/src/Main.java
new file mode 100644
index 0000000..da11577
--- /dev/null
+++ b/test/439-swap-double/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+// Test for the optimizing compiler's parallel swap support in
+// the presence of register pairs (in this case, doubles on ARM).
+public class Main {
+  public static void main(String[] args) {
+    new Main().foo();
+  }
+
+  public void foo() {
+    // Do multiple calls to force swapping of registers. Note that
+    // this depends on the calling convention, as a stack-only convention
+    // may not need the swapping.
+    callWithDoubles(a, b, c, d, e, f, g);
+    callWithDoubles(b, c, d, e, f, g, a);
+    callWithDoubles(c, d, e, f, g, a, b);
+    callWithDoubles(d, e, f, g, a, b, c);
+  }
+
+  public static void callWithDoubles(
+      double a, double b, double c, double d, double e, double f, double g) {
+    System.out.println(a - b - c - d - e - f - g);
+  }
+
+  double a = 1.0;
+  double b = 2.0;
+  double c = 3.0;
+  double d = 4.0;
+  double e = 5.0;
+  double f = 6.0;
+  double g = 7.0;
+}
diff --git a/test/440-stmp/expected.txt b/test/440-stmp/expected.txt
new file mode 100644
index 0000000..e995b05
--- /dev/null
+++ b/test/440-stmp/expected.txt
@@ -0,0 +1 @@
+-118.0
diff --git a/test/440-stmp/info.txt b/test/440-stmp/info.txt
new file mode 100644
index 0000000..c4a7bf1
--- /dev/null
+++ b/test/440-stmp/info.txt
@@ -0,0 +1,3 @@
+Regression test for optimizing, that used to consider
+a S/D register a temp, while it conflicted with the
+hard-float calling convention.
diff --git a/test/440-stmp/src/Main.java b/test/440-stmp/src/Main.java
new file mode 100644
index 0000000..2dd10f8
--- /dev/null
+++ b/test/440-stmp/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+  public static void main(String[] args) {
+    new Main().bar();
+  }
+
+  public void bar() {
+    // Use up all available D registers on ARM.
+    baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
+  }
+
+  public static void baz(float a, float b, float c, float d, float e, float f, float g,
+                         float h, float i, float j, float k, float l, float m, float n, float o) {
+    System.out.println(a - b - c - d - e - f - g - h - i - j - k - l - m - n - o);
+  }
+
+  float a = 1.0f;
+  float b = 2.0f;
+  float c = 3.0f;
+  float d = 4.0f;
+  float e = 5.0f;
+  float f = 6.0f;
+  float g = 7.0f;
+  float h = 8.0f;
+  float i = 9.0f;
+  float j = 10.0f;
+  float k = 11.0f;
+  float l = 12.0f;
+  float m = 13.0f;
+  float n = 14.0f;
+  float o = 15.0f;
+}
diff --git a/test/441-checker-inliner/expected.txt b/test/441-checker-inliner/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/441-checker-inliner/expected.txt
diff --git a/test/441-checker-inliner/info.txt b/test/441-checker-inliner/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/441-checker-inliner/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
new file mode 100644
index 0000000..631b140
--- /dev/null
+++ b/test/441-checker-inliner/src/Main.java
@@ -0,0 +1,242 @@
+/*
+* 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.
+*/
+
+public class Main {
+
+  // CHECK-START: void Main.InlineVoid() inliner (before)
+  // CHECK-DAG:     [[Const42:i\d+]] IntConstant 42
+  // CHECK-DAG:                      InvokeStaticOrDirect
+  // CHECK-DAG:                      InvokeStaticOrDirect [ [[Const42]] ]
+
+  // CHECK-START: void Main.InlineVoid() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  public static void InlineVoid() {
+    returnVoid();
+    returnVoidWithOneParameter(42);
+  }
+
+  // CHECK-START: int Main.InlineParameter(int) inliner (before)
+  // CHECK-DAG:     [[Param:i\d+]]  ParameterValue
+  // CHECK-DAG:     [[Result:i\d+]] InvokeStaticOrDirect [ [[Param]] ]
+  // CHECK-DAG:                     Return [ [[Result]] ]
+
+  // CHECK-START: int Main.InlineParameter(int) inliner (after)
+  // CHECK-DAG:     [[Param:i\d+]]  ParameterValue
+  // CHECK-DAG:                     Return [ [[Param]] ]
+
+  public static int InlineParameter(int a) {
+    return returnParameter(a);
+  }
+
+  // CHECK-START: long Main.InlineWideParameter(long) inliner (before)
+  // CHECK-DAG:     [[Param:j\d+]]  ParameterValue
+  // CHECK-DAG:     [[Result:j\d+]] InvokeStaticOrDirect [ [[Param]] ]
+  // CHECK-DAG:                     Return [ [[Result]] ]
+
+  // CHECK-START: long Main.InlineWideParameter(long) inliner (after)
+  // CHECK-DAG:     [[Param:j\d+]]  ParameterValue
+  // CHECK-DAG:                     Return [ [[Param]] ]
+
+  public static long InlineWideParameter(long a) {
+    return returnWideParameter(a);
+  }
+
+  // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
+  // CHECK-DAG:     [[Param:l\d+]]  ParameterValue
+  // CHECK-DAG:     [[Result:l\d+]] InvokeStaticOrDirect [ [[Param]] ]
+  // CHECK-DAG:                     Return [ [[Result]] ]
+
+  // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
+  // CHECK-DAG:     [[Param:l\d+]]  ParameterValue
+  // CHECK-DAG:                     Return [ [[Param]] ]
+
+  public static Object InlineReferenceParameter(Object o) {
+    return returnReferenceParameter(o);
+  }
+
+  // CHECK-START: int Main.InlineInt() inliner (before)
+  // CHECK-DAG:     [[Result:i\d+]] InvokeStaticOrDirect
+  // CHECK-DAG:                     Return [ [[Result]] ]
+
+  // CHECK-START: int Main.InlineInt() inliner (after)
+  // CHECK-DAG:     [[Const4:i\d+]] IntConstant 4
+  // CHECK-DAG:                     Return [ [[Const4]] ]
+
+  public static int InlineInt() {
+    return returnInt();
+  }
+
+  // CHECK-START: long Main.InlineWide() inliner (before)
+  // CHECK-DAG:     [[Result:j\d+]] InvokeStaticOrDirect
+  // CHECK-DAG:                     Return [ [[Result]] ]
+
+  // CHECK-START: long Main.InlineWide() inliner (after)
+  // CHECK-DAG:     [[Const8:j\d+]] LongConstant 8
+  // CHECK-DAG:                     Return [ [[Const8]] ]
+
+  public static long InlineWide() {
+    return returnWide();
+  }
+
+  // CHECK-START: int Main.InlineAdd() inliner (before)
+  // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
+  // CHECK-DAG:     [[Result:i\d+]] InvokeStaticOrDirect
+  // CHECK-DAG:                     Return [ [[Result]] ]
+
+  // CHECK-START: int Main.InlineAdd() inliner (after)
+  // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
+  // CHECK-DAG:     [[Add:i\d+]]    Add [ [[Const3]] [[Const5]] ]
+  // CHECK-DAG:                     Return [ [[Add]] ]
+
+  public static int InlineAdd() {
+    return returnAdd(3, 5);
+  }
+
+  // CHECK-START: int Main.InlineFieldAccess() inliner (before)
+  // CHECK-DAG:     [[After:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                     Return [ [[After]] ]
+
+  // CHECK-START: int Main.InlineFieldAccess() inliner (after)
+  // CHECK-DAG:     [[Const1:i\d+]] IntConstant 1
+  // CHECK-DAG:     [[Before:i\d+]] StaticFieldGet
+  // CHECK-DAG:     [[After:i\d+]]  Add [ [[Before]] [[Const1]] ]
+  // CHECK-DAG:                     StaticFieldSet [ {{l\d+}} [[After]] ]
+  // CHECK-DAG:                     Return [ [[After]] ]
+
+  // CHECK-START: int Main.InlineFieldAccess() inliner (after)
+  // CHECK-NOT:                     InvokeStaticOrDirect
+
+  public static int InlineFieldAccess() {
+    return incCounter();
+  }
+
+  // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before)
+  // CHECK-DAG:     [[Const1:i\d+]] IntConstant 1
+  // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
+  // CHECK-DAG:     [[Add:i\d+]]    InvokeStaticOrDirect [ [[Const1]] [[Const3]] ]
+  // CHECK-DAG:     [[Sub:i\d+]]    InvokeStaticOrDirect [ [[Const5]] [[Const3]] ]
+  // CHECK-DAG:     [[Phi:i\d+]]    Phi [ [[Add]] [[Sub]] ]
+  // CHECK-DAG:                     Return [ [[Phi]] ]
+
+  // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after)
+  // CHECK-DAG:     [[Const1:i\d+]] IntConstant 1
+  // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
+  // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
+  // CHECK-DAG:     [[Add:i\d+]]    Add [ [[Const1]] [[Const3]] ]
+  // CHECK-DAG:     [[Sub:i\d+]]    Sub [ [[Const5]] [[Const3]] ]
+  // CHECK-DAG:     [[Phi:i\d+]]    Phi [ [[Add]] [[Sub]] ]
+  // CHECK-DAG:                     Return [ [[Phi]] ]
+
+  public static int InlineWithControlFlow(boolean cond) {
+    int x, const1, const3, const5;
+    const1 = 1;
+    const3 = 3;
+    const5 = 5;
+    if (cond) {
+      x = returnAdd(const1, const3);
+    } else {
+      x = returnSub(const5, const3);
+    }
+    return x;
+  }
+
+
+  private static void returnVoid() {
+    return;
+  }
+
+  private static void returnVoidWithOneParameter(int a) {
+    return;
+  }
+
+  private static int returnParameter(int a) {
+    return a;
+  }
+
+  private static long returnWideParameter(long a) {
+    return a;
+  }
+
+  private static Object returnReferenceParameter(Object o) {
+    return o;
+  }
+
+  private static int returnInt() {
+    return 4;
+  }
+
+  private static long returnWide() {
+    return 8L;
+  }
+
+  private static int returnAdd(int a, int b) {
+    return a + b;
+  }
+
+  private static int returnSub(int a, int b) {
+    return a - b;
+  }
+
+  private static int counter = 42;
+
+  private static int incCounter() {
+    return ++counter;
+  }
+
+  public static void main(String[] args) {
+    InlineVoid();
+
+    if (InlineInt() != 4) {
+      throw new Error();
+    }
+
+    if (InlineWide() != 8L) {
+      throw new Error();
+    }
+
+    if (InlineParameter(42) != 42) {
+      throw new Error();
+    }
+
+    if (InlineWideParameter(0x100000001L) != 0x100000001L) {
+      throw new Error();
+    }
+
+    if (InlineReferenceParameter(Main.class) != Main.class) {
+      throw new Error();
+    }
+
+    if (InlineAdd() != 8) {
+      throw new Error();
+    }
+
+    if (InlineFieldAccess() != 43 || InlineFieldAccess() != 44) {
+      throw new Error();
+    }
+
+    if (InlineWithControlFlow(true) != 4) {
+      throw new Error();
+    }
+
+    if (InlineWithControlFlow(false) != 2) {
+      throw new Error();
+    }
+  }
+}
diff --git a/test/442-checker-constant-folding/expected.txt b/test/442-checker-constant-folding/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/442-checker-constant-folding/expected.txt
diff --git a/test/442-checker-constant-folding/info.txt b/test/442-checker-constant-folding/info.txt
new file mode 100644
index 0000000..5073972
--- /dev/null
+++ b/test/442-checker-constant-folding/info.txt
@@ -0,0 +1 @@
+Tests constant folding in the optimizing compiler.
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
new file mode 100644
index 0000000..de2c5c7
--- /dev/null
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -0,0 +1,259 @@
+/*
+* 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.
+*/
+
+public class Main {
+
+  /**
+   * Tiny three-register program exercising int constant folding
+   * on negation.
+   */
+
+  // CHECK-START: int Main.IntNegation() constant_folding (before)
+  // CHECK-DAG:     [[Const42:i\d+]]  IntConstant 42
+  // CHECK-DAG:     [[Neg:i\d+]]      Neg [ [[Const42]] ]
+  // CHECK-DAG:                       Return [ [[Neg]] ]
+
+  // CHECK-START: int Main.IntNegation() constant_folding (after)
+  // CHECK-DAG:     [[ConstN42:i\d+]] IntConstant -42
+  // CHECK-DAG:                       Return [ [[ConstN42]] ]
+
+  public static int IntNegation() {
+    int x, y;
+    x = 42;
+    y = -x;
+    return y;
+  }
+
+  /**
+   * Tiny three-register program exercising int constant folding
+   * on addition.
+   */
+
+  // CHECK-START: int Main.IntAddition1() constant_folding (before)
+  // CHECK-DAG:     [[Const1:i\d+]]  IntConstant 1
+  // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
+  // CHECK-DAG:     [[Add:i\d+]]     Add [ [[Const1]] [[Const2]] ]
+  // CHECK-DAG:                      Return [ [[Add]] ]
+
+  // CHECK-START: int Main.IntAddition1() constant_folding (after)
+  // CHECK-DAG:     [[Const3:i\d+]]  IntConstant 3
+  // CHECK-DAG:                      Return [ [[Const3]] ]
+
+  public static int IntAddition1() {
+    int a, b, c;
+    a = 1;
+    b = 2;
+    c = a + b;
+    return c;
+  }
+
+ /**
+  * Small three-register program exercising int constant folding
+  * on addition.
+  */
+
+  // CHECK-START: int Main.IntAddition2() constant_folding (before)
+  // CHECK-DAG:     [[Const1:i\d+]]  IntConstant 1
+  // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
+  // CHECK-DAG:     [[Const5:i\d+]]  IntConstant 5
+  // CHECK-DAG:     [[Const6:i\d+]]  IntConstant 6
+  // CHECK-DAG:     [[Add1:i\d+]]    Add [ [[Const1]] [[Const2]] ]
+  // CHECK-DAG:     [[Add2:i\d+]]    Add [ [[Const5]] [[Const6]] ]
+  // CHECK-DAG:     [[Add3:i\d+]]    Add [ [[Add1]] [[Add2]] ]
+  // CHECK-DAG:                      Return [ [[Add3]] ]
+
+  // CHECK-START: int Main.IntAddition2() constant_folding (after)
+  // CHECK-DAG:     [[Const14:i\d+]] IntConstant 14
+  // CHECK-DAG:                      Return [ [[Const14]] ]
+
+  public static int IntAddition2() {
+    int a, b, c;
+    a = 1;
+    b = 2;
+    a += b;
+    b = 5;
+    c = 6;
+    b += c;
+    c = a + b;
+    return c;
+  }
+
+  /**
+   * Tiny three-register program exercising int constant folding
+   * on subtraction.
+   */
+
+  // CHECK-START: int Main.IntSubtraction() constant_folding (before)
+  // CHECK-DAG:     [[Const6:i\d+]]  IntConstant 6
+  // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
+  // CHECK-DAG:     [[Sub:i\d+]]     Sub [ [[Const6]] [[Const2]] ]
+  // CHECK-DAG:                      Return [ [[Sub]] ]
+
+  // CHECK-START: int Main.IntSubtraction() constant_folding (after)
+  // CHECK-DAG:     [[Const4:i\d+]]  IntConstant 4
+  // CHECK-DAG:                      Return [ [[Const4]] ]
+
+  public static int IntSubtraction() {
+    int a, b, c;
+    a = 6;
+    b = 2;
+    c = a - b;
+    return c;
+  }
+
+  /**
+   * Tiny three-register program exercising long constant folding
+   * on addition.
+   */
+
+  // CHECK-START: long Main.LongAddition() constant_folding (before)
+  // CHECK-DAG:     [[Const1:j\d+]]  LongConstant 1
+  // CHECK-DAG:     [[Const2:j\d+]]  LongConstant 2
+  // CHECK-DAG:     [[Add:j\d+]]     Add [ [[Const1]] [[Const2]] ]
+  // CHECK-DAG:                      Return [ [[Add]] ]
+
+  // CHECK-START: long Main.LongAddition() constant_folding (after)
+  // CHECK-DAG:     [[Const3:j\d+]]  LongConstant 3
+  // CHECK-DAG:                      Return [ [[Const3]] ]
+
+  public static long LongAddition() {
+    long a, b, c;
+    a = 1L;
+    b = 2L;
+    c = a + b;
+    return c;
+  }
+
+  /**
+   * Tiny three-register program exercising long constant folding
+   * on subtraction.
+   */
+
+  // CHECK-START: long Main.LongSubtraction() constant_folding (before)
+  // CHECK-DAG:     [[Const6:j\d+]]  LongConstant 6
+  // CHECK-DAG:     [[Const2:j\d+]]  LongConstant 2
+  // CHECK-DAG:     [[Sub:j\d+]]     Sub [ [[Const6]] [[Const2]] ]
+  // CHECK-DAG:                      Return [ [[Sub]] ]
+
+  // CHECK-START: long Main.LongSubtraction() constant_folding (after)
+  // CHECK-DAG:     [[Const4:j\d+]]  LongConstant 4
+  // CHECK-DAG:                      Return [ [[Const4]] ]
+
+  public static long LongSubtraction() {
+    long a, b, c;
+    a = 6L;
+    b = 2L;
+    c = a - b;
+    return c;
+  }
+
+  /**
+   * Three-register program with a constant (static) condition.
+   */
+
+  // CHECK-START: int Main.StaticCondition() constant_folding (before)
+  // CHECK-DAG:     [[Const7:i\d+]]  IntConstant 7
+  // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
+  // CHECK-DAG:     [[Cond:z\d+]]    GreaterThanOrEqual [ [[Const7]] [[Const2]] ]
+  // CHECK-DAG:                      If [ [[Cond]] ]
+
+  // CHECK-START: int Main.StaticCondition() constant_folding (after)
+  // CHECK-DAG:     [[Const1:i\d+]]  IntConstant 1
+  // CHECK-DAG:                      If [ [[Const1]] ]
+
+  public static int StaticCondition() {
+    int a, b, c;
+    a = 7;
+    b = 2;
+    if (a < b)
+      c = a + b;
+    else
+      c = a - b;
+    return c;
+  }
+
+  /**
+   * Four-variable program with jumps leading to the creation of many
+   * blocks.
+   *
+   * The intent of this test is to ensure that all constant expressions
+   * are actually evaluated at compile-time, thanks to the reverse
+   * (forward) post-order traversal of the the dominator tree.
+   */
+
+  // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before)
+  // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
+  // CHECK-DAG:     [[Const5:i\d+]]  IntConstant 5
+  // CHECK-DAG:     [[Add:i\d+]]     Add [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Sub:i\d+]]     Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Phi:i\d+]]     Phi [ [[Add]] [[Sub]] ]
+  // CHECK-DAG:                      Return [ [[Phi]] ]
+
+  // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
+  // CHECK-DAG:     [[Const3:i\d+]]  IntConstant 3
+  // CHECK-DAG:     [[Const7:i\d+]]  IntConstant 7
+  // CHECK-DAG:     [[Phi:i\d+]]     Phi [ [[Const7]] [[Const3]] ]
+  // CHECK-DAG:                      Return [ [[Phi]] ]
+
+  public static int JumpsAndConditionals(boolean cond) {
+    int a, b, c;
+    a = 5;
+    b = 2;
+    if (cond)
+      c = a + b;
+    else
+      c = a - b;
+    return c;
+  }
+
+  public static void main(String[] args) {
+    if (IntNegation() != -42) {
+      throw new Error();
+    }
+
+    if (IntAddition1() != 3) {
+      throw new Error();
+    }
+
+    if (IntAddition2() != 14) {
+      throw new Error();
+    }
+
+    if (IntSubtraction() != 4) {
+      throw new Error();
+    }
+
+    if (LongAddition() != 3L) {
+      throw new Error();
+    }
+
+    if (LongSubtraction() != 4L) {
+      throw new Error();
+    }
+
+    if (StaticCondition() != 5) {
+      throw new Error();
+    }
+
+    if (JumpsAndConditionals(true) != 7) {
+      throw new Error();
+    }
+
+    if (JumpsAndConditionals(false) != 3) {
+      throw new Error();
+    }
+  }
+}
diff --git a/test/443-not-bool-inline/expected.txt b/test/443-not-bool-inline/expected.txt
new file mode 100644
index 0000000..3ee3849
--- /dev/null
+++ b/test/443-not-bool-inline/expected.txt
@@ -0,0 +1 @@
+Hello World 2
diff --git a/test/443-not-bool-inline/info.txt b/test/443-not-bool-inline/info.txt
new file mode 100644
index 0000000..31f2321
--- /dev/null
+++ b/test/443-not-bool-inline/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing, who used a wrong instruction
+for simplifying Equals(foo, false);
diff --git a/test/443-not-bool-inline/src/Main.java b/test/443-not-bool-inline/src/Main.java
new file mode 100644
index 0000000..3a6f3be
--- /dev/null
+++ b/test/443-not-bool-inline/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    // For some reason, dx wants != for generating if-eq.
+    if (falseField != false) {
+      System.out.println("Hello World 1");
+    }
+
+    if (trueField != false) {
+      System.out.println("Hello World 2");
+    }
+  }
+
+  static boolean falseField = false;
+  static boolean trueField = true;
+}
diff --git a/test/444-checker-nce/expected.txt b/test/444-checker-nce/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/444-checker-nce/expected.txt
diff --git a/test/444-checker-nce/info.txt b/test/444-checker-nce/info.txt
new file mode 100644
index 0000000..78ad0b9
--- /dev/null
+++ b/test/444-checker-nce/info.txt
@@ -0,0 +1 @@
+Tests for NullCheck elimination.
diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java
new file mode 100644
index 0000000..9fb9c46
--- /dev/null
+++ b/test/444-checker-nce/src/Main.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  // CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (before)
+  // CHECK:         NullCheck
+  // CHECK:         InvokeStaticOrDirect
+
+  // CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (after)
+  // CHECK:         NullCheck
+  // CHECK:         InvokeStaticOrDirect
+  public Main keepTest(Main m) {
+    return m.g();
+  }
+
+  // CHECK-START: Main Main.thisTest() instruction_simplifier (before)
+  // CHECK:         NullCheck
+  // CHECK:         InvokeStaticOrDirect
+
+  // CHECK-START: Main Main.thisTest() instruction_simplifier (after)
+  // CHECK-NOT:     NullCheck
+  // CHECK:         InvokeStaticOrDirect
+  public Main thisTest() {
+    return g();
+  }
+
+  // CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (before)
+  // CHECK:         NewInstance
+  // CHECK:         NullCheck
+  // CHECK:         InvokeStaticOrDirect
+  // CHECK:         NullCheck
+  // CHECK:         InvokeStaticOrDirect
+
+  // CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after)
+  // CHECK-NOT:     NullCheck
+  public Main newInstanceRemoveTest() {
+    Main m = new Main();
+    return m.g();
+  }
+
+  // CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (before)
+  // CHECK:         NewArray
+  // CHECK:         NullCheck
+  // CHECK:         ArrayGet
+
+  // CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after)
+  // CHECK:         NewArray
+  // CHECK-NOT:     NullCheck
+  // CHECK:         ArrayGet
+  public Main newArrayRemoveTest() {
+    Main[] ms = new Main[1];
+    return ms[0];
+  }
+
+  // CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (before)
+  // CHECK:         NewInstance
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (after)
+  // CHECK:         NewInstance
+  // CHECK-NOT:     NullCheck
+  public Main ifRemoveTest(boolean flag) {
+    Main m = null;
+    if (flag) {
+      m = new Main();
+    } else {
+      m = new Main(1);
+    }
+    return m.g();
+  }
+
+  // CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (before)
+  // CHECK:         NewInstance
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (after)
+  // CHECK:         NewInstance
+  // CHECK:         NullCheck
+  public Main ifKeepTest(boolean flag) {
+    Main m = null;
+    if (flag) {
+      m = new Main(1);
+    }
+    return m.g();
+  }
+
+  // CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (before)
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (after)
+  // CHECK-NOT:     NullCheck
+  public Main forRemoveTest(int count) {
+    Main a = new Main();
+    Main m = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 2 == 0) {
+        m = a;
+      }
+    }
+    return m.g();
+  }
+
+  // CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (before)
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (after)
+  // CHECK:         NullCheck
+  public Main forKeepTest(int count) {
+    Main a = new Main();
+    Main m = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 2 == 0) {
+        m = a;
+      } else {
+        m = null;
+      }
+    }
+    return m.g();
+  }
+
+  // CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (before)
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (after)
+  // CHECK-NOT:     NullCheck
+  public Main phiFlowRemoveTest(int count) {
+    Main a = new Main();
+    Main m = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 2 == 0) {
+        m = a;
+      }
+    }
+    Main n = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 3 == 0) {
+        n = m;
+      }
+    }
+    return n.g();
+  }
+
+  // CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (before)
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (after)
+  // CHECK:         NullCheck
+  public Main phiFlowKeepTest(int count) {
+    Main a = new Main();
+    Main m = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 2 == 0) {
+        m = a;
+      } else {
+        m = null;
+      }
+    }
+    Main n = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 3 == 0) {
+        n = m;
+      }
+    }
+    return n.g();
+  }
+
+  // CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (before)
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after)
+  // CHECK-NOT:     NullCheck
+  public Main scopeRemoveTest(int count, Main a) {
+    Main m = null;
+    for (int i = 0; i < count; i++) {
+      if (i % 2 == 0) {
+        m = new Main();
+        m.g();
+      } else {
+        m = a;
+      }
+    }
+    return m;
+  }
+
+  // CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (before)
+  // CHECK:         NullCheck
+
+  // CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (after)
+  // CHECK:         NullCheck
+  public Main scopeKeepTest(int count, Main a) {
+    Main m = new Main();
+    for (int i = 0; i < count; i++) {
+      if (i % 2 == 0) {
+        m = a;
+      } else {
+        m = a;
+        m.g();
+      }
+    }
+    return m;
+  }
+
+  public Main() {}
+  public Main(int dummy) {}
+
+  private Main g() {
+    // avoids inlining
+    throw new RuntimeException();
+  }
+
+  public static void main(String[] args) {
+    new Main();
+  }
+
+}
diff --git a/test/445-checker-licm/expected.txt b/test/445-checker-licm/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/445-checker-licm/expected.txt
diff --git a/test/445-checker-licm/info.txt b/test/445-checker-licm/info.txt
new file mode 100644
index 0000000..e09d958
--- /dev/null
+++ b/test/445-checker-licm/info.txt
@@ -0,0 +1 @@
+Checker test for testing loop invariant code motion.
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
new file mode 100644
index 0000000..91ac2ed
--- /dev/null
+++ b/test/445-checker-licm/src/Main.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.div() licm (before)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.div() licm (after)
+  // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.div() licm (after)
+  // CHECK-DAG: Div ( loop_header:null )
+
+  public static int div() {
+    int result = 0;
+    for (int i = 0; i < 10; ++i) {
+      result += staticField / 42;
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.innerDiv() licm (before)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.innerDiv() licm (after)
+  // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.innerDiv() licm (after)
+  // CHECK-DAG: Div ( loop_header:null )
+
+  public static int innerDiv() {
+    int result = 0;
+    for (int i = 0; i < 10; ++i) {
+      for (int j = 0; j < 10; ++j) {
+        result += staticField / 42;
+      }
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.innerDiv2() licm (before)
+  // CHECK-DAG: Mul ( loop_header:{{B4}} )
+
+  // CHECK-START: int Main.innerDiv2() licm (after)
+  // CHECK-DAG: Mul ( loop_header:{{B2}} )
+
+  public static int innerDiv2() {
+    int result = 0;
+    for (int i = 0; i < 10; ++i) {
+      for (int j = 0; j < 10; ++j) {
+        // The operation has been hoisted out of the inner loop.
+        // Note that we depend on the compiler's block numbering to
+        // check if it has been moved.
+        result += staticField * i;
+      }
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.innerDiv3(int, int) licm (before)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.innerDiv3(int, int) licm (after)
+  // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+  public static int innerDiv3(int a, int b) {
+    int result = 0;
+    while (b < 5) {
+      // a might be null, so we can't hoist the operation.
+      result += staticField / a;
+      b++;
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.arrayLength(int[]) licm (before)
+  // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:{{B\d+}} )
+  // CHECK-DAG:                    ArrayLength [ [[NullCheck]] ] ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.arrayLength(int[]) licm (after)
+  // CHECK-NOT:                    NullCheck ( loop_header:{{B\d+}} )
+  // CHECK-NOT:                    ArrayLength ( loop_header:{{B\d+}} )
+
+  // CHECK-START: int Main.arrayLength(int[]) licm (after)
+  // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:null )
+  // CHECK-DAG:                    ArrayLength [ [[NullCheck]] ] ( loop_header:null )
+
+  public static int arrayLength(int[] array) {
+    int result = 0;
+    for (int i = 0; i < array.length; ++i) {
+      result += array[i];
+    }
+    return result;
+  }
+
+  public static int staticField = 42;
+
+  public static void assertEquals(int expected, int actual) {
+    if (expected != actual) {
+      throw new Error("Expected " + expected + ", got " + actual);
+    }
+  }
+
+  public static void main(String[] args) {
+    assertEquals(10, div());
+    assertEquals(100, innerDiv());
+    assertEquals(12, arrayLength(new int[] { 4, 8 }));
+  }
+}
diff --git a/test/446-checker-inliner2/expected.txt b/test/446-checker-inliner2/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/446-checker-inliner2/expected.txt
diff --git a/test/446-checker-inliner2/info.txt b/test/446-checker-inliner2/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/446-checker-inliner2/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java
new file mode 100644
index 0000000..ecf071e
--- /dev/null
+++ b/test/446-checker-inliner2/src/Main.java
@@ -0,0 +1,72 @@
+/*
+* 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.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+  // CHECK-DAG:     [[Field:i\d+]]   InstanceFieldGet
+  // CHECK-DAG:                      Return [ [[Field]] ]
+
+  public static int inlineInstanceCall(Main m) {
+    return m.foo();
+  }
+
+  private int foo() {
+    return field;
+  }
+
+  int field = 42;
+
+  // CHECK-START: int Main.inlineNestedCall() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineNestedCall() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineNestedCall() inliner (after)
+  // CHECK-DAG:     [[Const38:i\d+]] IntConstant 38
+  // CHECK-DAG:                      Return [ [[Const38]] ]
+
+  public static int inlineNestedCall() {
+    return nestedCall();
+  }
+
+  public static int nestedCall() {
+    return bar();
+  }
+
+  public static int bar() {
+    return 38;
+  }
+
+  public static void main(String[] args) {
+    if (inlineInstanceCall(new Main()) != 42) {
+      throw new Error("Expected 42");
+    }
+
+    if (inlineNestedCall() != 38) {
+      throw new Error("Expected 38");
+    }
+  }
+}
diff --git a/test/447-checker-inliner3/expected.txt b/test/447-checker-inliner3/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/447-checker-inliner3/expected.txt
diff --git a/test/447-checker-inliner3/info.txt b/test/447-checker-inliner3/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/447-checker-inliner3/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java
new file mode 100644
index 0000000..db4b236
--- /dev/null
+++ b/test/447-checker-inliner3/src/Main.java
@@ -0,0 +1,77 @@
+/*
+* 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.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.inlineIfThenElse() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineIfThenElse() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  public static int inlineIfThenElse() {
+    return foo(true);
+  }
+
+  private static int foo(boolean value) {
+    if (value) {
+      return 1;
+    } else {
+      return 0;
+    }
+  }
+
+  // CHECK-START: int Main.inlineInLoop() inliner (before)
+  // CHECK-DAG:     InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineInLoop() inliner (after)
+  // CHECK-NOT:     InvokeStaticOrDirect
+
+  public static int inlineInLoop() {
+    int result = 0;
+    for (int i = 0; i < 32; ++i) {
+      result += foo(i % 2 == 0);
+    }
+    return result;
+  }
+
+  // CHECK-START: int Main.inlineInLoopHeader() inliner (before)
+  // CHECK-DAG:     InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineInLoopHeader() inliner (after)
+  // CHECK-NOT:     InvokeStaticOrDirect
+
+  public static int inlineInLoopHeader() {
+    int result = 0;
+    for (int i = 0; i < foo(i % 2 == 0); ++i) {
+      result += 42;
+    }
+    return result;
+  }
+
+  public static void main(String[] args) {
+    if (inlineIfThenElse() != 1) {
+      throw new Error("Expected 1");
+    }
+    if (inlineInLoop() != 16) {
+      throw new Error("Expected 16");
+    }
+    if (inlineInLoopHeader() != 42) {
+      throw new Error("Expected 16");
+    }
+  }
+}
diff --git a/test/448-multiple-returns/expected.txt b/test/448-multiple-returns/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/448-multiple-returns/expected.txt
diff --git a/test/448-multiple-returns/info.txt b/test/448-multiple-returns/info.txt
new file mode 100644
index 0000000..cdd354b
--- /dev/null
+++ b/test/448-multiple-returns/info.txt
@@ -0,0 +1,2 @@
+Tests inlining of a pattern not generated by DX: multiple
+returns in a single method.
diff --git a/test/448-multiple-returns/smali/MultipleReturns.smali b/test/448-multiple-returns/smali/MultipleReturns.smali
new file mode 100644
index 0000000..23815d8
--- /dev/null
+++ b/test/448-multiple-returns/smali/MultipleReturns.smali
@@ -0,0 +1,45 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMultipleReturns;
+
+.super Ljava/lang/Object;
+
+.method public static caller()I
+   .registers 1
+   invoke-static {},  LMultipleReturns;->$opt$CalleeReturnVoid()V
+   invoke-static {},  LMultipleReturns;->$opt$CalleeReturnInt()I
+   move-result v0
+   return v0
+.end method
+
+.method public static $opt$CalleeReturnVoid()V
+   .registers 2
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   if-eq v1, v0, :else
+   return-void
+   :else
+   return-void
+.end method
+
+.method public static $opt$CalleeReturnInt()I
+   .registers 2
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   if-eq v1, v0, :else
+   return v0
+   :else
+   return v1
+.end method
diff --git a/test/448-multiple-returns/src/Main.java b/test/448-multiple-returns/src/Main.java
new file mode 100644
index 0000000..4050ed1
--- /dev/null
+++ b/test/448-multiple-returns/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+  // Workaround for b/18051191.
+  class InnerClass {}
+
+  public static void main(String[] args) throws Exception {
+    Class<?> c = Class.forName("MultipleReturns");
+    Method m = c.getMethod("caller");
+    int result = (Integer)m.invoke(null);
+    if (result != 0) {
+      throw new Error("Expected 0, got " + result);
+    }
+  }
+}
diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/449-checker-bce/expected.txt
diff --git a/test/449-checker-bce/info.txt b/test/449-checker-bce/info.txt
new file mode 100644
index 0000000..0a08808
--- /dev/null
+++ b/test/449-checker-bce/info.txt
@@ -0,0 +1 @@
+Checker test for testing array bounds check elimination.
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
new file mode 100644
index 0000000..5a0e13b
--- /dev/null
+++ b/test/449-checker-bce/src/Main.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+  // CHECK-START: int Main.sieve(int) BCE (before)
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+  // CHECK: BoundsCheck
+  // CHECK: ArrayGet
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+
+  // CHECK-START: int Main.sieve(int) BCE (after)
+  // CHECK-NOT: BoundsCheck
+  // CHECK: ArraySet
+  // CHECK-NOT: BoundsCheck
+  // CHECK: ArrayGet
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+
+  static int sieve(int size) {
+    int primeCount = 0;
+    boolean[] flags = new boolean[size + 1];
+    for (int i = 1; i < size; i++) flags[i] = true; // Can eliminate.
+    for (int i = 2; i < size; i++) {
+      if (flags[i]) { // Can eliminate.
+        primeCount++;
+        for (int k = i + 1; k <= size; k += i)
+          flags[k - 1] = false; // Can't eliminate yet due to (k+i) may overflow.
+      }
+    }
+    return primeCount;
+  }
+
+  // CHECK-START: void Main.narrow(int[], int) BCE (before)
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+
+  // CHECK-START: void Main.narrow(int[], int) BCE (after)
+  // CHECK-NOT: BoundsCheck
+  // CHECK: ArraySet
+  // CHECK-NOT: BoundsCheck
+  // CHECK: ArraySet
+  // CHECK: BoundsCheck
+  // CHECK: ArraySet
+
+  static void narrow(int array[], int offset) {
+    if (offset < 0) {
+      return;
+    }
+    if (offset < array.length) {
+      // offset is in range [0, array.length-1].
+      // Bounds check can be eliminated.
+      array[offset] = 1;
+
+      int biased_offset1 = offset + 1;
+      // biased_offset1 is in range [1, array.length].
+      if (biased_offset1 < array.length) {
+        // biased_offset1 is in range [1, array.length-1].
+        // Bounds check can be eliminated.
+        array[biased_offset1] = 1;
+      }
+
+      int biased_offset2 = offset + 0x70000000;
+      // biased_offset2 is in range [0x70000000, array.length-1+0x70000000].
+      // It may overflow and be negative.
+      if (biased_offset2 < array.length) {
+        // Even with this test, biased_offset2 can be negative so we can't
+        // eliminate this bounds check.
+        array[biased_offset2] = 1;
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    sieve(20);
+  }
+}
diff --git a/test/704-multiply-accumulate/expected.txt b/test/704-multiply-accumulate/expected.txt
new file mode 100644
index 0000000..76f5a5a
--- /dev/null
+++ b/test/704-multiply-accumulate/expected.txt
@@ -0,0 +1 @@
+Done!
diff --git a/test/704-multiply-accumulate/info.txt b/test/704-multiply-accumulate/info.txt
new file mode 100644
index 0000000..a12fd44
--- /dev/null
+++ b/test/704-multiply-accumulate/info.txt
@@ -0,0 +1 @@
+Tests for multiply accumulate operations.
diff --git a/test/704-multiply-accumulate/src/Main.java b/test/704-multiply-accumulate/src/Main.java
new file mode 100644
index 0000000..7404b9b
--- /dev/null
+++ b/test/704-multiply-accumulate/src/Main.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+    static int imax = Integer.MAX_VALUE;
+    static int imin = Integer.MIN_VALUE;
+    static long lmax = Long.MAX_VALUE;
+    static long lmin = Long.MIN_VALUE;
+    static CA ca;
+
+    public static void expectEquals(long expected, long result) {
+        if (expected != result) {
+            throw new Error("Expected: " + expected + ", found: " + result);
+        }
+    }
+
+    public static void expectEquals(int expected, int result) {
+        if (expected != result) {
+            throw new Error("Expected: " + expected + ", found: " + result);
+        }
+    }
+
+    public static void test_int() {
+        int result = 0;
+        int a = imax;
+        int b = imin;
+        int c = 10;
+        int d = c;
+        int tmp = 0;
+        int [] ia = new int[5];
+        for (int i = 0; i < 100; i++) {
+            tmp = i*c;
+            result += i*i;
+            result = i - tmp;
+        }
+        expectEquals(result, -891);
+
+        result = c*c + (result - c);
+        expectEquals(result, -801);
+
+        result = a + a*a;
+        expectEquals(result, -2147483648);
+
+        result = b + b*b;
+        expectEquals(result, -2147483648);
+
+        result = b - a*a;
+        expectEquals(result, 2147483647);
+
+        result = d*d;
+        d++;
+        result += result;
+        expectEquals(result, 200);
+
+        result = c*c;
+        tmp++;
+        result += result;
+        expectEquals(result, 200);
+
+        result = 0;
+        try {
+            result = c*c;
+            ia[c] = d;  // array out of bound.
+            result += d;
+        } catch (Exception e) {
+        }
+        expectEquals(result, 100);
+
+        CA obj = new CA();
+        result = a*c + obj.ia;
+        expectEquals(result, 2);
+
+        result = 0;
+        obj = ca;
+        try {
+            result = a*c;
+            tmp = obj.ia;
+            result = result + tmp;
+        } catch (Exception e) {
+        }
+        expectEquals(result, -10);
+    }
+
+    public static void test_long() {
+        long result = 0;
+        long a = lmax;
+        long b = lmin;
+        long c = 10;
+        long d = c;
+        long tmp = 0;
+        int [] ia = new int[5];
+        for (long i = 0; i < 100; i++) {
+            tmp = i*c;
+            result += i*i;
+            result = i - tmp;
+        }
+        expectEquals(result, -891L);
+
+        result = c*c + (result - c);
+        expectEquals(result, -801L);
+
+        result = a + a*a;
+        expectEquals(result, -9223372036854775808L);
+
+        result = b + b*b;
+        expectEquals(result, -9223372036854775808L);
+
+        result = b - a*a;
+        expectEquals(result, 9223372036854775807L);
+
+        result = d*d;
+        d++;
+        result += result;
+        expectEquals(result, 200L);
+
+        result = c*c;
+        tmp++;
+        result += result;
+        expectEquals(result, 200L);
+
+        result = 0;
+        int index = 10;
+        try {
+            result = c*c;
+            ia[index] = 10;  // array out of bound.
+            result += d;
+        } catch (Exception e) {
+        }
+        expectEquals(result, 100L);
+
+        CA obj = new CA();
+        result = a*c + obj.la;
+        expectEquals(result, 113L);
+
+        result = 0;
+        obj = ca;
+        try {
+            result = a*c;
+            tmp = obj.la;
+            result = result + tmp;
+        } catch (Exception e) {
+        }
+        expectEquals(result, -10L);
+    }
+
+    public static void main(String[] args) {
+        test_int();
+        test_long();
+        System.out.println("Done!");
+    }
+
+}
+
+class CA {
+    public int ia = 12;
+    public long la = 123L;
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 0f7001f..019dc14 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -8,4 +8,10 @@
 invoke-super abstract
 BadCaseInOpRegRegReg
 CmpLong
+FloatIntConstPassing
+b/18718277
+b/18800943 (1)
+b/18800943 (2)
+MoveExc
+MoveExceptionOnEntry
 Done!
diff --git a/test/800-smali/smali/FloatIntConstPassing.smali b/test/800-smali/smali/FloatIntConstPassing.smali
new file mode 100644
index 0000000..a2916c5
--- /dev/null
+++ b/test/800-smali/smali/FloatIntConstPassing.smali
@@ -0,0 +1,29 @@
+.class public LFloatIntConstPassing;
+
+.super Ljava/lang/Object;
+
+.method public static getInt(I)I
+  .registers 2
+  const/4 v0, 1
+  add-int/2addr v0, p0
+  return v0
+.end method
+
+.method public static getFloat(F)F
+  .registers 2
+  const/4 v0, 0
+  mul-float/2addr v0, p0
+  return v0
+.end method
+
+.method public static run()I
+  .registers 3
+  const/4 v0, 1
+  invoke-static {v0}, LFloatIntConstPassing;->getInt(I)I
+  move-result v1
+  invoke-static {v0}, LFloatIntConstPassing;->getFloat(F)F
+  move-result v2
+  float-to-int v2, v2
+  add-int/2addr v1, v2
+  return v1
+.end method
diff --git a/test/800-smali/smali/b_18718277.smali b/test/800-smali/smali/b_18718277.smali
new file mode 100644
index 0000000..b14ad20
--- /dev/null
+++ b/test/800-smali/smali/b_18718277.smali
@@ -0,0 +1,29 @@
+.class public LB18718277;
+
+.super Ljava/lang/Object;
+
+.method public static helper(I)I
+    .locals 1
+    add-int/lit8 v0, p0, 2
+    neg-int v0, v0
+    return v0
+.end method
+
+.method public static getInt()I
+    .registers 2
+    const/4 v1, 3
+    invoke-static {v1}, LB18718277;->helper(I)I
+    move-result v0
+    :outer_loop
+    if-eqz v1, :exit_outer_loop
+    const/4 v0, 0
+    if-eqz v0, :skip_dead_loop
+    :dead_loop
+    add-int/2addr v0, v0
+    if-gez v0, :dead_loop
+    :skip_dead_loop
+    add-int/lit8 v1, v1, -1
+    goto :outer_loop
+    :exit_outer_loop
+    return v0
+.end method
diff --git a/test/800-smali/smali/b_18800943_1.smali b/test/800-smali/smali/b_18800943_1.smali
new file mode 100644
index 0000000..868438e
--- /dev/null
+++ b/test/800-smali/smali/b_18800943_1.smali
@@ -0,0 +1,9 @@
+.class public LB18800943_1;
+.super Ljava/lang/Object;
+
+# This constructor should fail verification as the object is not initialized by a super-call.
+.method public constructor <init>()V
+.registers 1
+       nop
+       return-void
+.end method
diff --git a/test/800-smali/smali/b_18800943_2.smali b/test/800-smali/smali/b_18800943_2.smali
new file mode 100644
index 0000000..6052ada
--- /dev/null
+++ b/test/800-smali/smali/b_18800943_2.smali
@@ -0,0 +1,9 @@
+.class public LB18800943_2;
+.super Ljava/lang/Object;
+
+# This constructor should fail verification as the object is not initialized by a super-call.
+.method public constructor <init>()V
+.registers 1
+       const v0, 0x0
+       return-void
+.end method
diff --git a/test/800-smali/smali/move_exc.smali b/test/800-smali/smali/move_exc.smali
new file mode 100644
index 0000000..4ade4bc
--- /dev/null
+++ b/test/800-smali/smali/move_exc.smali
@@ -0,0 +1,29 @@
+.class public LMoveExc;
+.super Ljava/lang/Object;
+
+
+.method public constructor <init>()V
+.registers 1
+       invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+       return-void
+.end method
+
+.method public static run()V
+.registers 6
+:Label1
+       const v1, 15
+       const v2, 0
+       div-int v0, v1, v2
+
+:Label2
+       goto :Label4
+
+:Label3
+       move-exception v3
+       throw v3
+
+:Label4
+       return-void
+
+.catchall {:Label1 .. :Label2} :Label3
+.end method
diff --git a/test/800-smali/smali/move_exception_on_entry.smali b/test/800-smali/smali/move_exception_on_entry.smali
new file mode 100644
index 0000000..e7da2e3
--- /dev/null
+++ b/test/800-smali/smali/move_exception_on_entry.smali
@@ -0,0 +1,30 @@
+.class public LMoveExceptionOnEntry;
+
+.super Ljava/lang/Object;
+
+# Test that we cannot have a catch-handler with move-exception at the beginning of a method.
+
+.method public static moveExceptionOnEntry(I)I
+.registers 4
+:Label1
+       move-exception v2
+       const v1, 100
+       move v0, p0
+       add-int/lit8 p0, p0, 1
+
+:Label2
+       invoke-static {v0}, LMoveExceptionOnEntry;->foo(I)V
+
+:Label3
+       return v1
+
+.catchall {:Label2 .. :Label3} :Label1
+.end method
+
+.method public static foo(I)I
+.registers 4
+:Label1
+       return-void
+
+.end method
+
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index f2c1ab5..b23896d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -50,20 +50,33 @@
         // Create the test cases.
         testCases = new LinkedList<TestCase>();
         testCases.add(new TestCase("PackedSwitch", "PackedSwitch", "packedSwitch",
-          new Object[]{123}, null, 123));
+                new Object[]{123}, null, 123));
 
         testCases.add(new TestCase("b/17790197", "B17790197", "getInt", null, null, 100));
-        testCases.add(new TestCase("b/17978759", "B17978759", "test", null, new VerifyError(), null));
+        testCases.add(new TestCase("b/17978759", "B17978759", "test", null, new VerifyError(),
+                null));
         testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt",
-            new Object[]{100}, null, 100));
+                new Object[]{100}, null, 100));
         testCases.add(new TestCase("negLong", "negLong", "negLong", null, null, 122142L));
         testCases.add(new TestCase("sameFieldNames", "sameFieldNames", "getInt", null, null, 7));
         testCases.add(new TestCase("b/18380491", "B18380491ConcreteClass", "foo",
-            new Object[]{42}, null, 42));
+                new Object[]{42}, null, 42));
         testCases.add(new TestCase("invoke-super abstract", "B18380491ConcreteClass", "foo",
-            new Object[]{0}, new AbstractMethodError(), null));
-        testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null, null, 2));
+                new Object[]{0}, new AbstractMethodError(), null));
+        testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null,
+                null, 2));
         testCases.add(new TestCase("CmpLong", "CmpLong", "run", null, null, 0));
+        testCases.add(new TestCase("FloatIntConstPassing", "FloatIntConstPassing", "run", null,
+                null, 2));
+        testCases.add(new TestCase("b/18718277", "B18718277", "getInt", null, null, 0));
+        testCases.add(new TestCase("b/18800943 (1)", "B18800943_1", "n_a", null, new VerifyError(),
+                0));
+        testCases.add(new TestCase("b/18800943 (2)", "B18800943_2", "n_a", null, new VerifyError(),
+                0));
+        testCases.add(new TestCase("MoveExc", "MoveExc", "run", null, new ArithmeticException(),
+                null));
+        testCases.add(new TestCase("MoveExceptionOnEntry", "MoveExceptionOnEntry",
+            "moveExceptionOnEntry", new Object[]{0}, new VerifyError(), null));
     }
 
     public void runTests() {
diff --git a/test/802-deoptimization/expected.txt b/test/802-deoptimization/expected.txt
new file mode 100644
index 0000000..d5f1f08
--- /dev/null
+++ b/test/802-deoptimization/expected.txt
@@ -0,0 +1 @@
+CatchHandlerOnEntryWithoutMoveException OK
diff --git a/test/802-deoptimization/info.txt b/test/802-deoptimization/info.txt
new file mode 100644
index 0000000..104d40f
--- /dev/null
+++ b/test/802-deoptimization/info.txt
@@ -0,0 +1 @@
+Tests related to deoptimization
diff --git a/test/802-deoptimization/smali/catch_handler_on_entry.smali b/test/802-deoptimization/smali/catch_handler_on_entry.smali
new file mode 100644
index 0000000..836101e
--- /dev/null
+++ b/test/802-deoptimization/smali/catch_handler_on_entry.smali
@@ -0,0 +1,29 @@
+.class public LCatchHandlerOnEntry;
+
+.super Ljava/lang/Object;
+
+# Test we can execute a method starting with a catch handler (without
+# move-exception instruction). This method must be called with parameter
+# initialized to 0.
+#
+# We execute the catch handler (Label1) for the first time with p0 == 0.
+# We save its value in v0, increment p0 to 1 and execute the div-int
+# instruction (Label2) which throws an ArithmeticException (division by zero).
+# That exception is caught by the catch handler so we execute it a second time.
+# Now p0 == 1. When we we execute the div-int instruction, it succeeds and we
+# return its result: this is the initial value of v1 because "v1 = v1 / 1".
+.method public static catchHandlerOnEntry(I)I
+.registers 4
+:Label1
+       const v1, 100
+       move v0, p0
+       add-int/lit8 p0, p0, 1
+
+:Label2
+       invoke-static {v0}, LCatchHandlerOnEntryHelper;->throwExceptionDuringDeopt(I)V
+
+:Label3
+       return v1
+
+.catchall {:Label2 .. :Label3} :Label1
+.end method
diff --git a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
new file mode 100644
index 0000000..9c41abf
--- /dev/null
+++ b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/**
+ * Helper class used by smali test classes.
+ */
+public class CatchHandlerOnEntryHelper {
+
+  public static void throwExceptionDuringDeopt(int i) {
+    if (i == 0) {
+      DeoptimizationController.startDeoptimization();
+      throw new RuntimeException("Test exception");
+    } else {
+      DeoptimizationController.stopDeoptimization();
+    }
+  }
+}
diff --git a/test/802-deoptimization/src/DeoptimizationController.java b/test/802-deoptimization/src/DeoptimizationController.java
new file mode 100644
index 0000000..c926669
--- /dev/null
+++ b/test/802-deoptimization/src/DeoptimizationController.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Controls deoptimization using dalvik.system.VMDebug class.
+ */
+public class DeoptimizationController {
+  private static File createTempFile() throws Exception {
+    try {
+      return  File.createTempFile("test", ".trace");
+    } catch (IOException e) {
+      System.setProperty("java.io.tmpdir", "/data/local/tmp");
+      try {
+        return File.createTempFile("test", ".trace");
+      } catch (IOException e2) {
+        System.setProperty("java.io.tmpdir", "/sdcard");
+        return File.createTempFile("test", ".trace");
+      }
+    }
+  }
+
+  public static void startDeoptimization() {
+    try {
+      File tempFile = createTempFile();
+      tempFile.deleteOnExit();
+      String tempFileName = tempFile.getPath();
+
+      VMDebug.startMethodTracing(tempFileName, 0, 0, false, 1000);
+      if (VMDebug.getMethodTracingMode() == 0) {
+        throw new IllegalStateException("Not tracing.");
+      }
+    } catch (Exception exc) {
+      exc.printStackTrace(System.err);
+    }
+  }
+
+  public static void stopDeoptimization() {
+    try {
+      VMDebug.stopMethodTracing();
+      if (VMDebug.getMethodTracingMode() != 0) {
+        throw new IllegalStateException("Still tracing.");
+      }
+    } catch (Exception exc) {
+      exc.printStackTrace(System.err);
+    }
+  }
+
+  private static class VMDebug {
+    private static final Method startMethodTracingMethod;
+    private static final Method stopMethodTracingMethod;
+    private static final Method getMethodTracingModeMethod;
+
+    static {
+      try {
+        Class<?> c = Class.forName("dalvik.system.VMDebug");
+        startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
+            Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE);
+        stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
+        getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    public static void startMethodTracing(String filename, int bufferSize, int flags,
+        boolean samplingEnabled, int intervalUs) throws Exception {
+      startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled,
+          intervalUs);
+    }
+    public static void stopMethodTracing() throws Exception {
+      stopMethodTracingMethod.invoke(null);
+    }
+    public static int getMethodTracingMode() throws Exception {
+      return (int) getMethodTracingModeMethod.invoke(null);
+    }
+  }
+}
diff --git a/test/802-deoptimization/src/Main.java b/test/802-deoptimization/src/Main.java
new file mode 100644
index 0000000..c8780de
--- /dev/null
+++ b/test/802-deoptimization/src/Main.java
@@ -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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+  private static final int EXPECTED_RESULT = 100;
+  private static final int PARAMETER_VALUE = 0;
+
+  public static void main(String[] args) throws Throwable {
+    testCatchHandlerOnEntryWithoutMoveException();
+  }
+
+  /**
+   * Tests we correctly execute a method starting with a catch handler without
+   * move-exception instruction when throwing an exception during deoptimization.
+   */
+  private static void testCatchHandlerOnEntryWithoutMoveException() throws Throwable {
+    Class<?> c = Class.forName("CatchHandlerOnEntry");
+    Method m = c.getMethod("catchHandlerOnEntry", int.class);
+    Object result = m.invoke(null, new Object[]{PARAMETER_VALUE});
+    int intResult = ((Integer) result).intValue();
+    if (intResult == EXPECTED_RESULT) {
+      System.out.println("CatchHandlerOnEntryWithoutMoveException OK");
+    } else {
+      System.out.println("CatchHandlerOnEntryWithoutMoveException KO: result==" + intResult);
+    }
+  }
+}
+
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index c9c0475..e64df5c 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -55,7 +55,6 @@
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
-  include external/libcxx/libcxx.mk
   ifeq ($$(art_target_or_host),target)
     $(call set-target-local-clang-vars)
     $(call set-target-local-cflags-vars,debug)
diff --git a/test/Android.libnativebridgetest.mk b/test/Android.libnativebridgetest.mk
index 1b20e69..452278a 100644
--- a/test/Android.libnativebridgetest.mk
+++ b/test/Android.libnativebridgetest.mk
@@ -47,7 +47,6 @@
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
   LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libnativebridgetest.mk
-  include external/libcxx/libcxx.mk
   ifeq ($$(art_target_or_host),target)
     $(call set-target-local-clang-vars)
     $(call set-target-local-cflags-vars,debug)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index b85685b..a8f2001 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -166,7 +166,8 @@
 # Tests that are timing sensitive and flaky on heavily loaded systems.
 TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
   053-wait-some \
-  055-enum-performance
+  055-enum-performance \
+  133-static-invoke-super
 
  # disable timing sensitive tests on "dist" builds.
 ifdef dist_goal
@@ -179,7 +180,8 @@
 
 # Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
 TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \
-  116-nodex2oat
+  116-nodex2oat \
+  118-noimage-dex2oat
 
 ifneq (,$(filter prebuild,$(PREBUILD_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),prebuild, \
@@ -203,7 +205,9 @@
 # Note 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without
 # --prebuild --relocate
 TEST_ART_BROKEN_NO_RELOCATE_TESTS := \
-  117-nopatchoat
+  117-nopatchoat \
+  118-noimage-dex2oat \
+  119-noimage-patchoat
 
 ifneq (,$(filter no-relocate,$(RELOCATE_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -214,9 +218,7 @@
 TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
 
 # Tests that are broken with GC stress.
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
-  004-SignalTest \
-  114-ParallelGC
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
 
 ifneq (,$(filter gcstress,$(GC_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -231,6 +233,14 @@
     $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),115-native-bridge, \
     $(ALL_ADDRESS_SIZES))
 
+# 130-hprof dumps the heap and runs hprof-conv to check whether the file is somewhat readable. This
+# is only possible on the host.
+# TODO: Turn off all the other combinations, this is more about testing actual ART code. A gtest is
+#       very hard to write here, as (for a complete test) JDWP must be set up.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
+    $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+    $(PICTEST_TYPES),130-hprof,$(ALL_ADDRESS_SIZES))
+
 # 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.
@@ -276,6 +286,7 @@
   117-nopatchoat \
   118-noimage-dex2oat \
   119-noimage-patchoat \
+  131-structural-change \
 
 ifneq (,$(filter ndebug,$(RUN_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \
@@ -296,33 +307,22 @@
 
 TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
 
+# Tests known to be broken for the optimizing compiler on 32-bit targets due to
+# inability to allocate registers for methods with long values.
+TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS := \
+  441-checker-inliner \
+  442-checker-constant-folding \
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS),32)
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS :=
+
 # Known broken tests for the arm64 optimizing compiler backend.
-TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \
-  003-omnibus-opcodes \
-  004-ReferenceMap \
-  005-annotations \
-  009-instanceof \
-  010-instance \
-  023-many-interfaces \
-  044-proxy \
-  045-reflect-array \
-  046-reflect \
-  047-returns \
-  062-character-encodings \
-  063-process-manager \
-  068-classloader \
-  069-field-type \
-  071-dexfile \
-  106-exceptions2 \
-  107-int-math2 \
-  201-built-in-exception-detail-messages \
-  407-arrays \
-  412-new-array \
-  422-instanceof \
-  424-checkcast \
-  427-bounds \
-  430-live-register-slow-path \
-  800-smali \
+TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
 
 ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -333,8 +333,9 @@
 TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
 
 # Known broken tests for the optimizing compiler.
-TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS := \
-  099-vmdebug \ # b/18098594
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS += 099-vmdebug # b/18098594
+TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS += 802-deoptimization # b/18547544
 
 ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -342,6 +343,14 @@
       $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
 endif
 
+# If ART_USE_OPTIMIZING_COMPILER is set to true, then the default core.art has been
+# compiled with the optimizing compiler.
+ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
 TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
 
 
@@ -599,7 +608,7 @@
   endif
 $$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
 .PHONY: $$(run_test_rule_name)
-$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $$(prereq_rule)
+$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $(HOST_OUT_EXECUTABLES)/hprof-conv $$(prereq_rule)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
 	  DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
 	    SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
diff --git a/test/Transaction/Transaction.java b/test/Transaction/Transaction.java
index 9ca7fbf..00e1fbb 100644
--- a/test/Transaction/Transaction.java
+++ b/test/Transaction/Transaction.java
@@ -25,13 +25,57 @@
       }
     }
 
-    static class BlacklistedClass {
+    static class FinalizableAbortClass {
+      public static AbortHelperClass finalizableObject;
       static {
-        NativeSupport.native_call();
+        finalizableObject = new AbortHelperClass();
       }
     }
 
-    static class NativeSupport {
-      public static native void native_call();
+    static class NativeCallAbortClass {
+      static {
+        AbortHelperClass.nativeMethod();
+      }
+    }
+
+    static class SynchronizedNativeCallAbortClass {
+      static {
+        synchronized (SynchronizedNativeCallAbortClass.class) {
+          AbortHelperClass.nativeMethod();
+        }
+      }
+    }
+
+    static class CatchNativeCallAbortClass {
+      static {
+        try {
+          AbortHelperClass.nativeMethod();
+        } catch (Throwable e) {
+          // ignore exception.
+        }
+      }
+    }
+
+    static class MultipleNativeCallAbortClass {
+      static {
+        // Call native method but catch the transaction exception.
+        try {
+          AbortHelperClass.nativeMethod();
+        } catch (Throwable e) {
+          // ignore exception.
+        }
+
+        // Call another native method.
+        AbortHelperClass.nativeMethod2();
+      }
+    }
+
+    // Helper class to abort transaction: finalizable class with natve methods.
+    static class AbortHelperClass {
+      public void finalize() throws Throwable {
+        super.finalize();
+      }
+      public static native void nativeMethod();
+      public static native void nativeMethod2();
     }
 }
diff --git a/test/etc/default-build b/test/etc/default-build
index 6731ad3..58c9564 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -17,6 +17,22 @@
 # Stop if something fails.
 set -e
 
+DX_FLAGS=""
+
+while true; do
+  if [ "x$1" = "x--dx-option" ]; then
+    shift
+    option="$1"
+    DX_FLAGS="${DX_FLAGS} $option"
+    shift
+  elif expr "x$1" : "x--" >/dev/null 2>&1; then
+    echo "unknown $0 option: $1" 1>&2
+    exit 1
+  else
+    break
+  fi
+done
+
 if [ -e classes.dex ]; then
   zip $TEST_NAME.jar classes.dex
   exit 0
@@ -30,7 +46,8 @@
 fi
 
 if [ ${NEED_DEX} = "true" ]; then
-  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+    --dump-width=1000 ${DX_FLAGS} classes
 fi
 
 if [ -d smali ]; then
@@ -43,7 +60,8 @@
   mkdir classes-ex
   ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
   if [ ${NEED_DEX} = "true" ]; then
-    ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex --dump-width=1000 classes-ex
+    ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
+      --dump-width=1000 ${DX_FLAGS} classes-ex
 
     # quick shuffle so that the stored name is "classes.dex"
     mv classes.dex classes-1.dex
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5c0f83f..907218a 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -10,7 +10,7 @@
 
 ANDROID_ROOT="/system"
 ARCHITECTURES_32="(arm|x86|mips|none)"
-ARCHITECTURES_64="(arm64|x86_64|none)"
+ARCHITECTURES_64="(arm64|x86_64|mips64|none)"
 ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
 BOOT_IMAGE=""
 COMPILE_FLAGS=""
@@ -37,7 +37,8 @@
 RELOCATE="y"
 SECONDARY_DEX=""
 TIME_OUT="y"
-TIME_OUT_VALUE=5m
+# Value in minutes.
+TIME_OUT_VALUE=10
 USE_GDB="n"
 USE_JVM="n"
 VERIFY="y"
@@ -308,6 +309,9 @@
                   $DALVIKVM_BOOT_OPT \
                   -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN"
 
+# Remove whitespace.
+dex2oat_cmdline=$(echo $dex2oat_cmdline)
+dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
 
 if [ "$HOST" = "n" ]; then
     adb root > /dev/null
@@ -377,7 +381,13 @@
 
     if [ "$TIME_OUT" = "y" ]; then
       # Add timeout command if time out is desired.
-      cmdline="timeout $TIME_OUT_VALUE $cmdline"
+      #
+      # Note: We use nested timeouts. The inner timeout sends SIGRTMIN+2 (usually 36) to ART, which
+      #       will induce a full thread dump before abort. However, dumping threads might deadlock,
+      #       so the outer timeout sends the regular SIGTERM after an additional minute to ensure
+      #       termination (without dumping all threads).
+      TIME_PLUS_ONE=$(($TIME_OUT_VALUE + 1))
+      cmdline="timeout ${TIME_PLUS_ONE}m timeout -s SIGRTMIN+2 ${TIME_OUT_VALUE}m $cmdline"
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
diff --git a/test/run-test b/test/run-test
index 2abc1fa..8c47663 100755
--- a/test/run-test
+++ b/test/run-test
@@ -39,6 +39,7 @@
 else
   tmp_dir="${TMPDIR}/$USER/${test_dir}"
 fi
+checker="${progdir}/../tools/checker.py"
 
 export JAVA="java"
 export JAVAC="javac -g"
@@ -74,8 +75,10 @@
 check_cmd="check"
 output="output.txt"
 build_output="build-output.txt"
+cfg_output="cfg-output.txt"
 lib="libartd.so"
 run_args="--quiet"
+build_args=""
 
 prebuild_mode="yes"
 target_mode="yes"
@@ -258,6 +261,9 @@
     elif [ "x$1" = "x--always-clean" ]; then
         always_clean="yes"
         shift
+    elif [ "x$1" = "x--dex2oat-swap" ]; then
+        run_args="${run_args} --dex2oat-swap"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -278,7 +284,8 @@
 mkdir -p $tmp_dir
 
 if [ "$basic_verify" = "true" ]; then
-  run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify"
+  # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
+  run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0"
 fi
 if [ "$gc_verify" = "true" ]; then
   run_args="${run_args} --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc"
@@ -294,7 +301,7 @@
 # Try to map the suffix64 flag and what we find in ${ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
 function guess_arch_name() {
     grep32bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm|x86|mips)$'`
-    grep64bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm64|x86_64)$'`
+    grep64bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm64|x86_64|mips64)$'`
     if [ "x${suffix64}" = "x64" ]; then
         target_arch_name=${grep64bit}
     else
@@ -452,6 +459,7 @@
         echo "    --gcverify            Run with gc verification"
         echo "    --always-clean        Delete the test files even if the test fails."
         echo "    --android-root [path] The path on target for the android root. (/system by default)."
+        echo "    --dex2oat-swap       Use a dex2oat swap file."
     ) 1>&2
     exit 1
 fi
@@ -498,6 +506,21 @@
 
 export TEST_NAME=`basename ${test_dir}`
 
+# Tests named '<number>-checker-*' will also have their CFGs verified with
+# Checker when compiled with Optimizing on host.
+if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then
+  # Build Checker DEX files without dx's optimizations so the input to dex2oat
+  # better resembles the Java source. We always build the DEX the same way, even
+  # if Checker is not invoked and the test only runs the program.
+  build_args="${build_args} --dx-option --no-optimize"
+
+  if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$target_mode" = "no" ]; then
+    run_checker="yes"
+    run_args="${run_args} -Xcompiler-option --dump-cfg=$tmp_dir/$cfg_output \
+                          -Xcompiler-option -j1"
+  fi
+fi
+
 # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB.
 file_size_limit=2048
 if echo "$test_dir" | grep 089; then
@@ -513,24 +536,37 @@
 good_build="yes"
 good_run="yes"
 if [ "$dev_mode" = "yes" ]; then
-    "./${build}" 2>&1
+    "./${build}" $build_args 2>&1
     build_exit="$?"
     echo "build exit status: $build_exit" 1>&2
     if [ "$build_exit" = '0' ]; then
         echo "${test_dir}: running..." 1>&2
         "./${run}" $run_args "$@" 2>&1
         run_exit="$?"
-        echo "run exit status: $run_exit" 1>&2
+
         if [ "$run_exit" = "0" ]; then
-            good="yes"
+            if [ "$run_checker" = "yes" ]; then
+                "$checker" "$cfg_output" "$tmp_dir" 2>&1
+                checker_exit="$?"
+                if [ "$checker_exit" = "0" ]; then
+                    good="yes"
+                fi
+                echo "checker exit status: $checker_exit" 1>&2
+            else
+                good="yes"
+            fi
         fi
+        echo "run exit status: $run_exit" 1>&2
     fi
 elif [ "$update_mode" = "yes" ]; then
-    "./${build}" >"$build_output" 2>&1
+    "./${build}" $build_args >"$build_output" 2>&1
     build_exit="$?"
     if [ "$build_exit" = '0' ]; then
         echo "${test_dir}: running..." 1>&2
         "./${run}" $run_args "$@" >"$output" 2>&1
+        if [ "$run_checker" = "yes" ]; then
+          "$checker" -q "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+        fi
         sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
         good="yes"
     else
@@ -539,7 +575,7 @@
     fi
 elif [ "$build_only" = "yes" ]; then
     good="yes"
-    "./${build}" >"$build_output" 2>&1
+    "./${build}" $build_args >"$build_output" 2>&1
     build_exit="$?"
     if [ "$build_exit" '!=' '0' ]; then
         cp "$build_output" "$output"
@@ -554,7 +590,7 @@
     find $tmp_dir -mindepth 1  ! -regex ".*/\(.*jar\|$output\|$expected\)" | xargs rm -rf
     exit 0
 else
-    "./${build}" >"$build_output" 2>&1
+    "./${build}" $build_args >"$build_output" 2>&1
     build_exit="$?"
     if [ "$build_exit" = '0' ]; then
         echo "${test_dir}: running..." 1>&2
@@ -563,6 +599,15 @@
         if [ "$run_exit" != "0" ]; then
             echo "run exit status: $run_exit" 1>&2
             good_run="no"
+        elif [ "$run_checker" = "yes" ]; then
+            "$checker" -q "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+            checker_exit="$?"
+            if [ "$checker_exit" != "0" ]; then
+                echo "checker exit status: $checker_exit" 1>&2
+                good_run="no"
+            else
+                good_run="yes"
+            fi
         else
             good_run="yes"
         fi
diff --git a/tools/art b/tools/art
index af96b47..2408f9f 100644
--- a/tools/art
+++ b/tools/art
@@ -1,5 +1,3 @@
-#!/bin/bash
-#
 # Copyright (C) 2011 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,6 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# This script is used on host and device. It uses a common subset
+# shell dialect that should work on the host (e.g. bash), and
+# Android (e.g. mksh).
+
 function follow_links() {
   if [ z"$BASH_SOURCE" != z ]; then
     file="$BASH_SOURCE"
@@ -28,7 +30,8 @@
 }
 
 function find_libdir() {
-  if [ "$(readlink "$ANDROID_ROOT/bin/$DALVIKVM")" = "dalvikvm64" ]; then
+  # Use realpath instead of readlink because Android does not have a readlink.
+  if [ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" = "$(realpath "$ANDROID_ROOT/bin/dalvikvm64")" ]; then
     echo "lib64"
   else
     echo "lib"
@@ -70,16 +73,22 @@
 PROG_NAME="$(follow_links)"
 PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
 ANDROID_ROOT=$PROG_DIR/..
-ANDROID_DATA=$PWD/android-data$$
 LIBDIR=$(find_libdir)
 LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
 
+DELETE_ANDROID_DATA=false
+# If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own,
+# and ensure we delete it at the end.
+if [ "$ANDROID_DATA" = "/data" ] || [ "$ANDROID_DATA" = "" ]; then
+  ANDROID_DATA=$PWD/android-data$$
+  mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
+  DELETE_ANDROID_DATA=true
+fi
 
 if [ z"$PERF" != z ]; then
   invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
 fi
 
-mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
 ANDROID_DATA=$ANDROID_DATA \
   ANDROID_ROOT=$ANDROID_ROOT \
   LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
@@ -97,7 +106,9 @@
   fi
   echo "Perf data saved in: $ANDROID_DATA/perf.data"
 else
-  rm -rf $ANDROID_DATA
+  if [ "$DELETE_ANDROID_DATA" = "true" ]; then
+    rm -rf $ANDROID_DATA
+  fi
 fi
 
 exit $EXIT_STATUS
diff --git a/tools/checker.py b/tools/checker.py
new file mode 100755
index 0000000..0bce236
--- /dev/null
+++ b/tools/checker.py
@@ -0,0 +1,777 @@
+#!/usr/bin/env python2
+#
+# 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.
+
+
+# Checker is a testing tool which compiles a given test file and compares the
+# state of the control-flow graph before and after each optimization pass
+# against a set of assertions specified alongside the tests.
+#
+# Tests are written in Java, turned into DEX and compiled with the Optimizing
+# compiler. "Check lines" are assertions formatted as comments of the Java file.
+# They begin with prefix 'CHECK' followed by a pattern that the engine attempts
+# to match in the compiler-generated output.
+#
+# Assertions are tested in groups which correspond to the individual compiler
+# passes. Each group of check lines therefore must start with a 'CHECK-START'
+# header which specifies the output group it should be tested against. The group
+# name must exactly match one of the groups recognized in the output (they can
+# be listed with the '--list-groups' command-line flag).
+#
+# Matching of check lines is carried out in the order of appearance in the
+# source file. There are three types of check lines:
+#  - CHECK:     Must match an output line which appears in the output group
+#               later than lines matched against any preceeding checks. Output
+#               lines must therefore match the check lines in the same order.
+#               These are referred to as "in-order" checks in the code.
+#  - CHECK-DAG: Must match an output line which appears in the output group
+#               later than lines matched against any preceeding in-order checks.
+#               In other words, the order of output lines does not matter
+#               between consecutive DAG checks.
+#  - CHECK-NOT: Must not match any output line which appears in the output group
+#               later than lines matched against any preceeding checks and
+#               earlier than lines matched against any subsequent checks.
+#               Surrounding non-negative checks (or boundaries of the group)
+#               therefore create a scope within which the assertion is verified.
+#
+# Check-line patterns are treated as plain text rather than regular expressions
+# but are whitespace agnostic.
+#
+# Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If
+# curly brackets need to be used inside the body of the regex, they need to be
+# enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse
+# the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'.
+#
+# Regex patterns can be named and referenced later. A new variable is defined
+# with '[[name:regex]]' and can be referenced with '[[name]]'. Variables are
+# only valid within the scope of the defining group. Within a group they cannot
+# be redefined or used undefined.
+#
+# Example:
+#   The following assertions can be placed in a Java source file:
+#
+#   // CHECK-START: int MyClass.MyMethod() constant_folding (after)
+#   // CHECK:         [[ID:i[0-9]+]] IntConstant {{11|22}}
+#   // CHECK:                        Return [ [[ID]] ]
+#
+#   The engine will attempt to match the check lines against the output of the
+#   group named on the first line. Together they verify that the CFG after
+#   constant folding returns an integer constant with value either 11 or 22.
+#
+
+from __future__ import print_function
+import argparse
+import os
+import re
+import shutil
+import sys
+import tempfile
+
+class Logger(object):
+
+  class Level(object):
+    NoOutput, Error, Info = range(3)
+
+  class Color(object):
+    Default, Blue, Gray, Purple, Red = range(5)
+
+    @staticmethod
+    def terminalCode(color, out=sys.stdout):
+      if not out.isatty():
+        return ''
+      elif color == Logger.Color.Blue:
+        return '\033[94m'
+      elif color == Logger.Color.Gray:
+        return '\033[37m'
+      elif color == Logger.Color.Purple:
+        return '\033[95m'
+      elif color == Logger.Color.Red:
+        return '\033[91m'
+      else:
+        return '\033[0m'
+
+  Verbosity = Level.Info
+
+  @staticmethod
+  def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout):
+    if level <= Logger.Verbosity:
+      text = Logger.Color.terminalCode(color, out) + text + \
+             Logger.Color.terminalCode(Logger.Color.Default, out)
+      if newLine:
+        print(text, file=out)
+      else:
+        print(text, end="", file=out)
+      out.flush()
+
+  @staticmethod
+  def fail(msg, file=None, line=-1):
+    location = ""
+    if file:
+      location += file + ":"
+    if line > 0:
+      location += str(line) + ":"
+    if location:
+      location += " "
+
+    Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr)
+    Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr)
+    Logger.log(msg, Logger.Level.Error, out=sys.stderr)
+    sys.exit(msg)
+
+  @staticmethod
+  def startTest(name):
+    Logger.log("TEST ", color=Logger.Color.Purple, newLine=False)
+    Logger.log(name + "... ", newLine=False)
+
+  @staticmethod
+  def testPassed():
+    Logger.log("PASS", color=Logger.Color.Blue)
+
+  @staticmethod
+  def testFailed(msg, file=None, line=-1):
+    Logger.log("FAIL", color=Logger.Color.Red)
+    Logger.fail(msg, file, line)
+
+class CommonEqualityMixin:
+  """Mixin for class equality as equality of the fields."""
+  def __eq__(self, other):
+    return (isinstance(other, self.__class__)
+           and self.__dict__ == other.__dict__)
+
+  def __ne__(self, other):
+    return not self.__eq__(other)
+
+  def __repr__(self):
+    return "<%s: %s>" % (type(self).__name__, str(self.__dict__))
+
+
+class CheckElement(CommonEqualityMixin):
+  """Single element of the check line."""
+
+  class Variant(object):
+    """Supported language constructs."""
+    Text, Pattern, VarRef, VarDef, Separator = range(5)
+
+  rStartOptional = r"("
+  rEndOptional = r")?"
+
+  rName = r"([a-zA-Z][a-zA-Z0-9]*)"
+  rRegex = r"(.+?)"
+  rPatternStartSym = r"(\{\{)"
+  rPatternEndSym = r"(\}\})"
+  rVariableStartSym = r"(\[\[)"
+  rVariableEndSym = r"(\]\])"
+  rVariableSeparator = r"(:)"
+
+  regexPattern = rPatternStartSym + rRegex + rPatternEndSym
+  regexVariable = rVariableStartSym + \
+                    rName + \
+                    (rStartOptional + rVariableSeparator + rRegex + rEndOptional) + \
+                  rVariableEndSym
+
+  def __init__(self, variant, name, pattern):
+    self.variant = variant
+    self.name = name
+    self.pattern = pattern
+
+  @staticmethod
+  def newSeparator():
+    return CheckElement(CheckElement.Variant.Separator, None, None)
+
+  @staticmethod
+  def parseText(text):
+    return CheckElement(CheckElement.Variant.Text, None, re.escape(text))
+
+  @staticmethod
+  def parsePattern(patternElem):
+    return CheckElement(CheckElement.Variant.Pattern, None, patternElem[2:-2])
+
+  @staticmethod
+  def parseVariable(varElem):
+    colonPos = varElem.find(":")
+    if colonPos == -1:
+      # Variable reference
+      name = varElem[2:-2]
+      return CheckElement(CheckElement.Variant.VarRef, name, None)
+    else:
+      # Variable definition
+      name = varElem[2:colonPos]
+      body = varElem[colonPos+1:-2]
+      return CheckElement(CheckElement.Variant.VarDef, name, body)
+
+class CheckLine(CommonEqualityMixin):
+  """Representation of a single assertion in the check file formed of one or
+     more regex elements. Matching against an output line is successful only
+     if all regex elements can be matched in the given order."""
+
+  class Variant(object):
+    """Supported types of assertions."""
+    InOrder, DAG, Not = range(3)
+
+  def __init__(self, content, variant=Variant.InOrder, fileName=None, lineNo=-1):
+    self.fileName = fileName
+    self.lineNo = lineNo
+    self.content = content.strip()
+
+    self.variant = variant
+    self.lineParts = self.__parse(self.content)
+    if not self.lineParts:
+      Logger.fail("Empty check line", self.fileName, self.lineNo)
+
+    if self.variant == CheckLine.Variant.Not:
+      for elem in self.lineParts:
+        if elem.variant == CheckElement.Variant.VarDef:
+          Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
+
+  def __eq__(self, other):
+    return (isinstance(other, self.__class__) and
+            self.variant == other.variant and
+            self.lineParts == other.lineParts)
+
+  # Returns True if the given Match object was at the beginning of the line.
+  def __isMatchAtStart(self, match):
+    return (match is not None) and (match.start() == 0)
+
+  # Takes in a list of Match objects and returns the minimal start point among
+  # them. If there aren't any successful matches it returns the length of
+  # the searched string.
+  def __firstMatch(self, matches, string):
+    starts = map(lambda m: len(string) if m is None else m.start(), matches)
+    return min(starts)
+
+  # This method parses the content of a check line stripped of the initial
+  # comment symbol and the CHECK keyword.
+  def __parse(self, line):
+    lineParts = []
+    # Loop as long as there is something to parse.
+    while line:
+      # Search for the nearest occurrence of the special markers.
+      matchWhitespace = re.search(r"\s+", line)
+      matchPattern = re.search(CheckElement.regexPattern, line)
+      matchVariable = re.search(CheckElement.regexVariable, line)
+
+      # If one of the above was identified at the current position, extract them
+      # from the line, parse them and add to the list of line parts.
+      if self.__isMatchAtStart(matchWhitespace):
+        # A whitespace in the check line creates a new separator of line parts.
+        # This allows for ignored output between the previous and next parts.
+        line = line[matchWhitespace.end():]
+        lineParts.append(CheckElement.newSeparator())
+      elif self.__isMatchAtStart(matchPattern):
+        pattern = line[0:matchPattern.end()]
+        line = line[matchPattern.end():]
+        lineParts.append(CheckElement.parsePattern(pattern))
+      elif self.__isMatchAtStart(matchVariable):
+        var = line[0:matchVariable.end()]
+        line = line[matchVariable.end():]
+        lineParts.append(CheckElement.parseVariable(var))
+      else:
+        # If we're not currently looking at a special marker, this is a plain
+        # text match all the way until the first special marker (or the end
+        # of the line).
+        firstMatch = self.__firstMatch([ matchWhitespace, matchPattern, matchVariable ], line)
+        text = line[0:firstMatch]
+        line = line[firstMatch:]
+        lineParts.append(CheckElement.parseText(text))
+    return lineParts
+
+  # Returns the regex pattern to be matched in the output line. Variable
+  # references are substituted with their current values provided in the
+  # 'varState' argument.
+  # An exception is raised if a referenced variable is undefined.
+  def __generatePattern(self, linePart, varState):
+    if linePart.variant == CheckElement.Variant.VarRef:
+      try:
+        return re.escape(varState[linePart.name])
+      except KeyError:
+        Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"",
+                          self.fileName, self.lineNo)
+    else:
+      return linePart.pattern
+
+  def __isSeparated(self, outputLine, matchStart):
+    return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace())
+
+  # Attempts to match the check line against a line from the output file with
+  # the given initial variable values. It returns the new variable state if
+  # successful and None otherwise.
+  def match(self, outputLine, initialVarState):
+    # Do the full matching on a shadow copy of the variable state. If the
+    # matching fails half-way, we will not need to revert the state.
+    varState = dict(initialVarState)
+
+    matchStart = 0
+    isAfterSeparator = True
+
+    # Now try to parse all of the parts of the check line in the right order.
+    # Variable values are updated on-the-fly, meaning that a variable can
+    # be referenced immediately after its definition.
+    for part in self.lineParts:
+      if part.variant == CheckElement.Variant.Separator:
+        isAfterSeparator = True
+        continue
+
+      # Find the earliest match for this line part.
+      pattern = self.__generatePattern(part, varState)
+      while True:
+        match = re.search(pattern, outputLine[matchStart:])
+        if (match is None) or (not isAfterSeparator and not self.__isMatchAtStart(match)):
+          return None
+        matchEnd = matchStart + match.end()
+        matchStart += match.start()
+
+        # Check if this is a valid match if we expect a whitespace separator
+        # before the matched text. Otherwise loop and look for another match.
+        if not isAfterSeparator or self.__isSeparated(outputLine, matchStart):
+          break
+        else:
+          matchStart += 1
+
+      if part.variant == CheckElement.Variant.VarDef:
+        if part.name in varState:
+          Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"",
+                            self.fileName, self.lineNo)
+        varState[part.name] = outputLine[matchStart:matchEnd]
+
+      matchStart = matchEnd
+      isAfterSeparator = False
+
+    # All parts were successfully matched. Return the new variable state.
+    return varState
+
+
+class CheckGroup(CommonEqualityMixin):
+  """Represents a named collection of check lines which are to be matched
+     against an output group of the same name."""
+
+  def __init__(self, name, lines, fileName=None, lineNo=-1):
+    self.fileName = fileName
+    self.lineNo = lineNo
+
+    if not name:
+      Logger.fail("Check group does not have a name", self.fileName, self.lineNo)
+    if not lines:
+      Logger.fail("Check group does not have a body", self.fileName, self.lineNo)
+
+    self.name = name
+    self.lines = lines
+
+  def __eq__(self, other):
+    return (isinstance(other, self.__class__) and
+            self.name == other.name and
+            self.lines == other.lines)
+
+  def __headAndTail(self, list):
+    return list[0], list[1:]
+
+  # Splits a list of check lines at index 'i' such that lines[i] is the first
+  # element whose variant is not equal to the given parameter.
+  def __splitByVariant(self, lines, variant):
+    i = 0
+    while i < len(lines) and lines[i].variant == variant:
+      i += 1
+    return lines[:i], lines[i:]
+
+  # Extracts the first sequence of check lines which are independent of each
+  # other's match location, i.e. either consecutive DAG lines or a single
+  # InOrder line. Any Not lines preceeding this sequence are also extracted.
+  def __nextIndependentChecks(self, checkLines):
+    notChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.Not)
+    if not checkLines:
+      return notChecks, [], []
+
+    head, tail = self.__headAndTail(checkLines)
+    if head.variant == CheckLine.Variant.InOrder:
+      return notChecks, [head], tail
+    else:
+      assert head.variant == CheckLine.Variant.DAG
+      independentChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.DAG)
+      return notChecks, independentChecks, checkLines
+
+  # If successful, returns the line number of the first output line matching the
+  # check line and the updated variable state. Otherwise returns -1 and None,
+  # respectively. The 'lineFilter' parameter can be used to supply a list of
+  # line numbers (counting from 1) which should be skipped.
+  def __findFirstMatch(self, checkLine, outputLines, startLineNo, lineFilter, varState):
+    matchLineNo = startLineNo
+    for outputLine in outputLines:
+      if matchLineNo not in lineFilter:
+        newVarState = checkLine.match(outputLine, varState)
+        if newVarState is not None:
+          return matchLineNo, newVarState
+      matchLineNo += 1
+    return -1, None
+
+  # Matches the given positive check lines against the output in order of
+  # appearance. Variable state is propagated but the scope of the search remains
+  # the same for all checks. Each output line can only be matched once.
+  # If all check lines are matched, the resulting variable state is returned
+  # together with the remaining output. The function also returns output lines
+  # which appear before either of the matched lines so they can be tested
+  # against Not checks.
+  def __matchIndependentChecks(self, checkLines, outputLines, startLineNo, varState):
+    # If no checks are provided, skip over the entire output.
+    if not checkLines:
+      return outputLines, [], startLineNo + len(outputLines), varState
+
+    # Keep track of which lines have been matched.
+    matchedLines = []
+
+    # Find first unused output line which matches each check line.
+    for checkLine in checkLines:
+      matchLineNo, varState = \
+        self.__findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState)
+      if varState is None:
+        Logger.testFailed("Could not match check line \"" + checkLine.content + "\" " +
+                          "starting from output line " + str(startLineNo),
+                          self.fileName, checkLine.lineNo)
+      matchedLines.append(matchLineNo)
+
+    # Return new variable state and the output lines which lie outside the
+    # match locations of this independent group.
+    minMatchLineNo = min(matchedLines)
+    maxMatchLineNo = max(matchedLines)
+    preceedingLines = outputLines[:minMatchLineNo - startLineNo]
+    remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:]
+    return preceedingLines, remainingLines, maxMatchLineNo + 1, varState
+
+  # Makes sure that the given check lines do not match any of the given output
+  # lines. Variable state does not change.
+  def __matchNotLines(self, checkLines, outputLines, startLineNo, varState):
+    for checkLine in checkLines:
+      assert checkLine.variant == CheckLine.Variant.Not
+      matchLineNo, matchVarState = \
+        self.__findFirstMatch(checkLine, outputLines, startLineNo, [], varState)
+      if matchVarState is not None:
+        Logger.testFailed("CHECK-NOT line \"" + checkLine.content + "\" matches output line " + \
+                          str(matchLineNo), self.fileName, checkLine.lineNo)
+
+  # Matches the check lines in this group against an output group. It is
+  # responsible for running the checks in the right order and scope, and
+  # for propagating the variable state between the check lines.
+  def match(self, outputGroup):
+    varState = {}
+    checkLines = self.lines
+    outputLines = outputGroup.body
+    startLineNo = outputGroup.lineNo
+
+    while checkLines:
+      # Extract the next sequence of location-independent checks to be matched.
+      notChecks, independentChecks, checkLines = self.__nextIndependentChecks(checkLines)
+
+      # Match the independent checks.
+      notOutput, outputLines, newStartLineNo, newVarState = \
+        self.__matchIndependentChecks(independentChecks, outputLines, startLineNo, varState)
+
+      # Run the Not checks against the output lines which lie between the last
+      # two independent groups or the bounds of the output.
+      self.__matchNotLines(notChecks, notOutput, startLineNo, varState)
+
+      # Update variable state.
+      startLineNo = newStartLineNo
+      varState = newVarState
+
+class OutputGroup(CommonEqualityMixin):
+  """Represents a named part of the test output against which a check group of
+     the same name is to be matched."""
+
+  def __init__(self, name, body, fileName=None, lineNo=-1):
+    if not name:
+      Logger.fail("Output group does not have a name", fileName, lineNo)
+    if not body:
+      Logger.fail("Output group does not have a body", fileName, lineNo)
+
+    self.name = name
+    self.body = body
+    self.lineNo = lineNo
+
+  def __eq__(self, other):
+    return (isinstance(other, self.__class__) and
+            self.name == other.name and
+            self.body == other.body)
+
+
+class FileSplitMixin(object):
+  """Mixin for representing text files which need to be split into smaller
+     chunks before being parsed."""
+
+  def _parseStream(self, stream):
+    lineNo = 0
+    allGroups = []
+    currentGroup = None
+
+    for line in stream:
+      lineNo += 1
+      line = line.strip()
+      if not line:
+        continue
+
+      # Let the child class process the line and return information about it.
+      # The _processLine method can modify the content of the line (or delete it
+      # entirely) and specify whether it starts a new group.
+      processedLine, newGroupName = self._processLine(line, lineNo)
+      if newGroupName is not None:
+        currentGroup = (newGroupName, [], lineNo)
+        allGroups.append(currentGroup)
+      if processedLine is not None:
+        if currentGroup is not None:
+          currentGroup[1].append(processedLine)
+        else:
+          self._exceptionLineOutsideGroup(line, lineNo)
+
+    # Finally, take the generated line groups and let the child class process
+    # each one before storing the final outcome.
+    return list(map(lambda group: self._processGroup(group[0], group[1], group[2]), allGroups))
+
+
+class CheckFile(FileSplitMixin):
+  """Collection of check groups extracted from the input test file."""
+
+  def __init__(self, prefix, checkStream, fileName=None):
+    self.fileName = fileName
+    self.prefix = prefix
+    self.groups = self._parseStream(checkStream)
+
+  # Attempts to parse a check line. The regex searches for a comment symbol
+  # followed by the CHECK keyword, given attribute and a colon at the very
+  # beginning of the line. Whitespaces are ignored.
+  def _extractLine(self, prefix, line):
+    rIgnoreWhitespace = r"\s*"
+    rCommentSymbols = [r"//", r"#"]
+    regexPrefix = rIgnoreWhitespace + \
+                  r"(" + r"|".join(rCommentSymbols) + r")" + \
+                  rIgnoreWhitespace + \
+                  prefix + r":"
+
+    # The 'match' function succeeds only if the pattern is matched at the
+    # beginning of the line.
+    match = re.match(regexPrefix, line)
+    if match is not None:
+      return line[match.end():].strip()
+    else:
+      return None
+
+  # This function is invoked on each line of the check file and returns a pair
+  # which instructs the parser how the line should be handled. If the line is to
+  # be included in the current check group, it is returned in the first value.
+  # If the line starts a new check group, the name of the group is returned in
+  # the second value.
+  def _processLine(self, line, lineNo):
+    # Lines beginning with 'CHECK-START' start a new check group.
+    startLine = self._extractLine(self.prefix + "-START", line)
+    if startLine is not None:
+      return None, startLine
+
+    # Lines starting only with 'CHECK' are matched in order.
+    plainLine = self._extractLine(self.prefix, line)
+    if plainLine is not None:
+      return (plainLine, CheckLine.Variant.InOrder, lineNo), None
+
+    # 'CHECK-DAG' lines are no-order assertions.
+    dagLine = self._extractLine(self.prefix + "-DAG", line)
+    if dagLine is not None:
+      return (dagLine, CheckLine.Variant.DAG, lineNo), None
+
+    # 'CHECK-NOT' lines are no-order negative assertions.
+    notLine = self._extractLine(self.prefix + "-NOT", line)
+    if notLine is not None:
+      return (notLine, CheckLine.Variant.Not, lineNo), None
+
+    # Other lines are ignored.
+    return None, None
+
+  def _exceptionLineOutsideGroup(self, line, lineNo):
+    Logger.fail("Check line not inside a group", self.fileName, lineNo)
+
+  # Constructs a check group from the parser-collected check lines.
+  def _processGroup(self, name, lines, lineNo):
+    checkLines = list(map(lambda line: CheckLine(line[0], line[1], self.fileName, line[2]), lines))
+    return CheckGroup(name, checkLines, self.fileName, lineNo)
+
+  def match(self, outputFile):
+    for checkGroup in self.groups:
+      # TODO: Currently does not handle multiple occurrences of the same group
+      # name, e.g. when a pass is run multiple times. It will always try to
+      # match a check group against the first output group of the same name.
+      outputGroup = outputFile.findGroup(checkGroup.name)
+      if outputGroup is None:
+        Logger.fail("Group \"" + checkGroup.name + "\" not found in the output",
+                    self.fileName, checkGroup.lineNo)
+      Logger.startTest(checkGroup.name)
+      checkGroup.match(outputGroup)
+      Logger.testPassed()
+
+
+class OutputFile(FileSplitMixin):
+  """Representation of the output generated by the test and split into groups
+     within which the checks are performed.
+
+     C1visualizer format is parsed with a state machine which differentiates
+     between the 'compilation' and 'cfg' blocks. The former marks the beginning
+     of a method. It is parsed for the method's name but otherwise ignored. Each
+     subsequent CFG block represents one stage of the compilation pipeline and
+     is parsed into an output group named "<method name> <pass name>".
+     """
+
+  class ParsingState:
+    OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4)
+
+  def __init__(self, outputStream, fileName=None):
+    self.fileName = fileName
+
+    # Initialize the state machine
+    self.lastMethodName = None
+    self.state = OutputFile.ParsingState.OutsideBlock
+    self.groups = self._parseStream(outputStream)
+
+  # This function is invoked on each line of the output file and returns a pair
+  # which instructs the parser how the line should be handled. If the line is to
+  # be included in the current group, it is returned in the first value. If the
+  # line starts a new output group, the name of the group is returned in the
+  # second value.
+  def _processLine(self, line, lineNo):
+    if self.state == OutputFile.ParsingState.StartingCfgBlock:
+      # Previous line started a new 'cfg' block which means that this one must
+      # contain the name of the pass (this is enforced by C1visualizer).
+      if re.match("name\s+\"[^\"]+\"", line):
+        # Extract the pass name, prepend it with the name of the method and
+        # return as the beginning of a new group.
+        self.state = OutputFile.ParsingState.InsideCfgBlock
+        return (None, self.lastMethodName + " " + line.split("\"")[1])
+      else:
+        Logger.fail("Expected output group name", self.fileName, lineNo)
+
+    elif self.state == OutputFile.ParsingState.InsideCfgBlock:
+      if line == "end_cfg":
+        self.state = OutputFile.ParsingState.OutsideBlock
+        return (None, None)
+      else:
+        return (line, None)
+
+    elif self.state == OutputFile.ParsingState.InsideCompilationBlock:
+      # Search for the method's name. Format: method "<name>"
+      if re.match("method\s+\"[^\"]*\"", line):
+        methodName = line.split("\"")[1].strip()
+        if not methodName:
+          Logger.fail("Empty method name in output", self.fileName, lineNo)
+        self.lastMethodName = methodName
+      elif line == "end_compilation":
+        self.state = OutputFile.ParsingState.OutsideBlock
+      return (None, None)
+
+    else:
+      assert self.state == OutputFile.ParsingState.OutsideBlock
+      if line == "begin_cfg":
+        # The line starts a new group but we'll wait until the next line from
+        # which we can extract the name of the pass.
+        if self.lastMethodName is None:
+          Logger.fail("Expected method header", self.fileName, lineNo)
+        self.state = OutputFile.ParsingState.StartingCfgBlock
+        return (None, None)
+      elif line == "begin_compilation":
+        self.state = OutputFile.ParsingState.InsideCompilationBlock
+        return (None, None)
+      else:
+        Logger.fail("Output line not inside a group", self.fileName, lineNo)
+
+  # Constructs an output group from the parser-collected output lines.
+  def _processGroup(self, name, lines, lineNo):
+    return OutputGroup(name, lines, self.fileName, lineNo + 1)
+
+  def findGroup(self, name):
+    for group in self.groups:
+      if group.name == name:
+        return group
+    return None
+
+
+def ParseArguments():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("tested_file",
+                      help="text file the checks should be verified against")
+  parser.add_argument("source_path", nargs="?",
+                      help="path to file/folder with checking annotations")
+  parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX",
+                      help="prefix of checks in the test files (default: CHECK)")
+  parser.add_argument("--list-groups", dest="list_groups", action="store_true",
+                      help="print a list of all groups found in the tested file")
+  parser.add_argument("--dump-group", dest="dump_group", metavar="GROUP",
+                      help="print the contents of an output group")
+  parser.add_argument("-q", "--quiet", action="store_true",
+                      help="print only errors")
+  return parser.parse_args()
+
+
+def ListGroups(outputFilename):
+  outputFile = OutputFile(open(outputFilename, "r"))
+  for group in outputFile.groups:
+    Logger.log(group.name)
+
+
+def DumpGroup(outputFilename, groupName):
+  outputFile = OutputFile(open(outputFilename, "r"))
+  group = outputFile.findGroup(groupName)
+  if group:
+    lineNo = group.lineNo
+    maxLineNo = lineNo + len(group.body)
+    lenLineNo = len(str(maxLineNo)) + 2
+    for line in group.body:
+      Logger.log((str(lineNo) + ":").ljust(lenLineNo) + line)
+      lineNo += 1
+  else:
+    Logger.fail("Group \"" + groupName + "\" not found in the output")
+
+
+# Returns a list of files to scan for check annotations in the given path. Path
+# to a file is returned as a single-element list, directories are recursively
+# traversed and all '.java' files returned.
+def FindCheckFiles(path):
+  if not path:
+    Logger.fail("No source path provided")
+  elif os.path.isfile(path):
+    return [ path ]
+  elif os.path.isdir(path):
+    foundFiles = []
+    for root, dirs, files in os.walk(path):
+      for file in files:
+        if os.path.splitext(file)[1] == ".java":
+          foundFiles.append(os.path.join(root, file))
+    return foundFiles
+  else:
+    Logger.fail("Source path \"" + path + "\" not found")
+
+
+def RunChecks(checkPrefix, checkPath, outputFilename):
+  outputBaseName = os.path.basename(outputFilename)
+  outputFile = OutputFile(open(outputFilename, "r"), outputBaseName)
+
+  for checkFilename in FindCheckFiles(checkPath):
+    checkBaseName = os.path.basename(checkFilename)
+    checkFile = CheckFile(checkPrefix, open(checkFilename, "r"), checkBaseName)
+    checkFile.match(outputFile)
+
+
+if __name__ == "__main__":
+  args = ParseArguments()
+
+  if args.quiet:
+    Logger.Verbosity = Logger.Level.Error
+
+  if args.list_groups:
+    ListGroups(args.tested_file)
+  elif args.dump_group:
+    DumpGroup(args.tested_file, args.dump_group)
+  else:
+    RunChecks(args.check_prefix, args.source_path, args.tested_file)
diff --git a/tools/checker_test.py b/tools/checker_test.py
new file mode 100755
index 0000000..667ca90
--- /dev/null
+++ b/tools/checker_test.py
@@ -0,0 +1,474 @@
+#!/usr/bin/env python2
+#
+# 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.
+
+# This is a test file which exercises all feautres supported by the domain-
+# specific markup language implemented by Checker.
+
+import checker
+import io
+import unittest
+
+# The parent type of exception expected to be thrown by Checker during tests.
+# It must be specific enough to not cover exceptions thrown due to actual flaws
+# in Checker.
+CheckerException = SystemExit
+
+
+class TestCheckFile_PrefixExtraction(unittest.TestCase):
+  def __tryParse(self, string):
+    checkFile = checker.CheckFile(None, [])
+    return checkFile._extractLine("CHECK", string)
+
+  def test_InvalidFormat(self):
+    self.assertIsNone(self.__tryParse("CHECK"))
+    self.assertIsNone(self.__tryParse(":CHECK"))
+    self.assertIsNone(self.__tryParse("CHECK:"))
+    self.assertIsNone(self.__tryParse("//CHECK"))
+    self.assertIsNone(self.__tryParse("#CHECK"))
+
+    self.assertIsNotNone(self.__tryParse("//CHECK:foo"))
+    self.assertIsNotNone(self.__tryParse("#CHECK:bar"))
+
+  def test_InvalidLabel(self):
+    self.assertIsNone(self.__tryParse("//ACHECK:foo"))
+    self.assertIsNone(self.__tryParse("#ACHECK:foo"))
+
+  def test_NotFirstOnTheLine(self):
+    self.assertIsNone(self.__tryParse("A// CHECK: foo"))
+    self.assertIsNone(self.__tryParse("A # CHECK: foo"))
+    self.assertIsNone(self.__tryParse("// // CHECK: foo"))
+    self.assertIsNone(self.__tryParse("# # CHECK: foo"))
+
+  def test_WhitespaceAgnostic(self):
+    self.assertIsNotNone(self.__tryParse("  //CHECK: foo"))
+    self.assertIsNotNone(self.__tryParse("//  CHECK: foo"))
+    self.assertIsNotNone(self.__tryParse("    //CHECK: foo"))
+    self.assertIsNotNone(self.__tryParse("//    CHECK: foo"))
+
+
+class TestCheckLine_Parse(unittest.TestCase):
+  def __getPartPattern(self, linePart):
+    if linePart.variant == checker.CheckElement.Variant.Separator:
+      return "\s+"
+    else:
+      return linePart.pattern
+
+  def __getRegex(self, checkLine):
+    return "".join(map(lambda x: "(" + self.__getPartPattern(x) + ")", checkLine.lineParts))
+
+  def __tryParse(self, string):
+    return checker.CheckLine(string)
+
+  def __parsesTo(self, string, expected):
+    self.assertEqual(expected, self.__getRegex(self.__tryParse(string)))
+
+  def __tryParseNot(self, string):
+    return checker.CheckLine(string, checker.CheckLine.Variant.Not)
+
+  def __parsesPattern(self, string, pattern):
+    line = self.__tryParse(string)
+    self.assertEqual(1, len(line.lineParts))
+    self.assertEqual(checker.CheckElement.Variant.Pattern, line.lineParts[0].variant)
+    self.assertEqual(pattern, line.lineParts[0].pattern)
+
+  def __parsesVarRef(self, string, name):
+    line = self.__tryParse(string)
+    self.assertEqual(1, len(line.lineParts))
+    self.assertEqual(checker.CheckElement.Variant.VarRef, line.lineParts[0].variant)
+    self.assertEqual(name, line.lineParts[0].name)
+
+  def __parsesVarDef(self, string, name, body):
+    line = self.__tryParse(string)
+    self.assertEqual(1, len(line.lineParts))
+    self.assertEqual(checker.CheckElement.Variant.VarDef, line.lineParts[0].variant)
+    self.assertEqual(name, line.lineParts[0].name)
+    self.assertEqual(body, line.lineParts[0].pattern)
+
+  def __doesNotParse(self, string, partType):
+    line = self.__tryParse(string)
+    self.assertEqual(1, len(line.lineParts))
+    self.assertNotEqual(partType, line.lineParts[0].variant)
+
+  # Test that individual parts of the line are recognized
+
+  def test_TextOnly(self):
+    self.__parsesTo("foo", "(foo)")
+    self.__parsesTo("  foo  ", "(foo)")
+    self.__parsesTo("f$o^o", "(f\$o\^o)")
+
+  def test_TextWithWhitespace(self):
+    self.__parsesTo("foo bar", "(foo)(\s+)(bar)")
+    self.__parsesTo("foo   bar", "(foo)(\s+)(bar)")
+
+  def test_RegexOnly(self):
+    self.__parsesPattern("{{a?b.c}}", "a?b.c")
+
+  def test_VarRefOnly(self):
+    self.__parsesVarRef("[[ABC]]", "ABC")
+
+  def test_VarDefOnly(self):
+    self.__parsesVarDef("[[ABC:a?b.c]]", "ABC", "a?b.c")
+
+  def test_TextWithRegex(self):
+    self.__parsesTo("foo{{abc}}bar", "(foo)(abc)(bar)")
+
+  def test_TextWithVar(self):
+    self.__parsesTo("foo[[ABC:abc]]bar", "(foo)(abc)(bar)")
+
+  def test_PlainWithRegexAndWhitespaces(self):
+    self.__parsesTo("foo {{abc}}bar", "(foo)(\s+)(abc)(bar)")
+    self.__parsesTo("foo{{abc}} bar", "(foo)(abc)(\s+)(bar)")
+    self.__parsesTo("foo {{abc}} bar", "(foo)(\s+)(abc)(\s+)(bar)")
+
+  def test_PlainWithVarAndWhitespaces(self):
+    self.__parsesTo("foo [[ABC:abc]]bar", "(foo)(\s+)(abc)(bar)")
+    self.__parsesTo("foo[[ABC:abc]] bar", "(foo)(abc)(\s+)(bar)")
+    self.__parsesTo("foo [[ABC:abc]] bar", "(foo)(\s+)(abc)(\s+)(bar)")
+
+  def test_AllKinds(self):
+    self.__parsesTo("foo [[ABC:abc]]{{def}}bar", "(foo)(\s+)(abc)(def)(bar)")
+    self.__parsesTo("foo[[ABC:abc]] {{def}}bar", "(foo)(abc)(\s+)(def)(bar)")
+    self.__parsesTo("foo [[ABC:abc]] {{def}} bar", "(foo)(\s+)(abc)(\s+)(def)(\s+)(bar)")
+
+  # Test that variables and patterns are parsed correctly
+
+  def test_ValidPattern(self):
+    self.__parsesPattern("{{abc}}", "abc")
+    self.__parsesPattern("{{a[b]c}}", "a[b]c")
+    self.__parsesPattern("{{(a{bc})}}", "(a{bc})")
+
+  def test_ValidRef(self):
+    self.__parsesVarRef("[[ABC]]", "ABC")
+    self.__parsesVarRef("[[A1BC2]]", "A1BC2")
+
+  def test_ValidDef(self):
+    self.__parsesVarDef("[[ABC:abc]]", "ABC", "abc")
+    self.__parsesVarDef("[[ABC:ab:c]]", "ABC", "ab:c")
+    self.__parsesVarDef("[[ABC:a[b]c]]", "ABC", "a[b]c")
+    self.__parsesVarDef("[[ABC:(a[bc])]]", "ABC", "(a[bc])")
+
+  def test_Empty(self):
+    self.__doesNotParse("{{}}", checker.CheckElement.Variant.Pattern)
+    self.__doesNotParse("[[]]", checker.CheckElement.Variant.VarRef)
+    self.__doesNotParse("[[:]]", checker.CheckElement.Variant.VarDef)
+
+  def test_InvalidVarName(self):
+    self.__doesNotParse("[[0ABC]]", checker.CheckElement.Variant.VarRef)
+    self.__doesNotParse("[[AB=C]]", checker.CheckElement.Variant.VarRef)
+    self.__doesNotParse("[[ABC=]]", checker.CheckElement.Variant.VarRef)
+    self.__doesNotParse("[[0ABC:abc]]", checker.CheckElement.Variant.VarDef)
+    self.__doesNotParse("[[AB=C:abc]]", checker.CheckElement.Variant.VarDef)
+    self.__doesNotParse("[[ABC=:abc]]", checker.CheckElement.Variant.VarDef)
+
+  def test_BodyMatchNotGreedy(self):
+    self.__parsesTo("{{abc}}{{def}}", "(abc)(def)")
+    self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)")
+
+  def test_NoVarDefsInNotChecks(self):
+    with self.assertRaises(CheckerException):
+      self.__tryParseNot("[[ABC:abc]]")
+
+class TestCheckLine_Match(unittest.TestCase):
+  def __matchSingle(self, checkString, outputString, varState={}):
+    checkLine = checker.CheckLine(checkString)
+    newVarState = checkLine.match(outputString, varState)
+    self.assertIsNotNone(newVarState)
+    return newVarState
+
+  def __notMatchSingle(self, checkString, outputString, varState={}):
+    checkLine = checker.CheckLine(checkString)
+    self.assertIsNone(checkLine.match(outputString, varState))
+
+  def test_TextAndWhitespace(self):
+    self.__matchSingle("foo", "foo")
+    self.__matchSingle("foo", "  foo  ")
+    self.__matchSingle("foo", "foo bar")
+    self.__notMatchSingle("foo", "XfooX")
+    self.__notMatchSingle("foo", "zoo")
+
+    self.__matchSingle("foo bar", "foo   bar")
+    self.__matchSingle("foo bar", "abc foo bar def")
+    self.__matchSingle("foo bar", "foo foo bar bar")
+
+    self.__matchSingle("foo bar", "foo X bar")
+    self.__notMatchSingle("foo bar", "foo Xbar")
+
+  def test_Pattern(self):
+    self.__matchSingle("foo{{A|B}}bar", "fooAbar")
+    self.__matchSingle("foo{{A|B}}bar", "fooBbar")
+    self.__notMatchSingle("foo{{A|B}}bar", "fooCbar")
+
+  def test_VariableReference(self):
+    self.__matchSingle("foo[[X]]bar", "foobar", {"X": ""})
+    self.__matchSingle("foo[[X]]bar", "fooAbar", {"X": "A"})
+    self.__matchSingle("foo[[X]]bar", "fooBbar", {"X": "B"})
+    self.__notMatchSingle("foo[[X]]bar", "foobar", {"X": "A"})
+    self.__notMatchSingle("foo[[X]]bar", "foo bar", {"X": "A"})
+    with self.assertRaises(CheckerException):
+      self.__matchSingle("foo[[X]]bar", "foobar", {})
+
+  def test_VariableDefinition(self):
+    self.__matchSingle("foo[[X:A|B]]bar", "fooAbar")
+    self.__matchSingle("foo[[X:A|B]]bar", "fooBbar")
+    self.__notMatchSingle("foo[[X:A|B]]bar", "fooCbar")
+
+    env = self.__matchSingle("foo[[X:A.*B]]bar", "fooABbar", {})
+    self.assertEqual(env, {"X": "AB"})
+    env = self.__matchSingle("foo[[X:A.*B]]bar", "fooAxxBbar", {})
+    self.assertEqual(env, {"X": "AxxB"})
+
+    self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarAbaz")
+    self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooBbarBbaz")
+    self.__notMatchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarBbaz")
+
+  def test_NoVariableRedefinition(self):
+    with self.assertRaises(CheckerException):
+      self.__matchSingle("[[X:...]][[X]][[X:...]][[X]]", "foofoobarbar")
+
+  def test_EnvNotChangedOnPartialMatch(self):
+    env = {"Y": "foo"}
+    self.__notMatchSingle("[[X:A]]bar", "Abaz", env)
+    self.assertFalse("X" in env.keys())
+
+  def test_VariableContentEscaped(self):
+    self.__matchSingle("[[X:..]]foo[[X]]", ".*foo.*")
+    self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA")
+
+
+CheckVariant = checker.CheckLine.Variant
+
+def prepareSingleCheck(line):
+  if isinstance(line, str):
+    return checker.CheckLine(line)
+  else:
+    return checker.CheckLine(line[0], line[1])
+
+def prepareChecks(lines):
+  if isinstance(lines, str):
+    lines = lines.splitlines()
+  return list(map(lambda line: prepareSingleCheck(line), lines))
+
+
+class TestCheckGroup_Match(unittest.TestCase):
+  def __matchMulti(self, checkLines, outputString):
+    checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines))
+    outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines())
+    return checkGroup.match(outputGroup)
+
+  def __notMatchMulti(self, checkString, outputString):
+    with self.assertRaises(CheckerException):
+      self.__matchMulti(checkString, outputString)
+
+  def test_TextAndPattern(self):
+    self.__matchMulti("""foo bar
+                         abc {{def}}""",
+                      """foo bar
+                         abc def""");
+    self.__matchMulti("""foo bar
+                         abc {{de.}}""",
+                      """=======
+                         foo bar
+                         =======
+                         abc de#
+                         =======""");
+    self.__notMatchMulti("""//XYZ: foo bar
+                            //XYZ: abc {{def}}""",
+                         """=======
+                            foo bar
+                            =======
+                            abc de#
+                            =======""");
+
+  def test_Variables(self):
+    self.__matchMulti("""foo[[X:.]]bar
+                         abc[[X]]def""",
+                      """foo bar
+                         abc def""");
+    self.__matchMulti("""foo[[X:([0-9]+)]]bar
+                         abc[[X]]def
+                         ### [[X]] ###""",
+                      """foo1234bar
+                         abc1234def
+                         ### 1234 ###""");
+
+  def test_Ordering(self):
+    self.__matchMulti([("foo", CheckVariant.InOrder),
+                       ("bar", CheckVariant.InOrder)],
+                      """foo
+                         bar""")
+    self.__notMatchMulti([("foo", CheckVariant.InOrder),
+                          ("bar", CheckVariant.InOrder)],
+                         """bar
+                            foo""")
+    self.__matchMulti([("abc", CheckVariant.DAG),
+                       ("def", CheckVariant.DAG)],
+                      """abc
+                         def""")
+    self.__matchMulti([("abc", CheckVariant.DAG),
+                       ("def", CheckVariant.DAG)],
+                      """def
+                         abc""")
+    self.__matchMulti([("foo", CheckVariant.InOrder),
+                       ("abc", CheckVariant.DAG),
+                       ("def", CheckVariant.DAG),
+                       ("bar", CheckVariant.InOrder)],
+                      """foo
+                         def
+                         abc
+                         bar""")
+    self.__notMatchMulti([("foo", CheckVariant.InOrder),
+                          ("abc", CheckVariant.DAG),
+                          ("def", CheckVariant.DAG),
+                          ("bar", CheckVariant.InOrder)],
+                         """foo
+                            abc
+                            bar""")
+    self.__notMatchMulti([("foo", CheckVariant.InOrder),
+                          ("abc", CheckVariant.DAG),
+                          ("def", CheckVariant.DAG),
+                          ("bar", CheckVariant.InOrder)],
+                         """foo
+                            def
+                            bar""")
+
+  def test_NotAssertions(self):
+    self.__matchMulti([("foo", CheckVariant.Not)],
+                      """abc
+                         def""")
+    self.__notMatchMulti([("foo", CheckVariant.Not)],
+                         """abc foo
+                            def""")
+    self.__notMatchMulti([("foo", CheckVariant.Not),
+                          ("bar", CheckVariant.Not)],
+                         """abc
+                            def bar""")
+
+  def test_LineOnlyMatchesOnce(self):
+    self.__matchMulti([("foo", CheckVariant.DAG),
+                       ("foo", CheckVariant.DAG)],
+                       """foo
+                          foo""")
+    self.__notMatchMulti([("foo", CheckVariant.DAG),
+                          ("foo", CheckVariant.DAG)],
+                          """foo
+                             bar""")
+
+class TestOutputFile_Parse(unittest.TestCase):
+  def __parsesTo(self, string, expected):
+    if isinstance(string, str):
+      string = unicode(string)
+    outputStream = io.StringIO(string)
+    return self.assertEqual(checker.OutputFile(outputStream).groups, expected)
+
+  def test_NoInput(self):
+    self.__parsesTo(None, [])
+    self.__parsesTo("", [])
+
+  def test_SingleGroup(self):
+    self.__parsesTo("""begin_compilation
+                         method "MyMethod"
+                       end_compilation
+                       begin_cfg
+                         name "pass1"
+                         foo
+                         bar
+                       end_cfg""",
+                    [ checker.OutputGroup("MyMethod pass1", [ "foo", "bar" ]) ])
+
+  def test_MultipleGroups(self):
+    self.__parsesTo("""begin_compilation
+                         name "xyz1"
+                         method "MyMethod1"
+                         date 1234
+                       end_compilation
+                       begin_cfg
+                         name "pass1"
+                         foo
+                         bar
+                       end_cfg
+                       begin_cfg
+                         name "pass2"
+                         abc
+                         def
+                       end_cfg""",
+                    [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
+                      checker.OutputGroup("MyMethod1 pass2", [ "abc", "def" ]) ])
+
+    self.__parsesTo("""begin_compilation
+                         name "xyz1"
+                         method "MyMethod1"
+                         date 1234
+                       end_compilation
+                       begin_cfg
+                         name "pass1"
+                         foo
+                         bar
+                       end_cfg
+                       begin_compilation
+                         name "xyz2"
+                         method "MyMethod2"
+                         date 5678
+                       end_compilation
+                       begin_cfg
+                         name "pass2"
+                         abc
+                         def
+                       end_cfg""",
+                    [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
+                      checker.OutputGroup("MyMethod2 pass2", [ "abc", "def" ]) ])
+
+class TestCheckFile_Parse(unittest.TestCase):
+  def __parsesTo(self, string, expected):
+    if isinstance(string, str):
+      string = unicode(string)
+    checkStream = io.StringIO(string)
+    return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected)
+
+  def test_NoInput(self):
+    self.__parsesTo(None, [])
+    self.__parsesTo("", [])
+
+  def test_SingleGroup(self):
+    self.__parsesTo("""// CHECK-START: Example Group
+                       // CHECK:  foo
+                       // CHECK:    bar""",
+                    [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ])
+
+  def test_MultipleGroups(self):
+    self.__parsesTo("""// CHECK-START: Example Group1
+                       // CHECK: foo
+                       // CHECK: bar
+                       // CHECK-START: Example Group2
+                       // CHECK: abc
+                       // CHECK: def""",
+                    [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])),
+                      checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ])
+
+  def test_CheckVariants(self):
+    self.__parsesTo("""// CHECK-START: Example Group
+                       // CHECK:     foo
+                       // CHECK-NOT: bar
+                       // CHECK-DAG: abc
+                       // CHECK-DAG: def""",
+                    [ checker.CheckGroup("Example Group",
+                                         prepareChecks([ ("foo", CheckVariant.InOrder),
+                                                         ("bar", CheckVariant.Not),
+                                                         ("abc", CheckVariant.DAG),
+                                                         ("def", CheckVariant.DAG) ])) ])
+
+if __name__ == '__main__':
+  checker.Logger.Verbosity = checker.Logger.Level.NoOutput
+  unittest.main()
diff --git a/tools/cpplint.py b/tools/cpplint.py
index c2f6514..4f063d9 100755
--- a/tools/cpplint.py
+++ b/tools/cpplint.py
@@ -3227,9 +3227,16 @@
     # virtually indistinguishable from int(x) casts. Likewise, gMock's
     # MockCallback takes a template parameter of the form return_type(arg_type),
     # which looks much like the cast we're trying to detect.
+    # BEGIN android-added
+    # The C++ 2011 std::function class template exhibits a similar issue.
+    # END android-added
     if (match.group(1) is None and  # If new operator, then this isn't a cast
         not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or
-             Match(r'^\s*MockCallback<.*>', line))):
+             # BEGIN android-changed
+             # Match(r'^\s*MockCallback<.*>', line))):
+             Match(r'^\s*MockCallback<.*>', line) or
+             Match(r'^\s*std::function<.*>', line))):
+             # END android-changed
       # Try a bit harder to catch gmock lines: the only place where
       # something looks like an old-style cast is where we declare the
       # return type of the mocked method, and the only time when we
diff --git a/tools/dexfuzz/Android.mk b/tools/dexfuzz/Android.mk
new file mode 100644
index 0000000..d8f5582
--- /dev/null
+++ b/tools/dexfuzz/Android.mk
@@ -0,0 +1,37 @@
+#
+# 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)
+
+# --- dexfuzz.jar ----------------
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := dexfuzz
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# --- dexfuzz script ----------------
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dexfuzz
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
new file mode 100644
index 0000000..c4795f2
--- /dev/null
+++ b/tools/dexfuzz/README
@@ -0,0 +1,130 @@
+DexFuzz
+=======
+
+DexFuzz is primarily a tool for fuzzing DEX files. Fuzzing is the introduction of
+subtle changes ("mutations") to a file to produce a new test case. These test cases
+can be used to test the various modes of execution available to ART (Interpreter,
+Quick compiler, Optimizing compiler) to check for bugs in these modes of execution.
+This is done by differential testing - each test file is executed with each mode of
+execution, and any differences between the resulting outputs may be an indication of
+a bug in one of the modes.
+
+For a wider overview of DexFuzz, see:
+
+http://community.arm.com/groups/android-community/blog/2014/11/26/the-art-of-fuzz-testing
+
+In typical operation, you provide DexFuzz with a set of DEX files that are the "seeds"
+for mutation - e.g. some tests taken from the ART test suite - and point it at an
+ADB-connected Android device, and it will fuzz these seed files, and execute the
+resulting new tests on the Android device.
+
+How to run DexFuzz
+==================
+
+1. Build dexfuzz with mmm tools/dexfuzz from within art/.
+2. Make sure you have an Android device connected via ADB, that is capable of
+   having DEX files pushed to it and executed with the dalvikvm command.
+3. Make sure you're in the Android build environment!
+   (That is, . build/envsetup.sh && lunch)
+4. Create a new directory, and place some DEX files in here. These are the seed files
+   that are mutated to form new tests.
+5. Create a directory on your device that mutated test files can be pushed to and
+   executed in, using dalvikvm. For example, /data/art-test/
+6. If you currently have multiple devices connected via ADB, find out the name of
+   your device using "adb devices -l".
+7. Run this command:
+
+dexfuzz --inputs=<seeds dir> --execute --repeat=<attempts> \
+    --dump-output <combination of ISA(s) and and backend(s)>
+
+You MUST specify one of the following ISAs:
+  --arm
+  --arm64
+  --x86
+  --x86_64
+  --mips
+  --mips64
+
+And also at least two of the following backends:
+  --interpreter
+  --quick
+  --optimizing
+
+Note that if you wanted to test both ARM and ARM64 on an ARM64 device, you can use
+--allarm. Also in this case only one backend is needed, if i.e., you wanted to test
+ARM Quick Backend vs. ARM64 Quick Backend.
+
+Some legal examples:
+  --arm --quick --optimizing
+  --x86 --quick --optimizing --interpreter
+  --allarm --quick
+
+Add in --device=<device name, e.g. device:generic> if you want to specify a device.
+Add in --execute-dir=<dir on device> if you want to specify an execution directory.
+  (The default is /data/art-test/)
+
+As the fuzzer works, you'll see output like:
+
+|-----------------------------------------------------------------|
+|Iterations|VerifyFail|MutateFail|Timed Out |Successful|Divergence|
+|-----------------------------------------------------------------|
+| 48       | 37       | 4        | 0        | 6        | 1        |
+
+Iterations - number of attempts we've made to mutate DEX files.
+VerifyFail - the number of mutated files that ended up failing to verify, either
+             on the host, or the target.
+MutateFail - because mutation is a random process, and has attempt thresholds to
+             avoid attempting to mutate a file indefinitely, it is possible that
+             an attempt to mutate a file doesn't actually mutate it. This counts
+             those occurrences.
+Timed Out  - mutated files that timed out for one or more backends.
+             Current timeouts are:
+               Quick - 5 seconds
+               Optimizing - 5 seconds
+               Intepreter - 30 seconds
+              (use --short-timeouts to set all backends to 2 seconds.)
+Successful - mutated files that executed and all backends agreed on the resulting
+             output. NB: if all backends crashed with the same output, this would
+             be considered a success - proper detection of crashes is still to come.
+Divergence - mutated files that executed and some backend disagreed about the
+             resulting output. Divergent programs are run multiple times with a
+             single backend, to check if they diverge from themselves, and these are
+             not included in the count. If multiple architectures are being used
+             (ARM/ARM64), and the divergences align with different architectures,
+             these are also not included in the count.
+
+8. Check report.log for the full report, including input file and RNG seed for each
+   test program. This allows you to recreate a bad program with, e.g.:
+
+dexfuzz --input=<input file> --seed=<seed value>
+
+Check dexfuzz --help for the full list of options.
+
+NOTE: DEX files with unicode strings are not fully supported yet, and DEX files with
+JNI elements are not supported at all currently.
+
+Mutation Likelihoods
+====================
+
+Each bytecode mutation has a chance out of 100% of firing. Following is the listing
+of each mutation's probability. If you wish to easily adjust these values, copy
+these values into a file called likelihoods.txt, and run dexfuzz with
+--likelihoods=likelihoods.txt.
+
+ArithOpChanger 75
+BranchShifter 30
+CmpBiasChanger 30
+ConstantValueChanger 70
+ConversionRepeater 50
+FieldFlagChanger 40
+InstructionDeleter 40
+InstructionDuplicator 80
+InstructionSwapper 80
+NewMethodCaller 10
+NonsenseStringPrinter 10
+PoolIndexChanger 30
+RandomInstructionGenerator 30
+SwitchBranchShifter 30
+TryBlockShifter 40
+ValuePrinter 40
+VRegChanger 60
diff --git a/tools/dexfuzz/dexfuzz b/tools/dexfuzz/dexfuzz
new file mode 100755
index 0000000..cd47008
--- /dev/null
+++ b/tools/dexfuzz/dexfuzz
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# 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.
+#
+
+#
+# Wrapper script for calling dexfuzz.jar.
+#
+DEBUG=
+#DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=127.0.0.1:8888,server=y,suspend=n -XX:+HeapDumpOnOutOfMemoryError -ea"
+
+java ${DEBUG} -jar ${ANDROID_HOST_OUT}/framework/dexfuzz.jar "$@"
diff --git a/tools/dexfuzz/manifest.txt b/tools/dexfuzz/manifest.txt
new file mode 100644
index 0000000..9e4c214
--- /dev/null
+++ b/tools/dexfuzz/manifest.txt
@@ -0,0 +1 @@
+Main-Class: dexfuzz.DexFuzz
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
new file mode 100644
index 0000000..2fb9663
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+import dexfuzz.fuzzers.Fuzzer;
+import dexfuzz.fuzzers.FuzzerMultipleExecute;
+import dexfuzz.fuzzers.FuzzerMultipleNoExecute;
+import dexfuzz.fuzzers.FuzzerSingleExecute;
+import dexfuzz.fuzzers.FuzzerSingleNoExecute;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.listeners.ConsoleLoggerListener;
+import dexfuzz.listeners.LogFileListener;
+import dexfuzz.listeners.MultiplexerListener;
+import dexfuzz.listeners.UniqueProgramTrackerListener;
+import dexfuzz.listeners.UpdatingConsoleListener;
+
+/**
+ * Entrypoint class for dexfuzz.
+ */
+public class DexFuzz {
+  private static int majorVersion = 1;
+  private static int minorVersion = 0;
+  private static int seedChangeVersion = 0;
+
+  /**
+   * Entrypoint to dexfuzz.
+   */
+  public static void main(String[] args) {
+    // Report the version number, which should be incremented every time something will cause
+    // the same input seed to produce a different result than before.
+    Log.always(String.format("DexFuzz v%d.%d.%d",
+        majorVersion, minorVersion, seedChangeVersion));
+    Log.always("");
+
+    if (!Options.readOptions(args)) {
+      Log.error("Failed to validate options.");
+      Options.usage();
+    }
+
+    // Create the Listener, which will listen for events and report them.
+    BaseListener listener = null;
+    if (Options.repeat > 1 && Options.execute) {
+      // Create a Listener that is responsible for multiple Listeners.
+      MultiplexerListener multipleListener = new MultiplexerListener();
+      multipleListener.setup();
+      // Add the live updating listener, but only if we're not printing out lots of logs.
+      if (!Log.likelyToLog()) {
+        multipleListener.addListener(new UpdatingConsoleListener());
+      } else {
+        // If we are dumping out lots of logs, then use the ConsoleLogger instead.
+        multipleListener.addListener(new ConsoleLoggerListener());
+      }
+      // Add the file logging listener.
+      multipleListener.addListener(new LogFileListener(Options.reportLogFile));
+      // Add the unique program tracker.
+      multipleListener.addListener(new UniqueProgramTrackerListener(Options.uniqueDatabaseFile));
+      listener = multipleListener;
+    } else {
+      // Just use the basic listener.
+      listener = new ConsoleLoggerListener();
+    }
+
+    // Create the Fuzzer that uses a particular strategy for fuzzing.
+    Fuzzer fuzzer = null;
+    if ((Options.repeat > 1) && Options.execute) {
+      fuzzer = new FuzzerMultipleExecute(listener);
+    } else if ((Options.repeat > 1) && !Options.execute) {
+      fuzzer = new FuzzerMultipleNoExecute(listener);
+    } else if ((Options.repeat == 1) && Options.execute) {
+      fuzzer = new FuzzerSingleExecute(listener);
+    } else if ((Options.repeat == 1) && !Options.execute) {
+      fuzzer = new FuzzerSingleNoExecute(listener);
+    } else {
+      Log.errorAndQuit("Invalid options provided, desired fuzzer unknown.");
+    }
+    // TODO: Implement FuzzerFindMinimalMutations.
+    // TODO: Implement FuzzerGenerational.
+
+    // Actually run the Fuzzer.
+    fuzzer.run();
+    fuzzer.printTimingInfo();
+    fuzzer.shutdown();
+
+    // Cleanup the Listener.
+    listener.shutdown();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/ExecutionResult.java b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
new file mode 100644
index 0000000..3a8c6cb
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+import java.util.List;
+
+/**
+ * Stores the output of an executed command.
+ */
+public class ExecutionResult {
+  public List<String> output;
+  public List<String> error;
+  public int returnValue;
+
+  private String flattenedOutput;
+  private String flattenedOutputWithNewlines;
+  private String flattenedError;
+  private String flattenedErrorWithNewlines;
+  private String flattenedAll;
+
+  private static final int TIMEOUT_RETURN_VALUE = 124;
+  private static final int SIGABORT_RETURN_VALUE = 134;
+
+  /**
+   * Get only the output, with all lines concatenated together, excluding newline characters.
+   */
+  public String getFlattenedOutput() {
+    if (flattenedOutput == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : output) {
+        builder.append(line);
+      }
+      flattenedOutput = builder.toString();
+    }
+    return flattenedOutput;
+  }
+
+  /**
+   * Get only the output, with all lines concatenated together, including newline characters.
+   */
+  public String getFlattenedOutputWithNewlines() {
+    if (flattenedOutputWithNewlines == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : output) {
+        builder.append(line).append("\n");
+      }
+      flattenedOutputWithNewlines = builder.toString();
+    }
+    return flattenedOutputWithNewlines;
+  }
+
+  /**
+   * Get only the error, with all lines concatenated together, excluding newline characters.
+   */
+  public String getFlattenedError() {
+    if (flattenedError == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : error) {
+        builder.append(line);
+      }
+      flattenedError = builder.toString();
+    }
+    return flattenedError;
+  }
+
+  /**
+   * Get only the error, with all lines concatenated together, including newline characters.
+   */
+  public String getFlattenedErrorWithNewlines() {
+    if (flattenedErrorWithNewlines == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : error) {
+        builder.append(line).append("\n");
+      }
+      flattenedErrorWithNewlines = builder.toString();
+    }
+    return flattenedErrorWithNewlines;
+  }
+
+  /**
+   * Get both the output and error, concatenated together, excluding newline characters.
+   */
+  public String getFlattenedAll() {
+    if (flattenedAll == null) {
+      flattenedAll = getFlattenedOutput() + getFlattenedError();
+    }
+    return flattenedAll;
+  }
+
+  public boolean isTimeout() {
+    return (returnValue == TIMEOUT_RETURN_VALUE);
+  }
+
+  public boolean isSigabort() {
+    return (returnValue == SIGABORT_RETURN_VALUE);
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/Log.java b/tools/dexfuzz/src/dexfuzz/Log.java
new file mode 100644
index 0000000..853550b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Log.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+/**
+ * Provides access to the logging facilities of dexfuzz.
+ */
+public class Log {
+  private static LogTag threshold = LogTag.ERROR;
+
+  // Disable the constructor for this class.
+  private Log() { }
+
+  public static enum LogTag {
+    DEBUG,
+    INFO,
+    WARN,
+    ERROR,
+    ALWAYS
+  }
+
+  public static void setLoggingLevel(LogTag tag) {
+    threshold = tag;
+  }
+
+  public static boolean likelyToLog() {
+    return (threshold.ordinal() < LogTag.ERROR.ordinal());
+  }
+
+  public static void debug(String msg) {
+    log(LogTag.DEBUG, msg);
+  }
+
+  public static void info(String msg) {
+    log(LogTag.INFO, msg);
+  }
+
+  public static void warn(String msg) {
+    log(LogTag.WARN, msg);
+  }
+
+  public static void error(String msg) {
+    log(LogTag.ERROR, msg);
+  }
+
+  public static void always(String msg) {
+    System.out.println(msg);
+  }
+
+  private static void log(LogTag tag, String msg) {
+    if (tag.ordinal() >= threshold.ordinal()) {
+      System.out.println("[" + tag.toString() + "] " + msg);
+    }
+  }
+
+  /**
+   * Reports error and then terminates the program.
+   */
+  public static void errorAndQuit(String msg) {
+    error(msg);
+    // TODO: Signal sleeping threads.
+    System.exit(1);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/MutationStats.java b/tools/dexfuzz/src/dexfuzz/MutationStats.java
new file mode 100644
index 0000000..c65b4f2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/MutationStats.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper for a dictionary tracking what mutations have been performed.
+ */
+public class MutationStats {
+
+  public static class StatNotFoundException extends RuntimeException {
+    private static final long serialVersionUID = -7038515184655168470L;
+  }
+
+  private Map<String,Long> stats;
+  private List<String> statsOrder;
+
+  public MutationStats() {
+    stats = new HashMap<String,Long>();
+    statsOrder = new ArrayList<String>();
+  }
+
+  public void incrementStat(String statName) {
+    increaseStat(statName, 1);
+  }
+
+  /**
+   * Increase the named stat by the specified amount.
+   */
+  public void increaseStat(String statName, long amt) {
+    if (!stats.containsKey(statName)) {
+      stats.put(statName, 0L);
+      statsOrder.add(statName);
+    }
+    stats.put(statName, stats.get(statName) + amt);
+  }
+
+  /**
+   * Get a string representing the collected stats - looks like a JSON dictionary.
+   */
+  public String getStatsString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("{");
+    boolean first = true;
+    for (String statName : statsOrder) {
+      if (!first) {
+        builder.append(", ");
+      } else {
+        first = false;
+      }
+      builder.append("\"").append(statName).append("\": ").append(stats.get(statName));
+    }
+    builder.append("}");
+    return builder.toString();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
new file mode 100644
index 0000000..1ae7b5e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -0,0 +1,434 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+import dexfuzz.Log.LogTag;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stores options for dexfuzz.
+ */
+public class Options {
+  /**
+   * Constructor has been disabled for this class, which should only be used statically.
+   */
+  private Options() { }
+
+  // KEY VALUE OPTIONS
+  public static final List<String> inputFileList = new ArrayList<String>();
+  public static String outputFile = "";
+  public static long rngSeed = -1;
+  public static boolean usingProvidedSeed = false;
+  public static int methodMutations = 3;
+  public static int minMethods = 2;
+  public static int maxMethods = 10;
+  public static final Map<String,Integer> mutationLikelihoods = new HashMap<String,Integer>();
+  public static String executeClass = "Main";
+  public static String deviceName = "";
+  public static boolean usingSpecificDevice = false;
+  public static int repeat = 1;
+  public static String executeDirectory = "/data/art-test";
+  public static String dumpMutationsFile = "mutations.dump";
+  public static String loadMutationsFile = "mutations.dump";
+  public static String reportLogFile = "report.log";
+  public static String uniqueDatabaseFile = "unique_progs.db";
+
+  // FLAG OPTIONS
+  public static boolean execute;
+  public static boolean local;
+  public static boolean noBootImage;
+  public static boolean useInterpreter;
+  public static boolean useQuick;
+  public static boolean useOptimizing;
+  public static boolean useArchArm;
+  public static boolean useArchArm64;
+  public static boolean useArchX86;
+  public static boolean useArchX86_64;
+  public static boolean useArchMips;
+  public static boolean useArchMips64;
+  public static boolean skipHostVerify;
+  public static boolean shortTimeouts;
+  public static boolean dumpOutput;
+  public static boolean dumpVerify;
+  public static boolean mutateLimit;
+  public static boolean reportUnique;
+  public static boolean skipMutation;
+  public static boolean dumpMutations;
+  public static boolean loadMutations;
+
+  /**
+   * Print out usage information about dexfuzz, and then exit.
+   */
+  public static void usage() {
+    Log.always("DexFuzz Usage:");
+    Log.always("  --input=<file>         : Seed DEX file to be fuzzed");
+    Log.always("                           (Can specify multiple times.)");
+    Log.always("  --inputs=<file>        : Directory containing DEX files to be fuzzed.");
+    Log.always("  --output=<file>        : Output DEX file to be produced");
+    Log.always("");
+    Log.always("  --execute              : Execute the resulting fuzzed program");
+    Log.always("    --local              : Execute on host (Not available yet.)");
+    Log.always("    --device=<device>    : Execute on an ADB-connected-device, where <device> is");
+    Log.always("                           the argument given to adb -s. Default execution mode.");
+    Log.always("    --execute-dir=<dir>  : Push tests to this directory to execute them.");
+    Log.always("                           (Default: /data/art-test)");
+    Log.always("    --no-boot-image      : Use this flag when boot.art is not available.");
+    Log.always("    --skip-host-verify   : When executing, skip host-verification stage");
+    Log.always("    --execute-class=<c>  : When executing, execute this class (default: Main)");
+    Log.always("");
+    Log.always("    --interpreter        : Include the Interpreter in comparisons");
+    Log.always("    --quick              : Include the Quick Compiler in comparisons");
+    Log.always("    --optimizing         : Include the Optimizing Compiler in comparisons");
+    Log.always("");
+    Log.always("    --arm                : Include ARM backends in comparisons");
+    Log.always("    --arm64              : Include ARM64 backends in comparisons");
+    Log.always("    --allarm             : Short for --arm --arm64");
+    Log.always("    --x86                : Include x86 backends in comparisons");
+    Log.always("    --x86-64             : Include x86-64 backends in comparisons");
+    Log.always("    --mips               : Include MIPS backends in comparisons");
+    Log.always("    --mips64             : Include MIPS64 backends in comparisons");
+    Log.always("");
+    Log.always("    --dump-output        : Dump outputs of executed programs");
+    Log.always("    --dump-verify        : Dump outputs of verification");
+    Log.always("    --repeat=<n>         : Fuzz N programs, executing each one.");
+    Log.always("    --short-timeouts     : Shorten timeouts (faster; use if");
+    Log.always("                           you want to focus on output divergences)");
+    Log.always("  --seed=<seed>          : RNG seed to use");
+    Log.always("  --method-mutations=<n> : Maximum number of mutations to perform on each method.");
+    Log.always("                           (Default: 3)");
+    Log.always("  --min-methods=<n>      : Minimum number of methods to mutate. (Default: 2)");
+    Log.always("  --max-methods=<n>      : Maximum number of methods to mutate. (Default: 10)");
+    Log.always("  --one-mutation         : Short for --method-mutations=1 ");
+    Log.always("                             --min-methods=1 --max-methods=1");
+    Log.always("  --likelihoods=<file>   : A file containing a table of mutation likelihoods");
+    Log.always("  --mutate-limit         : Mutate only methods whose names end with _MUTATE");
+    Log.always("  --skip-mutation        : Do not actually mutate the input, just output it");
+    Log.always("                           after parsing");
+    Log.always("");
+    Log.always("  --dump-mutations[=<file>] : Dump an editable set of mutations applied");
+    Log.always("                              to <file> (default: mutations.dump)");
+    Log.always("  --load-mutations[=<file>] : Load and apply a set of mutations");
+    Log.always("                              from <file> (default: mutations.dump)");
+    Log.always("  --log=<tag>            : Set more verbose logging level: DEBUG, INFO, WARN");
+    Log.always("  --report=<file>        : Use <file> to report results when using --repeat");
+    Log.always("                           (Default: report.log)");
+    Log.always("  --report-unique        : Print out information about unique programs generated");
+    Log.always("  --unique-db=<file>     : Use <file> store results about unique programs");
+    Log.always("                           (Default: unique_progs.db)");
+    Log.always("");
+    System.exit(0);
+  }
+
+  /**
+   * Given a flag option (one that does not feature an =), handle it
+   * accordingly. Report an error and print usage info if the flag is not
+   * recognised.
+   */
+  private static void handleFlagOption(String flag) {
+    if (flag.equals("execute")) {
+      execute = true;
+    } else if (flag.equals("local")) {
+      local = true;
+    } else if (flag.equals("no-boot-image")) {
+      noBootImage = true;
+    } else if (flag.equals("skip-host-verify")) {
+      skipHostVerify = true;
+    } else if (flag.equals("interpreter")) {
+      useInterpreter = true;
+    } else if (flag.equals("quick")) {
+      useQuick = true;
+    } else if (flag.equals("optimizing")) {
+      useOptimizing = true;
+    } else if (flag.equals("arm")) {
+      useArchArm = true;
+    } else if (flag.equals("arm64")) {
+      useArchArm64 = true;
+    } else if (flag.equals("allarm")) {
+      useArchArm = true;
+      useArchArm64 = true;
+    } else if (flag.equals("x86")) {
+      useArchX86 = true;
+    } else if (flag.equals("x86-64")) {
+      useArchX86_64 = true;
+    } else if (flag.equals("mips")) {
+      useArchMips = true;
+    } else if (flag.equals("mips64")) {
+      useArchMips64 = true;
+    } else if (flag.equals("mutate-limit")) {
+      mutateLimit = true;
+    } else if (flag.equals("report-unique")) {
+      reportUnique = true;
+    } else if (flag.equals("dump-output")) {
+      dumpOutput = true;
+    } else if (flag.equals("dump-verify")) {
+      dumpVerify = true;
+    } else if (flag.equals("short-timeouts")) {
+      shortTimeouts = true;
+    } else if (flag.equals("skip-mutation")) {
+      skipMutation = true;
+    } else if (flag.equals("dump-mutations")) {
+      dumpMutations = true;
+    } else if (flag.equals("load-mutations")) {
+      loadMutations = true;
+    } else if (flag.equals("one-mutation")) {
+      methodMutations = 1;
+      minMethods = 1;
+      maxMethods = 1;
+    } else if (flag.equals("help")) {
+      usage();
+    } else {
+      Log.error("Unrecognised flag: --" + flag);
+      usage();
+    }
+  }
+
+  /**
+   * Given a key-value option (one that features an =), handle it
+   * accordingly. Report an error and print usage info if the key is not
+   * recognised.
+   */
+  private static void handleKeyValueOption(String key, String value) {
+    if (key.equals("input")) {
+      inputFileList.add(value);
+    } else if (key.equals("inputs")) {
+      File folder = new File(value);
+      if (folder.listFiles() == null) {
+        Log.errorAndQuit("Specified argument to --inputs is not a directory!");
+      }
+      for (File file : folder.listFiles()) {
+        String inputName = value + "/" + file.getName();
+        Log.always("Adding " + inputName + " to input seed files.");
+        inputFileList.add(inputName);
+      }
+    } else if (key.equals("output")) {
+      outputFile = value;
+    } else if (key.equals("seed")) {
+      rngSeed = Long.parseLong(value);
+      usingProvidedSeed = true;
+    } else if (key.equals("method-mutations")) {
+      methodMutations = Integer.parseInt(value);
+    } else if (key.equals("min-methods")) {
+      minMethods = Integer.parseInt(value);
+    } else if (key.equals("max-methods")) {
+      maxMethods = Integer.parseInt(value);
+    } else if (key.equals("repeat")) {
+      repeat = Integer.parseInt(value);
+    } else if (key.equals("log")) {
+      Log.setLoggingLevel(LogTag.valueOf(value.toUpperCase()));
+    } else if (key.equals("likelihoods")) {
+      setupMutationLikelihoodTable(value);
+    } else if (key.equals("dump-mutations")) {
+      dumpMutations = true;
+      dumpMutationsFile = value;
+    } else if (key.equals("load-mutations")) {
+      loadMutations = true;
+      loadMutationsFile = value;
+    } else if (key.equals("report")) {
+      reportLogFile = value;
+    } else if (key.equals("unique-db")) {
+      uniqueDatabaseFile = value;
+    } else if (key.equals("execute-class")) {
+      executeClass = value;
+    } else if (key.equals("device")) {
+      deviceName = value;
+      usingSpecificDevice = true;
+    } else if (key.equals("execute-dir")) {
+      executeDirectory = value;
+    } else {
+      Log.error("Unrecognised key: --" + key);
+      usage();
+    }
+  }
+
+  private static void setupMutationLikelihoodTable(String tableFilename) {
+    try {
+      BufferedReader reader = new BufferedReader(new FileReader(tableFilename));
+      String line = reader.readLine();
+      while (line != null) {
+        line = line.replaceAll("\\s+", " ");
+        String[] entries = line.split(" ");
+        String name = entries[0].toLowerCase();
+        int likelihood = Integer.parseInt(entries[1]);
+        if (likelihood > 100) {
+          likelihood = 100;
+        }
+        if (likelihood < 0) {
+          likelihood = 0;
+        }
+        mutationLikelihoods.put(name, likelihood);
+        line = reader.readLine();
+      }
+      reader.close();
+    } catch (FileNotFoundException e) {
+      Log.error("Unable to open mutation probability table file: " + tableFilename);
+    } catch (IOException e) {
+      Log.error("Unable to read mutation probability table file: " + tableFilename);
+    }
+  }
+
+  /**
+   * Called by the DexFuzz class during program initialisation to parse
+   * the program's command line arguments.
+   * @return If options were successfully read and validated.
+   */
+  public static boolean readOptions(String[] args) {
+    for (String arg : args) {
+      if (!(arg.startsWith("--"))) {
+        Log.error("Unrecognised option: " + arg);
+        usage();
+      }
+
+      // cut off the --
+      arg = arg.substring(2);
+
+      // choose between a --X=Y option (keyvalue) and a --X option (flag)
+      if (arg.contains("=")) {
+        String[] split = arg.split("=");
+        handleKeyValueOption(split[0], split[1]);
+      } else {
+        handleFlagOption(arg);
+      }
+    }
+
+    return validateOptions();
+  }
+
+  /**
+   * Checks if the current options settings are valid, called after reading
+   * all options.
+   * @return If the options are valid or not.
+   */
+  private static boolean validateOptions() {
+    // Deal with option assumptions.
+    if (inputFileList.isEmpty()) {
+      File seedFile = new File("fuzzingseed.dex");
+      if (seedFile.exists()) {
+        Log.always("Assuming --input=fuzzingseed.dex");
+        inputFileList.add("fuzzingseed.dex");
+      } else {
+        Log.errorAndQuit("No input given, and couldn't find fuzzingseed.dex!");
+        return false;
+      }
+    }
+
+    if (outputFile.equals("")) {
+      Log.always("Assuming --output=fuzzingseed_fuzzed.dex");
+      outputFile = "fuzzingseed_fuzzed.dex";
+    }
+
+
+    if (mutationLikelihoods.isEmpty()) {
+      File likelihoodsFile = new File("likelihoods.txt");
+      if (likelihoodsFile.exists()) {
+        Log.always("Assuming --likelihoods=likelihoods.txt ");
+        setupMutationLikelihoodTable("likelihoods.txt");
+      } else {
+        Log.always("Using default likelihoods (see README for values)");
+      }
+    }
+
+    // Now check for hard failures.
+    if (repeat < 1) {
+      Log.error("--repeat must be at least 1!");
+      return false;
+    }
+    if (usingProvidedSeed && repeat > 1) {
+      Log.error("Cannot use --repeat with --seed");
+      return false;
+    }
+    if (loadMutations && dumpMutations) {
+      Log.error("Cannot both load and dump mutations");
+      return false;
+    }
+    if (repeat == 1 && inputFileList.size() > 1) {
+      Log.error("Must use --repeat if you have provided more than one input");
+      return false;
+    }
+    if (methodMutations < 0) {
+      Log.error("Cannot use --method-mutations with a negative value.");
+      return false;
+    }
+    if (minMethods < 0) {
+      Log.error("Cannot use --min-methods with a negative value.");
+      return false;
+    }
+    if (maxMethods < 0) {
+      Log.error("Cannot use --max-methods with a negative value.");
+      return false;
+    }
+    if (maxMethods < minMethods) {
+      Log.error("Cannot use --max-methods that's smaller than --min-methods");
+      return false;
+    }
+    if (local && usingSpecificDevice) {
+      Log.error("Cannot use --local and --device!");
+      return false;
+    }
+    if (execute) {
+      if (!(useArchArm
+          || useArchArm64
+          || useArchX86
+          || useArchX86_64
+          || useArchMips
+          || useArchMips64)) {
+        Log.error("No architecture to execute on was specified!");
+        return false;
+      }
+      if ((useArchArm || useArchArm64) && (useArchX86 || useArchX86_64)) {
+        Log.error("Did you mean to specify ARM and x86?");
+        return false;
+      }
+      if ((useArchArm || useArchArm64) && (useArchMips || useArchMips64)) {
+        Log.error("Did you mean to specify ARM and MIPS?");
+        return false;
+      }
+      if ((useArchX86 || useArchX86_64) && (useArchMips || useArchMips64)) {
+        Log.error("Did you mean to specify x86 and MIPS?");
+        return false;
+      }
+      int backends = 0;
+      if (useInterpreter) {
+        backends++;
+      }
+      if (useQuick) {
+        backends++;
+      }
+      if (useOptimizing) {
+        backends++;
+      }
+      if (useArchArm && useArchArm64) {
+        // Could just be comparing quick-ARM versus quick-ARM64?
+        backends++;
+      }
+      if (backends < 2) {
+        Log.error("Not enough backends specified! Try --quick --interpreter!");
+        return false;
+      }
+    }
+
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/StreamConsumer.java b/tools/dexfuzz/src/dexfuzz/StreamConsumer.java
new file mode 100644
index 0000000..cd93374
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/StreamConsumer.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+/**
+ * process.waitFor() can block if its output buffers are not drained.
+ * These threads are used to keep the buffers drained, and provide the final
+ * output once the command has finished executing. Each Executor has its own
+ * output and error StreamConsumers.
+ */
+public class StreamConsumer extends Thread {
+  private List<String> output;
+  private BufferedReader reader;
+
+  private State state;
+
+  private Semaphore workToBeDone;
+  private Semaphore outputIsReady;
+
+  enum State {
+    WAITING,
+    CONSUMING,
+    SHOULD_STOP_CONSUMING,
+    FINISHED,
+    ERROR
+  }
+
+  /**
+   * Create a StreamConsumer, will be immediately ready to start consuming.
+   */
+  public StreamConsumer() {
+    output = new ArrayList<String>();
+    workToBeDone = new Semaphore(0);
+    outputIsReady = new Semaphore(0);
+
+    state = State.WAITING;
+  }
+
+  /**
+   * Executor should call this to provide its StreamConsumers with the Streams
+   * for a Process it is about to call waitFor() on.
+   */
+  public void giveStreamAndStartConsuming(InputStream stream) {
+    output.clear();
+
+    reader = new BufferedReader(new InputStreamReader(stream));
+
+    changeState(State.CONSUMING, State.WAITING);
+
+    // Tell consumer there is work to be done.
+    workToBeDone.release();
+  }
+
+  /**
+   * Executor should call this once its call to waitFor() returns.
+   */
+  public void processFinished() {
+    changeState(State.SHOULD_STOP_CONSUMING, State.CONSUMING);
+  }
+
+  /**
+   * Executor should call this to get the captured output of this StreamConsumer.
+   */
+  public List<String> getOutput() {
+
+    try {
+      // Wait until the output is ready.
+      outputIsReady.acquire();
+    } catch (InterruptedException e) {
+      Log.error("Client of StreamConsumer was interrupted while waiting for output?");
+      return null;
+    }
+
+    // Take a copy of the Strings, so when we call output.clear(), we don't
+    // clear the ExecutionResult's list.
+    List<String> copy = new ArrayList<String>(output);
+    return copy;
+  }
+
+  /**
+   * Executor should call this when we're shutting down.
+   */
+  public void shutdown() {
+    changeState(State.FINISHED, State.WAITING);
+
+    // Tell Consumer there is work to be done (it will check first if FINISHED has been set.)
+    workToBeDone.release();
+  }
+
+  private void consume() {
+    try {
+
+      if (checkState(State.SHOULD_STOP_CONSUMING)) {
+        // Caller already called processFinished() before we even started
+        // consuming. Just get what we can and finish.
+        while (reader.ready()) {
+          output.add(reader.readLine());
+        }
+      } else {
+        // Caller's process is still executing, so just loop and consume.
+        while (checkState(State.CONSUMING)) {
+          Thread.sleep(50);
+          while (reader.ready()) {
+            output.add(reader.readLine());
+          }
+        }
+      }
+
+      if (checkState(State.SHOULD_STOP_CONSUMING)) {
+        changeState(State.WAITING, State.SHOULD_STOP_CONSUMING);
+      } else {
+        Log.error("StreamConsumer stopped consuming, but was not told to?");
+        setErrorState();
+      }
+
+      reader.close();
+
+    } catch (IOException e) {
+      Log.error("StreamConsumer caught IOException while consuming");
+      setErrorState();
+    } catch (InterruptedException e) {
+      Log.error("StreamConsumer caught InterruptedException while consuming");
+      setErrorState();
+    }
+
+    // Tell client of Consumer that the output is ready.
+    outputIsReady.release();
+  }
+
+  @Override
+  public void run() {
+    while (checkState(State.WAITING)) {
+      try {
+        // Wait until there is work to be done
+        workToBeDone.acquire();
+      } catch (InterruptedException e) {
+        Log.error("StreamConsumer caught InterruptedException while waiting for work");
+        setErrorState();
+        break;
+      }
+
+      // Check first if we're done
+      if (checkState(State.FINISHED)) {
+        break;
+      }
+
+      // Make sure we're either supposed to be consuming
+      // or supposed to be finishing up consuming
+      if (!(checkState(State.CONSUMING) || checkState(State.SHOULD_STOP_CONSUMING))) {
+        Log.error("invalid state: StreamConsumer told about work, but not CONSUMING?");
+        Log.error("state was: " + getCurrentState());
+        setErrorState();
+        break;
+      }
+
+      consume();
+    }
+  }
+
+  private synchronized boolean checkState(State expectedState) {
+    return (expectedState == state);
+  }
+
+  private synchronized void changeState(State newState, State previousState) {
+    if (state != previousState) {
+      Log.error("StreamConsumer Unexpected state: " + state + ", expected " + previousState);
+      state = State.ERROR;
+    } else {
+      state = newState;
+    }
+  }
+
+  private synchronized void setErrorState() {
+    state = State.ERROR;
+  }
+
+  private synchronized State getCurrentState() {
+    return state;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/Timer.java b/tools/dexfuzz/src/dexfuzz/Timer.java
new file mode 100644
index 0000000..8979b8a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Timer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package dexfuzz;
+
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * For timing splits of program execution.
+ */
+public class Timer {
+  /**
+   * The name of the timer, the phase of the program it is intended to time.
+   */
+  private String name;
+
+  /**
+   * A point in time remembered when start() is called.
+   */
+  private long startPoint;
+
+  /**
+   * A cumulative count of how much time has elapsed. Updated each time
+   * stop() is called.
+   */
+  private long elapsedTime;
+
+  /**
+   * Initialise a new timer with the provided name.
+   */
+  public Timer(String name) {
+    this.name = name;
+    this.elapsedTime = 0L;
+  }
+
+  /**
+   * Start timing.
+   */
+  public void start() {
+    startPoint = System.currentTimeMillis();
+  }
+
+  /**
+   * Stop timing, update how much time has elapsed.
+   */
+  public void stop() {
+    long endPoint = System.currentTimeMillis();
+    elapsedTime += (endPoint - startPoint);
+  }
+
+  /**
+   * Log the elapsed time this timer has recorded.
+   */
+  public void printTime(BaseListener listener) {
+    listener.handleTiming(name, ((float)elapsedTime) / 1000.0f);
+  }
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/executors/Architecture.java
similarity index 63%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to tools/dexfuzz/src/dexfuzz/executors/Architecture.java
index 9e2c030..5cdabc3 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/executors/Architecture.java
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-#include "asm_support_arm64.S"
+package dexfuzz.executors;
 
-    /*
-     * Portable invocation stub.
-     */
-UNIMPLEMENTED art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_proxy_invoke_handler
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+/**
+ * Every Executor must specify an Architecture. It is important that when reduced
+ * to lower case, these match the ISA string that ART would produce. For example,
+ * the architecture directory used for /data/dalvik-cache/${ISA}
+ */
+public enum Architecture {
+  ARM,
+  ARM64,
+  X86,
+  X86_64,
+  MIPS,
+  MIPS64
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
new file mode 100644
index 0000000..a945283
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64InterpreterExecutor extends Executor {
+
+  public Arm64InterpreterExecutor(BaseListener listener, Device device) {
+    super("ARM64 Interpreter", 30, listener, Architecture.ARM64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xint ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..2204ba8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64OptimizingBackendExecutor extends Executor {
+
+  public Arm64OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("ARM64 Optimizing Backend", 5, listener, Architecture.ARM64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
new file mode 100644
index 0000000..55c9c7a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64QuickBackendExecutor extends Executor {
+
+  public Arm64QuickBackendExecutor(BaseListener listener, Device device) {
+    super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
new file mode 100644
index 0000000..68ce2e0
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmInterpreterExecutor extends Executor {
+
+  public ArmInterpreterExecutor(BaseListener listener, Device device) {
+    super("ARM Interpreter", 30, listener, Architecture.ARM, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xint ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
new file mode 100644
index 0000000..78cf652
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmOptimizingBackendExecutor extends Executor {
+
+  public ArmOptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("ARM Optimizing Backend", 5, listener, Architecture.ARM, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
new file mode 100644
index 0000000..8f026b2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmQuickBackendExecutor extends Executor {
+
+  public ArmQuickBackendExecutor(BaseListener listener, Device device) {
+    super("ARM Quick Backend", 5, listener, Architecture.ARM, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
new file mode 100644
index 0000000..8c03103
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+/**
+ * Handles execution either on a remote device, or locally.
+ * Currently only remote execution, on an ADB-connected device, is supported.
+ */
+public class Device {
+  private boolean isLocal;
+  private String deviceName;
+  private boolean usingSpecificDevice;
+  private boolean noBootImage;
+
+  /**
+   * The constructor for a local "device". Not yet supported.
+   */
+  public Device() {
+    this.isLocal = true;
+    throw new UnsupportedOperationException("Currently local execution is not supported.");
+  }
+
+  /**
+   * The constructor for an ADB connected device.
+   */
+  public Device(String deviceName, boolean noBootImage) {
+    if (!deviceName.isEmpty()) {
+      this.deviceName = deviceName;
+      this.usingSpecificDevice = true;
+    }
+    this.noBootImage = noBootImage;
+  }
+
+  /**
+   * Get the name that would be provided to adb -s to communicate specifically with this device.
+   */
+  public String getName() {
+    if (isLocal) {
+      return "LOCAL DEVICE";
+    }
+    return deviceName;
+  }
+
+  public boolean isLocal() {
+    return isLocal;
+  }
+
+  /**
+   * Certain AOSP builds of Android may not have a full boot.art built. This will be set if
+   * we use --no-boot-image, and is used by Executors when deciding the arguments for dalvikvm
+   * and dex2oat when performing host-side verification.
+   */
+  public boolean noBootImageAvailable() {
+    return noBootImage;
+  }
+
+  /**
+   * Get the command prefix for this device if we want to use adb shell.
+   */
+  public String getExecutionShellPrefix() {
+    if (isLocal) {
+      return "";
+    }
+    return getExecutionPrefixWithAdb("shell");
+  }
+
+  /**
+   * Get the command prefix for this device if we want to use adb push.
+   */
+  public String getExecutionPushPrefix() {
+    if (isLocal) {
+      return "";
+    }
+    return getExecutionPrefixWithAdb("push");
+  }
+
+  private String getExecutionPrefixWithAdb(String command) {
+    if (usingSpecificDevice) {
+      return String.format("adb -s %s %s ", deviceName, command);
+    } else {
+      return String.format("adb %s ", command);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
new file mode 100644
index 0000000..7cc584d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -0,0 +1,303 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.StreamConsumer;
+import dexfuzz.listeners.BaseListener;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Base class containing the common methods for executing a particular backend of ART.
+ */
+public abstract class Executor {
+  private String androidHostOut;
+  private String androidProductOut;
+
+  private StreamConsumer outputConsumer;
+  private StreamConsumer errorConsumer;
+
+  protected ExecutionResult executionResult;
+  protected String executeClass;
+
+  // Set by subclasses.
+  protected String name;
+  protected int timeout;
+  protected BaseListener listener;
+  protected String testLocation;
+  protected Architecture architecture;
+  protected Device device;
+
+  protected Executor(String name, int timeout, BaseListener listener, Architecture architecture,
+      Device device) {
+    executeClass = Options.executeClass;
+
+    if (Options.shortTimeouts) {
+      this.timeout = 2;
+    } else {
+      this.timeout = timeout;
+    }
+
+    this.name = name;
+    this.listener = listener;
+    this.architecture = architecture;
+    this.device = device;
+
+    this.testLocation = Options.executeDirectory;
+
+    Map<String, String> envVars = System.getenv();
+    androidProductOut = checkForEnvVar(envVars, "ANDROID_PRODUCT_OUT");
+    androidHostOut = checkForEnvVar(envVars, "ANDROID_HOST_OUT");
+
+    outputConsumer = new StreamConsumer();
+    outputConsumer.start();
+    errorConsumer = new StreamConsumer();
+    errorConsumer.start();
+
+    if (!device.isLocal()) {
+      // Check for ADB.
+      try {
+        ProcessBuilder pb = new ProcessBuilder();
+        pb.command("adb", "devices");
+        Process process = pb.start();
+        int exitValue = process.waitFor();
+        if (exitValue != 0) {
+          Log.errorAndQuit("Problem executing ADB - is it in your $PATH?");
+        }
+      } catch (IOException e) {
+        Log.errorAndQuit("IOException when executing ADB, is it working?");
+      } catch (InterruptedException e) {
+        Log.errorAndQuit("InterruptedException when executing ADB, is it working?");
+      }
+
+      // Check we can run something on ADB.
+      ExecutionResult result = executeOnDevice("true", true);
+      if (result.getFlattenedAll().contains("device not found")) {
+        Log.errorAndQuit("Couldn't connect to specified ADB device: " + device.getName());
+      }
+    }
+  }
+
+  private String checkForEnvVar(Map<String, String> envVars, String key) {
+    if (!envVars.containsKey(key)) {
+      Log.errorAndQuit("Cannot run a fuzzed program if $" + key + " is not set!");
+    }
+    return envVars.get(key);
+  }
+
+  private ExecutionResult executeCommand(String command, boolean captureOutput) {
+    ExecutionResult result = new ExecutionResult();
+
+    Log.info("Executing: " + command);
+
+    try {
+      ProcessBuilder processBuilder = new ProcessBuilder(command.split(" "));
+      processBuilder.environment().put("ANDROID_ROOT", androidHostOut);
+      Process process = processBuilder.start();
+
+      if (captureOutput) {
+        // Give the streams to the StreamConsumers.
+        outputConsumer.giveStreamAndStartConsuming(process.getInputStream());
+        errorConsumer.giveStreamAndStartConsuming(process.getErrorStream());
+      }
+
+      // Wait until the process is done - the StreamConsumers will keep the
+      // buffers drained, so this shouldn't block indefinitely.
+      // Get the return value as well.
+      result.returnValue = process.waitFor();
+
+      Log.info("Return value: " + result.returnValue);
+
+      if (captureOutput) {
+        // Tell the StreamConsumers to stop consuming, and wait for them to finish
+        // so we know we have all of the output.
+        outputConsumer.processFinished();
+        errorConsumer.processFinished();
+        result.output = outputConsumer.getOutput();
+        result.error = errorConsumer.getOutput();
+
+        // Always explicitly indicate the return code in the text output now.
+        // NB: adb shell doesn't actually return exit codes currently, but this will
+        // be useful if/when it does.
+        result.output.add("RETURN CODE: " + result.returnValue);
+      }
+
+    } catch (IOException e) {
+      Log.errorAndQuit("ExecutionResult.execute() caught an IOException");
+    } catch (InterruptedException e) {
+      Log.errorAndQuit("ExecutionResult.execute() caught an InterruptedException");
+    }
+
+    return result;
+  }
+
+  /**
+   * Called by subclass Executors in their execute() implementations.
+   */
+  protected ExecutionResult executeOnDevice(String command, boolean captureOutput) {
+    String timeoutString = "timeout " + timeout + " ";
+    return executeCommand(timeoutString + device.getExecutionShellPrefix() + command,
+        captureOutput);
+  }
+
+  private ExecutionResult pushToDevice(String command) {
+    return executeCommand(device.getExecutionPushPrefix() + command, false);
+  }
+
+  /**
+   * Call this to make sure the StreamConsumer threads are stopped.
+   */
+  public void shutdown() {
+    outputConsumer.shutdown();
+    errorConsumer.shutdown();
+  }
+
+  /**
+   * Called by the Fuzzer after each execution has finished, to clear the results.
+   */
+  public void reset() {
+    executionResult = null;
+  }
+
+  /**
+   * Called by the Fuzzer to verify the mutated program using the host-side dex2oat.
+   */
+  public boolean verifyOnHost(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dex2oat ");
+
+    // This assumes that the Architecture enum's name, when reduced to lower-case,
+    // matches what dex2oat would expect.
+    commandBuilder.append("--instruction-set=").append(architecture.toString().toLowerCase());
+    commandBuilder.append(" --instruction-set-features=default ");
+
+    // Select the correct boot image.
+    commandBuilder.append("--boot-image=").append(androidProductOut);
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("/data/art-test/core.art ");
+    } else {
+      commandBuilder.append("/system/framework/boot.art ");
+    }
+
+    commandBuilder.append("--oat-file=output.oat ");
+    commandBuilder.append("--android-root=").append(androidHostOut).append(" ");
+    commandBuilder.append("--runtime-arg -classpath ");
+    commandBuilder.append("--runtime-arg ").append(programName).append(" ");
+    commandBuilder.append("--dex-file=").append(programName).append(" ");
+    commandBuilder.append("--compiler-filter=interpret-only --runtime-arg -Xnorelocate ");
+
+    ExecutionResult verificationResult = executeCommand(commandBuilder.toString(), true);
+
+    boolean success = true;
+
+    if (verificationResult.isSigabort()) {
+      listener.handleHostVerificationSigabort(verificationResult);
+      success = false;
+    }
+
+    if (success) {
+      // Search for a keyword that indicates verification was not successful.
+      // TODO: Determine if dex2oat crashed?
+      for (String line : verificationResult.error) {
+        if (line.contains("Verification error")
+            || line.contains("Failure to verify dex file")) {
+          success = false;
+        }
+        if (Options.dumpVerify) {
+          // Strip out the start of the log lines.
+          listener.handleDumpVerify(line.replaceFirst(".*(cc|h):\\d+] ",  ""));
+        }
+      }
+    }
+
+    if (!success) {
+      listener.handleFailedHostVerification(verificationResult);
+    }
+
+    executeCommand("rm output.oat", false);
+
+    return success;
+  }
+
+  /**
+   * Called by the Fuzzer to upload the program to the target device.
+   * TODO: Check if we're executing on a local device, and don't do this?
+   */
+  public void uploadToTarget(String programName) {
+    pushToDevice(programName + " " + testLocation);
+  }
+
+  /**
+   * Executor subclasses need to override this, to construct their arguments for dalvikvm
+   * invocation correctly.
+   */
+  public abstract void execute(String programName);
+
+  /**
+   * Executor subclasses need to override this, to delete their generated OAT file correctly.
+   */
+  public abstract void deleteGeneratedOatFile(String programName);
+
+  /**
+   * Executor subclasses need to override this, to report if they need a cleaned code cache.
+   */
+  public abstract boolean needsCleanCodeCache();
+
+  /**
+   * Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor.
+   */
+  public Architecture getArchitecture() {
+    return architecture;
+  }
+
+  /**
+   * Used in each subclass of Executor's deleteGeneratedOatFile() method, to know what to delete.
+   */
+  protected String getOatFileName(String programName) {
+    // Converts e.g. /data/art-test/file.dex to data@art-test@file.dex
+    return (testLocation.replace("/", "@").substring(1) + "@" + programName);
+  }
+
+  /**
+   * Used by the Fuzzer to get result of execution.
+   */
+  public ExecutionResult getResult() {
+    return executionResult;
+  }
+
+  /**
+   * Because dex2oat can accept a program with soft errors on the host, and then fail after
+   * performing hard verification on the target, we need to check if the Executor detected
+   * a target verification failure, before doing anything else with the resulting output.
+   * Used by the Fuzzer.
+   */
+  public boolean verifyOnTarget() {
+    // TODO: Remove this once host-verification can be forced to always fail?
+    if (executionResult.getFlattenedOutput().contains("VerifyError")) {
+      return false;
+    }
+    return true;
+  }
+
+  public String getName() {
+    return name;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
new file mode 100644
index 0000000..9f27b5e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64InterpreterExecutor extends Executor {
+
+  public Mips64InterpreterExecutor(BaseListener listener, Device device) {
+    super("MIPS64 Interpreter", 30, listener, Architecture.MIPS64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..b30240d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64OptimizingBackendExecutor extends Executor {
+
+  public Mips64OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS64 Optimizing Backend", 5, listener, Architecture.MIPS64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
new file mode 100644
index 0000000..42ccd1e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64QuickBackendExecutor extends Executor {
+
+  public Mips64QuickBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
new file mode 100644
index 0000000..524eaa9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsInterpreterExecutor extends Executor {
+
+  public MipsInterpreterExecutor(BaseListener listener, Device device) {
+    super("MIPS Interpreter", 30, listener, Architecture.MIPS, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
new file mode 100644
index 0000000..fcc92c8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsOptimizingBackendExecutor extends Executor {
+
+  public MipsOptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS Optimizing Backend", 5, listener, Architecture.MIPS, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
new file mode 100644
index 0000000..cb442f9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsQuickBackendExecutor extends Executor {
+
+  public MipsQuickBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
new file mode 100644
index 0000000..93c14e9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86InterpreterExecutor extends Executor {
+
+  public X86InterpreterExecutor(BaseListener listener, Device device) {
+    super("x86 Interpreter", 30, listener, Architecture.X86, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
new file mode 100644
index 0000000..b27d5ca
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86OptimizingBackendExecutor extends Executor {
+
+  public X86OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("x86 Optimizing Backend", 5, listener, Architecture.X86, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
new file mode 100644
index 0000000..d8ec217
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86QuickBackendExecutor extends Executor {
+
+  public X86QuickBackendExecutor(BaseListener listener, Device device) {
+    super("x86 Quick Backend", 5, listener, Architecture.X86, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
new file mode 100644
index 0000000..7497322
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64InterpreterExecutor extends Executor {
+
+  public X86_64InterpreterExecutor(BaseListener listener, Device device) {
+    super("x86_64 Interpreter", 30, listener, Architecture.X86_64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..a978f73
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64OptimizingBackendExecutor extends Executor {
+
+  public X86_64OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("x86_64 Optimizing Backend", 5, listener, Architecture.X86_64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
new file mode 100644
index 0000000..85532d8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64QuickBackendExecutor extends Executor {
+
+  public X86_64QuickBackendExecutor(BaseListener listener, Device device) {
+    super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
new file mode 100644
index 0000000..4c1acdb
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
@@ -0,0 +1,449 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.Timer;
+import dexfuzz.executors.Architecture;
+import dexfuzz.executors.Arm64InterpreterExecutor;
+import dexfuzz.executors.Arm64OptimizingBackendExecutor;
+import dexfuzz.executors.Arm64QuickBackendExecutor;
+import dexfuzz.executors.ArmInterpreterExecutor;
+import dexfuzz.executors.ArmOptimizingBackendExecutor;
+import dexfuzz.executors.ArmQuickBackendExecutor;
+import dexfuzz.executors.Device;
+import dexfuzz.executors.Executor;
+import dexfuzz.executors.Mips64InterpreterExecutor;
+import dexfuzz.executors.Mips64OptimizingBackendExecutor;
+import dexfuzz.executors.Mips64QuickBackendExecutor;
+import dexfuzz.executors.MipsInterpreterExecutor;
+import dexfuzz.executors.MipsOptimizingBackendExecutor;
+import dexfuzz.executors.MipsQuickBackendExecutor;
+import dexfuzz.executors.X86InterpreterExecutor;
+import dexfuzz.executors.X86OptimizingBackendExecutor;
+import dexfuzz.executors.X86QuickBackendExecutor;
+import dexfuzz.executors.X86_64InterpreterExecutor;
+import dexfuzz.executors.X86_64OptimizingBackendExecutor;
+import dexfuzz.executors.X86_64QuickBackendExecutor;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Mutation;
+import dexfuzz.program.Program;
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.OffsetTracker;
+import dexfuzz.rawdex.RawDexFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A particular fuzzing strategy, this class provides the common methods
+ * most fuzzing will involve, and subclasses override the run() method, to
+ * employ a particular strategy.
+ */
+public abstract class Fuzzer {
+  private List<Executor> executors;
+  private OffsetTracker offsetTracker;
+
+  /**
+   * This is the executor that we use to test for self-divergent programs.
+   */
+  private Executor goldenExecutor;
+
+  /*
+   * These two flags are set during fuzz(), and then cleared at the end of execute().
+   */
+  private boolean mutatedSuccessfully;
+  private boolean savedSuccessfully;
+
+  private Timer totalTimer = new Timer("Total Time");
+  private Timer timerDexInput = new Timer("DEX Input");
+  private Timer timerProgGen = new Timer("Program Generation");
+  private Timer timerMutation = new Timer("Mutation Time");
+  private Timer timerDexOutput = new Timer("DEX Output");
+  private Timer timerChecksumCalc = new Timer("Checksum Calculation");
+
+  protected BaseListener listener;
+
+  protected Fuzzer(BaseListener listener) {
+    totalTimer.start();
+    executors = new ArrayList<Executor>();
+    this.listener = listener;
+  }
+
+  public abstract void run();
+
+  protected abstract String getNextInputFilename();
+
+  protected abstract String getNextOutputFilename();
+
+  /**
+   * Call this after fuzzer execution to print out timing results.
+   */
+  public void printTimingInfo() {
+    totalTimer.stop();
+    timerDexInput.printTime(listener);
+    timerProgGen.printTime(listener);
+    timerMutation.printTime(listener);
+    timerDexOutput.printTime(listener);
+    timerChecksumCalc.printTime(listener);
+    totalTimer.printTime(listener);
+  }
+
+  /**
+   * Make sure this is called to correctly shutdown each Executor's StreamConsumers.
+   */
+  public void shutdown() {
+    if (executors != null) {
+      for (Executor executor : executors) {
+        executor.shutdown();
+      }
+    }
+  }
+
+  private void addExecutorsForArchitecture(Device device, Class<? extends Executor> quick,
+      Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) {
+    // NB: Currently QuickBackend MUST come immediately before same arch's Interpreter.
+    // This is because intepreter execution relies on there being an OAT file already
+    // created to produce correct debug information. Otherwise we will see
+    // false-positive divergences.
+    try {
+      if (Options.useQuick) {
+        Constructor<? extends Executor> constructor =
+            quick.getConstructor(BaseListener.class, Device.class);
+        executors.add(constructor.newInstance(listener, device));
+      }
+      if (Options.useOptimizing) {
+        Constructor<? extends Executor> constructor =
+            optimizing.getConstructor(BaseListener.class, Device.class);
+        executors.add(constructor.newInstance(listener, device));
+      }
+      if (Options.useInterpreter) {
+        Constructor<? extends Executor> constructor =
+            interpreter.getConstructor(BaseListener.class, Device.class);
+        executors.add(constructor.newInstance(listener, device));
+      }
+    } catch (NoSuchMethodException e) {
+      Log.errorAndQuit("Executor doesn't have correct constructor.");
+    } catch (InstantiationException e) {
+      Log.errorAndQuit("Executor couldn't be instantiated.");
+    } catch (IllegalAccessException e) {
+      Log.errorAndQuit("Executor couldn't be accessed.");
+    } catch (IllegalArgumentException e) {
+      Log.errorAndQuit("Invalid arguments to instantiation of Executor.");
+    } catch (InvocationTargetException e) {
+      Log.errorAndQuit("Instantiation of Executor threw an Exception!");
+    }
+  }
+
+  protected void addExecutors() {
+    Device device = null;
+    if (Options.local) {
+      device = new Device();
+    } else {
+      device = new Device(Options.deviceName, Options.noBootImage);
+    }
+
+    if (Options.useArchArm64) {
+      addExecutorsForArchitecture(device, Arm64QuickBackendExecutor.class,
+          Arm64OptimizingBackendExecutor.class, Arm64InterpreterExecutor.class);
+    }
+
+    if (Options.useArchArm) {
+      addExecutorsForArchitecture(device, ArmQuickBackendExecutor.class,
+          ArmOptimizingBackendExecutor.class, ArmInterpreterExecutor.class);
+    }
+
+    if (Options.useArchX86_64) {
+      addExecutorsForArchitecture(device, X86_64QuickBackendExecutor.class,
+          X86_64OptimizingBackendExecutor.class, X86_64InterpreterExecutor.class);
+    }
+
+    if (Options.useArchX86) {
+      addExecutorsForArchitecture(device, X86QuickBackendExecutor.class,
+          X86OptimizingBackendExecutor.class, X86InterpreterExecutor.class);
+    }
+
+    if (Options.useArchMips64) {
+      addExecutorsForArchitecture(device, Mips64QuickBackendExecutor.class,
+          Mips64OptimizingBackendExecutor.class, Mips64InterpreterExecutor.class);
+    }
+
+    if (Options.useArchMips) {
+      addExecutorsForArchitecture(device, MipsQuickBackendExecutor.class,
+          MipsOptimizingBackendExecutor.class, MipsInterpreterExecutor.class);
+    }
+
+    // Add the first backend as the golden executor for self-divergence tests.
+    goldenExecutor = executors.get(0);
+  }
+
+  /**
+   * Called from each Fuzzer subclass that we can instantiate. Parses the program, fuzzes it,
+   * and then saves it, if mutation was successful. We can use --skip-mutation to bypass
+   * the mutation phase, if we wanted to verify that a test program itself works.
+   */
+  protected Program fuzz() {
+    String inputFile = getNextInputFilename();
+    Program program = loadProgram(inputFile, null);
+    if (program == null) {
+      Log.errorAndQuit("Problem loading seed file.");
+    }
+    // Mutate the program.
+    if (!Options.skipMutation) {
+      timerMutation.start();
+      program.mutateTheProgram();
+
+      mutatedSuccessfully = program.updateRawDexFile();
+      timerMutation.stop();
+      if (!mutatedSuccessfully) {
+        listener.handleMutationFail();
+      }
+    } else {
+      Log.info("Skipping mutation stage as requested.");
+      mutatedSuccessfully = true;
+    }
+    if (mutatedSuccessfully) {
+      savedSuccessfully = saveProgram(program, getNextOutputFilename());
+    }
+    return program;
+  }
+
+  protected boolean safeToExecute() {
+    return mutatedSuccessfully && savedSuccessfully;
+  }
+
+  protected void execute(Program program) {
+    if (!safeToExecute()) {
+      Log.errorAndQuit("Your Fuzzer subclass called execute() "
+          + "without checking safeToExecute()!");
+    }
+
+    String programName = getNextOutputFilename();
+    boolean verified = true;
+    if (!Options.skipHostVerify) {
+      verified = goldenExecutor.verifyOnHost(programName);
+    }
+    if (verified) {
+      boolean skipAnalysis = false;
+      boolean uploadedToTarget = false;
+      if (!Options.skipHostVerify) {
+        listener.handleSuccessfulHostVerification();
+      }
+      for (Executor executor : executors) {
+        executor.reset();
+        if (!uploadedToTarget) {
+          executor.uploadToTarget(programName);
+        } else {
+          uploadedToTarget = true;
+        }
+        if (executor.needsCleanCodeCache()) {
+          executor.deleteGeneratedOatFile(programName);
+        }
+        executor.execute(programName);
+        if (!executor.verifyOnTarget()) {
+          listener.handleFailedTargetVerification();
+          skipAnalysis = true;
+          break;
+        }
+        // Results are saved in the executors until they reset, usually at the
+        // next iteration.
+      }
+
+      if (!skipAnalysis) {
+        listener.handleSuccessfullyFuzzedFile(programName);
+        analyseResults(program, programName);
+      }
+    }
+    mutatedSuccessfully = false;
+    savedSuccessfully = false;
+  }
+
+  /**
+   * Checks if the different outputs we observed align with different architectures.
+   */
+  private boolean checkForArchitectureSplit(Map<String, List<Executor>> outputMap) {
+    if (outputMap.size() != 2) {
+      // Cannot have a two-way split if we don't have 2 kinds of output.
+      return false;
+    }
+
+    Architecture[] architectures = new Architecture[2];
+    int archIdx = 0;
+
+    // For each kind of output we saw, make sure they all
+    // came from the same architecture.
+    for (List<Executor> executorList : outputMap.values()) {
+      architectures[archIdx] = executorList.get(0).getArchitecture();
+      for (int execIdx = 1; execIdx < executorList.size(); execIdx++) {
+        if (executorList.get(execIdx).getArchitecture() != architectures[archIdx]) {
+          // Not every executor with this output shared the same architecture.
+          return false;
+        }
+      }
+      archIdx++;
+    }
+
+    // Now make sure that the two outputs we saw were different architectures.
+    if (architectures[0] == architectures[1]) {
+      return false;
+    }
+    return true;
+  }
+
+  private boolean checkGoldenExecutorForSelfDivergence(String programName) {
+    // Run golden executor 5 times, make sure it always produces
+    // the same output, otherwise report that it is self-divergent.
+
+    // TODO: Instead, produce a list of acceptable outputs, and see if the divergent
+    // outputs of the backends fall within this set of outputs.
+    String seenOutput = null;
+    for (int i = 0; i < 5; i++) {
+      goldenExecutor.reset();
+      goldenExecutor.execute(programName);
+      String output = goldenExecutor.getResult().getFlattenedOutput();
+      if (seenOutput == null) {
+        seenOutput = output;
+      } else if (!seenOutput.equals(output)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void analyseResults(Program program, String programName) {
+    // Check timeouts.
+    // Construct two lists of executors, those who timed out, and those who did not.
+    // Report if we had some timeouts.
+    List<Executor> timedOut = new ArrayList<Executor>();
+    List<Executor> didNotTimeOut = new ArrayList<Executor>();
+    for (Executor executor : executors) {
+      if (executor.getResult().isTimeout()) {
+        timedOut.add(executor);
+      } else {
+        didNotTimeOut.add(executor);
+      }
+    }
+    if (!timedOut.isEmpty()) {
+      listener.handleTimeouts(timedOut, didNotTimeOut);
+      // Do not bother reporting divergence information.
+      return;
+    }
+
+    // Check divergences.
+    // Construct a map {output1: [executor that produced output1, ...], output2: [...]}
+    // If the map has more than one output, we had divergence, report it.
+    Map<String, List<Executor>> outputMap = new HashMap<String, List<Executor>>();
+    for (Executor executor : executors) {
+      String output = executor.getResult().getFlattenedOutput();
+      if (Options.dumpOutput) {
+        listener.handleDumpOutput(
+            executor.getResult().getFlattenedOutputWithNewlines(), executor);
+      }
+      if (outputMap.containsKey(output)) {
+        outputMap.get(output).add(executor);
+      } else {
+        List<Executor> newList = new ArrayList<Executor>();
+        newList.add(executor);
+        outputMap.put(output, newList);
+      }
+    }
+
+    if (outputMap.size() > 1) {
+      // Report that we had divergence.
+      listener.handleDivergences(outputMap);
+      listener.handleMutations(program.getMutations());
+      // If we found divergences, try running the "golden executor"
+      // a few times in succession, to see if the output it produces is different
+      // from run to run. If so, then we're probably executing something with either:
+      //  a) randomness
+      //  b) timing-dependent code
+      //  c) threads
+      // So we will not consider it a "true" divergence, but still useful?
+      if (checkGoldenExecutorForSelfDivergence(programName)) {
+        listener.handleSelfDivergence();
+        return;
+      }
+      // If we found divergences, try checking if the differences
+      // in outputs align with differences in architectures.
+      // For example, if we have: {Output1: [ARM, ARM], Output2: [ARM64, ARM64]}
+      if (checkForArchitectureSplit(outputMap)) {
+        listener.handleArchitectureSplit();
+      }
+    } else {
+      // No problems with execution.
+      listener.handleSuccess(outputMap);
+    }
+  }
+
+  private Program loadProgram(String inputName, List<Mutation> mutations) {
+    Program program = null;
+    try {
+      DexRandomAccessFile input = new DexRandomAccessFile(inputName, "r");
+      offsetTracker = new OffsetTracker();
+      input.setOffsetTracker(offsetTracker);
+      // Read the raw DexFile
+      RawDexFile rawDexFile = new RawDexFile();
+      timerDexInput.start();
+      rawDexFile.read(input);
+      timerDexInput.stop();
+      input.close();
+      // Create the program view.
+      timerProgGen.start();
+      program = new Program(rawDexFile, mutations, listener);
+      timerProgGen.stop();
+    } catch (FileNotFoundException e) {
+      Log.errorAndQuit("Couldn't open a file called " + inputName);
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException when trying to load a DEX test file!");
+    }
+    return program;
+  }
+
+  private boolean saveProgram(Program program, String outputName) {
+    boolean success = false;
+
+    try {
+      // Write out the results of mutation.
+      DexRandomAccessFile output = new DexRandomAccessFile(outputName, "rw");
+      output.setOffsetTracker(offsetTracker);
+      // Delete the contents of the file, in case it already existed.
+      output.setLength(0);
+      // Write out the file.
+      timerDexOutput.start();
+      program.writeRawDexFile(output);
+      timerDexOutput.stop();
+      // Recalculate the header info.
+      timerChecksumCalc.start();
+      program.updateRawDexFileHeader(output);
+      timerChecksumCalc.stop();
+      output.close();
+      success = true;
+    } catch (FileNotFoundException e) {
+      Log.errorAndQuit("Couldn't open a file called " + outputName);
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException when trying to save a DEX test file!");
+    }
+    return success;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java
new file mode 100644
index 0000000..8abaeb0
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Superclass for fuzzing strategies that perform multiple fuzzes, and want
+ * their inputs to come from the input list in a round-robin fashion.
+ */
+public abstract class FuzzerMultiple extends Fuzzer {
+  protected int iterations;
+
+  protected FuzzerMultiple(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  protected String getNextInputFilename() {
+    String inputFile = Options.inputFileList.get(0);
+    if (Options.inputFileList.size() > 1) {
+      int nextIndex = iterations % Options.inputFileList.size();
+      inputFile = Options.inputFileList.get(nextIndex);
+    }
+    listener.handleFuzzingFile(inputFile);
+    return inputFile;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java
new file mode 100644
index 0000000..0cf6df7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Program;
+
+/**
+ * Fuzz programs multiple times, testing each.
+ */
+public class FuzzerMultipleExecute extends FuzzerMultiple {
+  public FuzzerMultipleExecute(BaseListener listener) {
+    super(listener);
+    addExecutors();
+  }
+
+  @Override
+  protected String getNextOutputFilename() {
+    // In MultipleExecute, always use the same output.
+    return Options.outputFile;
+  }
+
+  @Override
+  public void run() {
+    // TODO: Test that all seed files execute correctly before they are mutated!
+    for (iterations = 0; iterations < Options.repeat; iterations++) {
+      listener.handleIterationStarted(iterations);
+      Program program = fuzz();
+      if (safeToExecute()) {
+        execute(program);
+      }
+      listener.handleIterationFinished(iterations);
+    }
+    listener.handleSummary();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java
new file mode 100644
index 0000000..fea8788
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Fuzz programs multiple times, writing each one to a new DEX file.
+ */
+public class FuzzerMultipleNoExecute extends FuzzerMultiple {
+  public FuzzerMultipleNoExecute(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  protected String getNextOutputFilename() {
+    // In MultipleNoExecute, produce multiple files, each prefixed
+    // with the iteration value.
+    return String.format("%09d_%s", iterations, Options.outputFile);
+  }
+
+  @Override
+  public void run() {
+    for (iterations = 0; iterations < Options.repeat; iterations++) {
+      listener.handleIterationStarted(iterations);
+      fuzz();
+      listener.handleIterationFinished(iterations);
+    }
+    listener.handleSummary();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java
new file mode 100644
index 0000000..68b47c2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Superclass for fuzzers that fuzz once.
+ */
+public abstract class FuzzerSingle extends Fuzzer {
+  protected FuzzerSingle(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  protected String getNextInputFilename() {
+    return Options.inputFileList.get(0);
+  }
+
+  protected String getNextOutputFilename() {
+    return Options.outputFile;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java
new file mode 100644
index 0000000..de0c7db
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Program;
+
+/**
+ * Fuzz a DEX file once, and test it.
+ */
+public class FuzzerSingleExecute extends FuzzerSingle {
+  public FuzzerSingleExecute(BaseListener listener) {
+    super(listener);
+    addExecutors();
+  }
+
+  @Override
+  public void run() {
+    Program program = fuzz();
+    if (safeToExecute()) {
+      execute(program);
+    }
+  }
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
similarity index 65%
rename from runtime/arch/arm64/portable_entrypoints_arm64.S
rename to tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
index 9e2c030..6015284 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-#include "asm_support_arm64.S"
+package dexfuzz.fuzzers;
 
-    /*
-     * Portable invocation stub.
-     */
-UNIMPLEMENTED art_portable_invoke_stub
+import dexfuzz.listeners.BaseListener;
 
-UNIMPLEMENTED art_portable_proxy_invoke_handler
+/**
+ * Fuzz a DEX file once, but don't test it.
+ */
+public class FuzzerSingleNoExecute extends FuzzerSingle {
+  public FuzzerSingleNoExecute(BaseListener listener) {
+    super(listener);
+  }
 
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+  @Override
+  public void run() {
+    fuzz();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java b/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java
new file mode 100644
index 0000000..e33fb09
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for Listeners, who are notified about certain events in dexfuzz's execution.
+ */
+public abstract class BaseListener {
+  public void setup() { }
+
+  public void shutdown() { }
+
+  public void handleSuccessfulHostVerification() { }
+
+  public void handleFailedHostVerification(ExecutionResult verificationResult) { }
+
+  public void handleFailedTargetVerification() { }
+
+  public void handleIterationStarted(int iteration) { }
+
+  public void handleIterationFinished(int iteration) { }
+
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) { }
+
+  public void handleDivergences(Map<String, List<Executor>> outputMap) { }
+
+  public void handleFuzzingFile(String inputFile) { }
+
+  public void handleSeed(long seed) { }
+
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) { }
+
+  public void handleSuccess(Map<String, List<Executor>> outputMap) { }
+
+  public void handleDumpOutput(String outputLine, Executor executor) { }
+
+  public void handleDumpVerify(String verifyLine) { }
+
+  public void handleMutationStats(String statsString) { }
+
+  public void handleTiming(String name, float elapsedTime) { }
+
+  public void handleMutationFail() { }
+
+  public void handleSummary() { }
+
+  public void handleSuccessfullyFuzzedFile(String programName) { }
+
+  public void handleSelfDivergence() { }
+
+  public void handleMessage(String msg) { }
+
+  public void handleMutations(List<Mutation> mutations) { }
+
+  public void handleArchitectureSplit() { }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java
new file mode 100644
index 0000000..1ea74d9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Logs output to the console, when not using --repeat.
+ */
+public class ConsoleLoggerListener extends BaseListener {
+  @Override
+  public void setup() {
+
+  }
+
+  @Override
+  public void shutdown() {
+
+  }
+
+  private void logToConsole(String msg) {
+    System.out.println("CONSOLE: " + msg);
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    logToConsole("Successful host verification");
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    logToConsole("Failed host verification");
+  }
+
+  @Override
+  public void handleMutations(List<Mutation> mutations) {
+    for (Mutation mutation : mutations) {
+      logToConsole("Applied mutation: " + mutation.toString());
+    }
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    logToConsole("Detected architectural split.");
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    logToConsole("Failed target verification");
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    logToConsole("Starting iteration " + iteration);
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    logToConsole("Finished iteration " + iteration);
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    logToConsole("Timed out: " + timedOut.size() + " Did not time out: " + didNotTimeOut.size());
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    logToConsole("Got divergences!");
+    int outputCount = 1;
+    for (List<Executor> executors : outputMap.values()) {
+      logToConsole("Output " + outputCount + ":");
+      for (Executor executor : executors) {
+        logToConsole("  " + executor.getName());
+      }
+      outputCount++;
+    }
+  }
+
+  @Override
+  public void handleFuzzingFile(String inputFile) {
+    logToConsole("Fuzzing: " + inputFile);
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    logToConsole("Seed: " + seed);
+  }
+
+  @Override
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+    logToConsole("Sigaborted host verification");
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    logToConsole("Program " + programName + " successfully fuzzed.");
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    logToConsole("Execution was successful");
+  }
+
+  @Override
+  public void handleDumpOutput(String outputLine, Executor executor) {
+    logToConsole(executor.getName() + " OUTPUT: " + outputLine);
+  }
+
+  @Override
+  public void handleDumpVerify(String verifyLine) {
+    logToConsole("VERIFY: " + verifyLine);
+  }
+
+  @Override
+  public void handleMutationFail() {
+    logToConsole("DEX file was not mutated");
+  }
+
+  @Override
+  public void handleMutationStats(String statsString) {
+    logToConsole("Mutations performed: " + statsString);
+  }
+
+  @Override
+  public void handleTiming(String name, float elapsedTime) {
+    logToConsole(String.format("'%s': %.3fs", name, elapsedTime));
+  }
+
+  @Override
+  public void handleSummary() {
+    logToConsole("--- SUMMARY ---");
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    logToConsole("Seen self divergence");
+  }
+
+  @Override
+  public void handleMessage(String msg) {
+    logToConsole(msg);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java b/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java
new file mode 100644
index 0000000..09ee756
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+import dexfuzz.program.MutationSerializer;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Logs events to a file.
+ */
+public class LogFileListener extends BaseListener {
+  private BufferedWriter writer;
+  boolean ready = false;
+
+  long successfulVerification;
+  long failedVerification;
+  long failedMutation;
+  long success;
+  long timedOut;
+  long divergence;
+  long selfDivergent;
+  long architectureSplit;
+  long iterations;
+
+  private String logFile;
+
+  public LogFileListener(String logFile) {
+    this.logFile = logFile;
+  }
+
+  @Override
+  public void setup() {
+    try {
+      writer = new BufferedWriter(new FileWriter(logFile));
+      ready = true;
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void shutdown() {
+    try {
+      writer.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    Log.always("Full log in " + logFile);
+  }
+
+  private void write(String msg) {
+    if (!ready) {
+      return;
+    }
+    try {
+      writer.write(msg + "\n");
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    write("Host verification: SUCCESS");
+    successfulVerification++;
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    write("Host verification: FAILED");
+    failedVerification++;
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    write("Target verification: FAILED");
+    failedVerification++;
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    write("--> FUZZ " + (iteration + 1));
+    Date now = new Date(System.currentTimeMillis());
+    write("Time started: " + now.toString());
+    iterations++;
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    write("Some executors timed out.");
+    write("Timed out:");
+    for (Executor executor : timedOut) {
+      write("  " + executor.getName());
+    }
+    if (!didNotTimeOut.isEmpty()) {
+      write("Did not time out:");
+      for (Executor executor : didNotTimeOut) {
+        write("  " + executor.getName());
+      }
+    }
+    this.timedOut++;
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    write("DIVERGENCE between some executors!");
+    int outputCount = 1;
+    for (List<Executor> executors : outputMap.values()) {
+      write("Output " + outputCount + ":");
+      for (Executor executor : executors) {
+        write("  " + executor.getName());
+      }
+      outputCount++;
+    }
+    divergence++;
+
+    // You are probably interested in reading about these divergences while fuzzing
+    // is taking place, so flush the writer now.
+    try {
+      writer.flush();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleFuzzingFile(String inputFile) {
+    write("Fuzzing file '" + inputFile + "'");
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    write("Using " + seed + " for seed.");
+    // Flush the seed as well, so if anything goes wrong we can see what seed lead
+    // to the issue.
+    try {
+      writer.flush();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+    write("Host verification: SIGABORTED");
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    write("All executors agreed on result.");
+    success++;
+  }
+
+  @Override
+  public void handleDumpOutput(String outputLine, Executor executor) {
+    write(executor.getName() + " OUTPUT:");
+    write(outputLine);
+  }
+
+  @Override
+  public void handleDumpVerify(String verifyLine) {
+    write("VERIFY: " + verifyLine);
+  }
+
+  @Override
+  public void handleMutationStats(String statsString) {
+    write("Mutation Stats: " + statsString);
+  }
+
+  @Override
+  public void handleTiming(String name, float elapsedTime) {
+    write(String.format("'%s': %.3fs", name, elapsedTime));
+  }
+
+  @Override
+  public void handleMutationFail() {
+    write("Mutation process: FAILED");
+    failedMutation++;
+  }
+
+  @Override
+  public void handleSummary() {
+    write("");
+    write("---+++--- SUMMARY ---+++---");
+    write("Fuzzing attempts: " + iterations);
+    write("  Failed verification: " + failedVerification);
+    write("  Failed mutation: " + failedMutation);
+    write("  Timed out: " + timedOut);
+    write("Successful: " + success);
+    write("  Self divergent: " + selfDivergent);
+    write("  Architecture split: " + architectureSplit);
+    write("Produced divergence: " + divergence);
+
+    long truelyDivergent = divergence - (selfDivergent + architectureSplit);
+    long verified = success + timedOut + truelyDivergent;
+
+    write("");
+
+    float verifiedTotalRatio =
+        (((float) (verified)) / iterations) * 100.0f;
+    write(String.format("Percentage Verified/Total: %.3f%%", verifiedTotalRatio));
+
+    float timedOutVerifiedRatio =
+        (((float) timedOut) / (verified)) * 100.0f;
+    write(String.format("Percentage Timed Out/Verified: %.3f%%", timedOutVerifiedRatio));
+
+    float successfulVerifiedRatio =
+        (((float) success) / (verified)) * 100.0f;
+    write(String.format("Percentage Successful/Verified: %.3f%%", successfulVerifiedRatio));
+
+    float divergentVerifiedRatio =
+        (((float) truelyDivergent) / (verified)) * 100.0f;
+    write(String.format("Percentage Divergent/Verified: %.3f%%", divergentVerifiedRatio));
+
+    write("---+++--- SUMMARY ---+++---");
+    write("");
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    write("");
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    write("Successfully fuzzed file '" + programName + "'");
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    write("Golden Executor was self-divergent!");
+    selfDivergent++;
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    write("Divergent outputs align with difference in architectures.");
+    architectureSplit++;
+  }
+
+  @Override
+  public void handleMessage(String msg) {
+    write(msg);
+  }
+
+  @Override
+  public void handleMutations(List<Mutation> mutations) {
+    write("Mutations Report");
+    for (Mutation mutation : mutations) {
+      write(MutationSerializer.getMutationString(mutation));
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java
new file mode 100644
index 0000000..28ebce7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles situation where multiple Listeners are wanted, passes notifications
+ * onto each Listener it is responsible for.
+ */
+public class MultiplexerListener extends BaseListener {
+
+  private List<BaseListener> listeners;
+
+  @Override
+  public void setup() {
+    listeners = new ArrayList<BaseListener>();
+  }
+
+  public void addListener(BaseListener listener) {
+    listeners.add(listener);
+    listener.setup();
+  }
+
+  @Override
+  public void shutdown() {
+    for (BaseListener listener : listeners) {
+      listener.shutdown();
+    }
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    for (BaseListener listener : listeners) {
+      listener.handleSuccessfulHostVerification();
+    }
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    for (BaseListener listener : listeners) {
+      listener.handleFailedHostVerification(verificationResult);
+    }
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    for (BaseListener listener : listeners) {
+      listener.handleFailedTargetVerification();
+    }
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    for (BaseListener listener : listeners) {
+      listener.handleIterationStarted(iteration);
+    }
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    for (BaseListener listener : listeners) {
+      listener.handleIterationFinished(iteration);
+    }
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    for (BaseListener listener : listeners) {
+      listener.handleTimeouts(timedOut, didNotTimeOut);
+    }
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    for (BaseListener listener : listeners) {
+      listener.handleDivergences(outputMap);
+    }
+  }
+
+  @Override
+  public void handleFuzzingFile(String inputFile) {
+    for (BaseListener listener : listeners) {
+      listener.handleFuzzingFile(inputFile);
+    }
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    for (BaseListener listener : listeners) {
+      listener.handleSeed(seed);
+    }
+  }
+
+  @Override
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+    for (BaseListener listener : listeners) {
+      listener.handleHostVerificationSigabort(verificationResult);
+    }
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    for (BaseListener listener : listeners) {
+      listener.handleSuccess(outputMap);
+    }
+  }
+
+  @Override
+  public void handleDumpOutput(String outputLine, Executor executor) {
+    for (BaseListener listener : listeners) {
+      listener.handleDumpOutput(outputLine, executor);
+    }
+  }
+
+  @Override
+  public void handleDumpVerify(String verifyLine) {
+    for (BaseListener listener : listeners) {
+      listener.handleDumpVerify(verifyLine);
+    }
+  }
+
+  @Override
+  public void handleMutationStats(String statsString) {
+    for (BaseListener listener : listeners) {
+      listener.handleMutationStats(statsString);
+    }
+  }
+
+  @Override
+  public void handleTiming(String name, float elapsedTime) {
+    for (BaseListener listener : listeners) {
+      listener.handleTiming(name, elapsedTime);
+    }
+  }
+
+  @Override
+  public void handleMutationFail() {
+    for (BaseListener listener : listeners) {
+      listener.handleMutationFail();
+    }
+  }
+
+  @Override
+  public void handleSummary() {
+    for (BaseListener listener : listeners) {
+      listener.handleSummary();
+    }
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    for (BaseListener listener : listeners) {
+      listener.handleSuccessfullyFuzzedFile(programName);
+    }
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    for (BaseListener listener : listeners) {
+      listener.handleSelfDivergence();
+    }
+  }
+
+  @Override
+  public void handleMessage(String msg) {
+    for (BaseListener listener : listeners) {
+      listener.handleMessage(msg);
+    }
+  }
+
+  @Override
+  public void handleMutations(List<Mutation> mutations) {
+    for (BaseListener listener : listeners) {
+      listener.handleMutations(mutations);
+    }
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    for (BaseListener listener : listeners) {
+      listener.handleArchitectureSplit();
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java
new file mode 100644
index 0000000..affaffc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.executors.Executor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tracks unique programs and outputs. Also saves divergent programs!
+ */
+public class UniqueProgramTrackerListener extends BaseListener {
+  /**
+   * Map of unique program MD5 sums, mapped to times seen.
+   */
+  private Map<String, Integer> uniquePrograms;
+
+  /**
+   * Map of unique program outputs (MD5'd), mapped to times seen.
+   */
+  private Map<String, Integer> uniqueOutputs;
+
+  /**
+   * Used to remember the seed used to fuzz the fuzzed file, so we can save it with this
+   * seed as a name, if we find a divergence.
+   */
+  private long currentSeed;
+
+  /**
+   * Used to remember the name of the file we've fuzzed, so we can save it if we
+   * find a divergence.
+   */
+  private String fuzzedFile;
+
+  private MessageDigest digest;
+  private String databaseFile;
+
+  /**
+   * Save the database every X number of iterations.
+   */
+  private static final int saveDatabasePeriod = 20;
+
+  public UniqueProgramTrackerListener(String databaseFile) {
+    this.databaseFile = databaseFile;
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    currentSeed = seed;
+  }
+
+  /**
+   * Given a program filename, calculate the MD5sum of
+   * this program.
+   */
+  private String getMD5SumOfProgram(String programName) {
+    byte[] buf = new byte[256];
+    try {
+      FileInputStream stream = new FileInputStream(programName);
+      boolean done = false;
+      while (!done) {
+        int bytesRead = stream.read(buf);
+        if (bytesRead == -1) {
+          done = true;
+        } else {
+          digest.update(buf);
+        }
+      }
+      stream.close();
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return new String(digest.digest());
+  }
+
+  private String getMD5SumOfOutput(String output) {
+    digest.update(output.getBytes());
+    return new String(digest.digest());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void loadUniqueProgsData() {
+    File file = new File(databaseFile);
+    if (!file.exists()) {
+      uniquePrograms = new HashMap<String, Integer>();
+      uniqueOutputs = new HashMap<String, Integer>();
+      return;
+    }
+
+    try {
+      ObjectInputStream objectStream =
+          new ObjectInputStream(new FileInputStream(databaseFile));
+      uniquePrograms = (Map<String, Integer>) objectStream.readObject();
+      uniqueOutputs = (Map<String, Integer>) objectStream.readObject();
+      objectStream.close();
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    } catch (ClassNotFoundException e) {
+      e.printStackTrace();
+    }
+
+  }
+
+  private void saveUniqueProgsData() {
+    // Since we could potentially stop the program while writing out this DB,
+    // copy the old file beforehand, and then delete it if we successfully wrote out the DB.
+    boolean oldWasSaved = false;
+    File file = new File(databaseFile);
+    if (file.exists()) {
+      try {
+        Process process =
+            Runtime.getRuntime().exec(String.format("cp %1$s %1$s.old", databaseFile));
+        // Shouldn't block, cp shouldn't produce output.
+        process.waitFor();
+        oldWasSaved = true;
+      } catch (IOException exception) {
+        exception.printStackTrace();
+      } catch (InterruptedException exception) {
+        exception.printStackTrace();
+      }
+    }
+
+    // Now write out the DB.
+    boolean success = false;
+    try {
+      ObjectOutputStream objectStream =
+          new ObjectOutputStream(new FileOutputStream(databaseFile));
+      objectStream.writeObject(uniquePrograms);
+      objectStream.writeObject(uniqueOutputs);
+      objectStream.close();
+      success = true;
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    // If we get here, and we successfully wrote out the DB, delete the saved one.
+    if (oldWasSaved && success) {
+      try {
+        Process process =
+            Runtime.getRuntime().exec(String.format("rm %s.old", databaseFile));
+        // Shouldn't block, rm shouldn't produce output.
+        process.waitFor();
+      } catch (IOException exception) {
+        exception.printStackTrace();
+      } catch (InterruptedException exception) {
+        exception.printStackTrace();
+      }
+    } else if (oldWasSaved && !success) {
+      Log.error("Failed to successfully write out the unique programs DB!");
+      Log.error("Old DB should be saved in " + databaseFile + ".old");
+    }
+  }
+
+  private void addToMap(String md5sum, Map<String, Integer> map) {
+    if (map.containsKey(md5sum)) {
+      map.put(md5sum, map.get(md5sum) + 1);
+    } else {
+      map.put(md5sum, 1);
+    }
+  }
+
+  private void saveDivergentProgram() {
+    File before = new File(fuzzedFile);
+    File after = new File(String.format("divergent_programs/%d.dex", currentSeed));
+    boolean success = before.renameTo(after);
+    if (!success) {
+      Log.error("Failed to save divergent program! Does divergent_programs/ exist?");
+    }
+  }
+
+  @Override
+  public void setup() {
+    try {
+      digest = MessageDigest.getInstance("MD5");
+      loadUniqueProgsData();
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    if ((iteration % saveDatabasePeriod) == (saveDatabasePeriod - 1)) {
+      saveUniqueProgsData();
+    }
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    String md5sum = getMD5SumOfProgram(programName);
+    addToMap(md5sum, uniquePrograms);
+
+    fuzzedFile = programName;
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    // Just use the first one.
+    String output = (String) outputMap.keySet().toArray()[0];
+    String md5sum = getMD5SumOfOutput(output);
+    addToMap(md5sum, uniqueOutputs);
+
+    saveDivergentProgram();
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    // There's only one, use it.
+    String output = (String) outputMap.keySet().toArray()[0];
+    String md5sum = getMD5SumOfOutput(output);
+    addToMap(md5sum, uniqueOutputs);
+  }
+
+  @Override
+  public void handleSummary() {
+    if (Options.reportUnique) {
+      Log.always("-- UNIQUE PROGRAM REPORT --");
+      Log.always("Unique Programs Seen: " + uniquePrograms.size());
+      Log.always("Unique Outputs Seen: " + uniqueOutputs.size());
+      Log.always("---------------------------");
+    }
+
+    saveUniqueProgsData();
+  }
+
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java b/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java
new file mode 100644
index 0000000..39d1d2f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements the live updating table of results when --repeat is being used.
+ */
+public class UpdatingConsoleListener extends BaseListener {
+  long successfulVerification;
+  long failedVerification;
+  long failedMutation;
+  long success;
+  long timedOut;
+  long divergence;
+  long selfDivergent;
+  long architectureSplit;
+  long iterations;
+
+  @Override
+  public void setup() {
+    System.out.println("|-----------------------------------------------------------------|");
+    System.out.println("|Iterations|VerifyFail|MutateFail|Timed Out |Successful|Divergence|");
+    System.out.println("|-----------------------------------------------------------------|");
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    successfulVerification++;
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    failedVerification++;
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    failedVerification++;
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    iterations++;
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    String output = String.format("| %-9d| %-9d| %-9d| %-9d| %-9d| %-9d|",
+        iterations, failedVerification, failedMutation, timedOut, success,
+        divergence - (selfDivergent + architectureSplit));
+    System.out.print("\r" + output);
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    this.timedOut++;
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    divergence++;
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    selfDivergent++;
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    architectureSplit++;
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    success++;
+  }
+
+  @Override
+  public void handleMutationFail() {
+    failedMutation++;
+  }
+
+  @Override
+  public void handleSummary() {
+    System.out.println("\n|-----------------------------------------------------------------|");
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
new file mode 100644
index 0000000..650501b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
@@ -0,0 +1,592 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.CodeItem;
+import dexfuzz.rawdex.EncodedCatchHandler;
+import dexfuzz.rawdex.EncodedTypeAddrPair;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.TryItem;
+import dexfuzz.rawdex.formats.ContainsTarget;
+import dexfuzz.rawdex.formats.RawInsnHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates from a CodeItem (the raw list of Instructions) to MutatableCode
+ * (graph of Instructions, using MInsns and subclasses) and vice-versa.
+ */
+public class CodeTranslator {
+
+  /**
+   * Given a raw DEX file's CodeItem, produce a MutatableCode object, that CodeMutators
+   * are designed to operate on.
+   * @param codeItemIdx Used to make sure the correct CodeItem is updated later after mutation.
+   * @return A new MutatableCode object, which contains all relevant information
+   *         obtained from the CodeItem.
+   */
+  public MutatableCode codeItemToMutatableCode(Program program, CodeItem codeItem,
+      int codeItemIdx, int mutatableCodeIdx) {
+    Log.debug("Translating CodeItem " + codeItemIdx
+        + " (" + codeItem.meta.methodName + ") to MutatableCode");
+
+    MutatableCode mutatableCode = new MutatableCode(program);
+
+    codeItem.registerMutatableCode(mutatableCode);
+
+    mutatableCode.name = codeItem.meta.methodName;
+    mutatableCode.shorty = codeItem.meta.shorty;
+    mutatableCode.isStatic = codeItem.meta.isStatic;
+
+    mutatableCode.codeItemIdx = codeItemIdx;
+
+    mutatableCode.mutatableCodeIdx = mutatableCodeIdx;
+
+    mutatableCode.registersSize = codeItem.registersSize;
+    mutatableCode.insSize = codeItem.insSize;
+    mutatableCode.outsSize = codeItem.outsSize;
+    mutatableCode.triesSize = codeItem.triesSize;
+
+    // Temporary map from bytecode offset -> instruction.
+    Map<Integer,MInsn> insnLocationMap = new HashMap<Integer,MInsn>();
+
+    List<Instruction> inputInsns = codeItem.insns;
+
+    // Create the MInsns.
+    int loc = 0;
+    for (Instruction insn : inputInsns) {
+      MInsn mInsn = null;
+
+      if (isInstructionSwitch(insn)) {
+        mInsn = new MSwitchInsn();
+      } else if (isInstructionBranch(insn)) {
+        mInsn = new MBranchInsn();
+      } else if (isInstructionFillArrayData(insn)) {
+        mInsn = new MInsnWithData();
+      } else {
+        mInsn = new MInsn();
+      }
+
+      mInsn.insn = insn;
+
+      // Populate the temporary map.
+      insnLocationMap.put(loc, mInsn);
+
+      // Populate the proper list of mutatable instructions.
+      mutatableCode.addInstructionToEnd(mInsn);
+
+      // Calculate the offsets for each instruction.
+      mInsn.location = loc;
+      mInsn.locationUpdated = false;
+
+      loc += mInsn.insn.getSize();
+    }
+
+    // Now make branch/switch instructions point at the right target instructions.
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        readSwitchInstruction((MSwitchInsn) mInsn, insnLocationMap);
+      } else if (mInsn instanceof MInsnWithData) {
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn);
+        ((MInsnWithData)mInsn).dataTarget = insnLocationMap.get(targetLoc);
+        if (((MInsnWithData)mInsn).dataTarget == null) {
+          Log.errorAndQuit("Bad offset calculation in data-target insn");
+        }
+      } else if (mInsn instanceof MBranchInsn) {
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn);
+        ((MBranchInsn)mInsn).target = insnLocationMap.get(targetLoc);
+        if (((MBranchInsn)mInsn).target == null) {
+          Log.errorAndQuit("Bad offset calculation in branch insn");
+        }
+      }
+    }
+
+    // Now create try blocks.
+    if (mutatableCode.triesSize > 0) {
+      readTryBlocks(codeItem, mutatableCode, insnLocationMap);
+    }
+
+    return mutatableCode;
+  }
+
+  /**
+   * Given a MutatableCode item that may have been mutated, update the original CodeItem
+   * correctly, to allow valid DEX to be written back to the output file.
+   */
+  public void mutatableCodeToCodeItem(CodeItem codeItem, MutatableCode mutatableCode) {
+    Log.debug("Translating MutatableCode " + mutatableCode.name
+        + " to CodeItem " + mutatableCode.codeItemIdx);
+
+    // We must first align any data instructions at the end of the code
+    // before we recalculate any offsets.
+    // This also updates their sizes...
+    alignDataInstructions(mutatableCode);
+
+    // Validate that the tracked locations for instructions are valid.
+    // Also mark locations as no longer being updated.
+    int loc = 0;
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.justRaw) {
+        // All just_raw instructions need alignment!
+        if ((loc % 2) != 0) {
+          loc++;
+        }
+      }
+      if (mInsn.location != loc) {
+        Log.errorAndQuit(String.format("%s does not have expected location 0x%x",
+            mInsn, loc));
+      }
+      mInsn.locationUpdated = false;
+      loc += mInsn.insn.getSize();
+    }
+
+    // This new list will be attached to the CodeItem at the end...
+    List<Instruction> outputInsns = new LinkedList<Instruction>();
+
+    // Go through our new list of MInsns, adding them to the new
+    // list of instructions that will be attached to the CodeItem.
+    // Also recalculate offsets for branches.
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        updateSwitchInstruction((MSwitchInsn)mInsn, mutatableCode);
+      } else if (mInsn instanceof MInsnWithData) {
+        MInsn target = ((MInsnWithData) mInsn).dataTarget;
+        int dataOffset = target.location - mInsn.location;
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        containsTarget.setTarget(mInsn.insn, dataOffset);
+      } else if (mInsn instanceof MBranchInsn) {
+        MInsn target = ((MBranchInsn) mInsn).target;
+        int branchOffset = target.location - mInsn.location;
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        containsTarget.setTarget(mInsn.insn, branchOffset);
+      }
+      outputInsns.add(mInsn.insn);
+    }
+
+    // Calculate the new insns_size.
+    int newInsnsSize = 0;
+    for (Instruction insn : outputInsns) {
+      newInsnsSize += insn.getSize();
+    }
+
+    if (mutatableCode.triesSize > 0) {
+      updateTryBlocks(codeItem, mutatableCode);
+    }
+
+    codeItem.insnsSize = newInsnsSize;
+    codeItem.insns = outputInsns;
+    codeItem.registersSize = mutatableCode.registersSize;
+    codeItem.insSize = mutatableCode.insSize;
+    codeItem.outsSize = mutatableCode.outsSize;
+    codeItem.triesSize = mutatableCode.triesSize;
+  }
+
+  /**
+   * The TryItem specifies an offset into the EncodedCatchHandlerList for a given CodeItem,
+   * but we only have an array of the EncodedCatchHandlers that the List contains.
+   * This function produces a map that offers a way to find out the index into our array,
+   * from the try handler's offset.
+   */
+  private Map<Short,Integer> createTryHandlerOffsetToIndexMap(CodeItem codeItem) {
+    // Create a sorted set of offsets.
+    List<Short> uniqueOffsets = new ArrayList<Short>();
+    for (TryItem tryItem : codeItem.tries) {
+      int index = 0;
+      while (true) {
+        if ((index == uniqueOffsets.size())
+            || (uniqueOffsets.get(index) > tryItem.handlerOff)) {
+          // First condition means we're at the end of the set (or we're inserting
+          //   into an empty set)
+          // Second condition means that the offset belongs here
+          // ...so insert it here, pushing the element currently in this position to the
+          //    right, if it exists
+          uniqueOffsets.add(index, tryItem.handlerOff);
+          break;
+        } else if (uniqueOffsets.get(index) == tryItem.handlerOff) {
+          // We've already seen it, and we're making a set, not a list.
+          break;
+        } else {
+          // Keep searching.
+          index++;
+        }
+      }
+    }
+    // Now we have an (implicit) index -> offset mapping!
+
+    // Now create the reverse mapping.
+    Map<Short,Integer> offsetIndexMap = new HashMap<Short,Integer>();
+    for (int i = 0; i < uniqueOffsets.size(); i++) {
+      offsetIndexMap.put(uniqueOffsets.get(i), i);
+    }
+
+    return offsetIndexMap;
+  }
+
+  private void readTryBlocks(CodeItem codeItem, MutatableCode mutatableCode,
+      Map<Integer,MInsn> insnLocationMap) {
+    mutatableCode.mutatableTries = new LinkedList<MTryBlock>();
+
+    Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
+
+    // Read each TryItem into a MutatableTryBlock.
+    for (TryItem tryItem : codeItem.tries) {
+      MTryBlock mTryBlock = new MTryBlock();
+
+      // Get the MInsns that form the start and end of the try block.
+      int startLocation = tryItem.startAddr;
+      mTryBlock.startInsn = insnLocationMap.get(startLocation);
+      int endLocation = tryItem.startAddr + tryItem.insnCount;
+      mTryBlock.endInsn = insnLocationMap.get(endLocation);
+
+      // Sanity checks.
+      if (mTryBlock.startInsn == null) {
+        Log.errorAndQuit(String.format(
+            "Couldn't find a mutatable insn at start offset 0x%x",
+            startLocation));
+      }
+      if (mTryBlock.endInsn == null) {
+        Log.errorAndQuit(String.format(
+            "Couldn't find a mutatable insn at end offset 0x%x",
+            endLocation));
+      }
+
+      // Get the EncodedCatchHandler.
+      int handlerIdx = offsetIndexMap.get(tryItem.handlerOff);
+      EncodedCatchHandler encodedCatchHandler = codeItem.handlers.list[handlerIdx];
+
+      // Do we have a catch all? If so, associate the MInsn that's there.
+      if (encodedCatchHandler.size <= 0) {
+        mTryBlock.catchAllHandler =
+            insnLocationMap.get(encodedCatchHandler.catchAllAddr);
+        // Sanity check.
+        if (mTryBlock.catchAllHandler == null) {
+          Log.errorAndQuit(
+              String.format("Couldn't find a mutatable insn at catch-all offset 0x%x",
+                  encodedCatchHandler.catchAllAddr));
+        }
+      }
+      // Do we have explicitly-typed handlers? This will remain empty if not.
+      mTryBlock.handlers = new LinkedList<MInsn>();
+
+      // Associate all the explicitly-typed handlers.
+      for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
+        EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
+        MInsn handlerInsn = insnLocationMap.get(handler.addr);
+        // Sanity check.
+        if (handlerInsn == null) {
+          Log.errorAndQuit(String.format(
+              "Couldn't find a mutatable instruction at handler offset 0x%x",
+              handler.addr));
+        }
+        mTryBlock.handlers.add(handlerInsn);
+      }
+
+      // Now finally add the new MutatableTryBlock into this MutatableCode's list!
+      mutatableCode.mutatableTries.add(mTryBlock);
+    }
+  }
+
+  private void updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode) {
+
+    // TODO: Support ability to add extra try blocks/handlers?
+
+    for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
+      if (mTryBlock.startInsn.location > mTryBlock.endInsn.location) {
+        // Mutation has put this try block's end insn before its start insn. Fix this.
+        MInsn tempInsn = mTryBlock.startInsn;
+        mTryBlock.startInsn = mTryBlock.endInsn;
+        mTryBlock.endInsn = tempInsn;
+      }
+    }
+
+    // First, manipulate the try blocks if they overlap.
+    for (int i = 0; i < mutatableCode.mutatableTries.size() - 1; i++) {
+      MTryBlock first = mutatableCode.mutatableTries.get(i);
+      MTryBlock second = mutatableCode.mutatableTries.get(i + 1);
+
+      // Do they overlap?
+      if (first.endInsn.location > second.startInsn.location) {
+
+        Log.debug("Found overlap in TryBlocks, moving 2nd TryBlock...");
+        Log.debug("1st TryBlock goes from " + first.startInsn + " to "  + first.endInsn);
+        Log.debug("2nd TryBlock goes from " + second.startInsn + " to "  + second.endInsn);
+
+        // Find the first instruction that comes after that does not overlap
+        // with the first try block.
+        MInsn newInsn = second.startInsn;
+        int ptr = mutatableCode.getInstructionIndex(newInsn);
+        while (first.endInsn.location > newInsn.location) {
+          ptr++;
+          newInsn = mutatableCode.getInstructionAt(ptr);
+        }
+        second.startInsn = newInsn;
+
+        Log.debug("Now 2nd TryBlock goes from " + second.startInsn + " to "  + second.endInsn);
+      }
+    }
+
+    Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
+
+    int tryItemIdx = 0;
+    for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
+      TryItem tryItem = codeItem.tries[tryItemIdx];
+
+      tryItem.startAddr = mTryBlock.startInsn.location;
+      tryItem.insnCount =
+          (short) (mTryBlock.endInsn.location - mTryBlock.startInsn.location);
+
+      // Get the EncodedCatchHandler.
+      EncodedCatchHandler encodedCatchHandler =
+          codeItem.handlers.list[offsetIndexMap.get(tryItem.handlerOff)];
+
+      if (encodedCatchHandler.size <= 0) {
+        encodedCatchHandler.catchAllAddr = mTryBlock.catchAllHandler.location;
+      }
+      for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
+        MInsn handlerInsn = mTryBlock.handlers.get(i);
+        EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
+        handler.addr = handlerInsn.location;
+      }
+      tryItemIdx++;
+    }
+  }
+
+  /**
+   * Given a switch instruction, find the associated data's raw[] form, and update
+   * the targets of the switch instruction to point to the correct instructions.
+   */
+  private void readSwitchInstruction(MSwitchInsn switchInsn,
+      Map<Integer,MInsn> insnLocationMap) {
+    // Find the data.
+    ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
+    int dataLocation = switchInsn.location + (int) containsTarget.getTarget(switchInsn.insn);
+    switchInsn.dataTarget = insnLocationMap.get(dataLocation);
+    if (switchInsn.dataTarget == null) {
+      Log.errorAndQuit("Bad offset calculation for data target in switch insn");
+    }
+
+    // Now read the data.
+    Instruction dataInsn = switchInsn.dataTarget.insn;
+
+    int rawPtr = 2;
+
+    int targetsSize = (int) RawInsnHelper.getUnsignedShortFromTwoBytes(dataInsn.rawBytes, rawPtr);
+    rawPtr += 2;
+
+    int[] keys = new int[targetsSize];
+    int[] targets = new int[targetsSize];
+
+    if (dataInsn.rawType == 1) {
+      switchInsn.packed = true;
+      // Dealing with a packed-switch.
+      // Read the first key.
+      keys[0] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, rawPtr);
+      rawPtr += 4;
+      // Calculate the rest of the keys.
+      for (int i = 1; i < targetsSize; i++) {
+        keys[i] = keys[i - 1] + 1;
+      }
+    } else if (dataInsn.rawType == 2) {
+      // Dealing with a sparse-switch.
+      // Read all of the keys.
+      for (int i = 0; i < targetsSize; i++) {
+        keys[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
+            rawPtr);
+        rawPtr += 4;
+      }
+    }
+
+    // Now read the targets.
+    for (int i = 0; i < targetsSize; i++) {
+      targets[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
+          rawPtr);
+      rawPtr += 4;
+    }
+
+    // Store the keys.
+    switchInsn.keys = keys;
+
+    // Convert our targets[] offsets into pointers to MInsns.
+    for (int target : targets) {
+      int targetLocation = switchInsn.location + target;
+      MInsn targetInsn = insnLocationMap.get(targetLocation);
+      switchInsn.targets.add(targetInsn);
+      if (targetInsn == null) {
+        Log.errorAndQuit("Bad offset calculation for target in switch insn");
+      }
+    }
+  }
+
+  /**
+   * Given a mutatable switch instruction, which may have had some of its branch
+   * targets moved, update all the target offsets in the raw[] form of the instruction.
+   */
+  private void updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode) {
+    // Update the offset to the data instruction
+    MInsn dataTarget = switchInsn.dataTarget;
+    int dataOffset = dataTarget.location - switchInsn.location;
+    ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
+    containsTarget.setTarget(switchInsn.insn, dataOffset);
+
+    int targetsSize = switchInsn.targets.size();
+
+    int[] keys = switchInsn.keys;
+    int[] targets = new int[targetsSize];
+
+    // Calculate the new offsets.
+    int targetIdx = 0;
+    for (MInsn target : switchInsn.targets) {
+      targets[targetIdx] = target.location - switchInsn.location;
+      targetIdx++;
+    }
+
+    // Now write the data back to the raw bytes.
+    Instruction dataInsn = switchInsn.dataTarget.insn;
+
+    int rawPtr = 2;
+
+    // Write out the size.
+    RawInsnHelper.writeUnsignedShortToTwoBytes(dataInsn.rawBytes, rawPtr, targetsSize);
+    rawPtr += 2;
+
+    // Write out the keys.
+    if (switchInsn.packed) {
+      // Only write out one key - the first.
+      RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[0]);
+      rawPtr += 4;
+    } else {
+      // Write out all the keys.
+      for (int i = 0; i < targetsSize; i++) {
+        RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[i]);
+        rawPtr += 4;
+      }
+    }
+
+    // Write out all the targets.
+    for (int i = 0; i < targetsSize; i++) {
+      RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, targets[i]);
+      rawPtr += 4;
+    }
+  }
+
+  /**
+   * After mutation, data instructions may no longer be 4-byte aligned.
+   * If this is the case, insert nops to align them all.
+   * This makes a number of assumptions about data currently:
+   * - data is always at the end of method insns
+   * - all data instructions are stored contiguously
+   */
+  private void alignDataInstructions(MutatableCode mutatableCode) {
+    // Find all the switch data instructions.
+    List<MInsn> dataInsns = new ArrayList<MInsn>();
+
+    // Update raw sizes of the data instructions as well.
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        // Update the raw size of the instruction.
+        MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
+        int targetsSize = switchInsn.targets.size();
+        Instruction dataInsn = switchInsn.dataTarget.insn;
+        if (switchInsn.packed) {
+          dataInsn.rawSize = (targetsSize * 2) + 4;
+        } else {
+          dataInsn.rawSize = (targetsSize * 4) + 2;
+        }
+        dataInsns.add(switchInsn.dataTarget);
+      } else if (mInsn instanceof MInsnWithData) {
+        MInsnWithData insnWithData =
+            (MInsnWithData) mInsn;
+        dataInsns.add(insnWithData.dataTarget);
+      }
+    }
+
+    // Only need to align switch data instructions if there are any!
+    if (!dataInsns.isEmpty()) {
+
+      Log.debug("Found data instructions, checking alignment...");
+
+      // Sort data_insns by location.
+      Collections.sort(dataInsns, new Comparator<MInsn>() {
+        @Override
+        public int compare(MInsn first, MInsn second) {
+          if (first.location < second.location) {
+            return -1;
+          } else if (first.location > second.location) {
+            return 1;
+          }
+          return 0;
+        }
+      });
+
+      boolean performedAlignment = false;
+
+      // Go through all the data insns, and insert an alignment nop if they're unaligned.
+      for (MInsn dataInsn : dataInsns) {
+        if (dataInsn.location % 2 != 0) {
+          Log.debug("Aligning data instruction with a nop.");
+          int alignmentNopIdx = mutatableCode.getInstructionIndex(dataInsn);
+          MInsn nop = new MInsn();
+          nop.insn = new Instruction();
+          nop.insn.info = Instruction.getOpcodeInfo(Opcode.NOP);
+          mutatableCode.insertInstructionAt(nop, alignmentNopIdx);
+          performedAlignment = true;
+        }
+      }
+
+      if (!performedAlignment) {
+        Log.debug("Alignment okay.");
+      }
+    }
+  }
+
+  /**
+   * Determine if a particular instruction is a branch instruction, based on opcode.
+   */
+  private boolean isInstructionBranch(Instruction insn) {
+    Opcode opcode = insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+        || Opcode.isBetween(opcode, Opcode.GOTO, Opcode.GOTO_32)) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Determine if a particular instruction is a switch instruction, based on opcode.
+   */
+  private boolean isInstructionSwitch(Instruction insn) {
+    Opcode opcode = insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)) {
+      return true;
+    }
+    return false;
+  }
+
+  private boolean isInstructionFillArrayData(Instruction insn) {
+    return (insn.info.opcode == Opcode.FILL_ARRAY_DATA);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/IdCreator.java b/tools/dexfuzz/src/dexfuzz/program/IdCreator.java
new file mode 100644
index 0000000..c506fa6
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/IdCreator.java
@@ -0,0 +1,804 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.FieldIdItem;
+import dexfuzz.rawdex.MethodIdItem;
+import dexfuzz.rawdex.Offset;
+import dexfuzz.rawdex.Offsettable;
+import dexfuzz.rawdex.ProtoIdItem;
+import dexfuzz.rawdex.RawDexFile;
+import dexfuzz.rawdex.RawDexObject.IndexUpdateKind;
+import dexfuzz.rawdex.StringDataItem;
+import dexfuzz.rawdex.StringIdItem;
+import dexfuzz.rawdex.TypeIdItem;
+import dexfuzz.rawdex.TypeItem;
+import dexfuzz.rawdex.TypeList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for the finding and creation of TypeIds, MethodIds, FieldIds, and StringIds,
+ * during mutation.
+ */
+public class IdCreator {
+  private RawDexFile rawDexFile;
+
+  public IdCreator(RawDexFile rawDexFile) {
+    this.rawDexFile = rawDexFile;
+  }
+
+  private int findProtoIdInsertionPoint(String signature) {
+    int returnTypeIdx = findTypeId(convertSignatureToReturnType(signature));
+    String[] parameterListStrings = convertSignatureToParameterList(signature);
+    TypeList parameterList = null;
+    if (parameterListStrings.length > 0) {
+      parameterList = findTypeList(parameterListStrings);
+    }
+
+    if (returnTypeIdx < 0) {
+      Log.errorAndQuit("Did not create necessary return type before finding insertion "
+          + "point for new proto!");
+    }
+
+    if (parameterListStrings.length > 0 && parameterList == null) {
+      Log.errorAndQuit("Did not create necessary parameter list before finding insertion "
+          + "point for new proto!");
+    }
+
+    int protoIdIdx = 0;
+    for (ProtoIdItem protoId : rawDexFile.protoIds) {
+      if (returnTypeIdx < protoId.returnTypeIdx) {
+        break;
+      }
+      if (returnTypeIdx == protoId.returnTypeIdx
+          && parameterListStrings.length == 0) {
+        break;
+      }
+      if (returnTypeIdx == protoId.returnTypeIdx
+          && parameterListStrings.length > 0
+          && protoId.parametersOff.pointsToSomething()
+          && parameterList.comesBefore(
+              (TypeList) protoId.parametersOff.getPointedToItem())) {
+        break;
+      }
+      protoIdIdx++;
+    }
+    return protoIdIdx;
+  }
+
+  private int findMethodIdInsertionPoint(String className, String methodName, String signature) {
+    int classIdx = findTypeId(className);
+    int nameIdx = findString(methodName);
+    int protoIdx = findProtoId(signature);
+
+    if (classIdx < 0 || nameIdx < 0 || protoIdx < 0) {
+      Log.errorAndQuit("Did not create necessary class, name or proto strings before finding "
+          + " insertion point for new method!");
+    }
+
+    int methodIdIdx = 0;
+    for (MethodIdItem methodId : rawDexFile.methodIds) {
+      if (classIdx < methodId.classIdx) {
+        break;
+      }
+      if (classIdx == methodId.classIdx && nameIdx < methodId.nameIdx) {
+        break;
+      }
+      if (classIdx == methodId.classIdx && nameIdx == methodId.nameIdx
+          && protoIdx < methodId.protoIdx) {
+        break;
+      }
+      methodIdIdx++;
+    }
+    return methodIdIdx;
+  }
+
+  private int findTypeIdInsertionPoint(String className) {
+    int descriptorIdx = findString(className);
+
+    if (descriptorIdx < 0) {
+      Log.errorAndQuit("Did not create necessary descriptor string before finding "
+          + " insertion point for new type!");
+    }
+
+    int typeIdIdx = 0;
+    for (TypeIdItem typeId : rawDexFile.typeIds) {
+      if (descriptorIdx < typeId.descriptorIdx) {
+        break;
+      }
+      typeIdIdx++;
+    }
+    return typeIdIdx;
+  }
+
+  private int findStringDataInsertionPoint(String string) {
+    int stringDataIdx = 0;
+    for (StringDataItem stringData : rawDexFile.stringDatas) {
+      if (stringData.getSize() > 0 && stringData.getString().compareTo(string) >= 0) {
+        break;
+      }
+      stringDataIdx++;
+    }
+    return stringDataIdx;
+  }
+
+  private int findFieldIdInsertionPoint(String className, String typeName, String fieldName) {
+    int classIdx = findTypeId(className);
+    int typeIdx = findTypeId(typeName);
+    int nameIdx = findString(fieldName);
+
+    if (classIdx < 0 || typeIdx < 0 || nameIdx < 0) {
+      Log.errorAndQuit("Did not create necessary class, type or name strings before finding "
+          + " insertion point for new field!");
+    }
+
+    int fieldIdIdx = 0;
+    for (FieldIdItem fieldId : rawDexFile.fieldIds) {
+      if (classIdx < fieldId.classIdx) {
+        break;
+      }
+      if (classIdx == fieldId.classIdx && nameIdx < fieldId.nameIdx) {
+        break;
+      }
+      if (classIdx == fieldId.classIdx && nameIdx == fieldId.nameIdx
+          && typeIdx < fieldId.typeIdx) {
+        break;
+      }
+      fieldIdIdx++;
+    }
+    return fieldIdIdx;
+  }
+
+  private int createMethodId(String className, String methodName, String signature) {
+    if (rawDexFile.methodIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many methods for the DEX file.");
+    }
+
+    // Search for (or create) the prototype.
+    int protoIdx = findOrCreateProtoId(signature);
+
+    // Search for (or create) the owning class.
+    // NB: findOrCreateProtoId could create new types, so this must come
+    //     after it!
+    int typeIdIdx = findOrCreateTypeId(className);
+
+    // Search for (or create) the string representing the method name.
+    // NB: findOrCreateProtoId/TypeId could create new strings, so this must come
+    //     after them!
+    int methodNameStringIdx = findOrCreateString(methodName);
+
+    // Create MethodIdItem.
+    MethodIdItem newMethodId = new MethodIdItem();
+    newMethodId.classIdx = (short) typeIdIdx;
+    newMethodId.protoIdx = (short) protoIdx;
+    newMethodId.nameIdx = methodNameStringIdx;
+
+    // MethodIds must be ordered.
+    int newMethodIdIdx = findMethodIdInsertionPoint(className, methodName, signature);
+
+    rawDexFile.methodIds.add(newMethodIdIdx, newMethodId);
+
+    // Insert into OffsetTracker.
+    if (newMethodIdIdx == 0) {
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstOfType(newMethodId, rawDexFile);
+    } else {
+      MethodIdItem prevMethodId = rawDexFile.methodIds.get(newMethodIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newMethodId, prevMethodId);
+    }
+
+    Log.info(String.format("Created new MethodIdItem for %s %s %s, index: 0x%04x",
+        className, methodName, signature, newMethodIdIdx));
+
+    // Now that we've potentially moved a lot of method IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.METHOD_ID, newMethodIdIdx);
+
+    // All done, return the index for the new method.
+    return newMethodIdIdx;
+  }
+
+  private int findMethodId(String className, String methodName, String signature) {
+    int classIdx = findTypeId(className);
+    if (classIdx == -1) {
+      return -1;
+    }
+    int nameIdx = findString(methodName);
+    if (nameIdx == -1) {
+      return -1;
+    }
+    int protoIdx = findProtoId(signature);
+    if (nameIdx == -1) {
+      return -1;
+    }
+
+    int methodIdIdx = 0;
+    for (MethodIdItem methodId : rawDexFile.methodIds) {
+      if (classIdx == methodId.classIdx
+          && nameIdx == methodId.nameIdx
+          && protoIdx == methodId.protoIdx) {
+        return methodIdIdx;
+      }
+      methodIdIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a fully qualified class name (Ljava/lang/System;), method name (gc) and
+   * and signature (()V), either find the MethodId in our DEX file's table, or create it.
+   */
+  public int findOrCreateMethodId(String className, String methodName, String shorty) {
+    int methodIdIdx = findMethodId(className, methodName, shorty);
+    if (methodIdIdx != -1) {
+      return methodIdIdx;
+    }
+    return createMethodId(className, methodName, shorty);
+  }
+
+  private int createTypeId(String className) {
+    if (rawDexFile.typeIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many classes for the DEX file.");
+    }
+
+    // Search for (or create) the string representing the class descriptor.
+    int descriptorStringIdx = findOrCreateString(className);
+
+    // Create TypeIdItem.
+    TypeIdItem newTypeId = new TypeIdItem();
+    newTypeId.descriptorIdx = descriptorStringIdx;
+
+    // TypeIds must be ordered.
+    int newTypeIdIdx = findTypeIdInsertionPoint(className);
+
+    rawDexFile.typeIds.add(newTypeIdIdx, newTypeId);
+
+    // Insert into OffsetTracker.
+    if (newTypeIdIdx == 0) {
+      rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newTypeId, rawDexFile);
+    } else {
+      TypeIdItem prevTypeId = rawDexFile.typeIds.get(newTypeIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newTypeId, prevTypeId);
+    }
+
+    Log.info(String.format("Created new ClassIdItem for %s, index: 0x%04x",
+        className, newTypeIdIdx));
+
+    // Now that we've potentially moved a lot of type IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.TYPE_ID, newTypeIdIdx);
+
+    // All done, return the index for the new class.
+    return newTypeIdIdx;
+  }
+
+  private int findTypeId(String className) {
+    int descriptorIdx = findString(className);
+    if (descriptorIdx == -1) {
+      return -1;
+    }
+
+    // Search for class.
+    int typeIdIdx = 0;
+    for (TypeIdItem typeId : rawDexFile.typeIds) {
+      if (descriptorIdx == typeId.descriptorIdx) {
+        return typeIdIdx;
+      }
+      typeIdIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a fully qualified class name (Ljava/lang/System;)
+   * either find the TypeId in our DEX file's table, or create it.
+   */
+  public int findOrCreateTypeId(String className) {
+    int typeIdIdx = findTypeId(className);
+    if (typeIdIdx != -1) {
+      return typeIdIdx;
+    }
+    return createTypeId(className);
+  }
+
+  private int createString(String string) {
+    // Didn't find it, create one...
+    int stringsCount = rawDexFile.stringIds.size();
+    if (stringsCount != rawDexFile.stringDatas.size()) {
+      Log.errorAndQuit("Corrupted DEX file, len(StringIDs) != len(StringDatas)");
+    }
+
+    // StringData must be ordered.
+    int newStringIdx = findStringDataInsertionPoint(string);
+
+    // Create StringDataItem.
+    StringDataItem newStringData = new StringDataItem();
+    newStringData.setSize(string.length());
+    newStringData.setString(string);
+
+    rawDexFile.stringDatas.add(newStringIdx, newStringData);
+
+    // Insert into OffsetTracker.
+    // (Need to save the Offsettable, because the StringIdItem will point to it.)
+    Offsettable offsettableStringData = null;
+    if (newStringIdx == 0) {
+      offsettableStringData =
+          rawDexFile.getOffsetTracker()
+          .insertNewOffsettableAsFirstOfType(newStringData, rawDexFile);
+    } else {
+      StringDataItem prevStringData = rawDexFile.stringDatas.get(newStringIdx - 1);
+      offsettableStringData = rawDexFile.getOffsetTracker()
+          .insertNewOffsettableAfter(newStringData, prevStringData);
+    }
+
+    // Create StringIdItem.
+    StringIdItem newStringId = new StringIdItem();
+    newStringId.stringDataOff = new Offset(false);
+    newStringId.stringDataOff.pointToNew(offsettableStringData);
+
+    rawDexFile.stringIds.add(newStringIdx, newStringId);
+
+    // Insert into OffsetTracker.
+    if (newStringIdx == 0) {
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstOfType(newStringId, rawDexFile);
+    } else {
+      StringIdItem prevStringId = rawDexFile.stringIds.get(newStringIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newStringId, prevStringId);
+    }
+
+
+    Log.info(String.format("Created new StringIdItem and StringDataItem for %s, index: 0x%04x",
+        string, newStringIdx));
+
+    // Now that we've potentially moved a lot of string IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.STRING_ID, newStringIdx);
+
+    // All done, return the index for the new string.
+    return newStringIdx;
+  }
+
+  private int findString(String string) {
+    // Search for string.
+    int stringIdx = 0;
+    for (StringDataItem stringDataItem : rawDexFile.stringDatas) {
+      if (stringDataItem.getSize() == 0 && string.isEmpty()) {
+        return stringIdx;
+      } else if (stringDataItem.getSize() > 0 && stringDataItem.getString().equals(string)) {
+        return stringIdx;
+      }
+      stringIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a string, either find the StringId in our DEX file's table, or create it.
+   */
+  public int findOrCreateString(String string) {
+    int stringIdx = findString(string);
+    if (stringIdx != -1) {
+      return stringIdx;
+    }
+    return createString(string);
+  }
+
+  private int createFieldId(String className, String typeName, String fieldName) {
+    if (rawDexFile.fieldIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many fields for the DEX file.");
+    }
+
+    // Search for (or create) the owning class.
+    int classIdx = findOrCreateTypeId(className);
+
+    // Search for (or create) the field's type.
+    int typeIdx = findOrCreateTypeId(typeName);
+
+    // The creation of the typeIdx may have changed the classIdx, search again!
+    classIdx = findOrCreateTypeId(className);
+
+    // Search for (or create) the string representing the field name.
+    int fieldNameStringIdx = findOrCreateString(fieldName);
+
+    // Create FieldIdItem.
+    FieldIdItem newFieldId = new FieldIdItem();
+    newFieldId.classIdx = (short) classIdx;
+    newFieldId.typeIdx = (short) typeIdx;
+    newFieldId.nameIdx = fieldNameStringIdx;
+
+    // FieldIds must be ordered.
+    int newFieldIdIdx = findFieldIdInsertionPoint(className, typeName, fieldName);
+
+    rawDexFile.fieldIds.add(newFieldIdIdx, newFieldId);
+
+    // Insert into OffsetTracker.
+    if (newFieldIdIdx == 0 && rawDexFile.fieldIds.size() == 1) {
+      // Special case: we didn't have any fields before!
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstEverField(newFieldId, rawDexFile);
+    } else if (newFieldIdIdx == 0) {
+      rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newFieldId, rawDexFile);
+    } else {
+      FieldIdItem prevFieldId = rawDexFile.fieldIds.get(newFieldIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newFieldId, prevFieldId);
+    }
+
+    Log.info(String.format("Created new FieldIdItem for %s %s %s, index: 0x%04x",
+        className, typeName, fieldName, newFieldIdIdx));
+
+    // Now that we've potentially moved a lot of field IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.FIELD_ID, newFieldIdIdx);
+
+    // All done, return the index for the new field.
+    return newFieldIdIdx;
+  }
+
+  private int findFieldId(String className, String typeName, String fieldName) {
+    int classIdx = findTypeId(className);
+    if (classIdx == -1) {
+      return -1;
+    }
+    int typeIdx = findTypeId(typeName);
+    if (typeIdx == -1) {
+      return -1;
+    }
+    int nameIdx = findString(fieldName);
+    if (nameIdx == -1) {
+      return -1;
+    }
+
+    int fieldIdIdx = 0;
+    for (FieldIdItem fieldId : rawDexFile.fieldIds) {
+      if (classIdx == fieldId.classIdx
+          && typeIdx == fieldId.typeIdx
+          && nameIdx == fieldId.nameIdx) {
+        return fieldIdIdx;
+      }
+      fieldIdIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a field's fully qualified class name, type name, and name,
+   * either find the FieldId in our DEX file's table, or create it.
+   */
+  public int findOrCreateFieldId(String className, String typeName, String fieldName) {
+    int fieldIdx = findFieldId(className, typeName, fieldName);
+    if (fieldIdx != -1) {
+      return fieldIdx;
+    }
+    return createFieldId(className, typeName, fieldName);
+  }
+
+  /**
+   * Returns a 1 or 2 element String[]. If 1 element, the only element is the return type
+   * part of the signature. If 2 elements, the first is the parameters, the second is
+   * the return type.
+   */
+  private String[] convertSignatureToParametersAndReturnType(String signature) {
+    if (signature.charAt(0) != '(' || !signature.contains(")")) {
+      Log.errorAndQuit("Invalid signature: " + signature);
+    }
+    String[] elems = signature.substring(1).split("\\)");
+    return elems;
+  }
+
+  private String[] convertSignatureToParameterList(String signature) {
+    String[] elems = convertSignatureToParametersAndReturnType(signature);
+    String parameters = "";
+    if (elems.length == 2) {
+      parameters = elems[0];
+    }
+
+    List<String> parameterList = new ArrayList<String>();
+
+    int typePointer = 0;
+    while (typePointer != parameters.length()) {
+      if (elems[0].charAt(typePointer) == 'L') {
+        int start = typePointer;
+        // Read up to the next ;
+        while (elems[0].charAt(typePointer) != ';') {
+          typePointer++;
+        }
+        parameterList.add(parameters.substring(start, typePointer + 1));
+      } else {
+        parameterList.add(Character.toString(parameters.charAt(typePointer)));
+      }
+      typePointer++;
+    }
+
+    return parameterList.toArray(new String[]{});
+  }
+
+  private String convertSignatureToReturnType(String signature) {
+    String[] elems = convertSignatureToParametersAndReturnType(signature);
+    String returnType = "";
+    if (elems.length == 1) {
+      returnType = elems[0];
+    } else {
+      returnType = elems[1];
+    }
+
+    return returnType;
+  }
+
+  private String convertSignatureToShorty(String signature) {
+    String[] elems = convertSignatureToParametersAndReturnType(signature);
+
+    StringBuilder shortyBuilder = new StringBuilder();
+
+    String parameters = "";
+    String returnType = "";
+
+    if (elems.length == 1) {
+      shortyBuilder.append("V");
+    } else {
+      parameters = elems[0];
+      returnType = elems[1];
+      char returnChar = returnType.charAt(0);
+      // Arrays are references in shorties.
+      if (returnChar == '[') {
+        returnChar = 'L';
+      }
+      shortyBuilder.append(returnChar);
+    }
+
+    int typePointer = 0;
+    while (typePointer != parameters.length()) {
+      if (parameters.charAt(typePointer) == 'L') {
+        shortyBuilder.append('L');
+        // Read up to the next ;
+        while (parameters.charAt(typePointer) != ';') {
+          typePointer++;
+          if (typePointer == parameters.length()) {
+            Log.errorAndQuit("Illegal type specified in signature - L with no ;!");
+          }
+        }
+      } else if (parameters.charAt(typePointer) == '[') {
+        // Arrays are references in shorties.
+        shortyBuilder.append('L');
+        // Read past all the [s
+        while (parameters.charAt(typePointer) == '[') {
+          typePointer++;
+        }
+        if (parameters.charAt(typePointer) == 'L') {
+          // Read up to the next ;
+          while (parameters.charAt(typePointer) != ';') {
+            typePointer++;
+            if (typePointer == parameters.length()) {
+              Log.errorAndQuit("Illegal type specified in signature - L with no ;!");
+            }
+          }
+        }
+      } else {
+        shortyBuilder.append(parameters.charAt(typePointer));
+      }
+
+      typePointer++;
+    }
+
+    return shortyBuilder.toString();
+  }
+
+  private Integer[] convertParameterListToTypeIdList(String[] parameterList) {
+    List<Integer> typeIdList = new ArrayList<Integer>();
+    for (String parameter : parameterList) {
+      int typeIdx = findTypeId(parameter);
+      if (typeIdx == -1) {
+        return null;
+      }
+      typeIdList.add(typeIdx);
+    }
+    return typeIdList.toArray(new Integer[]{});
+  }
+
+  private TypeList createTypeList(String[] parameterList) {
+    TypeList typeList = new TypeList();
+    List<TypeItem> typeItemList = new ArrayList<TypeItem>();
+
+    // This must be done as two passes, one to create all the types,
+    // and then one to put them in the type list.
+    for (String parameter : parameterList) {
+      findOrCreateTypeId(parameter);
+    }
+
+    // Now actually put them in the list.
+    for (String parameter : parameterList) {
+      TypeItem typeItem = new TypeItem();
+      typeItem.typeIdx = (short) findOrCreateTypeId(parameter);
+      typeItemList.add(typeItem);
+    }
+    typeList.list = typeItemList.toArray(new TypeItem[]{});
+    typeList.size = typeItemList.size();
+
+    // Insert into OffsetTracker.
+    if (rawDexFile.typeLists == null) {
+      // Special case: we didn't have any fields before!
+      Log.info("Need to create first type list.");
+      rawDexFile.typeLists = new ArrayList<TypeList>();
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstEverTypeList(typeList, rawDexFile);
+    } else {
+      TypeList prevTypeList =
+          rawDexFile.typeLists.get(rawDexFile.typeLists.size() - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(typeList, prevTypeList);
+    }
+
+    // Finally, add this new TypeList to the list of them.
+    rawDexFile.typeLists.add(typeList);
+
+    return typeList;
+  }
+
+  private TypeList findTypeList(String[] parameterList) {
+    Integer[] typeIdList = convertParameterListToTypeIdList(parameterList);
+    if (typeIdList == null) {
+      return null;
+    }
+
+    if (rawDexFile.typeLists == null) {
+      // There's no type lists yet!
+      return null;
+    }
+
+    for (TypeList typeList : rawDexFile.typeLists) {
+      if (typeList.size != typeIdList.length) {
+        continue;
+      }
+
+      boolean found = true;
+      int idx = 0;
+      for (TypeItem typeItem : typeList.list) {
+        if (typeItem.typeIdx != typeIdList[idx]) {
+          found = false;
+          break;
+        }
+        idx++;
+      }
+      if (found && idx == parameterList.length) {
+        return typeList;
+      }
+    }
+
+    return null;
+  }
+
+  private TypeList findOrCreateTypeList(String[] parameterList) {
+    TypeList typeList = findTypeList(parameterList);
+    if (typeList != null) {
+      return typeList;
+    }
+    return createTypeList(parameterList);
+  }
+
+  private int createProtoId(String signature) {
+    String shorty = convertSignatureToShorty(signature);
+    String returnType = convertSignatureToReturnType(signature);
+    String[] parameterList = convertSignatureToParameterList(signature);
+
+    if (rawDexFile.protoIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many protos for the DEX file.");
+    }
+
+    TypeList typeList = null;
+    Offsettable typeListOffsettable = null;
+
+    if (parameterList.length > 0) {
+      // Search for (or create) the parameter list.
+      typeList = findOrCreateTypeList(parameterList);
+
+      typeListOffsettable =
+          rawDexFile.getOffsetTracker().getOffsettableForItem(typeList);
+    }
+
+    // Search for (or create) the return type.
+    int returnTypeIdx = findOrCreateTypeId(returnType);
+
+    // Search for (or create) the shorty string.
+    int shortyIdx = findOrCreateString(shorty);
+
+    // Create ProtoIdItem.
+    ProtoIdItem newProtoId = new ProtoIdItem();
+    newProtoId.shortyIdx = shortyIdx;
+    newProtoId.returnTypeIdx = returnTypeIdx;
+    newProtoId.parametersOff = new Offset(false);
+    if (parameterList.length > 0) {
+      newProtoId.parametersOff.pointToNew(typeListOffsettable);
+    }
+
+    // ProtoIds must be ordered.
+    int newProtoIdIdx = findProtoIdInsertionPoint(signature);
+
+    rawDexFile.protoIds.add(newProtoIdIdx, newProtoId);
+
+    // Insert into OffsetTracker.
+    if (newProtoIdIdx == 0) {
+      rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newProtoId, rawDexFile);
+    } else {
+      ProtoIdItem prevProtoId = rawDexFile.protoIds.get(newProtoIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newProtoId, prevProtoId);
+    }
+
+    Log.info(String.format("Created new ProtoIdItem for %s, index: 0x%04x",
+        signature, newProtoIdIdx));
+
+    // Now that we've potentially moved a lot of proto IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.PROTO_ID, newProtoIdIdx);
+
+    // All done, return the index for the new proto.
+    return newProtoIdIdx;
+  }
+
+  private int findProtoId(String signature) {
+    String shorty = convertSignatureToShorty(signature);
+    String returnType = convertSignatureToReturnType(signature);
+    String[] parameterList = convertSignatureToParameterList(signature);
+
+    int shortyIdx = findString(shorty);
+    if (shortyIdx == -1) {
+      return -1;
+    }
+    int returnTypeIdx = findTypeId(returnType);
+    if (returnTypeIdx == -1) {
+      return -1;
+    }
+
+    // Only look for a TypeList if there's a parameter list.
+    TypeList typeList = null;
+    if (parameterList.length > 0) {
+      typeList = findTypeList(parameterList);
+      if (typeList == null) {
+        return -1;
+      }
+    }
+
+    int protoIdIdx = 0;
+    for (ProtoIdItem protoId : rawDexFile.protoIds) {
+      if (parameterList.length > 0) {
+        // With parameters.
+        if (shortyIdx == protoId.shortyIdx
+            && returnTypeIdx == protoId.returnTypeIdx
+            && typeList.equals(protoId.parametersOff.getPointedToItem())) {
+          return protoIdIdx;
+        }
+      } else {
+        // Without parameters.
+        if (shortyIdx == protoId.shortyIdx
+            && returnTypeIdx == protoId.returnTypeIdx) {
+          return protoIdIdx;
+        }
+      }
+      protoIdIdx++;
+    }
+    return -1;
+  }
+
+  private int findOrCreateProtoId(String signature) {
+    int protoIdx = findProtoId(signature);
+    if (protoIdx != -1) {
+      return protoIdx;
+    }
+    return createProtoId(signature);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java b/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java
new file mode 100644
index 0000000..ea66844
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+/**
+ * A subclass of the MInsn, that tracks its target instruction.
+ */
+public class MBranchInsn extends MInsn {
+  /**
+   * The MInsn this branch instruction branches to.
+   */
+  public MInsn target;
+
+  /**
+   * Clone this MBranchInsn, and clone the wrapped Instruction.
+   */
+  public MBranchInsn clone() {
+    MBranchInsn newInsn = new MBranchInsn();
+    newInsn.insn = insn.clone();
+    newInsn.target = target;
+    return newInsn;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MInsn.java b/tools/dexfuzz/src/dexfuzz/program/MInsn.java
new file mode 100644
index 0000000..10f7755
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MInsn.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Base class that is a thin wrapper for Instructions currently, also tracking location
+ * as the instruction is moved around.
+ */
+public class MInsn {
+  /**
+   * The raw DEX instruction that this instruction represents.
+   */
+  public Instruction insn;
+
+
+  /**
+   * The location of this instruction, as an offset in code words from the beginning.
+   * May become invalid if instructions around it are mutated.
+   */
+  public int location;
+
+  /**
+   * Denotes if the currently associated location can be trusted.
+   */
+  public boolean locationUpdated;
+
+  /**
+   * Clone this MInsn, and clone the wrapped Instruction.
+   */
+  public MInsn clone() {
+    MInsn newInsn = new MInsn();
+    newInsn.insn = insn.clone();
+    // It is the responsibility of the cloner to update these values.
+    newInsn.location = location;
+    newInsn.locationUpdated = locationUpdated;
+    return newInsn;
+  }
+
+  /**
+   * Get the String representation of an instruction.
+   */
+  public String toString() {
+    return String.format("{0x%04x%s: %s}",
+        location,
+        (locationUpdated) ? "!" : "",
+            insn.toString());
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java b/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java
new file mode 100644
index 0000000..ffed883
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+/**
+ * A subclass of the MInsn, that tracks the data instruction.
+ */
+public class MInsnWithData extends MInsn {
+  /**
+   * The MInsn that represents the data this instruction uses.
+   */
+  public MInsn dataTarget;
+
+  /**
+   * Clone this MInsnWithData, and clone the wrapped Instruction.
+   */
+  public MInsnWithData clone() {
+    MInsnWithData newInsn = new MInsnWithData();
+    newInsn.insn = insn.clone();
+    newInsn.dataTarget = dataTarget;
+    return newInsn;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java b/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java
new file mode 100644
index 0000000..d8693fe
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A subclass of the MInsnWithData, that also has multiple jump targets.
+ */
+public class MSwitchInsn extends MInsnWithData {
+  /**
+   * The MInsns this switch instruction branches to.
+   */
+  public List<MInsn> targets = new LinkedList<MInsn>();
+
+  public boolean packed;
+
+  public int[] keys;
+
+  /**
+   * Clone this MSwitchInsn, and clone the wrapped Instruction.
+   */
+  public MSwitchInsn clone() {
+    MSwitchInsn newInsn = new MSwitchInsn();
+    newInsn.insn = insn.clone();
+    newInsn.dataTarget = dataTarget;
+    newInsn.packed = packed;
+    for (MInsn target : targets) {
+      newInsn.targets.add(target);
+    }
+    newInsn.keys = new int[keys.length];
+    System.arraycopy(keys, 0, newInsn.keys, 0, keys.length);
+    return newInsn;
+  }
+}
\ No newline at end of file
diff --git a/runtime/closure.h b/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
similarity index 65%
copy from runtime/closure.h
copy to tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
index 9bea28f..a1dc029 100644
--- a/runtime/closure.h
+++ b/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,19 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_CLOSURE_H_
-#define ART_RUNTIME_CLOSURE_H_
+package dexfuzz.program;
 
-namespace art {
+import java.util.List;
 
-class Thread;
-
-class Closure {
- public:
-  virtual ~Closure() { }
-  virtual void Run(Thread* self) = 0;
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_CLOSURE_H_
+/**
+ * Tracks where try blocks start and end.
+ */
+public class MTryBlock {
+  public MInsn startInsn;
+  public MInsn endInsn;
+  public List<MInsn> handlers;
+  public MInsn catchAllHandler;
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java b/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java
new file mode 100644
index 0000000..c56b1bc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java
@@ -0,0 +1,410 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A class that represents a CodeItem in a way that is more amenable to mutation.
+ */
+public class MutatableCode {
+  /**
+   * To ensure we update the correct CodeItem in the raw DEX file.
+   */
+  public int codeItemIdx;
+
+  /**
+   * This is an index into the Program's list of MutatableCodes.
+   */
+  public int mutatableCodeIdx;
+
+  /**
+   * Number of registers this code uses.
+   */
+  public short registersSize;
+
+  /**
+   * Number of ins this code has.
+   */
+  public short insSize;
+
+  /**
+   * Number of outs this code has.
+   */
+  public short outsSize;
+
+  /**
+   * Number of tries this code has.
+   */
+  public short triesSize;
+
+  /**
+   * CodeTranslator is responsible for creating this, and
+   * converting it back to a list of Instructions.
+   */
+  private List<MInsn> mutatableInsns;
+
+  /**
+   * CodeTranslator is responsible for creating this, and
+   * converting it back to the correct form for CodeItems.
+   */
+  public List<MTryBlock> mutatableTries;
+
+  /**
+   * The name of the method this code represents.
+   */
+  public String name;
+  public String shorty;
+  public boolean isStatic;
+
+  /**
+   * The Program that owns this MutatableCode.
+   * Currently used to get the size of constant pools for
+   * PoolIndexChanger/RandomInstructionGenerator
+   */
+  public Program program;
+
+  private short originalInVReg;
+  private short tempVRegsAllocated;
+  private short initialTempVReg;
+  private boolean vregsNeedCopying;
+  private int numMoveInsnsGenerated;
+
+  public MutatableCode(Program program) {
+    this.program = program;
+    this.mutatableInsns = new LinkedList<MInsn>();
+  }
+
+  /**
+   * Call this to update all instructions after the provided mInsn, to have their
+   * locations adjusted by the provided offset. It will also mark that they have been updated.
+   */
+  public void updateInstructionLocationsAfter(MInsn mInsn, int offset) {
+    boolean updating = false;
+    for (MInsn mInsnChecking : mutatableInsns) {
+      if (updating) {
+        mInsnChecking.locationUpdated = true;
+        mInsnChecking.location += offset;
+      } else {
+        if (mInsnChecking == mInsn) {
+          updating = true;
+        }
+      }
+
+    }
+  }
+
+  private void recalculateLocations() {
+    int loc = 0;
+    for (MInsn mInsn : mutatableInsns) {
+      mInsn.location = loc;
+      loc += mInsn.insn.getSize();
+    }
+  }
+
+  public List<MInsn> getInstructions() {
+    return Collections.unmodifiableList(mutatableInsns);
+  }
+
+  public int getInstructionCount() {
+    return mutatableInsns.size();
+  }
+
+  public int getInstructionIndex(MInsn mInsn) {
+    return mutatableInsns.indexOf(mInsn);
+  }
+
+  public MInsn getInstructionAt(int idx) {
+    return mutatableInsns.get(idx);
+  }
+
+  public void addInstructionToEnd(MInsn mInsn) {
+    mutatableInsns.add(mInsn);
+  }
+
+  public void insertInstructionAfter(MInsn toBeInserted, int insertionIdx) {
+    if ((insertionIdx + 1) < mutatableInsns.size()) {
+      insertInstructionAt(toBeInserted, insertionIdx + 1);
+    } else {
+      // Appending to end.
+      MInsn finalInsn = mutatableInsns.get(mutatableInsns.size() - 1);
+      toBeInserted.location = finalInsn.location + finalInsn.insn.getSize();
+      mutatableInsns.add(toBeInserted);
+    }
+  }
+
+  /**
+   * Has same semantics as List.add()
+   */
+  public void insertInstructionAt(MInsn toBeInserted, int insertionIdx) {
+    MInsn currentInsn = mutatableInsns.get(insertionIdx);
+    toBeInserted.location = currentInsn.location;
+    mutatableInsns.add(insertionIdx , toBeInserted);
+    updateInstructionLocationsAfter(toBeInserted, toBeInserted.insn.getSize());
+  }
+
+  /**
+   * Checks if any MTryBlock's instruction refs pointed at the 'before' MInsn,
+   * and points them to the 'after' MInsn, if so. 'twoWay' will check if 'after'
+   * was pointed to, and point refs to the 'before' insn.
+   * (one-way is used when deleting instructions,
+   * two-way is used when swapping instructions.)
+   */
+  private void updateTryBlocksWithReplacementInsn(MInsn before, MInsn after,
+      boolean twoWay) {
+    if (triesSize > 0) {
+      for (MTryBlock mTryBlock : mutatableTries) {
+        if (mTryBlock.startInsn == before) {
+          Log.debug("Try block's first instruction was updated");
+          mTryBlock.startInsn = after;
+        } else if (twoWay && mTryBlock.startInsn == after) {
+          Log.debug("Try block's first instruction was updated");
+          mTryBlock.startInsn = before;
+        }
+        if (mTryBlock.endInsn == before) {
+          Log.debug("Try block's last instruction was updated");
+          mTryBlock.endInsn = after;
+        } else if (twoWay && mTryBlock.endInsn == after) {
+          Log.debug("Try block's last instruction was updated");
+          mTryBlock.endInsn = before;
+        }
+        if (mTryBlock.catchAllHandler == before) {
+          Log.debug("Try block's catch-all instruction was updated");
+          mTryBlock.catchAllHandler = after;
+        } else if (twoWay && mTryBlock.catchAllHandler == after) {
+          Log.debug("Try block's catch-all instruction was updated");
+          mTryBlock.catchAllHandler = before;
+        }
+
+        List<Integer> matchesIndicesToChange = new ArrayList<Integer>();
+        List<Integer> replacementIndicesToChange = null;
+        if (twoWay) {
+          replacementIndicesToChange = new ArrayList<Integer>();
+        }
+
+        int idx = 0;
+        for (MInsn handler : mTryBlock.handlers) {
+          if (handler == before) {
+            matchesIndicesToChange.add(idx);
+            Log.debug("Try block's handler instruction was updated");
+          } else if (twoWay && handler == after) {
+            replacementIndicesToChange.add(idx);
+            Log.debug("Try block's handler instruction was updated");
+          }
+          idx++;
+        }
+
+        for (int idxToChange : matchesIndicesToChange) {
+          mTryBlock.handlers.set(idxToChange, after);
+        }
+
+        if (twoWay) {
+          for (int idxToChange : replacementIndicesToChange) {
+            mTryBlock.handlers.set(idxToChange, before);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * The actual implementation of deleteInstruction called by
+   * the single-argument deleteInstructions.
+   */
+  private void deleteInstructionFull(MInsn toBeDeleted, int toBeDeletedIdx) {
+    // Make sure we update all locations afterwards first.
+    updateInstructionLocationsAfter(toBeDeleted, -(toBeDeleted.insn.getSize()));
+
+    // Remove it.
+    mutatableInsns.remove(toBeDeletedIdx);
+
+    // Update any branch instructions that branched to the instruction we just deleted!
+
+    // First, pick the replacement target.
+    int replacementTargetIdx = toBeDeletedIdx;
+    if (replacementTargetIdx == mutatableInsns.size()) {
+      replacementTargetIdx--;
+    }
+    MInsn replacementTarget = mutatableInsns.get(replacementTargetIdx);
+
+    for (MInsn mInsn : mutatableInsns) {
+      if (mInsn instanceof MBranchInsn) {
+        // Check if this branch insn points at the insn we just deleted.
+        MBranchInsn branchInsn = (MBranchInsn) mInsn;
+        MInsn target = branchInsn.target;
+        if (target == toBeDeleted) {
+          Log.debug(branchInsn + " was pointing at the deleted instruction, updated.");
+          branchInsn.target = replacementTarget;
+        }
+      } else if (mInsn instanceof MSwitchInsn) {
+        // Check if any of this switch insn's targets points at the insn we just deleted.
+        MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
+        List<Integer> indicesToChange = new ArrayList<Integer>();
+        int idx = 0;
+        for (MInsn target : switchInsn.targets) {
+          if (target == toBeDeleted) {
+            indicesToChange.add(idx);
+            Log.debug(switchInsn + "[" + idx
+                + "] was pointing at the deleted instruction, updated.");
+          }
+          idx++;
+        }
+        for (int idxToChange : indicesToChange) {
+          switchInsn.targets.remove(idxToChange);
+          switchInsn.targets.add(idxToChange, replacementTarget);
+        }
+      }
+    }
+
+    // Now update the try blocks.
+    updateTryBlocksWithReplacementInsn(toBeDeleted, replacementTarget, false);
+  }
+
+  /**
+   * Delete the provided MInsn.
+   */
+  public void deleteInstruction(MInsn toBeDeleted) {
+    deleteInstructionFull(toBeDeleted, mutatableInsns.indexOf(toBeDeleted));
+  }
+
+  /**
+   * Delete the MInsn at the provided index.
+   */
+  public void deleteInstruction(int toBeDeletedIdx) {
+    deleteInstructionFull(mutatableInsns.get(toBeDeletedIdx), toBeDeletedIdx);
+  }
+
+  public void swapInstructionsByIndex(int aIdx, int bIdx) {
+    MInsn aInsn = mutatableInsns.get(aIdx);
+    MInsn bInsn = mutatableInsns.get(bIdx);
+
+    mutatableInsns.set(aIdx, bInsn);
+    mutatableInsns.set(bIdx, aInsn);
+
+    updateTryBlocksWithReplacementInsn(aInsn, bInsn, true);
+
+    recalculateLocations();
+  }
+
+  /**
+   * Some mutators may require the use of temporary registers. For instance,
+   * to easily add in printing of values without having to look for registers
+   * that aren't currently live.
+   * The idea is to allocate these registers at the top of the set of registers.
+   * Because this will then shift where the arguments to the method are, we then
+   * change the start of the method to copy the arguments to the method
+   * into the place where the rest of the method's code expects them to be.
+   * Call allocateTemporaryVRegs(n), then use getTemporaryVReg(n),
+   * and then make sure finishedUsingTemporaryVRegs() is called!
+   */
+  public void allocateTemporaryVRegs(int count) {
+    if (count > tempVRegsAllocated) {
+      if (tempVRegsAllocated == 0) {
+        Log.info("Allocating temporary vregs for method...");
+        initialTempVReg = registersSize;
+        originalInVReg = (short) (registersSize - insSize);
+      } else {
+        Log.info("Extending allocation of temporary vregs for method...");
+      }
+      registersSize = (short) (initialTempVReg + count);
+      if (outsSize < count) {
+        outsSize = (short) count;
+      }
+      vregsNeedCopying = true;
+      tempVRegsAllocated = (short) count;
+    }
+  }
+
+  public int getTemporaryVReg(int number) {
+    if (number >= tempVRegsAllocated) {
+      Log.errorAndQuit("Not allocated enough temporary vregs!");
+    }
+    return initialTempVReg + number;
+  }
+
+  public void finishedUsingTemporaryVRegs() {
+    if (tempVRegsAllocated > 0 && vregsNeedCopying) {
+      // Just delete all the move instructions and generate again, if we already have some.
+      while (numMoveInsnsGenerated > 0) {
+        deleteInstruction(0);
+        numMoveInsnsGenerated--;
+      }
+
+      Log.info("Moving 'in' vregs to correct locations after allocating temporary vregs");
+
+      int shortyIdx = 0;
+      if (isStatic) {
+        shortyIdx = 1;
+      }
+
+      int insertionCounter = 0;
+
+      // Insert copy insns that move all the in VRs down.
+      for (int i = 0; i < insSize; i++) {
+        MInsn moveInsn = new MInsn();
+        moveInsn.insn = new Instruction();
+        moveInsn.insn.vregA = originalInVReg + i;
+        moveInsn.insn.vregB = originalInVReg + i + tempVRegsAllocated;
+
+        char type = 'L';
+        if (shortyIdx > 0) {
+          type = shorty.charAt(shortyIdx);
+        }
+        shortyIdx++;
+
+        if (type == 'L') {
+          moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
+        } else if (type == 'D' || type == 'J') {
+          moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
+          i++;
+        } else {
+          moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
+        }
+
+        insertInstructionAt(moveInsn, insertionCounter);
+        insertionCounter++;
+        Log.info("Temp vregs creation, Added instruction " + moveInsn);
+        numMoveInsnsGenerated++;
+      }
+
+      vregsNeedCopying = false;
+    }
+  }
+
+  /**
+   * When we insert new Field/Type/MethodIds into the DEX file, this may shunt some Ids
+   * into a new position in the table. If this happens, every reference to the Ids must
+   * be updated! Because CodeItems have their Instructions wrapped into a graph of MInsns
+   * during mutation, they don't have a view of all their instructions during mutation,
+   * and so if they are asked to update their instructions' indices into the tables, they
+   * must call this method to get the actual list of instructions they currently own.
+   */
+  public List<Instruction> requestLatestInstructions() {
+    List<Instruction> latestInsns = new ArrayList<Instruction>();
+    for (MInsn mInsn : mutatableInsns) {
+      latestInsns.add(mInsn.insn);
+    }
+    return latestInsns;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/Mutation.java b/tools/dexfuzz/src/dexfuzz/program/Mutation.java
new file mode 100644
index 0000000..2eba718
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/Mutation.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.program.mutators.CodeMutator;
+
+/**
+ * Mutation should be subclassed by an AssociatedMutation in each CodeMutator,
+ * which will describe the parameters of the mutation, and override the getString()
+ * and parseString() methods here to allow serialization of the mutations.
+ */
+public abstract class Mutation {
+
+  public MutatableCode mutatableCode;
+
+  // The first field of any serialized mutation - the mutator that uses it.
+  public Class<? extends CodeMutator> mutatorClass;
+  // The second field of any serialized mutation...
+  // This is an index into the Program's list of MutatableCodes
+  // i.e., it is NOT an index into the DEX file's CodeItems!
+  public int mutatableCodeIdx;
+
+  public void setup(Class<? extends CodeMutator> mutatorClass, MutatableCode mutatableCode) {
+    this.mutatorClass = mutatorClass;
+    this.mutatableCode = mutatableCode;
+    this.mutatableCodeIdx = mutatableCode.mutatableCodeIdx;
+  }
+
+  public abstract String getString();
+
+  public abstract void parseString(String[] elements);
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java b/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java
new file mode 100644
index 0000000..7f79517
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.program.mutators.CodeMutator;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * Responsible for serializing mutations, allowing replay of mutations, and searching
+ * for a minimal set of mutations.
+ */
+public class MutationSerializer {
+  public static String getMutationString(Mutation mutation) {
+    StringBuilder builder = new StringBuilder();
+    builder.append(mutation.mutatorClass.getCanonicalName()).append(" ");
+    builder.append(mutation.mutatableCodeIdx).append(" ");
+    builder.append(mutation.getString());
+    return builder.toString();
+  }
+
+  public static void writeMutation(BufferedWriter writer, Mutation mutation) throws IOException {
+    // Write out the common fields.
+    writer.write(mutation.mutatorClass.getCanonicalName() + " "
+        + mutation.mutatableCodeIdx + " ");
+
+    // Use the mutation's own function to write out the rest of the fields.
+    writer.write(mutation.getString() + "\n");
+  }
+
+  @SuppressWarnings("unchecked")
+  public static Mutation readMutation(BufferedReader reader) throws IOException {
+    String line = reader.readLine();
+    String[] fields = null;
+    if (line != null) {
+      fields = line.split(" ");
+    } else {
+      Log.errorAndQuit("Could not read line during mutation loading.");
+    }
+
+    // Read the mutator's class name
+    String mutatorClassName = fields[0];
+
+    // Get the class for that mutator
+    Class<? extends CodeMutator> mutatorClass = null;
+    try {
+      mutatorClass = (Class<? extends CodeMutator>) Class.forName(mutatorClassName);
+    } catch (ClassNotFoundException e) {
+      Log.errorAndQuit("Cannot find a mutator class called: " + mutatorClassName);
+    }
+
+    Mutation mutation = null;
+    try {
+      mutation = mutatorClass.newInstance().getNewMutation();
+    } catch (InstantiationException e) {
+      Log.errorAndQuit("Unable to instantiate " + mutatorClassName
+          + " using default constructor.");
+    } catch (IllegalAccessException e) {
+      Log.errorAndQuit("Unable to access methods in " + mutatorClassName + ".");
+    }
+
+    if (mutation == null) {
+      Log.errorAndQuit("Unable to get Mutation for Mutator: " + mutatorClassName);
+    }
+
+    // Populate the common fields of the mutation.
+    mutation.mutatorClass = mutatorClass;
+    // The Program must set this later, using the mutatable_code_idx
+    //   into its list of MutatableCodes.
+    mutation.mutatableCode = null;
+    mutation.mutatableCodeIdx = Integer.parseInt(fields[1]);
+
+    // Use the mutation's own method to read the rest of the fields.
+    mutation.parseString(fields);
+
+    return mutation;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/Program.java b/tools/dexfuzz/src/dexfuzz/program/Program.java
new file mode 100644
index 0000000..286fe52
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/Program.java
@@ -0,0 +1,602 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.mutators.ArithOpChanger;
+import dexfuzz.program.mutators.BranchShifter;
+import dexfuzz.program.mutators.CmpBiasChanger;
+import dexfuzz.program.mutators.CodeMutator;
+import dexfuzz.program.mutators.ConstantValueChanger;
+import dexfuzz.program.mutators.ConversionRepeater;
+import dexfuzz.program.mutators.FieldFlagChanger;
+import dexfuzz.program.mutators.InstructionDeleter;
+import dexfuzz.program.mutators.InstructionDuplicator;
+import dexfuzz.program.mutators.InstructionSwapper;
+import dexfuzz.program.mutators.NewMethodCaller;
+import dexfuzz.program.mutators.NonsenseStringPrinter;
+import dexfuzz.program.mutators.PoolIndexChanger;
+import dexfuzz.program.mutators.RandomInstructionGenerator;
+import dexfuzz.program.mutators.SwitchBranchShifter;
+import dexfuzz.program.mutators.TryBlockShifter;
+import dexfuzz.program.mutators.ValuePrinter;
+import dexfuzz.program.mutators.VRegChanger;
+import dexfuzz.rawdex.ClassDataItem;
+import dexfuzz.rawdex.ClassDefItem;
+import dexfuzz.rawdex.CodeItem;
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.EncodedField;
+import dexfuzz.rawdex.EncodedMethod;
+import dexfuzz.rawdex.FieldIdItem;
+import dexfuzz.rawdex.MethodIdItem;
+import dexfuzz.rawdex.ProtoIdItem;
+import dexfuzz.rawdex.RawDexFile;
+import dexfuzz.rawdex.TypeIdItem;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * After the raw DEX file has been parsed, it is passed into this class
+ * that represents the program in a mutatable form.
+ * The class uses a CodeTranslator to translate between the raw DEX form
+ * for a method, and the mutatable form. It also controls all CodeMutators,
+ * deciding which ones should be applied to each CodeItem.
+ */
+public class Program {
+  /**
+   * The RNG used during mutation.
+   */
+  private Random rng;
+
+  /**
+   * The seed that was given to the RNG.
+   */
+  public long rngSeed;
+
+  /**
+   * The parsed raw DEX file.
+   */
+  private RawDexFile rawDexFile;
+
+  /**
+   * The system responsible for translating from CodeItems to MutatableCode and vice-versa.
+   */
+  private CodeTranslator translator;
+
+  /**
+   * Responsible for adding new class ID items, method ID items, etc.
+   */
+  private IdCreator idCreator;
+
+  /**
+   * A list of all the MutatableCode that the CodeTranslator produced from
+   * CodeItems that are acceptable to mutate.
+   */
+  private List<MutatableCode> mutatableCodes;
+
+  /**
+   * A list of all MutatableCode items that were mutated when mutateTheProgram()
+   * was called. updateRawDexFile() will update the relevant CodeItems when called,
+   * and then clear this list.
+   */
+  private List<MutatableCode> mutatedCodes;
+
+  /**
+   * A list of all registered CodeMutators that this Program can use to mutate methods.
+   */
+  private List<CodeMutator> mutators;
+
+  /**
+   * Used if we're loading mutations from a file, so we can find the correct mutator.
+   */
+  private Map<Class<? extends CodeMutator>, CodeMutator> mutatorsLookupByClass;
+
+  /**
+   * Tracks mutation stats.
+   */
+  private MutationStats mutationStats;
+
+  /**
+   * A list of all mutations used for loading/dumping mutations from/to a file.
+   */
+  private List<Mutation> mutations;
+
+  /**
+   * The listener who is interested in events.
+   * May be a listener that is responsible for multiple listeners.
+   */
+  private BaseListener listener;
+
+  /**
+   * Given a maximum number of mutations that can be performed on a method, n,
+   * give up after attempting (n * this value) mutations for any method.
+   */
+  private static final int MAXIMUM_MUTATION_ATTEMPT_FACTOR = 10;
+
+  /**
+   * Construct the mutatable Program based on the raw DEX file that was parsed initially.
+   */
+  public Program(RawDexFile rawDexFile, List<Mutation> previousMutations,
+      BaseListener listener) {
+    this.listener = listener;
+
+    idCreator = new IdCreator(rawDexFile);
+
+    // Set up the RNG.
+    rng = new Random();
+    if (Options.usingProvidedSeed) {
+      rng.setSeed(Options.rngSeed);
+      rngSeed = Options.rngSeed;
+    } else {
+      long seed = System.currentTimeMillis();
+      listener.handleSeed(seed);
+      rng.setSeed(seed);
+      rngSeed = seed;
+    }
+
+    if (previousMutations != null) {
+      mutations = previousMutations;
+    } else {
+      // Allocate the mutations list.
+      mutations = new ArrayList<Mutation>();
+
+      // Read in the mutations if we need to.
+      if (Options.loadMutations) {
+        // Allocate the mutators lookup table.
+        mutatorsLookupByClass = new HashMap<Class<? extends CodeMutator>, CodeMutator>();
+        loadMutationsFromDisk(Options.loadMutationsFile);
+      }
+    }
+
+    // Allocate the mutators list.
+    mutators = new ArrayList<CodeMutator>();
+
+    this.rawDexFile = rawDexFile;
+
+    mutatableCodes = new ArrayList<MutatableCode>();
+    mutatedCodes = new ArrayList<MutatableCode>();
+
+    translator = new CodeTranslator();
+
+    mutationStats = new MutationStats();
+
+    // Register all the code mutators here.
+    registerMutator(new ArithOpChanger(rng, mutationStats, mutations));
+    registerMutator(new BranchShifter(rng, mutationStats, mutations));
+    registerMutator(new CmpBiasChanger(rng, mutationStats, mutations));
+    registerMutator(new ConstantValueChanger(rng, mutationStats, mutations));
+    registerMutator(new ConversionRepeater(rng, mutationStats, mutations));
+    registerMutator(new FieldFlagChanger(rng, mutationStats, mutations));
+    registerMutator(new InstructionDeleter(rng, mutationStats, mutations));
+    registerMutator(new InstructionDuplicator(rng, mutationStats, mutations));
+    registerMutator(new InstructionSwapper(rng, mutationStats, mutations));
+    registerMutator(new NewMethodCaller(rng, mutationStats, mutations));
+    registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations));
+    registerMutator(new PoolIndexChanger(rng, mutationStats, mutations));
+    registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations));
+    registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations));
+    registerMutator(new TryBlockShifter(rng, mutationStats, mutations));
+    registerMutator(new ValuePrinter(rng, mutationStats, mutations));
+    registerMutator(new VRegChanger(rng, mutationStats, mutations));
+
+    associateClassDefsAndClassData();
+    associateCodeItemsWithMethodNames();
+
+    int codeItemIdx = 0;
+    for (CodeItem codeItem : rawDexFile.codeItems) {
+      if (legalToMutate(codeItem)) {
+        Log.debug("Legal to mutate code item " + codeItemIdx);
+        int mutatableCodeIdx = mutatableCodes.size();
+        mutatableCodes.add(translator.codeItemToMutatableCode(this, codeItem,
+            codeItemIdx, mutatableCodeIdx));
+      } else {
+        Log.debug("Not legal to mutate code item " + codeItemIdx);
+      }
+      codeItemIdx++;
+    }
+  }
+
+  private void registerMutator(CodeMutator mutator) {
+    if (mutator.canBeTriggered()) {
+      Log.debug("Registering mutator " + mutator.getClass().getSimpleName());
+      mutators.add(mutator);
+    }
+    if (Options.loadMutations) {
+      mutatorsLookupByClass.put(mutator.getClass(), mutator);
+    }
+  }
+
+  /**
+   * Associate ClassDefItem to a ClassDataItem and vice-versa.
+   * This is so when we're associating method names with code items,
+   * we can find the name of the class the method belongs to.
+   */
+  private void associateClassDefsAndClassData() {
+    for (ClassDefItem classDefItem : rawDexFile.classDefs) {
+      if (classDefItem.classDataOff.pointsToSomething()) {
+        ClassDataItem classDataItem = (ClassDataItem)
+            classDefItem.classDataOff.getPointedToItem();
+        classDataItem.meta.classDefItem = classDefItem;
+        classDefItem.meta.classDataItem = classDataItem;
+      }
+    }
+  }
+
+  /**
+   * For each CodeItem, find the name of the method the item represents.
+   * This is done to allow the filtering of mutating methods based on if
+   * they have the name *_MUTATE, but also for debugging info.
+   */
+  private void associateCodeItemsWithMethodNames() {
+    // Associate method names with codeItems.
+    for (ClassDataItem classDataItem : rawDexFile.classDatas) {
+
+      String className = "";
+      if (classDataItem.meta.classDefItem != null) {
+        int typeIdx = classDataItem.meta.classDefItem.classIdx;
+        TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx);
+        className = rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString() + ".";
+      }
+
+      // Do direct methods...
+      // Track the current method index with this value, since the encoding in
+      // each EncodedMethod is the absolute index for the first EncodedMethod,
+      // and then relative index for the rest...
+      int methodIdx = 0;
+      for (EncodedMethod method : classDataItem.directMethods) {
+        methodIdx = associateMethod(method, methodIdx, className);
+      }
+      // Reset methodIdx for virtual methods...
+      methodIdx = 0;
+      for (EncodedMethod method : classDataItem.virtualMethods) {
+        methodIdx = associateMethod(method, methodIdx, className);
+      }
+    }
+  }
+
+  /**
+   * Associate the name of the provided method with its CodeItem, if it
+   * has one.
+   *
+   * @param methodIdx The method index of the last EncodedMethod that was handled in this class.
+   * @return The method index of the EncodedMethod that has just been handled in this class.
+   */
+  private int associateMethod(EncodedMethod method, int methodIdx, String className) {
+    if (!method.codeOff.pointsToSomething()) {
+      // This method doesn't have a code item, so we won't encounter it later.
+      return methodIdx;
+    }
+
+    // First method index is an absolute index.
+    // The rest are relative to the previous.
+    // (so if methodIdx is initialised to 0, this single line works)
+    methodIdx = methodIdx + method.methodIdxDiff;
+
+    // Get the name.
+    MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx);
+    ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx);
+    String shorty = rawDexFile.stringDatas.get(protoIdItem.shortyIdx).getString();
+    String methodName = className
+        + rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString();
+
+    // Get the codeItem.
+    if (method.codeOff.getPointedToItem() instanceof CodeItem) {
+      CodeItem codeItem = (CodeItem) method.codeOff.getPointedToItem();
+      codeItem.meta.methodName = methodName;
+      codeItem.meta.shorty = shorty;
+      codeItem.meta.isStatic = method.isStatic();
+    } else {
+      Log.errorAndQuit("You've got an EncodedMethod that points to an Offsettable"
+          + " that does not contain a CodeItem");
+    }
+
+    return methodIdx;
+  }
+
+  /**
+   * Determine, based on the current options supplied to dexfuzz, as well as
+   * its capabilities, if the provided CodeItem can be mutated.
+   * @param codeItem The CodeItem we may wish to mutate.
+   * @return If the CodeItem can be mutated.
+   */
+  private boolean legalToMutate(CodeItem codeItem) {
+    if (!Options.mutateLimit) {
+      Log.debug("Mutating everything.");
+      return true;
+    }
+    if (codeItem.meta.methodName.endsWith("_MUTATE")) {
+      Log.debug("Code item marked with _MUTATE.");
+      return true;
+    }
+    Log.debug("Code item not marked with _MUTATE, but not mutating all code items.");
+    return false;
+  }
+
+  private int getNumberOfMutationsToPerform() {
+    // We want n mutations to be twice as likely as n+1 mutations.
+    //
+    // So if we have max 3,
+    // then 0 has 8 chances ("tickets"),
+    //      1 has 4 chances
+    //      2 has 2 chances
+    //  and 3 has 1 chance
+
+    // Allocate the tickets
+    // n mutations need (2^(n+1) - 1) tickets
+    // e.g.
+    // 3 mutations => 15 tickets
+    // 4 mutations => 31 tickets
+    int tickets = (2 << Options.methodMutations) - 1;
+
+    // Pick the lucky ticket
+    int luckyTicket = rng.nextInt(tickets);
+
+    // The tickets are put into buckets with accordance with log-base-2.
+    // have to make sure it's luckyTicket + 1, because log(0) is undefined
+    // so:
+    // log_2(1) => 0
+    // log_2(2) => 1
+    // log_2(3) => 1
+    // log_2(4) => 2
+    // log_2(5) => 2
+    // log_2(6) => 2
+    // log_2(7) => 2
+    // log_2(8) => 3
+    // ...
+    // so to make the highest mutation value the rarest,
+    //   subtract log_2(luckyTicket+1) from the maximum number
+    // log2(x) <=> 31 - Integer.numberOfLeadingZeros(x)
+    int luckyMutation = Options.methodMutations
+        - (31 - Integer.numberOfLeadingZeros(luckyTicket + 1));
+
+    return luckyMutation;
+  }
+
+  /**
+   * Returns true if we completely failed to mutate this method's mutatable code after
+   * attempting to.
+   */
+  private boolean mutateAMutatableCode(MutatableCode mutatableCode) {
+    int mutations = getNumberOfMutationsToPerform();
+
+    Log.info("Attempting " + mutations + " mutations for method " + mutatableCode.name);
+
+    int mutationsApplied = 0;
+
+    int maximumMutationAttempts = Options.methodMutations * MAXIMUM_MUTATION_ATTEMPT_FACTOR;
+    int mutationAttempts = 0;
+    boolean hadToBail = false;
+
+    while (mutationsApplied < mutations) {
+      int mutatorIdx = rng.nextInt(mutators.size());
+      CodeMutator mutator = mutators.get(mutatorIdx);
+      Log.info("Running mutator " + mutator.getClass().getSimpleName());
+      if (mutator.attemptToMutate(mutatableCode)) {
+        mutationsApplied++;
+      }
+      mutationAttempts++;
+      if (mutationAttempts > maximumMutationAttempts) {
+        Log.info("Bailing out on mutation for this method, tried too many times...");
+        hadToBail = true;
+        break;
+      }
+    }
+
+    // If any of them actually mutated it, excellent!
+    if (mutationsApplied > 0) {
+      Log.info("Method was mutated.");
+      mutatedCodes.add(mutatableCode);
+    } else {
+      Log.info("Method was not mutated.");
+    }
+
+    return ((mutationsApplied == 0) && hadToBail);
+  }
+
+  /**
+   * Go through each mutatable method in turn, and attempt to mutate it.
+   * Afterwards, call updateRawDexFile() to apply the results of mutation to the
+   * original code.
+   */
+  public void mutateTheProgram() {
+    if (Options.loadMutations) {
+      applyMutationsFromList();
+      return;
+    }
+
+    // Typically, this is 2 to 10...
+    int methodsToMutate = Options.minMethods
+        + rng.nextInt((Options.maxMethods - Options.minMethods) + 1);
+
+    // Check we aren't trying to mutate more methods than we have.
+    if (methodsToMutate > mutatableCodes.size()) {
+      methodsToMutate = mutatableCodes.size();
+    }
+
+    // Check if we're going to end up mutating all the methods.
+    if (methodsToMutate == mutatableCodes.size()) {
+      // Just do them all in order.
+      Log.info("Mutating all possible methods.");
+      for (MutatableCode mutatableCode : mutatableCodes) {
+        if (mutatableCode == null) {
+          Log.errorAndQuit("Why do you have a null MutatableCode?");
+        }
+        mutateAMutatableCode(mutatableCode);
+      }
+      Log.info("Finished mutating all possible methods.");
+    } else {
+      // Pick them at random.
+      Log.info("Randomly selecting " + methodsToMutate + " methods to mutate.");
+      while (mutatedCodes.size() < methodsToMutate) {
+        int randomMethodIdx = rng.nextInt(mutatableCodes.size());
+        MutatableCode mutatableCode = mutatableCodes.get(randomMethodIdx);
+        if (mutatableCode == null) {
+          Log.errorAndQuit("Why do you have a null MutatableCode?");
+        }
+        if (!mutatedCodes.contains(mutatableCode)) {
+          boolean completelyFailedToMutate = mutateAMutatableCode(mutatableCode);
+          if (completelyFailedToMutate) {
+            methodsToMutate--;
+          }
+        }
+      }
+      Log.info("Finished mutating the methods.");
+    }
+
+    listener.handleMutationStats(mutationStats.getStatsString());
+
+    if (Options.dumpMutations) {
+      writeMutationsToDisk(Options.dumpMutationsFile);
+    }
+  }
+
+  private void writeMutationsToDisk(String fileName) {
+    Log.debug("Writing mutations to disk.");
+    try {
+      BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
+      for (Mutation mutation : mutations) {
+        MutationSerializer.writeMutation(writer, mutation);
+      }
+      writer.close();
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException while writing mutations to disk...");
+    }
+  }
+
+  private void loadMutationsFromDisk(String fileName) {
+    Log.debug("Loading mutations from disk.");
+    try {
+      BufferedReader reader = new BufferedReader(new FileReader(fileName));
+      while (reader.ready()) {
+        Mutation mutation = MutationSerializer.readMutation(reader);
+        mutations.add(mutation);
+      }
+      reader.close();
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException while loading mutations from disk...");
+    }
+  }
+
+  private void applyMutationsFromList() {
+    Log.info("Applying preloaded list of mutations...");
+    for (Mutation mutation : mutations) {
+      // Repopulate the MutatableCode field from the recorded index into the Program's list.
+      mutation.mutatableCode = mutatableCodes.get(mutation.mutatableCodeIdx);
+
+      // Get the right mutator.
+      CodeMutator mutator = mutatorsLookupByClass.get(mutation.mutatorClass);
+
+      // Apply the mutation.
+      mutator.forceMutate(mutation);
+
+      // Add this mutatable code to the list of mutated codes, if we haven't already.
+      if (!mutatedCodes.contains(mutation.mutatableCode)) {
+        mutatedCodes.add(mutation.mutatableCode);
+      }
+    }
+    Log.info("...finished applying preloaded list of mutations.");
+  }
+
+  public List<Mutation> getMutations() {
+    return mutations;
+  }
+
+  /**
+   * Updates any CodeItems that need to be updated after mutation.
+   */
+  public boolean updateRawDexFile() {
+    boolean anythingMutated = !(mutatedCodes.isEmpty());
+    for (MutatableCode mutatedCode : mutatedCodes) {
+      translator.mutatableCodeToCodeItem(rawDexFile.codeItems
+          .get(mutatedCode.codeItemIdx), mutatedCode);
+    }
+    mutatedCodes.clear();
+    return anythingMutated;
+  }
+
+  public void writeRawDexFile(DexRandomAccessFile file) throws IOException {
+    rawDexFile.write(file);
+  }
+
+  public void updateRawDexFileHeader(DexRandomAccessFile file) throws IOException {
+    rawDexFile.updateHeader(file);
+  }
+
+  /**
+   * Used by the CodeMutators to determine legal index values.
+   */
+  public int getTotalPoolIndicesByKind(PoolIndexKind poolIndexKind) {
+    switch (poolIndexKind) {
+      case Type:
+        return rawDexFile.typeIds.size();
+      case Field:
+        return rawDexFile.fieldIds.size();
+      case String:
+        return rawDexFile.stringIds.size();
+      case Method:
+        return rawDexFile.methodIds.size();
+      case Invalid:
+        return 0;
+      default:
+    }
+    return 0;
+  }
+
+  /**
+   * Used by the CodeMutators to lookup and/or create Ids.
+   */
+  public IdCreator getNewItemCreator() {
+    return idCreator;
+  }
+
+  /**
+   * Used by FieldFlagChanger, to find an EncodedField for a specified field in an insn,
+   * if that field is actually defined in this DEX file. If not, null is returned.
+   */
+  public EncodedField getEncodedField(int fieldIdx) {
+    if (fieldIdx >= rawDexFile.fieldIds.size()) {
+      Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
+          fieldIdx));
+      return null;
+    }
+    FieldIdItem fieldId = rawDexFile.fieldIds.get(fieldIdx);
+
+    for (ClassDefItem classDef : rawDexFile.classDefs) {
+      if (classDef.classIdx == fieldId.classIdx) {
+        ClassDataItem classData = classDef.meta.classDataItem;
+        return classData.getEncodedFieldWithIndex(fieldIdx);
+      }
+    }
+
+    Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
+        fieldIdx));
+    return null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java
new file mode 100644
index 0000000..4c69694
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ArithOpChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int arithmeticInsnIdx;
+    public int newOpcode;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(arithmeticInsnIdx).append(" ");
+      builder.append(newOpcode);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      arithmeticInsnIdx = Integer.parseInt(elements[2]);
+      newOpcode = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ArithOpChanger() { }
+
+  public ArithOpChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 75;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> arithmeticInsns = null;
+
+  private void generateCachedArithmeticInsns(MutatableCode mutatableCode) {
+    if (arithmeticInsns != null) {
+      return;
+    }
+
+    arithmeticInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isArithmeticOperation(mInsn)) {
+        arithmeticInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isArithmeticOperation(mInsn)) {
+        return true;
+      }
+    }
+
+    Log.debug("No arithmetic operations in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedArithmeticInsns(mutatableCode);
+
+    int arithmeticInsnIdx = rng.nextInt(arithmeticInsns.size());
+
+    MInsn randomInsn = arithmeticInsns.get(arithmeticInsnIdx);
+
+    OpcodeInfo oldOpcodeInfo = randomInsn.insn.info;
+
+    OpcodeInfo newOpcodeInfo = oldOpcodeInfo;
+
+    while (newOpcodeInfo.value == oldOpcodeInfo.value) {
+      newOpcodeInfo = Instruction.getOpcodeInfo(getLegalDifferentOpcode(randomInsn));
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.arithmeticInsnIdx = arithmeticInsnIdx;
+    mutation.newOpcode = newOpcodeInfo.value;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedArithmeticInsns(mutatableCode);
+
+    MInsn randomInsn = arithmeticInsns.get(mutation.arithmeticInsnIdx);
+
+    String oldInsnString = randomInsn.toString();
+
+    OpcodeInfo newOpcodeInfo = Instruction.getOpcodeInfo(mutation.newOpcode);
+
+    randomInsn.insn.info = newOpcodeInfo;
+
+    Log.info("Changed " + oldInsnString + " to " + randomInsn);
+
+    stats.incrementStat("Changed arithmetic opcode");
+
+    // Clear the cache.
+    arithmeticInsns = null;
+  }
+
+  private boolean isArithmeticOperation(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT_LIT8)) {
+      return true;
+    }
+    return false;
+  }
+
+  private Opcode getLegalDifferentOpcode(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+
+    for (List<Opcode> opcodeList : opcodeLists) {
+      Opcode first = opcodeList.get(0);
+      Opcode last = opcodeList.get(opcodeList.size() - 1);
+      if (Opcode.isBetween(opcode, first, last)) {
+        int newOpcodeIdx = rng.nextInt(opcodeList.size());
+        return opcodeList.get(newOpcodeIdx);
+      }
+    }
+
+    return opcode;
+  }
+
+  private static List<Opcode> intOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> int2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> longOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> long2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> floatOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> float2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> doubleOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> double2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> intLit8Opcodes = new ArrayList<Opcode>();
+  private static List<Opcode> intLit16Opcodes = new ArrayList<Opcode>();
+  private static List<List<Opcode>> opcodeLists = new ArrayList<List<Opcode>>();
+
+  static {
+    intOpcodes.add(Opcode.ADD_INT);
+    intOpcodes.add(Opcode.SUB_INT);
+    intOpcodes.add(Opcode.MUL_INT);
+    intOpcodes.add(Opcode.DIV_INT);
+    intOpcodes.add(Opcode.REM_INT);
+    intOpcodes.add(Opcode.AND_INT);
+    intOpcodes.add(Opcode.OR_INT);
+    intOpcodes.add(Opcode.XOR_INT);
+    intOpcodes.add(Opcode.SHL_INT);
+    intOpcodes.add(Opcode.SHR_INT);
+    intOpcodes.add(Opcode.USHR_INT);
+
+    int2addrOpcodes.add(Opcode.ADD_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.SUB_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.MUL_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.DIV_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.REM_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.AND_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.OR_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.XOR_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.SHL_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.SHR_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.USHR_INT_2ADDR);
+
+    longOpcodes.add(Opcode.ADD_LONG);
+    longOpcodes.add(Opcode.SUB_LONG);
+    longOpcodes.add(Opcode.MUL_LONG);
+    longOpcodes.add(Opcode.DIV_LONG);
+    longOpcodes.add(Opcode.REM_LONG);
+    longOpcodes.add(Opcode.AND_LONG);
+    longOpcodes.add(Opcode.OR_LONG);
+    longOpcodes.add(Opcode.XOR_LONG);
+    longOpcodes.add(Opcode.SHL_LONG);
+    longOpcodes.add(Opcode.SHR_LONG);
+    longOpcodes.add(Opcode.USHR_LONG);
+
+    long2addrOpcodes.add(Opcode.ADD_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.SUB_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.MUL_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.DIV_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.REM_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.AND_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.OR_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.XOR_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.SHL_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.SHR_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.USHR_LONG_2ADDR);
+
+    floatOpcodes.add(Opcode.ADD_FLOAT);
+    floatOpcodes.add(Opcode.SUB_FLOAT);
+    floatOpcodes.add(Opcode.MUL_FLOAT);
+    floatOpcodes.add(Opcode.DIV_FLOAT);
+    floatOpcodes.add(Opcode.REM_FLOAT);
+
+    float2addrOpcodes.add(Opcode.ADD_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.SUB_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.MUL_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.DIV_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.REM_FLOAT_2ADDR);
+
+    doubleOpcodes.add(Opcode.ADD_DOUBLE);
+    doubleOpcodes.add(Opcode.SUB_DOUBLE);
+    doubleOpcodes.add(Opcode.MUL_DOUBLE);
+    doubleOpcodes.add(Opcode.DIV_DOUBLE);
+    doubleOpcodes.add(Opcode.REM_DOUBLE);
+
+    double2addrOpcodes.add(Opcode.ADD_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.SUB_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.MUL_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.DIV_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.REM_DOUBLE_2ADDR);
+
+    intLit8Opcodes.add(Opcode.ADD_INT_LIT8);
+    intLit8Opcodes.add(Opcode.RSUB_INT_LIT8);
+    intLit8Opcodes.add(Opcode.MUL_INT_LIT8);
+    intLit8Opcodes.add(Opcode.DIV_INT_LIT8);
+    intLit8Opcodes.add(Opcode.REM_INT_LIT8);
+    intLit8Opcodes.add(Opcode.AND_INT_LIT8);
+    intLit8Opcodes.add(Opcode.OR_INT_LIT8);
+    intLit8Opcodes.add(Opcode.XOR_INT_LIT8);
+    intLit8Opcodes.add(Opcode.SHL_INT_LIT8);
+    intLit8Opcodes.add(Opcode.SHR_INT_LIT8);
+    intLit8Opcodes.add(Opcode.USHR_INT_LIT8);
+
+    intLit16Opcodes.add(Opcode.ADD_INT_LIT16);
+    intLit16Opcodes.add(Opcode.RSUB_INT);
+    intLit16Opcodes.add(Opcode.MUL_INT_LIT16);
+    intLit16Opcodes.add(Opcode.DIV_INT_LIT16);
+    intLit16Opcodes.add(Opcode.REM_INT_LIT16);
+    intLit16Opcodes.add(Opcode.AND_INT_LIT16);
+    intLit16Opcodes.add(Opcode.OR_INT_LIT16);
+    intLit16Opcodes.add(Opcode.XOR_INT_LIT16);
+
+    opcodeLists.add(intOpcodes);
+    opcodeLists.add(longOpcodes);
+    opcodeLists.add(floatOpcodes);
+    opcodeLists.add(doubleOpcodes);
+    opcodeLists.add(int2addrOpcodes);
+    opcodeLists.add(long2addrOpcodes);
+    opcodeLists.add(float2addrOpcodes);
+    opcodeLists.add(double2addrOpcodes);
+    opcodeLists.add(intLit8Opcodes);
+    opcodeLists.add(intLit16Opcodes);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java
new file mode 100644
index 0000000..a28f5ba
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MBranchInsn;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class BranchShifter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int branchInsnIdx;
+    public int newTargetIdx;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(branchInsnIdx).append(" ");
+      builder.append(newTargetIdx);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      branchInsnIdx = Integer.parseInt(elements[2]);
+      newTargetIdx = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public BranchShifter() { }
+
+  public BranchShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MBranchInsn> branchInsns;
+
+  private void generateCachedBranchInsns(MutatableCode mutatableCode) {
+    if (branchInsns != null) {
+      return;
+    }
+
+    branchInsns = new ArrayList<MBranchInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MBranchInsn) {
+        branchInsns.add((MBranchInsn) mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    // Can't shift a branch if there's only one instruction in the method.
+    if (mutatableCode.getInstructionCount() == 1) {
+      Log.debug("Method contains only one instruction, skipping.");
+      return false;
+    }
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MBranchInsn) {
+        return true;
+      }
+    }
+
+    Log.debug("Method contains no branch instructions.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedBranchInsns(mutatableCode);
+
+    // Pick a random branching instruction.
+    int branchInsnIdx = rng.nextInt(branchInsns.size());
+    MBranchInsn branchInsn = branchInsns.get(branchInsnIdx);
+
+    // Get its original target, find its index.
+    MInsn oldTargetInsn = branchInsn.target;
+    int oldTargetInsnIdx = mutatableCode.getInstructionIndex(oldTargetInsn);
+
+    int newTargetIdx = oldTargetInsnIdx;
+
+    int delta = 0;
+
+    // Keep searching for a new index.
+    while (newTargetIdx == oldTargetInsnIdx) {
+      // Vary by +/- 2 instructions.
+      delta = 0;
+      while (delta == 0) {
+        delta = (rng.nextInt(5) - 2);
+      }
+
+      newTargetIdx = oldTargetInsnIdx + delta;
+
+      // Check the new index is legal.
+      if (newTargetIdx < 0) {
+        newTargetIdx = 0;
+      } else if (newTargetIdx >= mutatableCode.getInstructionCount()) {
+        newTargetIdx = mutatableCode.getInstructionCount() - 1;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.branchInsnIdx = branchInsnIdx;
+    mutation.newTargetIdx = newTargetIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedBranchInsns(mutatableCode);
+
+    MBranchInsn branchInsn = branchInsns.get(mutation.branchInsnIdx);
+
+    // Get the new target.
+    MInsn newTargetInsn = mutatableCode.getInstructionAt(mutation.newTargetIdx);
+
+    // Set the new target.
+    branchInsn.target = newTargetInsn;
+
+    Log.info("Shifted the target of " + branchInsn + " to point to " + newTargetInsn);
+
+    stats.incrementStat("Shifted branch target");
+
+    // Clear cache.
+    branchInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java
new file mode 100644
index 0000000..dc60e79
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class CmpBiasChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int cmpBiasInsnIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(cmpBiasInsnIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      cmpBiasInsnIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public CmpBiasChanger() { }
+
+  public CmpBiasChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> cmpBiasInsns = null;
+
+  private void generateCachedCmpBiasInsns(MutatableCode mutatableCode) {
+    if (cmpBiasInsns != null) {
+      return;
+    }
+
+    cmpBiasInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isCmpBiasOperation(mInsn)) {
+        cmpBiasInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isCmpBiasOperation(mInsn)) {
+        return true;
+      }
+    }
+
+    Log.debug("No cmp-with-bias operations in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedCmpBiasInsns(mutatableCode);
+
+    int cmpBiasInsnIdx = rng.nextInt(cmpBiasInsns.size());
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.cmpBiasInsnIdx = cmpBiasInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedCmpBiasInsns(mutatableCode);
+
+    MInsn cmpBiasInsn = cmpBiasInsns.get(mutation.cmpBiasInsnIdx);
+
+    String oldInsnString = cmpBiasInsn.toString();
+
+    Opcode newOpcode = getLegalDifferentOpcode(cmpBiasInsn);
+
+    cmpBiasInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
+
+    Log.info("Changed " + oldInsnString + " to " + cmpBiasInsn);
+
+    stats.incrementStat("Changed comparison bias");
+
+    // Clear cache.
+    cmpBiasInsns = null;
+  }
+
+  private Opcode getLegalDifferentOpcode(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (opcode == Opcode.CMPG_DOUBLE) {
+      return Opcode.CMPL_DOUBLE;
+    }
+    if (opcode == Opcode.CMPL_DOUBLE) {
+      return Opcode.CMPG_DOUBLE;
+    }
+    if (opcode == Opcode.CMPG_FLOAT) {
+      return Opcode.CMPL_FLOAT;
+    }
+    return Opcode.CMPG_FLOAT;
+  }
+
+  private boolean isCmpBiasOperation(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.CMPL_FLOAT, Opcode.CMPG_DOUBLE)) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java
new file mode 100644
index 0000000..be566ad
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.Options;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * The base class for all classes that can mutate methods.
+ */
+public abstract class CodeMutator {
+  /**
+   * The RNG, passed in by the Program that initialised us.
+   */
+  protected Random rng;
+
+  /**
+   * Used to track which mutations happen.
+   */
+  protected MutationStats stats;
+
+  /**
+   * Used to track mutations that have been applied so far.
+   */
+  protected List<Mutation> mutations;
+
+  /**
+   * The chance, out of 100, that this mutator actually mutates the the program
+   * when asked to by the Program. The default is 50% chance, but each mutator that
+   * extends CodeMutator should its own default.
+   */
+  protected int likelihood = 50;
+
+  /**
+   * This constructor is only intended for use in MutationRecorder...
+   */
+  public CodeMutator() {
+
+  }
+
+  /**
+   * Constructor that all subclasses must call...
+   *
+   * @param rng The RNG that the Program created.
+   */
+  public CodeMutator(Random rng, MutationStats stats, List<Mutation> mutations) {
+    this.rng = rng;
+    this.stats = stats;
+    this.mutations = mutations;
+
+    String name = this.getClass().getSimpleName().toLowerCase();
+
+    if (Options.mutationLikelihoods.containsKey(name)) {
+      likelihood = Options.mutationLikelihoods.get(name);
+      Log.info("Set mutation likelihood to " + likelihood
+          + "% for " + this.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * When the Program picks a particular mutator to mutate the code, it calls
+   * this function, that determines if the mutator will actually mutate the code.
+   * If so, it then calls the mutationFunction() method, that every subclass CodeMutator
+   * is expected to implement to perform its mutation.
+   *
+   * @return If mutation took place.
+   */
+  public boolean attemptToMutate(MutatableCode mutatableCode) {
+    if (shouldMutate(mutatableCode)) {
+      generateAndApplyMutation(mutatableCode);
+      return true;
+    }
+    Log.info("Skipping mutation.");
+    return false;
+  }
+
+  public void forceMutate(Mutation mutation) {
+    Log.info("Forcing mutation.");
+    applyMutation(mutation);
+  }
+
+  public boolean canBeTriggered() {
+    return (likelihood > 0);
+  }
+
+  /**
+   * Randomly determine if the mutator will actually mutate a method, based on its
+   * provided likelihood of mutation.
+   *
+   * @return If the method should be mutated.
+   */
+  private boolean shouldMutate(MutatableCode mutatableCode) {
+    return ((rng.nextInt(100) < likelihood) && canMutate(mutatableCode));
+  }
+
+  private void generateAndApplyMutation(MutatableCode mutatableCode) {
+    Mutation mutation = generateMutation(mutatableCode);
+    // Always save the mutation.
+    mutations.add(mutation);
+    applyMutation(mutation);
+  }
+
+  /**
+   * A CodeMutator must override this method if there is any reason why could not mutate
+   * a particular method, and return false in that case.
+   */
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    return true;
+  }
+
+  protected abstract Mutation generateMutation(MutatableCode mutatableCode);
+
+  protected abstract void applyMutation(Mutation uncastMutation);
+
+  public abstract Mutation getNewMutation();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java
new file mode 100644
index 0000000..22f04e8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsConst;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ConstantValueChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int constInsnIdx;
+    public long newConstant;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(constInsnIdx).append(" ");
+      builder.append(newConstant);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      constInsnIdx = Integer.parseInt(elements[2]);
+      newConstant = Long.parseLong(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ConstantValueChanger() { }
+
+  public ConstantValueChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 70;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> constInsns = null;
+
+  private void generateCachedConstInsns(MutatableCode mutatableCode) {
+    if (constInsns != null) {
+      return;
+    }
+
+    constInsns = new ArrayList<MInsn>();
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsConst) {
+        constInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsConst) {
+        return true;
+      }
+    }
+
+    Log.debug("Method contains no const instructions.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedConstInsns(mutatableCode);
+
+    // Pick a random const instruction.
+    int constInsnIdx = rng.nextInt(constInsns.size());
+    MInsn constInsn = constInsns.get(constInsnIdx);
+
+    // Get the constant.
+    long oldConstant = ((ContainsConst)constInsn.insn.info.format).getConst(constInsn.insn);
+
+    long newConstant = oldConstant;
+
+    // Make a new constant.
+    while (newConstant == oldConstant) {
+      newConstant = rng.nextLong()
+          % ((ContainsConst)constInsn.insn.info.format).getConstRange();
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.constInsnIdx = constInsnIdx;
+    mutation.newConstant = newConstant;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedConstInsns(mutatableCode);
+
+    MInsn constInsn = constInsns.get(mutation.constInsnIdx);
+
+    long oldConstant = ((ContainsConst)constInsn.insn.info.format).getConst(constInsn.insn);
+
+    Log.info("Changed constant value #" + oldConstant + " to #" + mutation.newConstant
+        + " in " + constInsn);
+
+    stats.incrementStat("Changed constant value");
+
+    // Set the new constant.
+    ((ContainsConst)constInsn.insn.info.format).setConst(constInsn.insn, mutation.newConstant);
+
+    // Clear cache.
+    constInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java
new file mode 100644
index 0000000..7fdf304
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ConversionRepeater extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int conversionInsnIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(conversionInsnIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      conversionInsnIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ConversionRepeater() { }
+
+  public ConversionRepeater(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 50;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> conversionInsns = null;
+
+  private void generateCachedConversionInsns(MutatableCode mutatableCode) {
+    if (conversionInsns != null) {
+      return;
+    }
+
+    conversionInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isConversionInstruction(mInsn)) {
+        conversionInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isConversionInstruction(mInsn)) {
+        return true;
+      }
+    }
+
+    Log.debug("No conversion operations in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedConversionInsns(mutatableCode);
+    int conversionInsnIdx = rng.nextInt(conversionInsns.size());
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.conversionInsnIdx = conversionInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedConversionInsns(mutatableCode);
+
+    MInsn originalInsn = conversionInsns.get(mutation.conversionInsnIdx);
+
+    // We want to create two new instructions:
+    // [original conversion] eg float-to-int
+    // NEW: [there] eg int-to-float (with vregs of first inst swapped)
+    // NEW: [back] eg float-to-int
+
+    // Create the "there" instruction.
+    MInsn newInsnThere = originalInsn.clone();
+
+    // Flip the opcode.
+    Opcode oppositeOpcode = null;
+    switch (newInsnThere.insn.info.opcode) {
+      case INT_TO_LONG:
+        oppositeOpcode = Opcode.LONG_TO_INT;
+        break;
+      case INT_TO_FLOAT:
+        oppositeOpcode = Opcode.FLOAT_TO_INT;
+        break;
+      case INT_TO_DOUBLE:
+        oppositeOpcode = Opcode.DOUBLE_TO_INT;
+        break;
+      case LONG_TO_INT:
+        oppositeOpcode = Opcode.INT_TO_LONG;
+        break;
+      case LONG_TO_FLOAT:
+        oppositeOpcode = Opcode.FLOAT_TO_LONG;
+        break;
+      case LONG_TO_DOUBLE:
+        oppositeOpcode = Opcode.DOUBLE_TO_LONG;
+        break;
+      case FLOAT_TO_INT:
+        oppositeOpcode = Opcode.INT_TO_FLOAT;
+        break;
+      case FLOAT_TO_LONG:
+        oppositeOpcode = Opcode.LONG_TO_FLOAT;
+        break;
+      case FLOAT_TO_DOUBLE:
+        oppositeOpcode = Opcode.DOUBLE_TO_FLOAT;
+        break;
+      case DOUBLE_TO_INT:
+        oppositeOpcode = Opcode.INT_TO_DOUBLE;
+        break;
+      case DOUBLE_TO_LONG:
+        oppositeOpcode = Opcode.LONG_TO_DOUBLE;
+        break;
+      case DOUBLE_TO_FLOAT:
+        oppositeOpcode = Opcode.FLOAT_TO_DOUBLE;
+        break;
+      default:
+        Log.errorAndQuit(
+            "Trying to repeat the conversion in an insn that is not a conversion insn.");
+    }
+    newInsnThere.insn.info = Instruction.getOpcodeInfo(oppositeOpcode);
+
+    // Swap the vregs.
+    long tempReg = newInsnThere.insn.vregA;
+    newInsnThere.insn.vregA = newInsnThere.insn.vregB;
+    newInsnThere.insn.vregB = tempReg;
+
+    // Create the "back" instruction.
+    MInsn newInsnBack = originalInsn.clone();
+
+    // Get the index into the MutatableCode's mInsns list for this insn.
+    int originalInsnIdx = mutatableCode.getInstructionIndex(originalInsn);
+
+    // Insert the new instructions.
+    mutatableCode.insertInstructionAfter(newInsnThere, originalInsnIdx);
+    mutatableCode.insertInstructionAfter(newInsnBack, originalInsnIdx + 1);
+
+    Log.info("Performing conversion repetition for " + originalInsn);
+
+    stats.incrementStat("Repeating conversion");
+
+    // Clear the cache.
+    conversionInsns = null;
+  }
+
+  private boolean isConversionInstruction(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.INT_TO_LONG, Opcode.DOUBLE_TO_FLOAT)) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java
new file mode 100644
index 0000000..0849d12
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.EncodedField;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class FieldFlagChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int fieldInsnIdx;
+    public boolean setVolatile;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(fieldInsnIdx).append(" ");
+      builder.append(setVolatile);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      fieldInsnIdx = Integer.parseInt(elements[2]);
+      setVolatile = Boolean.parseBoolean(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public FieldFlagChanger() { }
+
+  public FieldFlagChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> fieldInsns = null;
+
+  private void generateCachedFieldInsns(MutatableCode mutatableCode) {
+    if (fieldInsns != null) {
+      return;
+    }
+
+    fieldInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) {
+        fieldInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) {
+        return true;
+      }
+    }
+
+    Log.debug("No field instructions in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedFieldInsns(mutatableCode);
+
+    int fieldInsnIdx = rng.nextInt(fieldInsns.size());
+
+    Instruction insn = fieldInsns.get(fieldInsnIdx).insn;
+    ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+    int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+    EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx);
+
+    boolean setVolatile = false;
+    if (!encodedField.isVolatile()) {
+      setVolatile = true;
+    }
+    // TODO: Flip more flags?
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.fieldInsnIdx = fieldInsnIdx;
+    mutation.setVolatile = setVolatile;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedFieldInsns(mutatableCode);
+
+    Instruction insn = fieldInsns.get(mutation.fieldInsnIdx).insn;
+    ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+    int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+    EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx);
+
+    if (mutation.setVolatile) {
+      encodedField.setVolatile(true);
+      Log.info("Set field idx " + fieldIdx + " as volatile");
+    } else {
+      encodedField.setVolatile(false);
+      Log.info("Set field idx " + fieldIdx + " as not volatile");
+    }
+
+    stats.incrementStat("Changed volatility of field");
+
+    // Clear cache.
+    fieldInsns = null;
+  }
+
+  private boolean isFileDefinedFieldInstruction(MInsn mInsn, MutatableCode mutatableCode) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.SPUT_SHORT)) {
+      Instruction insn = mInsn.insn;
+      ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+      int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+      if (mutatableCode.program.getEncodedField(fieldIdx) != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java
new file mode 100644
index 0000000..8ffa4c5
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MInsnWithData;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionDeleter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insnToDeleteIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(insnToDeleteIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insnToDeleteIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public InstructionDeleter() { }
+
+  public InstructionDeleter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.getInstructionCount() < 4) {
+      // TODO: Make this more sophisticated - right now this is to avoid problems with
+      // a method that has 3 instructions: fill-array-data; return-void; <data for fill-array-data>
+      Log.debug("Cannot delete insns in a method with only a few.");
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Pick an instruction at random...
+    int insnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insnToDeleteIdx = insnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn toBeDeleted =
+        mutatableCode.getInstructionAt(mutation.insnToDeleteIdx);
+
+    Log.info("Deleting " + toBeDeleted);
+
+    stats.incrementStat("Deleted instruction");
+
+    // Delete the instruction.
+    mutatableCode.deleteInstruction(mutation.insnToDeleteIdx);
+
+    // If we delete a with-data insn, we should delete the associated data insn as well.
+    if (toBeDeleted instanceof MInsnWithData) {
+      Log.info(toBeDeleted + " had associated data, so the data insn was deleted.");
+      // Get the data instruction.
+      MInsn dataInsn =
+          ((MInsnWithData)toBeDeleted).dataTarget;
+      mutatableCode.deleteInstruction(dataInsn);
+      stats.incrementStat("Deleted a with-data insn's data");
+    }
+    // If we delete a data insn, we should delete the associated with-data insn as well.
+    if (toBeDeleted.insn.justRaw) {
+      // .justRaw implies that this is a data insn.
+      Log.info(toBeDeleted
+          + " was data, so the associated with-data insn was deleted.");
+
+      // First, find the with-data insn that is pointing to this insn.
+      MInsn withDataInsn = null;
+      for (MInsn mInsn : mutatableCode.getInstructions()) {
+        if (mInsn instanceof MInsnWithData) {
+          if (((MInsnWithData)mInsn).dataTarget == toBeDeleted) {
+            withDataInsn = mInsn;
+            break;
+          }
+        }
+      }
+
+      // Now delete the with-data insn.
+      if (withDataInsn != null) {
+        mutatableCode.deleteInstruction(withDataInsn);
+        stats.incrementStat("Deleted a data insn's with-data insn");
+      } else {
+        Log.errorAndQuit("Tried to delete a data insn, "
+            + "but it didn't have any with-data insn pointing at it?");
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java
new file mode 100644
index 0000000..4917056
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionDuplicator extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insnToDuplicateIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(insnToDuplicateIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insnToDuplicateIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public InstructionDuplicator() { }
+
+  public InstructionDuplicator(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 80;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    boolean foundInsn = false;
+    int insnIdx = 0;
+
+    while (!foundInsn) {
+      // Pick an instruction at random...
+      insnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn oldInsn = mutatableCode.getInstructionAt(insnIdx);
+      foundInsn = true;
+      Opcode opcode = oldInsn.insn.info.opcode;
+      // ...check it's a legal instruction to duplicate.
+      if (opcode == Opcode.SPARSE_SWITCH || opcode == Opcode.PACKED_SWITCH
+          || opcode == Opcode.FILL_ARRAY_DATA || oldInsn.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insnToDuplicateIdx = insnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn oldInsn = mutatableCode.getInstructionAt(mutation.insnToDuplicateIdx);
+
+    MInsn newInsn = oldInsn.clone();
+
+    Log.info("Duplicating " + oldInsn);
+
+    stats.incrementStat("Duplicated instruction");
+
+    mutatableCode.insertInstructionAt(newInsn, mutation.insnToDuplicateIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java
new file mode 100644
index 0000000..17ea939
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionSwapper extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int swapInsnIdx;
+    public int swapWithInsnIdx;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(swapInsnIdx).append(" ");
+      builder.append(swapWithInsnIdx);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      swapInsnIdx = Integer.parseInt(elements[2]);
+      swapWithInsnIdx = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public InstructionSwapper() { }
+
+  public InstructionSwapper(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 80;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.getInstructionCount() == 1) {
+      // Cannot swap one instruction.
+      Log.debug("Cannot swap insns in a method with only one.");
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    int swapInsnIdx = 0;
+    int swapWithInsnIdx = 0;
+
+    boolean foundFirstInsn = false;
+    boolean foundSecondInsn = false;
+
+    while (!foundFirstInsn || !foundSecondInsn) {
+      // Look for the first insn.
+      while (!foundFirstInsn) {
+        swapInsnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+        MInsn toBeSwapped = mutatableCode.getInstructionAt(swapInsnIdx);
+        foundFirstInsn = true;
+        if (toBeSwapped.insn.justRaw) {
+          foundFirstInsn = false;
+        }
+      }
+
+      // Look for the second insn.
+      int secondInsnAttempts = 0;
+      while (!foundSecondInsn) {
+        int delta = rng.nextInt(5) - 1;
+
+        if (delta == 0) {
+          continue;
+        }
+
+        swapWithInsnIdx = swapInsnIdx + delta;
+        foundSecondInsn = true;
+
+        // Check insn is in valid range.
+        if (swapWithInsnIdx < 0) {
+          foundSecondInsn = false;
+        } else if (swapWithInsnIdx >= mutatableCode.getInstructionCount()) {
+          foundSecondInsn = false;
+        }
+
+        // Finally, check if we're swapping with a raw insn.
+        if (foundSecondInsn) {
+          if (mutatableCode.getInstructionAt(swapWithInsnIdx).insn.justRaw) {
+            foundSecondInsn = false;
+          }
+        }
+
+        // If we've checked 10 times for an insn to swap with,
+        // and still found nothing, then try a new first insn.
+        if (!foundSecondInsn) {
+          secondInsnAttempts++;
+          if (secondInsnAttempts == 10) {
+            foundFirstInsn = false;
+            break;
+          }
+        }
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.swapInsnIdx = swapInsnIdx;
+    mutation.swapWithInsnIdx = swapWithInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn toBeSwapped = mutatableCode.getInstructionAt(mutation.swapInsnIdx);
+    MInsn swappedWith = mutatableCode.getInstructionAt(mutation.swapWithInsnIdx);
+
+    Log.info("Swapping " + toBeSwapped + " with " + swappedWith);
+
+    stats.incrementStat("Swapped two instructions");
+
+    mutatableCode.swapInstructionsByIndex(mutation.swapInsnIdx, mutation.swapWithInsnIdx);
+
+    Log.info("Now " + swappedWith + " is swapped with " + toBeSwapped);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java
new file mode 100644
index 0000000..88a2f9a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Instruction.InvokeFormatInfo;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class NewMethodCaller extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public enum InvokeType {
+      VIRTUAL,
+      DIRECT,
+      SUPER,
+      STATIC,
+      INTERFACE
+    }
+
+    public int insertionIdx;
+    public InvokeType invokeType;
+    public String className;
+    public String methodName;
+    public String signature;
+    public int numArgs;
+    public int[] args;
+
+    @Override
+    public String getString() {
+      StringBuilder argsString = new StringBuilder();
+      for (int i = 0; i < numArgs; i++) {
+        argsString.append(args[i]);
+        if (i < (numArgs - 1)) {
+          argsString.append(" ");
+        }
+      }
+      String result = String.format("%d %d %s %s %s %d %s",
+          insertionIdx,
+          invokeType.ordinal(),
+          className,
+          methodName,
+          signature,
+          numArgs,
+          argsString);
+      return result;
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insertionIdx = Integer.parseInt(elements[2]);
+      invokeType = InvokeType.values()[Integer.parseInt(elements[3])];
+      className = elements[4];
+      methodName = elements[5];
+      signature = elements[6];
+      numArgs = Integer.parseInt(elements[7]);
+      args = new int[numArgs];
+      for (int i = 0; i < numArgs; i++) {
+        args[i] = Integer.parseInt(elements[8 + i]);
+      }
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public NewMethodCaller() { }
+
+  public NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 10;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find the insertion point.
+    int insertionIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insertionPoint =
+          mutatableCode.getInstructionAt(insertionIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insertionPoint.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insertionIdx = insertionIdx;
+
+    // TODO: Right now this mutator can only insert calls to System.gc() Add more!
+
+    mutation.invokeType = AssociatedMutation.InvokeType.STATIC;
+    mutation.className = "Ljava/lang/System;";
+    mutation.methodName = "gc";
+    mutation.signature = "()V";
+    mutation.numArgs = 0;
+
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn newInsn = new MInsn();
+    newInsn.insn = new Instruction();
+
+    switch (mutation.invokeType) {
+      case VIRTUAL:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL);
+        break;
+      case DIRECT:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_DIRECT);
+        break;
+      case SUPER:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_SUPER);
+        break;
+      case STATIC:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_STATIC);
+        break;
+      case INTERFACE:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_INTERFACE);
+        break;
+      default:
+    }
+
+    // TODO: Handle more than just static invokes.
+
+    int methodIdx = mutatableCode.program.getNewItemCreator()
+        .findOrCreateMethodId(mutation.className,
+            mutation.methodName, mutation.signature);
+
+    newInsn.insn.vregB = methodIdx;
+    newInsn.insn.invokeFormatInfo = new InvokeFormatInfo();
+
+    // TODO: More field population, when we call methods that take arguments.
+
+    MInsn insertionPoint =
+        mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+    Log.info(String.format("Called new method %s %s %s, inserting at %s",
+        mutation.className, mutation.methodName, mutation.signature, insertionPoint));
+
+    stats.incrementStat("Called new method");
+
+    mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java
new file mode 100644
index 0000000..b4c9d7b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class NonsenseStringPrinter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insertionIdx;
+    public String nonsenseString;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(insertionIdx).append(" ");
+      builder.append(nonsenseString);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insertionIdx = Integer.parseInt(elements[2]);
+      nonsenseString = elements[3];
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public NonsenseStringPrinter() { }
+
+  public NonsenseStringPrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 10;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find the insertion point
+    int insertionIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insertionPoint =
+          mutatableCode.getInstructionAt(insertionIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insertionPoint.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insertionIdx = insertionIdx;
+    mutation.nonsenseString = getRandomString();
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
+        "Ljava/lang/System;",
+        "Ljava/io/PrintStream;",
+        "out");
+    int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
+        "Ljava/io/PrintStream;",
+        "print",
+        "(Ljava/lang/String;)V");
+    int nonsenseStringIdx = mutatableCode.program.getNewItemCreator().findOrCreateString(
+        mutation.nonsenseString);
+
+    MInsn insertionPoint = mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+    mutatableCode.allocateTemporaryVRegs(2);
+
+    int streamRegister = mutatableCode.getTemporaryVReg(0);
+    int stringRegister = mutatableCode.getTemporaryVReg(1);
+
+    // Load into string and stream into the temporary registers.
+    // then call print(stream, string)
+    MInsn constStringInsn = new MInsn();
+    constStringInsn.insn = new Instruction();
+    constStringInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_STRING);
+    constStringInsn.insn.vregB = nonsenseStringIdx;
+    constStringInsn.insn.vregA = stringRegister;
+
+    MInsn streamLoadInsn = new MInsn();
+    streamLoadInsn.insn = new Instruction();
+    streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
+    streamLoadInsn.insn.vregB = outFieldIdx;
+    streamLoadInsn.insn.vregA = streamRegister;
+
+    MInsn invokeInsn = new MInsn();
+    invokeInsn.insn = new Instruction();
+    invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
+    invokeInsn.insn.vregA = 2;
+    invokeInsn.insn.vregB = printMethodIdx;
+    invokeInsn.insn.vregC = streamRegister;
+
+    Log.info(String.format("Printing nonsense string '%s', inserting at %s",
+        mutation.nonsenseString, insertionPoint));
+
+    stats.incrementStat("Printed nonsense string");
+
+    mutatableCode.insertInstructionAt(invokeInsn, mutation.insertionIdx);
+    mutatableCode.insertInstructionAt(streamLoadInsn, mutation.insertionIdx);
+    mutatableCode.insertInstructionAt(constStringInsn, mutation.insertionIdx);
+
+    mutatableCode.finishedUsingTemporaryVRegs();
+  }
+
+  private String getRandomString() {
+    int size = rng.nextInt(10);
+    int start = (int) 'A';
+    int end = (int) 'Z';
+    StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < size; i++) {
+      builder.append((char) (rng.nextInt((end + 1) - start) + start));
+    }
+    return builder.toString();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java
new file mode 100644
index 0000000..cae5dc1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class PoolIndexChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int poolIndexInsnIdx;
+    public int newPoolIndex;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(poolIndexInsnIdx).append(" ");
+      builder.append(newPoolIndex);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      poolIndexInsnIdx = Integer.parseInt(elements[2]);
+      newPoolIndex = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public PoolIndexChanger() { }
+
+  public PoolIndexChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> poolIndexInsns = null;
+
+  private void generateCachedPoolIndexInsns(MutatableCode mutatableCode) {
+    if (poolIndexInsns != null) {
+      return;
+    }
+
+    poolIndexInsns = new ArrayList<MInsn>();
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsPoolIndex) {
+        poolIndexInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    // Remember what kinds of pool indices we see.
+    List<PoolIndexKind> seenKinds = new ArrayList<PoolIndexKind>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsPoolIndex) {
+
+        ContainsPoolIndex containsPoolIndex =
+            (ContainsPoolIndex)mInsn.insn.info.format;
+
+        PoolIndexKind newPoolIndexKind =
+            containsPoolIndex.getPoolIndexKind(mInsn.insn.info);
+
+        seenKinds.add(newPoolIndexKind);
+      }
+    }
+
+    // Now check that there exists a kind such that the max pool index for
+    // the kind is greater than 1 (i.e., something can be changed)
+    if (!seenKinds.isEmpty()) {
+
+      for (PoolIndexKind kind : seenKinds) {
+        int numPoolIndices = mutatableCode.program.getTotalPoolIndicesByKind(kind);
+        if (numPoolIndices > 1) {
+          return true;
+        }
+      }
+
+      Log.debug("Method does not contain any insns that index into a const pool size > 1");
+      return false;
+    }
+
+    Log.debug("Method contains no instructions that index into the constant pool.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedPoolIndexInsns(mutatableCode);
+
+    int poolIndexInsnIdx = 0;
+    boolean found = false;
+
+    int oldPoolIndex = 0;
+    int newPoolIndex = 0;
+    int maxPoolIndex = 0;
+
+    // Pick a random instruction.
+    while (!found) {
+      poolIndexInsnIdx = rng.nextInt(poolIndexInsns.size());
+      MInsn poolIndexInsn = poolIndexInsns.get(poolIndexInsnIdx);
+
+      found = true;
+
+      ContainsPoolIndex containsPoolIndex =
+          (ContainsPoolIndex)poolIndexInsn.insn.info.format;
+
+      // Get the pool index.
+      oldPoolIndex = containsPoolIndex.getPoolIndex(poolIndexInsn.insn);
+      newPoolIndex = oldPoolIndex;
+
+      // Get the largest pool index available for the provided kind of pool index.
+      PoolIndexKind poolIndexKind =
+          containsPoolIndex.getPoolIndexKind(poolIndexInsn.insn.info);
+      maxPoolIndex = mutatableCode.program.getTotalPoolIndicesByKind(poolIndexKind);
+
+      if (maxPoolIndex <= 1) {
+        found = false;
+      }
+    }
+
+    // Get a new pool index.
+    while (newPoolIndex == oldPoolIndex) {
+      newPoolIndex = rng.nextInt(maxPoolIndex);
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.poolIndexInsnIdx = poolIndexInsnIdx;
+    mutation.newPoolIndex = newPoolIndex;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedPoolIndexInsns(mutatableCode);
+
+    MInsn poolIndexInsn = poolIndexInsns.get(mutation.poolIndexInsnIdx);
+
+    ContainsPoolIndex containsPoolIndex =
+        (ContainsPoolIndex) poolIndexInsn.insn.info.format;
+
+    int oldPoolIndex = containsPoolIndex.getPoolIndex(poolIndexInsn.insn);
+
+    Log.info("Changed pool index " + oldPoolIndex + " to " + mutation.newPoolIndex
+        + " in " + poolIndexInsn);
+
+    stats.incrementStat("Changed constant pool index");
+
+    // Set the new pool index.
+    containsPoolIndex.setPoolIndex(poolIndexInsn.insn, mutation.newPoolIndex);
+
+    // Clear cache.
+    poolIndexInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java
new file mode 100644
index 0000000..ff43c7c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MBranchInsn;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+import dexfuzz.rawdex.formats.AbstractFormat;
+import dexfuzz.rawdex.formats.ContainsConst;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+
+import java.util.List;
+import java.util.Random;
+
+public class RandomInstructionGenerator extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insertionIdx;
+    public int newOpcode;
+    public boolean hasConst;
+    public long constValue;
+    public boolean hasPoolIndex;
+    public int poolIndexValue;
+    public boolean hasVregs;
+    public int vregCount;
+    public int vregA;
+    public int vregB;
+    public int vregC;
+    public int branchTargetIdx;
+
+    @Override
+    public String getString() {
+      String result = String.format("%d %d %s %d %s %d %s %d %d %d %d %d",
+          insertionIdx,
+          newOpcode,
+          (hasConst ? "T" : "F"),
+          constValue,
+          (hasPoolIndex ? "T" : "F"),
+          poolIndexValue,
+          (hasVregs ? "T" : "F"),
+          vregCount,
+          vregA,
+          vregB,
+          vregC,
+          branchTargetIdx
+          );
+      return result;
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insertionIdx = Integer.parseInt(elements[2]);
+      newOpcode = Integer.parseInt(elements[3]);
+      hasConst = (elements[4].equals("T"));
+      constValue = Long.parseLong(elements[5]);
+      hasPoolIndex = (elements[6].equals("T"));
+      poolIndexValue = Integer.parseInt(elements[7]);
+      hasVregs = (elements[8].equals("T"));
+      vregCount = Integer.parseInt(elements[9]);
+      vregA = Integer.parseInt(elements[10]);
+      vregB = Integer.parseInt(elements[11]);
+      vregC = Integer.parseInt(elements[12]);
+      branchTargetIdx = Integer.parseInt(elements[13]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public RandomInstructionGenerator() { }
+
+  public RandomInstructionGenerator(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find the insertion point.
+    int insertionIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insertionPoint =
+          mutatableCode.getInstructionAt(insertionIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insertionPoint.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    Opcode newOpcode = null;
+    int opcodeCount = Opcode.values().length;
+    boolean foundOpcode = false;
+
+    while (!foundOpcode) {
+      newOpcode = Opcode.values()[rng.nextInt(opcodeCount)];
+      foundOpcode = true;
+      if (Opcode.isBetween(newOpcode, Opcode.FILLED_NEW_ARRAY, Opcode.FILL_ARRAY_DATA)
+          || Opcode.isBetween(newOpcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)
+          || Opcode.isBetween(newOpcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
+          || Opcode.isBetween(newOpcode,
+              Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)
+              // Can never accept these instructions at compile time.
+              || Opcode.isBetween(newOpcode, Opcode.IGET_QUICK, Opcode.IPUT_SHORT_QUICK)
+              // Unused opcodes...
+              || Opcode.isBetween(newOpcode, Opcode.UNUSED_3E, Opcode.UNUSED_43)
+              || Opcode.isBetween(newOpcode, Opcode.UNUSED_79, Opcode.UNUSED_7A)
+              || Opcode.isBetween(newOpcode, Opcode.UNUSED_EF, Opcode.UNUSED_FF)) {
+        foundOpcode = false;
+      }
+    }
+
+    OpcodeInfo newOpcodeInfo = Instruction.getOpcodeInfo(newOpcode);
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insertionIdx = insertionIdx;
+    mutation.newOpcode = newOpcodeInfo.value;
+
+    AbstractFormat fmt = newOpcodeInfo.format;
+
+    if (fmt instanceof ContainsConst) {
+      mutation.hasConst = true;
+      mutation.constValue = rng.nextLong() % ((ContainsConst)fmt).getConstRange();
+    }
+    if (fmt instanceof ContainsPoolIndex) {
+      mutation.hasPoolIndex = true;
+      ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) fmt;
+      PoolIndexKind poolIndexKind = containsPoolIndex.getPoolIndexKind(newOpcodeInfo);
+      int maxPoolIndex = mutatableCode.program.getTotalPoolIndicesByKind(poolIndexKind);
+      if (maxPoolIndex > 0) {
+        mutation.poolIndexValue = rng.nextInt(maxPoolIndex);
+      } else {
+        mutation.poolIndexValue = 0;
+      }
+    }
+    if (mutatableCode.registersSize == 0) {
+      mutatableCode.registersSize = 1;
+    }
+    if (fmt instanceof ContainsVRegs) {
+      mutation.hasVregs = true;
+      ContainsVRegs containsVregs = (ContainsVRegs) fmt;
+      mutation.vregCount = containsVregs.getVRegCount();
+      switch (mutation.vregCount) {
+        case 3:
+          mutation.vregC = rng.nextInt(mutatableCode.registersSize);
+          // fallthrough
+        case 2:
+          mutation.vregB = rng.nextInt(mutatableCode.registersSize);
+          // fallthrough
+        case 1:
+          mutation.vregA = rng.nextInt(mutatableCode.registersSize);
+          break;
+        default:
+          Log.errorAndQuit("Invalid number of vregs specified.");
+      }
+    }
+    // If we have some kind of branch, pick a random target.
+    if (Opcode.isBetween(newOpcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+        || Opcode.isBetween(newOpcode, Opcode.GOTO, Opcode.GOTO_32)) {
+      mutation.branchTargetIdx = rng.nextInt(mutatableCode.getInstructionCount());
+    }
+
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    Opcode newOpcode = Instruction.getOpcodeInfo(mutation.newOpcode).opcode;
+
+    boolean isBranch = false;
+    if (Opcode.isBetween(newOpcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+        || Opcode.isBetween(newOpcode, Opcode.GOTO, Opcode.GOTO_32)) {
+      isBranch = true;
+    }
+
+    MInsn newInsn = null;
+    if (!isBranch) {
+      newInsn = new MInsn();
+    } else {
+      newInsn = new MBranchInsn();
+    }
+    newInsn.insn = new Instruction();
+    newInsn.insn.info = Instruction.getOpcodeInfo(mutation.newOpcode);
+    AbstractFormat fmt = newInsn.insn.info.format;
+
+    if (mutation.hasConst) {
+      ContainsConst containsConst = (ContainsConst) fmt;
+      containsConst.setConst(newInsn.insn, mutation.constValue);
+    }
+    if (mutation.hasPoolIndex) {
+      ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) fmt;
+      containsPoolIndex.setPoolIndex(newInsn.insn, mutation.poolIndexValue);
+    }
+    if (mutation.hasVregs) {
+      switch (mutation.vregCount) {
+        case 3:
+          newInsn.insn.vregC = mutation.vregC;
+          // fallthrough
+        case 2:
+          newInsn.insn.vregB = mutation.vregB;
+          // fallthrough
+        case 1:
+          newInsn.insn.vregA = mutation.vregA;
+          break;
+        default:
+          Log.errorAndQuit("Invalid number of vregs specified.");
+      }
+    }
+
+    if (isBranch) {
+      // We have a branch instruction, point it at its target.
+      MBranchInsn newBranchInsn = (MBranchInsn) newInsn;
+      newBranchInsn.target = mutatableCode.getInstructionAt(mutation.branchTargetIdx);
+    }
+
+    MInsn insertionPoint =
+        mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+    Log.info("Generated random instruction: " + newInsn
+        + ", inserting at " + insertionPoint);
+
+    stats.incrementStat("Generated random instruction");
+
+    mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
+
+    // If we've generated a monitor insn, generate the matching opposing insn.
+    if (newInsn.insn.info.opcode == Opcode.MONITOR_ENTER) {
+      MInsn exitInsn = newInsn.clone();
+      exitInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MONITOR_EXIT);
+      mutatableCode.insertInstructionAfter(exitInsn, mutation.insertionIdx);
+      Log.info("Generated matching monitor-exit: " + exitInsn);
+    } else if (newInsn.insn.info.opcode == Opcode.MONITOR_EXIT) {
+      MInsn enterInsn = newInsn.clone();
+      enterInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MONITOR_ENTER);
+      mutatableCode.insertInstructionAt(enterInsn, mutation.insertionIdx);
+      Log.info("Generated matching monitor-enter: " + enterInsn);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java
new file mode 100644
index 0000000..6981034
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MSwitchInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class SwitchBranchShifter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int switchInsnIdx;
+    public int switchTargetIdx;
+    public int newTargetIdx;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(switchInsnIdx).append(" ");
+      builder.append(switchTargetIdx).append(" ");
+      builder.append(newTargetIdx);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      switchInsnIdx = Integer.parseInt(elements[2]);
+      switchTargetIdx = Integer.parseInt(elements[3]);
+      newTargetIdx = Integer.parseInt(elements[4]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public SwitchBranchShifter() { }
+
+  public SwitchBranchShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MSwitchInsn> switchInsns;
+
+  private void generateCachedSwitchInsns(MutatableCode mutatableCode) {
+    if (switchInsns != null) {
+      return;
+    }
+
+    switchInsns = new ArrayList<MSwitchInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        switchInsns.add((MSwitchInsn) mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        return true;
+      }
+    }
+
+    Log.debug("Method contains no switch instructions.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedSwitchInsns(mutatableCode);
+
+    // Pick a random switch instruction.
+    int switchInsnIdx = rng.nextInt(switchInsns.size());
+    MSwitchInsn switchInsn = switchInsns.get(switchInsnIdx);
+
+    // Pick a random one of its targets.
+    int switchTargetIdx = rng.nextInt(switchInsn.targets.size());
+
+    // Get the original target, find its index.
+    MInsn oldTargetInsn = switchInsn.targets.get(switchTargetIdx);
+    int oldTargetInsnIdx = mutatableCode.getInstructionIndex(oldTargetInsn);
+
+    int newTargetIdx = oldTargetInsnIdx;
+
+    int delta = 0;
+
+    // Keep searching for a new index.
+    while (newTargetIdx == oldTargetInsnIdx) {
+      // Vary by +/- 2 instructions.
+      delta = 0;
+      while (delta == 0) {
+        delta = (rng.nextInt(5) - 2);
+      }
+
+      newTargetIdx = oldTargetInsnIdx + delta;
+
+      // Check the new index is legal
+      if (newTargetIdx < 0) {
+        newTargetIdx = 0;
+      } else if (newTargetIdx >= mutatableCode.getInstructionCount()) {
+        newTargetIdx = mutatableCode.getInstructionCount() - 1;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.switchInsnIdx = switchInsnIdx;
+    mutation.switchTargetIdx = switchTargetIdx;
+    mutation.newTargetIdx = newTargetIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedSwitchInsns(mutatableCode);
+
+    MSwitchInsn switchInsn = switchInsns.get(mutation.switchInsnIdx);
+
+    // Get the new target.
+    MInsn newTargetInsn =
+        mutatableCode.getInstructionAt(mutation.newTargetIdx);
+
+    // Set the new target.
+    switchInsn.targets.remove(mutation.switchTargetIdx);
+    switchInsn.targets.add(mutation.switchTargetIdx, newTargetInsn);
+
+    Log.info("Shifted target #" + mutation.switchTargetIdx + " of " + switchInsn
+        + " to point to " + newTargetInsn);
+
+    stats.incrementStat("Shifted switch target");
+
+    // Clear cache.
+    switchInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
new file mode 100644
index 0000000..1bf6463
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MTryBlock;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class TryBlockShifter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int tryIdx;
+    public boolean shiftingTryBlock; // false => shifting handler
+    public boolean shiftingStart; // false => shifting end (try block only)
+    public boolean shiftingHandlerCatchall;
+    public int shiftingHandlerIdx;
+    public int newShiftedInsnIdx;
+
+    @Override
+    public String getString() {
+      String result = String.format("%d %s %s %s %d %d",
+          tryIdx,
+          (shiftingTryBlock ? "T" : "F"),
+          (shiftingStart ? "T" : "F"),
+          (shiftingHandlerCatchall ? "T" : "F"),
+          shiftingHandlerIdx,
+          newShiftedInsnIdx
+          );
+      return result;
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      tryIdx = Integer.parseInt(elements[2]);
+      shiftingTryBlock = elements[3].equals("T");
+      shiftingStart = elements[4].equals("T");
+      shiftingHandlerCatchall = elements[5].equals("T");
+      shiftingHandlerIdx = Integer.parseInt(elements[6]);
+      newShiftedInsnIdx = Integer.parseInt(elements[7]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public TryBlockShifter() { }
+
+  public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.triesSize > 0) {
+      return true;
+    }
+
+    Log.debug("Method contains no tries.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Pick a random try.
+    int tryIdx = rng.nextInt(mutatableCode.triesSize);
+    MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx);
+
+    boolean shiftingTryBlock = rng.nextBoolean();
+    boolean shiftingStart = false;
+    boolean shiftingHandlerCatchall = false;
+    int shiftingHandlerIdx = -1;
+    if (shiftingTryBlock) {
+      // We're shifting the boundaries of the try block
+      // determine if we shift the start or the end.
+      shiftingStart = rng.nextBoolean();
+    } else {
+      // We're shifting the start of a handler of the try block.
+      if (tryBlock.handlers.isEmpty()) {
+        // No handlers, so we MUST mutate the catchall
+        shiftingHandlerCatchall = true;
+      } else if (tryBlock.catchAllHandler != null) {
+        // There is a catchall handler, so potentially mutate it.
+        shiftingHandlerCatchall = rng.nextBoolean();
+      }
+      // If we're not going to shift the catchall handler, then
+      // pick an explicit handler to shift.
+      if (!shiftingHandlerCatchall) {
+        shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size());
+      }
+    }
+
+    // Get the original instruction wherever we're shifting.
+    MInsn oldInsn = null;
+    if (shiftingTryBlock && shiftingStart) {
+      oldInsn = tryBlock.startInsn;
+    } else if (shiftingTryBlock && !(shiftingStart)) {
+      oldInsn = tryBlock.endInsn;
+    } else if (!(shiftingTryBlock) && shiftingHandlerCatchall) {
+      oldInsn = tryBlock.catchAllHandler;
+    } else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall)
+        && (shiftingHandlerIdx != -1)) {
+      oldInsn = tryBlock.handlers.get(shiftingHandlerIdx);
+    } else {
+      Log.errorAndQuit("Faulty logic in TryBlockShifter!");
+    }
+
+    // Find the index of this instruction.
+    int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn);
+
+    int newInsnIdx = oldInsnIdx;
+
+    int delta = 0;
+
+    // Keep searching for a new index.
+    while (newInsnIdx == oldInsnIdx) {
+      // Vary by +/- 2 instructions.
+      delta = 0;
+      while (delta == 0) {
+        delta = (rng.nextInt(5) - 2);
+      }
+
+      newInsnIdx = oldInsnIdx + delta;
+
+      // Check the new index is legal.
+      if (newInsnIdx < 0) {
+        newInsnIdx = 0;
+      } else if (newInsnIdx >= mutatableCode.getInstructionCount()) {
+        newInsnIdx = mutatableCode.getInstructionCount() - 1;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.tryIdx = tryIdx;
+    mutation.shiftingTryBlock = shiftingTryBlock;
+    mutation.shiftingStart = shiftingStart;
+    mutation.shiftingHandlerCatchall = shiftingHandlerCatchall;
+    mutation.shiftingHandlerIdx = shiftingHandlerIdx;
+    mutation.newShiftedInsnIdx = newInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx);
+
+    MInsn newInsn =
+        mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx);
+
+    // Find the right mutatable instruction in try block, and point it at the new instruction.
+    if (mutation.shiftingTryBlock && mutation.shiftingStart) {
+      tryBlock.startInsn = newInsn;
+      Log.info("Shifted the start of try block #" + mutation.tryIdx
+          + " to be at " + newInsn);
+    } else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) {
+      tryBlock.endInsn = newInsn;
+      Log.info("Shifted the end of try block #" + mutation.tryIdx
+          + " to be at " + newInsn);
+    } else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) {
+      tryBlock.catchAllHandler = newInsn;
+      Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx
+          + " to be at " + newInsn);
+    } else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall)
+        && (mutation.shiftingHandlerIdx != -1)) {
+      tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn);
+      Log.info("Shifted handler #" + mutation.shiftingHandlerIdx
+          + " of try block #" + mutation.tryIdx + " to be at " + newInsn);
+    } else {
+      Log.errorAndQuit("faulty logic in TryBlockShifter");
+    }
+
+    stats.incrementStat("Shifted boundary in a try block");
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java
new file mode 100644
index 0000000..d685f7d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class VRegChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int vregInsnIdx;
+    public int mutatingVreg;
+    public int newVregValue;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(vregInsnIdx).append(" ");
+      builder.append(mutatingVreg).append(" ");
+      builder.append(newVregValue);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      vregInsnIdx = Integer.parseInt(elements[2]);
+      mutatingVreg = Integer.parseInt(elements[3]);
+      newVregValue = Integer.parseInt(elements[4]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public VRegChanger() { }
+
+  public VRegChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 60;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> vregInsns = null;
+
+  private void generateCachedVRegInsns(MutatableCode mutatableCode) {
+    if (vregInsns != null) {
+      return;
+    }
+
+    vregInsns = new ArrayList<MInsn>();
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsVRegs) {
+        vregInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.registersSize < 2) {
+      Log.debug("Impossible to change vregs in a method with fewer than 2 registers.");
+      return false;
+    }
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsVRegs) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedVRegInsns(mutatableCode);
+
+    // Pick a random vreg instruction.
+    int vregInsnIdx = rng.nextInt(vregInsns.size());
+    MInsn vregInsn = vregInsns.get(vregInsnIdx);
+
+    // Get the number of VRegs this instruction uses.
+    int numVregs = ((ContainsVRegs)vregInsn.insn.info.format).getVRegCount();
+
+    // Pick which vreg to mutate.
+    int mutatingVreg = rng.nextInt(numVregs);
+
+    // Find the old index.
+    int oldVregValue = 0;
+
+    switch (mutatingVreg) {
+      case 0:
+        oldVregValue = (int) vregInsn.insn.vregA;
+        break;
+      case 1:
+        oldVregValue = (int) vregInsn.insn.vregB;
+        break;
+      case 2:
+        oldVregValue = (int) vregInsn.insn.vregC;
+        break;
+      default:
+        Log.errorAndQuit("Invalid number of vregs reported by a Format.");
+    }
+
+    // Search for a new vreg value.
+    int newVregValue = oldVregValue;
+    while (newVregValue == oldVregValue) {
+      newVregValue = rng.nextInt(mutatableCode.registersSize);
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.vregInsnIdx = vregInsnIdx;
+    mutation.mutatingVreg = mutatingVreg;
+    mutation.newVregValue = newVregValue;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedVRegInsns(mutatableCode);
+
+    MInsn vregInsn = vregInsns.get(mutation.vregInsnIdx);
+
+    // Remember what the instruction used to look like.
+    String oldInsnString = vregInsn.toString();
+
+    int oldVregValue = 0;
+
+    String vregId = "A";
+    switch (mutation.mutatingVreg) {
+      case 0:
+        oldVregValue = (int) vregInsn.insn.vregA;
+        vregInsn.insn.vregA = (long) mutation.newVregValue;
+        break;
+      case 1:
+        oldVregValue = (int) vregInsn.insn.vregB;
+        vregInsn.insn.vregB = (long) mutation.newVregValue;
+        vregId = "B";
+        break;
+      case 2:
+        oldVregValue = (int) vregInsn.insn.vregC;
+        vregInsn.insn.vregC = (long) mutation.newVregValue;
+        vregId = "C";
+        break;
+      default:
+        Log.errorAndQuit("Invalid number of vregs specified in a VRegChanger mutation.");
+    }
+
+    Log.info("In " + oldInsnString + " changed v" + vregId + ": v" + oldVregValue
+        + " to v" + mutation.newVregValue);
+
+    stats.incrementStat("Changed a virtual register");
+
+    // Clear cache.
+    vregInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java
new file mode 100644
index 0000000..271aca3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class ValuePrinter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int printedOutputIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(printedOutputIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      printedOutputIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ValuePrinter() { }
+
+  public ValuePrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (getInstructionOutputType(mInsn) != OutputType.UNKNOWN) {
+        return true;
+      }
+    }
+
+    Log.debug("No instructions with legible output in method, skipping.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find an instruction whose output we wish to print.
+    int printedOutputIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      printedOutputIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insnOutputToPrint =
+          mutatableCode.getInstructionAt(printedOutputIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insnOutputToPrint.insn.justRaw) {
+        foundInsn = false;
+      }
+
+      if (getInstructionOutputType(insnOutputToPrint) == OutputType.UNKNOWN) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.printedOutputIdx = printedOutputIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn insnOutputToPrint =
+        mutatableCode.getInstructionAt(mutation.printedOutputIdx);
+
+    int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
+        "Ljava/lang/System;",
+        "Ljava/io/PrintStream;",
+        "out");
+
+    OutputType outputType = getInstructionOutputType(insnOutputToPrint);
+
+    if (outputType == OutputType.UNKNOWN) {
+      Log.errorAndQuit("Requested to print output of an instruction, whose output"
+          + " type is unknown.");
+    }
+    int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
+        "Ljava/io/PrintStream;",
+        "print",
+        outputType.getSignatureForPrintln());
+
+    boolean isWide = false;
+    boolean isRef = false;
+    if (outputType == OutputType.LONG || outputType == OutputType.DOUBLE) {
+      isWide = true;
+    }
+    if (outputType == OutputType.STRING) {
+      isRef = true;
+    }
+
+    // If we're printing a wide value, we need to allocate 3 registers!
+    if (isWide) {
+      mutatableCode.allocateTemporaryVRegs(3);
+    } else {
+      mutatableCode.allocateTemporaryVRegs(2);
+    }
+
+    int streamRegister = mutatableCode.getTemporaryVReg(0);
+    int valueRegister = mutatableCode.getTemporaryVReg(1);
+
+    // Copy the value we want to print to the 2nd temporary register
+    // Then load the out stream
+    // Then call print(out stream, value)
+
+    MInsn valueCopyInsn = new MInsn();
+    valueCopyInsn.insn = new Instruction();
+    if (isRef) {
+      valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
+    } else if (isWide) {
+      valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
+    } else {
+      valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
+    }
+    valueCopyInsn.insn.vregB = insnOutputToPrint.insn.vregA;
+    valueCopyInsn.insn.vregA = valueRegister;
+
+    MInsn streamLoadInsn = new MInsn();
+    streamLoadInsn.insn = new Instruction();
+    streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
+    streamLoadInsn.insn.vregB = outFieldIdx;
+    streamLoadInsn.insn.vregA = streamRegister;
+
+    MInsn invokeInsn = new MInsn();
+    invokeInsn.insn = new Instruction();
+    invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
+    if (isWide) {
+      invokeInsn.insn.vregA = 3;
+    } else {
+      invokeInsn.insn.vregA = 2;
+    }
+    invokeInsn.insn.vregB = printMethodIdx;
+    invokeInsn.insn.vregC = streamRegister;
+
+    Log.info(String.format("Printing output value of instruction %s", insnOutputToPrint));
+
+    stats.incrementStat("Printed output value");
+
+    mutatableCode.insertInstructionAfter(invokeInsn, mutation.printedOutputIdx);
+    mutatableCode.insertInstructionAfter(streamLoadInsn, mutation.printedOutputIdx);
+    mutatableCode.insertInstructionAfter(valueCopyInsn, mutation.printedOutputIdx);
+
+    mutatableCode.finishedUsingTemporaryVRegs();
+  }
+
+  private static enum OutputType {
+    STRING("(Ljava/lang/String;)V"),
+    BOOLEAN("(Z)V"),
+    BYTE("(B)V"),
+    CHAR("(C)V"),
+    SHORT("(S)V"),
+    INT("(I)V"),
+    LONG("(J)V"),
+    FLOAT("(F)V"),
+    DOUBLE("(D)V"),
+    UNKNOWN("UNKNOWN");
+
+    private String printingSignature;
+    private OutputType(String s) {
+      printingSignature = s;
+    }
+
+    public String getSignatureForPrintln() {
+      return printingSignature;
+    }
+  }
+
+  private OutputType getInstructionOutputType(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
+      return OutputType.STRING;
+    }
+    if (opcode == Opcode.IGET_BOOLEAN || opcode == Opcode.SGET_BOOLEAN) {
+      return OutputType.BOOLEAN;
+    }
+    if (opcode == Opcode.IGET_BYTE || opcode == Opcode.SGET_BYTE
+        || opcode == Opcode.INT_TO_BYTE) {
+      return OutputType.BYTE;
+    }
+    if (opcode == Opcode.IGET_CHAR || opcode == Opcode.SGET_CHAR
+        || opcode == Opcode.INT_TO_CHAR) {
+      return OutputType.CHAR;
+    }
+    if (opcode == Opcode.IGET_SHORT || opcode == Opcode.SGET_SHORT
+        || opcode == Opcode.INT_TO_SHORT) {
+      return OutputType.SHORT;
+    }
+    if (opcode == Opcode.NEG_INT || opcode == Opcode.NOT_INT
+        || opcode == Opcode.LONG_TO_INT || opcode == Opcode.FLOAT_TO_INT
+        || opcode == Opcode.DOUBLE_TO_INT
+        || Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT)
+        || Opcode.isBetween(opcode, Opcode.ADD_INT_2ADDR, Opcode.USHR_INT_2ADDR)
+        || Opcode.isBetween(opcode, Opcode.ADD_INT_LIT16, Opcode.USHR_INT_LIT8)) {
+      return OutputType.INT;
+    }
+    if (opcode == Opcode.NEG_LONG || opcode == Opcode.NOT_LONG
+        || opcode == Opcode.INT_TO_LONG || opcode == Opcode.FLOAT_TO_LONG
+        || opcode == Opcode.DOUBLE_TO_LONG
+        || Opcode.isBetween(opcode, Opcode.ADD_LONG, Opcode.USHR_LONG)
+        || Opcode.isBetween(opcode, Opcode.ADD_LONG_2ADDR, Opcode.USHR_LONG_2ADDR)) {
+      return OutputType.LONG;
+    }
+    if (opcode == Opcode.NEG_FLOAT
+        || opcode == Opcode.INT_TO_FLOAT || opcode == Opcode.LONG_TO_FLOAT
+        || opcode == Opcode.DOUBLE_TO_FLOAT
+        || Opcode.isBetween(opcode, Opcode.ADD_FLOAT, Opcode.REM_FLOAT)
+        || Opcode.isBetween(opcode, Opcode.ADD_FLOAT_2ADDR, Opcode.REM_FLOAT_2ADDR)) {
+      return OutputType.FLOAT;
+    }
+    if (opcode == Opcode.NEG_DOUBLE
+        || opcode == Opcode.INT_TO_DOUBLE || opcode == Opcode.LONG_TO_DOUBLE
+        || opcode == Opcode.FLOAT_TO_DOUBLE
+        || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE, Opcode.REM_DOUBLE)
+        || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE_2ADDR, Opcode.REM_DOUBLE_2ADDR)) {
+      return OutputType.DOUBLE;
+    }
+    return OutputType.UNKNOWN;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java
new file mode 100644
index 0000000..6bb2f96
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationElement implements RawDexObject {
+  public int nameIdx;
+  public EncodedValue value;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    nameIdx = file.readUleb128();
+    (value = new EncodedValue()).read(file);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(nameIdx);
+    value.write(file);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+      nameIdx++;
+    }
+    value.incrementIndex(kind, insertedIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java
new file mode 100644
index 0000000..40cf5e4
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationItem implements RawDexObject {
+  public int visibility;
+  public EncodedAnnotation annotation;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    visibility = file.readUnsignedByte();
+    (annotation = new EncodedAnnotation()).read(file);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeByte(visibility);
+    annotation.write(file);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    annotation.incrementIndex(kind, insertedIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java
new file mode 100644
index 0000000..b44cd18
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationOffItem implements RawDexObject {
+  public Offset annotationOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    annotationOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().tryToWriteOffset(annotationOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java
new file mode 100644
index 0000000..1e1c540
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetItem implements RawDexObject {
+  public int size;
+  public AnnotationOffItem[] entries;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUInt();
+    entries = new AnnotationOffItem[size];
+    for (int i = 0; i < size; i++) {
+      (entries[i] = new AnnotationOffItem()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(size);
+    for (AnnotationOffItem annotationOffItem : entries) {
+      annotationOffItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java
new file mode 100644
index 0000000..cb543de
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetRefItem implements RawDexObject {
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java
new file mode 100644
index 0000000..1d27053
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetRefList implements RawDexObject {
+  public int size;
+  public AnnotationSetRefItem[] list;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUInt();
+    list = new AnnotationSetRefItem[size];
+    for (int i = 0; i < size; i++) {
+      (list[i] = new AnnotationSetRefItem()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(size);
+    for (AnnotationSetRefItem annotationSetRefItem : list) {
+      annotationSetRefItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..8285472
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationsDirectoryItem implements RawDexObject {
+  public Offset classAnnotationsOff;
+  public int fieldsSize;
+  public int annotatedMethodsSize;
+  public int annotatedParametersSize;
+  public FieldAnnotation[] fieldAnnotations;
+  public MethodAnnotation[] methodAnnotations;
+  public ParameterAnnotation[] parameterAnnotations;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classAnnotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    fieldsSize = file.readUInt();
+    annotatedMethodsSize = file.readUInt();
+    annotatedParametersSize = file.readUInt();
+    if (fieldsSize != 0) {
+      fieldAnnotations = new FieldAnnotation[fieldsSize];
+      for (int i = 0; i < fieldsSize; i++) {
+        (fieldAnnotations[i] = new FieldAnnotation()).read(file);
+      }
+    }
+    if (annotatedMethodsSize != 0) {
+      methodAnnotations = new MethodAnnotation[annotatedMethodsSize];
+      for (int i = 0; i < annotatedMethodsSize; i++) {
+        (methodAnnotations[i] = new MethodAnnotation()).read(file);
+      }
+    }
+    if (annotatedParametersSize != 0) {
+      parameterAnnotations = new ParameterAnnotation[annotatedParametersSize];
+      for (int i = 0; i < annotatedParametersSize; i++) {
+        (parameterAnnotations[i] = new ParameterAnnotation()).read(file);
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.getOffsetTracker().tryToWriteOffset(classAnnotationsOff, file, false /* ULEB128 */);
+    file.writeUInt(fieldsSize);
+    file.writeUInt(annotatedMethodsSize);
+    file.writeUInt(annotatedParametersSize);
+    if (fieldAnnotations != null) {
+      for (FieldAnnotation fieldAnnotation : fieldAnnotations) {
+        fieldAnnotation.write(file);
+      }
+    }
+    if (methodAnnotations != null) {
+      for (MethodAnnotation methodAnnotation : methodAnnotations) {
+        methodAnnotation.write(file);
+      }
+    }
+    if (parameterAnnotations != null) {
+      for (ParameterAnnotation parameterAnnotation : parameterAnnotations) {
+        parameterAnnotation.write(file);
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (fieldAnnotations != null) {
+      for (FieldAnnotation fieldAnnotation : fieldAnnotations) {
+        fieldAnnotation.incrementIndex(kind, insertedIdx);
+      }
+    }
+    if (methodAnnotations != null) {
+      for (MethodAnnotation methodAnnotation : methodAnnotations) {
+        methodAnnotation.incrementIndex(kind, insertedIdx);
+      }
+    }
+    if (parameterAnnotations != null) {
+      for (ParameterAnnotation parameterAnnotation : parameterAnnotations) {
+        parameterAnnotation.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java
new file mode 100644
index 0000000..79564bc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ClassDataItem implements RawDexObject {
+  public int staticFieldsSize;
+  public int instanceFieldsSize;
+  public int directMethodsSize;
+  public int virtualMethodsSize;
+
+  public EncodedField[] staticFields;
+  public EncodedField[] instanceFields;
+  public EncodedMethod[] directMethods;
+  public EncodedMethod[] virtualMethods;
+
+  public static class MetaInfo {
+    public ClassDefItem classDefItem;
+  }
+
+  public MetaInfo meta = new MetaInfo();
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    staticFieldsSize = file.readUleb128();
+    instanceFieldsSize = file.readUleb128();
+    directMethodsSize = file.readUleb128();
+    virtualMethodsSize = file.readUleb128();
+
+    staticFields = new EncodedField[staticFieldsSize];
+    for (int i = 0; i < staticFieldsSize; i++) {
+      (staticFields[i] = new EncodedField()).read(file);
+    }
+    instanceFields = new EncodedField[instanceFieldsSize];
+    for (int i = 0; i < instanceFieldsSize; i++) {
+      (instanceFields[i] = new EncodedField()).read(file);
+    }
+    directMethods = new EncodedMethod[directMethodsSize];
+    for (int i = 0; i < directMethodsSize; i++) {
+      (directMethods[i] = new EncodedMethod()).read(file);
+    }
+    virtualMethods = new EncodedMethod[virtualMethodsSize];
+    for (int i = 0; i < virtualMethodsSize; i++) {
+      (virtualMethods[i] = new EncodedMethod()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUleb128(staticFieldsSize);
+    file.writeUleb128(instanceFieldsSize);
+    file.writeUleb128(directMethodsSize);
+    file.writeUleb128(virtualMethodsSize);
+    for (int i = 0; i < staticFieldsSize; i++) {
+      staticFields[i].write(file);
+    }
+    for (int i = 0; i < instanceFieldsSize; i++) {
+      instanceFields[i].write(file);
+    }
+    for (int i = 0; i < directMethodsSize; i++) {
+      directMethods[i].write(file);
+    }
+    for (int i = 0; i < virtualMethodsSize; i++) {
+      virtualMethods[i].write(file);
+    }
+  }
+
+  private void incrementEncodedFields(int insertedIdx, EncodedField[] fields) {
+    int fieldIdx = 0;
+    for (EncodedField field : fields) {
+      fieldIdx = field.fieldIdxDiff;
+      if (fieldIdx >= insertedIdx) {
+        field.fieldIdxDiff++;
+        // Only need to increment one, as all the others are diffed from the previous.
+        break;
+      }
+    }
+  }
+
+  private void incrementEncodedMethods(int insertedIdx, EncodedMethod[] methods) {
+    int methodIdx = 0;
+    for (EncodedMethod method : methods) {
+      methodIdx = method.methodIdxDiff;
+      if (methodIdx >= insertedIdx) {
+        method.methodIdxDiff++;
+        // Only need to increment one, as all the others are diffed from the previous.
+        break;
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.FIELD_ID) {
+      incrementEncodedFields(insertedIdx, staticFields);
+      incrementEncodedFields(insertedIdx, instanceFields);
+    }
+    if (kind == IndexUpdateKind.METHOD_ID) {
+      incrementEncodedMethods(insertedIdx, directMethods);
+      incrementEncodedMethods(insertedIdx, virtualMethods);
+    }
+  }
+
+  /**
+   * For a given field index, search this ClassDataItem for a definition of this field.
+   * @return null if the field isn't in this ClassDataItem.
+   */
+  public EncodedField getEncodedFieldWithIndex(int fieldIdx) {
+    int searchFieldIdx = 0;
+    for (EncodedField field : instanceFields) {
+      searchFieldIdx += field.fieldIdxDiff;
+      if (searchFieldIdx == fieldIdx) {
+        return field;
+      }
+    }
+    searchFieldIdx = 0;
+    for (EncodedField field : staticFields) {
+      searchFieldIdx += field.fieldIdxDiff;
+      if (searchFieldIdx == fieldIdx) {
+        return field;
+      }
+    }
+    return null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java
new file mode 100644
index 0000000..5e68d24
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ClassDefItem implements RawDexObject {
+  public static int data_size = 0x20;
+
+  public int classIdx;
+  public int accessFlags;
+  public int superclassIdx;
+  public Offset interfacesOff;
+  public int sourceFileIdx;
+  public Offset annotationsOff;
+  public Offset classDataOff;
+  public Offset staticValuesOff;
+
+  public static class MetaInfo {
+    public ClassDataItem classDataItem;
+  }
+
+  public MetaInfo meta = new MetaInfo();
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classIdx = file.readUInt();
+    accessFlags = file.readUInt();
+    superclassIdx = file.readUInt();
+    interfacesOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    sourceFileIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    classDataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    staticValuesOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(classIdx);
+    file.writeUInt(accessFlags);
+    file.writeUInt(superclassIdx);
+    file.getOffsetTracker().tryToWriteOffset(interfacesOff, file, false /* ULEB128 */);
+    file.writeUInt(sourceFileIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+    file.getOffsetTracker().tryToWriteOffset(classDataOff, file, false /* ULEB128 */);
+    file.getOffsetTracker().tryToWriteOffset(staticValuesOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+      classIdx++;
+    }
+    if (kind == IndexUpdateKind.TYPE_ID && superclassIdx >= insertedIdx) {
+      superclassIdx++;
+    }
+    if (kind == IndexUpdateKind.STRING_ID && sourceFileIdx >= insertedIdx) {
+      sourceFileIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java
new file mode 100644
index 0000000..af3c377
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+import dexfuzz.program.MutatableCode;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+public class CodeItem implements RawDexObject {
+  public short registersSize;
+  public short insSize;
+  public short outsSize;
+  public short triesSize;
+  public int debugInfoOff; // NB: this is a special case
+  public int insnsSize;
+  public List<Instruction> insns;
+  public TryItem[] tries;
+  public EncodedCatchHandlerList handlers;
+
+  private MutatableCode mutatableCode;
+
+  public static class MethodMetaInfo {
+    public String methodName;
+    public boolean isStatic;
+    public String shorty;
+  }
+
+  public MethodMetaInfo meta = new MethodMetaInfo();
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    registersSize = file.readUShort();
+    insSize = file.readUShort();
+    outsSize = file.readUShort();
+    triesSize = file.readUShort();
+    debugInfoOff = file.readUInt();
+    insnsSize = file.readUInt();
+    populateInstructionList(file);
+    if (triesSize > 0) {
+      if ((insnsSize % 2) != 0) {
+        // Consume padding.
+        file.readUShort();
+      }
+      tries = new TryItem[triesSize];
+      for (int i = 0; i < triesSize; i++) {
+        (tries[i] = new TryItem()).read(file);
+      }
+      (handlers = new EncodedCatchHandlerList()).read(file);
+    }
+  }
+
+  private void populateInstructionList(DexRandomAccessFile file) throws IOException {
+    insns = new LinkedList<Instruction>();
+    long insnsOffset = file.getFilePointer();
+    if (insnsOffset != 0) {
+      long finger = insnsOffset;
+      long insnsEnd = insnsOffset + (2 * insnsSize);
+
+      while (finger < insnsEnd) {
+        file.seek(finger);
+        Instruction newInsn = new Instruction();
+        newInsn.read(file);
+        insns.add(newInsn);
+        finger += (2 * newInsn.getSize());
+      }
+
+      file.seek(finger);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUShort(registersSize);
+    file.writeUShort(insSize);
+    file.writeUShort(outsSize);
+    file.writeUShort(triesSize);
+    // We do not support retaining debug info currently.
+    file.writeUInt(0 /*debug_info_off*/);
+    file.writeUInt(insnsSize);
+    for (Instruction insn : insns) {
+      insn.write(file);
+    }
+    if (triesSize > 0) {
+      if ((insnsSize % 2) != 0) {
+        // produce padding
+        file.writeUShort((short) 0);
+      }
+      for (TryItem tryItem : tries) {
+        tryItem.write(file);
+      }
+      handlers.write(file);
+    }
+  }
+
+  /**
+   * CodeTranslator should call this to notify a CodeItem about its
+   * mutatable code, so it can always get the "latest" view of its
+   * instructions.
+   */
+  public void registerMutatableCode(MutatableCode mutatableCode) {
+    this.mutatableCode = mutatableCode;
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) {
+      // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns)
+      // are only interested in TYPE_IDs.
+      handlers.incrementIndex(kind, insertedIdx);
+    }
+
+    if (kind == IndexUpdateKind.PROTO_ID) {
+      // The only kind we can't encounter in an instruction.
+      return;
+    }
+
+    List<Instruction> insnsToIncrement = insns;
+
+    // If we have an associated MutatableCode, then it may have created some new insns
+    // that we won't know about yet, during the mutation phase.
+    //
+    // Ask for the latest view of the insns.
+    if (mutatableCode != null) {
+      insnsToIncrement = mutatableCode.requestLatestInstructions();
+    }
+
+    for (Instruction insn : insnsToIncrement) {
+      Opcode opcode = insn.info.opcode;
+      switch (kind) {
+        case STRING_ID:
+          if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
+            // STRING@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          }
+          break;
+        case TYPE_ID:
+          if (opcode == Opcode.CONST_CLASS
+              || opcode == Opcode.CHECK_CAST
+              || opcode == Opcode.NEW_INSTANCE
+              || opcode == Opcode.FILLED_NEW_ARRAY
+              || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
+            // TYPE@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) {
+            // TYPE@CCCC
+            if (insn.vregC >= insertedIdx) {
+              insn.vregC++;
+            }
+          }
+          break;
+        case FIELD_ID:
+          if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) {
+            // FIELD@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) {
+            // FIELD@CCCC
+            if (insn.vregC >= insertedIdx) {
+              insn.vregC++;
+            }
+          }
+          break;
+        case METHOD_ID:
+          if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
+              || Opcode.isBetween(opcode,
+                  Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) {
+            // METHOD@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          }
+          break;
+        default:
+          Log.errorAndQuit("Unexpected IndexUpdateKind requested "
+              + "in Instruction.incrementIndex()");
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
new file mode 100644
index 0000000..922ee58
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+// Right now we are not parsing debug_info_item, just take the raw size
+public class DebugInfoItem implements RawDexObject {
+  private int size;
+  private byte[] data;
+
+  public DebugInfoItem(int size) {
+    this.size = size;
+  }
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    data = new byte[size];
+    file.read(data);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.write(data);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java b/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java
new file mode 100644
index 0000000..ec75585
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * An extension to RandomAccessFile that allows reading/writing
+ * DEX files in little-endian form, the variable-length LEB format
+ * and also provides word-alignment functions.
+ */
+public class DexRandomAccessFile extends RandomAccessFile {
+  private OffsetTracker offsetTracker;
+
+  public OffsetTracker getOffsetTracker() {
+    return offsetTracker;
+  }
+
+  public void setOffsetTracker(OffsetTracker offsetTracker) {
+    this.offsetTracker = offsetTracker;
+  }
+
+  /**
+   * Constructor, passes straight on to RandomAccessFile currently.
+   * @param filename The file to open.
+   * @param mode Strings "r" or "rw" work best.
+   */
+  public DexRandomAccessFile(String filename, String mode)
+      throws FileNotFoundException {
+    super(filename, mode);
+  }
+
+  /**
+   * @return A 16-bit number, read from the file as little-endian.
+   */
+  public short readUShort() throws IOException {
+    int b1 = readUnsignedByte();
+    int b2 = readUnsignedByte();
+    return (short) ((b2 << 8) | b1);
+  }
+
+  /**
+   * @param value A 16-bit number to be written to the file in little-endian.
+   */
+  public void writeUShort(short value) throws IOException {
+    int b1 = value & 0xff;
+    int b2 = (value & 0xff00) >> 8;
+    writeByte(b1);
+    writeByte(b2);
+  }
+
+  /**
+   * @return A 32-bit number, read from the file as little-endian.
+   */
+  public int readUInt() throws IOException {
+    int b1 = readUnsignedByte();
+    int b2 = readUnsignedByte();
+    int b3 = readUnsignedByte();
+    int b4 = readUnsignedByte();
+    return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
+  }
+
+  /**
+   * @param value A 32-bit number to be written to the file in little-endian.
+   */
+  public void writeUInt(int value) throws IOException {
+    int b1 = value & 0xff;
+    writeByte(b1);
+    int b2 = (value & 0xff00) >> 8;
+    writeByte(b2);
+    int b3 = (value & 0xff0000) >> 16;
+    writeByte(b3);
+    int b4 = (value & 0xff000000) >> 24;
+    writeByte(b4);
+  }
+
+  /**
+   * @return An up to 32-bit number, read from the file in ULEB128 form.
+   */
+  public int readUleb128() throws IOException {
+    int shift = 0;
+    int value = 0;
+    int rawByte = readUnsignedByte();
+    boolean done = false;
+    while (!done) {
+      // Get the lower seven bits.
+      // 0x7f = 0111 1111
+      value |= ((rawByte & 0x7f) << shift);
+      shift += 7;
+      // Check the 8th bit - if it's 0, we're done.
+      // 0x80 = 1000 0000
+      if ((rawByte & 0x80) == 0) {
+        done = true;
+      } else {
+        rawByte = readUnsignedByte();
+      }
+    }
+    return value;
+  }
+
+  /**
+   * @param value A 32-bit number to be written to the file in ULEB128 form.
+   */
+  public void writeUleb128(int value) throws IOException {
+    if (value == 0) {
+      writeByte(0);
+      return;
+    }
+
+    while (value != 0) {
+      int marker = 1;
+      // If we're down to the last 7 bits, the marker will be 0.
+      if ((value & 0xffffff80) == 0) {
+        marker = 0;
+      }
+      // Get the lowest 7 bits, add on the marker in the high bit.
+      int nextByte = value & 0x7f | (marker << 7);
+      writeByte(nextByte);
+      value >>>= 7;
+    }
+  }
+
+  /**
+   * Write out ULEB128 value always using 5 bytes.
+   * A version of ULEB128 that will always write out 5 bytes, because this
+   * value will be patched later, and if we used a smaller encoding, the new value
+   * may overflow the previously selected encoding size.
+   * The largest encoding for 0 in ULEB128 would be:
+   *   0x80 0x80 0x80 0x80 0x00
+   * and for 1 would be:
+   *   0x81 0x80 0x80 0x80 0x00
+   */
+  public void writeLargestUleb128(int value) throws IOException {
+    Log.debug("Writing " + value + " using the largest possible ULEB128 encoding.");
+    if (value == 0) {
+      writeByte(0x80);
+      writeByte(0x80);
+      writeByte(0x80);
+      writeByte(0x80);
+      writeByte(0x0);
+      return;
+    }
+
+    for (int i = 0; i < 5; i++) {
+      int marker = 1;
+      // If we're writing the 5th byte, the marker is 0.
+      if (i == 4) {
+        marker = 0;
+      }
+      // Get the lowest 7 bits, add on the marker in the high bit.
+      int nextByte = value & 0x7f | (marker << 7);
+      writeByte(nextByte);
+      value >>>= 7;
+    }
+  }
+
+  /**
+   * @return An up to 32-bit number, read from the file in SLEB128 form.
+   */
+  public int readSleb128() throws IOException {
+    int shift = 0;
+    int value = 0;
+    int rawByte = readUnsignedByte();
+    boolean done = false;
+    boolean mustSignExtend = false;
+    while (!done) {
+      // Get the lower seven bits.
+      // 0x7f = 0111 1111
+      value |= ((rawByte & 0x7f) << shift);
+      shift += 7;
+      // Check the 8th bit - if it's 0, we're done.
+      // 0x80 = 1000 0000
+      if ((rawByte & 0x80) == 0) {
+        // Check the 7th bit - if it's a 1, we need to sign extend.
+        if ((rawByte & 0x60) != 0) {
+          mustSignExtend = true;
+        }
+        done = true;
+      } else {
+        rawByte = readUnsignedByte();
+      }
+    }
+    if (mustSignExtend) {
+      // Example:
+      // say we shifted 7 bits, we need
+      // to make all the upper 25 bits 1s.
+      // load a 1...
+      // 00000000 00000000 00000000 00000001
+      // << 7
+      // 00000000 00000000 00000000 10000000
+      // - 1
+      // 00000000 00000000 00000000 01111111
+      // ~
+      // 11111111 11111111 11111111 10000000
+      int upperOnes = ~((1 << shift) - 1);
+      value |= (upperOnes);
+    }
+    return value;
+  }
+
+  /**
+   * @param value A 32-bit number to be written to the file in SLEB128 form.
+   */
+  public void writeSleb128(int value) throws IOException {
+    if (value == 0) {
+      writeByte(0);
+      return;
+    }
+    if (value > 0) {
+      writeUleb128(value);
+      return;
+    }
+    if (value == -1) {
+      writeByte(0x7f);
+    }
+
+    // When it's all 1s (0xffffffff), we're done!
+    while (value != 0xffffffff) {
+      int marker = 1;
+      // If we're down to the last 7 bits (i.e., shifting a further 7 is all 1s),
+      // the marker will be 0.
+      if ((value >> 7) == 0xffffffff) {
+        marker = 0;
+      }
+      // Get the lowest 7 bits, add on the marker in the high bit.
+      int nextByte = value & 0x7f | (marker << 7);
+      writeByte(nextByte);
+      value >>= 7;
+    }
+  }
+
+  /**
+   * In DEX format, strings are in MUTF-8 format, the first ULEB128 value is the decoded size
+   * (i.e., string.length), and then follows a null-terminated series of characters.
+   * @param decodedSize The ULEB128 value that should have been read just before this.
+   * @return The raw bytes of the string, not including the null character.
+   */
+  public byte[] readDexUtf(int decodedSize) throws IOException {
+    // In the dex MUTF-8, the encoded size can never be larger than 3 times
+    // the actual string's length (which is the ULEB128 value just before this
+    // string, the "decoded size")
+
+    // Therefore, allocate as much space as we might need.
+    byte[] str = new byte[decodedSize * 3];
+
+    // Get our first byte.
+    int encodedSize = 0;
+    byte rawByte = readByte();
+
+    // Keep reading until we find the end marker.
+    while (rawByte != 0) {
+      str[encodedSize++] = rawByte;
+      rawByte = readByte();
+    }
+
+    // Copy everything we read into str into the correctly-sized actual string.
+    byte[] actualString = new byte[encodedSize];
+    for (int i = 0; i < encodedSize; i++) {
+      actualString[i] = str[i];
+    }
+
+    return actualString;
+  }
+
+  /**
+   * Writes out raw bytes that would have been read by readDexUTF().
+   * Will automatically write out the null-byte at the end.
+   * @param data Bytes to be written out.
+   */
+  public void writeDexUtf(byte[] data) throws IOException {
+    write(data);
+    // Remember to add the end marker.
+    writeByte(0);
+  }
+
+  /**
+   * Align the file handle's seek pointer to the next N bytes.
+   * @param alignment N to align to.
+   */
+  public void alignForwards(int alignment) throws IOException {
+    long offset = getFilePointer();
+    long mask = alignment - 1;
+    if ((offset & mask) != 0) {
+      int extra = alignment - (int) (offset & mask);
+      seek(offset + extra);
+    }
+  }
+
+  /**
+   * Align the file handle's seek pointer backwards to the previous N bytes.
+   * @param alignment N to align to.
+   */
+  public void alignBackwards(int alignment) throws IOException {
+    long offset = getFilePointer();
+    long mask = alignment - 1;
+    if ((offset & mask) != 0) {
+      offset &= (~mask);
+      seek(offset);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java
new file mode 100644
index 0000000..085a4a8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedAnnotation implements RawDexObject {
+  public int typeIdx;
+  public int size;
+  public AnnotationElement[] elements;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    typeIdx = file.readUleb128();
+    size = file.readUleb128();
+    if (size != 0) {
+      elements = new AnnotationElement[size];
+      for (int i = 0; i < size; i++) {
+        (elements[i] = new AnnotationElement()).read(file);
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(typeIdx);
+    file.writeUleb128(size);
+    if (size != 0) {
+      for (AnnotationElement annotationElement : elements) {
+        annotationElement.write(file);
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+    if (size != 0) {
+      for (AnnotationElement element : elements) {
+        element.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java
new file mode 100644
index 0000000..267ff09
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedArray implements RawDexObject {
+  public int size;
+  public EncodedValue[] values;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    size = file.readUleb128();
+    if (size != 0) {
+      values = new EncodedValue[size];
+      for (int i = 0; i < size; i++) {
+        (values[i] = new EncodedValue()).read(file);
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(size);
+    if (size != 0) {
+      for (EncodedValue encodedValue : values) {
+        encodedValue.write(file);
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (size != 0) {
+      for (EncodedValue value : values) {
+        value.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java
new file mode 100644
index 0000000..e461a54
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedArrayItem implements RawDexObject {
+  public EncodedArray value;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    (value = new EncodedArray()).read(file);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    value.write(file);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java
new file mode 100644
index 0000000..83786c8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedCatchHandler implements RawDexObject {
+  public int size;
+  public EncodedTypeAddrPair[] handlers;
+  public int catchAllAddr;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    size = file.readSleb128();
+    int absoluteSize = Math.abs(size);
+    if (absoluteSize > 0) {
+      handlers = new EncodedTypeAddrPair[absoluteSize];
+      for (int i = 0; i < Math.abs(size); i++) {
+        (handlers[i] = new EncodedTypeAddrPair()).read(file);
+      }
+    }
+    if (size <= 0) {
+      catchAllAddr = file.readUleb128();
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeSleb128(size);
+    if (handlers != null) {
+      for (EncodedTypeAddrPair encodedTypeAddrPair : handlers) {
+        encodedTypeAddrPair.write(file);
+      }
+    }
+    if (size <= 0) {
+      file.writeUleb128(catchAllAddr);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (handlers != null) {
+      for (EncodedTypeAddrPair handler : handlers) {
+        handler.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java
new file mode 100644
index 0000000..5619b5f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedCatchHandlerList implements RawDexObject {
+  public int size;
+  public EncodedCatchHandler[] list;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    size = file.readUleb128();
+    list = new EncodedCatchHandler[size];
+    for (int i = 0; i < size; i++) {
+      (list[i] = new EncodedCatchHandler()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(size);
+    for (EncodedCatchHandler encodedCatchHandler : list) {
+      encodedCatchHandler.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    for (EncodedCatchHandler handler : list) {
+      handler.incrementIndex(kind, insertedIdx);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java
new file mode 100644
index 0000000..18c1d43
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedField implements RawDexObject {
+  public int fieldIdxDiff;
+  public int accessFlags;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    fieldIdxDiff = file.readUleb128();
+    accessFlags = file.readUleb128();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(fieldIdxDiff);
+    file.writeUleb128(accessFlags);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+    // NB: our idx_diff is handled in ClassDataItem...
+  }
+
+  public boolean isVolatile() {
+    return ((accessFlags & Flag.ACC_VOLATILE.getValue()) != 0);
+  }
+
+  /**
+   * Set/unset the volatile flag for this EncodedField.
+   */
+  public void setVolatile(boolean turnOn) {
+    if (turnOn) {
+      accessFlags |= Flag.ACC_VOLATILE.getValue();
+    } else {
+      accessFlags &= ~(Flag.ACC_VOLATILE.getValue());
+    }
+  }
+
+  private static enum Flag {
+    ACC_PUBLIC(0x1),
+    ACC_PRIVATE(0x2),
+    ACC_PROTECTED(0x4),
+    ACC_STATIC(0x8),
+    ACC_FINAL(0x10),
+    ACC_VOLATILE(0x40),
+    ACC_TRANSIENT(0x80),
+    ACC_SYNTHETIC(0x1000),
+    ACC_ENUM(0x4000);
+
+    private int value;
+
+    private Flag(int value) {
+      this.value = value;
+    }
+
+    public int getValue() {
+      return value;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java
new file mode 100644
index 0000000..3a8163a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+
+public class EncodedMethod implements RawDexObject {
+  public int methodIdxDiff;
+  public int accessFlags;
+  public Offset codeOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    methodIdxDiff = file.readUleb128();
+    accessFlags = file.readUleb128();
+    codeOff = file.getOffsetTracker().getNewOffset(file.readUleb128());
+    if (isNative()) {
+      Log.errorAndQuit("Sorry, DEX files with native methods are not supported yet.");
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(methodIdxDiff);
+    file.writeUleb128(accessFlags);
+    file.getOffsetTracker().tryToWriteOffset(codeOff, file, true /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+    // NB: our idx_diff is handled in ClassDataItem...
+  }
+
+  public boolean isStatic() {
+    return ((accessFlags & Flag.ACC_STATIC.getValue()) != 0);
+  }
+
+  public boolean isNative() {
+    return ((accessFlags & Flag.ACC_NATIVE.getValue()) != 0);
+  }
+
+  /**
+   * Set/unset the static flag for this EncodedMethod.
+   */
+  public void setStatic(boolean turnOn) {
+    if (turnOn) {
+      accessFlags |= Flag.ACC_STATIC.getValue();
+    } else {
+      accessFlags &= ~(Flag.ACC_STATIC.getValue());
+    }
+  }
+
+  private static enum Flag {
+    ACC_PUBLIC(0x1),
+    ACC_PRIVATE(0x2),
+    ACC_PROTECTED(0x4),
+    ACC_STATIC(0x8),
+    ACC_FINAL(0x10),
+    ACC_SYNCHRONIZED(0x20),
+    ACC_VARARGS(0x80),
+    ACC_NATIVE(0x100),
+    ACC_ABSTRACT(0x400),
+    ACC_STRICT(0x800),
+    ACC_SYNTHETIC(0x1000),
+    ACC_ENUM(0x4000),
+    ACC_CONSTRUCTOR(0x10000);
+
+    private int value;
+
+    private Flag(int value) {
+      this.value = value;
+    }
+
+    public int getValue() {
+      return value;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java
new file mode 100644
index 0000000..ea49ae1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedTypeAddrPair implements RawDexObject {
+  public int typeIdx;
+  public int addr;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    typeIdx = file.readUleb128();
+    addr = file.readUleb128();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(typeIdx);
+    file.writeUleb128(addr);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java
new file mode 100644
index 0000000..fdf133f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedValue implements RawDexObject {
+  public byte valueArg;
+  public byte valueType;
+  public byte[] value;
+  public EncodedArray encodedArray;
+  public EncodedAnnotation encodedAnnotation;
+
+  private static final byte VALUE_BYTE = 0x00;
+  private static final byte VALUE_ARRAY = 0x1c;
+  private static final byte VALUE_ANNOTATION = 0x1d;
+  private static final byte VALUE_NULL = 0x1e;
+  private static final byte VALUE_BOOLEAN = 0x1f;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    int valueArgAndType = file.readUnsignedByte();
+
+    // Get lower 5 bits.
+    valueType = (byte) (valueArgAndType & 0x1f);
+    // Get upper 3 bits.
+    valueArg = (byte) ((valueArgAndType & 0xe0) >> 5);
+
+    int size = 0;
+
+    switch (valueType) {
+      case VALUE_BYTE:
+        size = 1;
+        break;
+      case VALUE_ARRAY:
+        (encodedArray = new EncodedArray()).read(file);
+        size = 0; // So we don't read into value.
+        break;
+      case VALUE_ANNOTATION:
+        (encodedAnnotation = new EncodedAnnotation()).read(file);
+        size = 0; // So we don't read into value.
+        break;
+      case VALUE_NULL:
+      case VALUE_BOOLEAN:
+        // No value
+        size = 0;
+        break;
+      default:
+        // All others encode value_arg as (size - 1), so...
+        size = valueArg + 1;
+        break;
+    }
+
+    if (size != 0) {
+      value = new byte[size];
+      for (int i = 0; i < size; i++) {
+        value[i] = file.readByte();
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    int valueArgAndType = ((valueType) | (valueArg << 5));
+    file.writeByte(valueArgAndType);
+
+    if (encodedArray != null) {
+      encodedArray.write(file);
+    } else if (encodedAnnotation != null) {
+      encodedAnnotation.write(file);
+    } else if (value != null) {
+      file.write(value);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (encodedArray != null) {
+      encodedArray.incrementIndex(kind, insertedIdx);
+    } else if (encodedAnnotation != null) {
+      encodedAnnotation.incrementIndex(kind, insertedIdx);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java
new file mode 100644
index 0000000..98f812e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class FieldAnnotation implements RawDexObject {
+  public int fieldIdx;
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    fieldIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(fieldIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.FIELD_ID && fieldIdx >= insertedIdx) {
+      fieldIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java
new file mode 100644
index 0000000..75f2078
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class FieldIdItem implements RawDexObject {
+  public short classIdx;
+  public short typeIdx;
+  public int nameIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classIdx = file.readUShort();
+    typeIdx = file.readUShort();
+    nameIdx = file.readUInt();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUShort(classIdx);
+    file.writeUShort(typeIdx);
+    file.writeUInt(nameIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+      classIdx++;
+    }
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+    if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+      nameIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java
new file mode 100644
index 0000000..e6b290c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+
+public class HeaderItem implements RawDexObject {
+  public byte[] magic;
+  public int checksum;
+  public byte[] signature; // Verification doesn't depend on this, so we don't update it.
+  public int fileSize;
+  public int headerSize;
+  public int endianTag;
+  public int linkSize;
+  public Offset linkOff;
+  public Offset mapOff;
+  public int stringIdsSize;
+  public Offset stringIdsOff;
+  public int typeIdsSize;
+  public Offset typeIdsOff;
+  public int protoIdsSize;
+  public Offset protoIdsOff;
+  public int fieldIdsSize;
+  public Offset fieldIdsOff;
+  public int methodIdsSize;
+  public Offset methodIdsOff;
+  public int classDefsSize;
+  public Offset classDefsOff;
+  public int dataSize;
+  public Offset dataOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    magic = new byte[8];
+    for (int i = 0; i < 8; i++) {
+      magic[i] = file.readByte();
+    }
+    checksum = file.readUInt();
+    signature = new byte[20];
+    for (int i = 0; i < 20; i++) {
+      signature[i] = file.readByte();
+    }
+    fileSize = file.readUInt();
+    headerSize = file.readUInt();
+    endianTag = file.readUInt();
+    linkSize = file.readUInt();
+    linkOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    mapOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    stringIdsSize = file.readUInt();
+    stringIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    typeIdsSize = file.readUInt();
+    typeIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    protoIdsSize = file.readUInt();
+    protoIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    fieldIdsSize = file.readUInt();
+    fieldIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    methodIdsSize = file.readUInt();
+    methodIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    classDefsSize = file.readUInt();
+    classDefsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    dataSize = file.readUInt();
+    dataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    if (headerSize != 0x70) {
+      Log.errorAndQuit("Invalid header size in header.");
+    }
+    if (file.getFilePointer() != headerSize) {
+      Log.errorAndQuit("Read a different amount than expected in header: "
+          + file.getFilePointer());
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    for (int i = 0; i < 8; i++) {
+      file.writeByte(magic[i]);
+    }
+    // Will be recalculated later!
+    file.writeUInt(checksum);
+    for (int i = 0; i < 20; i++) {
+      file.writeByte(signature[i]);
+    }
+    // Will be recalculated later!
+    file.writeUInt(fileSize);
+    file.writeUInt(headerSize);
+    file.writeUInt(endianTag);
+    file.writeUInt(linkSize);
+    file.getOffsetTracker().tryToWriteOffset(linkOff, file, false /* ULEB128 */);
+    file.getOffsetTracker().tryToWriteOffset(mapOff, file, false /* ULEB128 */);
+    file.writeUInt(stringIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(stringIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(typeIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(typeIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(protoIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(protoIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(fieldIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(fieldIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(methodIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(methodIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(classDefsSize);
+    file.getOffsetTracker().tryToWriteOffset(classDefsOff, file, false /* ULEB128 */);
+    // will be recalculated later
+    file.writeUInt(dataSize);
+    file.getOffsetTracker().tryToWriteOffset(dataOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java b/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java
new file mode 100644
index 0000000..2dda78f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java
@@ -0,0 +1,584 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.formats.AbstractFormat;
+import dexfuzz.rawdex.formats.ContainsConst;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsTarget;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+import dexfuzz.rawdex.formats.Format10t;
+import dexfuzz.rawdex.formats.Format10x;
+import dexfuzz.rawdex.formats.Format11n;
+import dexfuzz.rawdex.formats.Format11x;
+import dexfuzz.rawdex.formats.Format12x;
+import dexfuzz.rawdex.formats.Format20t;
+import dexfuzz.rawdex.formats.Format21c;
+import dexfuzz.rawdex.formats.Format21h;
+import dexfuzz.rawdex.formats.Format21s;
+import dexfuzz.rawdex.formats.Format21t;
+import dexfuzz.rawdex.formats.Format22b;
+import dexfuzz.rawdex.formats.Format22c;
+import dexfuzz.rawdex.formats.Format22s;
+import dexfuzz.rawdex.formats.Format22t;
+import dexfuzz.rawdex.formats.Format22x;
+import dexfuzz.rawdex.formats.Format23x;
+import dexfuzz.rawdex.formats.Format30t;
+import dexfuzz.rawdex.formats.Format31c;
+import dexfuzz.rawdex.formats.Format31i;
+import dexfuzz.rawdex.formats.Format31t;
+import dexfuzz.rawdex.formats.Format32x;
+import dexfuzz.rawdex.formats.Format35c;
+import dexfuzz.rawdex.formats.Format3rc;
+import dexfuzz.rawdex.formats.Format51l;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Instruction implements RawDexObject {
+  // Only used by Format35* instructions
+  public static class InvokeFormatInfo {
+    public byte vregD;
+    public byte vregE;
+    public byte vregF;
+    public byte vregG;
+  }
+
+  // Immutable information about this class of instruction.
+  public OpcodeInfo info;
+
+  // The raw bytes of the instruction.
+  // Only used during reading, and writing out is done from the decoded instruction data.
+  //  Except in the case of the 3 "data" instructions.
+  public byte[] rawBytes;
+
+  public static final int RAW_TYPE_PACKED_SWITCH_DATA = 1;
+  public static final int RAW_TYPE_SPARSE_SWITCH_DATA = 2;
+  public static final int RAW_TYPE_FILL_ARRAY_DATA_DATA = 3;
+
+  public int rawType;
+  public boolean justRaw;
+  public int rawSize;
+
+  public long vregA = 0;
+  public long vregB = 0;
+  public long vregC = 0;
+
+  public InvokeFormatInfo invokeFormatInfo;
+
+  /**
+   * Clone an instruction.
+   */
+  public Instruction clone() {
+    Instruction newInsn = new Instruction();
+    // If we've generated a new instruction, we won't have calculated its raw array.
+    if (newInsn.rawBytes != null) {
+      newInsn.rawBytes = new byte[rawBytes.length];
+      for (int i = 0; i < rawBytes.length; i++) {
+        newInsn.rawBytes[i] = rawBytes[i];
+      }
+    }
+    newInsn.justRaw = justRaw;
+    newInsn.rawType = rawType;
+    newInsn.rawSize = rawSize;
+
+    newInsn.vregA = vregA;
+    newInsn.vregB = vregB;
+    newInsn.vregC = vregC;
+    newInsn.info = info;
+    if (invokeFormatInfo != null) {
+      newInsn.invokeFormatInfo = new InvokeFormatInfo();
+      newInsn.invokeFormatInfo.vregD = invokeFormatInfo.vregD;
+      newInsn.invokeFormatInfo.vregE = invokeFormatInfo.vregE;
+      newInsn.invokeFormatInfo.vregF = invokeFormatInfo.vregF;
+      newInsn.invokeFormatInfo.vregG = invokeFormatInfo.vregG;
+    }
+    return newInsn;
+  }
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    // Remember the offset, so after reading the opcode, we can read the whole
+    // insn into raw_bytes.
+    long offset = file.getFilePointer();
+    int opcodeValue = readOpcode(file);
+    info = getOpcodeInfo(opcodeValue);
+    if (info == null) {
+      Log.errorAndQuit("Couldn't find OpcodeInfo for opcode with value: "
+          + opcodeValue);
+    }
+
+    rawBytes = new byte[2 * getSize()];
+    file.seek(offset);
+    file.read(rawBytes);
+
+    vregA = info.format.getA(rawBytes);
+    vregB = info.format.getB(rawBytes);
+    vregC = info.format.getC(rawBytes);
+
+    // Special case for 35* formats.
+    if (info.format.needsInvokeFormatInfo()) {
+      invokeFormatInfo = new InvokeFormatInfo();
+      invokeFormatInfo.vregD = (byte) (rawBytes[4] >> 4);
+      invokeFormatInfo.vregE = (byte) (rawBytes[5] & 0xf);
+      invokeFormatInfo.vregF = (byte) (rawBytes[5] >> 4);
+      invokeFormatInfo.vregG = (byte) (rawBytes[1] & 0xf);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    if (justRaw) {
+      // It is the responsibility of the CodeTranslator to make
+      // sure the raw bytes have been updated.
+      file.write(rawBytes);
+    } else {
+      info.format.writeToFile(file, this);
+    }
+  }
+
+  /**
+   * Get the size of an instruction, in code-words. (Code-words are 16-bits.)
+   */
+  public int getSize() {
+    if (justRaw) {
+      // It is the responsibility of the CodeTranslator to make sure
+      // the raw size has been updated.
+      return rawSize;
+    }
+    return info.format.getSize();
+  }
+
+  private int readOpcode(DexRandomAccessFile file) throws IOException {
+    short firstCodeWord = file.readUShort();
+    int opcode = (firstCodeWord & 0xff);
+    int upperBits = (firstCodeWord & 0xff00) >> 8;
+    if (opcode == 0x0 && upperBits != 0x0) {
+      justRaw = true;
+      rawType = upperBits;
+      // Need to calculate special sizes.
+      switch (rawType) {
+        case RAW_TYPE_PACKED_SWITCH_DATA:
+          rawSize = (file.readUShort() * 2) + 4;
+          break;
+        case RAW_TYPE_SPARSE_SWITCH_DATA:
+          rawSize = (file.readUShort() * 4) + 2;
+          break;
+        case RAW_TYPE_FILL_ARRAY_DATA_DATA:
+        {
+          int elementWidth = file.readUShort();
+          rawSize = ((file.readUInt() * elementWidth + 1) / 2) + 4;
+          break;
+        }
+        default:
+          Log.errorAndQuit("Unrecognised ident in data-payload instruction: " + rawType);
+      }
+    }
+    return opcode;
+  }
+
+  @Override
+  public String toString() {
+    if (justRaw) {
+      switch (rawType) {
+        case RAW_TYPE_PACKED_SWITCH_DATA:
+          return "PACKED SWITCH DATA";
+        case RAW_TYPE_SPARSE_SWITCH_DATA:
+          return "SPARSE SWITCH DATA";
+        case RAW_TYPE_FILL_ARRAY_DATA_DATA:
+          return "FILL ARRAY DATA DATA";
+        default:
+      }
+
+    }
+
+    String repr = info.name;
+
+    AbstractFormat format = info.format;
+
+    if (invokeFormatInfo != null) {
+      String vregs = "";
+
+      int numVregs = (int) vregA;
+
+      if (numVregs > 5) {
+        Log.debug("vA in an 35c invoke was greater than 5? Assuming 5.");
+        numVregs = 5;
+      } else if (numVregs < 0) {
+        Log.debug("vA in an 35c invoke was less than 0? Assuming 0.");
+        numVregs = 0;
+      }
+
+      switch (numVregs) {
+        case 5:
+          vregs = ", v" + invokeFormatInfo.vregG;
+          // fallthrough
+        case 4:
+          vregs = ", v" + invokeFormatInfo.vregF + vregs;
+          // fallthrough
+        case 3:
+          vregs = ", v" + invokeFormatInfo.vregE + vregs;
+          // fallthrough
+        case 2:
+          vregs = ", v" + invokeFormatInfo.vregD + vregs;
+          // fallthrough
+        case 1:
+          vregs = "v" + vregC + vregs;
+          break;
+        default:
+      }
+
+      repr += "(" + vregs + ")";
+
+      long poolIndex = ((ContainsPoolIndex)format).getPoolIndex(this);
+      repr += " meth@" + poolIndex;
+
+      return repr;
+    }
+
+
+
+    if (format instanceof ContainsVRegs) {
+      String vregs = "";
+      switch (((ContainsVRegs)format).getVRegCount()) {
+        case 3:
+          vregs = ", v" + vregC;
+          // fallthrough
+        case 2:
+          vregs = ", v" + vregB + vregs;
+          // fallthrough
+        case 1:
+          vregs = "v" + vregA + vregs;
+          break;
+        default:
+          Log.errorAndQuit("Invalid number of vregs reported by a Format.");
+      }
+
+      repr += " " + vregs;
+    }
+    if (format instanceof ContainsConst) {
+      long constant = ((ContainsConst)format).getConst(this);
+      repr += " #" + constant;
+    }
+    if (format instanceof ContainsPoolIndex) {
+      long poolIndex = ((ContainsPoolIndex)format).getPoolIndex(this);
+      repr += " pool@" + poolIndex;
+    }
+    if (format instanceof ContainsTarget) {
+      long target = ((ContainsTarget)format).getTarget(this);
+      repr += " +" + target;
+    }
+
+    return repr;
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+
+  // STATIC INSTRUCTION CODE
+  private static Map<Integer,OpcodeInfo> opcode_map_by_int = new HashMap<Integer,OpcodeInfo>();
+  private static Map<Opcode,OpcodeInfo> opcode_map_by_enum = new HashMap<Opcode,OpcodeInfo>();
+
+  public static OpcodeInfo getOpcodeInfo(Opcode opcode) {
+    return opcode_map_by_enum.get(opcode);
+  }
+
+  public static OpcodeInfo getOpcodeInfo(int opcodeValue) {
+    return opcode_map_by_int.get(opcodeValue);
+  }
+
+  private static void addOpcodeInfo(Opcode opcode, String name,
+      int opcodeValue, AbstractFormat fmt) {
+    OpcodeInfo info = new OpcodeInfo(opcode, name, opcodeValue, fmt);
+    if (opcode.ordinal() != opcodeValue) {
+      Log.errorAndQuit(String.format("Opcode: %s (enum ordinal 0x%x) != (value 0x%x)",
+          opcode.toString(), opcode.ordinal(), opcodeValue));
+    }
+    opcode_map_by_int.put(opcodeValue, info);
+    opcode_map_by_enum.put(opcode, info);
+  }
+
+  static {
+    addOpcodeInfo(Opcode.NOP, "nop", 0x00, new Format10x());
+    addOpcodeInfo(Opcode.MOVE, "move", 0x01, new Format12x());
+    addOpcodeInfo(Opcode.MOVE_FROM16, "move/from16", 0x02, new Format22x());
+    addOpcodeInfo(Opcode.MOVE_16, "move/16", 0x03, new Format32x());
+    addOpcodeInfo(Opcode.MOVE_WIDE, "move-wide", 0x04, new Format12x());
+    addOpcodeInfo(Opcode.MOVE_WIDE_FROM16, "move-wide/from16", 0x05, new Format22x());
+    addOpcodeInfo(Opcode.MOVE_WIDE_16, "move-wide/16", 0x06, new Format32x());
+    addOpcodeInfo(Opcode.MOVE_OBJECT, "move-object", 0x07, new Format12x());
+    addOpcodeInfo(Opcode.MOVE_OBJECT_FROM16, "move-object/from16", 0x08, new Format22x());
+    addOpcodeInfo(Opcode.MOVE_OBJECT_16, "move-object/16", 0x09, new Format32x());
+    addOpcodeInfo(Opcode.MOVE_RESULT, "move-result", 0x0a, new Format11x());
+    addOpcodeInfo(Opcode.MOVE_RESULT_WIDE, "move-result-wide", 0x0b, new Format11x());
+    addOpcodeInfo(Opcode.MOVE_RESULT_OBJECT, "move-result-object", 0x0c, new Format11x());
+    addOpcodeInfo(Opcode.MOVE_EXCEPTION, "move-exception", 0x0d, new Format11x());
+    addOpcodeInfo(Opcode.RETURN_VOID, "return-void", 0x0e, new Format10x());
+    addOpcodeInfo(Opcode.RETURN, "return", 0x0f, new Format11x());
+    addOpcodeInfo(Opcode.RETURN_WIDE, "return-wide", 0x10, new Format11x());
+    addOpcodeInfo(Opcode.RETURN_OBJECT, "return-object", 0x11, new Format11x());
+    addOpcodeInfo(Opcode.CONST_4, "const/4", 0x12, new Format11n());
+    addOpcodeInfo(Opcode.CONST_16, "const/16", 0x13, new Format21s());
+    addOpcodeInfo(Opcode.CONST, "const", 0x14, new Format31i());
+    addOpcodeInfo(Opcode.CONST_HIGH16, "const/high16", 0x15, new Format21h());
+    addOpcodeInfo(Opcode.CONST_WIDE_16, "const-wide/16", 0x16, new Format21s());
+    addOpcodeInfo(Opcode.CONST_WIDE_32, "const-wide/32", 0x17, new Format31i());
+    addOpcodeInfo(Opcode.CONST_WIDE, "const-wide", 0x18, new Format51l());
+    addOpcodeInfo(Opcode.CONST_WIDE_HIGH16, "const-wide/high16", 0x19, new Format21h());
+    addOpcodeInfo(Opcode.CONST_STRING, "const-string", 0x1a, new Format21c());
+    addOpcodeInfo(Opcode.CONST_STRING_JUMBO, "const-string/jumbo", 0x1b, new Format31c());
+    addOpcodeInfo(Opcode.CONST_CLASS, "const-class", 0x1c, new Format21c());
+    addOpcodeInfo(Opcode.MONITOR_ENTER, "monitor-enter", 0x1d, new Format11x());
+    addOpcodeInfo(Opcode.MONITOR_EXIT, "monitor-exit", 0x1e, new Format11x());
+    addOpcodeInfo(Opcode.CHECK_CAST, "check-cast", 0x1f, new Format21c());
+    addOpcodeInfo(Opcode.INSTANCE_OF, "instance-of", 0x20, new Format22c());
+    addOpcodeInfo(Opcode.ARRAY_LENGTH, "array-length", 0x21, new Format12x());
+    addOpcodeInfo(Opcode.NEW_INSTANCE, "new-instance", 0x22, new Format21c());
+    addOpcodeInfo(Opcode.NEW_ARRAY, "new-array", 0x23, new Format22c());
+    addOpcodeInfo(Opcode.FILLED_NEW_ARRAY, "filled-new-array", 0x24, new Format35c());
+    addOpcodeInfo(Opcode.FILLED_NEW_ARRAY_RANGE, "filled-new-array/range",
+        0x25, new Format3rc());
+    addOpcodeInfo(Opcode.FILL_ARRAY_DATA, "fill-array-data", 0x26, new Format31t());
+    addOpcodeInfo(Opcode.THROW, "throw", 0x27, new Format11x());
+    addOpcodeInfo(Opcode.GOTO, "goto", 0x28, new Format10t());
+    addOpcodeInfo(Opcode.GOTO_16, "goto/16", 0x29, new Format20t());
+    addOpcodeInfo(Opcode.GOTO_32, "goto/32", 0x2a, new Format30t());
+    addOpcodeInfo(Opcode.PACKED_SWITCH, "packed-switch", 0x2b, new Format31t());
+    addOpcodeInfo(Opcode.SPARSE_SWITCH, "sparse-switch", 0x2c, new Format31t());
+    addOpcodeInfo(Opcode.CMPL_FLOAT, "cmpl-float", 0x2d, new Format23x());
+    addOpcodeInfo(Opcode.CMPG_FLOAT, "cmpg-float", 0x2e, new Format23x());
+    addOpcodeInfo(Opcode.CMPL_DOUBLE, "cmpl-double", 0x2f, new Format23x());
+    addOpcodeInfo(Opcode.CMPG_DOUBLE, "cmpg-double", 0x30, new Format23x());
+    addOpcodeInfo(Opcode.CMP_LONG, "cmp-long", 0x31, new Format23x());
+    addOpcodeInfo(Opcode.IF_EQ, "if-eq", 0x32, new Format22t());
+    addOpcodeInfo(Opcode.IF_NE, "if-ne", 0x33, new Format22t());
+    addOpcodeInfo(Opcode.IF_LT, "if-lt", 0x34, new Format22t());
+    addOpcodeInfo(Opcode.IF_GE, "if-ge", 0x35, new Format22t());
+    addOpcodeInfo(Opcode.IF_GT, "if-gt", 0x36, new Format22t());
+    addOpcodeInfo(Opcode.IF_LE, "if-le", 0x37, new Format22t());
+    addOpcodeInfo(Opcode.IF_EQZ, "if-eqz", 0x38, new Format21t());
+    addOpcodeInfo(Opcode.IF_NEZ, "if-nez", 0x39, new Format21t());
+    addOpcodeInfo(Opcode.IF_LTZ, "if-ltz", 0x3a, new Format21t());
+    addOpcodeInfo(Opcode.IF_GEZ, "if-gez", 0x3b, new Format21t());
+    addOpcodeInfo(Opcode.IF_GTZ, "if-gtz", 0x3c, new Format21t());
+    addOpcodeInfo(Opcode.IF_LEZ, "if-lez", 0x3d, new Format21t());
+    addOpcodeInfo(Opcode.UNUSED_3E, "unused-3e", 0x3e, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_3F, "unused-3f", 0x3f, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_40, "unused-40", 0x40, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_41, "unused-41", 0x41, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_42, "unused-42", 0x42, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_43, "unused-43", 0x43, new Format10x());
+    addOpcodeInfo(Opcode.AGET, "aget", 0x44, new Format23x());
+    addOpcodeInfo(Opcode.AGET_WIDE, "aget-wide", 0x45, new Format23x());
+    addOpcodeInfo(Opcode.AGET_WIDE, "aget-wide", 0x45, new Format23x());
+    addOpcodeInfo(Opcode.AGET_OBJECT, "aget-object", 0x46, new Format23x());
+    addOpcodeInfo(Opcode.AGET_BOOLEAN, "aget-boolean", 0x47, new Format23x());
+    addOpcodeInfo(Opcode.AGET_BYTE, "aget-byte", 0x48, new Format23x());
+    addOpcodeInfo(Opcode.AGET_CHAR, "aget-char", 0x49, new Format23x());
+    addOpcodeInfo(Opcode.AGET_SHORT, "aget-short", 0x4a, new Format23x());
+    addOpcodeInfo(Opcode.APUT, "aput", 0x4b, new Format23x());
+    addOpcodeInfo(Opcode.APUT_WIDE, "aput-wide", 0x4c, new Format23x());
+    addOpcodeInfo(Opcode.APUT_OBJECT, "aput-object", 0x4d, new Format23x());
+    addOpcodeInfo(Opcode.APUT_BOOLEAN, "aput-boolean", 0x4e, new Format23x());
+    addOpcodeInfo(Opcode.APUT_BYTE, "aput-byte", 0x4f, new Format23x());
+    addOpcodeInfo(Opcode.APUT_CHAR, "aput-char", 0x50, new Format23x());
+    addOpcodeInfo(Opcode.APUT_SHORT, "aput-short", 0x51, new Format23x());
+    addOpcodeInfo(Opcode.IGET, "iget", 0x52, new Format22c());
+    addOpcodeInfo(Opcode.IGET_WIDE, "iget-wide", 0x53, new Format22c());
+    addOpcodeInfo(Opcode.IGET_OBJECT, "iget-object", 0x54, new Format22c());
+    addOpcodeInfo(Opcode.IGET_BOOLEAN, "iget-boolean", 0x55, new Format22c());
+    addOpcodeInfo(Opcode.IGET_BYTE, "iget-byte", 0x56, new Format22c());
+    addOpcodeInfo(Opcode.IGET_CHAR, "iget-char", 0x57, new Format22c());
+    addOpcodeInfo(Opcode.IGET_SHORT, "iget-short", 0x58, new Format22c());
+    addOpcodeInfo(Opcode.IPUT, "iput", 0x59, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_WIDE, "iput-wide", 0x5a, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_OBJECT, "iput-object", 0x5b, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_BOOLEAN, "iput-boolean", 0x5c, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_BYTE, "iput-byte", 0x5d, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_CHAR, "iput-char", 0x5e, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_SHORT, "iput-short", 0x5f, new Format22c());
+    addOpcodeInfo(Opcode.SGET, "sget", 0x60, new Format21c());
+    addOpcodeInfo(Opcode.SGET_WIDE, "sget-wide", 0x61, new Format21c());
+    addOpcodeInfo(Opcode.SGET_OBJECT, "sget-object", 0x62, new Format21c());
+    addOpcodeInfo(Opcode.SGET_BOOLEAN, "sget-boolean", 0x63, new Format21c());
+    addOpcodeInfo(Opcode.SGET_BYTE, "sget-byte", 0x64, new Format21c());
+    addOpcodeInfo(Opcode.SGET_CHAR, "sget-char", 0x65, new Format21c());
+    addOpcodeInfo(Opcode.SGET_SHORT, "sget-short", 0x66, new Format21c());
+    addOpcodeInfo(Opcode.SPUT, "sput", 0x67, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_WIDE, "sput-wide", 0x68, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_OBJECT, "sput-object", 0x69, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_BOOLEAN, "sput-boolean", 0x6a, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_BYTE, "sput-byte", 0x6b, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_CHAR, "sput-char", 0x6c, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_SHORT, "sput-short", 0x6d, new Format21c());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL, "invoke-virtual", 0x6e, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_SUPER, "invoke-super", 0x6f, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_DIRECT, "invoke-direct", 0x70, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_STATIC, "invoke-static", 0x71, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_INTERFACE, "invoke-interface", 0x72, new Format35c());
+    addOpcodeInfo(Opcode.RETURN_VOID_BARRIER, "return-void-barrier", 0x73, new Format10x());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", 0x74, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_SUPER_RANGE, "invoke-super/range", 0x75, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_DIRECT_RANGE, "invoke-direct/range", 0x76, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_STATIC_RANGE, "invoke-static/range", 0x77, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_INTERFACE_RANGE, "invoke-interface/range",
+        0x78, new Format3rc());
+    addOpcodeInfo(Opcode.UNUSED_79, "unused-79", 0x79, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_7A, "unused-7a", 0x7a, new Format10x());
+    addOpcodeInfo(Opcode.NEG_INT, "neg-int", 0x7b, new Format12x());
+    addOpcodeInfo(Opcode.NOT_INT, "not-int", 0x7c, new Format12x());
+    addOpcodeInfo(Opcode.NEG_LONG, "neg-long", 0x7d, new Format12x());
+    addOpcodeInfo(Opcode.NOT_LONG, "not-long", 0x7e, new Format12x());
+    addOpcodeInfo(Opcode.NEG_FLOAT, "neg-float", 0x7f, new Format12x());
+    addOpcodeInfo(Opcode.NEG_DOUBLE, "neg-double", 0x80, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_LONG, "int-to-long", 0x81, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_FLOAT, "int-to-float", 0x82, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_DOUBLE, "int-to-double", 0x83, new Format12x());
+    addOpcodeInfo(Opcode.LONG_TO_INT, "long-to-int", 0x84, new Format12x());
+    addOpcodeInfo(Opcode.LONG_TO_FLOAT, "long-to-float", 0x85, new Format12x());
+    addOpcodeInfo(Opcode.LONG_TO_DOUBLE, "long-to-double", 0x86, new Format12x());
+    addOpcodeInfo(Opcode.FLOAT_TO_INT, "float-to-int", 0x87, new Format12x());
+    addOpcodeInfo(Opcode.FLOAT_TO_LONG, "float-to-long", 0x88, new Format12x());
+    addOpcodeInfo(Opcode.FLOAT_TO_DOUBLE, "float-to-double", 0x89, new Format12x());
+    addOpcodeInfo(Opcode.DOUBLE_TO_INT, "double-to-int", 0x8a, new Format12x());
+    addOpcodeInfo(Opcode.DOUBLE_TO_LONG, "double-to-long", 0x8b, new Format12x());
+    addOpcodeInfo(Opcode.DOUBLE_TO_FLOAT, "double-to-float", 0x8c, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_BYTE, "int-to-byte", 0x8d, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_CHAR, "int-to-char", 0x8e, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_SHORT, "int-to-short", 0x8f, new Format12x());
+    addOpcodeInfo(Opcode.ADD_INT, "add-int", 0x90, new Format23x());
+    addOpcodeInfo(Opcode.SUB_INT, "sub-int", 0x91, new Format23x());
+    addOpcodeInfo(Opcode.MUL_INT, "mul-int", 0x92, new Format23x());
+    addOpcodeInfo(Opcode.DIV_INT, "div-int", 0x93, new Format23x());
+    addOpcodeInfo(Opcode.REM_INT, "rem-int", 0x94, new Format23x());
+    addOpcodeInfo(Opcode.AND_INT, "and-int", 0x95, new Format23x());
+    addOpcodeInfo(Opcode.OR_INT, "or-int", 0x96, new Format23x());
+    addOpcodeInfo(Opcode.XOR_INT, "xor-int", 0x97, new Format23x());
+    addOpcodeInfo(Opcode.SHL_INT, "shl-int", 0x98, new Format23x());
+    addOpcodeInfo(Opcode.SHR_INT, "shr-int", 0x99, new Format23x());
+    addOpcodeInfo(Opcode.USHR_INT, "ushr-int", 0x9a, new Format23x());
+    addOpcodeInfo(Opcode.ADD_LONG, "add-long", 0x9b, new Format23x());
+    addOpcodeInfo(Opcode.SUB_LONG, "sub-long", 0x9c, new Format23x());
+    addOpcodeInfo(Opcode.MUL_LONG, "mul-long", 0x9d, new Format23x());
+    addOpcodeInfo(Opcode.DIV_LONG, "div-long", 0x9e, new Format23x());
+    addOpcodeInfo(Opcode.REM_LONG, "rem-long", 0x9f, new Format23x());
+    addOpcodeInfo(Opcode.AND_LONG, "and-long", 0xa0, new Format23x());
+    addOpcodeInfo(Opcode.OR_LONG, "or-long", 0xa1, new Format23x());
+    addOpcodeInfo(Opcode.XOR_LONG, "xor-long", 0xa2, new Format23x());
+    addOpcodeInfo(Opcode.SHL_LONG, "shl-long", 0xa3, new Format23x());
+    addOpcodeInfo(Opcode.SHR_LONG, "shr-long", 0xa4, new Format23x());
+    addOpcodeInfo(Opcode.USHR_LONG, "ushr-long", 0xa5, new Format23x());
+    addOpcodeInfo(Opcode.ADD_FLOAT, "add-float", 0xa6, new Format23x());
+    addOpcodeInfo(Opcode.SUB_FLOAT, "sub-float", 0xa7, new Format23x());
+    addOpcodeInfo(Opcode.MUL_FLOAT, "mul-float", 0xa8, new Format23x());
+    addOpcodeInfo(Opcode.DIV_FLOAT, "div-float", 0xa9, new Format23x());
+    addOpcodeInfo(Opcode.REM_FLOAT, "rem-float", 0xaa, new Format23x());
+    addOpcodeInfo(Opcode.ADD_DOUBLE, "add-double", 0xab, new Format23x());
+    addOpcodeInfo(Opcode.SUB_DOUBLE, "sub-double", 0xac, new Format23x());
+    addOpcodeInfo(Opcode.MUL_DOUBLE, "mul-double", 0xad, new Format23x());
+    addOpcodeInfo(Opcode.DIV_DOUBLE, "div-double", 0xae, new Format23x());
+    addOpcodeInfo(Opcode.REM_DOUBLE, "rem-double", 0xaf, new Format23x());
+    addOpcodeInfo(Opcode.ADD_INT_2ADDR, "add-int/2addr", 0xb0, new Format12x());
+    addOpcodeInfo(Opcode.SUB_INT_2ADDR, "sub-int/2addr", 0xb1, new Format12x());
+    addOpcodeInfo(Opcode.MUL_INT_2ADDR, "mul-int/2addr", 0xb2, new Format12x());
+    addOpcodeInfo(Opcode.DIV_INT_2ADDR, "div-int/2addr", 0xb3, new Format12x());
+    addOpcodeInfo(Opcode.REM_INT_2ADDR, "rem-int/2addr", 0xb4, new Format12x());
+    addOpcodeInfo(Opcode.AND_INT_2ADDR, "and-int/2addr", 0xb5, new Format12x());
+    addOpcodeInfo(Opcode.OR_INT_2ADDR, "or-int/2addr", 0xb6, new Format12x());
+    addOpcodeInfo(Opcode.XOR_INT_2ADDR, "xor-int/2addr", 0xb7, new Format12x());
+    addOpcodeInfo(Opcode.SHL_INT_2ADDR, "shl-int/2addr", 0xb8, new Format12x());
+    addOpcodeInfo(Opcode.SHR_INT_2ADDR, "shr-int/2addr", 0xb9, new Format12x());
+    addOpcodeInfo(Opcode.USHR_INT_2ADDR, "ushr-int/2addr", 0xba, new Format12x());
+    addOpcodeInfo(Opcode.ADD_LONG_2ADDR, "add-long/2addr", 0xbb, new Format12x());
+    addOpcodeInfo(Opcode.SUB_LONG_2ADDR, "sub-long/2addr", 0xbc, new Format12x());
+    addOpcodeInfo(Opcode.MUL_LONG_2ADDR, "mul-long/2addr", 0xbd, new Format12x());
+    addOpcodeInfo(Opcode.DIV_LONG_2ADDR, "div-long/2addr", 0xbe, new Format12x());
+    addOpcodeInfo(Opcode.REM_LONG_2ADDR, "rem-long/2addr", 0xbf, new Format12x());
+    addOpcodeInfo(Opcode.AND_LONG_2ADDR, "and-long/2addr", 0xc0, new Format12x());
+    addOpcodeInfo(Opcode.OR_LONG_2ADDR, "or-long/2addr", 0xc1, new Format12x());
+    addOpcodeInfo(Opcode.XOR_LONG_2ADDR, "xor-long/2addr", 0xc2, new Format12x());
+    addOpcodeInfo(Opcode.SHL_LONG_2ADDR, "shl-long/2addr", 0xc3, new Format12x());
+    addOpcodeInfo(Opcode.SHR_LONG_2ADDR, "shr-long/2addr", 0xc4, new Format12x());
+    addOpcodeInfo(Opcode.USHR_LONG_2ADDR, "ushr-long/2addr", 0xc5, new Format12x());
+    addOpcodeInfo(Opcode.ADD_FLOAT_2ADDR, "add-float/2addr", 0xc6, new Format12x());
+    addOpcodeInfo(Opcode.SUB_FLOAT_2ADDR, "sub-float/2addr", 0xc7, new Format12x());
+    addOpcodeInfo(Opcode.MUL_FLOAT_2ADDR, "mul-float/2addr", 0xc8, new Format12x());
+    addOpcodeInfo(Opcode.DIV_FLOAT_2ADDR, "div-float/2addr", 0xc9, new Format12x());
+    addOpcodeInfo(Opcode.REM_FLOAT_2ADDR, "rem-float/2addr", 0xca, new Format12x());
+    addOpcodeInfo(Opcode.ADD_DOUBLE_2ADDR, "add-double/2addr", 0xcb, new Format12x());
+    addOpcodeInfo(Opcode.SUB_DOUBLE_2ADDR, "sub-double/2addr", 0xcc, new Format12x());
+    addOpcodeInfo(Opcode.MUL_DOUBLE_2ADDR, "mul-double/2addr", 0xcd, new Format12x());
+    addOpcodeInfo(Opcode.DIV_DOUBLE_2ADDR, "div-double/2addr", 0xce, new Format12x());
+    addOpcodeInfo(Opcode.REM_DOUBLE_2ADDR, "rem-double/2addr", 0xcf, new Format12x());
+    addOpcodeInfo(Opcode.ADD_INT_LIT16, "add-int/lit16", 0xd0, new Format22s());
+    addOpcodeInfo(Opcode.RSUB_INT, "rsub-int", 0xd1, new Format22s());
+    addOpcodeInfo(Opcode.MUL_INT_LIT16, "mul-int/lit16", 0xd2, new Format22s());
+    addOpcodeInfo(Opcode.DIV_INT_LIT16, "div-int/lit16", 0xd3, new Format22s());
+    addOpcodeInfo(Opcode.REM_INT_LIT16, "rem-int/lit16", 0xd4, new Format22s());
+    addOpcodeInfo(Opcode.AND_INT_LIT16, "and-int/lit16", 0xd5, new Format22s());
+    addOpcodeInfo(Opcode.OR_INT_LIT16, "or-int/lit16", 0xd6, new Format22s());
+    addOpcodeInfo(Opcode.XOR_INT_LIT16, "xor-int/lit16", 0xd7, new Format22s());
+    addOpcodeInfo(Opcode.ADD_INT_LIT8, "add-int/lit8", 0xd8, new Format22b());
+    addOpcodeInfo(Opcode.RSUB_INT_LIT8, "rsub-int/lit8", 0xd9, new Format22b());
+    addOpcodeInfo(Opcode.MUL_INT_LIT8, "mul-int/lit8", 0xda, new Format22b());
+    addOpcodeInfo(Opcode.DIV_INT_LIT8, "div-int/lit8", 0xdb, new Format22b());
+    addOpcodeInfo(Opcode.REM_INT_LIT8, "rem-int/lit8", 0xdc, new Format22b());
+    addOpcodeInfo(Opcode.AND_INT_LIT8, "and-int/lit8", 0xdd, new Format22b());
+    addOpcodeInfo(Opcode.OR_INT_LIT8, "or-int/lit8", 0xde, new Format22b());
+    addOpcodeInfo(Opcode.XOR_INT_LIT8, "xor-int/lit8", 0xdf, new Format22b());
+    addOpcodeInfo(Opcode.SHL_INT_LIT8, "shl-int/lit8", 0xe0, new Format22b());
+    addOpcodeInfo(Opcode.SHR_INT_LIT8, "shr-int/lit8", 0xe1, new Format22b());
+    addOpcodeInfo(Opcode.USHR_INT_LIT8, "ushr-int/lit8", 0xe2, new Format22b());
+    addOpcodeInfo(Opcode.IGET_QUICK, "+iget-quick", 0xe3, new Format22c());
+    addOpcodeInfo(Opcode.IGET_WIDE_QUICK, "+iget-wide-quick", 0xe4, new Format22c());
+    addOpcodeInfo(Opcode.IGET_OBJECT_QUICK, "+iget-object-quick", 0xe5, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_QUICK, "+iput-quick", 0xe6, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_WIDE_QUICK, "+iput-wide-quick", 0xe7, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_OBJECT_QUICK, "+iput-object-quick", 0xe8, new Format22c());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL_QUICK, "+invoke-virtual-quick", 0xe9, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL_QUICK_RANGE, "+invoke-virtual-quick/range",
+        0xea, new Format3rc());
+    addOpcodeInfo(Opcode.IPUT_BOOLEAN_QUICK, "+iput-boolean-quick", 0xeb, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_BYTE_QUICK, "+iput-byte-quick", 0xec, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_CHAR_QUICK, "+iput-char-quick", 0xed, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_SHORT_QUICK, "+iput-short-quick", 0xee, new Format22c());
+    addOpcodeInfo(Opcode.UNUSED_EF, "unused-ef", 0xef, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F0, "unused-f0", 0xf0, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F1, "unused-f1", 0xf1, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F2, "unused-f2", 0xf2, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F3, "unused-f3", 0xf3, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F4, "unused-f4", 0xf4, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F5, "unused-f5", 0xf5, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F6, "unused-f6", 0xf6, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F7, "unused-f7", 0xf7, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F8, "unused-f8", 0xf8, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F9, "unused-f9", 0xf9, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FA, "unused-fa", 0xfa, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FB, "unused-fb", 0xfb, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FC, "unused-fc", 0xfc, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FD, "unused-fd", 0xfd, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FE, "unused-fe", 0xfe, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FF, "unused-ff", 0xff, new Format10x());
+    if (opcode_map_by_int.size() != 256) {
+      Log.errorAndQuit("Incorrect number of bytecodes defined.");
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java
new file mode 100644
index 0000000..4ca2463
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MapItem implements RawDexObject {
+  public static final int TYPE_HEADER_ITEM = 0x0;
+  public static final int TYPE_STRING_ID_ITEM = 0x1;
+  public static final int TYPE_TYPE_ID_ITEM = 0x2;
+  public static final int TYPE_PROTO_ID_ITEM = 0x3;
+  public static final int TYPE_FIELD_ID_ITEM = 0x4;
+  public static final int TYPE_METHOD_ID_ITEM = 0x5;
+  public static final int TYPE_CLASS_DEF_ITEM = 0x6;
+  public static final int TYPE_MAP_LIST = 0x1000;
+  public static final int TYPE_TYPE_LIST = 0x1001;
+  public static final int TYPE_ANNOTATION_SET_REF_LIST = 0x1002;
+  public static final int TYPE_ANNOTATION_SET_ITEM = 0x1003;
+  public static final int TYPE_CLASS_DATA_ITEM = 0x2000;
+  public static final int TYPE_CODE_ITEM = 0x2001;
+  public static final int TYPE_STRING_DATA_ITEM = 0x2002;
+  public static final int TYPE_DEBUG_INFO_ITEM = 0x2003;
+  public static final int TYPE_ANNOTATION_ITEM = 0x2004;
+  public static final int TYPE_ENCODED_ARRAY_ITEM = 0x2005;
+  public static final int TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006;
+
+  public short type;
+  public int size;
+  public Offset offset;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    type = file.readUShort();
+    file.readUShort(); // Unused padding.
+    size = file.readUInt();
+    if (type == TYPE_HEADER_ITEM) {
+      offset = file.getOffsetTracker().getNewHeaderOffset(file.readUInt());
+    } else {
+      offset = file.getOffsetTracker().getNewOffset(file.readUInt());
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUShort(type);
+    file.writeUShort((short) 0); // Unused padding.
+    file.writeUInt(size);
+    file.getOffsetTracker().tryToWriteOffset(offset, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
new file mode 100644
index 0000000..080b5a4
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MapList implements RawDexObject {
+
+  private RawDexFile rawDexFile;
+
+  public int size;
+  public List<MapItem> mapItems;
+
+  public MapList(RawDexFile rawDexFile) {
+    this.rawDexFile = rawDexFile;
+  }
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    // Find the map list.
+    file.seek(rawDexFile.header.mapOff.getOriginalOffset());
+
+    file.getOffsetTracker().getNewOffsettable(file, this);
+
+    // Get the number of entries.
+    size = file.readUInt();
+
+    // Allocate and populate the array.
+    mapItems = new ArrayList<MapItem>(size);
+    for (int i = 0; i < size; i++) {
+      MapItem mapItem = new MapItem();
+      mapItems.add(mapItem);
+      mapItem.read(file);
+    }
+
+    file.getOffsetTracker().rememberPointAfterMapList();
+
+    // NB: We track the current index into the MapList, so when we encounter the DebugInfoItem
+    // MapItem, we know how to find the next MapItem, so we know how large the DebugInfo
+    // area is, so we can copy it as a blob.
+    int mapItemIdx = 0;
+
+    // Iterate through the list, and create all the other data structures.
+    for (MapItem mapItem : mapItems) {
+      file.seek(mapItem.offset.getOriginalOffset());
+      switch (mapItem.type) {
+        case MapItem.TYPE_HEADER_ITEM:
+          // Already read it; skip.
+          break;
+        case MapItem.TYPE_STRING_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            StringIdItem newStringId = new StringIdItem();
+            rawDexFile.stringIds.add(newStringId);
+            newStringId.read(file);
+          }
+          break;
+        case MapItem.TYPE_TYPE_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            TypeIdItem newTypeId = new TypeIdItem();
+            rawDexFile.typeIds.add(newTypeId);
+            newTypeId.read(file);
+          }
+          break;
+        case MapItem.TYPE_PROTO_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            ProtoIdItem newProtoId = new ProtoIdItem();
+            rawDexFile.protoIds.add(newProtoId);
+            newProtoId.read(file);
+          }
+          break;
+        case MapItem.TYPE_FIELD_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            FieldIdItem newFieldId = new FieldIdItem();
+            rawDexFile.fieldIds.add(newFieldId);
+            newFieldId.read(file);
+          }
+          break;
+        case MapItem.TYPE_METHOD_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            MethodIdItem newMethodId = new MethodIdItem();
+            rawDexFile.methodIds.add(newMethodId);
+            newMethodId.read(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DEF_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            ClassDefItem newClassDef = new ClassDefItem();
+            rawDexFile.classDefs.add(newClassDef);
+            newClassDef.read(file);
+          }
+          break;
+        case MapItem.TYPE_MAP_LIST:
+          // Already read it; skip.
+          break;
+        case MapItem.TYPE_TYPE_LIST:
+          rawDexFile.typeLists = new ArrayList<TypeList>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            TypeList newTypeList = new TypeList();
+            rawDexFile.typeLists.add(newTypeList);
+            newTypeList.read(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_REF_LIST:
+          rawDexFile.annotationSetRefLists =
+            new ArrayList<AnnotationSetRefList>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationSetRefList newAnnotationSetRefList = new AnnotationSetRefList();
+            rawDexFile.annotationSetRefLists.add(newAnnotationSetRefList);
+            newAnnotationSetRefList.read(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_ITEM:
+          rawDexFile.annotationSetItems = new ArrayList<AnnotationSetItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationSetItem newAnnotationSetItem = new AnnotationSetItem();
+            rawDexFile.annotationSetItems.add(newAnnotationSetItem);
+            newAnnotationSetItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DATA_ITEM:
+          rawDexFile.classDatas = new ArrayList<ClassDataItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            ClassDataItem newClassData = new ClassDataItem();
+            rawDexFile.classDatas.add(newClassData);
+            newClassData.read(file);
+          }
+          break;
+        case MapItem.TYPE_CODE_ITEM:
+          rawDexFile.codeItems = new ArrayList<CodeItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            CodeItem newCodeItem = new CodeItem();
+            rawDexFile.codeItems.add(newCodeItem);
+            newCodeItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_STRING_DATA_ITEM:
+          rawDexFile.stringDatas = new ArrayList<StringDataItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            StringDataItem newStringData = new StringDataItem();
+            rawDexFile.stringDatas.add(newStringData);
+            newStringData.read(file);
+          }
+          break;
+        case MapItem.TYPE_DEBUG_INFO_ITEM:
+        {
+          // We aren't interested in updating the debug data, so just read it as a blob.
+          int start = mapItem.offset.getOriginalOffset();
+          int end = mapItems.get(mapItemIdx + 1).offset.getOriginalOffset();
+          int size = end - start;
+          rawDexFile.debugInfoItem = new DebugInfoItem(size);
+          rawDexFile.debugInfoItem.read(file);
+          break;
+        }
+        case MapItem.TYPE_ANNOTATION_ITEM:
+          rawDexFile.annotationItems = new ArrayList<AnnotationItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationItem newAnnotationItem = new AnnotationItem();
+            rawDexFile.annotationItems.add(newAnnotationItem);
+            newAnnotationItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_ENCODED_ARRAY_ITEM:
+          rawDexFile.encodedArrayItems = new ArrayList<EncodedArrayItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            EncodedArrayItem newEncodedArrayItem = new EncodedArrayItem();
+            rawDexFile.encodedArrayItems.add(newEncodedArrayItem);
+            newEncodedArrayItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+          rawDexFile.annotationsDirectoryItems =
+          new ArrayList<AnnotationsDirectoryItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationsDirectoryItem newAnnotationsDirectoryItem = new AnnotationsDirectoryItem();
+            rawDexFile.annotationsDirectoryItems.add(newAnnotationsDirectoryItem);
+            newAnnotationsDirectoryItem.read(file);
+          }
+          break;
+        default:
+          Log.errorAndQuit("Encountered unknown map item when reading map item list.");
+      }
+      mapItemIdx++;
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(mapItems.size());
+    for (MapItem mapItem : mapItems) {
+      mapItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java
new file mode 100644
index 0000000..1a24fb6
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MethodAnnotation implements RawDexObject {
+  public int methodIdx;
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    methodIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(methodIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.METHOD_ID && methodIdx >= insertedIdx) {
+      methodIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java
new file mode 100644
index 0000000..12d0187
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MethodIdItem implements RawDexObject {
+  public short classIdx;
+  public short protoIdx;
+  public int nameIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classIdx = file.readUShort();
+    protoIdx = file.readUShort();
+    nameIdx = file.readUInt();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUShort(classIdx);
+    file.writeUShort(protoIdx);
+    file.writeUInt(nameIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+      classIdx++;
+    }
+    if (kind == IndexUpdateKind.PROTO_ID && protoIdx >= insertedIdx) {
+      protoIdx++;
+    }
+    if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+      nameIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java b/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java
new file mode 100644
index 0000000..b6718e3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+public class Offset {
+  /**
+   * The absolute value of this offset as it was originally read.
+   */
+  private int originalOffset;
+
+  /**
+   * The Offsettable that this Offset points to.
+   */
+  private Offsettable offsettable;
+
+  /**
+   * The location of this Offset in the new file, ONLY SET IF the Offset
+   * couldn't be written because what it points to hasn't been written
+   * yet.
+   */
+  private int outputLocation;
+
+  /**
+   * Was the output location for this Offset set?.
+   */
+  private boolean outputLocationSet;
+
+  /**
+   * Does this Offset need to be written out using ULEB128?.
+   */
+  private boolean useUleb128;
+
+  /**
+   * Was this Offset created after reading, during mutation?.
+   */
+  private boolean isNewOffset;
+
+  /**
+   * Only one Offset should have this flag set, the MapItem that points
+   * to the HeaderItem.
+   */
+  private boolean pointsAtHeader;
+
+  /**
+   * If an Offset pointed at 0 (because it is not actually a valid Offset),
+   * and it's not pointing at the header, then this is set.
+   */
+  private boolean pointsAtNull;
+
+  public Offset(boolean header) {
+    pointsAtHeader = header;
+  }
+
+  public RawDexObject getPointedToItem() {
+    return offsettable.getItem();
+  }
+
+  public boolean pointsToSomething() {
+    return offsettable != null;
+  }
+
+  public boolean pointsAtNull() {
+    return pointsAtNull;
+  }
+
+  public boolean pointsAtHeader() {
+    return pointsAtHeader;
+  }
+
+  /**
+   * Returns true if this Offset points at the provided RawDexObject.
+   */
+  public boolean pointsToThisItem(RawDexObject thisItem) {
+    if (!pointsToSomething()) {
+      return false;
+    }
+    return (offsettable.getItem().equals(thisItem));
+  }
+
+  /**
+   * Returns true if this Offset points at the provided Offsettable.
+   */
+  public boolean pointsToThisOffsettable(Offsettable thisOffsettable) {
+    if (!pointsToSomething()) {
+      return false;
+    }
+    return (offsettable.equals(thisOffsettable));
+  }
+
+  /**
+   * Makes this Offset point at a new Offsettable.
+   */
+  public void pointTo(Offsettable offsettableItem) {
+    if (offsettable != null) {
+      Log.debug("Updating what an Offset points to...");
+    }
+    offsettable = offsettableItem;
+  }
+
+  /**
+   * Call this to make an Offset that pointed at null before now point at something.
+   * An Offset may have previously pointed at null before...
+   * Example: if there are no fields referred to in a DEX file, then header.field_ids_off
+   * will point at null. We distinguish when Offsets point at null, and are not pointing
+   * at the header (only the header MapItem should do this) with a flag. Therefore, this
+   * method is needed to indicate that this Offset now points at something.
+   */
+  public void unsetNullAndPointTo(Offsettable offsettableItem) {
+    pointsAtNull = false;
+    if (offsettable != null) {
+      Log.debug("Updating what an Offset points to...");
+    }
+    offsettable = offsettableItem;
+  }
+
+  public void pointToNew(Offsettable offsettableItem) {
+    offsettable = offsettableItem;
+    isNewOffset = true;
+  }
+
+  public int getNewPositionOfItem() {
+    return offsettable.getNewPosition();
+  }
+
+  public boolean usesUleb128() {
+    return useUleb128;
+  }
+
+  /**
+   * Mark this Offset as using the ULEB128 encoding.
+   */
+  public void setUsesUleb128() {
+    if (useUleb128) {
+      throw new Error("Offset is already marked as using ULEB128!");
+    }
+    useUleb128 = true;
+  }
+
+  public boolean isNewOffset() {
+    return isNewOffset;
+  }
+
+  public void setPointsAtNull() {
+    pointsAtNull = true;
+  }
+
+  public void setOutputLocation(int loc) {
+    outputLocation = loc;
+    outputLocationSet = true;
+  }
+
+  /**
+   * Get the location in the output DEX file where this offset has been written.
+   * (This is used when patching Offsets when the Offsettable position was not
+   * known at the time of writing out the Offset.)
+   */
+  public int getOutputLocation() {
+    if (!outputLocationSet) {
+      throw new Error("Output location was not set yet!");
+    }
+    return outputLocation;
+  }
+
+  public void setOriginalOffset(int offset) {
+    originalOffset = offset;
+  }
+
+  public int getOriginalOffset() {
+    return originalOffset;
+  }
+
+  public boolean readyForWriting() {
+    return offsettable.readyForFinalOffsetToBeWritten();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java b/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java
new file mode 100644
index 0000000..34d609a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java
@@ -0,0 +1,513 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class allows the recording of both Offsettable items (that is, items that can be
+ * referred to by an offset somewhere else in the file - RawDexObjects) and Offsets.
+ * The idea in a nutshell is that for every Offsettable item we read, we remember
+ * its original position in the file using a map, and the order in which the Offsettables were
+ * written out. We also remember every Offset we read in, and its value. Then, after reading
+ * the whole file, we use the map to find the Offsettable it pointed at.
+ * Then, as we write out the file, for every Offsettable we write out, we record its new position,
+ * using the order we collected earlier. For every Offset we write out, we look at its Offsettable
+ * to see where it was written. If it hasn't been written yet, then we write out a blank value
+ * for the time being, remember where that blank value was written, and put the Offset into a
+ * table for patching once everything has been written out.
+ * There are some variables (index_after_map_list, restore_point) used for remembering certain
+ * points to jump forward and back to, because we cannot read and write the file out in exactly
+ * the same order.
+ * TODO: Perhaps it makes more sense to just reorder the offsettable_table once it's been read,
+ * in preparation for the order in which the file is written out?
+ * Finally, we provide methods for adding new Offsettable items into the right place in the order
+ * table.
+ */
+public class OffsetTracker {
+  /**
+   * A map from the original offset in the input DEX file to
+   * the Offsettable it points to. (That Offsettable will contain
+   * the actual item, and later on the new offset for the item when
+   * the item is written out.
+   */
+  private Map<Integer, Offsettable> offsettableMap;
+
+  /**
+   * A table of all Offsettables. We need to ensure we write out
+   * all items in the same order we read them in, to make sure we update
+   * the Offsettable.new_position field with the correct value wrt to
+   * the original_position field.
+   */
+  private List<Offsettable> offsettableTable;
+
+  /**
+   * A table of all offsets that is populated as we read in the DEX file.
+   * As the end, we find the correct Offsettable for the Offset in the above
+   * map, and associate them.
+   */
+  private List<Offset> needsAssociationTable;
+
+  /**
+   * A table of all offsets that we could not write out an updated offset for
+   * as we write out a DEX file. Will be checked after writing is complete,
+   * to allow specific patching of each offset's location as at that point
+   * all Offsettables will have been updated with their new position.
+   */
+  private List<Offset> needsUpdateTable;
+
+  /**
+   * Tracks how far we are through the offsettable_table as we write out the file.
+   */
+  private int offsettableTableIdx;
+
+  /**
+   * Because we write in a slightly different order to how we read
+   * (why? to read, we read the header, then the map list, and then use the map
+   *  list to read everything else.
+   *  when we write, we write the header, and then we cannot write the map list
+   *  because we don't know where it will go yet, so we write everything else first)
+   * so: we remember the position in the offsettable_table after we read the map list,
+   * so we can skip there after we write out the header.
+   */
+  private int indexAfterMapList;
+
+  /**
+   * Related to index_after_map_list, this is the index we save when we're jumping back to
+   * write the map list.
+   */
+  private int restorePoint;
+
+  /**
+   * Create a new OffsetTracker. Should persist between parsing a DEX file, and outputting
+   * the mutated DEX file.
+   */
+  public OffsetTracker() {
+    offsettableMap = new HashMap<Integer,Offsettable>();
+    offsettableTable = new ArrayList<Offsettable>();
+    needsAssociationTable = new ArrayList<Offset>();
+    needsUpdateTable = new ArrayList<Offset>();
+  }
+
+  /**
+   * Lookup an Item by the offset it had in the input DEX file.
+   * @param offset The offset in the input DEX file.
+   * @return The corresponding Item.
+   */
+  public RawDexObject getItemByOffset(int offset) {
+    return offsettableMap.get(offset).getItem();
+  }
+
+  /**
+   * As Items are read in, they call this function once they have word-aligned the file pointer,
+   * to record their position and themselves into an Offsettable object, that will be tracked.
+   * @param file Used for recording position into the new Offsettable.
+   * @param item Used for recording the relevant Item into the new Offsettable.
+   */
+  public void getNewOffsettable(DexRandomAccessFile file, RawDexObject item) throws IOException {
+    Offsettable offsettable = new Offsettable(item, false);
+    offsettable.setOriginalPosition((int) file.getFilePointer());
+    offsettableMap.put(offsettable.getOriginalPosition(), offsettable);
+    offsettableTable.add(offsettable);
+  }
+
+  /**
+   * As Items read in Offsets, they call this function with the offset they originally
+   * read from the file, to allow later association with an Offsettable.
+   * @param originalOffset The original offset read from the input DEX file.
+   * @return An Offset that will later be associated with an Offsettable.
+   */
+  public Offset getNewOffset(int originalOffset) throws IOException {
+    Offset offset = new Offset(false);
+    offset.setOriginalOffset(originalOffset);
+    needsAssociationTable.add(offset);
+    return offset;
+  }
+
+  /**
+   * Only MapItem should call this method, when the MapItem that points to the header
+   * is read.
+   */
+  public Offset getNewHeaderOffset(int originalOffset) throws IOException {
+    Offset offset = new Offset(true);
+    offset.setOriginalOffset(originalOffset);
+    needsAssociationTable.add(offset);
+    return offset;
+  }
+
+  /**
+   * Call this after reading, to associate Offsets with Offsettables.
+   */
+  public void associateOffsets() {
+    for (Offset offset : needsAssociationTable) {
+      if (offset.getOriginalOffset() == 0 && !(offset.pointsAtHeader())) {
+        offset.setPointsAtNull();
+      } else {
+        offset.pointTo(offsettableMap.get(offset.getOriginalOffset()));
+        if (!offset.pointsToSomething()) {
+          Log.error(String.format("Couldn't find original offset 0x%x!",
+              offset.getOriginalOffset()));
+        }
+      }
+    }
+    needsAssociationTable.clear();
+  }
+
+  /**
+   * As Items are written out into the output DEX file, this function is called
+   * to update the next Offsettable with the file pointer's current position.
+   * This should allow the tracking of new offset locations.
+   * This also requires that reading and writing of all items happens in the same order
+   * (with the exception of the map list, see above)
+   * @param file Used for recording the new position.
+   */
+  public void updatePositionOfNextOffsettable(DexRandomAccessFile file) throws IOException {
+    if (offsettableTableIdx == offsettableTable.size()) {
+      Log.errorAndQuit("Not all created Offsettable items have been added to the "
+          + "Offsettable Table!");
+    }
+    Offsettable offsettable = offsettableTable.get(offsettableTableIdx);
+    offsettable.setNewPosition((int) file.getFilePointer());
+    offsettableTableIdx++;
+  }
+
+  /**
+   * As Items are written out, any writing out of an offset must call this function, passing
+   * in the relevant offset. This function will write out the offset, if the associated
+   * Offsettable has been updated with its new position, or else will write out a null value, and
+   * the Offset will be stored for writing after all Items have been written, and all
+   * Offsettables MUST have been updated.
+   * @param offset The offset received from getNewOffset().
+   * @param file Used for writing out to the file.
+   * @param useUleb128 Whether or not the offset should be written in UINT or ULEB128 form.
+   */
+  public void tryToWriteOffset(Offset offset, DexRandomAccessFile file, boolean useUleb128)
+      throws IOException {
+    if (!offset.isNewOffset() && (!offset.pointsToSomething())) {
+      if (useUleb128) {
+        file.writeUleb128(0);
+      } else {
+        file.writeUInt(0);
+      }
+      return;
+    }
+
+    if (offset.readyForWriting()) {
+      if (useUleb128) {
+        file.writeUleb128(offset.getNewPositionOfItem());
+      } else {
+        file.writeUInt(offset.getNewPositionOfItem());
+      }
+    } else {
+      offset.setOutputLocation((int) file.getFilePointer());
+      if (useUleb128) {
+        file.writeLargestUleb128(offset.getOriginalOffset());
+        offset.setUsesUleb128();
+      } else {
+        file.writeUInt(offset.getOriginalOffset());
+      }
+      needsUpdateTable.add(offset);
+    }
+  }
+
+  /**
+   * This is called after all writing has finished, to write out any Offsets
+   * that could not be written out during the original writing phase, because their
+   * associated Offsettables hadn't had their new positions calculated yet.
+   * @param file Used for writing out to the file.
+   */
+  public void updateOffsets(DexRandomAccessFile file) throws IOException {
+    if (offsettableTableIdx != offsettableTable.size()) {
+      Log.errorAndQuit("Being asked to update dangling offsets but the "
+          + "correct number of offsettables has not been written out!");
+    }
+    for (Offset offset : needsUpdateTable) {
+      file.seek(offset.getOutputLocation());
+      if (offset.usesUleb128()) {
+        file.writeLargestUleb128(offset.getNewPositionOfItem());
+      } else {
+        file.writeUInt(offset.getNewPositionOfItem());
+      }
+    }
+    needsUpdateTable.clear();
+  }
+
+  /**
+   * Called after writing out the header, to skip to after the map list.
+   */
+  public void skipToAfterMapList() {
+    offsettableTableIdx = indexAfterMapList;
+  }
+
+  /**
+   * Called once the map list needs to be written out, to set the
+   * offsettable table index back to the right place.
+   */
+  public void goBackToMapList() {
+    restorePoint = offsettableTableIdx;
+    offsettableTableIdx = (indexAfterMapList - 1);
+  }
+
+  /**
+   * Called once the map list has been written out, to set the
+   * offsettable table index back to where it was before.
+   */
+  public void goBackToPreviousPoint() {
+    if (offsettableTableIdx != indexAfterMapList) {
+      Log.errorAndQuit("Being asked to go to the point before the MapList was written out,"
+          + " but we're not in the right place.");
+    }
+    offsettableTableIdx = restorePoint;
+  }
+
+  /**
+   * Called after reading in the map list, to remember the point to be skipped
+   * to later.
+   */
+  public void rememberPointAfterMapList() {
+    indexAfterMapList = offsettableTable.size();
+  }
+
+  private void updateHeaderOffsetIfValid(Offset offset, Offsettable previousFirst,
+      Offsettable newFirst, String offsetName) {
+    if (offset.pointsToThisOffsettable(previousFirst)) {
+      offset.pointToNew(newFirst);
+    } else {
+      Log.errorAndQuit("Header " + offsetName + " offset not pointing at first element?");
+    }
+  }
+
+  private void addTypeListsToMapFile(RawDexFile rawDexFile, Offsettable typeListOffsettable) {
+    // Create a MapItem for the TypeLists
+    MapItem typeListMapItem = new MapItem();
+    typeListMapItem.offset = new Offset(false);
+    typeListMapItem.offset.pointToNew(typeListOffsettable);
+    typeListMapItem.type = MapItem.TYPE_TYPE_LIST;
+    typeListMapItem.size = 1;
+
+    // Insert into the MapList.
+    // (first, find the MapItem that points to StringDataItems...)
+    int idx = 0;
+    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+      if (mapItem.type == MapItem.TYPE_STRING_DATA_ITEM) {
+        break;
+      }
+      idx++;
+    }
+    // (now insert the TypeList MapItem just before the StringDataItem one...)
+    rawDexFile.mapList.mapItems.add(idx, typeListMapItem);
+  }
+
+  private void addFieldIdsToHeaderAndMapFile(RawDexFile rawDexFile,
+      Offsettable fieldOffsettable) {
+    // Add the field IDs to the header.
+    rawDexFile.header.fieldIdsOff.unsetNullAndPointTo(fieldOffsettable);
+    rawDexFile.header.fieldIdsSize = 1;
+
+    // Create a MapItem for the field IDs.
+    MapItem fieldMapItem = new MapItem();
+    fieldMapItem.offset = new Offset(false);
+    fieldMapItem.offset.pointToNew(fieldOffsettable);
+    fieldMapItem.type = MapItem.TYPE_FIELD_ID_ITEM;
+    fieldMapItem.size = 1;
+
+    // Insert into the MapList.
+    // (first, find the MapItem that points to MethodIdItems...)
+    int idx = 0;
+    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+      if (mapItem.type == MapItem.TYPE_METHOD_ID_ITEM) {
+        break;
+      }
+      idx++;
+    }
+    // (now insert the FieldIdItem MapItem just before the MethodIdItem one...)
+    rawDexFile.mapList.mapItems.add(idx, fieldMapItem);
+  }
+
+
+  private void updateOffsetsInHeaderAndMapFile(RawDexFile rawDexFile,
+      Offsettable newFirstOffsettable) {
+    Offsettable prevFirstOffsettable = null;
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i) == newFirstOffsettable) {
+        prevFirstOffsettable = offsettableTable.get(i + 1);
+        break;
+      }
+    }
+    if (prevFirstOffsettable == null) {
+      Log.errorAndQuit("When calling updateMapListOffsets, could not find new "
+          + "first offsettable?");
+    }
+
+    // Based on the type of the item we just added, check the relevant Offset in the header
+    // and if it pointed at the prev_first_offsettable, make it point at the new one.
+    // NB: if it isn't pointing at the prev one, something is wrong.
+    HeaderItem header = rawDexFile.header;
+    if (newFirstOffsettable.getItem() instanceof StringIdItem) {
+      updateHeaderOffsetIfValid(header.stringIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "StringID");
+    } else if (newFirstOffsettable.getItem() instanceof TypeIdItem) {
+      updateHeaderOffsetIfValid(header.typeIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "TypeID");
+    } else if (newFirstOffsettable.getItem() instanceof ProtoIdItem) {
+      updateHeaderOffsetIfValid(header.protoIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "ProtoID");
+    } else if (newFirstOffsettable.getItem() instanceof FieldIdItem) {
+      updateHeaderOffsetIfValid(header.fieldIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "FieldID");
+    } else if (newFirstOffsettable.getItem() instanceof MethodIdItem) {
+      updateHeaderOffsetIfValid(header.methodIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "MethodID");
+    } else if (newFirstOffsettable.getItem() instanceof ClassDefItem) {
+      updateHeaderOffsetIfValid(header.classDefsOff, prevFirstOffsettable,
+          newFirstOffsettable, "ClassDef");
+    }
+
+    // Now iterate through the MapList's MapItems, and see if their Offsets pointed at the
+    // prev_first_offsettable, and if so, make them now point at the new_first_offsettable.
+    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+      if (mapItem.offset.pointsToThisOffsettable(prevFirstOffsettable)) {
+        Log.info("Updating offset in MapItem (type: " + mapItem.type + ") after "
+            + "we called insertNewOffsettableAsFirstOfType()");
+        mapItem.offset.pointToNew(newFirstOffsettable);
+      }
+    }
+  }
+
+  private void insertOffsettableAt(int idx, Offsettable offsettable) {
+    offsettableTable.add(idx, offsettable);
+    if (indexAfterMapList > idx) {
+      indexAfterMapList++;
+    }
+    if (restorePoint > idx) {
+      restorePoint++;
+    }
+  }
+
+  /**
+   * If we're creating our first TypeList, then IdCreator has to call this method to
+   * ensure it gets put into the correct place in the offsettable table.
+   * This assumes TypeLists always come before StringDatas.
+   */
+  public Offsettable insertNewOffsettableAsFirstEverTypeList(RawDexObject item,
+      RawDexFile rawDexFile) {
+    // We find the first StringDataItem, the type lists will come before this.
+    Log.info("Calling insertNewOffsettableAsFirstEverTypeList()");
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() instanceof StringDataItem) {
+        Offsettable offsettable = new Offsettable(item, true);
+        insertOffsettableAt(i, offsettable);
+        addTypeListsToMapFile(rawDexFile, offsettable);
+        return offsettable;
+      }
+    }
+    Log.errorAndQuit("Could not find any StringDataItems to insert the type list before.");
+    return null;
+  }
+
+  /**
+   * If we're creating our first FieldId, then IdCreator has to call this method to
+   * ensure it gets put into the correct place in the offsettable table.
+   * This assumes FieldIds always come before MethodIds.
+   */
+  public Offsettable insertNewOffsettableAsFirstEverField(RawDexObject item,
+      RawDexFile rawDexFile) {
+    // We find the first MethodIdItem, the fields will come before this.
+    Log.info("Calling insertNewOffsettableAsFirstEverField()");
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() instanceof MethodIdItem) {
+        Offsettable offsettable = new Offsettable(item, true);
+        insertOffsettableAt(i, offsettable);
+        addFieldIdsToHeaderAndMapFile(rawDexFile, offsettable);
+        return offsettable;
+      }
+    }
+    Log.errorAndQuit("Could not find any MethodIdItems to insert the field before.");
+    return null;
+  }
+
+  /**
+   * If we're creating a new Item (such as FieldId, MethodId) that is placed into the
+   * first position of the relevant ID table, then IdCreator has to call this method to
+   * ensure it gets put into the correct place in the offsettable table.
+   */
+  public Offsettable insertNewOffsettableAsFirstOfType(RawDexObject item,
+      RawDexFile rawDexFile) {
+    Log.debug("Calling insertNewOffsettableAsFirstOfType()");
+    int index = getOffsettableIndexForFirstItemType(item);
+    if (index == -1) {
+      Log.errorAndQuit("Could not find any object of class: " + item.getClass());
+    }
+    Offsettable offsettable = new Offsettable(item, true);
+    insertOffsettableAt(index, offsettable);
+    updateOffsetsInHeaderAndMapFile(rawDexFile, offsettable);
+    return offsettable;
+  }
+
+  /**
+   * IdCreator has to call this method when it creates a new IdItem, to make sure it
+   * gets put into the correct place in the offsettable table. IdCreator should
+   * provide the IdItem that should come before this new IdItem.
+   */
+  public Offsettable insertNewOffsettableAfter(RawDexObject item, RawDexObject itemBefore) {
+    Log.debug("Calling insertNewOffsettableAfter()");
+    int index = getOffsettableIndexForItem(itemBefore);
+    if (index == -1) {
+      Log.errorAndQuit("Did not find specified 'after' object in offsettable table.");
+    }
+    Offsettable offsettable = new Offsettable(item, true);
+    insertOffsettableAt(index + 1, offsettable);
+    return offsettable;
+  }
+
+  private int getOffsettableIndexForFirstItemType(RawDexObject item) {
+    Class<?> itemClass = item.getClass();
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem().getClass().equals(itemClass)) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  private int getOffsettableIndexForItem(RawDexObject item) {
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() == item) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Given a RawDexObject, get the Offsettable that contains it.
+   */
+  public Offsettable getOffsettableForItem(RawDexObject item) {
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() == item) {
+        return offsettableTable.get(i);
+      }
+    }
+    return null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java b/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java
new file mode 100644
index 0000000..1b8cb24
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+/**
+ * Tracks the original and updated positions of a RawDexObject when it is
+ * parsed in from a DEX file, and written out to a mutated DEX file.
+ */
+public class Offsettable {
+  /**
+   * The position of this Offsettable's item when it was read in.
+   */
+  private int originalPosition;
+
+  /**
+   * Set as we write out any Offsettable, so the Offset knows what its
+   * new value should be.
+   */
+  private int newPosition;
+
+  /**
+   * The actual Item this Offsettable contains.
+   */
+  private RawDexObject item;
+
+  /**
+   *  Set either when getOriginalPosition() is called by the OffsetTracker
+   *  to put the location in the offsettable map, so when Offsets are being
+   *  associated, they know which Offsettable to point at.
+   *  Or when an Offsettable is created that is marked as new, so we don't
+   *  need to know its original position, because an Offset will be directly
+   *  associated with it.
+   */
+  private boolean originalPositionKnown;
+
+  /**
+   * Set when we calculate the new position of this Offsettable as the file is
+   * being output.
+   */
+  private boolean updated;
+
+  /**
+   * Only the OffsetTracker should be able to create a new Offsettable.
+   */
+  public Offsettable(RawDexObject item, boolean isNew) {
+    this.item = item;
+    if (isNew) {
+      // We no longer care about the original position of the Offsettable, because
+      // we are at the stage where we manually point Offsets at Offsettables, and
+      // don't need to use the OffsetTracker's offsettable map.
+      // So just lie and say we know it now.
+      originalPositionKnown = true;
+    }
+  }
+
+  public RawDexObject getItem() {
+    return item;
+  }
+
+  /**
+   * Gets the offset from the beginning of the file to the RawDexObject this Offsettable
+   * contains, when the file was originally read.
+   * Called when we're associating Offsets with Offsettables using the OffsetTracker's
+   * offsettable map.
+   */
+  public int getOriginalPosition() {
+    if (!originalPositionKnown) {
+      throw new Error("Cannot get the original position of an Offsettable when not yet set.");
+    }
+    return originalPosition;
+  }
+
+  public void setOriginalPosition(int pos) {
+    originalPosition = pos;
+    originalPositionKnown = true;
+  }
+
+  /**
+   * Get the new position of this Offsettable, once it's been written out to the output file.
+   */
+  public int getNewPosition() {
+    if (!updated) {
+      throw new Error("Cannot request new position before it has been set!");
+    }
+    return newPosition;
+  }
+
+  /**
+   * Record the new position of this Offsettable, as it is written out to the output file.
+   */
+  public void setNewPosition(int pos) {
+    if (!updated) {
+      newPosition = pos;
+      updated = true;
+    } else {
+      throw new Error("Cannot update an Offsettable twice!");
+    }
+  }
+
+  public boolean readyForFinalOffsetToBeWritten() {
+    return (originalPositionKnown && updated);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java b/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java
new file mode 100644
index 0000000..312e855
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+public enum Opcode {
+  NOP,
+  MOVE,
+  MOVE_FROM16,
+  MOVE_16,
+  MOVE_WIDE,
+  MOVE_WIDE_FROM16,
+  MOVE_WIDE_16,
+  MOVE_OBJECT,
+  MOVE_OBJECT_FROM16,
+  MOVE_OBJECT_16,
+  MOVE_RESULT,
+  MOVE_RESULT_WIDE,
+  MOVE_RESULT_OBJECT,
+  MOVE_EXCEPTION,
+  RETURN_VOID,
+  RETURN,
+  RETURN_WIDE,
+  RETURN_OBJECT,
+  CONST_4,
+  CONST_16,
+  CONST,
+  CONST_HIGH16,
+  CONST_WIDE_16,
+  CONST_WIDE_32,
+  CONST_WIDE,
+  CONST_WIDE_HIGH16,
+  CONST_STRING,
+  CONST_STRING_JUMBO,
+  CONST_CLASS,
+  MONITOR_ENTER,
+  MONITOR_EXIT,
+  CHECK_CAST,
+  INSTANCE_OF,
+  ARRAY_LENGTH,
+  NEW_INSTANCE,
+  NEW_ARRAY,
+  FILLED_NEW_ARRAY,
+  FILLED_NEW_ARRAY_RANGE,
+  FILL_ARRAY_DATA,
+  THROW,
+  GOTO,
+  GOTO_16,
+  GOTO_32,
+  PACKED_SWITCH,
+  SPARSE_SWITCH,
+  CMPL_FLOAT,
+  CMPG_FLOAT,
+  CMPL_DOUBLE,
+  CMPG_DOUBLE,
+  CMP_LONG,
+  IF_EQ,
+  IF_NE,
+  IF_LT,
+  IF_GE,
+  IF_GT,
+  IF_LE,
+  IF_EQZ,
+  IF_NEZ,
+  IF_LTZ,
+  IF_GEZ,
+  IF_GTZ,
+  IF_LEZ,
+  UNUSED_3E,
+  UNUSED_3F,
+  UNUSED_40,
+  UNUSED_41,
+  UNUSED_42,
+  UNUSED_43,
+  AGET,
+  AGET_WIDE,
+  AGET_OBJECT,
+  AGET_BOOLEAN,
+  AGET_BYTE,
+  AGET_CHAR,
+  AGET_SHORT,
+  APUT,
+  APUT_WIDE,
+  APUT_OBJECT,
+  APUT_BOOLEAN,
+  APUT_BYTE,
+  APUT_CHAR,
+  APUT_SHORT,
+  IGET,
+  IGET_WIDE,
+  IGET_OBJECT,
+  IGET_BOOLEAN,
+  IGET_BYTE,
+  IGET_CHAR,
+  IGET_SHORT,
+  IPUT,
+  IPUT_WIDE,
+  IPUT_OBJECT,
+  IPUT_BOOLEAN,
+  IPUT_BYTE,
+  IPUT_CHAR,
+  IPUT_SHORT,
+  SGET,
+  SGET_WIDE,
+  SGET_OBJECT,
+  SGET_BOOLEAN,
+  SGET_BYTE,
+  SGET_CHAR,
+  SGET_SHORT,
+  SPUT,
+  SPUT_WIDE,
+  SPUT_OBJECT,
+  SPUT_BOOLEAN,
+  SPUT_BYTE,
+  SPUT_CHAR,
+  SPUT_SHORT,
+  INVOKE_VIRTUAL,
+  INVOKE_SUPER,
+  INVOKE_DIRECT,
+  INVOKE_STATIC,
+  INVOKE_INTERFACE,
+  RETURN_VOID_BARRIER,
+  INVOKE_VIRTUAL_RANGE,
+  INVOKE_SUPER_RANGE,
+  INVOKE_DIRECT_RANGE,
+  INVOKE_STATIC_RANGE,
+  INVOKE_INTERFACE_RANGE,
+  UNUSED_79,
+  UNUSED_7A,
+  NEG_INT,
+  NOT_INT,
+  NEG_LONG,
+  NOT_LONG,
+  NEG_FLOAT,
+  NEG_DOUBLE,
+  INT_TO_LONG,
+  INT_TO_FLOAT,
+  INT_TO_DOUBLE,
+  LONG_TO_INT,
+  LONG_TO_FLOAT,
+  LONG_TO_DOUBLE,
+  FLOAT_TO_INT,
+  FLOAT_TO_LONG,
+  FLOAT_TO_DOUBLE,
+  DOUBLE_TO_INT,
+  DOUBLE_TO_LONG,
+  DOUBLE_TO_FLOAT,
+  INT_TO_BYTE,
+  INT_TO_CHAR,
+  INT_TO_SHORT,
+  ADD_INT,
+  SUB_INT,
+  MUL_INT,
+  DIV_INT,
+  REM_INT,
+  AND_INT,
+  OR_INT,
+  XOR_INT,
+  SHL_INT,
+  SHR_INT,
+  USHR_INT,
+  ADD_LONG,
+  SUB_LONG,
+  MUL_LONG,
+  DIV_LONG,
+  REM_LONG,
+  AND_LONG,
+  OR_LONG,
+  XOR_LONG,
+  SHL_LONG,
+  SHR_LONG,
+  USHR_LONG,
+  ADD_FLOAT,
+  SUB_FLOAT,
+  MUL_FLOAT,
+  DIV_FLOAT,
+  REM_FLOAT,
+  ADD_DOUBLE,
+  SUB_DOUBLE,
+  MUL_DOUBLE,
+  DIV_DOUBLE,
+  REM_DOUBLE,
+  ADD_INT_2ADDR,
+  SUB_INT_2ADDR,
+  MUL_INT_2ADDR,
+  DIV_INT_2ADDR,
+  REM_INT_2ADDR,
+  AND_INT_2ADDR,
+  OR_INT_2ADDR,
+  XOR_INT_2ADDR,
+  SHL_INT_2ADDR,
+  SHR_INT_2ADDR,
+  USHR_INT_2ADDR,
+  ADD_LONG_2ADDR,
+  SUB_LONG_2ADDR,
+  MUL_LONG_2ADDR,
+  DIV_LONG_2ADDR,
+  REM_LONG_2ADDR,
+  AND_LONG_2ADDR,
+  OR_LONG_2ADDR,
+  XOR_LONG_2ADDR,
+  SHL_LONG_2ADDR,
+  SHR_LONG_2ADDR,
+  USHR_LONG_2ADDR,
+  ADD_FLOAT_2ADDR,
+  SUB_FLOAT_2ADDR,
+  MUL_FLOAT_2ADDR,
+  DIV_FLOAT_2ADDR,
+  REM_FLOAT_2ADDR,
+  ADD_DOUBLE_2ADDR,
+  SUB_DOUBLE_2ADDR,
+  MUL_DOUBLE_2ADDR,
+  DIV_DOUBLE_2ADDR,
+  REM_DOUBLE_2ADDR,
+  ADD_INT_LIT16,
+  RSUB_INT,
+  MUL_INT_LIT16,
+  DIV_INT_LIT16,
+  REM_INT_LIT16,
+  AND_INT_LIT16,
+  OR_INT_LIT16,
+  XOR_INT_LIT16,
+  ADD_INT_LIT8,
+  RSUB_INT_LIT8,
+  MUL_INT_LIT8,
+  DIV_INT_LIT8,
+  REM_INT_LIT8,
+  AND_INT_LIT8,
+  OR_INT_LIT8,
+  XOR_INT_LIT8,
+  SHL_INT_LIT8,
+  SHR_INT_LIT8,
+  USHR_INT_LIT8,
+  IGET_QUICK,
+  IGET_WIDE_QUICK,
+  IGET_OBJECT_QUICK,
+  IPUT_QUICK,
+  IPUT_WIDE_QUICK,
+  IPUT_OBJECT_QUICK,
+  INVOKE_VIRTUAL_QUICK,
+  INVOKE_VIRTUAL_QUICK_RANGE,
+  IPUT_BOOLEAN_QUICK,
+  IPUT_BYTE_QUICK,
+  IPUT_CHAR_QUICK,
+  IPUT_SHORT_QUICK,
+  UNUSED_EF,
+  UNUSED_F0,
+  UNUSED_F1,
+  UNUSED_F2,
+  UNUSED_F3,
+  UNUSED_F4,
+  UNUSED_F5,
+  UNUSED_F6,
+  UNUSED_F7,
+  UNUSED_F8,
+  UNUSED_F9,
+  UNUSED_FA,
+  UNUSED_FB,
+  UNUSED_FC,
+  UNUSED_FD,
+  UNUSED_FE,
+  UNUSED_FF;
+
+  public static boolean isBetween(Opcode opcode, Opcode opcode1, Opcode opcode2) {
+    return (opcode.ordinal() >= opcode1.ordinal() && opcode.ordinal() <= opcode2.ordinal());
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java b/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java
new file mode 100644
index 0000000..fb1d093
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.rawdex.formats.AbstractFormat;
+
+/**
+ * Every Instruction points to an OpcodeInfo object that holds useful information
+ * about that kind of instruction, including the Format that allows us to read the
+ * instructions fields correctly.
+ */
+public class OpcodeInfo {
+  public final Opcode opcode;
+  public final String name;
+  public final int value;
+  public final AbstractFormat format;
+
+  /**
+   * Construct an OpcodeInfo. A static list of these is created in Instruction.java.
+   */
+  public OpcodeInfo(Opcode opcode, String name, int opcodeValue, AbstractFormat fmt) {
+    this.opcode = opcode;
+    this.name = name;
+    this.value = opcodeValue;
+    this.format = fmt;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java
new file mode 100644
index 0000000..0db5efd
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ParameterAnnotation implements RawDexObject {
+  public int methodIdx;
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    methodIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(methodIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.METHOD_ID && methodIdx >= insertedIdx) {
+      methodIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java
new file mode 100644
index 0000000..3ccc82f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ProtoIdItem implements RawDexObject {
+  public int shortyIdx;
+  public int returnTypeIdx;
+  public Offset parametersOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    shortyIdx = file.readUInt();
+    returnTypeIdx = file.readUInt();
+    parametersOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(shortyIdx);
+    file.writeUInt(returnTypeIdx);
+    file.getOffsetTracker().tryToWriteOffset(parametersOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.STRING_ID && shortyIdx >= insertedIdx) {
+      shortyIdx++;
+    }
+    if (kind == IndexUpdateKind.TYPE_ID && returnTypeIdx >= insertedIdx) {
+      returnTypeIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java
new file mode 100644
index 0000000..483ed6c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RawDexFile implements RawDexObject {
+  private OffsetTracker offsetTracker;
+
+  public HeaderItem header;
+
+  public MapList mapList;
+
+  // Can be allocated after reading the header.
+  public List<StringIdItem> stringIds;
+  public List<TypeIdItem> typeIds;
+  public List<ProtoIdItem> protoIds;
+  public List<FieldIdItem> fieldIds;
+  public List<MethodIdItem> methodIds;
+  public List<ClassDefItem> classDefs;
+
+  // Need to be allocated later (will be allocated in MapList.java)
+  public List<StringDataItem> stringDatas;
+  public List<ClassDataItem> classDatas;
+  public List<TypeList> typeLists;
+  public List<CodeItem> codeItems;
+  public DebugInfoItem debugInfoItem;
+  public List<AnnotationsDirectoryItem> annotationsDirectoryItems;
+  public List<AnnotationSetRefList> annotationSetRefLists;
+  public List<AnnotationSetItem> annotationSetItems;
+  public List<AnnotationItem> annotationItems;
+  public List<EncodedArrayItem> encodedArrayItems;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    // Get a reference to the OffsetTracker, so that IdCreator can use it.
+    offsetTracker = file.getOffsetTracker();
+
+    file.seek(0);
+
+    // Read header.
+    (header = new HeaderItem()).read(file);
+
+    // We can allocate all of these now.
+    stringIds = new ArrayList<StringIdItem>(header.stringIdsSize);
+    typeIds = new ArrayList<TypeIdItem>(header.typeIdsSize);
+    protoIds = new ArrayList<ProtoIdItem>(header.protoIdsSize);
+    fieldIds = new ArrayList<FieldIdItem>(header.fieldIdsSize);
+    methodIds = new ArrayList<MethodIdItem>(header.methodIdsSize);
+    classDefs = new ArrayList<ClassDefItem>(header.classDefsSize);
+
+    mapList = new MapList(this);
+    mapList.read(file);
+
+    file.getOffsetTracker().associateOffsets();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.seek(0);
+
+    // We read the header first, and then the map list, and then everything
+    // else. Therefore, when we get to the end of the header, tell OffsetTracker
+    // to skip past the map list offsets, and then when we get to the map list,
+    // tell OffsetTracker to skip back there, and then return to where it was previously.
+
+    // Update the map items' sizes first
+    // - but only update the items that we expect to have changed size.
+    // ALSO update the header's table sizes!
+    for (MapItem mapItem : mapList.mapItems) {
+      switch (mapItem.type) {
+        case MapItem.TYPE_STRING_ID_ITEM:
+          if (mapItem.size != stringIds.size()) {
+            Log.debug("Updating StringIDs List size: " + stringIds.size());
+            mapItem.size = stringIds.size();
+            header.stringIdsSize = stringIds.size();
+          }
+          break;
+        case MapItem.TYPE_STRING_DATA_ITEM:
+          if (mapItem.size != stringDatas.size()) {
+            Log.debug("Updating StringDatas List size: " + stringDatas.size());
+            mapItem.size = stringDatas.size();
+          }
+          break;
+        case MapItem.TYPE_METHOD_ID_ITEM:
+          if (mapItem.size != methodIds.size()) {
+            Log.debug("Updating MethodIDs List size: " + methodIds.size());
+            mapItem.size = methodIds.size();
+            header.methodIdsSize = methodIds.size();
+          }
+          break;
+        case MapItem.TYPE_FIELD_ID_ITEM:
+          if (mapItem.size != fieldIds.size()) {
+            Log.debug("Updating FieldIDs List size: " + fieldIds.size());
+            mapItem.size = fieldIds.size();
+            header.fieldIdsSize = fieldIds.size();
+          }
+          break;
+        case MapItem.TYPE_PROTO_ID_ITEM:
+          if (mapItem.size != protoIds.size()) {
+            Log.debug("Updating ProtoIDs List size: " + protoIds.size());
+            mapItem.size = protoIds.size();
+            header.protoIdsSize = protoIds.size();
+          }
+          break;
+        case MapItem.TYPE_TYPE_ID_ITEM:
+          if (mapItem.size != typeIds.size()) {
+            Log.debug("Updating TypeIDs List size: " + typeIds.size());
+            mapItem.size = typeIds.size();
+            header.typeIdsSize = typeIds.size();
+          }
+          break;
+        case MapItem.TYPE_TYPE_LIST:
+          if (mapItem.size != typeLists.size()) {
+            Log.debug("Updating TypeLists List size: " + typeLists.size());
+            mapItem.size = typeLists.size();
+          }
+          break;
+        default:
+      }
+    }
+
+    // Use the map list to write the file.
+    for (MapItem mapItem : mapList.mapItems) {
+      switch (mapItem.type) {
+        case MapItem.TYPE_HEADER_ITEM:
+          header.write(file);
+          file.getOffsetTracker().skipToAfterMapList();
+          break;
+        case MapItem.TYPE_STRING_ID_ITEM:
+          if (mapItem.size != stringIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches StringIDs table size " + stringIds.size());
+          }
+          for (StringIdItem stringId : stringIds) {
+            stringId.write(file);
+          }
+          break;
+        case MapItem.TYPE_TYPE_ID_ITEM:
+          if (mapItem.size != typeIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches TypeIDs table size " + typeIds.size());
+          }
+          for (TypeIdItem typeId : typeIds) {
+            typeId.write(file);
+          }
+          break;
+        case MapItem.TYPE_PROTO_ID_ITEM:
+          if (mapItem.size != protoIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches ProtoIDs table size " + protoIds.size());
+          }
+          for (ProtoIdItem protoId : protoIds) {
+            protoId.write(file);
+          }
+          break;
+        case MapItem.TYPE_FIELD_ID_ITEM:
+          if (mapItem.size != fieldIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches FieldIDs table size " + fieldIds.size());
+          }
+          for (FieldIdItem fieldId : fieldIds) {
+            fieldId.write(file);
+          }
+          break;
+        case MapItem.TYPE_METHOD_ID_ITEM:
+          if (mapItem.size != methodIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches MethodIDs table size " + methodIds.size());
+          }
+          for (MethodIdItem methodId : methodIds) {
+            methodId.write(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DEF_ITEM:
+          if (mapItem.size != classDefs.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches ClassDefs table size " + classDefs.size());
+          }
+          for (ClassDefItem classDef : classDefs) {
+            classDef.write(file);
+          }
+          break;
+        case MapItem.TYPE_MAP_LIST:
+          file.getOffsetTracker().goBackToMapList();
+          mapList.write(file);
+          file.getOffsetTracker().goBackToPreviousPoint();
+          break;
+        case MapItem.TYPE_TYPE_LIST:
+          if (mapItem.size != typeLists.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches TypeLists table size " + typeLists.size());
+          }
+          for (TypeList typeList : typeLists) {
+            typeList.write(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_REF_LIST:
+          if (mapItem.size != annotationSetRefLists.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationSetRefLists table size "
+                + annotationSetRefLists.size());
+          }
+          for (AnnotationSetRefList annotationSetRefList : annotationSetRefLists) {
+            annotationSetRefList.write(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_ITEM:
+          if (mapItem.size != annotationSetItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationSetItems table size "
+                + annotationSetItems.size());
+          }
+          for (AnnotationSetItem annotationSetItem : annotationSetItems) {
+            annotationSetItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DATA_ITEM:
+          if (mapItem.size != classDatas.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches ClassDataItems table size " + classDatas.size());
+          }
+          for (ClassDataItem classData : classDatas) {
+            classData.write(file);
+          }
+          break;
+        case MapItem.TYPE_CODE_ITEM:
+          if (mapItem.size != codeItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches CodeItems table size " + codeItems.size());
+          }
+          for (CodeItem codeItem : codeItems) {
+            codeItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_STRING_DATA_ITEM:
+          if (mapItem.size != stringDatas.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches StringDataItems table size "
+                + stringDatas.size());
+          }
+          for (StringDataItem stringDataItem : stringDatas) {
+            stringDataItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_DEBUG_INFO_ITEM:
+          debugInfoItem.write(file);
+          break;
+        case MapItem.TYPE_ANNOTATION_ITEM:
+          if (mapItem.size != annotationItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationItems table size "
+                + annotationItems.size());
+          }
+          for (AnnotationItem annotationItem : annotationItems) {
+            annotationItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_ENCODED_ARRAY_ITEM:
+          if (mapItem.size != encodedArrayItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches EncodedArrayItems table size "
+                + encodedArrayItems.size());
+          }
+          for (EncodedArrayItem encodedArrayItem : encodedArrayItems) {
+            encodedArrayItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+          if (mapItem.size != annotationsDirectoryItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationDirectoryItems table size "
+                + annotationsDirectoryItems.size());
+          }
+          for (AnnotationsDirectoryItem annotationsDirectory : annotationsDirectoryItems) {
+            annotationsDirectory.write(file);
+          }
+          break;
+        default:
+          Log.errorAndQuit("Encountered unknown map item in map item list.");
+      }
+    }
+
+    file.getOffsetTracker().updateOffsets(file);
+  }
+
+  /**
+   * Given a DexRandomAccessFile, calculate the correct adler32 checksum for it.
+   */
+  private int calculateAdler32Checksum(DexRandomAccessFile file) throws IOException {
+    // Skip magic + checksum.
+    file.seek(12);
+    int a = 1;
+    int b = 0;
+    while (file.getFilePointer() < file.length()) {
+      a = (a + file.readUnsignedByte()) % 65521;
+      b = (b + a) % 65521;
+    }
+    return (b << 16) | a;
+  }
+
+  /**
+   * Given a DexRandomAccessFile, update the file size, data size, and checksum.
+   */
+  public void updateHeader(DexRandomAccessFile file) throws IOException {
+    // File size must be updated before checksum.
+    int newFileSize = (int) file.length();
+    file.seek(32);
+    file.writeUInt(newFileSize);
+
+    // Data size must be updated before checksum.
+    int newDataSize = newFileSize - header.dataOff.getNewPositionOfItem();
+    file.seek(104);
+    file.writeUInt(newDataSize);
+
+    // Now update the checksum.
+    int newChecksum = calculateAdler32Checksum(file);
+    file.seek(8);
+    file.writeUInt(newChecksum);
+
+    header.fileSize = newFileSize;
+    header.dataSize = newDataSize;
+    header.checksum = newChecksum;
+  }
+
+  /**
+   * This should only be called from NewItemCreator.
+   */
+  public OffsetTracker getOffsetTracker() {
+    return offsetTracker;
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    for (TypeIdItem typeId : typeIds) {
+      typeId.incrementIndex(kind, insertedIdx);
+    }
+    for (ProtoIdItem protoId : protoIds) {
+      protoId.incrementIndex(kind, insertedIdx);
+    }
+    for (FieldIdItem fieldId : fieldIds) {
+      fieldId.incrementIndex(kind, insertedIdx);
+    }
+    for (MethodIdItem methodId : methodIds) {
+      methodId.incrementIndex(kind, insertedIdx);
+    }
+    for (ClassDefItem classDef : classDefs) {
+      classDef.incrementIndex(kind, insertedIdx);
+    }
+    for (ClassDataItem classData : classDatas) {
+      classData.incrementIndex(kind, insertedIdx);
+    }
+    if (typeLists != null) {
+      for (TypeList typeList : typeLists) {
+        typeList.incrementIndex(kind, insertedIdx);
+      }
+    }
+    for (CodeItem codeItem : codeItems) {
+      codeItem.incrementIndex(kind, insertedIdx);
+    }
+    if (annotationsDirectoryItems != null) {
+      for (AnnotationsDirectoryItem annotationsDirectoryItem : annotationsDirectoryItems) {
+        annotationsDirectoryItem.incrementIndex(kind, insertedIdx);
+      }
+    }
+    if (annotationItems != null) {
+      for (AnnotationItem annotationItem : annotationItems) {
+        annotationItem.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java
new file mode 100644
index 0000000..0b67e9c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+/**
+ * Base class for any data structure that we may read or write from a DEX file.
+ */
+public interface RawDexObject {
+  /**
+   * Populate information for this DEX data from the file.
+   * @param file Input file, should already be "seeked" to the correct position.
+   * @throws IOException If there's a problem writing to the file.
+   */
+  public void read(DexRandomAccessFile file) throws IOException;
+
+  /**
+   * Write information for this DEX data to the file.
+   * @param file Output file, should already be "seeked" to the correct position.
+   * @throws IOException If there's a problem writing to the file.
+   */
+  public void write(DexRandomAccessFile file) throws IOException;
+
+  public static enum IndexUpdateKind {
+    STRING_ID,
+    TYPE_ID,
+    PROTO_ID,
+    FIELD_ID,
+    METHOD_ID
+  }
+
+  /**
+   * When we insert a new string, type, proto, field or method into the DEX file,
+   * this must be called. We may have inserted something into the middle of a table,
+   * so any indices pointing afterwards must be updated.
+   */
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx);
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java
new file mode 100644
index 0000000..87c603e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class StringDataItem implements RawDexObject {
+  private int size;
+  private String data;
+  private byte[] dataAsBytes;
+  private boolean writeRawBytes;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUleb128();
+    if (size != 0) {
+      dataAsBytes = file.readDexUtf(size);
+      data = new String(dataAsBytes, StandardCharsets.US_ASCII);
+      if (size != data.length()) {
+        Log.warn("Don't have full support for decoding MUTF-8 yet, DEX file "
+            + "may be incorrectly mutated. Avoid using this test case for now.");
+        writeRawBytes = true;
+      }
+    } else {
+      // Read past the null byte.
+      file.readByte();
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUleb128(size);
+    if (size > 0) {
+      if (writeRawBytes) {
+        file.writeDexUtf(dataAsBytes);
+      } else {
+        file.writeDexUtf(data.getBytes(StandardCharsets.US_ASCII));
+      }
+    } else {
+      // Write out the null byte.
+      file.writeByte(0);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+
+  public void setSize(int size) {
+    this.size = size;
+  }
+
+  public int getSize() {
+    return size;
+  }
+
+  public void setString(String data) {
+    this.data = data;
+  }
+
+  public String getString() {
+    if (writeRawBytes) {
+      Log.warn("Reading a string that hasn't been properly decoded! Returning empty string.");
+      return "";
+    }
+    return data;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java
new file mode 100644
index 0000000..da8c294
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class StringIdItem implements RawDexObject {
+  public Offset stringDataOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    stringDataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.getOffsetTracker().tryToWriteOffset(stringDataOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java
new file mode 100644
index 0000000..99be6de
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TryItem implements RawDexObject {
+  public int startAddr;
+  public short insnCount;
+  public short handlerOff; // Not a global offset; don't need to adjust like an Offset.
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    startAddr = file.readUInt();
+    insnCount = file.readUShort();
+    handlerOff = file.readUShort();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(startAddr);
+    file.writeUShort(insnCount);
+    file.writeUShort(handlerOff);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java
new file mode 100644
index 0000000..bb3eff1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeIdItem implements RawDexObject {
+  public int descriptorIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    descriptorIdx = file.readUInt();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(descriptorIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.STRING_ID && descriptorIdx >= insertedIdx) {
+      descriptorIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java
new file mode 100644
index 0000000..a788b47
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeItem implements RawDexObject {
+  public short typeIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    typeIdx = file.readUShort();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUShort(typeIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java
new file mode 100644
index 0000000..90a2b57
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeList implements RawDexObject {
+  public int size;
+  public TypeItem[] list;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUInt();
+    list = new TypeItem[size];
+    for (int i = 0; i < size; i++) {
+      (list[i] = new TypeItem()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(size);
+    for (TypeItem typeItem : list) {
+      typeItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    for (TypeItem type : list) {
+      type.incrementIndex(kind, insertedIdx);
+    }
+  }
+
+  /**
+   * Returns if this TypeList comes before the provided TypeList, considering the legal
+   * ordering of TypeLists in DEX files.
+   */
+  public boolean comesBefore(TypeList other) {
+    int checkSize = Math.min(size, other.size);
+    for (int i = 0; i < checkSize; i++) {
+      if (list[i].typeIdx < other.list[i].typeIdx) {
+        return true;
+      } else if (list[i].typeIdx > other.list[i].typeIdx) {
+        return false;
+      }
+    }
+    if (size == other.size) {
+      return false;
+    }
+    return true;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java
new file mode 100644
index 0000000..29b15ae
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+/**
+ * Every Format subclasses this AbstractFormat. The subclasses then implement these
+ * methods to write out a provided Instruction according to this format, and also methods
+ * to read the vregs from an Instruction's raw bytes.
+ * Hierarchy is as follows:
+ * AbstractFormat
+ *   |____________Format1
+ *   |              |_____Format10t
+ *   |              |_____Format10x
+ *   |              |_____Format11n
+ *   |              |_____Format11x
+ *   |              |_____Format12x
+ *   |____________Format2
+ *   |              |_____Format20bc
+ *   |              |_____Format20t
+ *     etc...
+ */
+public abstract class AbstractFormat {
+  /**
+   * Get the size of an Instruction that has this format.
+   */
+  public abstract int getSize();
+
+  /**
+   * Given a file handle and an instruction, write that Instruction out to the file
+   * correctly, considering the current format.
+   */
+  public abstract void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException;
+
+  /**
+   * Read the value of vA, considering this format.
+   */
+  public abstract long getA(byte[] raw) throws IOException;
+
+  /**
+   * Read the value of vB, considering this format.
+   */
+  public abstract long getB(byte[] raw) throws IOException;
+
+  /**
+   * Read the value of vC, considering this format.
+   */
+  public abstract long getC(byte[] raw) throws IOException;
+
+  /**
+   * Only Format35c should return true for this.
+   */
+  public abstract boolean needsInvokeFormatInfo();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java
new file mode 100644
index 0000000..e2b164a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Every Format that contains a value that is a constant (that includes instructions like
+ * const/4, but also add-int/lit8) should implement this interface, to allow the constant
+ * part of a provided Instruction to be read and set correctly.
+ */
+public interface ContainsConst {
+  public long getConst(Instruction insn);
+
+  public void setConst(Instruction insn, long constant);
+
+  public long getConstRange();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java
new file mode 100644
index 0000000..5f66b0e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+/**
+ * Every Format that contains a value that is an index into the ID tables of this DEX file
+ * should implement this interface, to allow the index part of a provided Instruction
+ * to be read and set correctly.
+ */
+public interface ContainsPoolIndex {
+  public enum PoolIndexKind {
+    Type,
+    Field,
+    String,
+    Method,
+    Invalid
+  }
+
+  public int getPoolIndex(Instruction insn);
+
+  public void setPoolIndex(Instruction insn, int poolIndex);
+
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info);
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
similarity index 62%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
index 9e2c030..bb24e61 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-#include "asm_support_arm64.S"
+package dexfuzz.rawdex.formats;
 
-    /*
-     * Portable invocation stub.
-     */
-UNIMPLEMENTED art_portable_invoke_stub
+import dexfuzz.rawdex.Instruction;
 
-UNIMPLEMENTED art_portable_proxy_invoke_handler
+/**
+ * Every Format that contains an offset to a target instruction
+ * should implement this interface, to allow the offset to be read and set correctly.
+ */
+public interface ContainsTarget {
+  public long getTarget(Instruction insn);
 
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+  public void setTarget(Instruction insn, long target);
+}
diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
similarity index 65%
copy from runtime/arch/arm64/portable_entrypoints_arm64.S
copy to tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
index 9e2c030..40ba5ac 100644
--- a/runtime/arch/arm64/portable_entrypoints_arm64.S
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
@@ -14,17 +14,12 @@
  * limitations under the License.
  */
 
-#include "asm_support_arm64.S"
+package dexfuzz.rawdex.formats;
 
-    /*
-     * Portable invocation stub.
-     */
-UNIMPLEMENTED art_portable_invoke_stub
-
-UNIMPLEMENTED art_portable_proxy_invoke_handler
-
-UNIMPLEMENTED art_portable_resolution_trampoline
-
-UNIMPLEMENTED art_portable_to_interpreter_bridge
-
-UNIMPLEMENTED art_portable_imt_conflict_trampoline
+/**
+ * Every Format that contains virtual registers should implement this interface,
+ * to allow the number of virtual registers specified by the format to be found.
+ */
+public interface ContainsVRegs {
+  public int getVRegCount();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java
new file mode 100644
index 0000000..aae7469
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format00x extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 0;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java
new file mode 100644
index 0000000..e503513
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format1 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 1;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java
new file mode 100644
index 0000000..a9e13f1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format10t extends Format1 implements ContainsTarget {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregA;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregA = target;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java
new file mode 100644
index 0000000..aabf725
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format10x extends Format1 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java
new file mode 100644
index 0000000..4b8c35c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format11n extends Format1 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 4);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java
new file mode 100644
index 0000000..e8963f1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format11x extends Format1 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java
new file mode 100644
index 0000000..170ebe1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format12x extends Format1 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java
new file mode 100644
index 0000000..5ddc124
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format2 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 2;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java
new file mode 100644
index 0000000..4f21489
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+// NB: This format is only used for statically determined verification errors,
+// so we shouldn't encounter it in ART. (Or even before, as they are only written in during
+// verification, which comes after our fuzzing...)
+// Therefore, no need to say this implements ContainsPoolIndex, even though it is a *c format
+public class Format20bc extends Format2 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java
new file mode 100644
index 0000000..1e33c15
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format20t extends Format2 implements ContainsTarget {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    file.writeUShort((short) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregA;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregA = target;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java
new file mode 100644
index 0000000..e60b54a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format21c extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.CONST_STRING) {
+      return PoolIndexKind.String;
+    }
+    if (info.opcode == Opcode.CONST_CLASS
+        || info.opcode == Opcode.CHECK_CAST
+        || info.opcode == Opcode.NEW_INSTANCE) {
+      return PoolIndexKind.Type;
+    }
+    // everything else is static field accesses
+    return PoolIndexKind.Field;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java
new file mode 100644
index 0000000..c279f6c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21h extends Format2 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 16);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java
new file mode 100644
index 0000000..594d1a7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21s extends Format2 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 16);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java
new file mode 100644
index 0000000..dc3d659
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21t extends Format2 implements ContainsTarget, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregB = target;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java
new file mode 100644
index 0000000..bbdc7e3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22b extends Format2 implements ContainsVRegs, ContainsConst {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeByte((byte) insn.vregB);
+    file.writeByte((byte) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedByteFromByte(raw, 3);
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregC;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregC = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 8);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java
new file mode 100644
index 0000000..4dff336
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format22c extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregC;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregC = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.INSTANCE_OF || info.opcode == Opcode.NEW_ARRAY) {
+      return PoolIndexKind.Type;
+    }
+    return PoolIndexKind.Field;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java
new file mode 100644
index 0000000..b66e14c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format22cs extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    throw new Error("Did not expect to have to write a 22cs instruction!");
+    // If for some reason 22cs instructions were in DEX files in the future, uncomment:
+    //file.writeByte((byte) insn.info.value);
+    //file.writeByte((byte) (insn.vreg_a | (insn.vreg_b << 4)));
+    //file.writeUShort((short) insn.vreg_c);
+    //return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregC;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregC = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    // This should technically be a byte offset, but we should never encounter
+    // this format during DEX mutation anyway. (see writeToFile())
+    return PoolIndexKind.Field;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java
new file mode 100644
index 0000000..9497179
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22s extends Format2 implements ContainsVRegs, ContainsConst {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregC;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregC = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 16);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java
new file mode 100644
index 0000000..5ea9579
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22t extends Format2 implements ContainsTarget, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregC;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregC = target;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java
new file mode 100644
index 0000000..5cd3d73
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22x extends Format2 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java
new file mode 100644
index 0000000..8ce7c7c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format23x extends Format2 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeByte((byte) insn.vregB);
+    file.writeByte((byte) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 3);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 3;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java
new file mode 100644
index 0000000..d9d7ce1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 3;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java
new file mode 100644
index 0000000..0b62646
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format30t extends Format3 implements ContainsTarget {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    file.writeUInt((int) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregA;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregA = target;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java
new file mode 100644
index 0000000..435fa19
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format31c extends Format3 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUInt((int) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    return PoolIndexKind.String;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java
new file mode 100644
index 0000000..d54c074
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format31i extends Format3 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUInt((int) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1L << 32);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java
new file mode 100644
index 0000000..b74db8f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format31t extends Format3 implements ContainsTarget, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUInt((int) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregB = target;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java
new file mode 100644
index 0000000..2f46105
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format32x extends Format3 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    file.writeUShort((short) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java
new file mode 100644
index 0000000..e4a50ff
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format35c extends Format3 implements ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+    file.writeUShort((short) insn.vregB);
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+        | insn.invokeFormatInfo.vregE));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return true;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.FILLED_NEW_ARRAY) {
+      return PoolIndexKind.Type;
+    }
+    return PoolIndexKind.Method;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java
new file mode 100644
index 0000000..f622385
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format35mi extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+    file.writeUShort((short) insn.vregB);
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+        | insn.invokeFormatInfo.vregE));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java
new file mode 100644
index 0000000..3be9707
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format35ms extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+    file.writeUShort((short) insn.vregB);
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+        | insn.invokeFormatInfo.vregE));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java
new file mode 100644
index 0000000..630825d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format3rc extends Format3 implements ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
+      return PoolIndexKind.Type;
+    }
+    return PoolIndexKind.Method;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java
new file mode 100644
index 0000000..7b6ceea
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3rmi extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java
new file mode 100644
index 0000000..17535f9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3rms extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java
new file mode 100644
index 0000000..bc141be
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format5 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 5;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java
new file mode 100644
index 0000000..fc2e0ed
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format51l extends Format5 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) (insn.vregB & 0xffff));
+    file.writeUShort((short) ((insn.vregB & 0xffff0000) >> 16));
+    file.writeUShort((short) ((insn.vregB & 0xffff00000000L) >> 32));
+    file.writeUShort((short) ((insn.vregB & 0xffff000000000000L) >> 48));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedLongFromEightBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1L << 63);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java
new file mode 100644
index 0000000..b16a1b5
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package dexfuzz.rawdex.formats;
+
+/**
+ * Class consisting of static methods used for common read/write operations
+ * perfomed in the Format classes.
+ */
+public class RawInsnHelper {
+  /**
+   * Read a signed byte from the idx into the raw array.
+   */
+  public static long getSignedByteFromByte(byte[] raw, int idx) {
+    return (long) raw[idx];
+  }
+
+  /**
+   * Read an unsigned byte from the idx into the raw array.
+   */
+  public static long getUnsignedByteFromByte(byte[] raw, int idx) {
+    return ((long) raw[idx]) & 0xff;
+  }
+
+  /**
+   * Read an unsigned lower 4 bits from the idx into the raw array.
+   */
+  public static long getUnsignedLowNibbleFromByte(byte[] raw, int idx) {
+    return ((long) raw[idx]) & 0xf;
+  }
+
+  /**
+   * Read an unsigned higher 4 bits from the idx into the raw array.
+   */
+  public static long getUnsignedHighNibbleFromByte(byte[] raw, int idx) {
+    return (((long) raw[idx]) >> 4) & 0xf;
+  }
+
+  /**
+   * Read an unsigned 2 bytes as a short from the idx into the raw array.
+   */
+  public static long getUnsignedShortFromTwoBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8));
+  }
+
+  /**
+   * Read a signed 2 bytes as a short from the idx into the raw array.
+   */
+  public static long getSignedShortFromTwoBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | (((long) raw[idx + 1]) << 8));
+  }
+
+  /**
+   * Read an unsigned 4 bytes as an int from the idx into the raw array.
+   */
+  public static long getUnsignedIntFromFourBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8)
+        | ((((long) raw[idx + 2]) & 0xff) << 16)
+        | ((((long) raw[idx + 3]) & 0xff) << 24) );
+  }
+
+  /**
+   * Read a signed 4 bytes as an int from the idx into the raw array.
+   */
+  public static long getSignedIntFromFourBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8)
+        | ((((long) raw[idx + 2]) & 0xff) << 16)
+        | (((long) raw[idx + 3]) << 24) );
+  }
+
+  /**
+   * Read a signed 8 bytes as a long from the idx into the raw array.
+   */
+  public static long getSignedLongFromEightBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8)
+        | ((((long) raw[idx + 2]) & 0xff) << 16)
+        | ((((long) raw[idx + 3]) & 0xff) << 24)
+        | ((((long) raw[idx + 4]) & 0xff) << 32)
+        | ((((long) raw[idx + 5]) & 0xff) << 40)
+        | ((((long) raw[idx + 6]) & 0xff) << 48)
+        | (((long) raw[idx + 7]) << 56) );
+  }
+
+  /**
+   * Given an idx into a raw array, and an int, write that int into the array at that position.
+   */
+  public static void writeUnsignedIntToFourBytes(byte[] raw, int idx, int value) {
+    raw[idx] = (byte) (value & 0xFF);
+    raw[idx + 1] = (byte) ((value & 0xFF00) >>> 8);
+    raw[idx + 2] = (byte) ((value & 0xFF0000) >>> 16);
+    raw[idx + 3] = (byte) ((value & 0xFF000000) >>> 24);
+  }
+
+  /**
+   * Given an idx into a raw array, and a short, write that int into the array at that position.
+   */
+  public static void writeUnsignedShortToTwoBytes(byte[] raw, int idx, int value) {
+    raw[idx] = (byte) (value & 0xFF);
+    raw[idx + 1] = (byte) ((value & 0xFF00) >>> 8);
+  }
+}
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 8eac1d3..92d2202 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -11,11 +11,44 @@
 {
   description: "Assert.java differences between vogar and junit.",
   result: EXEC_FAILED,
+  modes: [host],
   name: "libcore.java.math.RunCSVTests#test_csv"
 },
 {
-  description: "Test is currently being updated.",
+  description: "Differences between vogar and cts in user directory",
   result: EXEC_FAILED,
-  name: "libcore.java.util.OldTimeZoneTest#test_getDisplayNameZILjava_util_Locale"
+  modes: [device],
+  name: "libcore.java.lang.SystemTest#testSystemProperties_mutable"
+},
+{
+  description: "Differences between vogar and cts",
+  result: EXEC_FAILED,
+  modes: [device],
+  names: ["libcore.java.lang.OldSystemTest#test_getProperties",
+          "org.apache.harmony.tests.java.lang.Process2Test#test_getErrorStream",
+          "org.apache.harmony.tests.java.lang.ProcessTest#test_exitValue"]
+},
+{
+  description: "Failures needing investigation",
+  result: EXEC_FAILED,
+  modes: [device],
+  names: ["libcore.java.util.TimeZoneTest#testDisplayNames",
+          "libcore.java.util.TimeZoneTest#test_useDaylightTime_Taiwan",
+          "org.apache.harmony.tests.java.util.DateTest#test_Constructor",
+          "org.apache.harmony.tests.java.util.ScannerTest#test_Constructor_LReadableByteChannel",
+          "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone",
+          "libcore.java.util.TimeZoneTest#testAllDisplayNames"]
+},
+{
+  description: "Test timeouts",
+  result: EXEC_TIMEOUT,
+  modes: [device],
+  names: ["org.apache.harmony.tests.java.util.ScannerTest#testPerformance"]
+},
+{
+  description: "Needs the newest cat version on the device",
+  result: EXEC_FAILED,
+  modes: [device],
+  names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getErrorStream"]
 }
 ]
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
index b66191f..0168e7d 100755
--- a/tools/symbolize.sh
+++ b/tools/symbolize.sh
@@ -52,7 +52,7 @@
   DIRS=$(adbls /data/dalvik-cache/)
   for DIR in $DIRS ; do
     case $DIR in
-      arm|arm64|mips|x86|x86_64)
+      arm|arm64|mips|mips64|x86|x86_64)
         FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
         for FILE in $FILES ; do
           # Cannot use basename as the file doesn't exist.