MethodHandles: change asType() / invoke() implementation

This CL changes how we deal with non-exact invokes and asType().

Specifically:

1. It drops the concept of `nominalType` from our implementation and
   adds an AsTypeAdapter transformer for performing pairwise
   conversions. This means asType() conversions are orthogonal to other
   transforms and easier to reason about.

2. It switches to use the asTypeCache to reduce the cost of non-exact
   invokes.

3. It adds MethodType::IsInPlaceConvertible in the fast-path for
   MethodHandle.invoke() to avoid an asTypeAdapter transformer when the
   required conversions can be performed in-place with no-ops. This is
   possible because the ShadowFrame deals in 32-bit vregs so
   conversions like byte -> int are no-ops.

Bug: 207844518
Test: art/test.py --host
Test: atest CtsLibcoreTestCases

Change-Id: I2faf260b164767104c7ae0bc061fbf4f6579bbff
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 2400f9f..a789831 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -743,10 +743,10 @@
   MethodHandleOffsets() : CheckOffsets<mirror::MethodHandle>(
       false, "Ljava/lang/invoke/MethodHandle;") {
     addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, art_field_or_method_), "artFieldOrMethod");
+    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, as_type_cache_), "asTypeCache");
     addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, cached_spread_invoker_),
               "cachedSpreadInvoker");
     addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, handle_kind_), "handleKind");
-    addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, nominal_type_), "nominalType");
     addOffset(OFFSETOF_MEMBER(mirror::MethodHandle, method_type_), "type");
   }
 };
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 76ad139..aa456f5 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -423,7 +423,6 @@
 static inline bool MethodHandleInvokeMethod(ArtMethod* called_method,
                                             Handle<mirror::MethodType> callsite_type,
                                             Handle<mirror::MethodType> target_type,
-                                            Handle<mirror::MethodType> nominal_type,
                                             Thread* self,
                                             ShadowFrame& shadow_frame,
                                             const InstructionOperands* const operands,
@@ -545,12 +544,6 @@
     DCHECK(self->IsExceptionPending());
     return false;
   }
-
-  if (nominal_type != nullptr) {
-    return ConvertReturnValue(nominal_type, target_type, result) &&
-        ConvertReturnValue(callsite_type, nominal_type, result);
-  }
-
   return ConvertReturnValue(callsite_type, target_type, result);
 }
 
@@ -722,7 +715,6 @@
   REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<2> hs(self);
   Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
-  Handle<mirror::MethodType> nominal_handle_type(hs.NewHandle(method_handle->GetNominalType()));
   const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
   DCHECK(IsInvoke(handle_kind));
 
@@ -768,7 +760,6 @@
     return MethodHandleInvokeMethod(called_method,
                                     callsite_type,
                                     handle_type,
-                                    nominal_handle_type,
                                     self,
                                     shadow_frame,
                                     operands,
@@ -896,7 +887,6 @@
   return field_value;
 }
 
-template <bool do_conversions>
 bool MethodHandleFieldAccess(Thread* self,
                              ShadowFrame& shadow_frame,
                              Handle<mirror::MethodHandle> method_handle,
@@ -904,7 +894,6 @@
                              const InstructionOperands* const operands,
                              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();
   ArtField* field = method_handle->GetTargetField();
   Primitive::Type field_type = field->GetTypeAsPrimitiveType();
@@ -913,10 +902,6 @@
       size_t obj_reg = operands->GetOperand(0);
       ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
       MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result);
-      if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) {
-        DCHECK(self->IsExceptionPending());
-        return false;
-      }
       return true;
     }
     case mirror::MethodHandle::kStaticGet: {
@@ -926,10 +911,6 @@
         return false;
       }
       MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result);
-      if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) {
-        DCHECK(self->IsExceptionPending());
-        return false;
-      }
       return true;
     }
     case mirror::MethodHandle::kInstancePut: {
@@ -942,13 +923,6 @@
           shadow_frame,
           callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(),
           value_reg);
-      if (do_conversions && !ConvertArgumentValue(callsite_type,
-                                                  handle_type,
-                                                  kPTypeIndex,
-                                                  &value)) {
-        DCHECK(self->IsExceptionPending());
-        return false;
-      }
       ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
       return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value);
     }
@@ -966,13 +940,6 @@
           shadow_frame,
           callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(),
           value_reg);
-      if (do_conversions && !ConvertArgumentValue(callsite_type,
-                                                  handle_type,
-                                                  kPTypeIndex,
-                                                  &value)) {
-        DCHECK(self->IsExceptionPending());
-        return false;
-      }
       return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value);
     }
     default:
