Performance improvements in invoke-polymorphic runtime support

Fineses the runtime support for invoke-polymorphic to improve
MethodHandle benchmark measurements by an average of 14%.

- Faster conversion checks for non-exact invokes.
- Avoid conversion checks for CallerTransformers and Transforms.
- Fast path for exact invokes avoiding conversion checks.
- Use cheaper constructs where possible.

Most of these changes are driven by simulation in callgrind.

Bug: 30550796
Test: m test-art-host
Change-Id: I38722b0d41db8e528f2730a826b1710b9fd1f6a9
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 58c5d17..bd7c4ad 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -49,10 +49,19 @@
 bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedAssertNoThreadSuspension ants(__FUNCTION__);
-#define LOOKUP_PRIMITIVE(primitive, _, __, ___)                         \
-  if (klass->DescriptorEquals(Primitive::BoxedDescriptor(primitive))) { \
-    *type = primitive;                                                  \
-    return true;                                                        \
+  std::string storage;
+  const char* descriptor = klass->GetDescriptor(&storage);
+  static const char kJavaLangPrefix[] = "Ljava/lang/";
+  static const size_t kJavaLangPrefixSize = sizeof(kJavaLangPrefix) - 1;
+  if (strncmp(descriptor, kJavaLangPrefix, kJavaLangPrefixSize) != 0) {
+    return false;
+  }
+
+  descriptor += kJavaLangPrefixSize;
+#define LOOKUP_PRIMITIVE(primitive, _, java_name, ___) \
+  if (strcmp(descriptor, #java_name ";") == 0) {       \
+    *type = primitive;                                 \
+    return true;                                       \
   }
 
   PRIMITIVES_LIST(LOOKUP_PRIMITIVE);
@@ -141,21 +150,23 @@
     if (from->DescriptorEquals("Ljava/lang/Object;")) {
       // Object might be converted into a primitive during unboxing.
       return true;
-    } else if (Primitive::IsNumericType(to_primitive) &&
-               from->DescriptorEquals("Ljava/lang/Number;")) {
+    }
+
+    if (Primitive::IsNumericType(to_primitive) && from->DescriptorEquals("Ljava/lang/Number;")) {
       // Number might be unboxed into any of the number primitive types.
       return true;
     }
+
     Primitive::Type unboxed_type;
     if (GetUnboxedPrimitiveType(from, &unboxed_type)) {
       if (unboxed_type == to_primitive) {
         // Straightforward unboxing conversion such as Boolean => boolean.
         return true;
-      } else {
-        // Check if widening operations for numeric primitives would work,
-        // such as Byte => byte => long.
-        return Primitive::IsWidenable(unboxed_type, to_primitive);
       }
+
+      // Check if widening operations for numeric primitives would work,
+      // such as Byte => byte => long.
+      return Primitive::IsWidenable(unboxed_type, to_primitive);
     }
   }
 
@@ -372,25 +383,18 @@
 static inline size_t GetInsForProxyOrNativeMethod(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(method->IsNative() || method->IsProxyMethod());
-
   method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-  size_t num_ins = 0;
-  // Separate accounting for the receiver, which isn't a part of the
-  // shorty.
-  if (!method->IsStatic()) {
-    ++num_ins;
-  }
+  uint32_t shorty_length = 0;
+  const char* shorty = method->GetShorty(&shorty_length);
 
-  uint32_t shorty_len = 0;
-  const char* shorty = method->GetShorty(&shorty_len);
-  for (size_t i = 1; i < shorty_len; ++i) {
-    const char c = shorty[i];
-    ++num_ins;
-    if (c == 'J' || c == 'D') {
+  // Static methods do not include the receiver. The receiver isn't included
+  // in the shorty_length though the return value is.
+  size_t num_ins = method->IsStatic() ? shorty_length - 1 : shorty_length;
+  for (const char* c = shorty + 1; *c != '\0'; ++c) {
+    if (*c == 'J' || *c == 'D') {
       ++num_ins;
     }
   }
-
   return num_ins;
 }
 
@@ -402,7 +406,10 @@
   ObjPtr<mirror::ObjectArray<mirror::Class>> param_types(callsite_type->GetPTypes());
   if (param_types->GetLength() == 1) {
     ObjPtr<mirror::Class> param(param_types->GetWithoutChecks(0));
-    return param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame);
+    // NB Comparing descriptor here as it appears faster in cycle simulation than using:
+    //   param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame)
+    // Costs are 98 vs 173 cycles per invocation.
+    return param->DescriptorEquals("Ldalvik/system/EmulatedStackFrame;");
   }
 
   return false;
@@ -416,35 +423,8 @@
                                      ShadowFrame& shadow_frame,
                                      const uint32_t (&args)[Instruction::kMaxVarArgRegs],
                                      uint32_t first_arg,
-                                     JValue* result,
-                                     const mirror::MethodHandle::Kind handle_kind)
+                                     JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  // For virtual and interface methods ensure called_method points to
-  // the actual method to invoke.
-  if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual ||
-      handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) {
-    uint32_t receiver_reg = is_range ? first_arg : args[0];
-    ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(receiver_reg));
-    if (IsCallerTransformer(callsite_type)) {
-      // The current receiver is an emulated stack frame, the method's
-      // receiver needs to be fetched from there as the emulated frame
-      // will be unpacked into a new frame.
-      receiver = ObjPtr<mirror::EmulatedStackFrame>::DownCast(receiver)->GetReceiver();
-    }
-
-    ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass());
-    if (receiver == nullptr || receiver->GetClass() != declaring_class) {
-      // Verify that _vRegC is an object reference and of the type expected by
-      // the receiver.
-      if (!VerifyObjectIsClass(receiver, declaring_class)) {
-        DCHECK(self->IsExceptionPending());
-        return false;
-      }
-      called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
-          called_method, kRuntimePointerSize);
-    }
-  }
-
   // Compute method information.
   const DexFile::CodeItem* code_item = called_method->GetCodeItem();
 
