Merge "Hidden API: only log what we deny."
diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp
index 441b706..95fc274 100644
--- a/adbconnection/Android.bp
+++ b/adbconnection/Android.bp
@@ -65,6 +65,7 @@
     defaults: ["adbconnection-defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
     ],
 }
 
@@ -76,5 +77,6 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
     ],
 }
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 3daaf01..b481352 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -277,6 +277,16 @@
   $(TARGET_CORE_IMAGE_DEFAULT_32) \
   dexdump2-target
 
+# The dexanalyze test requires an image and the dexanalyze utility.
+ART_GTEST_dexanalyze_test_HOST_DEPS := \
+  $(HOST_CORE_IMAGE_DEFAULT_64) \
+  $(HOST_CORE_IMAGE_DEFAULT_32) \
+  dexanalyze-host
+ART_GTEST_dexanalyze_test_TARGET_DEPS := \
+  $(TARGET_CORE_IMAGE_DEFAULT_64) \
+  $(TARGET_CORE_IMAGE_DEFAULT_32) \
+  dexanalyze-target
+
 # The dexlayout test requires an image and the dexlayout utility.
 # TODO: rename into dexdump when migration completes
 ART_GTEST_dexlayout_test_HOST_DEPS := \
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 5884a54..be963fb 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -246,6 +246,7 @@
         "libart",
         "libprofile",
         "libdexfile",
+        "libartbase",
     ],
 
     target: {
@@ -295,6 +296,7 @@
         "libartd",
         "libprofiled",
         "libdexfiled",
+        "libartbased",
     ],
 }
 
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 0e20a65..42031f9 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1360,9 +1360,15 @@
   if (arg_this->IsNewInstance()) {
     ssa_builder_->AddUninitializedString(arg_this->AsNewInstance());
   } else {
+    // The only reason a HPhi can flow in a String.<init> is when there is an
+    // irreducible loop, which will create HPhi for all dex registers at loop entry.
     DCHECK(arg_this->IsPhi());
-    // NewInstance is not the direct input of the StringFactory call. It might
-    // be redundant but optimizing this case is not worth the effort.
+    DCHECK(graph_->HasIrreducibleLoops());
+    // Don't bother compiling a method in that situation. While we could look at all
+    // phis related to the HNewInstance, it's not worth the trouble.
+    MaybeRecordStat(compilation_stats_,
+                    MethodCompilationStat::kNotCompiledIrreducibleAndStringInit);
+    return false;
   }
 
   // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`.
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 9a26f2f..f246228 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -50,6 +50,7 @@
   kNotCompiledThrowCatchLoop,
   kNotCompiledAmbiguousArrayOp,
   kNotCompiledHugeMethod,
+  kNotCompiledIrreducibleAndStringInit,
   kNotCompiledLargeMethodNoBranches,
   kNotCompiledMalformedOpcode,
   kNotCompiledNoCodegen,
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 18548ba..e74947a 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -202,6 +202,7 @@
         "libart-dexlayout",
         "libart",
         "libdexfile",
+        "libartbase",
         "libbase",
         "liblz4",
         "libsigchain",
@@ -239,6 +240,7 @@
         "libartd-dexlayout",
         "libartd",
         "libdexfiled",
+        "libartbased",
         "libbase",
         "liblz4",
         "libsigchain",
@@ -267,12 +269,13 @@
         "-z muldefs",
     ],
     static_libs: [
-        "libprofile",
         "libart-dex2oat",
         "libart-compiler",
         "libart-dexlayout",
         "libart",
+        "libartbase",
         "libdexfile",
+        "libprofile",
         "libvixl-arm",
         "libvixl-arm64",
     ] + art_static_dependencies,
@@ -309,6 +312,7 @@
         "libartd-compiler",
         "libartd-dexlayout",
         "libartd",
+        "libartbased",
         "libprofiled",
         "libdexfiled",
         "libvixld-arm",
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index c63d6c3..2f0962c 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -35,6 +35,7 @@
     host_supported: true,
     shared_libs: [
         "libdexfile",
+        "libartbase",
         "libbase",
     ],
 }
@@ -46,6 +47,7 @@
     device_supported: false,
     static_libs: [
         "libdexfile",
+        "libartbase",
     ] + art_static_dependencies,
     target: {
         darwin: {
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index b009774..147af0c 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -39,8 +39,8 @@
         "dex2oat-pgo-defaults",
     ],
     shared_libs: [
-        "libart",
         "libdexfile",
+        "libartbase",
         "libprofile",
     ],
 
@@ -60,8 +60,8 @@
       "art_debug_defaults",
     ],
     shared_libs: [
-        "libartd",
         "libdexfiled",
+        "libartbased",
         "libprofiled",
     ],
 }
@@ -70,7 +70,6 @@
     name: "dexlayout-defaults",
     defaults: ["art_defaults"],
     host_supported: true,
-    srcs: ["dexlayout_main.cc"],
     shared_libs: [
         "libbase",
     ],
@@ -79,9 +78,11 @@
 art_cc_binary {
     name: "dexlayout",
     defaults: ["dexlayout-defaults"],
+    srcs: ["dexlayout_main.cc"],
     shared_libs: [
+        "libdexfile",
         "libprofile",
-        "libart",
+        "libartbase",
         "libart-dexlayout",
     ],
 }
@@ -92,9 +93,11 @@
         "art_debug_defaults",
         "dexlayout-defaults",
     ],
+    srcs: ["dexlayout_main.cc"],
     shared_libs: [
+        "libdexfiled",
         "libprofiled",
-        "libartd",
+        "libartbased",
         "libartd-dexlayout",
     ],
 }
@@ -117,6 +120,8 @@
     cflags: ["-Wall"],
     shared_libs: [
         "libart",
+        "libdexfile",
+        "libartbase",
         "libart-dexlayout",
         "libbase",
     ],
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 6cb141f..aa4e6d0 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -27,7 +27,6 @@
 #include "android-base/stringprintf.h"
 
 #include "base/logging.h"  // For InitLogging.
-#include "base/mutex.h"
 #include "base/stringpiece.h"
 
 #include "dexlayout.h"
@@ -37,7 +36,6 @@
 #ifdef ART_TARGET_ANDROID
 #include "pagemap/pagemap.h"
 #endif
-#include "runtime.h"
 #include "vdex_file.h"
 
 namespace art {
@@ -446,6 +444,11 @@
   PrintLetterKey();
 }
 
+NO_RETURN static void Abort(const char* msg) {
+  std::cerr << msg;
+  exit(1);
+}
+
 static int DexDiagMain(int argc, char* argv[]) {
   if (argc < 2) {
     Usage(argv[0]);
@@ -471,8 +474,7 @@
   }
 
   // Art specific set up.
-  Locks::Init();
-  InitLogging(argv, Runtime::Abort);
+  InitLogging(argv, Abort);
   MemMap::Init();
 
 #ifdef ART_TARGET_ANDROID
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 185c142..71e56d19 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -34,7 +34,6 @@
 #include "base/logging.h"  // For InitLogging.
 #include "base/mem_map.h"
 #include "profile/profile_compilation_info.h"
-#include "runtime.h"
 
 namespace art {
 
@@ -66,12 +65,17 @@
   LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'";
 }
 
+NO_RETURN static void Abort(const char* msg) {
+  LOG(ERROR) << msg;
+  exit(1);
+}
+
 /*
  * Main driver of the dexlayout utility.
  */
 int DexlayoutDriver(int argc, char** argv) {
   // Art specific set up.
-  InitLogging(argv, Runtime::Abort);
+  InitLogging(argv, Abort);
   MemMap::Init();
 
   Options options;
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
index 2703732..bd521ac 100644
--- a/dexlist/Android.bp
+++ b/dexlist/Android.bp
@@ -17,7 +17,11 @@
     host_supported: true,
     srcs: ["dexlist.cc"],
     cflags: ["-Wall", "-Werror"],
-    shared_libs: ["libdexfile", "libbase"],
+    shared_libs: [
+        "libdexfile",
+        "libartbase",
+        "libbase"
+    ],
     // TODO: fix b/72216369 and remove the need for this.
     include_dirs: [
         "art/runtime"  // dex utils.
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 33366ad..99a11cd 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -38,6 +38,7 @@
     defaults: ["dexoptanalyzer-defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
     ],
 }
 
@@ -49,6 +50,7 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
     ],
 }
 
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index 2b89497..972c8f7 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -57,6 +57,7 @@
     defaults: ["imgdiag-defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
         "libart-compiler",
     ],
 }
@@ -69,6 +70,7 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
         "libartd-compiler",
     ],
 }
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 9d49f37..3818624 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -19,6 +19,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "dex/art_dex_file_loader.cc",
         "dex/compact_dex_file.cc",
         "dex/compact_offset_table.cc",
         "dex/descriptors_names.cc",
@@ -55,25 +56,20 @@
     },
     generated_sources: ["dexfile_operator_srcs"],
     shared_libs: [
+        // For MemMap.
         "libartbase",
-        // Important note: relying on libartbase's header_lib is perfectly acceptable.
-        // However, relying on the libartbase shared library introduces further, possibly cyclic,
-	// dependencies for clients outside of ART.
         "liblog",
+        // For atrace.
+        "libcutils",
         // For common macros.
         "libbase",
         "libz",
     ],
-    header_libs: [
-        "art_libartbase_headers",
-    ],
     export_include_dirs: ["."],
     export_shared_lib_headers: [
+        "libartbase",
         "libbase",
     ],
-    export_header_lib_headers: [
-        "art_libartbase_headers",
-    ],
 }
 
 gensrcs {
@@ -115,6 +111,7 @@
         "art_gtest_defaults",
     ],
     srcs: [
+        "dex/art_dex_file_loader_test.cc",
         "dex/code_item_accessors_test.cc",
         "dex/compact_dex_file_test.cc",
         "dex/compact_offset_table_test.cc",
diff --git a/runtime/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
similarity index 100%
rename from runtime/dex/art_dex_file_loader.cc
rename to libdexfile/dex/art_dex_file_loader.cc
diff --git a/runtime/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h
similarity index 97%
rename from runtime/dex/art_dex_file_loader.h
rename to libdexfile/dex/art_dex_file_loader.h
index 7577945..a460aee 100644
--- a/runtime/dex/art_dex_file_loader.h
+++ b/libdexfile/dex/art_dex_file_loader.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_
-#define ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_
+#ifndef ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_
+#define ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_
 
 #include <cstdint>
 #include <memory>
@@ -137,4 +137,4 @@
 
 }  // namespace art
 
