Merge "Optimize x86_64 TLAB allocation speed"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index bac0ff3..d38fad6 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -252,6 +252,7 @@
 art_debug_cflags := \
   $(ART_DEBUG_OPT_FLAG) \
   -DDYNAMIC_ANNOTATIONS_ENABLED=1 \
+  -DVIXL_DEBUG \
   -UNDEBUG
 
 # Assembler flags for non-debug ART and ART tools.
@@ -263,20 +264,45 @@
 art_host_non_debug_cflags := $(art_non_debug_cflags)
 art_target_non_debug_cflags := $(art_non_debug_cflags)
 
+###
+# Frame size
+###
+
+# Size of the stack-overflow gap.
+ART_STACK_OVERFLOW_GAP_arm := 8192
+ART_STACK_OVERFLOW_GAP_arm64 := 8192
+ART_STACK_OVERFLOW_GAP_mips := 16384
+ART_STACK_OVERFLOW_GAP_mips64 := 16384
+ART_STACK_OVERFLOW_GAP_x86 := 8192
+ART_STACK_OVERFLOW_GAP_x86_64 := 8192
+ART_COMMON_STACK_OVERFLOW_DEFINES := \
+  -DART_STACK_OVERFLOW_GAP_arm=$(ART_STACK_OVERFLOW_GAP_arm) \
+  -DART_STACK_OVERFLOW_GAP_arm64=$(ART_STACK_OVERFLOW_GAP_arm64) \
+  -DART_STACK_OVERFLOW_GAP_mips=$(ART_STACK_OVERFLOW_GAP_mips) \
+  -DART_STACK_OVERFLOW_GAP_mips64=$(ART_STACK_OVERFLOW_GAP_mips64) \
+  -DART_STACK_OVERFLOW_GAP_x86=$(ART_STACK_OVERFLOW_GAP_x86) \
+  -DART_STACK_OVERFLOW_GAP_x86_64=$(ART_STACK_OVERFLOW_GAP_x86_64) \
+
+# Larger frame-size for host builds today.
+ART_HOST_FRAME_SIZE_LIMIT := 2700
+ART_TARGET_FRAME_SIZE_LIMIT := 1736
+
+# Frame size adaptations for instrumented builds.
+ifdef SANITIZE_TARGET
+  ART_TARGET_FRAME_SIZE_LIMIT := 6400
+endif
+
+# Add frame-size checks for non-debug builds.
 ifeq ($(HOST_OS),linux)
-  # Larger frame-size for host clang builds today
   ifneq ($(ART_COVERAGE),true)
     ifneq ($(NATIVE_COVERAGE),true)
-      art_host_non_debug_cflags += -Wframe-larger-than=2700
-      ifdef SANITIZE_TARGET
-        art_target_non_debug_cflags += -Wframe-larger-than=6400
-      else
-        art_target_non_debug_cflags += -Wframe-larger-than=1736
-      endif
+      art_host_non_debug_cflags += -Wframe-larger-than=$(ART_HOST_FRAME_SIZE_LIMIT)
+      art_target_non_debug_cflags += -Wframe-larger-than=$(ART_TARGET_FRAME_SIZE_LIMIT)
     endif
   endif
 endif
 
+
 ART_HOST_CFLAGS := $(art_cflags)
 ART_TARGET_CFLAGS := $(art_cflags)
 
@@ -293,6 +319,10 @@
 ART_HOST_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
 ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default $(art_host_cflags)
 
+ART_HOST_CFLAGS += -DART_FRAME_SIZE_LIMIT=$(ART_HOST_FRAME_SIZE_LIMIT) \
+                   $(ART_COMMON_STACK_OVERFLOW_DEFINES)
+
+
 ifndef LIBART_IMG_TARGET_BASE_ADDRESS
   $(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
 endif
@@ -300,6 +330,9 @@
 ART_TARGET_CFLAGS += -DART_TARGET \
                      -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) \
 
+ART_TARGET_CFLAGS += -DART_FRAME_SIZE_LIMIT=$(ART_TARGET_FRAME_SIZE_LIMIT) \
+                     $(ART_COMMON_STACK_OVERFLOW_DEFINES)
+
 ifeq ($(ART_TARGET_LINUX),true)
 # Setting ART_TARGET_LINUX to true compiles art/ assuming that the target device
 # will be running linux rather than android.
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c79205f..3d07fc0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -634,7 +634,7 @@
   ifeq ($$(art_target_or_host),target)
     $$(eval LOCAL_CLANG := $$(ART_TARGET_CLANG))
     $$(eval $$(call set-target-local-cflags-vars,debug))
-    LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixl-arm64
+    LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixld-arm64
     LOCAL_MODULE_PATH_32 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_32)
     LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64)
     LOCAL_MULTILIB := both
@@ -678,7 +678,7 @@
     LOCAL_CLANG := $$(ART_HOST_CLANG)
     LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS)
     LOCAL_ASFLAGS += $$(ART_HOST_ASFLAGS) $$(ART_HOST_DEBUG_ASFLAGS)
-    LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixl-arm64
+    LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixld-arm64
     LOCAL_LDLIBS := -lpthread -ldl
     LOCAL_IS_HOST_MODULE := true
     LOCAL_MULTILIB := both
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 1146f95..b57383b 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -24,14 +24,16 @@
 
 // Includes for the types that are being specialized
 #include <string>
-#include "unit.h"
-#include "jdwp/jdwp.h"
 #include "base/logging.h"
 #include "base/time_utils.h"
 #include "experimental_flags.h"
 #include "gc/collector_type.h"
 #include "gc/space/large_object_space.h"
+#include "jdwp/jdwp.h"
 #include "jit/profile_saver_options.h"
+#include "plugin.h"
+#include "ti/agent.h"
+#include "unit.h"
 
 namespace art {
 
@@ -381,6 +383,38 @@
 };
 
 template <>
+struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> {
+  Result Parse(const std::string& args) {
+    assert(false && "Use AppendValues() for a Plugin vector type");
+    return Result::Failure("Unconditional failure: Plugin vector must be appended: " + args);
+  }
+
+  Result ParseAndAppend(const std::string& args,
+                        std::vector<Plugin>& existing_value) {
+    existing_value.push_back(Plugin::Create(args));
+    return Result::SuccessNoValue();
+  }
+
+  static const char* Name() { return "std::vector<Plugin>"; }
+};
+
+template <>
+struct CmdlineType<std::vector<ti::Agent>> : CmdlineTypeParser<std::vector<ti::Agent>> {
+  Result Parse(const std::string& args) {
+    assert(false && "Use AppendValues() for an Agent vector type");
+    return Result::Failure("Unconditional failure: Agent vector must be appended: " + args);
+  }
+
+  Result ParseAndAppend(const std::string& args,
+                        std::vector<ti::Agent>& existing_value) {
+    existing_value.push_back(ti::Agent::Create(args));
+    return Result::SuccessNoValue();
+  }
+
+  static const char* Name() { return "std::vector<ti::Agent>"; }
+};
+
+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");
@@ -625,6 +659,8 @@
         log_verbosity.image = true;
       } else if (verbose_options[j] == "systrace-locks") {
         log_verbosity.systrace_lock_logging = true;
+      } else if (verbose_options[j] == "agents") {
+        log_verbosity.agents = true;
       } else {
         return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
       }
@@ -735,6 +771,10 @@
   Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
     if (option == "none") {
       existing = ExperimentalFlags::kNone;
+    } else if (option == "agents") {
+      existing = existing | ExperimentalFlags::kAgents;
+    } else if (option == "runtime-plugins") {
+      existing = existing | ExperimentalFlags::kRuntimePlugins;
     } else {
       return Result::Failure(std::string("Unknown option '") + option + "'");
     }
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index 4b56804..84beff5 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -497,7 +497,7 @@
       std::function<void(TArg&)> save_argument_;
       std::function<TArg&(void)> load_argument_;
     };
-  } // namespace detail // NOLINT [readability/namespace] [5] [whitespace/comments] [2]
+  }  // namespace detail  // NOLINT [readability/namespace] [5]
 }  // 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
index 9b43bb0..24dbca2 100644
--- a/cmdline/detail/cmdline_parser_detail.h
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -35,7 +35,7 @@
      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]
+                                                  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);
@@ -53,7 +53,7 @@
      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]
+                                                 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);
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 0ede30d..46b7e5d 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -287,9 +287,9 @@
   # Vixl assembly support for ARM64 targets.
   ifeq ($$(art_ndebug_or_debug),debug)
     ifeq ($$(art_static_or_shared), static)
-      LOCAL_WHOLESTATIC_LIBRARIES += libvixl-arm64
+      LOCAL_WHOLESTATIC_LIBRARIES += libvixld-arm64
     else
-      LOCAL_SHARED_LIBRARIES += libvixl-arm64
+      LOCAL_SHARED_LIBRARIES += libvixld-arm64
     endif
   else
     ifeq ($$(art_static_or_shared), static)
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8286033..d0a8335 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -77,10 +77,6 @@
 
 static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
 
-// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
-// given, too all compilations.
-static constexpr bool kRestrictCompilationFiltersToImage = true;
-
 // Print additional info during profile guided compilation.
 static constexpr bool kDebugProfileGuidedCompilation = false;
 
@@ -946,10 +942,6 @@
 }
 
 bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
-  if (kRestrictCompilationFiltersToImage && !IsBootImage()) {
-    return true;
-  }
-
   if (classes_to_compile_ == nullptr) {
     return true;
   }
@@ -957,10 +949,6 @@
 }
 
 bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const {
-  if (kRestrictCompilationFiltersToImage && !IsBootImage()) {
-    return true;
-  }
-
   if (methods_to_compile_ == nullptr) {
     return true;
   }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index ab85c12..2bf675d 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -59,8 +59,8 @@
 
 static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
 
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
 
 class NullCheckSlowPathARM : public SlowPathCode {
@@ -706,8 +706,8 @@
 };
 
 #undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(GetAssembler())->  // NOLINT
 
 inline Condition ARMCondition(IfCondition cond) {
   switch (cond) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 9ceb310..3b8a521 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -131,8 +131,8 @@
   return ARM64ReturnLocation(return_type);
 }
 
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, x).Int32Value()
 
 // Calculate memory accessing operand for save/restore live registers.
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 59e103a..58879bc 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -145,8 +145,8 @@
   return MipsReturnLocation(type);
 }
 
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<CodeGeneratorMIPS*>(codegen)->GetAssembler()-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorMIPS*>(codegen)->GetAssembler()->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
 
 class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