@@ -1016,23 +983,11 @@
 
 bool DoVarHandleInvokeTranslation(Thread* self,
                                   ShadowFrame& shadow_frame,
-                                  bool invokeExact,
                                   Handle<mirror::MethodHandle> method_handle,
                                   Handle<mirror::MethodType> callsite_type,
                                   const InstructionOperands* const operands,
                                   JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (!invokeExact) {
-    // Exact invokes are checked for compatability higher up. The
-    // non-exact invoke path doesn't have a similar check due to
-    // transformers which have EmulatedStack frame arguments with the
-    // actual method type associated with the frame.
-    if (UNLIKELY(!callsite_type->IsConvertible(method_handle->GetMethodType()))) {
-      ThrowWrongMethodTypeException(method_handle->GetMethodType(), callsite_type.Get());
-      return false;
-    }
-  }
-
   //
   // Basic checks that apply in all cases.
   //
@@ -1098,47 +1053,6 @@
                                                result);
 }
 
-static inline bool MethodHandleInvokeInternal(Thread* self,
-                                              ShadowFrame& shadow_frame,
-                                              Handle<mirror::MethodHandle> method_handle,
-                                              Handle<mirror::MethodType> callsite_type,
-                                              const InstructionOperands* const operands,
-                                              JValue* result)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
-  if (IsFieldAccess(handle_kind)) {
-    ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType());
-    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 MethodHandleFieldAccess<do_convert>(
-        self,
-        shadow_frame,
-        method_handle,
-        callsite_type,
-        operands,
-        result);
-  }
-  if (IsInvokeVarHandle(handle_kind)) {
-    return DoVarHandleInvokeTranslation(self,
-                                        shadow_frame,
-                                        /*invokeExact=*/ false,
-                                        method_handle,
-                                        callsite_type,
-                                        operands,
-                                        result);
-  }
-  return DoInvokePolymorphicMethod(self,
-                                   shadow_frame,
-                                   method_handle,
-                                   callsite_type,
-                                   operands,
-                                   result);
-}
-
 static inline bool MethodHandleInvokeExactInternal(
     Thread* self,
     ShadowFrame& shadow_frame,
@@ -1149,20 +1063,20 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(self);
   Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType()));
+  const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
+
   if (!callsite_type->IsExactMatch(method_handle_type.Get())) {
     ThrowWrongMethodTypeException(method_handle_type.Get(), callsite_type.Get());
     return false;
   }
 
-  const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
   if (IsFieldAccess(handle_kind)) {
-    const bool do_convert = false;
-    return MethodHandleFieldAccess<do_convert>(self,
-                                               shadow_frame,
-                                               method_handle,
-                                               callsite_type,
-                                               operands,
-                                               result);
+    return MethodHandleFieldAccess(self,
+                                   shadow_frame,
+                                   method_handle,
+                                   callsite_type,
+                                   operands,
+                                   result);
   }
 
   // Slow-path check.
