Revert "Use the thread local cache in interpreter / unresolved entrypoints"

This reverts commit 16032a421a08fb6396de8e8f168a62fe29a0f7ad.

Reason for revert: Need to handle non-read barrier GCs.

Change-Id: Ic3c1cbc97f4d62e701fdcecff9598295cf79e748
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 172d857..8e77b43 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -173,6 +173,7 @@
         "interpreter/interpreter.cc",
         "interpreter/interpreter_cache.cc",
         "interpreter/interpreter_common.cc",
+        "interpreter/interpreter_intrinsics.cc",
         "interpreter/interpreter_switch_impl0.cc",
         "interpreter/interpreter_switch_impl1.cc",
         "interpreter/interpreter_switch_impl2.cc",
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index b454d49..4ee1013 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -486,131 +486,6 @@
 #undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
 #undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
 
-static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  const dex::MethodId& method_id = dex_file->GetMethodId(method_idx);
-  const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
-  const char* method_name = dex_file->GetMethodName(method_id);
-  // Instead of calling ResolveMethod() which has suspend point and can trigger
-  // GC, look up the method symbolically.
-  // Compare method's class name and method name against string init.
-  // It's ok since it's not allowed to create your own java/lang/String.
-  // TODO: verify that assumption.
-  if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
-      (strcmp(method_name, "<init>") == 0)) {
-    return true;
-  }
-  return false;
-}
-
-static inline bool IsStringInit(const Instruction& instr, ArtMethod* caller)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (instr.Opcode() == Instruction::INVOKE_DIRECT ||
-      instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
-    uint16_t callee_method_idx = (instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
-        instr.VRegB_3rc() : instr.VRegB_35c();
-    return IsStringInit(caller->GetDexFile(), callee_method_idx);
-  }
-  return false;
-}
-
-extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr);
-
-template <InvokeType type>
-ArtMethod* FindMethodToCall(Thread* self,
-                            ArtMethod* caller,
-                            ObjPtr<mirror::Object>* this_object,
-                            const Instruction& inst,
-                            /*out*/ bool* string_init)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-
-  // Try to find the method in thread-local cache.
-  size_t tls_value = 0u;
-  if (!self->GetInterpreterCache()->Get(self, &inst, &tls_value)) {
-    DCHECK(!self->IsExceptionPending());
-    // NterpGetMethod can suspend, so save this_object.
-    StackHandleScope<1> hs(self);
-    HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
-    tls_value = NterpGetMethod(self, caller, reinterpret_cast<const uint16_t*>(&inst));
-    if (self->IsExceptionPending()) {
-      return nullptr;
-    }
-  }
-
-  if (type != kStatic && UNLIKELY((*this_object) == nullptr)) {
-    if (UNLIKELY(IsStringInit(inst, caller))) {
-      // Hack for String init:
-      //
-      // We assume that the input of String.<init> in verified code is always
-      // an uninitialized reference. If it is a null constant, it must have been
-      // optimized out by the compiler and we arrive here after deoptimization.
-      // Do not throw NullPointerException.
-    } else {
-      // Maintain interpreter-like semantics where NullPointerException is thrown
-      // after potential NoSuchMethodError from class linker.
-      const uint32_t method_idx = inst.VRegB();
-      ThrowNullPointerExceptionForMethodAccess(method_idx, type);
-      return nullptr;
-    }
-  }
-
-  static constexpr size_t kStringInitMethodFlag = 0b1;
-  static constexpr size_t kInvokeInterfaceOnObjectMethodFlag = 0b1;
-  static constexpr size_t kDefaultMethodFlag = 0b10;
-  static constexpr size_t kMethodMask = ~0b11;
-
-  ArtMethod* called_method = nullptr;
-  switch (type) {
-    case kDirect:
-    case kSuper:
-    case kStatic:
-      // Note: for the interpreter, the String.<init> special casing for invocation is handled
-      // in DoCallCommon.
-      *string_init = ((tls_value & kStringInitMethodFlag) != 0);
-      DCHECK_EQ(*string_init, IsStringInit(inst, caller));
-      called_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask);
-      break;
-    case kInterface:
-      if ((tls_value & kInvokeInterfaceOnObjectMethodFlag) != 0) {
-        // invokeinterface on a j.l.Object method.
-        uint16_t method_index = tls_value >> 16;
-        called_method = (*this_object)->GetClass()->GetVTableEntry(method_index, pointer_size);
-      } else {
-        ArtMethod* interface_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask);
-        if ((tls_value & kDefaultMethodFlag) != 0) {
-          // Default non-abstract method.
-          called_method = (*this_object)->GetClass()->GetImt(pointer_size)->Get(
-              interface_method->GetMethodIndex(), pointer_size);
-        } else {
-          // Regular abstract interface method.
-          called_method = (*this_object)->GetClass()->GetImt(pointer_size)->Get(
-              interface_method->GetImtIndex(), pointer_size);
-        }
-        if (called_method->IsRuntimeMethod()) {
-          called_method = (*this_object)->GetClass()->FindVirtualMethodForInterface(
-              interface_method, pointer_size);
-          if (UNLIKELY(called_method == nullptr)) {
-            ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
-                interface_method, *this_object, caller);
-            return nullptr;
-          }
-        }
-      }
-      break;
-    case kVirtual:
-      called_method = (*this_object)->GetClass()->GetVTableEntry(tls_value, pointer_size);
-      break;
-  }
-
-  if (UNLIKELY(!called_method->IsInvokable())) {
-    called_method->ThrowInvocationTimeError((type == kStatic) ? nullptr : *this_object);
-    return nullptr;
-  }
-  DCHECK(!called_method->IsRuntimeMethod()) << called_method->PrettyMethod();
-  return called_method;
-}
-
 template<bool access_check>
 ALWAYS_INLINE ArtMethod* FindSuperMethodToCall(uint32_t method_idx,
                                               ArtMethod* resolved_method,
@@ -671,6 +546,130 @@
   return super_class->GetVTableEntry(vtable_index, linker->GetImagePointerSize());
 }
 