-#endif  // ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_
+#endif  // ART_LIBDEXFILE_DEX_ART_DEX_FILE_LOADER_H_
diff --git a/runtime/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
similarity index 100%
rename from runtime/dex/art_dex_file_loader_test.cc
rename to libdexfile/dex/art_dex_file_loader_test.cc
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
index bcb90cb..b9883f6 100644
--- a/libprofile/Android.bp
+++ b/libprofile/Android.bp
@@ -45,6 +45,7 @@
     shared_libs: [
         "libartbase",
         "libdexfile",
+        "libartbase",
 	// For atrace.
         "libcutils",
     ],
@@ -97,6 +98,7 @@
     shared_libs: [
         "libartbased",
         "libdexfiled",
+        "libartbased",
         "libziparchive",
     ],
 }
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 535acdf..77dede3 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -39,6 +39,7 @@
         "libart-compiler",
         "libart-disassembler",
         "libdexfile",
+        "libartbase",
         "libprofile",
         "libbase",
     ],
@@ -55,6 +56,7 @@
         "libartd-compiler",
         "libartd-disassembler",
         "libdexfiled",
+        "libartbased",
         "libprofiled",
         "libbase",
     ],
@@ -82,6 +84,7 @@
         "libart",
         "libdexfile",
         "libprofile",
+        "libartbase",
         "libart-compiler",
         "libart-disassembler",
         "libvixl-arm",
@@ -117,6 +120,7 @@
         "libartd",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
         "libartd-compiler",
         "libartd-disassembler",
         "libvixld-arm",
diff --git a/openjdkjvm/Android.bp b/openjdkjvm/Android.bp
index a178993..907315e 100644
--- a/openjdkjvm/Android.bp
+++ b/openjdkjvm/Android.bp
@@ -29,7 +29,10 @@
 art_cc_library {
     name: "libopenjdkjvm",
     defaults: ["libopenjdkjvm_defaults"],
-    shared_libs: ["libart"],
+    shared_libs: [
+        "libart",
+        "libartbase",
+    ],
 }
 
 art_cc_library {
@@ -38,5 +41,8 @@
         "art_debug_defaults",
         "libopenjdkjvm_defaults",
     ],
-    shared_libs: ["libartd"],
+    shared_libs: [
+        "libartd",
+        "libartbased",
+    ],
 }
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index 81b69e8..d8902d6 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -71,6 +71,7 @@
         "libart-compiler",
         "libart-dexlayout",
         "libdexfile",
+        "libartbase",
     ],
 }
 
@@ -85,5 +86,6 @@
         "libartd-compiler",
         "libartd-dexlayout",
         "libdexfiled",
+        "libartbased",
     ],
 }
diff --git a/patchoat/Android.bp b/patchoat/Android.bp
index 1e2f328..13c8f47 100644
--- a/patchoat/Android.bp
+++ b/patchoat/Android.bp
@@ -59,7 +59,6 @@
         "patchoat_test.cc",
     ],
     shared_libs: [
-        "libartd",
         "libcrypto", // For computing the digest of image file
     ],
 }
diff --git a/profman/Android.bp b/profman/Android.bp
index 3c8c72c..5aaccb0 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -42,6 +42,7 @@
         "libart",
         "libprofile",
         "libdexfile",
+        "libartbase",
     ],
 }
 
@@ -55,6 +56,7 @@
         "libartd",
         "libprofiled",
         "libdexfiled",
+        "libartbased",
     ],
 }
 
diff --git a/profman/profman.cc b/profman/profman.cc
index cd88d03..1f77239 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -33,6 +33,7 @@
 
 #include "base/dumpable.h"
 #include "base/logging.h"  // For InitLogging.
+#include "base/mem_map.h"
 #include "base/scoped_flock.h"
 #include "base/stringpiece.h"
 #include "base/time_utils.h"
@@ -49,7 +50,6 @@
 #include "dex/type_reference.h"
 #include "profile/profile_compilation_info.h"
 #include "profile_assistant.h"
-#include "runtime.h"
 
 namespace art {
 
@@ -177,6 +177,11 @@
 static constexpr char kMethodFlagStringStartup = 'S';
 static constexpr char kMethodFlagStringPostStartup = 'P';
 
+NO_RETURN static void Abort(const char* msg) {
+  LOG(ERROR) << msg;
+  exit(1);
+}
+
 // TODO(calin): This class has grown too much from its initial design. Split the functionality
 // into smaller, more contained pieces.
 class ProfMan FINAL {
@@ -202,8 +207,8 @@
     original_argc = argc;
     original_argv = argv;
 
-    Locks::Init();
-    InitLogging(argv, Runtime::Abort);
+    MemMap::Init();
+    InitLogging(argv, Abort);
 
     // Skip over the command name.
     argv++;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 3db4ee5..92607f5 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -45,7 +45,6 @@
         "compiler_filter.cc",
         "debug_print.cc",
         "debugger.cc",
-        "dex/art_dex_file_loader.cc",
         "dex/dex_file_annotations.cc",
         "dex_to_dex_decompiler.cc",
         "elf_file.cc",
@@ -195,6 +194,7 @@
         "ti/agent.cc",
         "trace.cc",
         "transaction.cc",
+        "var_handles.cc",
         "vdex_file.cc",
         "verifier/instruction_flags.cc",
         "verifier/method_verifier.cc",
@@ -463,9 +463,9 @@
         keep_symbols: true,
     },
     whole_static_libs: [
-        "libartbase",
     ],
     shared_libs: [
+        "libartbase",
         "libdexfile",
         "libprofile",
     ],
@@ -488,9 +488,9 @@
         "libart_defaults",
     ],
     whole_static_libs: [
-        "libartbased",
     ],
     shared_libs: [
+        "libartbased",
         "libdexfiled",
         "libprofiled",
     ],
@@ -544,7 +544,6 @@
         "class_loader_context_test.cc",
         "class_table_test.cc",
         "compiler_filter_test.cc",
-        "dex/art_dex_file_loader_test.cc",
         "entrypoints/math_entrypoints_test.cc",
         "entrypoints/quick/quick_trampoline_entrypoints_test.cc",
         "entrypoints_order_test.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index be9e08f..b88aa5e 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -828,9 +828,24 @@
   return true;
 }
 
+static void CreateStringInitBindings(Thread* self, ClassLinker* class_linker)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Find String.<init> -> StringFactory bindings.
+  ObjPtr<mirror::Class> string_factory_class =
+      class_linker->FindSystemClass(self, "Ljava/lang/StringFactory;");
+  CHECK(string_factory_class != nullptr);
+  ObjPtr<mirror::Class> string_class =
+      class_linker->GetClassRoot(ClassLinker::ClassRoot::kJavaLangString);
+  WellKnownClasses::InitStringInit(string_class, string_factory_class);
+  // Update the primordial thread.
+  self->InitStringEntryPoints();
+}
+
 void ClassLinker::FinishInit(Thread* self) {
   VLOG(startup) << "ClassLinker::FinishInit entering";
 
+  CreateStringInitBindings(self, this);
+
   // Let the heap know some key offsets into java.lang.ref instances
   // Note: we hard code the field indexes here rather than using FindInstanceField
   // as the types of the field can't be resolved prior to the runtime being
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 37e074d..9c2a40b 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -29,6 +29,7 @@
 #include "instrumentation.h"
 #include "interpreter/shadow_frame.h"
 #include "interpreter/unstarted_runtime.h"
+#include "jvalue-inl.h"
 #include "mirror/class.h"
 #include "mirror/object.h"
 #include "obj_ptr-inl.h"
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 7e3c3db..0a186f4 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -42,6 +42,7 @@
 #include "mirror/method_handle_impl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/var_handle.h"
 #include "oat_file.h"
 #include "oat_quick_method_header.h"
 #include "quick_exception_handler.h"
@@ -49,6 +50,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "thread-inl.h"
+#include "var_handles.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -2789,13 +2791,6 @@
     return static_cast<uintptr_t>('V');
   }
 