@@ -1177,7 +1091,6 @@
   } else if (IsInvokeVarHandle(handle_kind)) {
     return DoVarHandleInvokeTranslation(self,
                                         shadow_frame,
-                                        /*invokeExact=*/ true,
                                         method_handle,
                                         callsite_type,
                                         operands,
@@ -1310,14 +1223,50 @@
                    Handle<mirror::MethodType> callsite_type,
                    const InstructionOperands* const operands,
                    JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (UNLIKELY(callsite_type->IsExactMatch(method_handle->GetMethodType()))) {
-      // A non-exact invoke that can be invoked exactly.
-      return MethodHandleInvokeExactInternal(
-          self, shadow_frame, method_handle, callsite_type, operands, result);
-    } else {
-      return MethodHandleInvokeInternal(
-          self, shadow_frame, method_handle, callsite_type, operands, result);
+    StackHandleScope<2> hs(self);
+    Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType()));
+
+    // Non-exact invoke behaves as calling mh.asType(newType). In ART, asType() is implemented
+    // as a transformer and it is expensive to call so check first if it's really necessary.
+    //
+    // There are two cases where the asType() transformation can be skipped:
+    //
+    // 1) the call site and type of the MethodHandle match, ie code is calling invoke()
+    //    unnecessarily.
+    //
+    // 2) when the call site can be trivally converted to the MethodHandle type due to how
+    //    values are represented in the ShadowFrame, ie all registers in the shadow frame are
+    //    32-bit, there is no byte, short, char, etc. So a call site with arguments of these
+    //    kinds can be trivially converted to one with int arguments. Similarly if the reference
+    //    types are assignable between the call site and MethodHandle type, then as asType()
+    //    transformation isn't really doing any work.
+    if (callsite_type->IsInPlaceConvertible(method_handle_type.Get())) {
+      return MethodHandleInvokeExact(
+          self, shadow_frame, method_handle, method_handle_type, operands, result);
     }
+
+    // Use asType() variant of this MethodHandle to adapt callsite to the target.
+    MutableHandle<mirror::MethodHandle> atc(hs.NewHandle(method_handle->GetAsTypeCache()));
+    if (atc == nullptr || !callsite_type->IsExactMatch(atc->GetMethodType())) {
+      // Cached asType adapter does not exist or is for another call site. Call
+      // MethodHandle::asType() to get an appropriate adapter.
+      ArtMethod* as_type =
+          jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_asType);
+      uint32_t as_type_args[] = {
+          static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_handle.Get())),
+          static_cast<uint32_t>(reinterpret_cast<uintptr_t>(callsite_type.Get()))};
+      JValue atc_result;
+      as_type->Invoke(self, as_type_args, sizeof(as_type_args), &atc_result, "LL");
+      if (atc_result.GetL() == nullptr) {
+        DCHECK(self->IsExceptionPending());
+        return false;
+      }
+      ObjPtr<mirror::MethodHandle> atc_method_handle =
+          down_cast<mirror::MethodHandle*>(atc_result.GetL());
+      atc.Assign(atc_method_handle);
+      DCHECK(!atc.IsNull());
+    }
+    return MethodHandleInvokeExact(self, shadow_frame, atc, callsite_type, operands, result);
   };
   return InvokeInContext(
       self, shadow_frame, method_handle, callsite_type, operands, result, invoke);
@@ -1336,22 +1285,6 @@
                    Handle<mirror::MethodType> callsite_type,
                    const InstructionOperands* const operands,
                    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;
-      }
-      if (LIKELY(!nominal_type->IsExactMatch(method_handle->GetMethodType()))) {
-        // Different nominal type means we have to treat as non-exact.
-        return MethodHandleInvokeInternal(
-            self, shadow_frame, method_handle, callsite_type, operands, result);
-      }
-    }
     return MethodHandleInvokeExactInternal(
         self, shadow_frame, method_handle, callsite_type, operands, result);
   };
diff --git a/runtime/mirror/method_handle_impl-inl.h b/runtime/mirror/method_handle_impl-inl.h
index 27ccc53..8c5e479 100644
--- a/runtime/mirror/method_handle_impl-inl.h
+++ b/runtime/mirror/method_handle_impl-inl.h
@@ -25,12 +25,31 @@
 namespace art {
 namespace mirror {
 
-inline ObjPtr<mirror::MethodType> MethodHandle::GetMethodType() {
+inline MethodHandle::Kind MethodHandle::GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) {
+  const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_));
+  DCHECK(handle_kind >= 0 && handle_kind <= static_cast<int32_t>(Kind::kLastValidKind));
+  return static_cast<Kind>(handle_kind);
+}
+
+inline ObjPtr<mirror::MethodType> MethodHandle::GetMethodType()
+    REQUIRES_SHARED(Locks::mutator_lock_) {
   return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_));
 }
 
-inline ObjPtr<mirror::MethodType> MethodHandle::GetNominalType() {
-  return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, nominal_type_));
+inline ObjPtr<mirror::MethodHandle> MethodHandle::GetAsTypeCache()
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return GetFieldObject<mirror::MethodHandle>(
+      OFFSET_OF_OBJECT_MEMBER(MethodHandle, as_type_cache_));
+}
+
+inline ArtField* MethodHandle::GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return reinterpret_cast<ArtField*>(
+      GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
+}
+
+inline ArtMethod* MethodHandle::GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
+  return reinterpret_cast<ArtMethod*>(
+      GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc
index 4f1a18b..79ed2b0 100644
--- a/runtime/mirror/method_handle_impl.cc
+++ b/runtime/mirror/method_handle_impl.cc
@@ -36,8 +36,8 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   CHECK(!Runtime::Current()->IsActiveTransaction());
   SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr);
-  SetFieldObject<false>(NominalTypeOffset(), nullptr);
   SetFieldObject<false>(MethodTypeOffset(), method_type.Get());