@@ -513,17 +493,23 @@
           result->SetL(0);
           return false;
         }
-      } else if (!ConvertAndCopyArgumentsFromCallerFrame<is_range>(self,
-                                                                   callsite_type,
-                                                                   target_type,
-                                                                   shadow_frame,
-                                                                   args,
-                                                                   first_arg,
-                                                                   first_dest_reg,
-                                                                   new_shadow_frame)) {
-        DCHECK(self->IsExceptionPending());
-        result->SetL(0);
-        return false;
+      } else {
+        if (!callsite_type->IsConvertible(target_type.Get())) {
+          ThrowWrongMethodTypeException(target_type.Get(), callsite_type.Get());
+          return false;
+        }
+        if (!ConvertAndCopyArgumentsFromCallerFrame<is_range>(self,
+                                                              callsite_type,
+                                                              target_type,
+                                                              shadow_frame,
+                                                              args,
+                                                              first_arg,
+                                                              first_dest_reg,
+                                                              new_shadow_frame)) {
+          DCHECK(self->IsExceptionPending());
+          result->SetL(0);
+          return false;
+        }
       }
     }
   }
@@ -548,13 +534,13 @@
     if (ConvertReturnValue(emulated_stack_type, target_type, &local_result)) {
       emulated_stack_frame->SetReturnValue(self, local_result);
       return true;
-    } else {
-      DCHECK(self->IsExceptionPending());
-      return false;
     }
-  } else {
-    return ConvertReturnValue(callsite_type, target_type, result);
+
+    DCHECK(self->IsExceptionPending());
+    return false;
   }
+
+  return ConvertReturnValue(callsite_type, target_type, result);
 }
 
 template <bool is_range>
@@ -650,98 +636,130 @@
   return klass;
 }
 