-  // TODO(oth): Ensure this path isn't taken for VarHandle accessors (b/65872996).
-  DCHECK_EQ(resolved_method->GetDeclaringClass(),
-            WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_MethodHandle));
-
-  Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
-      ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
-
   Handle<mirror::MethodType> method_type(
       hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
 
@@ -2835,24 +2830,43 @@
   // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in
   // consecutive order.
   RangeInstructionOperands operands(first_arg + 1, num_vregs - 1);
-  bool isExact = (jni::EncodeArtMethod(resolved_method) ==
-                  WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+  Intrinsics intrinsic = static_cast<Intrinsics>(resolved_method->GetIntrinsic());
   bool success = false;
-  if (isExact) {
-    success = MethodHandleInvokeExact(self,
+  if (resolved_method->GetDeclaringClass() == mirror::MethodHandle::StaticClass()) {
+    Handle<mirror::MethodHandle> method_handle(hs.NewHandle(
+        ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+    if (intrinsic == Intrinsics::kMethodHandleInvokeExact) {
+      success = MethodHandleInvokeExact(self,
+                                        *shadow_frame,
+                                        method_handle,
+                                        method_type,
+                                        &operands,
+                                        result);
+    } else {
+      DCHECK_EQ(static_cast<uint32_t>(intrinsic),
+                static_cast<uint32_t>(Intrinsics::kMethodHandleInvoke));
+      success = MethodHandleInvoke(self,
+                                   *shadow_frame,
+                                   method_handle,
+                                   method_type,
+                                   &operands,
+                                   result);
+    }
+  } else {
+    DCHECK_EQ(mirror::VarHandle::StaticClass(), resolved_method->GetDeclaringClass());
+    Handle<mirror::VarHandle> var_handle(hs.NewHandle(
+        ObjPtr<mirror::VarHandle>::DownCast(MakeObjPtr(receiver_handle.Get()))));
+    mirror::VarHandle::AccessMode access_mode =
+        mirror::VarHandle::GetAccessModeByIntrinsic(intrinsic);
+    success = VarHandleInvokeAccessor(self,
                                       *shadow_frame,
-                                      method_handle,
+                                      var_handle,
                                       method_type,
+                                      access_mode,
                                       &operands,
                                       result);
-  } else {
-    success = MethodHandleInvoke(self,
-                                 *shadow_frame,
-                                 method_handle,
-                                 method_type,
-                                 &operands,
-                                 result);
   }
+
   DCHECK(success || self->IsExceptionPending());
 
   // Pop transition record.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ded8cef..5a50ec5 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -24,7 +24,7 @@
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "intrinsics_enum.h"
 #include "jit/jit.h"
-#include "jvalue.h"
+#include "jvalue-inl.h"
 #include "method_handles-inl.h"
 #include "method_handles.h"
 #include "mirror/array-inl.h"
@@ -37,6 +37,7 @@
 #include "stack.h"
 #include "thread-inl.h"
 #include "transaction.h"
+#include "var_handles.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -725,38 +726,6 @@
   }
 }
 
-static bool DoVarHandleInvokeChecked(Thread* self,
-                                     Handle<mirror::VarHandle> var_handle,
-                                     Handle<mirror::MethodType> callsite_type,
-                                     mirror::VarHandle::AccessMode access_mode,
-                                     ShadowFrame& shadow_frame,
-                                     InstructionOperands* operands,
-                                     JValue* result)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  // TODO(oth): GetMethodTypeForAccessMode() allocates a MethodType()
-  // which is only required if we need to convert argument and/or
-  // return types.
-  StackHandleScope<1> hs(self);
-  Handle<mirror::MethodType> accessor_type(hs.NewHandle(
-      var_handle->GetMethodTypeForAccessMode(self, access_mode)));
-  const size_t num_vregs = accessor_type->NumberOfVRegs();
-  const int num_params = accessor_type->GetPTypes()->GetLength();
-  ShadowFrameAllocaUniquePtr accessor_frame =
-      CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
-  ShadowFrameGetter getter(shadow_frame, operands);
-  static const uint32_t kFirstDestinationReg = 0;
-  ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
-  if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
-    return false;
-  }
-  RangeInstructionOperands accessor_operands(kFirstDestinationReg,
-                                             kFirstDestinationReg + num_vregs);
-  if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
-    return false;
-  }
-  return ConvertReturnValue(callsite_type, accessor_type, result);
-}
-
 static bool DoVarHandleInvokeCommon(Thread* self,
                                     ShadowFrame& shadow_frame,
                                     const Instruction* inst,
@@ -769,21 +738,8 @@
     return false;
   }
 
-  bool is_var_args = inst->HasVarArgs();
-  const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
-  ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
-  if (receiver.IsNull()) {
-    ThrowNullPointerExceptionFromDexPC();
-    return false;
-  }
-
   StackHandleScope<2> hs(self);
-  Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
-  if (!var_handle->IsAccessModeSupported(access_mode)) {
-    ThrowUnsupportedOperationException();
-    return false;
-  }
-
+  bool is_var_args = inst->HasVarArgs();
   const uint16_t vRegH = is_var_args ? inst->VRegH_45cc() : inst->VRegH_4rcc();
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   Handle<mirror::MethodType> callsite_type(hs.NewHandle(
@@ -794,34 +750,31 @@
     return false;
   }
 
-  if (!var_handle->IsMethodTypeCompatible(access_mode, callsite_type.Get())) {
-    ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode),
-                                  callsite_type.Get());
-    return false;
-  }
-
+  const uint32_t vRegC = is_var_args ? inst->VRegC_45cc() : inst->VRegC_4rcc();
+  ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(vRegC));
+  Handle<mirror::VarHandle> var_handle(hs.NewHandle(down_cast<mirror::VarHandle*>(receiver.Ptr())));
   if (is_var_args) {
     uint32_t args[Instruction::kMaxVarArgRegs];
     inst->GetVarArgs(args, inst_data);
     VarArgsInstructionOperands all_operands(args, inst->VRegA_45cc());
     NoReceiverInstructionOperands operands(&all_operands);
-    return DoVarHandleInvokeChecked(self,
-                                    var_handle,
-                                    callsite_type,
-                                    access_mode,
-                                    shadow_frame,
-                                    &operands,
-                                    result);
+    return VarHandleInvokeAccessor(self,
+                                   shadow_frame,
+                                   var_handle,
+                                   callsite_type,
+                                   access_mode,
+                                   &operands,
+                                   result);
   } else {
     RangeInstructionOperands all_operands(inst->VRegC_4rcc(), inst->VRegA_4rcc());
     NoReceiverInstructionOperands operands(&all_operands);
-    return DoVarHandleInvokeChecked(self,
-                                    var_handle,
-                                    callsite_type,
-                                    access_mode,
-                                    shadow_frame,
-                                    &operands,
-                                    result);
+    return VarHandleInvokeAccessor(self,
+                                   shadow_frame,
+                                   var_handle,
+                                   callsite_type,
+                                   access_mode,
+                                   &operands,
+                                   result);
   }
 }
 
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 0684b46..86e69f4 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -46,8 +46,6 @@
 namespace jit {
 
 static constexpr bool kEnableOnStackReplacement = true;
-// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
-static constexpr int kJitPoolThreadPthreadPriority = 9;
 
 // Different compilation threshold constants. These can be overridden on the command line.
 static constexpr size_t kJitDefaultCompileThreshold           = 10000;  // Non-debug default.
@@ -80,6 +78,8 @@
       options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
   jit_options->profile_saver_options_ =
       options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts);
+  jit_options->thread_pool_pthread_priority_ =
+      options.GetOrDefault(RuntimeArgumentMap::JITPoolThreadPthreadPriority);
 
   if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) {
     jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold);
@@ -167,21 +167,14 @@
   cumulative_timings_.AddLogger(logger);
 }
 
-Jit::Jit() : dump_info_on_shutdown_(false),
-             cumulative_timings_("JIT timings"),
-             memory_use_("Memory used for compilation", 16),
-             lock_("JIT memory use lock"),
-             use_jit_compilation_(true),
-             hot_method_threshold_(0),
-             warm_method_threshold_(0),
-             osr_method_threshold_(0),
-             priority_thread_weight_(0),
-             invoke_transition_weight_(0) {}
+Jit::Jit(JitOptions* options) : options_(options),
+                                cumulative_timings_("JIT timings"),
+                                memory_use_("Memory used for compilation", 16),
+                                lock_("JIT memory use lock") {}
 
 Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
   DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled());
-  std::unique_ptr<Jit> jit(new Jit);
-  jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
+  std::unique_ptr<Jit> jit(new Jit(options));
   if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
     return nullptr;
   }
@@ -195,8 +188,6 @@
   if (jit->GetCodeCache() == nullptr) {
     return nullptr;
   }
-  jit->use_jit_compilation_ = options->UseJitCompilation();
-  jit->profile_saver_options_ = options->GetProfileSaverOptions();
   VLOG(jit) << "JIT created with initial_capacity="
       << PrettySize(options->GetCodeCacheInitialCapacity())
       << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
@@ -204,12 +195,6 @@
       << ", profile_saver_options=" << options->GetProfileSaverOptions();
 
 
-  jit->hot_method_threshold_ = options->GetCompileThreshold();
-  jit->warm_method_threshold_ = options->GetWarmupThreshold();
-  jit->osr_method_threshold_ = options->GetOsrThreshold();
-  jit->priority_thread_weight_ = options->GetPriorityThreadWeight();
-  jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight();
-
   jit->CreateThreadPool();
 
   // Notify native debugger about the classes already loaded before the creation of the jit.
@@ -330,7 +315,7 @@
   constexpr bool kJitPoolNeedsPeers = true;
   thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
 
-  thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
+  thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
   Start();
 }
 