@@ -501,8 +501,8 @@
 }
 
 #undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<MipsAssembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<MipsAssembler*>(GetAssembler())->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
 
 void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index fe1fddc..4e7a272 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -102,8 +102,8 @@
   return Mips64ReturnLocation(type);
 }
 
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64PointerSize, x).Int32Value()
 
 class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
@@ -427,8 +427,8 @@
 }
 
 #undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<Mips64Assembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<Mips64Assembler*>(GetAssembler())->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64PointerSize, x).Int32Value()
 
 void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index ade2117..7a561bb 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -47,8 +47,8 @@
 
 static constexpr int kFakeReturnRegister = Register(8);
 
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86Assembler*>(codegen->GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86Assembler*>(codegen->GetAssembler())->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, x).Int32Value()
 
 class NullCheckSlowPathX86 : public SlowPathCode {
@@ -729,8 +729,8 @@
 };
 
 #undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86Assembler*>(GetAssembler())-> /* NOLINT */
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86Assembler*>(GetAssembler())->  // NOLINT
 
 inline Condition X86Condition(IfCondition cond) {
   switch (cond) {
@@ -7099,12 +7099,6 @@
   // /* LockWord */ lock_word = LockWord(monitor)
   static_assert(sizeof(LockWord) == sizeof(int32_t),
                 "art::LockWord and int32_t have different sizes.");
-  // /* uint32_t */ rb_state = lock_word.ReadBarrierState()
-  __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift));
-  __ andl(temp_reg, Immediate(LockWord::kReadBarrierStateMask));
-  static_assert(
-      LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_,
-      "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_.");
 
   // Load fence to prevent load-load reordering.
   // Note that this is a no-op, thanks to the x86 memory model.
@@ -7124,8 +7118,13 @@
 
   // if (rb_state == ReadBarrier::gray_ptr_)
   //   ref = ReadBarrier::Mark(ref);
-  __ cmpl(temp_reg, Immediate(ReadBarrier::gray_ptr_));
-  __ j(kEqual, slow_path->GetEntryLabel());
+  // Given the numeric representation, it's enough to check the low bit of the
+  // rb_state. We do that by shifting the bit out of the lock word with SHR.
+  static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
+  static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
+  static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
+  __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift + 1));
+  __ j(kCarrySet, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
 
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index eadb431..cf01a79 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -51,8 +51,8 @@
 
 static constexpr int kC2ConditionMask = 0x400;
 
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86_64Assembler*>(codegen->GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86_64Assembler*>(codegen->GetAssembler())->  // NOLINT
 #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86_64PointerSize, x).Int32Value()
 
 class NullCheckSlowPathX86_64 : public SlowPathCode {
@@ -748,8 +748,8 @@
 };
 
 #undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86_64Assembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86_64Assembler*>(GetAssembler())->  // NOLINT
 
 inline Condition X86_64IntegerCondition(IfCondition cond) {
   switch (cond) {
@@ -6551,12 +6551,6 @@
   // /* LockWord */ lock_word = LockWord(monitor)
   static_assert(sizeof(LockWord) == sizeof(int32_t),
                 "art::LockWord and int32_t have different sizes.");
-  // /* uint32_t */ rb_state = lock_word.ReadBarrierState()
-  __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift));
-  __ andl(temp_reg, Immediate(LockWord::kReadBarrierStateMask));
-  static_assert(
-      LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_,
-      "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_.");
 
   // Load fence to prevent load-load reordering.
   // Note that this is a no-op, thanks to the x86-64 memory model.
@@ -6576,8 +6570,13 @@
 
   // if (rb_state == ReadBarrier::gray_ptr_)
   //   ref = ReadBarrier::Mark(ref);
-  __ cmpl(temp_reg, Immediate(ReadBarrier::gray_ptr_));
-  __ j(kEqual, slow_path->GetEntryLabel());
+  // Given the numeric representation, it's enough to check the low bit of the
+  // rb_state. We do that by shifting the bit out of the lock word with SHR.
+  static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
+  static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
+  static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
+  __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift + 1));
+  __ j(kCarrySet, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
 
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 3429a8f..1a8eb58 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -27,9 +27,6 @@
 class CompilerDriver;
 class DexFile;
 
-// Temporary measure until we have caught up with the Java 7 definition of Math.round. b/26327751
-static constexpr bool kRoundIsPlusPointFive = false;
-
 // Positive floating-point infinities.
 static constexpr uint32_t kPositiveInfinityFloat = 0x7f800000U;
 static constexpr uint64_t kPositiveInfinityDouble = UINT64_C(0x7ff0000000000000);
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index e7c40e6..e233672 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1160,8 +1160,10 @@
   MacroAssembler* masm = GetVIXLAssembler();
   LocationSummary* locations = invoke->GetLocations();
 
-  Register str = XRegisterFrom(locations->InAt(0));
-  Register arg = XRegisterFrom(locations->InAt(1));
+  Register str = InputRegisterAt(invoke, 0);
+  Register arg = InputRegisterAt(invoke, 1);
+  DCHECK(str.IsW());
+  DCHECK(arg.IsW());
   Register out = OutputRegister(invoke);
 
   Register temp0 = WRegisterFrom(locations->GetTemp(0));
@@ -1192,8 +1194,8 @@
   __ Subs(out, str, arg);
   __ B(&end, eq);
   // Load lengths of this and argument strings.
-  __ Ldr(temp0, MemOperand(str.X(), count_offset));
-  __ Ldr(temp1, MemOperand(arg.X(), count_offset));
+  __ Ldr(temp0, HeapOperand(str, count_offset));
+  __ Ldr(temp1, HeapOperand(arg, count_offset));
   // Return zero if both strings are empty.
   __ Orr(out, temp0, temp1);
   __ Cbz(out, &end);
@@ -1222,8 +1224,8 @@
 
   // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
   __ Bind(&loop);
-  __ Ldr(temp4, MemOperand(str.X(), temp1));
-  __ Ldr(temp0, MemOperand(arg.X(), temp1));
+  __ Ldr(temp4, MemOperand(str.X(), temp1.X()));
+  __ Ldr(temp0, MemOperand(arg.X(), temp1.X()));
   __ Cmp(temp4, temp0);
   __ B(ne, &find_char_diff);
   __ Add(temp1, temp1, char_size * 4);
@@ -1242,14 +1244,14 @@
   __ Clz(temp1, temp1);
   // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
   // the difference occurs outside the remaining string data, so just return length diff (out).
-  __ Cmp(temp2, Operand(temp1, LSR, 4));
+  __ Cmp(temp2, Operand(temp1.W(), LSR, 4));
   __ B(le, &end);
   // Extract the characters and calculate the difference.
   __ Bic(temp1, temp1, 0xf);
   __ Lsr(temp0, temp0, temp1);
   __ Lsr(temp4, temp4, temp1);
   __ And(temp4, temp4, 0xffff);
-  __ Sub(out, temp4, Operand(temp0, UXTH));
+  __ Sub(out, temp4.W(), Operand(temp0.W(), UXTH));
 
   __ Bind(&end);
 
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index dc409c9..22f4181 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -753,11 +753,6 @@
 }
 
 void IntrinsicLocationsBuilderX86::VisitMathRoundFloat(HInvoke* invoke) {
-  // See intrinsics.h.
-  if (!kRoundIsPlusPointFive) {
-    return;
-  }
-
   // Do we have instruction support?
   if (codegen_->GetInstructionSetFeatures().HasSSE4_1()) {
     HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
@@ -795,7 +790,6 @@
   }
 
   XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>();
-  Register constant_area = locations->InAt(1).AsRegister<Register>();
   XmmRegister t1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
   XmmRegister t2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>();
   Register out = locations->Out().AsRegister<Register>();
@@ -810,10 +804,23 @@
   __ movss(t2, in);
   __ roundss(t1, in, Immediate(1));
   __ subss(t2, t1);
-  __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f), constant_area));
-  __ j(kBelow, &skip_incr);
-  __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f), constant_area));
-  __ Bind(&skip_incr);
+  if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) {
+    // Direct constant area available.
+    Register constant_area = locations->InAt(1).AsRegister<Register>();
+    __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f), constant_area));
+    __ j(kBelow, &skip_incr);
+    __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f), constant_area));
+    __ Bind(&skip_incr);
+  } else {
+    // No constant area: go through stack.
+    __ pushl(Immediate(bit_cast<int32_t, float>(0.5f)));
+    __ pushl(Immediate(bit_cast<int32_t, float>(1.0f)));
+    __ comiss(t2, Address(ESP, 4));
+    __ j(kBelow, &skip_incr);
+    __ addss(t1, Address(ESP, 0));
+    __ Bind(&skip_incr);
+    __ addl(ESP, Immediate(8));
+  }
 
   // Final conversion to an integer. Unfortunately this also does not have a
   // direct x86 instruction, since NaN should map to 0 and large positive
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 7dfbfb0..ab8b05c 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -598,10 +598,6 @@
 }
 
 void IntrinsicLocationsBuilderX86_64::VisitMathRoundFloat(HInvoke* invoke) {
-  // See intrinsics.h.
-  if (!kRoundIsPlusPointFive) {
-    return;
-  }
   CreateSSE41FPToIntLocations(arena_, invoke, codegen_);
 }
 
@@ -646,10 +642,6 @@
 }
 
 void IntrinsicLocationsBuilderX86_64::VisitMathRoundDouble(HInvoke* invoke) {
-  // See intrinsics.h.
-  if (!kRoundIsPlusPointFive) {
-    return;
-  }
   CreateSSE41FPToIntLocations(arena_, invoke, codegen_);
 }
 
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index ff0bbaf..86548e1 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -680,6 +680,8 @@
   virtual void vpushd(DRegister reg, int nregs, Condition cond = AL) = 0;
   virtual void vpops(SRegister reg, int nregs, Condition cond = AL) = 0;
   virtual void vpopd(DRegister reg, int nregs, Condition cond = AL) = 0;
+  virtual void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) = 0;
+  virtual void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) = 0;
 
   // Branch instructions.
   virtual void b(Label* label, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 6f9d5f3..b8eb60c 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1106,6 +1106,18 @@
 }
 
 