+ArtMethod* RefineTargetMethod(Thread* self,
+                              ShadowFrame& shadow_frame,
+                              const mirror::MethodHandle::Kind& handle_kind,
+                              Handle<mirror::MethodType> handle_type,
+                              Handle<mirror::MethodType> callsite_type,
+                              const uint32_t receiver_reg,
+                              ArtMethod* target_method)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual ||
+      handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) {
+    // For virtual and interface methods ensure target_method points to
+    // the actual method to invoke.
+    ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(receiver_reg));
+    if (IsCallerTransformer(callsite_type)) {
+      // The current receiver is an emulated stack frame, the method's
+      // receiver needs to be fetched from there as the emulated frame
+      // will be unpacked into a new frame.
+      receiver = ObjPtr<mirror::EmulatedStackFrame>::DownCast(receiver)->GetReceiver();
+    }
+
+    ObjPtr<mirror::Class> declaring_class(target_method->GetDeclaringClass());
+    if (receiver == nullptr || receiver->GetClass() != declaring_class) {
+      // Verify that _vRegC is an object reference and of the type expected by
+      // the receiver.
+      if (!VerifyObjectIsClass(receiver, declaring_class)) {
+        DCHECK(self->IsExceptionPending());
+        return nullptr;
+      }
+      return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
+          target_method, kRuntimePointerSize);
+    }
+  } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeDirect) {
+    // String constructors are a special case, they are replaced with
+    // StringFactory methods.
+    if (target_method->IsConstructor() && target_method->GetDeclaringClass()->IsStringClass()) {
+      DCHECK(handle_type->GetRType()->IsStringClass());
+      return WellKnownClasses::StringInitToStringFactory(target_method);
+    }
+  } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeSuper) {
+    ObjPtr<mirror::Class> declaring_class = target_method->GetDeclaringClass();
+
+    // Note that we're not dynamically dispatching on the type of the receiver
+    // here. We use the static type of the "receiver" object that we've
+    // recorded in the method handle's type, which will be the same as the
+    // special caller that was specified at the point of lookup.
+    ObjPtr<mirror::Class> referrer_class = handle_type->GetPTypes()->Get(0);
+    if (!declaring_class->IsInterface()) {
+      ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass();
+      uint16_t vtable_index = target_method->GetMethodIndex();
+      DCHECK(super_class != nullptr);
+      DCHECK(super_class->HasVTable());
+      // Note that super_class is a super of referrer_class and target_method
+      // will always be declared by super_class (or one of its super classes).
+      DCHECK_LT(vtable_index, super_class->GetVTableLength());
+      return super_class->GetVTableEntry(vtable_index, kRuntimePointerSize);
+    } else {
+      return referrer_class->FindVirtualMethodForInterfaceSuper(target_method, kRuntimePointerSize);
+    }
+  }
+  return target_method;
+}
+
 template <bool is_range>
-bool DoInvokePolymorphicUnchecked(Thread* self,
-                                  ShadowFrame& shadow_frame,
-                                  Handle<mirror::MethodHandle> method_handle,
-                                  Handle<mirror::MethodType> callsite_type,
-                                  const uint32_t (&args)[Instruction::kMaxVarArgRegs],
-                                  uint32_t first_arg,
-                                  JValue* result)
+bool DoInvokePolymorphicMethod(Thread* self,
+                               ShadowFrame& shadow_frame,
+                               Handle<mirror::MethodHandle> method_handle,
+                               Handle<mirror::MethodType> callsite_type,
+                               const uint32_t (&args)[Instruction::kMaxVarArgRegs],
+                               uint32_t first_arg,
+                               JValue* result)
   REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(self);
   Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
   const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