@@ -360,8 +345,8 @@
 
 void Jit::StartProfileSaver(const std::string& filename,
                             const std::vector<std::string>& code_paths) {
-  if (profile_saver_options_.IsEnabled()) {
-    ProfileSaver::Start(profile_saver_options_,
+  if (options_->GetSaveProfilingInfo()) {
+    ProfileSaver::Start(options_->GetProfileSaverOptions(),
                         filename,
                         code_cache_.get(),
                         code_paths);
@@ -369,8 +354,8 @@
 }
 
 void Jit::StopProfileSaver() {
-  if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) {
-    ProfileSaver::Stop(dump_info_on_shutdown_);
+  if (options_->GetSaveProfilingInfo() && ProfileSaver::IsStarted()) {
+    ProfileSaver::Stop(options_->DumpJitInfoOnShutdown());
   }
 }
 
@@ -383,8 +368,8 @@
 }
 
 Jit::~Jit() {
-  DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted());
-  if (dump_info_on_shutdown_) {
+  DCHECK(!options_->GetSaveProfilingInfo() || !ProfileSaver::IsStarted());
+  if (options_->DumpJitInfoOnShutdown()) {
     DumpInfo(LOG_STREAM(INFO));
     Runtime::Current()->DumpDeoptimizations(LOG_STREAM(INFO));
   }
@@ -671,25 +656,25 @@
   if (IgnoreSamplesForMethod(method)) {
     return;
   }
-  if (hot_method_threshold_ == 0) {
+  if (HotMethodThreshold() == 0) {
     // Tests might request JIT on first use (compiled synchronously in the interpreter).
     return;
   }
   DCHECK(thread_pool_ != nullptr);
-  DCHECK_GT(warm_method_threshold_, 0);
-  DCHECK_GT(hot_method_threshold_, warm_method_threshold_);
-  DCHECK_GT(osr_method_threshold_, hot_method_threshold_);
-  DCHECK_GE(priority_thread_weight_, 1);
-  DCHECK_LE(priority_thread_weight_, hot_method_threshold_);
+  DCHECK_GT(WarmMethodThreshold(), 0);
+  DCHECK_GT(HotMethodThreshold(), WarmMethodThreshold());
+  DCHECK_GT(OSRMethodThreshold(), HotMethodThreshold());
+  DCHECK_GE(PriorityThreadWeight(), 1);
+  DCHECK_LE(PriorityThreadWeight(), HotMethodThreshold());
 
-  int32_t starting_count = method->GetCounter();
+  uint16_t starting_count = method->GetCounter();
   if (Jit::ShouldUsePriorityThreadWeight(self)) {
-    count *= priority_thread_weight_;
+    count *= PriorityThreadWeight();
   }
-  int32_t new_count = starting_count + count;   // int32 here to avoid wrap-around;
+  uint32_t new_count = starting_count + count;
   // Note: Native method have no "warm" state or profiling info.
-  if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) {
-    if ((new_count >= warm_method_threshold_) &&
+  if (LIKELY(!method->IsNative()) && starting_count < WarmMethodThreshold()) {
+    if ((new_count >= WarmMethodThreshold()) &&
         (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) {
       bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
       if (success) {
@@ -710,23 +695,23 @@
       }
     }
     // Avoid jumping more than one state at a time.
-    new_count = std::min(new_count, hot_method_threshold_ - 1);
-  } else if (use_jit_compilation_) {
-    if (starting_count < hot_method_threshold_) {
-      if ((new_count >= hot_method_threshold_) &&
+    new_count = std::min(new_count, static_cast<uint32_t>(HotMethodThreshold() - 1));
+  } else if (UseJitCompilation()) {
+    if (starting_count < HotMethodThreshold()) {
+      if ((new_count >= HotMethodThreshold()) &&
           !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
         DCHECK(thread_pool_ != nullptr);
         thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
       }
       // Avoid jumping more than one state at a time.
-      new_count = std::min(new_count, osr_method_threshold_ - 1);
-    } else if (starting_count < osr_method_threshold_) {
+      new_count = std::min(new_count, static_cast<uint32_t>(OSRMethodThreshold() - 1));
+    } else if (starting_count < OSRMethodThreshold()) {
       if (!with_backedges) {
         // If the samples don't contain any back edge, we don't increment the hotness.
         return;
       }
       DCHECK(!method->IsNative());  // No back edges reported for native methods.
-      if ((new_count >= osr_method_threshold_) &&  !code_cache_->IsOsrCompiled(method)) {
+      if ((new_count >= OSRMethodThreshold()) &&  !code_cache_->IsOsrCompiled(method)) {
         DCHECK(thread_pool_ != nullptr);
         thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
       }
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 4b8b891..edaf348 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -44,6 +44,110 @@
 
 static constexpr int16_t kJitCheckForOSR = -1;
 static constexpr int16_t kJitHotnessDisabled = -2;
+// At what priority to schedule jit threads. 9 is the lowest foreground priority on device.
+// See android/os/Process.java.
+static constexpr int kJitPoolThreadPthreadDefaultPriority = 9;
+
+class JitOptions {
+ public:
+  static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options);
+
+  uint16_t GetCompileThreshold() const {
+    return compile_threshold_;
+  }
+
+  uint16_t GetWarmupThreshold() const {
+    return warmup_threshold_;
+  }
+
+  uint16_t GetOsrThreshold() const {
+    return osr_threshold_;
+  }
+
+  uint16_t GetPriorityThreadWeight() const {
+    return priority_thread_weight_;
+  }
+
+  uint16_t GetInvokeTransitionWeight() const {
+    return invoke_transition_weight_;
+  }
+
+  size_t GetCodeCacheInitialCapacity() const {
+    return code_cache_initial_capacity_;
+  }
+
+  size_t GetCodeCacheMaxCapacity() const {
+    return code_cache_max_capacity_;
+  }
+
+  bool DumpJitInfoOnShutdown() const {
+    return dump_info_on_shutdown_;
+  }
+
+  const ProfileSaverOptions& GetProfileSaverOptions() const {
+    return profile_saver_options_;
+  }
+
+  bool GetSaveProfilingInfo() const {
+    return profile_saver_options_.IsEnabled();
+  }
+
+  int GetThreadPoolPthreadPriority() const {
+    return thread_pool_pthread_priority_;
+  }
+
+  bool UseJitCompilation() const {
+    return use_jit_compilation_;
+  }
+
+  void SetUseJitCompilation(bool b) {
+    use_jit_compilation_ = b;
+  }
+
+  void SetSaveProfilingInfo(bool save_profiling_info) {
+    profile_saver_options_.SetEnabled(save_profiling_info);
+  }
+
+  void SetWaitForJitNotificationsToSaveProfile(bool value) {
+    profile_saver_options_.SetWaitForJitNotificationsToSave(value);
+  }
+
+  void SetProfileAOTCode(bool value) {
+    profile_saver_options_.SetProfileAOTCode(value);
+  }
+
+  void SetJitAtFirstUse() {
+    use_jit_compilation_ = true;
+    compile_threshold_ = 0;
+  }
+
+ private:
+  bool use_jit_compilation_;
+  size_t code_cache_initial_capacity_;
+  size_t code_cache_max_capacity_;
+  uint16_t compile_threshold_;
+  uint16_t warmup_threshold_;
+  uint16_t osr_threshold_;
+  uint16_t priority_thread_weight_;
+  uint16_t invoke_transition_weight_;
+  bool dump_info_on_shutdown_;
+  int thread_pool_pthread_priority_;
+  ProfileSaverOptions profile_saver_options_;
+
+  JitOptions()
+      : use_jit_compilation_(false),
+        code_cache_initial_capacity_(0),
+        code_cache_max_capacity_(0),
+        compile_threshold_(0),
+        warmup_threshold_(0),
+        osr_threshold_(0),
+        priority_thread_weight_(0),
+        invoke_transition_weight_(0),
+        dump_info_on_shutdown_(false),
+        thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority) {}
+
+  DISALLOW_COPY_AND_ASSIGN(JitOptions);
+};
 
 class Jit {
  public:
@@ -77,29 +181,29 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  size_t OSRMethodThreshold() const {
-    return osr_method_threshold_;
+  uint16_t OSRMethodThreshold() const {
+    return options_->GetOsrThreshold();
   }
 
-  size_t HotMethodThreshold() const {
-    return hot_method_threshold_;
+  uint16_t HotMethodThreshold() const {
+    return options_->GetCompileThreshold();
   }
 
-  size_t WarmMethodThreshold() const {
-    return warm_method_threshold_;
+  uint16_t WarmMethodThreshold() const {
+    return options_->GetWarmupThreshold();
   }
 
   uint16_t PriorityThreadWeight() const {
-    return priority_thread_weight_;
+    return options_->GetPriorityThreadWeight();
   }
 
   // Returns false if we only need to save profile information and not compile methods.
   bool UseJitCompilation() const {
-    return use_jit_compilation_;
+    return options_->UseJitCompilation();
   }
 
   bool GetSaveProfilingInfo() const {
-    return profile_saver_options_.IsEnabled();
+    return options_->GetSaveProfilingInfo();
   }
 
   // Wait until there is no more pending compilation tasks.
@@ -120,12 +224,12 @@
 
   void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    AddSamples(self, caller, invoke_transition_weight_, false);
+    AddSamples(self, caller, options_->GetInvokeTransitionWeight(), false);
   }
 
   void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    AddSamples(self, callee, invoke_transition_weight_, false);
+    AddSamples(self, callee, options_->GetInvokeTransitionWeight(), false);
   }
 
   // Starts the profile saver if the config options allow profile recording.
@@ -177,7 +281,7 @@
   void Start();
 
  private:
-  Jit();
+  explicit Jit(JitOptions* options);
 
   static bool LoadCompiler(std::string* error_msg);
 
@@ -189,107 +293,22 @@
   static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool);
   static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count);
 
+  // We make this static to simplify the interaction with libart-compiler.so.
+  static bool generate_debug_info_;
+
+  const JitOptions* const options_;
+
+  std::unique_ptr<jit::JitCodeCache> code_cache_;
+  std::unique_ptr<ThreadPool> thread_pool_;
+
   // Performance monitoring.
-  bool dump_info_on_shutdown_;
   CumulativeLogger cumulative_timings_;
   Histogram<uint64_t> memory_use_ GUARDED_BY(lock_);
   Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 
-  std::unique_ptr<jit::JitCodeCache> code_cache_;
-
-  bool use_jit_compilation_;
-  ProfileSaverOptions profile_saver_options_;
-  static bool generate_debug_info_;
-  uint16_t hot_method_threshold_;
-  uint16_t warm_method_threshold_;
-  uint16_t osr_method_threshold_;
-  uint16_t priority_thread_weight_;
-  uint16_t invoke_transition_weight_;
-  std::unique_ptr<ThreadPool> thread_pool_;
-
   DISALLOW_COPY_AND_ASSIGN(Jit);
 };
 
-class JitOptions {
- public:
-  static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options);
-  size_t GetCompileThreshold() const {
-    return compile_threshold_;
-  }
-  size_t GetWarmupThreshold() const {
-    return warmup_threshold_;
-  }
-  size_t GetOsrThreshold() const {
-    return osr_threshold_;
-  }
-  uint16_t GetPriorityThreadWeight() const {
-    return priority_thread_weight_;
-  }
-  size_t GetInvokeTransitionWeight() const {
-    return invoke_transition_weight_;
-  }
-  size_t GetCodeCacheInitialCapacity() const {
-    return code_cache_initial_capacity_;
-  }
-  size_t GetCodeCacheMaxCapacity() const {
-    return code_cache_max_capacity_;
-  }
-  bool DumpJitInfoOnShutdown() const {
-    return dump_info_on_shutdown_;
-  }
-  const ProfileSaverOptions& GetProfileSaverOptions() const {
-    return profile_saver_options_;
-  }
-  bool GetSaveProfilingInfo() const {
-    return profile_saver_options_.IsEnabled();
-  }
-  bool UseJitCompilation() const {
-    return use_jit_compilation_;
-  }
-  void SetUseJitCompilation(bool b) {
-    use_jit_compilation_ = b;
-  }
-  void SetSaveProfilingInfo(bool save_profiling_info) {
-    profile_saver_options_.SetEnabled(save_profiling_info);
-  }
-  void SetWaitForJitNotificationsToSaveProfile(bool value) {
-    profile_saver_options_.SetWaitForJitNotificationsToSave(value);
-  }
-  void SetProfileAOTCode(bool value) {
-    profile_saver_options_.SetProfileAOTCode(value);
-  }
-
-  void SetJitAtFirstUse() {
-    use_jit_compilation_ = true;
-    compile_threshold_ = 0;
-  }
-
- private:
-  bool use_jit_compilation_;
-  size_t code_cache_initial_capacity_;
-  size_t code_cache_max_capacity_;
-  size_t compile_threshold_;
-  size_t warmup_threshold_;
-  size_t osr_threshold_;
-  uint16_t priority_thread_weight_;
-  size_t invoke_transition_weight_;
-  bool dump_info_on_shutdown_;
-  ProfileSaverOptions profile_saver_options_;
-
-  JitOptions()
-      : use_jit_compilation_(false),
-        code_cache_initial_capacity_(0),
-        code_cache_max_capacity_(0),
-        compile_threshold_(0),
-        warmup_threshold_(0),
-        osr_threshold_(0),
-        priority_thread_weight_(0),
-        invoke_transition_weight_(0),
-        dump_info_on_shutdown_(false) {}
-
-  DISALLOW_COPY_AND_ASSIGN(JitOptions);
-};
-
 // Helper class to stop the JIT for a given scope. This will wait for the JIT to quiesce.
 class ScopedJitSuspend {
  public:
diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h
index 25e34b2..5bd4f17 100644
--- a/runtime/jvalue-inl.h
+++ b/runtime/jvalue-inl.h
@@ -19,7 +19,7 @@
 
 #include "jvalue.h"
 
-#include "obj_ptr.h"
+#include "obj_ptr-inl.h"
 
 namespace art {
 
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index 266abcf..b42d995 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -33,7 +33,7 @@
   // We default initialize JValue instances to all-zeros.
   JValue() : j(0) {}
 
-  template<typename T> static JValue FromPrimitive(T v);
+  template<typename T> ALWAYS_INLINE static JValue FromPrimitive(T v);
 
   int8_t GetB() const { return b; }
   void SetB(int8_t new_b) {
@@ -62,6 +62,7 @@
   mirror::Object* GetL() const REQUIRES_SHARED(Locks::mutator_lock_) {
     return l;
   }
+  ALWAYS_INLINE
   void SetL(ObjPtr<mirror::Object> new_l) REQUIRES_SHARED(Locks::mutator_lock_);
 
   int16_t GetS() const { return s; }
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index 41c8384..00a8c00 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -22,7 +22,7 @@
 #include "common_throws.h"
 #include "dex/dex_instruction.h"
 #include "interpreter/interpreter_common.h"
-#include "jvalue.h"
+#include "jvalue-inl.h"
 #include "mirror/class.h"
 #include "mirror/method_type.h"
 #include "mirror/object.h"
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 64ab789..1d45aae 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -20,7 +20,6 @@
 
 #include "common_dex_operations.h"
 #include "jvalue-inl.h"
-#include "jvalue.h"
 #include "mirror/emulated_stack_frame.h"
 #include "mirror/method_handle_impl-inl.h"
 #include "mirror/method_type.h"
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index b309f59..d31e06c 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -1425,21 +1425,24 @@
   return GetField32(AccessModesBitMaskOffset());
 }
 
-bool VarHandle::IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type) {
-  StackHandleScope<3> hs(Thread::Current());
-  Handle<Class> mt_rtype(hs.NewHandle(method_type->GetRType()));
-  Handle<VarHandle> vh(hs.NewHandle(this));
-  Handle<Class> var_type(hs.NewHandle(vh->GetVarType()));
+VarHandle::MatchKind VarHandle::GetMethodTypeMatchForAccessMode(AccessMode access_mode,
+                                                                MethodType* method_type) {
+  MatchKind match = MatchKind::kExact;
+
+  ObjPtr<VarHandle> vh = this;
+  ObjPtr<Class> var_type = vh->GetVarType();
+  ObjPtr<Class> mt_rtype = method_type->GetRType();
   AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
 
-  // Check return type first.
-  if (mt_rtype->GetPrimitiveType() == Primitive::Type::kPrimVoid) {
-    // The result of the operation will be discarded. The return type
-    // of the VarHandle is immaterial.
-  } else {
-    ObjPtr<Class> vh_rtype(GetReturnType(access_mode_template, var_type.Get()));
-    if (!IsReturnTypeConvertible(vh_rtype, mt_rtype.Get())) {
-      return false;
+  // Check return type first. If the return type of the method
+  // of the VarHandle is immaterial.
+  if (mt_rtype->GetPrimitiveType() != Primitive::Type::kPrimVoid) {
+    ObjPtr<Class> vh_rtype = GetReturnType(access_mode_template, var_type.Ptr());
+    if (vh_rtype != mt_rtype) {
+      if (!IsReturnTypeConvertible(vh_rtype, mt_rtype)) {
+        return MatchKind::kNone;
+      }
+      match = MatchKind::kWithConversions;
     }
   }
 
@@ -1447,21 +1450,25 @@
   ObjPtr<Class> vh_ptypes[VarHandle::kMaxAccessorParameters];
   const int32_t vh_ptypes_count = BuildParameterArray(vh_ptypes,
                                                       access_mode_template,
-                                                      var_type.Get(),
+                                                      var_type,
                                                       GetCoordinateType0(),
                                                       GetCoordinateType1());
   if (vh_ptypes_count != method_type->GetPTypes()->GetLength()) {
-    return false;
+    return MatchKind::kNone;
   }
 
   // Check the parameter types are compatible.
   ObjPtr<ObjectArray<Class>> mt_ptypes = method_type->GetPTypes();
   for (int32_t i = 0; i < vh_ptypes_count; ++i) {
-    if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
-      return false;
+    if (mt_ptypes->Get(i) == vh_ptypes[i]) {
+      continue;
     }
+    if (!IsParameterTypeConvertible(mt_ptypes->Get(i), vh_ptypes[i])) {
+      return MatchKind::kNone;
+    }
+    match = MatchKind::kWithConversions;
   }
-  return true;
+  return match;
 }
 
 bool VarHandle::IsInvokerMethodTypeCompatible(AccessMode access_mode,
@@ -1508,7 +1515,7 @@
 MethodType* VarHandle::GetMethodTypeForAccessMode(Thread* self,
                                                   ObjPtr<VarHandle> var_handle,
                                                   AccessMode access_mode) {
-  // This is a static as the var_handle might be moved by the GC during it's execution.
+  // This is a static method as the var_handle might be moved by the GC during it's execution.
   AccessModeTemplate access_mode_template = GetAccessModeTemplate(access_mode);
 
   StackHandleScope<3> hs(self);
@@ -1540,7 +1547,7 @@
 
 bool VarHandle::Access(AccessMode access_mode,
                        ShadowFrame* shadow_frame,
-                       InstructionOperands* operands,
+                       const InstructionOperands* const operands,
                        JValue* result) {
   Class* klass = GetClass();
   if (klass == FieldVarHandle::StaticClass()) {
@@ -1671,7 +1678,7 @@
 
 bool FieldVarHandle::Access(AccessMode access_mode,
                             ShadowFrame* shadow_frame,
-                            InstructionOperands* operands,
+                            const InstructionOperands* const operands,
                             JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
   ArtField* field = GetField();
@@ -1743,7 +1750,7 @@
 
 bool ArrayElementVarHandle::Access(AccessMode access_mode,
                                    ShadowFrame* shadow_frame,
-                                   InstructionOperands* operands,
+                                   const InstructionOperands* const operands,
                                    JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
@@ -1856,7 +1863,7 @@
 
 bool ByteArrayViewVarHandle::Access(AccessMode access_mode,
                                     ShadowFrame* shadow_frame,
-                                    InstructionOperands* operands,
+                                    const InstructionOperands* const operands,
                                     JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
@@ -1965,7 +1972,7 @@
 
 bool ByteBufferViewVarHandle::Access(AccessMode access_mode,
                                      ShadowFrame* shadow_frame,
-                                     InstructionOperands* operands,
+                                     const InstructionOperands* const operands,
                                      JValue* result) {
   ShadowFrameGetter getter(*shadow_frame, operands);
 
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index d46d900..eb3704e 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -99,14 +99,16 @@
     return (GetAccessModesBitMask() & (1u << static_cast<uint32_t>(accessMode))) != 0;
   }
 
-  // Returns true if the MethodType specified is compatible with the
-  // method type associated with the specified AccessMode. The
-  // supplied MethodType is assumed to be from the point of invocation
-  // so it is valid for the supplied MethodType to have a void return
-  // value when the return value for the AccessMode is non-void. This
-  // corresponds to the result of the accessor being discarded.
-  bool IsMethodTypeCompatible(AccessMode access_mode, MethodType* method_type)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  enum MatchKind : uint8_t {
+    kNone,
+    kWithConversions,
+    kExact
+  };
+
+  // Returns match information on the compatability between the exact method type for
+  // 'access_mode' and the provided 'method_type'.
+  MatchKind GetMethodTypeMatchForAccessMode(AccessMode access_mode, MethodType* method_type)
+        REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if the MethodType specified is compatible with the
   // specified access_mode if the first parameter of method_type is
@@ -124,7 +126,7 @@
 
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -192,7 +194,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -225,7 +227,7 @@
  public:
     bool Access(AccessMode access_mode,
                 ShadowFrame* shadow_frame,
-                InstructionOperands* operands,
+                const InstructionOperands* const operands,
                 JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -248,7 +250,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -281,7 +283,7 @@
  public:
   bool Access(AccessMode access_mode,
               ShadowFrame* shadow_frame,
-              InstructionOperands* operands,
+              const InstructionOperands* const operands,
               JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index d9fa07f..005aba3 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -246,6 +246,47 @@
   return MethodType::Create(self, rtype, ptypes);
 }
 
+static bool AccessModeMatch(VarHandle* vh,
+                            VarHandle::AccessMode access_mode,
+                            MethodType* method_type,
+                            VarHandle::MatchKind expected_match)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return vh->GetMethodTypeMatchForAccessMode(access_mode, method_type) == expected_match;
+}
+
+template <typename VH>
+static bool AccessModeExactMatch(Handle<VH> vh,
+                                 VarHandle::AccessMode access_mode,
+                                 const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kExact);
+}
+
+template <typename VH>
+static bool AccessModeWithConversionsMatch(Handle<VH> vh,
+                                          VarHandle::AccessMode access_mode,
+                                          const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kWithConversions);
+}
+
+template <typename VH>
+static bool AccessModeNoMatch(Handle<VH> vh,
+                              VarHandle::AccessMode access_mode,
+                              const char* descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return AccessModeMatch(vh.Get(),
+                         access_mode,
+                         MethodTypeOf(descriptor),
+                         VarHandle::MatchKind::kNone);
+}
+
 TEST_F(VarHandleTest, InstanceFieldVarHandle) {
   Thread * const self = Thread::Current();
   ScopedObjectAccess soa(self);
@@ -296,47 +337,53 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;)D"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;S)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(Ljava/lang/Integer;II)I")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Z"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)Ljava/lang/Boolean;"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;IB)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;II)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIII)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;II)V"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;II)J"));
+    EXPECT_TRUE(AccessModeWithConversionsMatch(fvh, access_mode, "(Ljava/lang/Integer;BS)F"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIII)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/lang/Integer;I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(Ljava/lang/Integer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Ljava/lang/Integer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S"));
   }
 
   // Check synthesized method types match expected forms.
@@ -430,48 +477,47 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "()V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(F)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(F)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(II)Ljava/lang/String;")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("()Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "()Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(ID)I")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)S")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(IIJ)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(II)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(ID)I"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)S"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(IIJ)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)I")));
-    EXPECT_TRUE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)V")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(I)Z")));
-    EXPECT_FALSE(fvh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)I"));
+    EXPECT_TRUE(AccessModeExactMatch(fvh, access_mode, "(I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(fvh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -594,50 +640,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;Ljava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;Ljava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("([Ljava/lang/String;III)I")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;III)I"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;II)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([Ljava/lang/String;ILjava/lang/String;)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String;"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([Ljava/lang/String;ILjava/lang/String;)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -747,50 +789,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BI)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("([BICC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("([BIII)I")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BI)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIII)I"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BI)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BICC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BII)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BICC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BII)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)C")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("([BIC)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)C"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "([BIC)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "([BIC)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
@@ -900,50 +938,46 @@
   // Check compatibility - "Get" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;D)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)Z")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;D)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)Z"));
   }
 
   // Check compatibility - "Set" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kSet;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndSet" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndSet;
-    EXPECT_TRUE(
-        vh->IsMethodTypeCompatible(
-            access_mode,
-            MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode,
-                                             MethodTypeOf("(Ljava/nio/ByteBuffer;IDI)D")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;I)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Z)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDI)D"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;I)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Z)V"));
   }
 
   // Check compatibility - "CompareAndExchange" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kCompareAndExchange;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;IDD)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;II)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(III)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;IDD)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;II)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(III)V"));
   }
 
   // Check compatibility - "GetAndUpdate" pattern
   {
     const VarHandle::AccessMode access_mode = VarHandle::AccessMode::kGetAndAdd;
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)D")));
-    EXPECT_TRUE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)V")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(Ljava/nio/ByteBuffer;ID)Z")));
-    EXPECT_FALSE(vh->IsMethodTypeCompatible(access_mode, MethodTypeOf("(II)V")));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)D"));
+    EXPECT_TRUE(AccessModeExactMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)V"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(Ljava/nio/ByteBuffer;ID)Z"));
+    EXPECT_TRUE(AccessModeNoMatch(vh, access_mode, "(II)V"));
   }
 
   // Check synthesized method types match expected forms.
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index e0afbee..2559984 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -27,6 +27,7 @@
 #include "dex/dex_file-inl.h"
 #include "dex/dex_file_annotations.h"
 #include "jni/jni_internal.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/field-inl.h"
 #include "native_util.h"
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 3aa481a..7383d47 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -194,6 +194,9 @@
       .Define("-Xjittransitionweight:_")
           .WithType<unsigned int>()
           .IntoKey(M::JITInvokeTransitionWeight)