+void Arm32Assembler::vldmiad(Register, DRegister, int, Condition) {
+  LOG(FATAL) << "Unimplemented.";
+  UNREACHABLE();
+}
+
+
+void Arm32Assembler::vstmiad(Register, DRegister, int, Condition) {
+  LOG(FATAL) << "Unimplemented.";
+  UNREACHABLE();
+}
+
+
 void Arm32Assembler::EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond) {
   CHECK_NE(cond, kNoCondition);
   CHECK_GT(nregs, 0);
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 044eaa1..0cb6b17 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -212,6 +212,8 @@
   void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
   void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
   void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+  void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+  void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
 
   // Branch instructions.
   void b(Label* label, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index ee69698..ebdfc98 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -3020,9 +3020,49 @@
 }
 
 
+void Thumb2Assembler::vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond) {
+  int32_t rest = B23;
+  EmitVLdmOrStm(rest,
+                static_cast<uint32_t>(reg),
+                nregs,
+                base_reg,
+                /*is_load*/ true,
+                /*dbl*/ true,
+                cond);
+}
+
+
+void Thumb2Assembler::vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond) {
+  int32_t rest = B23;
+  EmitVLdmOrStm(rest,
+                static_cast<uint32_t>(reg),
+                nregs,
+                base_reg,
+                /*is_load*/ false,
+                /*dbl*/ true,
+                cond);
+}
+
+
 void Thumb2Assembler::EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond) {
+  int32_t rest = B21 | (push ? B24 : B23);
+  EmitVLdmOrStm(rest, reg, nregs, SP, /*is_load*/ !push, dbl, cond);
+}
+
+
+void Thumb2Assembler::EmitVLdmOrStm(int32_t rest,
+                                    uint32_t reg,
+                                    int nregs,
+                                    Register rn,
+                                    bool is_load,
+                                    bool dbl,
+                                    Condition cond) {
   CheckCondition(cond);
 
+  DCHECK_GT(nregs, 0);
+  DCHECK_LE(reg + nregs, 32u);
+  DCHECK(!dbl || (nregs <= 16));
+
   uint32_t D;
   uint32_t Vd;
   if (dbl) {
@@ -3034,14 +3074,17 @@
     D = reg & 1;
     Vd = (reg >> 1) & 15U /* 0b1111 */;
   }
-  int32_t encoding = B27 | B26 | B21 | B19 | B18 | B16 |
-                    B11 | B9 |
-        (dbl ? B8 : 0) |
-        (push ? B24 : (B23 | B20)) |
-        14U /* 0b1110 */ << 28 |
-        nregs << (dbl ? 1 : 0) |
-        D << 22 |
-        Vd << 12;
+
+  int32_t encoding = rest |
+                     14U /* 0b1110 */ << 28 |
+                     B27 | B26 | B11 | B9 |
+                     (is_load ? B20 : 0) |
+                     static_cast<int16_t>(rn) << 16 |
+                     D << 22 |
+                     Vd << 12 |
+                     (dbl ? B8 : 0) |
+                     nregs << (dbl ? 1 : 0);
+
   Emit32(encoding);
 }
 
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 1c1c98b..13f3bec 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -258,6 +258,8 @@
   void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
   void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
   void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+  void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+  void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
 
   // Branch instructions.
   void b(Label* label, Condition cond = AL);
@@ -748,6 +750,14 @@
                   SRegister sn,
                   SRegister sm);
 
+  void EmitVLdmOrStm(int32_t rest,
+                     uint32_t reg,
+                     int nregs,
+                     Register rn,
+                     bool is_load,
+                     bool dbl,
+                     Condition cond);
+
   void EmitVFPddd(Condition cond,
                   int32_t opcode,
                   DRegister dd,
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 3ca3714..d0799d6 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1611,4 +1611,46 @@
   DriverStr(expected, "LoadFromShiftedRegOffset");
 }
 
+TEST_F(AssemblerThumb2Test, VStmLdmPushPop) {
+  // Different D register numbers are used here, to test register encoding.
+  // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
+  // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
+  // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
+  // Different data types (signed and unsigned) are also tested.
+  __ vstmiad(arm::R0, arm::D0, 4);
+  __ vldmiad(arm::R1, arm::D9, 5);
+  __ vpopd(arm::D0, 4);
+  __ vpushd(arm::D9, 5);
+  __ vpops(arm::S0, 4);
+  __ vpushs(arm::S9, 5);
+  __ vpushs(arm::S16, 5);
+  __ vpushd(arm::D0, 16);
+  __ vpushd(arm::D1, 15);
+  __ vpushd(arm::D8, 16);
+  __ vpushd(arm::D31, 1);
+  __ vpushs(arm::S0, 32);
+  __ vpushs(arm::S1, 31);
+  __ vpushs(arm::S16, 16);
+  __ vpushs(arm::S31, 1);
+
+  std::string expected =
+      "vstmia r0, {d0 - d3}\n"
+      "vldmia r1, {d9 - d13}\n"
+      "vpop {d0 - d3}\n"
+      "vpush {d9 - d13}\n"
+      "vpop {s0 - s3}\n"
+      "vpush {s9 - s13}\n"
+      "vpush {s16 - s20}\n"
+      "vpush {d0 - d15}\n"
+      "vpush {d1 - d15}\n"
+      "vpush {d8 - d23}\n"
+      "vpush {d31}\n"
+      "vpush {s0 - s31}\n"
+      "vpush {s1 - s31}\n"
+      "vpush {s16 - s31}\n"
+      "vpush {s31}\n";
+
+  DriverStr(expected, "VStmLdmPushPop");
+}
+
 }  // namespace art
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc
index c039816..af5ebb4 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm.cc
@@ -243,14 +243,16 @@
 }
 
 void ArmJNIMacroAssembler::LoadRef(ManagedRegister mdest,
-                                   ManagedRegister base,
+                                   ManagedRegister mbase,
                                    MemberOffset offs,
                                    bool unpoison_reference) {
+  ArmManagedRegister base = mbase.AsArm();
   ArmManagedRegister dst = mdest.AsArm();
-  CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+  CHECK(base.IsCoreRegister()) << base;
+  CHECK(dst.IsCoreRegister()) << dst;
   __ LoadFromOffset(kLoadWord,
                     dst.AsCoreRegister(),
-                    base.AsArm().AsCoreRegister(),
+                    base.AsCoreRegister(),
                     offs.Int32Value());
   if (unpoison_reference) {
     __ MaybeUnpoisonHeapReference(dst.AsCoreRegister());
@@ -263,13 +265,16 @@
   __ LoadFromOffset(kLoadWord, dst.AsCoreRegister(), SP, src.Int32Value());
 }
 
-void ArmJNIMacroAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
-                           Offset offs) {
+void ArmJNIMacroAssembler::LoadRawPtr(ManagedRegister mdest,
+                                      ManagedRegister mbase,
+                                      Offset offs) {
+  ArmManagedRegister base = mbase.AsArm();
   ArmManagedRegister dst = mdest.AsArm();
-  CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+  CHECK(base.IsCoreRegister()) << base;
+  CHECK(dst.IsCoreRegister()) << dst;
   __ LoadFromOffset(kLoadWord,
                     dst.AsCoreRegister(),
-                    base.AsArm().AsCoreRegister(),
+                    base.AsCoreRegister(),
                     offs.Int32Value());
 }
 
@@ -530,8 +535,9 @@
   // TODO: not validating references.
 }
 
-void ArmJNIMacroAssembler::Call(ManagedRegister mbase, Offset offset,
-                        ManagedRegister mscratch) {
+void ArmJNIMacroAssembler::Call(ManagedRegister mbase,
+                                Offset offset,
+                                ManagedRegister mscratch) {
   ArmManagedRegister base = mbase.AsArm();
   ArmManagedRegister scratch = mscratch.AsArm();
   CHECK(base.IsCoreRegister()) << base;
diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h
index 2dfb65c..0bc1560 100644
--- a/compiler/utils/x86/constants_x86.h
+++ b/compiler/utils/x86/constants_x86.h
@@ -97,6 +97,8 @@
   kNotZero      = kNotEqual,
   kNegative     = kSign,
   kPositive     = kNotSign,
+  kCarrySet     = kBelow,
+  kCarryClear   = kAboveEqual,
   kUnordered    = kParityEven
 };
 
diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h
index 37db6b1..cc508a1 100644
--- a/compiler/utils/x86_64/constants_x86_64.h
+++ b/compiler/utils/x86_64/constants_x86_64.h
@@ -106,6 +106,8 @@
   kNotZero      = kNotEqual,
   kNegative     = kSign,
   kPositive     = kNotSign,
+  kCarrySet     = kBelow,
+  kCarryClear   = kAboveEqual,
   kUnordered    = kParityEven
 };
 
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index 47fb59b..3e687a7 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -288,21 +288,27 @@
 }
 
 void X86_64JNIMacroAssembler::LoadRef(ManagedRegister mdest,
-                                      ManagedRegister base,
+                                      ManagedRegister mbase,
                                       MemberOffset offs,
                                       bool unpoison_reference) {
+  X86_64ManagedRegister base = mbase.AsX86_64();
   X86_64ManagedRegister dest = mdest.AsX86_64();
-  CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
-  __ movl(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+  CHECK(base.IsCpuRegister());
+  CHECK(dest.IsCpuRegister());
+  __ movl(dest.AsCpuRegister(), Address(base.AsCpuRegister(), offs));
   if (unpoison_reference) {
     __ MaybeUnpoisonHeapReference(dest.AsCpuRegister());
   }
 }
 
-void X86_64JNIMacroAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
+void X86_64JNIMacroAssembler::LoadRawPtr(ManagedRegister mdest,
+                                         ManagedRegister mbase,
+                                         Offset offs) {
+  X86_64ManagedRegister base = mbase.AsX86_64();
   X86_64ManagedRegister dest = mdest.AsX86_64();
-  CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
-  __ movq(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+  CHECK(base.IsCpuRegister());
+  CHECK(dest.IsCpuRegister());
+  __ movq(dest.AsCpuRegister(), Address(base.AsCpuRegister(), offs));
 }
 
 void X86_64JNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset64 offs) {
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index f5f02cd..37acef6 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -62,7 +62,6 @@
   libnativebridge \
   libnativeloader \
   libsigchain_dummy \
-  libvixl-arm64 \
   liblog \
   libz \
   libbacktrace \
@@ -83,14 +82,14 @@
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch)))
   ifeq ($(ART_BUILD_HOST_STATIC),true)
-    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static))
+    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libvixl-arm64 $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static))
   endif
 endif
 
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host liblz4,art/compiler,host,debug,$(dex2oat_host_arch)))
   ifeq ($(ART_BUILD_HOST_STATIC),true)
-    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
+    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libvixld-arm64 $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
   endif
 endif
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index eb11f6d..cfcfe1c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1576,7 +1576,7 @@
                                      IsAppImage(),
                                      image_classes_.release(),
                                      compiled_classes_.release(),
-                                     /* compiled_methods */ nullptr,
+                                     compiled_methods_.release(),
                                      thread_count_,
                                      dump_stats_,
                                      dump_passes_,
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index 778fe8e..db327fc 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -90,9 +90,9 @@
   LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
   # For disassembler_arm64.
   ifeq ($$(art_ndebug_or_debug),debug)