-  if (IsInvoke(handle_kind)) {
-    // Get the method we're actually invoking along with the kind of
-    // invoke that is desired. We don't need to perform access checks at this
-    // point because they would have been performed on our behalf at the point
-    // of creation of the method handle.
-    ArtMethod* called_method = method_handle->GetTargetMethod();
-    CHECK(called_method != nullptr);
+  DCHECK(IsInvoke(handle_kind));
 
-    if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual ||
-        handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) {
-      // TODO: Unfortunately, we have to postpone dynamic receiver based checks
-      // because the receiver might be cast or might come from an emulated stack
-      // frame, which means that it is unknown at this point. We perform these
-      // checks inside DoCallPolymorphic right before we do the actual invoke.
-    } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeDirect) {
-      // String constructors are a special case, they are replaced with StringFactory
-      // methods.
-      if (called_method->IsConstructor() && called_method->GetDeclaringClass()->IsStringClass()) {
-        DCHECK(handle_type->GetRType()->IsStringClass());
-        called_method = WellKnownClasses::StringInitToStringFactory(called_method);
-      }
-    } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeSuper) {
-      ObjPtr<mirror::Class> declaring_class = called_method->GetDeclaringClass();
+  // Get the method we're actually invoking along with the kind of
+  // invoke that is desired. We don't need to perform access checks at this
+  // point because they would have been performed on our behalf at the point
+  // of creation of the method handle.
+  ArtMethod* target_method = method_handle->GetTargetMethod();
+  uint32_t receiver_reg = is_range ? first_arg: args[0];
+  ArtMethod* called_method = RefineTargetMethod(self,
+                                                shadow_frame,
+                                                handle_kind,
+                                                handle_type,
+                                                callsite_type,
+                                                receiver_reg,
+                                                target_method);
+  if (called_method == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return false;
+  }
 
-      // Note that we're not dynamically dispatching on the type of the receiver
-      // here. We use the static type of the "receiver" object that we've
-      // recorded in the method handle's type, which will be the same as the
-      // special caller that was specified at the point of lookup.
-      ObjPtr<mirror::Class> referrer_class = handle_type->GetPTypes()->Get(0);
-      if (!declaring_class->IsInterface()) {
-        ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass();
-        uint16_t vtable_index = called_method->GetMethodIndex();
-        DCHECK(super_class != nullptr);
-        DCHECK(super_class->HasVTable());
-        // Note that super_class is a super of referrer_class and called_method
-        // will always be declared by super_class (or one of its super classes).
-        DCHECK_LT(vtable_index, super_class->GetVTableLength());
-        called_method = super_class->GetVTableEntry(vtable_index, kRuntimePointerSize);
-      } else {
-        called_method = referrer_class->FindVirtualMethodForInterfaceSuper(
-            called_method, kRuntimePointerSize);
-      }
-      CHECK(called_method != nullptr);
-    }
-    if (IsInvokeTransform(handle_kind)) {
-      // There are two cases here - method handles representing regular
-      // transforms and those representing call site transforms. Method
-      // handles for call site transforms adapt their MethodType to match
-      // the call site. For these, the |callee_type| is the same as the
-      // |callsite_type|. The VarargsCollector is such a tranform, its
-      // method type depends on the call site, ie. x(a) or x(a, b), or
-      // x(a, b, c). The VarargsCollector invokes a variable arity method
-      // with the arity arguments in an array.
-      Handle<mirror::MethodType> callee_type =
-          (handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform) ? callsite_type
-          : handle_type;
-      return DoCallTransform<is_range>(called_method,
+  if (IsInvokeTransform(handle_kind)) {
+    // There are two cases here - method handles representing regular
+    // transforms and those representing call site transforms. Method
+    // handles for call site transforms adapt their MethodType to match
+    // the call site. For these, the |callee_type| is the same as the
+    // |callsite_type|. The VarargsCollector is such a tranform, its
+    // method type depends on the call site, ie. x(a) or x(a, b), or
+    // x(a, b, c). The VarargsCollector invokes a variable arity method
+    // with the arity arguments in an array.
+    Handle<mirror::MethodType> callee_type =
+        (handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform) ? callsite_type
+        : handle_type;
+    return DoCallTransform<is_range>(called_method,
+                                     callsite_type,
+                                     callee_type,
+                                     self,
+                                     shadow_frame,
+                                     method_handle /* receiver */,
+                                     args,
+                                     first_arg,
+                                     result);
+  } else {
+    return DoCallPolymorphic<is_range>(called_method,
                                        callsite_type,
-                                       callee_type,
+                                       handle_type,
                                        self,
                                        shadow_frame,
-                                       method_handle /* receiver */,
                                        args,
                                        first_arg,
                                        result);
-
-    } else {
-      return DoCallPolymorphic<is_range>(called_method,
-                                         callsite_type,
-                                         handle_type,
-                                         self,
-                                         shadow_frame,
-                                         args,
-                                         first_arg,
-                                         result,
-                                         handle_kind);
-    }
-  } else {
-    LOG(FATAL) << "Unreachable: " << handle_kind;
-    UNREACHABLE();
   }
 }
 