+      .Define("-Xjitpthreadpriority:_")
+          .WithType<int>()
+          .IntoKey(M::JITPoolThreadPthreadPriority)
       .Define("-Xjitsaveprofilinginfo")
           .WithType<ProfileSaverOptions>()
           .AppendValues()
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index dfa4b3d..66eba1e 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -25,6 +25,7 @@
 #include "indirect_reference_table-inl.h"
 #include "jni/java_vm_ext.h"
 #include "jni/jni_internal.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/executable.h"
 #include "mirror/object_array-inl.h"
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 427385d..e647423 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -77,6 +77,7 @@
 RUNTIME_OPTIONS_KEY (unsigned int,        JITOsrThreshold)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITPriorityThreadWeight)
 RUNTIME_OPTIONS_KEY (unsigned int,        JITInvokeTransitionWeight)
+RUNTIME_OPTIONS_KEY (int,                 JITPoolThreadPthreadPriority,   jit::kJitPoolThreadPthreadDefaultPriority)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheInitialCapacity,    jit::JitCodeCache::kInitialCapacity)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           JITCodeCacheMaxCapacity,        jit::JitCodeCache::kMaxCapacity)
 RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc
new file mode 100644
index 0000000..e6730c6
--- /dev/null
+++ b/runtime/var_handles.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "var_handles.h"
+
+#include "common_throws.h"
+#include "dex/dex_instruction.h"
+#include "handle.h"
+#include "method_handles-inl.h"
+#include "mirror/method_type.h"
+#include "mirror/var_handle.h"
+
+namespace art {
+
+namespace {
+
+bool VarHandleInvokeAccessorWithConversions(Thread* self,
+                                            ShadowFrame& shadow_frame,
+                                            Handle<mirror::VarHandle> var_handle,
+                                            Handle<mirror::MethodType> callsite_type,
+                                            const mirror::VarHandle::AccessMode access_mode,
+                                            const InstructionOperands* const operands,
+                                            JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::MethodType> accessor_type(hs.NewHandle(
+      var_handle->GetMethodTypeForAccessMode(self, access_mode)));
+  const size_t num_vregs = accessor_type->NumberOfVRegs();
+  const int num_params = accessor_type->GetPTypes()->GetLength();
+  ShadowFrameAllocaUniquePtr accessor_frame =
+      CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+  ShadowFrameGetter getter(shadow_frame, operands);
+  static const uint32_t kFirstDestinationReg = 0;
+  ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
+  if (!PerformConversions(self, callsite_type, accessor_type, &getter, &setter, num_params)) {
+    return false;
+  }
+  RangeInstructionOperands accessor_operands(kFirstDestinationReg,
+                                             kFirstDestinationReg + num_vregs);
+  if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
+    return false;
+  }
+  return ConvertReturnValue(callsite_type, accessor_type, result);
+}
+
+}  // namespace
+
+bool VarHandleInvokeAccessor(Thread* self,
+                             ShadowFrame& shadow_frame,
+                             Handle<mirror::VarHandle> var_handle,
+                             Handle<mirror::MethodType> callsite_type,
+                             const mirror::VarHandle::AccessMode access_mode,
+                             const InstructionOperands* const operands,
+                             JValue* result) {
+  if (var_handle.IsNull()) {
+    ThrowNullPointerExceptionFromDexPC();
+    return false;
+  }
+
+  if (!var_handle->IsAccessModeSupported(access_mode)) {
+    ThrowUnsupportedOperationException();
+    return false;
+  }
+
+  mirror::VarHandle::MatchKind match_kind =
+      var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type.Get());
+  if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) {
+    return var_handle->Access(access_mode, &shadow_frame, operands, result);
+  } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) {
+    return VarHandleInvokeAccessorWithConversions(self,
+                                                  shadow_frame,
+                                                  var_handle,
+                                                  callsite_type,
+                                                  access_mode,
+                                                  operands,
+                                                  result);
+  } else {
+    DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone);
+    ThrowWrongMethodTypeException(var_handle->GetMethodTypeForAccessMode(self, access_mode),
+                                  callsite_type.Get());
+    return false;
+  }
+}
+
+}  // namespace art
diff --git a/runtime/var_handles.h b/runtime/var_handles.h
new file mode 100644
index 0000000..2ff8405
--- /dev/null
+++ b/runtime/var_handles.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_VAR_HANDLES_H_
+#define ART_RUNTIME_VAR_HANDLES_H_
+
+#include "mirror/var_handle.h"
+
+namespace art {
+
+bool VarHandleInvokeAccessor(Thread* self,
+                             ShadowFrame& shadow_frame,
+                             Handle<mirror::VarHandle> var_handle,
+                             Handle<mirror::MethodType> callsite_type,
+                             const mirror::VarHandle::AccessMode access_mode,
+                             const InstructionOperands* const operands,
+                             JValue* result)
+    REQUIRES_SHARED(Locks::mutator_lock_);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_VAR_HANDLES_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 92ee98a..91cec23 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4210,8 +4210,6 @@
     expected_return_descriptor = mirror::MethodHandle::GetReturnTypeDescriptor(method_name);
   } else if (klass == mirror::VarHandle::StaticClass()) {
     expected_return_descriptor = mirror::VarHandle::GetReturnTypeDescriptor(method_name);
-    // TODO: add compiler support for VarHandle accessor methods (b/71781600)
-    Fail(VERIFY_ERROR_FORCE_INTERPRETER);
   } else {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD)
         << "Signature polymorphic method in unsuppported class: " << klass->PrettyDescriptor();
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 4843061..f7cdf39 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -23,6 +23,8 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
+#include "base/enums.h"
+#include "class_linker.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "hidden_api.h"
 #include "jni/jni_internal.h"