+  SetFieldObject<false>(AsTypeCacheOffset(), nullptr);
   SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind));
   SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method);
 }
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 6ba6e25..0a70422 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -63,26 +63,15 @@
     kLastInvokeKind = kInvokeVarHandleExact
   };
 
-  Kind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) {
-    const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_));
-    DCHECK(handle_kind >= 0 &&
-           handle_kind <= static_cast<int32_t>(Kind::kLastValidKind));
-    return static_cast<Kind>(handle_kind);
-  }
+  Kind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ALWAYS_INLINE ObjPtr<mirror::MethodType> GetMethodType() REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<mirror::MethodType> GetMethodType() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ALWAYS_INLINE ObjPtr<mirror::MethodType> GetNominalType() REQUIRES_SHARED(Locks::mutator_lock_);
+  ObjPtr<mirror::MethodHandle> GetAsTypeCache() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return reinterpret_cast<ArtField*>(
-        GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
-  }
+  ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
-    return reinterpret_cast<ArtMethod*>(
-        GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
-  }
+  ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Gets the return type for a named invoke method, or nullptr if the invoke method is not
   // supported.
@@ -97,8 +86,8 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
+  HeapReference<mirror::MethodHandle> as_type_cache_;
   HeapReference<mirror::MethodHandle> cached_spread_invoker_;
-  HeapReference<mirror::MethodType> nominal_type_;
   HeapReference<mirror::MethodType> method_type_;
   uint32_t handle_kind_;
   uint64_t art_field_or_method_;
@@ -107,8 +96,8 @@
   static MemberOffset CachedSpreadInvokerOffset() {
     return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_));
   }
-  static MemberOffset NominalTypeOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_));
+  static MemberOffset AsTypeCacheOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(MethodHandle, as_type_cache_));
   }
   static MemberOffset MethodTypeOffset() {
     return MemberOffset(OFFSETOF_MEMBER(MethodHandle, method_type_));
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 7e6ae45..ccc2b9d 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -161,6 +161,57 @@
   return true;
 }
 
+static bool IsParameterInPlaceConvertible(ObjPtr<Class> from, ObjPtr<Class> to)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (from == to) {
+    return true;
+  }
+
+  if (from->IsPrimitive() != to->IsPrimitive()) {
+    return false;  // No in-place conversion from place conversion for box/unboxing.
+  }
+
+  if (from->IsPrimitive()) {
+    // `from` and `to` are both primitives. The supported in-place conversions use a 32-bit
+    // interpreter representation and are a subset of permitted conversions for MethodHandles.
+    // Conversions are documented in JLS 11 S5.1.2 "Widening Primitive Conversion".
+    Primitive::Type src = from->GetPrimitiveType();
+    Primitive::Type dst = to->GetPrimitiveType();
+    switch (src) {
+      case Primitive::Type::kPrimByte:
+        return dst == Primitive::Type::kPrimShort || dst == Primitive::Type::kPrimInt;
+      case Primitive::Type::kPrimChar:
+        FALLTHROUGH_INTENDED;
+      case Primitive::Type::kPrimShort:
+        return dst == Primitive::Type::kPrimInt;
+      default:
+        return false;
+    }
+  }
+
+  // `from` and `to` are both references, apply an assignability check.
+  return to->IsAssignableFrom(from);
+}
+
+bool MethodType::IsInPlaceConvertible(ObjPtr<MethodType> target) {
+  const ObjPtr<ObjectArray<Class>> ptypes = GetPTypes();
+  const ObjPtr<ObjectArray<Class>> target_ptypes = target->GetPTypes();
+  const int32_t ptypes_length = ptypes->GetLength();
+  if (ptypes_length != target_ptypes->GetLength()) {
+    return false;
+  }
+
+  for (int32_t i = 0; i < ptypes_length; ++i) {
+    if (!IsParameterInPlaceConvertible(ptypes->GetWithoutChecks(i),
+                                       target_ptypes->GetWithoutChecks(i))) {
+      return false;
+    }
+  }
+
+  return GetRType()->IsPrimitiveVoid() ||
+         IsParameterInPlaceConvertible(target->GetRType(), GetRType());
+}
+
 std::string MethodType::PrettyDescriptor() {
   std::ostringstream ss;
   ss << "(";
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 872c07b..19444bb 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -67,6 +67,11 @@
   // iff. they have convertible return types and parameter types.
   bool IsConvertible(ObjPtr<MethodType> target) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns true iff. |this| can be converted to match |target| method type within the
+  // current frame of the current MethodType. This limits conversions to assignability check
+  // for references and between scalar 32-bit types.
+  bool IsInPlaceConvertible(ObjPtr<MethodType> target) REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns the pretty descriptor for this method type, suitable for display in
   // exception messages and the like.
   std::string PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc
index 742960b..c9d7e91 100644
--- a/runtime/mirror/method_type_test.cc
+++ b/runtime/mirror/method_type_test.cc
@@ -38,32 +38,39 @@
   return "Ljava/lang/" + shorthand + ";";
 }
 