@@ -948,55 +966,30 @@
   ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
   CHECK(handle_type != nullptr);
 
-  if (!IsInvokeTransform(handle_kind)) {
-    if (UNLIKELY(!IsCallerTransformer(callsite_type) &&
-                 !callsite_type->IsConvertible(handle_type.Ptr()))) {
+  if (IsFieldAccess(handle_kind)) {
+    DCHECK(!callsite_type->IsExactMatch(handle_type.Ptr()));
+    if (!callsite_type->IsConvertible(handle_type.Ptr())) {
       ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get());
       return false;
     }
+    const bool do_convert = true;
+    return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
+        self,
+        shadow_frame,
+        method_handle,
+        callsite_type,
+        args,
+        first_arg,
+        result);
   }
 
-  if (IsFieldAccess(handle_kind)) {
-    if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) {
-      const bool do_convert = false;
-      return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
-          self,
-          shadow_frame,
-          method_handle,
-          callsite_type,
-          args,
-          first_arg,
-          result);
-    } else {
-      const bool do_convert = true;
-      return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
-          self,
-          shadow_frame,
-          method_handle,
-          callsite_type,
-          args,
-          first_arg,
-          result);
-    }
-  }
-
-  if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) {
-    return DoInvokePolymorphicUnchecked<is_range>(self,
-                                                  shadow_frame,
-                                                  method_handle,
-                                                  callsite_type,
-                                                  args,
-                                                  first_arg,
-                                                  result);
-  } else {
-    return DoInvokePolymorphicUnchecked<is_range>(self,
-                                                  shadow_frame,
-                                                  method_handle,
-                                                  callsite_type,
-                                                  args,
-                                                  first_arg,
-                                                  result);
-  }
+  return DoInvokePolymorphicMethod<is_range>(self,
+                                             shadow_frame,
+                                             method_handle,
+                                             callsite_type,
+                                             args,
+                                             first_arg,
+                                             result);
 }
 
 template <bool is_range>
@@ -1008,32 +1001,9 @@
                               uint32_t first_arg,
                               JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  // We need to check the nominal type of the handle in addition to the
-  // real type. The "nominal" type is present when MethodHandle.asType is
-  // called any handle, and results in the declared type of the handle
-  // changing.
-  ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType());
-  if (UNLIKELY(nominal_type != nullptr)) {
-    if (UNLIKELY(!callsite_type->IsExactMatch(nominal_type.Ptr()))) {
-      ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
-      return false;
-    }
-    return DoInvokePolymorphicNonExact<is_range>(self,
-                                                 shadow_frame,
-                                                 method_handle,
-                                                 callsite_type,
-                                                 args,
-                                                 first_arg,
-                                                 result);
-  }
-
-  ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
-  if (UNLIKELY(!callsite_type->IsExactMatch(handle_type.Ptr()))) {
-    ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get());
-    return false;
-  }
-
+  StackHandleScope<1> hs(self);
   const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
+  Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType()));
   if (IsFieldAccess(handle_kind)) {
     const bool do_convert = false;
     return DoInvokePolymorphicFieldAccess<is_range, do_convert>(
@@ -1046,13 +1016,68 @@
         result);
   }
 