@@ -30,6 +32,7 @@
 #include "mirror/throwable.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "obj_ptr-inl.h"
+#include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
 
@@ -231,19 +234,28 @@
   V(java_lang_String_init_StringBuilder, "(Ljava/lang/StringBuilder;)V", newStringFromStringBuilder, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;", NewStringFromStringBuilder) \
 
 #define STATIC_STRING_INIT(init_runtime_name, init_signature, new_runtime_name, ...) \
-    static ArtMethod* init_runtime_name; \
-    static ArtMethod* new_runtime_name;
+    static ArtMethod* init_runtime_name = nullptr; \
+    static ArtMethod* new_runtime_name = nullptr;
     STRING_INIT_LIST(STATIC_STRING_INIT)
 #undef STATIC_STRING_INIT
 
-void WellKnownClasses::InitStringInit(JNIEnv* env) {
-  ScopedObjectAccess soa(Thread::Current());
-  #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name,             \
-                           new_java_name, new_signature, ...)                               \
-      init_runtime_name = jni::DecodeArtMethod(                                             \
-          CacheMethod(env, java_lang_String, false, "<init>", init_signature));             \
-      new_runtime_name = jni::DecodeArtMethod(                                              \
-          CacheMethod(env, java_lang_StringFactory, true, new_java_name, new_signature));
+void WellKnownClasses::InitStringInit(ObjPtr<mirror::Class> string_class,
+                                      ObjPtr<mirror::Class> string_builder_class) {
+  PointerSize p_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+  auto find_method = [p_size](ObjPtr<mirror::Class> klass,
+                              const char* name,
+                              const char* sig,
+                              bool expext_static) REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtMethod* ret = klass->FindClassMethod(name, sig, p_size);
+    CHECK(ret != nullptr);
+    CHECK_EQ(expext_static, ret->IsStatic());
+    return ret;
+  };
+
+  #define LOAD_STRING_INIT(init_runtime_name, init_signature, new_runtime_name,                  \
+                           new_java_name, new_signature, ...)                                    \
+      init_runtime_name = find_method(string_class, "<init>", init_signature, false);            \
+      new_runtime_name = find_method(string_builder_class, new_java_name, new_signature, true);
       STRING_INIT_LIST(LOAD_STRING_INIT)
   #undef LOAD_STRING_INIT
 }