+// Follow virtual/interface indirections if applicable.
+// Will throw null-pointer exception the if the object is null.
+template<InvokeType type, bool access_check>
+ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx,
+                                          ArtMethod* resolved_method,
+                                          ObjPtr<mirror::Object>* this_object,
+                                          ArtMethod* referrer,
+                                          Thread* self)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  // Null pointer check.
+  if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
+    if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
+                 resolved_method->IsConstructor())) {
+      // Hack for String init:
+      //
+      // We assume that the input of String.<init> in verified code is always
+      // an unitialized reference. If it is a null constant, it must have been
+      // optimized out by the compiler. Do not throw NullPointerException.
+    } else {
+      // Maintain interpreter-like semantics where NullPointerException is thrown
+      // after potential NoSuchMethodError from class linker.
+      ThrowNullPointerExceptionForMethodAccess(method_idx, type);
+      return nullptr;  // Failure.
+    }
+  }
+  switch (type) {
+    case kStatic:
+    case kDirect:
+      return resolved_method;
+    case kVirtual: {
+      ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
+      uint16_t vtable_index = resolved_method->GetMethodIndex();
+      if (access_check &&
+          (!klass->HasVTable() ||
+           vtable_index >= static_cast<uint32_t>(klass->GetVTableLength()))) {
+        // Behavior to agree with that of the verifier.
+        ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
+                               resolved_method->GetName(), resolved_method->GetSignature());
+        return nullptr;  // Failure.
+      }
+      DCHECK(klass->HasVTable()) << klass->PrettyClass();
+      return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());
+    }
+    case kSuper: {
+      return FindSuperMethodToCall<access_check>(method_idx, resolved_method, referrer, self);
+    }
+    case kInterface: {
+      size_t imt_index = resolved_method->GetImtIndex();
+      PointerSize pointer_size = class_linker->GetImagePointerSize();
+      ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
+      ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size);
+      if (!imt_method->IsRuntimeMethod()) {
+        if (kIsDebugBuild) {
+          ArtMethod* method = klass->FindVirtualMethodForInterface(
+              resolved_method, class_linker->GetImagePointerSize());
+          CHECK_EQ(imt_method, method) << ArtMethod::PrettyMethod(resolved_method) << " / "
+                                       << imt_method->PrettyMethod() << " / "
+                                       << ArtMethod::PrettyMethod(method) << " / "
+                                       << klass->PrettyClass();
+        }
+        return imt_method;
+      } else {
+        ArtMethod* interface_method = klass->FindVirtualMethodForInterface(
+            resolved_method, class_linker->GetImagePointerSize());
+        if (UNLIKELY(interface_method == nullptr)) {
+          ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method,
+                                                                     *this_object, referrer);
+          return nullptr;  // Failure.
+        }
+        return interface_method;
+      }
+    }
+    default:
+      LOG(FATAL) << "Unknown invoke type " << type;
+      return nullptr;  // Failure.
+  }
+}
+
+template<InvokeType type, bool access_check>
+inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
+                                     ObjPtr<mirror::Object>* this_object,
+                                     ArtMethod* referrer,
+                                     Thread* self) {
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  constexpr ClassLinker::ResolveMode resolve_mode =
+      access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
+                   : ClassLinker::ResolveMode::kNoChecks;
+  ArtMethod* resolved_method;
+  if (type == kStatic) {
+    resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
+  } else {
+    StackHandleScope<1> hs(self);
+    HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
+    resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
+  }
+  if (UNLIKELY(resolved_method == nullptr)) {
+    DCHECK(self->IsExceptionPending());  // Throw exception and unwind.
+    return nullptr;  // Failure.
+  }
+  return FindMethodToCall<type, access_check>(
+      method_idx, resolved_method, this_object, referrer, self);
+}
+
+// Explicit template declarations of FindMethodFromCode for all invoke types.
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check)                 \
+  template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE                       \
+  ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx,         \
+                                                      ObjPtr<mirror::Object>* this_object, \
+                                                      ArtMethod* referrer, \
+                                                      Thread* self)
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+    EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false);   \
+    EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
+
 inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx,
                                                     ArtMethod* referrer,
                                                     Thread* self,
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index ae56875..8b6fc69 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -143,12 +143,11 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
-template<InvokeType type>
-inline ArtMethod* FindMethodToCall(Thread* self,
-                                   ArtMethod* referrer,
-                                   ObjPtr<mirror::Object>* this_object,
-                                   const Instruction& inst,
-                                   /*out*/ bool* string_init)
+template<InvokeType type, bool access_check>
+inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
+                                     ObjPtr<mirror::Object>* this_object,
+                                     ArtMethod* referrer,
+                                     Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index e7fa148..fc77110 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2267,18 +2267,12 @@
     uint32_t shorty_len;
     const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
     {
-      // Remember the args in case a GC happens in FindMethodToCall.
+      // Remember the args in case a GC happens in FindMethodFromCode.
       ScopedObjectAccessUnchecked soa(self->GetJniEnv());
       RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa);
       visitor.VisitArguments();
-
-      uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
-      CodeItemInstructionAccessor accessor(caller_method->DexInstructions());
-      CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits());
-      const Instruction& instr = accessor.InstructionAt(dex_pc);
-      bool string_init = false;
-      method = FindMethodToCall<type>(self, caller_method, &this_object, instr, &string_init);
-
+      method = FindMethodFromCode<type, /*access_check=*/true>(
+          method_idx, &this_object, caller_method, self);
       visitor.FixupReferences();
     }
 
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 6f0a5c9..0ffc38b 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -511,7 +511,7 @@
         new_dex_pc = dex_pc + instr->SizeInCodeUnits();
       } else if (instr->IsInvoke()) {
         DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
-        if (IsStringInit(*instr, shadow_frame->GetMethod())) {
+        if (IsStringInit(instr, shadow_frame->GetMethod())) {
           uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr);
           // Move the StringFactory.newStringFromChars() result into the register representing
           // "this object" when invoking the string constructor in the original dex instruction.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 69a9c0e..c6b2ddc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -242,8 +242,7 @@
                                        JValue* result,
                                        uint16_t number_of_inputs,
                                        uint32_t (&arg)[Instruction::kMaxVarArgRegs],
-                                       uint32_t vregC,
-                                       bool string_init) REQUIRES_SHARED(Locks::mutator_lock_);
+                                       uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_);
 
 template <bool is_range>
 ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame,
@@ -1195,8 +1194,15 @@
                                 JValue* result,
                                 uint16_t number_of_inputs,
                                 uint32_t (&arg)[Instruction::kMaxVarArgRegs],
-                                uint32_t vregC,
-                                bool string_init) {
+                                uint32_t vregC) {
+  bool string_init = false;
+  // Replace calls to String.<init> with equivalent StringFactory call.
+  if (UNLIKELY(called_method->GetDeclaringClass()->IsStringClass()
+               && called_method->IsConstructor())) {
+    called_method = WellKnownClasses::StringInitToStringFactory(called_method);
+    string_init = true;
+  }
+
   // Compute method information.
   CodeItemDataAccessor accessor(called_method->DexInstructionData());
   // Number of registers for the callee's call frame.
@@ -1387,13 +1393,8 @@
 
 template<bool is_range, bool do_assignability_check>
 NO_STACK_PROTECTOR
-bool DoCall(ArtMethod* called_method,
-            Thread* self,
-            ShadowFrame& shadow_frame,
-            const Instruction* inst,
-            uint16_t inst_data,
-            bool is_string_init,
-            JValue* result) {
+bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
+            const Instruction* inst, uint16_t inst_data, JValue* result) {
   // Argument word count.
   const uint16_t number_of_inputs =
       (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
@@ -1410,14 +1411,8 @@
   }
 
   return DoCallCommon<is_range, do_assignability_check>(
-      called_method,
-      self,
-      shadow_frame,
-      result,
-      number_of_inputs,
-      arg,
-      vregC,
-      is_string_init);
+      called_method, self, shadow_frame,
+      result, number_of_inputs, arg, vregC);
 }
 
 template <bool is_range, bool do_access_check, bool transaction_active>