-  return DoInvokePolymorphicUnchecked<is_range>(self,
+  // Slow-path check.
+  if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) {
+    return DoInvokePolymorphicMethod<is_range>(self,
+                                               shadow_frame,
+                                               method_handle,
+                                               callsite_type,
+                                               args,
+                                               first_arg,
+                                               result);
+  }
+
+  // On the fast-path. This is equivalent to DoCallPolymoprhic without the conversion paths.
+  ArtMethod* target_method = method_handle->GetTargetMethod();
+  uint32_t receiver_reg = is_range ? first_arg : args[0];
+  ArtMethod* called_method = RefineTargetMethod(self,
                                                 shadow_frame,
-                                                method_handle,
+                                                handle_kind,
+                                                method_handle_type,
                                                 callsite_type,
-                                                args,
-                                                first_arg,
-                                                result);
+                                                receiver_reg,
+                                                target_method);
+  if (called_method == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return false;
+  }
+
+  // Compute method information.
+  const DexFile::CodeItem* code_item = called_method->GetCodeItem();
+  uint16_t num_regs;
+  size_t num_input_regs;
+  size_t first_dest_reg;
+  if (LIKELY(code_item != nullptr)) {
+    num_regs = code_item->registers_size_;
+    first_dest_reg = num_regs - code_item->ins_size_;
+    num_input_regs = code_item->ins_size_;
+    // Parameter registers go at the end of the shadow frame.
+    DCHECK_NE(first_dest_reg, (size_t)-1);
+  } else {
+    // No local regs for proxy and native methods.
+    DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
+    num_regs = num_input_regs = GetInsForProxyOrNativeMethod(called_method);
+    first_dest_reg = 0;
+  }
+
+  // Allocate shadow frame on the stack.
+  const char* old_cause = self->StartAssertNoThreadSuspension("DoCallCommon");
+  ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+      CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
+  ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
+  CopyArgumentsFromCallerFrame<is_range>(shadow_frame,
+                                         new_shadow_frame,
+                                         args,
+                                         first_arg,
+                                         first_dest_reg,
+                                         num_input_regs);
+  self->EndAssertNoThreadSuspension(old_cause);
+
+  PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
+  if (self->IsExceptionPending()) {
+    return false;
+  }
+  return true;
 }
 
 }  // namespace
@@ -1067,7 +1092,35 @@
                          uint32_t first_arg,
                          JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::MethodType> method_handle_type = method_handle->GetMethodType();
   if (IsMethodHandleInvokeExact(invoke_method)) {
+    // We need to check the nominal type of the handle in addition to the
+    // real type. The "nominal" type is present when MethodHandle.asType is
+    // called any handle, and results in the declared type of the handle
+    // changing.
+    ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType());
+    if (UNLIKELY(nominal_type != nullptr)) {
+      if (UNLIKELY(!callsite_type->IsExactMatch(nominal_type.Ptr()))) {
+        ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get());
+        return false;
+      }
+
+      if (LIKELY(!nominal_type->IsExactMatch(method_handle_type.Ptr()))) {
+        // Different nominal type means we have to treat as non-exact.
+        return DoInvokePolymorphicNonExact<is_range>(self,
+                                                     shadow_frame,
+                                                     method_handle,
+                                                     callsite_type,
+                                                     args,
+                                                     first_arg,
+                                                     result);
+      }
+    }
+
+    if (!callsite_type->IsExactMatch(method_handle_type.Ptr())) {
+      ThrowWrongMethodTypeException(method_handle_type.Ptr(), callsite_type.Get());
+      return false;
+    }
     return DoInvokePolymorphicExact<is_range>(self,
                                               shadow_frame,
                                               method_handle,
@@ -1076,6 +1129,16 @@
                                               first_arg,
                                               result);
   } else {
+    if (UNLIKELY(callsite_type->IsExactMatch(method_handle_type.Ptr()))) {
+      // A non-exact invoke that can be invoked exactly.
+      return DoInvokePolymorphicExact<is_range>(self,
+                                                shadow_frame,
+                                                method_handle,
+                                                callsite_type,
+                                                args,
+                                                first_arg,
+                                                result);
+    }
     return DoInvokePolymorphicNonExact<is_range>(self,
                                                  shadow_frame,
                                                  method_handle,