@@ -252,6 +264,7 @@
   QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints;
   #define SET_ENTRY_POINT(init_runtime_name, init_signature, new_runtime_name,              \
                           new_java_name, new_signature, entry_point_name)                   \
+      DCHECK(!Runtime::Current()->IsStarted() || (new_runtime_name) != nullptr);            \
       qpoints->p ## entry_point_name = reinterpret_cast<void(*)()>(new_runtime_name);
       STRING_INIT_LIST(SET_ENTRY_POINT)
   #undef SET_ENTRY_POINT
@@ -260,7 +273,9 @@
 ArtMethod* WellKnownClasses::StringInitToStringFactory(ArtMethod* string_init) {
   #define TO_STRING_FACTORY(init_runtime_name, init_signature, new_runtime_name,            \
                             new_java_name, new_signature, entry_point_name)                 \
+      DCHECK((init_runtime_name) != nullptr);                                               \
       if (string_init == (init_runtime_name)) {                                             \
+        DCHECK((new_runtime_name) != nullptr);                                              \
         return (new_runtime_name);                                                          \
       }
       STRING_INIT_LIST(TO_STRING_FACTORY)
@@ -410,9 +425,6 @@
   java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer");
   java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long");
   java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short");
-
-  InitStringInit(env);
-  Thread::Current()->InitStringEntryPoints();
 }
 
 void WellKnownClasses::LateInit(JNIEnv* env) {
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 25c07b2..c06e4a7 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -40,6 +40,9 @@
 
   static void Clear();
 
+  static void InitStringInit(ObjPtr<mirror::Class> string_class,
+                             ObjPtr<mirror::Class> string_builder_class)
+      REQUIRES_SHARED(Locks::mutator_lock_);
   static ArtMethod* StringInitToStringFactory(ArtMethod* method);
   static uint32_t StringInitToEntryPoint(ArtMethod* method);
 
@@ -168,9 +171,6 @@
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset;
   static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type;
-
- private:
-  static void InitStringInit(JNIEnv* env);
 };
 
 }  // namespace art
diff --git a/simulator/Android.bp b/simulator/Android.bp
index 74b5a90..8690426 100644
--- a/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -44,6 +44,7 @@
     defaults: ["libart_simulator_defaults"],
     shared_libs: [
         "libart",
+        "libartbase",
         "libvixl-arm64",
     ],
 }
@@ -56,6 +57,7 @@
     ],
     shared_libs: [
         "libartd",
+        "libartbased",
         "libvixld-arm64",
     ],
 }
@@ -80,6 +82,7 @@
     name: "libart-simulator-container",
     defaults: ["libart_simulator_container_defaults"],
     shared_libs: [
+        "libartbase",
         "libart",
     ],
 }
@@ -91,6 +94,7 @@
         "libart_simulator_container_defaults",
     ],
     shared_libs: [
+        "libartbased",
         "libartd",
     ],
 }
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index adafb78..8898c48 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -133,17 +133,8 @@
 
 .end method
 
-# Test that the compiler does not assume that the first argument of String.<init>
-# is a NewInstance by inserting an irreducible loop between them (b/26676472).
-
-# We verify the type of the input instruction (Phi) in debuggable mode, because
-# it is eliminated by later stages of SsaBuilder otherwise.
-
-## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance1(byte[], boolean) register (after)
-## CHECK-DAG:                   InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}}
-## CHECK-DAG:     <<Phi>>       Phi
-
-.method public static thisNotNewInstance1([BZ)Ljava/lang/String;
+# Test #1 for irreducible loops and String.<init>.
+.method public static irreducibleLoopAndStringInit1([BZ)Ljava/lang/String;
    .registers 5
 
    new-instance v0, Ljava/lang/String;
@@ -164,11 +155,8 @@
 
 .end method
 
-## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance2(byte[], boolean) register (after)
-## CHECK-DAG:                   InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}}
-## CHECK-DAG:     <<Phi>>       Phi
-
-.method public static thisNotNewInstance2([BZ)Ljava/lang/String;
+# Test #2 for irreducible loops and String.<init>.
+.method public static irreducibleLoopAndStringInit2([BZ)Ljava/lang/String;
    .registers 5
 
    new-instance v0, Ljava/lang/String;
@@ -188,3 +176,26 @@
    return-object v0
 
 .end method
+
+# Test #3 for irreducible loops and String.<init> alias.
+.method public static irreducibleLoopAndStringInit3([BZ)Ljava/lang/String;
+   .registers 5
+
+   new-instance v0, Ljava/lang/String;
+   move-object v2, v0
+
+   # Irreducible loop
+   if-eqz p1, :loop_entry
+   :loop_header
+   const v1, 0x1
+   xor-int p1, p1, v1
+   :loop_entry
+   if-eqz p1, :string_init
+   goto :loop_header
+
+   :string_init
+   const-string v1, "UTF8"
+   invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
+   return-object v2
+
+.end method
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index 78cb37a..d38b7f4 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -65,14 +65,21 @@
     }
 
     {
-      Method m = c.getMethod("thisNotNewInstance1", byte[].class, boolean.class);
+      Method m = c.getMethod("irreducibleLoopAndStringInit1", byte[].class, boolean.class);
       String result = (String) m.invoke(null, new Object[] { testData, true });
       assertEqual(testString, result);
       result = (String) m.invoke(null, new Object[] { testData, false });
       assertEqual(testString, result);
     }
     {
-      Method m = c.getMethod("thisNotNewInstance2", byte[].class, boolean.class);
+      Method m = c.getMethod("irreducibleLoopAndStringInit2", byte[].class, boolean.class);
+      String result = (String) m.invoke(null, new Object[] { testData, true });
+      assertEqual(testString, result);
+      result = (String) m.invoke(null, new Object[] { testData, false });
+      assertEqual(testString, result);
+    }
+    {
+      Method m = c.getMethod("irreducibleLoopAndStringInit3", byte[].class, boolean.class);
       String result = (String) m.invoke(null, new Object[] { testData, true });
       assertEqual(testString, result);
       result = (String) m.invoke(null, new Object[] { testData, false });
diff --git a/test/Android.bp b/test/Android.bp
index 84f2e22..7909bf8 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -64,6 +64,7 @@
         "libart-gtest",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
 
         "libbase",
         "libicuuc",
@@ -117,6 +118,7 @@
         "libartd-compiler",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
     ],
     static_libs: [
         "libgtest",
@@ -156,6 +158,7 @@
         "libartd-compiler",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
         "libbase",
         "libbacktrace",
     ],
@@ -187,6 +190,7 @@
         "libart",
         "libdexfile",
         "libprofile",
+        "libartbase",
     ],
 }
 
@@ -201,6 +205,7 @@
         "libartd",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
     ],
 }
 
@@ -322,6 +327,7 @@
         "libart",
         "libdexfile",
         "libprofile",
+        "libartbase",
     ],
 }
 
@@ -335,6 +341,7 @@
         "libartd",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
     ],
 }
 
@@ -496,6 +503,7 @@
         "libart",
         "libdexfile",
         "libprofile",
+        "libartbase",
     ],
 }
 
@@ -509,6 +517,7 @@
         "libartd",
         "libdexfiled",
         "libprofiled",
+        "libartbased",
     ],
 }
 
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index fad8011..1ba433e 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -861,9 +861,9 @@
     # System libraries needed by libarttestd.so
     PUBLIC_LIBS=libc++.so:libbacktrace.so:libbase.so:libnativehelper.so
     if [ "$TEST_IS_NDEBUG" = "y" ]; then
-      PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so:libprofile.so
+      PUBLIC_LIBS=$PUBLIC_LIBS:libart.so:libdexfile.so:libprofile.so:libartbase.so
     else
-      PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so:libprofiled.so
+      PUBLIC_LIBS=$PUBLIC_LIBS:libartd.so:libdexfiled.so:libprofiled.so:libartbased.so
     fi
 
     # Create a script with the command. The command can get longer than the longest
diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp
index 39e57bd..23cc917 100644
--- a/tools/cpp-define-generator/Android.bp
+++ b/tools/cpp-define-generator/Android.bp
@@ -31,6 +31,7 @@
     include_dirs: [
         "art/libartbase",
         "art/libdexfile",
+        "art/libartbase",
         "art/runtime",
     ],
     srcs: ["main.cc"],
diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp
index 2754e64..a229d73 100644
--- a/tools/dexanalyze/Android.bp
+++ b/tools/dexanalyze/Android.bp
@@ -37,6 +37,7 @@
     defaults: ["dexanalyze-defaults"],
     shared_libs: [
         "libdexfile",
+        "libartbase",
         "libbase",
     ],
 }
diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc
index c4aebc6..58b1fc7 100644
--- a/tools/dexanalyze/dexanalyze.cc
+++ b/tools/dexanalyze/dexanalyze.cc
@@ -63,6 +63,8 @@
           run_all_experiments_ = true;
         } else if (arg == "-count-indices") {
           exp_count_indices_ = true;
+        } else if (arg == "-analyze-strings") {
+          exp_analyze_strings_ = true;
         } else if (arg == "-d") {
           dump_per_input_dex_ = true;
         } else if (!arg.empty() && arg[0] == '-') {
@@ -82,6 +84,7 @@
     bool run_dex_file_verifier_ = true;
     bool dump_per_input_dex_ = false;
     bool exp_count_indices_ = false;
+    bool exp_analyze_strings_ = false;
     bool run_all_experiments_ = false;
     std::vector<std::string> filenames_;
   };