@@ -1544,12 +1539,9 @@
 // Explicit DoCall template function declarations.
 #define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check)                      \
   template REQUIRES_SHARED(Locks::mutator_lock_)                                                \
-  bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method,                            \
-                                                  Thread* self,                                 \
+  bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Thread* self,              \
                                                   ShadowFrame& shadow_frame,                    \
-                                                  const Instruction* inst,                      \
-                                                  uint16_t inst_data,                           \
-                                                  bool string_init,                             \
+                                                  const Instruction* inst, uint16_t inst_data,  \
                                                   JValue* result)
 EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false);
 EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 49d7e64..fe9cf57 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -20,6 +20,7 @@
 #include "android-base/macros.h"
 #include "instrumentation.h"
 #include "interpreter.h"
+#include "interpreter_intrinsics.h"
 #include "transaction.h"
 
 #include <math.h>
@@ -125,13 +126,8 @@
 // DoFastInvoke and DoInvokeVirtualQuick functions.
 // Returns true on success, otherwise throws an exception and returns false.
 template<bool is_range, bool do_assignability_check>
-bool DoCall(ArtMethod* called_method,
-            Thread* self,
-            ShadowFrame& shadow_frame,
-            const Instruction* inst,
-            uint16_t inst_data,
-            bool string_init,
-            JValue* result);
+bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
+            const Instruction* inst, uint16_t inst_data, JValue* result);
 
 // Called by the switch interpreter to know if we can stay in it.
 bool ShouldStayInSwitchInterpreter(ArtMethod* method)
@@ -224,7 +220,7 @@
 
 // Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
 // Returns true on success, otherwise throws an exception and returns false.
-template<InvokeType type, bool is_range, bool do_access_check>
+template<InvokeType type, bool is_range, bool do_access_check, bool is_mterp>
 static ALWAYS_INLINE bool DoInvoke(Thread* self,
                                    ShadowFrame& shadow_frame,
                                    const Instruction* inst,
@@ -235,19 +231,68 @@
   if (UNLIKELY(self->ObserveAsyncException())) {
     return false;
   }
-  const uint32_t vregC = is_range ? inst->VRegC_3rc() : inst->VRegC_35c();
-  ObjPtr<mirror::Object> obj = type == kStatic ? nullptr : shadow_frame.GetVRegReference(vregC);
+  const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+  const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
   ArtMethod* sf_method = shadow_frame.GetMethod();
-  bool string_init = false;
-  ArtMethod* called_method = FindMethodToCall<type>(self, sf_method, &obj, *inst, &string_init);
-  if (called_method == nullptr) {
-    DCHECK(self->IsExceptionPending());
+
+  // Try to find the method in small thread-local cache first (only used when
+  // nterp is not used as mterp and nterp use the cache in an incompatible way).
+  InterpreterCache* tls_cache = self->GetInterpreterCache();
+  size_t tls_value;
+  ArtMethod* resolved_method;
+  if (!IsNterpSupported() && LIKELY(tls_cache->Get(self, inst, &tls_value))) {
+    resolved_method = reinterpret_cast<ArtMethod*>(tls_value);
+  } else {
+    ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+    constexpr ClassLinker::ResolveMode resolve_mode =
+        do_access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
+                        : ClassLinker::ResolveMode::kNoChecks;
+    resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, sf_method, type);
+    if (UNLIKELY(resolved_method == nullptr)) {
+      CHECK(self->IsExceptionPending());
+      result->SetJ(0);
+      return false;
+    }
+    if (!IsNterpSupported()) {
+      tls_cache->Set(self, inst, reinterpret_cast<size_t>(resolved_method));
+    }
+  }
+
+  // Null pointer check and virtual method resolution.
+  ArtMethod* called_method = nullptr;
+  {
+    // `FindMethodToCall` might suspend, so don't keep `receiver` as a local
+    // variable after the call.
+    ObjPtr<mirror::Object> receiver =
+        (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
+    called_method = FindMethodToCall<type, do_access_check>(
+        method_idx, resolved_method, &receiver, sf_method, self);
+    if (UNLIKELY(called_method == nullptr)) {
+      CHECK(self->IsExceptionPending());
+      result->SetJ(0);
+      return false;
+    }
+  }
+  if (UNLIKELY(!called_method->IsInvokable())) {
+    called_method->ThrowInvocationTimeError(
+        (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC));
     result->SetJ(0);
     return false;
   }
 
-  return DoCall<is_range, do_access_check>(
-      called_method, self, shadow_frame, inst, inst_data, string_init, result);
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (is_mterp && !is_range && called_method->IsIntrinsic()) {
+    if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
+                             shadow_frame.GetResultRegister())) {
+      if (jit != nullptr && sf_method != nullptr) {
+        jit->NotifyInterpreterToCompiledCodeTransition(self, sf_method);
+      }
+      return !self->IsExceptionPending();
+    }
+  }
+
+  return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
+                                           result);
 }
 
 static inline ObjPtr<mirror::MethodHandle> ResolveMethodHandle(Thread* self,
@@ -715,6 +760,34 @@
                                         uint16_t arg_offset,
                                         JValue* result);
 
+static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  const dex::MethodId& method_id = dex_file->GetMethodId(method_idx);
+  const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
+  const char* method_name = dex_file->GetMethodName(method_id);
+  // Instead of calling ResolveMethod() which has suspend point and can trigger
+  // GC, look up the method symbolically.
+  // Compare method's class name and method name against string init.
+  // It's ok since it's not allowed to create your own java/lang/String.
+  // TODO: verify that assumption.
+  if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
+      (strcmp(method_name, "<init>") == 0)) {
+    return true;
+  }
+  return false;
+}
+
+static inline bool IsStringInit(const Instruction* instr, ArtMethod* caller)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (instr->Opcode() == Instruction::INVOKE_DIRECT ||
+      instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
+    uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
+        instr->VRegB_3rc() : instr->VRegB_35c();
+    return IsStringInit(caller->GetDexFile(), callee_method_idx);
+  }
+  return false;
+}
+
 // Set string value created from StringFactory.newStringFromXXX() into all aliases of
 // StringFactory.newEmptyString().
 void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