-     LOCAL_SHARED_LIBRARIES += libvixl-arm64
+    LOCAL_SHARED_LIBRARIES += libvixld-arm64
   else
-     LOCAL_SHARED_LIBRARIES += libvixl-arm64
+    LOCAL_SHARED_LIBRARIES += libvixl-arm64
   endif
   ifeq ($$(art_target_or_host),target)
     include $(BUILD_SHARED_LIBRARY)
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 2f8b113..b31eaf6 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -164,6 +164,7 @@
   offsets.cc \
   os_linux.cc \
   parsed_options.cc \
+  plugin.cc \
   primitive.cc \
   quick_exception_handler.cc \
   quick/inline_method_analyser.cc \
@@ -177,6 +178,7 @@
   thread.cc \
   thread_list.cc \
   thread_pool.cc \
+  ti/agent.cc \
   trace.cc \
   transaction.cc \
   type_lookup_table.cc \
@@ -370,6 +372,7 @@
   stack.h \
   thread.h \
   thread_state.h \
+  ti/agent.h \
   verifier/method_verifier.h
 
 LIBOPENJDKJVM_SRC_FILES := openjdkjvm/OpenjdkJvm.cc
@@ -419,7 +422,7 @@
   endif
   ifneq ($(4),libart)
     ifneq ($(4),libopenjdkjvm)
-      $$(error expected libart of libopenjdkjvm for argument 4, received $(4))
+      $$(error expected libart or libopenjdkjvm for argument 4, received $(4))
     endif
   endif
 
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 0fcf866..3d0da80 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -191,7 +191,7 @@
     .cfi_rel_offset r11, 44
     .cfi_rel_offset ip, 48
     .cfi_rel_offset lr, 52
-    vpush {s0-s31}                      @ 32 words of float args.
+    vpush {d0-d15}                      @ 32 words of float args.
     .cfi_adjust_cfa_offset 128
     sub sp, #8                          @ 2 words of space, alignment padding and Method*
     .cfi_adjust_cfa_offset 8
@@ -210,7 +210,7 @@
 .macro RESTORE_SAVE_EVERYTHING_FRAME
     add  sp, #8                         @ rewind sp
     .cfi_adjust_cfa_offset -8
-    vpop {s0-s31}
+    vpop {d0-d15}
     .cfi_adjust_cfa_offset -128
     pop {r0-r12, lr}                    @ 14 words of callee saves
     .cfi_restore r0
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 80cac61..35f5c56 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -331,22 +331,23 @@
 #endif
 
     // Save FP registers.