@@ -92,25 +95,30 @@
       if (options->run_all_experiments_ || options->exp_count_indices_) {
         experiments_.emplace_back(new CountDexIndices);
       }
+      if (options->run_all_experiments_ || options->exp_analyze_strings_) {
+        experiments_.emplace_back(new AnalyzeStrings);
+      }
     }
 
     bool ProcessDexFile(const DexFile& dex_file) {
       for (std::unique_ptr<Experiment>& experiment : experiments_) {
         experiment->ProcessDexFile(dex_file);
       }
+      total_size_ += dex_file.Size();
       ++dex_count_;
       return true;
     }
 
     void Dump(std::ostream& os) {
       for (std::unique_ptr<Experiment>& experiment : experiments_) {
-        experiment->Dump(os);
+        experiment->Dump(os, total_size_);
       }
     }
 
     const Options* const options_;
     std::vector<std::unique_ptr<Experiment>> experiments_;
     size_t dex_count_ = 0;
+    uint64_t total_size_ = 0u;
   };
 
  public:
@@ -130,7 +138,7 @@
       // TODO: once added, use an api to android::base to read a std::vector<uint8_t>.
       if (!android::base::ReadFileToString(filename.c_str(), &content)) {
         LOG(ERROR) << "ReadFileToString failed for " + filename << std::endl;
-        continue;
+        return 2;
       }
       std::vector<std::unique_ptr<const DexFile>> dex_files;
       const DexFileLoader dex_file_loader;
@@ -142,14 +150,14 @@
                                    &error_msg,
                                    &dex_files)) {
         LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl;
-        continue;
+        return 3;
       }
       for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
         if (options.dump_per_input_dex_) {
           Analysis current(&options);
           if (!current.ProcessDexFile(*dex_file)) {
             LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg;
-            continue;
+            return 4;
           }
           LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl;
           current.Dump(LOG_STREAM(INFO));
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index e1f119d..bfeb4b9 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -15,12 +15,91 @@
  */
 
 #include "dexanalyze_experiments.h"
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <iostream>
+#include <map>
+#include <vector>
+
+#include "android-base/stringprintf.h"
 #include "dex/code_item_accessors-inl.h"
 #include "dex/dex_instruction-inl.h"
 #include "dex/standard_dex_file.h"
 
 namespace art {
 
+std::string Percent(uint64_t value, uint64_t max) {
+  if (max == 0) {
+    ++max;
+  }
+  return android::base::StringPrintf("%" PRId64 "(%.2f%%)",
+                                     value,
+                                     static_cast<double>(value * 100) / static_cast<double>(max));
+}
+
+static size_t PrefixLen(const std::string& a, const std::string& b) {
+  size_t len = 0;
+  for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {}
+  return len;
+}
+
+void AnalyzeStrings::ProcessDexFile(const DexFile& dex_file) {
+  std::vector<std::string> strings;
+  for (size_t i = 0; i < dex_file.NumStringIds(); ++i) {
+    uint32_t length = 0;
+    const char* data =
+        dex_file.GetStringDataAndUtf16Length(dex_file.GetStringId(dex::StringIndex(i)), &length);
+    strings.push_back(data);
+  }
+  // Note that the strings are probably already sorted.
+  std::sort(strings.begin(), strings.end());
+
+  // Tunable parameters.
+  static const size_t kMinPrefixLen = 3;
+  static const size_t kPrefixConstantCost = 5;
+  static const size_t kPrefixIndexCost = 2;
+
+  // Calculate total shared prefix.
+  std::vector<size_t> shared_len;
+  std::set<std::string> prefixes;
+  for (size_t i = 0; i < strings.size(); ++i) {
+    size_t best_len = 0;
+    if (i > 0) {
+      best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
+    }
+    if (i < strings.size() - 1) {
+      best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
+    }
+    std::string prefix;
+    if (best_len >= kMinPrefixLen) {
+      prefix = strings[i].substr(0, best_len);
+      prefixes.insert(prefix);
+      total_prefix_savings_ += prefix.length();
+    }
+    total_prefix_index_cost_ += kPrefixIndexCost;
+  }
+  total_num_prefixes_ += prefixes.size();
+  for (const std::string& s : prefixes) {
+    // 4 bytes for an offset, one for length.
+    total_prefix_dict_ += s.length();
+    total_prefix_table_ += kPrefixConstantCost;
+  }
+}
+
+void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
+  os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n";
+  os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
+  os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
+  os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
+  int64_t net_savings = total_prefix_savings_;
+  net_savings -= total_prefix_dict_;
+  net_savings -= total_prefix_table_;
+  net_savings -= total_prefix_index_cost_;
+  os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
+  os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
+}
+
 void CountDexIndices::ProcessDexFile(const DexFile& dex_file) {
   num_string_ids_ += dex_file.NumStringIds();
   num_method_ids_ += dex_file.NumMethodIds();
@@ -107,7 +186,7 @@
   }
 }
 
-void CountDexIndices::Dump(std::ostream& os) const {
+void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
   os << "Num string ids: " << num_string_ids_ << "\n";
   os << "Num method ids: " << num_method_ids_ << "\n";
   os << "Num field ids: " << num_field_ids_ << "\n";
@@ -127,6 +206,7 @@
   os << "Same class invoke: " << same_class_total << "\n";
   os << "Other class invoke: " << other_class_total << "\n";
   os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
+  os << "Total dex size: " << total_size << "\n";
 }
 
 }  // namespace art
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index 5d0f51b..6f70f5d 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -24,12 +24,28 @@
 
 class DexFile;
 
+std::string Percent(uint64_t value, uint64_t max);
+
 // An experiment a stateful visitor that runs on dex files. Results are cumulative.
 class Experiment {
  public:
   virtual ~Experiment() {}
   virtual void ProcessDexFile(const DexFile& dex_file) = 0;
-  virtual void Dump(std::ostream& os) const = 0;
+  virtual void Dump(std::ostream& os, uint64_t total_size) const = 0;
+};
+
+// Analyze string data and strings accessed from code.
+class AnalyzeStrings : public Experiment {
+ public:
+  void ProcessDexFile(const DexFile& dex_file);
+  void Dump(std::ostream& os, uint64_t total_size) const;
+
+ private:
+  int64_t total_prefix_savings_ = 0u;
+  int64_t total_prefix_dict_ = 0u;
+  int64_t total_prefix_table_ = 0u;
+  int64_t total_prefix_index_cost_ = 0u;
+  int64_t total_num_prefixes_ = 0u;
 };
 
 // Count numbers of dex indices.
@@ -37,7 +53,7 @@
  public:
   void ProcessDexFile(const DexFile& dex_file);
 
-  void Dump(std::ostream& os) const;
+  void Dump(std::ostream& os, uint64_t total_size) const;
 
  private:
   // Total string ids loaded from dex code.
@@ -65,4 +81,3 @@
 }  // namespace art
 
 #endif  // ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
-
diff --git a/tools/dexanalyze/dexanalyze_test.cc b/tools/dexanalyze/dexanalyze_test.cc
index c9b8f53..96be3f9 100644
--- a/tools/dexanalyze/dexanalyze_test.cc
+++ b/tools/dexanalyze/dexanalyze_test.cc
@@ -36,10 +36,22 @@
   }
 };
 
+TEST_F(DexAnalyzeTest, NoInputFileGiven) {
+  DexAnalyzeExec({ "-a" }, /*expect_success*/ false);
+}
+
+TEST_F(DexAnalyzeTest, CantOpenInput) {
+  DexAnalyzeExec({ "-a", "/non/existent/path" }, /*expect_success*/ false);
+}
+
 TEST_F(DexAnalyzeTest, TestAnalyzeMultidex) {
   DexAnalyzeExec({ "-a", GetTestDexFileName("MultiDex") }, /*expect_success*/ true);
 }
 
+TEST_F(DexAnalyzeTest, TestAnalizeCoreDex) {
+  DexAnalyzeExec({ "-a", GetLibCoreDexFileNames()[0] }, /*expect_success*/ true);
+}
+
 TEST_F(DexAnalyzeTest, TestInvalidArg) {
   DexAnalyzeExec({ "-invalid-option" }, /*expect_success*/ false);
 }
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
index af87d31..3b364b6 100644
--- a/tools/hiddenapi/Android.bp
+++ b/tools/hiddenapi/Android.bp
@@ -40,6 +40,7 @@
     shared_libs: [
         "libart",
         "libdexfile",
+        "libartbase",
     ],
 }
 
@@ -52,6 +53,7 @@
     shared_libs: [
         "libartd",
         "libdexfiled",
+        "libartbased",
     ],
 }
 
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
index 6c81145..9b171a2 100644
--- a/tools/public.libraries.buildbot.txt
+++ b/tools/public.libraries.buildbot.txt
@@ -1,5 +1,7 @@
 libart.so
 libartd.so
+libartbase.so
+libartbased.so
 libdexfile.so
 libdexfiled.so
 libbacktrace.so
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index 952fba8..428304e 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -62,7 +62,7 @@
 #define FOR_ALL_SUPPORTED_EVENTS(fun) \
     fun(SingleStep, EVENT(SINGLE_STEP), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation)) \
     fun(MethodEntry, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID)) \
-    fun(MethodExit, EVENT(METHOD_ENTRY), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \
+    fun(MethodExit, EVENT(METHOD_EXIT), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jboolean, jvalue)) \
     fun(NativeMethodBind, EVENT(NATIVE_METHOD_BIND), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, void*, void**)) \
     fun(Exception, EVENT(EXCEPTION), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject, jmethodID, jlocation)) \
     fun(ExceptionCatch, EVENT(EXCEPTION_CATCH), (jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject)) \
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index 570960c..5186c43 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -24,7 +24,11 @@
         "veridex.cc",
     ],
     cflags: ["-Wall", "-Werror"],
-    shared_libs: ["libdexfile", "libbase"],
+    shared_libs: [
+        "libdexfile",
+        "libartbase",
+        "libbase",
+    ],
     header_libs: [
         "art_libartbase_headers",
     ],