+ObjPtr<mirror::Class> FindClass(Thread* self, ClassLinker* const cl, const std::string& shorthand)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+  if (shorthand.size() == 1) {
+    return cl->FindSystemClass(self, shorthand.c_str());
+  } else if (shorthand.find('/') == std::string::npos) {
+    return cl->FindClass(self, FullyQualifiedType(shorthand).c_str(), boot_class_loader);
+  } else {
+    return cl->FindClass(self, shorthand.c_str(), boot_class_loader);
+  }
+}
+
 static ObjPtr<mirror::MethodType> CreateMethodType(const std::string& return_type,
                                                    const std::vector<std::string>& param_types) {
-  CHECK_LT(param_types.size(), 3u);
-
   Runtime* const runtime = Runtime::Current();
   ClassLinker* const class_linker = runtime->GetClassLinker();
   Thread* const self = Thread::Current();
 
   ScopedObjectAccess soa(self);
-  StackHandleScope<5> hs(soa.Self());
+  StackHandleScope<2> hs(soa.Self());
 
-  Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
-
-  Handle<mirror::Class> return_clazz = hs.NewHandle(class_linker->FindClass(
-          soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader));
+  Handle<mirror::Class> return_clazz = hs.NewHandle(FindClass(self, class_linker, return_type));
   CHECK(return_clazz != nullptr);
 
   ObjPtr<mirror::Class> class_array_type =
       GetClassRoot<mirror::ObjectArray<mirror::Class>>(class_linker);
   Handle<mirror::ObjectArray<mirror::Class>> param_classes = hs.NewHandle(
       mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, param_types.size()));
-
   for (uint32_t i = 0; i < param_types.size(); ++i) {
-    Handle<mirror::Class> param = hs.NewHandle(class_linker->FindClass(
-        soa.Self(), FullyQualifiedType(param_types[i]).c_str(), boot_class_loader));
-    param_classes->Set(i, param.Get());
+    ObjPtr<mirror::Class> param = FindClass(self, class_linker, param_types[i]);
+    CHECK(!param.IsNull());
+    param_classes->Set(i, param);
   }
 
   return mirror::MethodType::Create(self, return_clazz, param_classes);
@@ -105,5 +112,100 @@
   }
 }
 