new file mode 100644
index 0000000..c8344bc
--- /dev/null
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "interpreter/interpreter_intrinsics.h"
+
+#include "dex/dex_instruction.h"
+#include "intrinsics_enum.h"
+#include "interpreter/interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+
+#define BINARY_INTRINSIC(name, op, get1, get2, set)                 \
+static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame,           \
+                               const Instruction* inst,             \
+                               uint16_t inst_data,                  \
+                               JValue* result_register)             \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                         \
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};                   \
+  inst->GetVarArgs(arg, inst_data);                                 \
+  result_register->set(op(shadow_frame->get1, shadow_frame->get2)); \
+  return true;                                                      \
+}
+
+#define BINARY_II_INTRINSIC(name, op, set) \
+    BINARY_INTRINSIC(name, op, GetVReg(arg[0]), GetVReg(arg[1]), set)
+
+#define BINARY_JJ_INTRINSIC(name, op, set) \
+    BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVRegLong(arg[2]), set)
+
+#define BINARY_JI_INTRINSIC(name, op, set) \
+    BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVReg(arg[2]), set)
+
+#define UNARY_INTRINSIC(name, op, get, set)                  \
+static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame,    \
+                               const Instruction* inst,      \
+                               uint16_t inst_data,           \
+                               JValue* result_register)      \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                  \
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};            \
+  inst->GetVarArgs(arg, inst_data);                          \
+  result_register->set(op(shadow_frame->get(arg[0])));       \
+  return true;                                               \
+}
+
+
+// java.lang.Integer.reverse(I)I
+UNARY_INTRINSIC(MterpIntegerReverse, ReverseBits32, GetVReg, SetI);
+
+// java.lang.Integer.reverseBytes(I)I
+UNARY_INTRINSIC(MterpIntegerReverseBytes, BSWAP, GetVReg, SetI);
+
+// java.lang.Integer.bitCount(I)I
+UNARY_INTRINSIC(MterpIntegerBitCount, POPCOUNT, GetVReg, SetI);
+
+// java.lang.Integer.compare(II)I
+BINARY_II_INTRINSIC(MterpIntegerCompare, Compare, SetI);
+
+// java.lang.Integer.highestOneBit(I)I
+UNARY_INTRINSIC(MterpIntegerHighestOneBit, HighestOneBitValue, GetVReg, SetI);
+
+// java.lang.Integer.LowestOneBit(I)I
+UNARY_INTRINSIC(MterpIntegerLowestOneBit, LowestOneBitValue, GetVReg, SetI);
+
+// java.lang.Integer.numberOfLeadingZeros(I)I
+UNARY_INTRINSIC(MterpIntegerNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVReg, SetI);
+
+// java.lang.Integer.numberOfTrailingZeros(I)I
+UNARY_INTRINSIC(MterpIntegerNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVReg, SetI);
+
+// java.lang.Integer.rotateRight(II)I
+BINARY_II_INTRINSIC(MterpIntegerRotateRight, (Rot<int32_t, false>), SetI);
+
+// java.lang.Integer.rotateLeft(II)I
+BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot<int32_t, true>), SetI);
+
+// java.lang.Integer.signum(I)I
+UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI);
+
+// java.lang.Long.reverse(J)J
+UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ);
+
+// java.lang.Long.reverseBytes(J)J
+UNARY_INTRINSIC(MterpLongReverseBytes, BSWAP, GetVRegLong, SetJ);
+
+// java.lang.Long.bitCount(J)I
+UNARY_INTRINSIC(MterpLongBitCount, POPCOUNT, GetVRegLong, SetI);
+
+// java.lang.Long.compare(JJ)I
+BINARY_JJ_INTRINSIC(MterpLongCompare, Compare, SetI);
+
+// java.lang.Long.highestOneBit(J)J
+UNARY_INTRINSIC(MterpLongHighestOneBit, HighestOneBitValue, GetVRegLong, SetJ);
+
+// java.lang.Long.lowestOneBit(J)J
+UNARY_INTRINSIC(MterpLongLowestOneBit, LowestOneBitValue, GetVRegLong, SetJ);
+
+// java.lang.Long.numberOfLeadingZeros(J)I
+UNARY_INTRINSIC(MterpLongNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVRegLong, SetJ);
+
+// java.lang.Long.numberOfTrailingZeros(J)I
+UNARY_INTRINSIC(MterpLongNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVRegLong, SetJ);
+
+// java.lang.Long.rotateRight(JI)J
+BINARY_JI_INTRINSIC(MterpLongRotateRight, (Rot<int64_t, false>), SetJ);
+
+// java.lang.Long.rotateLeft(JI)J
+BINARY_JI_INTRINSIC(MterpLongRotateLeft, (Rot<int64_t, true>), SetJ);
+
+// java.lang.Long.signum(J)I
+UNARY_INTRINSIC(MterpLongSignum, Signum, GetVRegLong, SetI);
+
+// java.lang.Short.reverseBytes(S)S
+UNARY_INTRINSIC(MterpShortReverseBytes, BSWAP, GetVRegShort, SetS);
+
+// java.lang.Math.min(II)I
+BINARY_II_INTRINSIC(MterpMathMinIntInt, std::min, SetI);
+
+// java.lang.Math.min(JJ)J
+BINARY_JJ_INTRINSIC(MterpMathMinLongLong, std::min, SetJ);
+
+// java.lang.Math.max(II)I
+BINARY_II_INTRINSIC(MterpMathMaxIntInt, std::max, SetI);
+
+// java.lang.Math.max(JJ)J
+BINARY_JJ_INTRINSIC(MterpMathMaxLongLong, std::max, SetJ);
+
+// java.lang.Math.abs(I)I
+UNARY_INTRINSIC(MterpMathAbsInt, std::abs, GetVReg, SetI);
+
+// java.lang.Math.abs(J)J
+UNARY_INTRINSIC(MterpMathAbsLong, std::abs, GetVRegLong, SetJ);
+
+// java.lang.Math.abs(F)F
+UNARY_INTRINSIC(MterpMathAbsFloat, 0x7fffffff&, GetVReg, SetI);
+
+// java.lang.Math.abs(D)D
+UNARY_INTRINSIC(MterpMathAbsDouble, INT64_C(0x7fffffffffffffff)&, GetVRegLong, SetJ);
+
+// java.lang.Math.sqrt(D)D
+UNARY_INTRINSIC(MterpMathSqrt, std::sqrt, GetVRegDouble, SetD);
+
+// java.lang.Math.ceil(D)D
+UNARY_INTRINSIC(MterpMathCeil, std::ceil, GetVRegDouble, SetD);
+
+// java.lang.Math.floor(D)D
+UNARY_INTRINSIC(MterpMathFloor, std::floor, GetVRegDouble, SetD);
+
+// java.lang.Math.sin(D)D
+UNARY_INTRINSIC(MterpMathSin, std::sin, GetVRegDouble, SetD);
+
+// java.lang.Math.cos(D)D
+UNARY_INTRINSIC(MterpMathCos, std::cos, GetVRegDouble, SetD);
+
+// java.lang.Math.tan(D)D
+UNARY_INTRINSIC(MterpMathTan, std::tan, GetVRegDouble, SetD);
+
+// java.lang.Math.asin(D)D
+UNARY_INTRINSIC(MterpMathAsin, std::asin, GetVRegDouble, SetD);
+
+// java.lang.Math.acos(D)D
+UNARY_INTRINSIC(MterpMathAcos, std::acos, GetVRegDouble, SetD);
+
+// java.lang.Math.atan(D)D
+UNARY_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD);
+
+// java.lang.String.charAt(I)C
+static ALWAYS_INLINE bool MterpStringCharAt(ShadowFrame* shadow_frame,
+                                            const Instruction* inst,
+                                            uint16_t inst_data,
+                                            JValue* result_register)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+  inst->GetVarArgs(arg, inst_data);
+  ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
+  int length = str->GetLength();
+  int index = shadow_frame->GetVReg(arg[1]);
+  uint16_t res;
+  if (UNLIKELY(index < 0) || (index >= length)) {
+    return false;  // Punt and let non-intrinsic version deal with the throw.
+  }
+  if (str->IsCompressed()) {
+    res = str->GetValueCompressed()[index];
+  } else {
+    res = str->GetValue()[index];
+  }
+  result_register->SetC(res);
+  return true;
+}
+
+// java.lang.String.compareTo(Ljava/lang/string)I
+static ALWAYS_INLINE bool MterpStringCompareTo(ShadowFrame* shadow_frame,
+                                               const Instruction* inst,
+                                               uint16_t inst_data,
+                                               JValue* result_register)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+  inst->GetVarArgs(arg, inst_data);
+  ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
+  ObjPtr<mirror::Object> arg1 = shadow_frame->GetVRegReference(arg[1]);
+  if (arg1 == nullptr) {
+    return false;
+  }
+  result_register->SetI(str->CompareTo(arg1->AsString()));
+  return true;
+}
+
+#define STRING_INDEXOF_INTRINSIC(name, starting_pos)             \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
+                                      const Instruction* inst,   \
+                                      uint16_t inst_data,        \
+                                      JValue* result_register)   \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                      \
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};                \
+  inst->GetVarArgs(arg, inst_data);                              \
+  ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
+  int ch = shadow_frame->GetVReg(arg[1]);                        \
+  if (ch >= 0x10000) {                                           \
+    /* Punt if supplementary char. */                            \
+    return false;                                                \
+  }                                                              \
+  result_register->SetI(str->FastIndexOf(ch, starting_pos));     \
+  return true;                                                   \
+}
+
+// java.lang.String.indexOf(I)I
+STRING_INDEXOF_INTRINSIC(StringIndexOf, 0);
+
+// java.lang.String.indexOf(II)I
+STRING_INDEXOF_INTRINSIC(StringIndexOfAfter, shadow_frame->GetVReg(arg[2]));
+
+#define SIMPLE_STRING_INTRINSIC(name, operation)                 \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
+                                      const Instruction* inst,   \
+                                      uint16_t inst_data,        \
+                                      JValue* result_register)   \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                      \
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};                \
+  inst->GetVarArgs(arg, inst_data);                              \
+  ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
+  result_register->operation;                                    \
+  return true;                                                   \
+}
+
+// java.lang.String.isEmpty()Z
+SIMPLE_STRING_INTRINSIC(StringIsEmpty, SetZ(str->GetLength() == 0))
+
+// java.lang.String.length()I
+SIMPLE_STRING_INTRINSIC(StringLength, SetI(str->GetLength()))
+
+// java.lang.String.getCharsNoCheck(II[CI)V
+static ALWAYS_INLINE bool MterpStringGetCharsNoCheck(ShadowFrame* shadow_frame,
+                                                     const Instruction* inst,
+                                                     uint16_t inst_data,
+                                                     JValue* result_register ATTRIBUTE_UNUSED)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Start, end & index already checked by caller - won't throw.  Destination is uncompressed.
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+  inst->GetVarArgs(arg, inst_data);
+  ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
+  int32_t start = shadow_frame->GetVReg(arg[1]);
+  int32_t end = shadow_frame->GetVReg(arg[2]);
+  int32_t index = shadow_frame->GetVReg(arg[4]);
+  ObjPtr<mirror::CharArray> array = shadow_frame->GetVRegReference(arg[3])->AsCharArray();
+  uint16_t* dst = array->GetData() + index;
+  int32_t len = (end - start);
+  if (str->IsCompressed()) {
+    const uint8_t* src_8 = str->GetValueCompressed() + start;
+    for (int i = 0; i < len; i++) {
+      dst[i] = src_8[i];
+    }
+  } else {
+    uint16_t* src_16 = str->GetValue() + start;
+    memcpy(dst, src_16, len * sizeof(uint16_t));
+  }
+  return true;
+}
+
+// java.lang.String.equalsLjava/lang/Object;)Z
+static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame,
+                                            const Instruction* inst,
+                                            uint16_t inst_data,
+                                            JValue* result_register)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+  inst->GetVarArgs(arg, inst_data);
+  ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
+  ObjPtr<mirror::Object> obj = shadow_frame->GetVRegReference(arg[1]);
+  bool res = false;  // Assume not equal.
+  if ((obj != nullptr) && obj->IsString()) {
+    ObjPtr<mirror::String> str2 = obj->AsString();
+    if (str->GetCount() == str2->GetCount()) {
+      // Length & compression status are same.  Can use block compare.
+      void* bytes1;
+      void* bytes2;
+      int len = str->GetLength();
+      if (str->IsCompressed()) {
+        bytes1 = str->GetValueCompressed();
+        bytes2 = str2->GetValueCompressed();
+      } else {
+        len *= sizeof(uint16_t);
+        bytes1 = str->GetValue();
+        bytes2 = str2->GetValue();
+      }
+      res = (memcmp(bytes1, bytes2, len) == 0);
+    }
+  }
+  result_register->SetZ(res);
+  return true;
+}
+
+#define VARHANDLE_FENCE_INTRINSIC(name, std_memory_operation)              \
+static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, \
+                               const Instruction* inst ATTRIBUTE_UNUSED,   \
+                               uint16_t inst_data ATTRIBUTE_UNUSED,        \
+                               JValue* result_register ATTRIBUTE_UNUSED)   \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                                \
+  std::atomic_thread_fence(std_memory_operation);                          \
+  return true;                                                             \
+}
+
+// The VarHandle fence methods are static (unlike jdk.internal.misc.Unsafe versions).
+// The fences for the LoadLoadFence and StoreStoreFence are stronger
+// than strictly required, but the impact should be marginal.
+VARHANDLE_FENCE_INTRINSIC(MterpVarHandleFullFence, std::memory_order_seq_cst)
+VARHANDLE_FENCE_INTRINSIC(MterpVarHandleAcquireFence, std::memory_order_acquire)
+VARHANDLE_FENCE_INTRINSIC(MterpVarHandleReleaseFence, std::memory_order_release)
+VARHANDLE_FENCE_INTRINSIC(MterpVarHandleLoadLoadFence, std::memory_order_acquire)
+VARHANDLE_FENCE_INTRINSIC(MterpVarHandleStoreStoreFence, std::memory_order_release)
+
+#define METHOD_HANDLE_INVOKE_INTRINSIC(name)                                                      \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame,                                  \
+                               const Instruction* inst,                                           \
+                               uint16_t inst_data,                                                \
+                               JValue* result)                                                    \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                                                       \
+  if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) {                                        \
+    return DoInvokePolymorphic<false>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
+  } else {                                                                                        \
+    return DoInvokePolymorphic<true>(Thread::Current(), *shadow_frame, inst, inst_data, result);  \
+  }                                                                                               \
+}
+
+METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvokeExact)
+METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvoke)
+
+#define VAR_HANDLE_ACCESSOR_INTRINSIC(name)                                   \
+static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame,              \
+                               const Instruction* inst,                       \
+                               uint16_t inst_data,                            \
+                               JValue* result)                                \
+    REQUIRES_SHARED(Locks::mutator_lock_) {                                   \
+  return Do##name(Thread::Current(), *shadow_frame, inst, inst_data, result); \
+}
+
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchange)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGet);
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAdd)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAnd)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOr)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXor)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetOpaque)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetVolatile)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetOpaque)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetRelease)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetVolatile)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSet)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetAcquire)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain)
+VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease)
+
+static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
+                                                 const Instruction* inst ATTRIBUTE_UNUSED,
+                                                 uint16_t inst_data ATTRIBUTE_UNUSED,
+                                                 JValue* result_register ATTRIBUTE_UNUSED)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Do nothing; Its only purpose is to keep the argument reference live
+  // at preceding suspend points. That's automatic in the interpreter.
+  return true;
+}
+
+// Macro to help keep track of what's left to implement.
+#define UNIMPLEMENTED_CASE(name)    \
+    case Intrinsics::k##name:       \
+      res = false;                  \
+      break;
+
+#define INTRINSIC_CASE(name)                                           \
+    case Intrinsics::k##name:                                          \
+      res = Mterp##name(shadow_frame, inst, inst_data, result_register); \
+      break;
+
+bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
+                          ArtMethod* const called_method,
+                          const Instruction* inst,
+                          uint16_t inst_data,
+                          JValue* result_register)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  Intrinsics intrinsic = static_cast<Intrinsics>(called_method->GetIntrinsic());
+  bool res = false;  // Assume failure
+  switch (intrinsic) {
+    UNIMPLEMENTED_CASE(DoubleDoubleToRawLongBits /* (D)J */)
+    UNIMPLEMENTED_CASE(DoubleDoubleToLongBits /* (D)J */)
+    UNIMPLEMENTED_CASE(DoubleIsInfinite /* (D)Z */)
+    UNIMPLEMENTED_CASE(DoubleIsNaN /* (D)Z */)
+    UNIMPLEMENTED_CASE(DoubleLongBitsToDouble /* (J)D */)
+    UNIMPLEMENTED_CASE(FloatFloatToRawIntBits /* (F)I */)
+    UNIMPLEMENTED_CASE(FloatFloatToIntBits /* (F)I */)
+    UNIMPLEMENTED_CASE(FloatIsInfinite /* (F)Z */)
+    UNIMPLEMENTED_CASE(FloatIsNaN /* (F)Z */)
+    UNIMPLEMENTED_CASE(FloatIntBitsToFloat /* (I)F */)
+    UNIMPLEMENTED_CASE(IntegerDivideUnsigned /* (II)I */)
+    UNIMPLEMENTED_CASE(LongDivideUnsigned /* (JJ)J */)
+    INTRINSIC_CASE(IntegerReverse)
+    INTRINSIC_CASE(IntegerReverseBytes)
+    INTRINSIC_CASE(IntegerBitCount)
+    INTRINSIC_CASE(IntegerCompare)
+    INTRINSIC_CASE(IntegerHighestOneBit)
+    INTRINSIC_CASE(IntegerLowestOneBit)
+    INTRINSIC_CASE(IntegerNumberOfLeadingZeros)
+    INTRINSIC_CASE(IntegerNumberOfTrailingZeros)
+    INTRINSIC_CASE(IntegerRotateRight)
+    INTRINSIC_CASE(IntegerRotateLeft)
+    INTRINSIC_CASE(IntegerSignum)
+    INTRINSIC_CASE(LongReverse)
+    INTRINSIC_CASE(LongReverseBytes)
+    INTRINSIC_CASE(LongBitCount)
+    INTRINSIC_CASE(LongCompare)
+    INTRINSIC_CASE(LongHighestOneBit)
+    INTRINSIC_CASE(LongLowestOneBit)
+    INTRINSIC_CASE(LongNumberOfLeadingZeros)
+    INTRINSIC_CASE(LongNumberOfTrailingZeros)
+    INTRINSIC_CASE(LongRotateRight)
+    INTRINSIC_CASE(LongRotateLeft)
+    INTRINSIC_CASE(LongSignum)
+    INTRINSIC_CASE(ShortReverseBytes)
+    INTRINSIC_CASE(MathAbsDouble)
+    INTRINSIC_CASE(MathAbsFloat)
+    INTRINSIC_CASE(MathAbsLong)
+    INTRINSIC_CASE(MathAbsInt)
+    UNIMPLEMENTED_CASE(MathFmaDouble /* (DDD)D */)
+    UNIMPLEMENTED_CASE(MathFmaFloat /* (FFF)F */)
+    UNIMPLEMENTED_CASE(MathMinDoubleDouble /* (DD)D */)
+    UNIMPLEMENTED_CASE(MathMinFloatFloat /* (FF)F */)
+    INTRINSIC_CASE(MathMinLongLong)
+    INTRINSIC_CASE(MathMinIntInt)
+    UNIMPLEMENTED_CASE(MathMaxDoubleDouble /* (DD)D */)
+    UNIMPLEMENTED_CASE(MathMaxFloatFloat /* (FF)F */)
+    INTRINSIC_CASE(MathMaxLongLong)
+    INTRINSIC_CASE(MathMaxIntInt)
+    INTRINSIC_CASE(MathCos)
+    INTRINSIC_CASE(MathSin)
+    INTRINSIC_CASE(MathAcos)
+    INTRINSIC_CASE(MathAsin)
+    INTRINSIC_CASE(MathAtan)
+    UNIMPLEMENTED_CASE(MathAtan2 /* (DD)D */)
+    UNIMPLEMENTED_CASE(MathCbrt /* (D)D */)
+    UNIMPLEMENTED_CASE(MathCosh /* (D)D */)
+    UNIMPLEMENTED_CASE(MathExp /* (D)D */)
+    UNIMPLEMENTED_CASE(MathExpm1 /* (D)D */)
+    UNIMPLEMENTED_CASE(MathHypot /* (DD)D */)
+    UNIMPLEMENTED_CASE(MathLog /* (D)D */)
+    UNIMPLEMENTED_CASE(MathLog10 /* (D)D */)
+    UNIMPLEMENTED_CASE(MathNextAfter /* (DD)D */)
+    UNIMPLEMENTED_CASE(MathPow /* (DD)D */)
+    UNIMPLEMENTED_CASE(MathSinh /* (D)D */)
+    INTRINSIC_CASE(MathTan)
+    UNIMPLEMENTED_CASE(MathTanh /* (D)D */)
+    INTRINSIC_CASE(MathSqrt)
+    INTRINSIC_CASE(MathCeil)
+    INTRINSIC_CASE(MathFloor)
+    UNIMPLEMENTED_CASE(MathRint /* (D)D */)
+    UNIMPLEMENTED_CASE(MathRoundDouble /* (D)J */)
+    UNIMPLEMENTED_CASE(MathRoundFloat /* (F)I */)
+    UNIMPLEMENTED_CASE(MathMultiplyHigh /* (JJ)J */)
+    UNIMPLEMENTED_CASE(SystemArrayCopyByte /* ([BI[BII)V */)
+    UNIMPLEMENTED_CASE(SystemArrayCopyChar /* ([CI[CII)V */)
+    UNIMPLEMENTED_CASE(SystemArrayCopyInt /* ([II[III)V */)
+    UNIMPLEMENTED_CASE(SystemArrayCopy /* (Ljava/lang/Object;ILjava/lang/Object;II)V */)
+    UNIMPLEMENTED_CASE(ThreadCurrentThread /* ()Ljava/lang/Thread; */)
+    UNIMPLEMENTED_CASE(MemoryPeekByte /* (J)B */)
+    UNIMPLEMENTED_CASE(MemoryPeekIntNative /* (J)I */)
+    UNIMPLEMENTED_CASE(MemoryPeekLongNative /* (J)J */)
+    UNIMPLEMENTED_CASE(MemoryPeekShortNative /* (J)S */)
+    UNIMPLEMENTED_CASE(MemoryPokeByte /* (JB)V */)
+    UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */)
+    UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */)
+    UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */)
+    INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */)
+    INTRINSIC_CASE(StringCharAt)
+    INTRINSIC_CASE(StringCompareTo)
+    INTRINSIC_CASE(StringEquals)
+    INTRINSIC_CASE(StringGetCharsNoCheck)
+    INTRINSIC_CASE(StringIndexOf)
+    INTRINSIC_CASE(StringIndexOfAfter)
+    UNIMPLEMENTED_CASE(StringStringIndexOf /* (Ljava/lang/String;)I */)
+    UNIMPLEMENTED_CASE(StringStringIndexOfAfter /* (Ljava/lang/String;I)I */)
+    INTRINSIC_CASE(StringIsEmpty)
+    INTRINSIC_CASE(StringLength)
+    UNIMPLEMENTED_CASE(StringNewStringFromBytes /* ([BIII)Ljava/lang/String; */)
+    UNIMPLEMENTED_CASE(StringNewStringFromChars /* (II[C)Ljava/lang/String; */)
+    UNIMPLEMENTED_CASE(StringNewStringFromString /* (Ljava/lang/String;)Ljava/lang/String; */)
+    UNIMPLEMENTED_CASE(StringBufferAppend /* (Ljava/lang/String;)Ljava/lang/StringBuffer; */)
+    UNIMPLEMENTED_CASE(StringBufferLength /* ()I */)
+    UNIMPLEMENTED_CASE(StringBufferToString /* ()Ljava/lang/String; */)
+    UNIMPLEMENTED_CASE(
+        StringBuilderAppendObject /* (Ljava/lang/Object;)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(
+        StringBuilderAppendString /* (Ljava/lang/String;)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(
+        StringBuilderAppendCharSequence /* (Ljava/lang/CharSequence;)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendCharArray /* ([C)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendBoolean /* (Z)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendChar /* (C)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendInt /* (I)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendLong /* (J)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendFloat /* (F)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderAppendDouble /* (D)Ljava/lang/StringBuilder; */)
+    UNIMPLEMENTED_CASE(StringBuilderLength /* ()I */)
+    UNIMPLEMENTED_CASE(StringBuilderToString /* ()Ljava/lang/String; */)
+    UNIMPLEMENTED_CASE(UnsafeCASInt /* (Ljava/lang/Object;JII)Z */)
+    UNIMPLEMENTED_CASE(UnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */)
+    UNIMPLEMENTED_CASE(UnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
+    UNIMPLEMENTED_CASE(UnsafeGet /* (Ljava/lang/Object;J)I */)
+    UNIMPLEMENTED_CASE(UnsafeGetVolatile /* (Ljava/lang/Object;J)I */)
+    UNIMPLEMENTED_CASE(UnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(UnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(UnsafeGetLong /* (Ljava/lang/Object;J)J */)
+    UNIMPLEMENTED_CASE(UnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */)
+    UNIMPLEMENTED_CASE(UnsafePut /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(UnsafePutOrdered /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(UnsafePutVolatile /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(UnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(UnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(UnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(UnsafePutLong /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(UnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(UnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(UnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */)
+    UNIMPLEMENTED_CASE(UnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */)
+    UNIMPLEMENTED_CASE(UnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */)
+    UNIMPLEMENTED_CASE(UnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */)
+    UNIMPLEMENTED_CASE(UnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(UnsafeLoadFence /* ()V */)
+    UNIMPLEMENTED_CASE(UnsafeStoreFence /* ()V */)
+    UNIMPLEMENTED_CASE(UnsafeFullFence /* ()V */)
+    UNIMPLEMENTED_CASE(JdkUnsafeCASInt /* (Ljava/lang/Object;JII)Z */)
+    UNIMPLEMENTED_CASE(JdkUnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */)
+    UNIMPLEMENTED_CASE(JdkUnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
+    UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetInt /* (Ljava/lang/Object;JII)Z */)
+    UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetLong /* (Ljava/lang/Object;JJJ)Z */)
+    UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGet /* (Ljava/lang/Object;J)I */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetVolatile /* (Ljava/lang/Object;J)I */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetAcquire /* (Ljava/lang/Object;J)I */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetObjectAcquire /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetLong /* (Ljava/lang/Object;J)J */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetLongAcquire /* (Ljava/lang/Object;J)J */)
+    UNIMPLEMENTED_CASE(JdkUnsafePut /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutOrdered /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutVolatile /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutRelease /* (Ljava/lang/Object;JI)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutObjectRelease /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutLong /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafePutLongRelease /* (Ljava/lang/Object;JJ)V */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */)
+    UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(JdkUnsafeLoadFence /* ()V */)
+    UNIMPLEMENTED_CASE(JdkUnsafeStoreFence /* ()V */)
+    UNIMPLEMENTED_CASE(JdkUnsafeFullFence /* ()V */)
+    UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */)
+    UNIMPLEMENTED_CASE(ReferenceRefersTo /* (Ljava/lang/Object;)Z */)
+    UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
+    UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
+    UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
+    UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */)
+    UNIMPLEMENTED_CASE(CRC32UpdateByteBuffer /* (IJII)I */)
+    UNIMPLEMENTED_CASE(FP16Compare /* (SS)I */)
+    UNIMPLEMENTED_CASE(FP16ToFloat /* (S)F */)
+    UNIMPLEMENTED_CASE(FP16ToHalf /* (F)S */)
+    UNIMPLEMENTED_CASE(FP16Floor /* (S)S */)
+    UNIMPLEMENTED_CASE(FP16Ceil /* (S)S */)
+    UNIMPLEMENTED_CASE(FP16Rint /* (S)S */)
+    UNIMPLEMENTED_CASE(FP16Greater /* (SS)Z */)
+    UNIMPLEMENTED_CASE(FP16GreaterEquals /* (SS)Z */)
+    UNIMPLEMENTED_CASE(FP16Less /* (SS)Z */)
+    UNIMPLEMENTED_CASE(FP16LessEquals /* (SS)Z */)
+    UNIMPLEMENTED_CASE(FP16Min /* (SS)S */)
+    UNIMPLEMENTED_CASE(FP16Max /* (SS)S */)
+    INTRINSIC_CASE(VarHandleFullFence)
+    INTRINSIC_CASE(VarHandleAcquireFence)
+    INTRINSIC_CASE(VarHandleReleaseFence)
+    INTRINSIC_CASE(VarHandleLoadLoadFence)
+    INTRINSIC_CASE(VarHandleStoreStoreFence)
+    INTRINSIC_CASE(MethodHandleInvokeExact)
+    INTRINSIC_CASE(MethodHandleInvoke)
+    INTRINSIC_CASE(VarHandleCompareAndExchange)
+    INTRINSIC_CASE(VarHandleCompareAndExchangeAcquire)
+    INTRINSIC_CASE(VarHandleCompareAndExchangeRelease)
+    INTRINSIC_CASE(VarHandleCompareAndSet)
+    INTRINSIC_CASE(VarHandleGet)
+    INTRINSIC_CASE(VarHandleGetAcquire)
+    INTRINSIC_CASE(VarHandleGetAndAdd)
+    INTRINSIC_CASE(VarHandleGetAndAddAcquire)
+    INTRINSIC_CASE(VarHandleGetAndAddRelease)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseAnd)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseAndAcquire)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseAndRelease)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseOr)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseOrAcquire)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseOrRelease)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseXor)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseXorAcquire)
+    INTRINSIC_CASE(VarHandleGetAndBitwiseXorRelease)
+    INTRINSIC_CASE(VarHandleGetAndSet)
+    INTRINSIC_CASE(VarHandleGetAndSetAcquire)
+    INTRINSIC_CASE(VarHandleGetAndSetRelease)
+    INTRINSIC_CASE(VarHandleGetOpaque)
+    INTRINSIC_CASE(VarHandleGetVolatile)
+    INTRINSIC_CASE(VarHandleSet)
+    INTRINSIC_CASE(VarHandleSetOpaque)
+    INTRINSIC_CASE(VarHandleSetRelease)
+    INTRINSIC_CASE(VarHandleSetVolatile)
+    INTRINSIC_CASE(VarHandleWeakCompareAndSet)
+    INTRINSIC_CASE(VarHandleWeakCompareAndSetAcquire)
+    INTRINSIC_CASE(VarHandleWeakCompareAndSetPlain)
+    INTRINSIC_CASE(VarHandleWeakCompareAndSetRelease)
+    case Intrinsics::kNone:
+      res = false;
+      break;
+    // Note: no default case to ensure we catch any newly added intrinsics.
+  }
+  return res;
+}
+
+}  // namespace interpreter
+}  // namespace art
diff --git a/runtime/interpreter/interpreter_intrinsics.h b/runtime/interpreter/interpreter_intrinsics.h
new file mode 100644
index 0000000..2a23002
--- /dev/null
+++ b/runtime/interpreter/interpreter_intrinsics.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
+#define ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
+
+#include "jvalue.h"
+
+namespace art {
+
+class ArtMethod;
+class Instruction;
+class ShadowFrame;
+
+namespace interpreter {
+
+// Invokes to methods identified as intrinics are routed here.  If there is
+// no interpreter implementation, return false and a normal invoke will proceed.
+bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
+                          ArtMethod* const called_method,
+                          const Instruction* inst,
+                          uint16_t inst_data,
+                          JValue* result_register);
+
+}  // namespace interpreter
+}  // namespace art
+
+#endif  // ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index 2858daf..62bf7d8 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -353,7 +353,7 @@
 
   template<InvokeType type, bool is_range>
   HANDLER_ATTRIBUTES bool HandleInvoke() {
-    bool success = DoInvoke<type, is_range, do_access_check>(
+    bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false>(
         Self(), shadow_frame_, inst_, inst_data_, ResultRegister());
     return PossiblyHandlePendingExceptionOnInvoke(!success);
   }
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index cc2ec4f..17f17af 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -26,6 +26,7 @@
 #include "entrypoints/entrypoint_utils-inl.h"
 #include "interpreter/interpreter_cache-inl.h"
 #include "interpreter/interpreter_common.h"
+#include "interpreter/interpreter_intrinsics.h"
 #include "interpreter/shadow_frame-inl.h"
 #include "mirror/string-alloc-inl.h"
 #include "nterp_helpers.h"
@@ -95,13 +96,13 @@
 }
 
 template<typename T>
-inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T value) {
+inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T value) {
   DCHECK(kUseReadBarrier) << "Nterp only works with read barriers";
   self->GetInterpreterCache()->Set(self, dex_pc_ptr, value);
 }
 
 template<typename T>
-inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T* value) {
+inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T* value) {
   UpdateCache(self, dex_pc_ptr, reinterpret_cast<size_t>(value));
 }
 
@@ -251,7 +252,7 @@
 }
 
 FLATTEN
-extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr)
+extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   UpdateHotness(caller);
   const Instruction* inst = Instruction::At(dex_pc_ptr);
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 70948b8..75a692e 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -420,13 +420,11 @@
   shadow_frame->SetVRegReference(0, reference_empty_string.Get());
   shadow_frame->SetVRegReference(1, string_arg.Get());
 
-  ArtMethod* factory = WellKnownClasses::StringInitToStringFactory(method);
-  interpreter::DoCall<false, false>(factory,
+  interpreter::DoCall<false, false>(method,
                                     self,
                                     *shadow_frame,
                                     Instruction::At(inst_data),
                                     inst_data[0],
-                                    /* string_init= */ true,
                                     &result);
   ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
   EXPECT_EQ(string_arg->GetLength(), string_result->GetLength());
@@ -1026,7 +1024,6 @@
                                     *shadow_frame,
                                     Instruction::At(inst_data),
                                     inst_data[0],
-                                    /* string_init= */ false,
                                     &result);
   ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
   ASSERT_TRUE(string_result != nullptr);
@@ -1182,7 +1179,6 @@
                                         *shadow_frame,
                                         Instruction::At(inst_data),
                                         inst_data[0],
-                                        /* string_init= */ false,
                                         &result);
       CHECK(!self->IsExceptionPending());
     }