-    stp d0, d1,   [sp, #8]
-    stp d2, d3,   [sp, #24]
-    stp d4, d5,   [sp, #40]
-    stp d6, d7,   [sp, #56]
-    stp d8, d9,   [sp, #72]
-    stp d10, d11, [sp, #88]
-    stp d12, d13, [sp, #104]
-    stp d14, d15, [sp, #120]
-    stp d16, d17, [sp, #136]
-    stp d18, d19, [sp, #152]
-    stp d20, d21, [sp, #168]
-    stp d22, d23, [sp, #184]
-    stp d24, d25, [sp, #200]
-    stp d26, d27, [sp, #216]
-    stp d28, d29, [sp, #232]
-    stp d30, d31, [sp, #248]
+    str d0,       [sp, #8]
+    stp d1, d2,   [sp, #16]
+    stp d3, d4,   [sp, #32]
+    stp d5, d6,   [sp, #48]
+    stp d7, d8,   [sp, #64]
+    stp d9, d10,  [sp, #80]
+    stp d11, d12, [sp, #96]
+    stp d13, d14, [sp, #112]
+    stp d15, d16, [sp, #128]
+    stp d17, d18, [sp, #144]
+    stp d19, d20, [sp, #160]
+    stp d21, d22, [sp, #176]
+    stp d23, d24, [sp, #192]
+    stp d25, d26, [sp, #208]
+    stp d27, d28, [sp, #224]
+    stp d29, d30, [sp, #240]
+    str d31,      [sp, #256]
 
     // Save core registers.
     str x0,       [sp, #264]
@@ -430,22 +431,23 @@
 
 .macro RESTORE_SAVE_EVERYTHING_FRAME
     // Restore FP registers.
-    ldp d0, d1,   [sp, #8]
-    ldp d2, d3,   [sp, #24]
-    ldp d4, d5,   [sp, #40]
-    ldp d6, d7,   [sp, #56]
-    ldp d8, d9,   [sp, #72]
-    ldp d10, d11, [sp, #88]
-    ldp d12, d13, [sp, #104]
-    ldp d14, d15, [sp, #120]
-    ldp d16, d17, [sp, #136]
-    ldp d18, d19, [sp, #152]
-    ldp d20, d21, [sp, #168]
-    ldp d22, d23, [sp, #184]
-    ldp d24, d25, [sp, #200]
-    ldp d26, d27, [sp, #216]
-    ldp d28, d29, [sp, #232]
-    ldp d30, d31, [sp, #248]
+    ldr d0,       [sp, #8]
+    ldp d1, d2,   [sp, #16]
+    ldp d3, d4,   [sp, #32]
+    ldp d5, d6,   [sp, #48]
+    ldp d7, d8,   [sp, #64]
+    ldp d9, d10,  [sp, #80]
+    ldp d11, d12, [sp, #96]
+    ldp d13, d14, [sp, #112]
+    ldp d15, d16, [sp, #128]
+    ldp d17, d18, [sp, #144]
+    ldp d19, d20, [sp, #160]
+    ldp d21, d22, [sp, #176]
+    ldp d23, d24, [sp, #192]
+    ldp d25, d26, [sp, #208]
+    ldp d27, d28, [sp, #224]
+    ldp d29, d30, [sp, #240]
+    ldr d31,      [sp, #256]
 
     // Restore core registers.
     ldr x0,       [sp, #264]
diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc
index 81ca010..b35e088 100644
--- a/runtime/arch/instruction_set.cc
+++ b/runtime/arch/instruction_set.cc
@@ -18,6 +18,7 @@
 
 // Explicitly include our own elf.h to avoid Linux and other dependencies.
 #include "../elf.h"
+#include "base/bit_utils.h"
 #include "globals.h"
 
 namespace art {
@@ -113,14 +114,44 @@
   }
 }
 
-static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
-static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-static constexpr size_t kMips64StackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
+#if !defined(ART_STACK_OVERFLOW_GAP_arm) || !defined(ART_STACK_OVERFLOW_GAP_arm64) || \
+    !defined(ART_STACK_OVERFLOW_GAP_mips) || !defined(ART_STACK_OVERFLOW_GAP_mips64) || \
+    !defined(ART_STACK_OVERFLOW_GAP_x86) || !defined(ART_STACK_OVERFLOW_GAP_x86_64)
+#error "Missing defines for stack overflow gap"
+#endif
 
-static constexpr size_t kArmStackOverflowReservedBytes =    8 * KB;
-static constexpr size_t kArm64StackOverflowReservedBytes =  8 * KB;
-static constexpr size_t kX86StackOverflowReservedBytes =    8 * KB;
-static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB;
+static constexpr size_t kArmStackOverflowReservedBytes    = ART_STACK_OVERFLOW_GAP_arm;
+static constexpr size_t kArm64StackOverflowReservedBytes  = ART_STACK_OVERFLOW_GAP_arm64;
+static constexpr size_t kMipsStackOverflowReservedBytes   = ART_STACK_OVERFLOW_GAP_mips;
+static constexpr size_t kMips64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_mips64;
+static constexpr size_t kX86StackOverflowReservedBytes    = ART_STACK_OVERFLOW_GAP_x86;
+static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
+
+static_assert(IsAligned<kPageSize>(kArmStackOverflowReservedBytes), "ARM gap not page aligned");
+static_assert(IsAligned<kPageSize>(kArm64StackOverflowReservedBytes), "ARM64 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kMipsStackOverflowReservedBytes), "Mips gap not page aligned");
+static_assert(IsAligned<kPageSize>(kMips64StackOverflowReservedBytes),
+              "Mips64 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kX86StackOverflowReservedBytes), "X86 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kX86_64StackOverflowReservedBytes),
+              "X86_64 gap not page aligned");
+
+#if !defined(ART_FRAME_SIZE_LIMIT)
+#error "ART frame size limit missing"
+#endif
+
+// TODO: Should we require an extra page (RoundUp(SIZE) + kPageSize)?
+static_assert(ART_FRAME_SIZE_LIMIT < kArmStackOverflowReservedBytes, "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kArm64StackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kMipsStackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kMips64StackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kX86StackOverflowReservedBytes,
+              "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kX86_64StackOverflowReservedBytes,
+              "Frame size limit too large");
 
 size_t GetStackOverflowReservedBytes(InstructionSet isa) {
   switch (isa) {
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 6323eee..ac21a3f 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -57,6 +57,7 @@
   bool verifier;
   bool image;
   bool systrace_lock_logging;  // Enabled with "-verbose:sys-locks".
+  bool agents;
 };
 
 // Global log verbosity setting, initialized by InitLogging.
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 3c43253..5a50247 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -75,7 +75,7 @@
     ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; } \
     ALWAYS_INLINE void operator delete(void*, void*) noexcept { } \
   private: \
-    void* operator new(size_t) = delete // NOLINT
+    void* operator new(size_t) = delete  // NOLINT
 
 // The arraysize(arr) macro returns the # of elements in an array arr.
 // The expression is a compile-time constant, and therefore can be
@@ -135,13 +135,13 @@
 #define ARRAYSIZE_UNSAFE(a) \
   ((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
 
-#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f) // NOLINT
+#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f)  // NOLINT
 
 #define OFFSETOF_MEMBER(t, f) \
-  (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
+  (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u))  // NOLINT
 
 #define OFFSETOF_MEMBERPTR(t, f) \
-  (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
+  (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16))  // NOLINT
 
 #define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 46722ec..4d48da6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4630,18 +4630,23 @@
         } else {
           value_it.ReadValueToField<false>(field);
         }
+        if (self->IsExceptionPending()) {
+          break;
+        }
         DCHECK(!value_it.HasNext() || field_it.HasNextStaticField());
       }
     }
   }
 
-  ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
-  if (clinit != nullptr) {
-    CHECK(can_init_statics);
-    JValue result;
-    clinit->Invoke(self, nullptr, 0, &result, "V");
-  }
 
+  if (!self->IsExceptionPending()) {
+    ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
+    if (clinit != nullptr) {
+      CHECK(can_init_statics);
+      JValue result;
+      clinit->Invoke(self, nullptr, 0, &result, "V");
+    }
+  }
   self->AllowThreadSuspension();
   uint64_t t1 = NanoTime();
 
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index fde1a5f..7faa2dc 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -26,6 +26,8 @@
   // The actual flag values.
   enum {
     kNone           = 0x0000,
+    kAgents         = 0x0001,  // 0b00000001
+    kRuntimePlugins = 0x0002,  // 0b00000010
   };
 
   constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -61,9 +63,19 @@
   uint32_t value_;
 };
 
-inline std::ostream& operator<<(std::ostream& stream,
-                                const ExperimentalFlags& e ATTRIBUTE_UNUSED) {
-  stream << "kNone";
+inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e) {
+  bool started = false;
+  if (e & ExperimentalFlags::kAgents) {
+    stream << (started ? "|" : "") << "kAgents";
+    started = true;
+  }
+  if (e & ExperimentalFlags::kRuntimePlugins) {
+    stream << (started ? "|" : "") << "kRuntimePlugins";
+    started = true;
+  }
+  if (!started) {
+    stream << "kNone";
+  }
   return stream;
 }
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 88fbf78..b574c3b 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -230,6 +230,9 @@
       total_wait_time_(0),
       verify_object_mode_(kVerifyObjectModeDisabled),
       disable_moving_gc_count_(0),
+      semi_space_collector_(nullptr),
+      mark_compact_collector_(nullptr),
+      concurrent_copying_collector_(nullptr),
       is_running_on_memory_tool_(Runtime::Current()->IsRunningOnMemoryTool()),
       use_tlab_(use_tlab),
       main_space_backup_(nullptr),
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index c2e2a1e..8d406b3 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -529,6 +529,17 @@
                                error_msg);
     }
     if (space != nullptr) {
+      // Check whether there is enough space left over in the data partition. Even if we can load
+      // the image, we need to be conservative, as some parts of the platform are not very tolerant
+      // of space constraints.
+      // ImageSpace doesn't know about the data partition per se, it relies on the FindImageFilename
+      // helper (which relies on GetDalvikCache). So for now, if we load an image out of /system,
+      // ignore the check (as it would test for free space in /system instead).
+      if (!is_system && !CheckSpace(*image_filename, error_msg)) {
+        // No. Delete the generated image and try to run out of the dex files.
+        PruneDalvikCache(image_isa);
+        return nullptr;
+      }
       return space;
     }
 
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index eac52f7..4d539d2 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -223,8 +223,21 @@
     file.reset(OS::CreateEmptyFile(file_name));
 
     if (file.get() == nullptr) {
+      int saved_errno = errno;
       PLOG(WARNING) << "Failed to create boot marker.";
-      return;
+      if (saved_errno != ENOSPC) {
+        return;
+      }
+
+      LOG(WARNING) << "Pruning dalvik cache because of low-memory situation.";
+      impl::DeleteDirectoryContents(isa_subdir, false);
+
+      // Try once more.
+      file.reset(OS::OpenFileReadWrite(file_name));
+      if (file == nullptr) {
+        PLOG(WARNING) << "Failed to create boot marker.";
+        return;
+      }
     }
   } else {
     if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index c644cde..2401bec 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -48,7 +48,7 @@
 static const size_t kWeakGlobalsInitial = 16;  // Arbitrary.
 static const size_t kWeakGlobalsMax = 51200;  // Arbitrary sanity check. (Must fit in 16 bits.)
 
-static bool IsBadJniVersion(int version) {
+bool JavaVMExt::IsBadJniVersion(int version) {
   // We don't support JNI_VERSION_1_1. These are the only other valid versions.
   return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
 }
@@ -344,13 +344,6 @@
   }
 
   static jint GetEnv(JavaVM* vm, void** env, jint version) {
-    // GetEnv always returns a JNIEnv* for the most current supported JNI version,
-    // and unlike other calls that take a JNI version doesn't care if you supply
-    // JNI_VERSION_1_1, which we don't otherwise support.
-    if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
-      LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
-      return JNI_EVERSION;
-    }
     if (vm == nullptr || env == nullptr) {
       return JNI_ERR;
     }
@@ -359,8 +352,8 @@
       *env = nullptr;
       return JNI_EDETACHED;
     }
-    *env = thread->GetJniEnv();
-    return JNI_OK;
+    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+    return raw_vm->HandleGetEnv(env, version);
   }
 
  private:
@@ -388,7 +381,7 @@
     const char* thread_name = nullptr;
     jobject thread_group = nullptr;
     if (args != nullptr) {
-      if (IsBadJniVersion(args->version)) {
+      if (JavaVMExt::IsBadJniVersion(args->version)) {
         LOG(ERROR) << "Bad JNI version passed to "
                    << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
                    << args->version;
@@ -436,7 +429,8 @@
       weak_globals_lock_("JNI weak global reference table lock", kJniWeakGlobalsLock),
       weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
       allow_accessing_weak_globals_(true),
-      weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
+      weak_globals_add_condition_("weak globals add condition", weak_globals_lock_),
+      env_hooks_() {
   functions = unchecked_functions_;
   SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
 }
@@ -444,6 +438,26 @@
 JavaVMExt::~JavaVMExt() {
 }
 
+jint JavaVMExt::HandleGetEnv(/*out*/void** env, jint version) {
+  for (GetEnvHook hook : env_hooks_) {
+    jint res = hook(this, env, version);
+    if (res == JNI_OK) {
+      return JNI_OK;
+    } else if (res != JNI_EVERSION) {
+      LOG(ERROR) << "Error returned from a plugin GetEnv handler! " << res;
+      return res;
+    }
+  }
+  LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
+  return JNI_EVERSION;
+}
+
+// Add a hook to handle getting environments from the GetEnv call.
+void JavaVMExt::AddEnvironmentHook(GetEnvHook hook) {
+  CHECK(hook != nullptr) << "environment hooks shouldn't be null!";
+  env_hooks_.push_back(hook);
+}
+
 void JavaVMExt::JniAbort(const char* jni_function_name, const char* msg) {
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
@@ -866,7 +880,7 @@
 
     if (version == JNI_ERR) {
       StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
-    } else if (IsBadJniVersion(version)) {
+    } else if (JavaVMExt::IsBadJniVersion(version)) {
       StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
                     path.c_str(), version);
       // It's unwise to call dlclose() here, but we can mark it
@@ -939,7 +953,7 @@
 extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
   ScopedTrace trace(__FUNCTION__);
   const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
-  if (IsBadJniVersion(args->version)) {
+  if (JavaVMExt::IsBadJniVersion(args->version)) {
     LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
     return JNI_EVERSION;
   }
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 3d055cd..ed9d3ab 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -36,6 +36,10 @@
 class Runtime;
 struct RuntimeArgumentMap;
 
+class JavaVMExt;
+// Hook definition for runtime plugins.
+using GetEnvHook = jint (*)(JavaVMExt* vm, /*out*/void** new_env, jint version);
+
 class JavaVMExt : public JavaVM {
  public:
   JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
@@ -171,6 +175,12 @@
   void TrimGlobals() SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!globals_lock_);
 
+  jint HandleGetEnv(/*out*/void** env, jint version);
+
+  void AddEnvironmentHook(GetEnvHook hook);
+
+  static bool IsBadJniVersion(int version);
+
  private:
   // Return true if self can currently access weak globals.
   bool MayAccessWeakGlobalsUnlocked(Thread* self) const SHARED_REQUIRES(Locks::mutator_lock_);
@@ -215,6 +225,9 @@
   Atomic<bool> allow_accessing_weak_globals_;
   ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
 
+  // TODO Maybe move this to Runtime.
+  std::vector<GetEnvHook> env_hooks_;
+
   DISALLOW_COPY_AND_ASSIGN(JavaVMExt);
 };
 
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 1ee1611..40efc89 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -45,6 +45,20 @@
   return in->locals.IsValid();
 }
 
+jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
+  UNUSED(vm);
+  // GetEnv always returns a JNIEnv* for the most current supported JNI version,
+  // and unlike other calls that take a JNI version doesn't care if you supply
+  // JNI_VERSION_1_1, which we don't otherwise support.
+  if (JavaVMExt::IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+    return JNI_EVERSION;
+  }
+  Thread* thread = Thread::Current();
+  CHECK(thread != nullptr);
+  *env = thread->GetJniEnv();
+  return JNI_OK;
+}
+
 JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in) {
   std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in));
   if (CheckLocalsValid(ret.get())) {
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index d4accc3..ac287d4 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -54,6 +54,8 @@
   static Offset LocalRefCookieOffset(size_t pointer_size);
   static Offset SelfOffset(size_t pointer_size);
 
+  static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version);
+
   jobject NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
   void DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
 
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c7e4f8b..174da79 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -23,6 +23,7 @@
 #include "gc/heap.h"
 #include "monitor.h"
 #include "runtime.h"
+#include "ti/agent.h"
 #include "trace.h"
 #include "utils.h"
 
@@ -90,6 +91,13 @@
       .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
           .WithType<JDWP::JdwpOptions>()
           .IntoKey(M::JdwpOptions)
+      // TODO Re-enable -agentlib: once I have a good way to transform the values.
+      // .Define("-agentlib:_")
+      //     .WithType<std::vector<ti::Agent>>().AppendValues()
+      //     .IntoKey(M::AgentLib)
+      .Define("-agentpath:_")
+          .WithType<std::vector<ti::Agent>>().AppendValues()
+          .IntoKey(M::AgentPath)
       .Define("-Xms_")
           .WithType<MemoryKiB>()
           .IntoKey(M::MemoryInitialSize)
@@ -289,6 +297,9 @@
           .IntoKey(M::Experimental)
       .Define("-Xforce-nb-testing")
           .IntoKey(M::ForceNativeBridge)
+      .Define("-Xplugin:_")
+          .WithType<std::vector<Plugin>>().AppendValues()
+          .IntoKey(M::Plugins)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -583,6 +594,42 @@
     args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
   }
 
+  if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kRuntimePlugins) {
+    LOG(WARNING) << "Experimental runtime plugin support has been enabled. No guarantees are made "
+                 << "about stability or usage of this plugin support. Use at your own risk. Do "
+                 << "not attempt to write shipping code that relies on the implementation of "
+                 << "runtime plugins.";
+  } else if (!args.GetOrDefault(M::Plugins).empty()) {
+    LOG(WARNING) << "Experimental runtime plugin support has not been enabled. Ignored options: ";
+    for (auto& op : args.GetOrDefault(M::Plugins)) {
+      LOG(WARNING) << "    -plugin:" << op.GetLibrary();
+    }
+  }
+
+  if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kAgents) {
+    LOG(WARNING) << "Experimental runtime agent support has been enabled. No guarantees are made "
+                 << "the completeness, accuracy, reliability, or stability of the agent "
+                 << "implementation. Use at your own risk. Do not attempt to write shipping code "
+                 << "that relies on the implementation of any part of this api.";
+  } else if (!args.GetOrDefault(M::AgentLib).empty() || !args.GetOrDefault(M::AgentPath).empty()) {
+    LOG(WARNING) << "agent support has not been enabled. Enable experimental agent "
+                 << " support with '-XExperimental:agent'. Ignored options are:";
+    for (auto op : args.GetOrDefault(M::AgentLib)) {
+      if (op.HasArgs()) {
+        LOG(WARNING) << "    -agentlib:" << op.GetName() << "=" << op.GetArgs();
+      } else {
+        LOG(WARNING) << "    -agentlib:" << op.GetName();
+      }
+    }
+    for (auto op : args.GetOrDefault(M::AgentPath)) {
+      if (op.HasArgs()) {
+        LOG(WARNING) << "    -agentpath:" << op.GetName() << "=" << op.GetArgs();
+      } else {
+        LOG(WARNING) << "    -agentpath:" << op.GetName();
+      }
+    }
+  }
+
   *runtime_options = std::move(args);
   return true;
 }
@@ -627,6 +674,11 @@
   UsageMessage(stream, "  -showversion\n");
   UsageMessage(stream, "  -help\n");
   UsageMessage(stream, "  -agentlib:jdwp=options\n");
+  // TODO add back in once -agentlib actually does something.
+  // UsageMessage(stream, "  -agentlib:library=options (Experimental feature, "
+  //                      "requires -Xexperimental:agent, some features might not be supported)\n");
+  UsageMessage(stream, "  -agentpath:library_path=options (Experimental feature, "
+                       "requires -Xexperimental:agent, some features might not be supported)\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following extended options are supported:\n");
@@ -703,6 +755,12 @@
   UsageMessage(stream, "  -X[no]image-dex2oat (Whether to create and use a boot image)\n");
   UsageMessage(stream, "  -Xno-dex-file-fallback "
                        "(Don't fall back to dex files without oat files)\n");
+  UsageMessage(stream, "  -Xplugin:<library.so> "
+                       "(Load a runtime plugin, requires -Xexperimental:runtime-plugins)\n");
+  UsageMessage(stream, "  -Xexperimental:runtime-plugins"
+                       "(Enable new and experimental agent support)\n");
+  UsageMessage(stream, "  -Xexperimental:agents"
+                       "(Enable new and experimental agent support)\n");
   UsageMessage(stream, "\n");
 
   UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/plugin.cc b/runtime/plugin.cc
new file mode 100644
index 0000000..481b1ca
--- /dev/null
+++ b/runtime/plugin.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "plugin.h"
+
+#include <dlfcn.h>
+#include "base/stringprintf.h"
+#include "base/logging.h"
+
+namespace art {
+
+const char* PLUGIN_INITIALIZATION_FUNCTION_NAME = "ArtPlugin_Initialize";
+const char* PLUGIN_DEINITIALIZATION_FUNCTION_NAME = "ArtPlugin_Deinitialize";
+
+Plugin::Plugin(const Plugin& other) : library_(other.library_), dlopen_handle_(nullptr) {
+  if (other.IsLoaded()) {
+    std::string err;
+    Load(&err);
+  }
+}
+
+bool Plugin::Load(/*out*/std::string* error_msg) {
+  DCHECK(!IsLoaded());
+  void* res = dlopen(library_.c_str(), RTLD_LAZY);
+  if (res == nullptr) {
+    *error_msg = StringPrintf("dlopen failed: %s", dlerror());
+    return false;
+  }
+  // Get the initializer function
+  PluginInitializationFunction init = reinterpret_cast<PluginInitializationFunction>(
+      dlsym(res, PLUGIN_INITIALIZATION_FUNCTION_NAME));
+  if (init != nullptr) {
+    if (!init()) {
+      dlclose(res);
+      *error_msg = StringPrintf("Initialization of plugin failed");
+      return false;
+    }
+  } else {
+    LOG(WARNING) << this << " does not include an initialization function";
+  }
+  dlopen_handle_ = res;
+  return true;
+}
+
+bool Plugin::Unload() {
+  DCHECK(IsLoaded());
+  bool ret = true;
+  void* handle = dlopen_handle_;
+  PluginDeinitializationFunction deinit = reinterpret_cast<PluginDeinitializationFunction>(
+      dlsym(handle, PLUGIN_DEINITIALIZATION_FUNCTION_NAME));
+  if (deinit != nullptr) {
+    if (!deinit()) {
+      LOG(WARNING) << this << " failed deinitialization";
+      ret = false;
+    }
+  } else {
+    LOG(WARNING) << this << " does not include a deinitialization function";
+  }
+  dlopen_handle_ = nullptr;
+  if (dlclose(handle) != 0) {
+    LOG(ERROR) << this << " failed to dlclose: " << dlerror();
+    ret = false;
+  }
+  return ret;
+}
+
+std::ostream& operator<<(std::ostream &os, const Plugin* m) {
+  return os << *m;
+}
+
+std::ostream& operator<<(std::ostream &os, Plugin const& m) {
+  return os << "Plugin { library=\"" << m.library_ << "\", handle=" << m.dlopen_handle_ << " }";
+}
+
+}  // namespace art
diff --git a/runtime/plugin.h b/runtime/plugin.h
new file mode 100644
index 0000000..18f3977
--- /dev/null
+++ b/runtime/plugin.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_PLUGIN_H_
+#define ART_RUNTIME_PLUGIN_H_
+
+#include <string>
+#include "base/logging.h"
+
+namespace art {
+
+// This function is loaded from the plugin (if present) and called during runtime initialization.
+// By the time this has been called the runtime has been fully initialized but not other native
+// libraries have been loaded yet. Failure to initialize is considered a fatal error.
+// TODO might want to give initialization function some arguments
+using PluginInitializationFunction = bool (*)();
+using PluginDeinitializationFunction = bool (*)();
+
+// A class encapsulating a plugin. There is no stable plugin ABI or API and likely never will be.
+// TODO Might want to put some locking in this but ATM we only load these at initialization in a
+// single-threaded fashion so not much need
+class Plugin {
+ public:
+  static Plugin Create(std::string lib) {
+    return Plugin(lib);
+  }
+
+  bool IsLoaded() const {
+    return dlopen_handle_ != nullptr;
+  }
+
+  const std::string& GetLibrary() const {
+    return library_;
+  }
+
+  bool Load(/*out*/std::string* error_msg);
+  bool Unload();
+
+
+  ~Plugin() {
+    if (IsLoaded() && !Unload()) {
+      LOG(ERROR) << "Error unloading " << this;
+    }
+  }
+
+  Plugin(const Plugin& other);
+
+  // Create move constructor for putting this in a list
+  Plugin(Plugin&& other)
+      : library_(other.library_),
+        dlopen_handle_(other.dlopen_handle_) {
+    other.dlopen_handle_ = nullptr;
+  }
+
+ private:
+  explicit Plugin(std::string library) : library_(library), dlopen_handle_(nullptr) { }
+
+  std::string library_;
+  void* dlopen_handle_;
+
+  friend std::ostream& operator<<(std::ostream &os, Plugin const& m);
+};
+
+std::ostream& operator<<(std::ostream &os, Plugin const& m);
+std::ostream& operator<<(std::ostream &os, const Plugin* m);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_PLUGIN_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 68fa0d3..ddcfb6d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -130,6 +130,7 @@
 #include "signal_set.h"
 #include "thread.h"
 #include "thread_list.h"
+#include "ti/agent.h"
 #include "trace.h"
 #include "transaction.h"
 #include "utils.h"
@@ -281,6 +282,16 @@
     jit_->StopProfileSaver();
   }
 
+  // TODO Maybe do some locking.
+  for (auto& agent : agents_) {
+    agent.Unload();
+  }
+
+  // TODO Maybe do some locking
+  for (auto& plugin : plugins_) {
+    plugin.Unload();
+  }
+
   // Make sure our internal threads are dead before we start tearing down things they're using.
   Dbg::StopJdwp();
   delete signal_catcher_;
@@ -960,6 +971,16 @@
   experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
   is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
 
+  if (experimental_flags_ & ExperimentalFlags::kRuntimePlugins) {
+    plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
+  }
+  if (experimental_flags_ & ExperimentalFlags::kAgents) {
+    agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
+    // TODO Add back in -agentlib
+    // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) {
+    //   agents_.push_back(lib);
+    // }
+  }
   XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
   heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
                        runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
@@ -1084,6 +1105,10 @@
 
   java_vm_ = new JavaVMExt(this, runtime_options);
 
+  // Add the JniEnv handler.
+  // TODO Refactor this stuff.
+  java_vm_->AddEnvironmentHook(JNIEnvExt::GetEnvHandler);
+
   Thread::Startup();
 
   // ClassLinker needs an attached thread, but we can't fully attach a thread without creating
@@ -1200,6 +1225,16 @@
   pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException());
   self->ClearException();
 
+  // Runtime initialization is largely done now.
+  // We load plugins first since that can modify the runtime state slightly.
+  // Load all plugins
+  for (auto& plugin : plugins_) {
+    std::string err;
+    if (!plugin.Load(&err)) {
+      LOG(FATAL) << plugin << " failed to load: " << err;
+    }
+  }
+
   // Look for a native bridge.
   //
   // The intended flow here is, in the case of a running system:
@@ -1232,6 +1267,20 @@
     is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
   }
 
+  // Startup agents
+  // TODO Maybe we should start a new thread to run these on. Investigate RI behavior more.
+  for (auto& agent : agents_) {
+    // TODO Check err
+    int res = 0;
+    std::string err = "";
+    ti::Agent::LoadError result = agent.Load(&res, &err);
+    if (result == ti::Agent::kInitializationError) {
+      LOG(FATAL) << "Unable to initialize agent!";
+    } else if (result != ti::Agent::kNoError) {
+      LOG(ERROR) << "Unable to load an agent: " << err;
+    }
+  }
+
   VLOG(startup) << "Runtime::Init exiting";
 
   return true;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c971646..6da60f2 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -63,6 +63,9 @@
   class String;
   class Throwable;
 }  // namespace mirror
+namespace ti {
+  class Agent;
+}  // namespace ti
 namespace verifier {
   class MethodVerifier;
   enum class VerifyMode : int8_t;
@@ -80,6 +83,7 @@
 class MonitorPool;
 class NullPointerHandler;
 class OatFileManager;
+class Plugin;
 struct RuntimeArgumentMap;
 class SignalCatcher;
 class StackOverflowHandler;
@@ -698,6 +702,9 @@
   std::string class_path_string_;
   std::vector<std::string> properties_;
 
+  std::vector<ti::Agent> agents_;
+  std::vector<Plugin> plugins_;
+
   // The default stack size for managed threads created by the runtime.
   size_t default_stack_size_;
 
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index b95dfad..146afc7 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,10 @@
 RUNTIME_OPTIONS_KEY (Unit,                NoDexFileFallback)
 RUNTIME_OPTIONS_KEY (std::string,         CpuAbiList)
 RUNTIME_OPTIONS_KEY (std::string,         Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none}
+RUNTIME_OPTIONS_KEY (ExperimentalFlags,   Experimental,     ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentLib)  // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>,         AgentPath)  // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
+RUNTIME_OPTIONS_KEY (std::vector<Plugin>,            Plugins)  // -Xplugin:<library> Requires -Xexperimental:runtime-plugins
 
 // Not parse-able from command line, but can be provided explicitly.
 // (Do not add anything here that is defined in ParsedOptions::MakeParser)
diff --git a/runtime/simulator/Android.mk b/runtime/simulator/Android.mk
index 953a377..a34a841 100644
--- a/runtime/simulator/Android.mk
+++ b/runtime/simulator/Android.mk
@@ -88,9 +88,9 @@
   LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
   # For simulator_arm64.
   ifeq ($$(art_ndebug_or_debug),debug)
-     LOCAL_SHARED_LIBRARIES += libvixl-arm64
+    LOCAL_SHARED_LIBRARIES += libvixld-arm64
   else
-     LOCAL_SHARED_LIBRARIES += libvixl-arm64
+    LOCAL_SHARED_LIBRARIES += libvixl-arm64
   endif
   ifeq ($$(art_target_or_host),target)
     include $(BUILD_SHARED_LIBRARY)
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 4647d67..dd7e531 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1050,7 +1050,7 @@
       inline_info_encoding = *reinterpret_cast<const InlineInfoEncoding*>(ptr);
       ptr += sizeof(InlineInfoEncoding);
     } else {
-      inline_info_encoding = InlineInfoEncoding{}; // NOLINT.
+      inline_info_encoding = InlineInfoEncoding{};  // NOLINT.
     }
     header_size = dchecked_integral_cast<uint8_t>(ptr - reinterpret_cast<const uint8_t*>(data));
   }
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
new file mode 100644
index 0000000..41a21f7
--- /dev/null
+++ b/runtime/ti/agent.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "agent.h"
+#include "java_vm_ext.h"
+#include "runtime.h"
+
+namespace art {
+namespace ti {
+
+const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
+const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
+const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
+
+Agent Agent::Create(std::string arg) {
+  size_t eq = arg.find_first_of('=');
+  if (eq == std::string::npos) {
+    return Agent(arg, "");
+  } else {
+    return Agent(arg.substr(0, eq), arg.substr(eq + 1, arg.length()));
+  }
+}
+
+// TODO We need to acquire some locks probably.
+Agent::LoadError Agent::Load(/*out*/jint* call_res, /*out*/ std::string* error_msg) {
+  DCHECK(call_res != nullptr);
+  DCHECK(error_msg != nullptr);
+  if (IsStarted()) {
+    *error_msg = StringPrintf("the agent at %s has already been started!", name_.c_str());
+    VLOG(agents) << "err: " << *error_msg;
+    return kAlreadyStarted;
+  }
+  LoadError err = DoDlOpen(error_msg);
+  if (err != kNoError) {
+    VLOG(agents) << "err: " << *error_msg;
+    return err;
+  }
+  if (onload_ == nullptr) {
+    *error_msg = StringPrintf("Unable to start agent %s: No Agent_OnLoad function found",
+                              name_.c_str());
+    VLOG(agents) << "err: " << *error_msg;
+    return kLoadingError;
+  }
+  // TODO Need to do some checks that we are at a good spot etc.
+  *call_res = onload_(static_cast<JavaVM*>(Runtime::Current()->GetJavaVM()),
+                      args_.c_str(),
+                      nullptr);
+  if (*call_res != 0) {
+    *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
+                              name_.c_str(), *call_res);
+    VLOG(agents) << "err: " << *error_msg;
+    return kInitializationError;
+  } else {
+    return kNoError;
+  }
+}
+
+Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
+  DCHECK(error_msg != nullptr);
+  dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY);
+  if (dlopen_handle_ == nullptr) {
+    *error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror());
+    return kLoadingError;
+  }
+
+  onload_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
+                                                        AGENT_ON_LOAD_FUNCTION_NAME));
+  if (onload_ == nullptr) {
+    VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
+  }
+  onattach_ = reinterpret_cast<AgentOnAttachFunction>(dlsym(dlopen_handle_,
+                                                            AGENT_ON_ATTACH_FUNCTION_NAME));
+  if (onattach_ == nullptr) {
+    VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
+  }
+  onunload_= reinterpret_cast<AgentOnUnloadFunction>(dlsym(dlopen_handle_,
+                                                           AGENT_ON_UNLOAD_FUNCTION_NAME));
+  if (onunload_ == nullptr) {
+    VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
+  }
+  return kNoError;
+}
+
+// TODO Lock some stuff probably.
+void Agent::Unload() {
+  if (dlopen_handle_ != nullptr) {
+    if (onunload_ != nullptr) {
+      onunload_(Runtime::Current()->GetJavaVM());
+    }
+    dlclose(dlopen_handle_);
+    dlopen_handle_ = nullptr;
+  } else {
+    VLOG(agents) << this << " is not currently loaded!";
+  }
+}
+
+Agent::Agent(const Agent& other)
+  : name_(other.name_),
+    args_(other.args_),
+    dlopen_handle_(other.dlopen_handle_),
+    onload_(other.onload_),
+    onattach_(other.onattach_),
+    onunload_(other.onunload_) {
+  if (other.dlopen_handle_ != nullptr) {
+    dlopen(other.name_.c_str(), 0);
+  }
+}
+
+Agent::~Agent() {
+  if (dlopen_handle_ != nullptr) {
+    dlclose(dlopen_handle_);
+  }
+}
+
+std::ostream& operator<<(std::ostream &os, const Agent* m) {
+  return os << *m;
+}
+
+std::ostream& operator<<(std::ostream &os, Agent const& m) {
+  return os << "Agent { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\", handle="
+            << m.dlopen_handle_ << " }";
+}
+
+}  // namespace ti
+}  // namespace art
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
new file mode 100644
index 0000000..521e21e
--- /dev/null
+++ b/runtime/ti/agent.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_TI_AGENT_H_
+#define ART_RUNTIME_TI_AGENT_H_
+
+#include <dlfcn.h>
+#include <jni.h>  // for jint, JavaVM* etc declarations
+
+#include "base/stringprintf.h"
+#include "runtime.h"
+#include "utils.h"
+
+namespace art {
+namespace ti {
+
+using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*);
+using AgentOnAttachFunction = jint (*)(JavaVM*, const char*, void*);
+using AgentOnUnloadFunction = void (*)(JavaVM*);
+
+class Agent {
+ public:
+  enum LoadError {
+    kNoError,              // No error occurred..
+    kAlreadyStarted,       // The agent has already been loaded.
+    kLoadingError,         // dlopen or dlsym returned an error.
+    kInitializationError,  // The entrypoint did not return 0. This might require an abort.
+  };
+
+  bool IsStarted() const {
+    return dlopen_handle_ != nullptr;
+  }
+
+  const std::string& GetName() const {
+    return name_;
+  }
+
+  const std::string& GetArgs() const {
+    return args_;
+  }
+
+  bool HasArgs() const {
+    return !GetArgs().empty();
+  }
+
+  // TODO We need to acquire some locks probably.
+  LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg);
+
+  // TODO We need to acquire some locks probably.
+  void Unload();
+
+  // Tries to attach the agent using its OnAttach method. Returns true on success.
+  // TODO We need to acquire some locks probably.
+  LoadError Attach(std::string* error_msg) {
+    // TODO
+    *error_msg = "Attach has not yet been implemented!";
+    return kLoadingError;
+  }
+
+  static Agent Create(std::string arg);
+
+  static Agent Create(std::string name, std::string args) {
+    return Agent(name, args);
+  }
+
+  ~Agent();
+
+  // We need move constructor and copy for vectors
+  Agent(const Agent& other);
+
+  Agent(Agent&& other)
+      : name_(other.name_),
+        args_(other.args_),
+        dlopen_handle_(nullptr),
+        onload_(nullptr),
+        onattach_(nullptr),
+        onunload_(nullptr) {
+    other.dlopen_handle_ = nullptr;
+    other.onload_ = nullptr;
+    other.onattach_ = nullptr;
+    other.onunload_ = nullptr;
+  }
+
+  // We don't need an operator=
+  void operator=(const Agent&) = delete;
+
+ private:
+  Agent(std::string name, std::string args)
+      : name_(name),
+        args_(args),
+        dlopen_handle_(nullptr),
+        onload_(nullptr),
+        onattach_(nullptr),
+        onunload_(nullptr) { }
+
+  LoadError DoDlOpen(/*out*/std::string* error_msg);
+
+  const std::string name_;
+  const std::string args_;
+  void* dlopen_handle_;
+
+  // The entrypoints.
+  AgentOnLoadFunction onload_;
+  AgentOnAttachFunction onattach_;
+  AgentOnUnloadFunction onunload_;
+
+  friend std::ostream& operator<<(std::ostream &os, Agent const& m);
+};
+
+std::ostream& operator<<(std::ostream &os, Agent const& m);
+std::ostream& operator<<(std::ostream &os, const Agent* m);
+
+}  // namespace ti
+}  // namespace art
+
+#endif  // ART_RUNTIME_TI_AGENT_H_
+
diff --git a/test/617-clinit-oome/expected.txt b/test/617-clinit-oome/expected.txt
new file mode 100644
index 0000000..c1d33ff
--- /dev/null
+++ b/test/617-clinit-oome/expected.txt
@@ -0,0 +1 @@
+Filling heap
diff --git a/test/617-clinit-oome/info.txt b/test/617-clinit-oome/info.txt
new file mode 100644
index 0000000..ece35b2
--- /dev/null
+++ b/test/617-clinit-oome/info.txt
@@ -0,0 +1 @@
+Regression test for encoded static strings caussing OOME b/30690988
diff --git a/test/617-clinit-oome/src/Main.java b/test/617-clinit-oome/src/Main.java
new file mode 100644
index 0000000..749a232
--- /dev/null
+++ b/test/617-clinit-oome/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    Class klass = Other.class;
+    Object[] data = new Object[100000];
+    try {
+        System.out.println("Filling heap");
+        int size = 256 * 1024 * 1024;
+        int index = 0;
+        while (true) {
+            try {
+                data[index] = new byte[size];
+                index++;
+            } catch (OutOfMemoryError e) {
+                size /= 2;
+                if (size == 0) {
+                    break;
+                }
+            }
+        }
+        // Initialize now that the heap is full.
+        Other.print();
+    } catch (OutOfMemoryError e) {
+    } catch (Exception e) {
+        System.err.println(e);
+    }
+  }
+}
diff --git a/test/617-clinit-oome/src/Other.java b/test/617-clinit-oome/src/Other.java
new file mode 100644
index 0000000..20306ee
--- /dev/null
+++ b/test/617-clinit-oome/src/Other.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class Other {
+    public static final String string1 = "ABCDEFG1";
+    public static final String string2 = "ABCDEFG2";
+    public static final String string3 = "ABCDEFG3";
+    public static final String string4 = "ABCDEFG4";
+    public static final String string5 = "ABCDEFG5";
+    public static final int int1 = 12;
+
+    public static void print() {
+        System.out.println(string2);
+    }
+}
diff --git a/test/900-hello-plugin/build b/test/900-hello-plugin/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/900-hello-plugin/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/900-hello-plugin/expected.txt b/test/900-hello-plugin/expected.txt
new file mode 100644
index 0000000..43db31c
--- /dev/null
+++ b/test/900-hello-plugin/expected.txt
@@ -0,0 +1,8 @@
+ArtPlugin_Initialize called in test 900
+Agent_OnLoad called with options "test_900"
+GetEnvHandler called in test 900
+GetEnvHandler called with version 0x900fffff
+GetEnv returned '900' environment!
+Hello, world!
+Agent_OnUnload called
+ArtPlugin_Deinitialize called in test 900
diff --git a/test/900-hello-plugin/info.txt b/test/900-hello-plugin/info.txt
new file mode 100644
index 0000000..47b15c2
--- /dev/null
+++ b/test/900-hello-plugin/info.txt
@@ -0,0 +1,2 @@
+Tests that agents and plugins are loaded.
+
diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc
new file mode 100644
index 0000000..a38cc3d
--- /dev/null
+++ b/test/900-hello-plugin/load_unload.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+#include "art_method-inl.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace art {
+
+constexpr jint TEST_900_ENV_VERSION_NUMBER = 0x900FFFFF;
+constexpr uintptr_t ENV_VALUE = 900;
+
+// Allow this library to be used as a plugin too so we can test the stack.
+static jint GetEnvHandler(JavaVMExt* vm ATTRIBUTE_UNUSED, void** new_env, jint version) {
+  printf("%s called in test 900\n", __func__);
+  if (version != TEST_900_ENV_VERSION_NUMBER) {
+    return JNI_EVERSION;
+  }
+  printf("GetEnvHandler called with version 0x%x\n", version);
+  *new_env = reinterpret_cast<void*>(ENV_VALUE);
+  return JNI_OK;
+}
+
+extern "C" bool ArtPlugin_Initialize() {
+  printf("%s called in test 900\n", __func__);
+  Runtime::Current()->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
+  return true;
+}
+
+extern "C" bool ArtPlugin_Deinitialize() {
+  printf("%s called in test 900\n", __func__);
+  return true;
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+                                               char* options,
+                                               void* reserved ATTRIBUTE_UNUSED) {
+  printf("Agent_OnLoad called with options \"%s\"\n", options);
+  uintptr_t env = 0;
+  jint res = vm->GetEnv(reinterpret_cast<void**>(&env), TEST_900_ENV_VERSION_NUMBER);
+  if (res != JNI_OK) {
+    printf("GetEnv(TEST_900_ENV_VERSION_NUMBER) returned non-zero\n");
+  }
+  printf("GetEnv returned '%" PRIdPTR "' environment!\n", env);
+  return 0;
+}
+
+extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm ATTRIBUTE_UNUSED) {
+  printf("Agent_OnUnload called\n");
+}
+
+}  // namespace art
diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run
new file mode 100755
index 0000000..bb9b415
--- /dev/null
+++ b/test/900-hello-plugin/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:libartagentd.so=test_900 \
+                   --android-runtime-option -Xplugin:libartagentd.so
diff --git a/test/900-hello-plugin/src/Main.java b/test/900-hello-plugin/src/Main.java
new file mode 100644
index 0000000..1ef6289
--- /dev/null
+++ b/test/900-hello-plugin/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+  }
+}
diff --git a/test/Android.libartagent.mk b/test/Android.libartagent.mk
new file mode 100644
index 0000000..729de3f
--- /dev/null
+++ b/test/Android.libartagent.mk
@@ -0,0 +1,101 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common_build.mk
+
+LIBARTAGENT_COMMON_SRC_FILES := \
+    900-hello-plugin/load_unload.cc
+
+# $(1): target or host
+# $(2): debug or <empty>
+define build-libartagent
+  ifneq ($(1),target)
+    ifneq ($(1),host)
+      $$(error expected target or host for argument 1, received $(1))
+    endif
+  endif
+  ifneq ($(2),debug)
+    ifneq ($(2),)
+      $$(error d or empty for argument 2, received $(2))
+    endif
+    suffix := d
+  else
+    suffix :=
+  endif
+
+  art_target_or_host := $(1)
+
+  include $(CLEAR_VARS)
+  LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+  LOCAL_MODULE := libartagent$$(suffix)
+  ifeq ($$(art_target_or_host),target)
+    LOCAL_MODULE_TAGS := tests
+  endif
+  LOCAL_SRC_FILES := $(LIBARTAGENT_COMMON_SRC_FILES)
+  LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace libnativehelper
+  LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+  LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+  LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libartagent.mk
+  ifeq ($$(art_target_or_host),target)
+    $(call set-target-local-clang-vars)
+    ifeq ($$(suffix),d)
+      $(call set-target-local-cflags-vars,debug)
+    else
+      $(call set-target-local-cflags-vars,ndebug)
+    endif
+    LOCAL_SHARED_LIBRARIES += libdl
+    LOCAL_MULTILIB := both
+    LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32)
+    LOCAL_MODULE_PATH_64 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_64)
+    LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
+    include $(BUILD_SHARED_LIBRARY)
+  else # host
+    LOCAL_CLANG := $(ART_HOST_CLANG)
+    LOCAL_CFLAGS := $(ART_HOST_CFLAGS)
+    LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
+    ifeq ($$(suffix),d)
+      LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+      LOCAL_ASFLAGS += $(ART_HOST_DEBUG_ASFLAGS)
+    else
+      LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+      LOCAL_ASFLAGS += $(ART_HOST_NON_DEBUG_ASFLAGS)
+    endif
+    LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
+    LOCAL_IS_HOST_MODULE := true
+    LOCAL_MULTILIB := both
+    include $(BUILD_HOST_SHARED_LIBRARY)
+  endif
+
+  # Clear locally used variables.
+  art_target_or_host :=
+  suffix :=
+endef
+
+ifeq ($(ART_BUILD_TARGET),true)
+  $(eval $(call build-libartagent,target,))
+  $(eval $(call build-libartagent,target,debug))
+endif
+ifeq ($(ART_BUILD_HOST),true)
+  $(eval $(call build-libartagent,host,))
+  $(eval $(call build-libartagent,host,debug))
+endif
+
+# Clear locally used variables.
+LOCAL_PATH :=
+LIBARTAGENT_COMMON_SRC_FILES :=
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index bba6f8e..05ac5f4 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -653,6 +653,14 @@
 # only once).
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
 
+# Also need libartagent.
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libartagent.so
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libartagentd.so
+ifdef TARGET_2ND_ARCH
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libartagent.so
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libartagentd.so
+endif
+
 # Also need libarttest.
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
@@ -671,6 +679,8 @@
 # specific version depending on the compiler.
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
+  $(ART_HOST_OUT_SHARED_LIBRARIES)/libartagent$(ART_HOST_SHLIB_EXTENSION) \
+  $(ART_HOST_OUT_SHARED_LIBRARIES)/libartagentd$(ART_HOST_SHLIB_EXTENSION) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
   $(ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
@@ -680,6 +690,8 @@
 
 ifneq ($(HOST_PREFER_32_BIT),true)
 ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
+  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libartagent$(ART_HOST_SHLIB_EXTENSION) \
+  $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libartagentd$(ART_HOST_SHLIB_EXTENSION) \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
   $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
@@ -1092,5 +1104,9 @@
 RUN_TYPES :=
 DEBUGGABLE_TYPES :=
 
-include $(LOCAL_PATH)/Android.libarttest.mk
-include art/test/Android.libnativebridgetest.mk
+MY_LOCAL_PATH := $(LOCAL_PATH)
+include $(MY_LOCAL_PATH)/Android.libartagent.mk
+include $(MY_LOCAL_PATH)/Android.libarttest.mk
+include $(MY_LOCAL_PATH)/Android.libnativebridgetest.mk
+MY_LOCAL_PATH :=
+LOCAL_PATH :=
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c6c9380..0ea5d52 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -21,6 +21,7 @@
 EXPERIMENTAL=""
 FALSE_BIN="/system/bin/false"
 FLAGS=""
+ANDROID_FLAGS=""
 GDB=""
 GDB_ARGS=""
 GDB_SERVER="gdbserver"
@@ -93,6 +94,11 @@
         FLAGS="${FLAGS} -Xcompiler-option $option"
         COMPILE_FLAGS="${COMPILE_FLAGS} $option"
         shift
+    elif [ "x$1" = "x--android-runtime-option" ]; then
+        shift
+        option="$1"
+        ANDROID_FLAGS="${ANDROID_FLAGS} $option"
+        shift
     elif [ "x$1" = "x--runtime-option" ]; then
         shift
         option="$1"
@@ -233,6 +239,7 @@
 done
 
 if [ "$USE_JVM" = "n" ]; then
+    FLAGS="${FLAGS} ${ANDROID_FLAGS}"
     for feature in ${EXPERIMENTAL}; do
         FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
         COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
@@ -469,12 +476,12 @@
       adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
     fi
 
-    LD_LIBRARY_PATH=
+    LD_LIBRARY_PATH=/data/art-test/$ISA
     if [ "$ANDROID_ROOT" != "/system" ]; then
       # Current default installation is dalvikvm 64bits and dex2oat 32bits,
       # so we can only use LD_LIBRARY_PATH when testing on a local
       # installation.
-      LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY
+      LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH
     fi
 
     PUBLIC_LIBS=libart.so:libartd.so