+TEST_F(MethodTypeTest, IsInPlaceConvertible) {
+  ScopedObjectAccess soa(Thread::Current());
+
+  // Call site has void return type, value is discarded.
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "Integer" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("String", { "Integer" }));
+    ASSERT_TRUE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+
+  // MethodHandle has void return type, value is required
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("String", { "Integer" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "Integer" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+
+  // Assignable Reference Types
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("Object", { "Integer" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("String", { "Object" }));
+    ASSERT_TRUE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+
+  // Not assignable Reference Types
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("Integer", { "Object" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("Object", { "String" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+
+  // Widenable primitives
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("I", { "B", "C", "S" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("S", { "I", "I", "I" }));
+    ASSERT_TRUE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+
+  // Non-widenable primitives
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "Z" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "I" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "I" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "Z" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "S" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "C" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+    ASSERT_FALSE(mh->IsInPlaceConvertible(cs.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "C" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "S" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "I" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "J" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "F" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "D" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("V", { "D" }));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("V", { "F" }));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+  {
+    StackHandleScope<2> hs(soa.Self());
+    Handle<mirror::MethodType> cs = hs.NewHandle(CreateMethodType("I", {}));
+    Handle<mirror::MethodType> mh = hs.NewHandle(CreateMethodType("Z", {}));
+    ASSERT_FALSE(cs->IsInPlaceConvertible(mh.Get()));
+  }
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
index 45d9a8a..00ce01f 100644
--- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc
+++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc
@@ -23,7 +23,7 @@
 #include "jni/jni_internal.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
-#include "mirror/method_handle_impl.h"
+#include "mirror/method_handle_impl-inl.h"
 #include "native_util.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 4bd34c9..62e405a 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -104,6 +104,7 @@
 jmethodID WellKnownClasses::java_lang_Float_floatToRawIntBits;
 jmethodID WellKnownClasses::java_lang_Float_valueOf;
 jmethodID WellKnownClasses::java_lang_Integer_valueOf;
+jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_asType;
 jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
 jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
 jmethodID WellKnownClasses::java_lang_Long_valueOf;
@@ -401,6 +402,7 @@
   java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
   java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
   java_lang_Daemons_waitForDaemonStart = CacheMethod(env, java_lang_Daemons, true, "waitForDaemonStart", "()V");
+  java_lang_invoke_MethodHandle_asType = CacheMethod(env, "java/lang/invoke/MethodHandle", false, "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
   java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
   java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
 
@@ -601,6 +603,7 @@
   java_lang_Float_floatToRawIntBits = nullptr;
   java_lang_Float_valueOf = nullptr;
   java_lang_Integer_valueOf = nullptr;
+  java_lang_invoke_MethodHandle_asType = nullptr;
   java_lang_invoke_MethodHandles_lookup = nullptr;
   java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr;
   java_lang_Long_valueOf = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 41801e1..67a47f0 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -117,6 +117,7 @@
   static jmethodID java_lang_Float_floatToRawIntBits;
   static jmethodID java_lang_Float_valueOf;
   static jmethodID java_lang_Integer_valueOf;
+  static jmethodID java_lang_invoke_MethodHandle_asType;
   static jmethodID java_lang_invoke_MethodHandles_lookup;
   static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor;
   static jmethodID java_lang_Long_valueOf;
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index cbe6fe9..168862c 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -1922,4 +1922,46 @@
       System.out.println("Got expected IAE when invoke-special on an abstract interface method");
     }
   }
+
+  private static int returnInput(int value) { return value; }
+  private static byte returnInput(byte value) { return value; }
+  private static char returnInput(char value) { return value; }
+
+  private static void testFastInvoke() throws Throwable {
+    // This tests use of invoke() that have different types and require widening, but do not
+    // require require an explicit asType() transform.
+    MethodHandle mh0 =
+        MethodHandles.lookup().findStatic(
+            Main.class, "returnInput", MethodType.methodType(int.class, int.class));
+    assertEquals((byte) 127, (byte) (int) mh0.invoke((byte) 127));
+    assertEquals((byte) -128, (byte) (int) mh0.invoke((byte) -128));
+    assertEquals((short) 127, (short) (int) mh0.invoke((byte) 127));
+    assertEquals((short) -128, (short) (int) mh0.invoke((byte) -128));
+    assertEquals((char) 127, (char) (int) mh0.invoke((byte) 127));
+    assertEquals((char) 65535, (char) (int) mh0.invoke((byte) -1));
+    assertEquals((char) 0, (char) (int) mh0.invoke((char) 0));
+    assertEquals((char) 65535, (char) (int) mh0.invoke((char) 65535));
+    assertEquals((short) 127, (short) (int) mh0.invoke((short) 127));
+    assertEquals((short) -128, (short) (int) mh0.invoke((short) -128));
+    assertEquals((int) 127, (int) mh0.invoke((byte) 127));
+    assertEquals((int) -128, (int) mh0.invoke((byte) -128));
+    assertEquals((int) 127, (int) mh0.invoke((short) 127));
+    assertEquals((int) -128, (int) mh0.invoke((short) -128));
+    assertEquals((int) 0, (int) mh0.invoke((char) 0));
+    assertEquals((int) 65535, (int) mh0.invoke((char) 65535));
+
+    MethodHandle mh1 =
+        MethodHandles.lookup().findStatic(
+            Main.class, "returnInput", MethodType.methodType(char.class, char.class));
+    assertEquals((int) 0, (int) mh1.invoke((char) 0));
+    assertEquals((int) 65535, (int) mh1.invoke((char) 65535));
+
+    MethodHandle mh2 =
+        MethodHandles.lookup().findStatic(
+            Main.class, "returnInput", MethodType.methodType(byte.class, byte.class));
+    assertEquals((int) -128, (int) mh2.invoke((byte) -128));
+    assertEquals((int) 127, (int) mh2.invoke((byte) 127));
+    assertEquals((short) -128, (short) mh2.invoke((byte) -128));
+    assertEquals((short) 127, (short) mh2.invoke((byte) 127));
+  }
 }