ART: Compiler support for invoke-polymorphic.

Adds basic support to invoke method handles in compiled code.

Enables method verification for methods containing invoke-polymorphic.

Adds k45cc/k45rc output to Instruction::DumpString() which
was found to be missing when enabling verification.

Include stack traces in test 957-methodhandle-transforms for
failures so they can be easily identified.

Bug: 30550796,33191393
Test: art/test/run-test 953
Test: m test-art-run-test
Change-Id: Ic9a96ea24906087597d96ad8159a5bc349d06950
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 64030f3..2d0932a 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -109,8 +109,13 @@
 extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
 extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
+
 extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*);
 
+// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or
+// reference return type.
+extern "C" void art_quick_invoke_polymorphic(uint32_t, void*);
+
 // Thread entrypoints.
 extern "C" void art_quick_test_suspend();
 
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 78dad94..8ce61c1 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -106,6 +106,7 @@
       art_quick_invoke_super_trampoline_with_access_check;
   qpoints->pInvokeVirtualTrampolineWithAccessCheck =
       art_quick_invoke_virtual_trampoline_with_access_check;
+  qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
 
   // Thread
   qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 0911aeb..4d5d6de 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -133,6 +133,7 @@
   V(InvokeStaticTrampolineWithAccessCheck, void, uint32_t, void*) \
   V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \
   V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \
+  V(InvokePolymorphic, void, uint32_t, void*) \
 \
   V(TestSuspend, void, void) \
 \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index bf1d4ea..bbd4fba 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -27,10 +27,12 @@
 #include "imtable-inl.h"
 #include "interpreter/interpreter.h"
 #include "linear_alloc.h"
+#include "method_handles.h"
 #include "method_reference.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/method.h"
+#include "mirror/method_handle_impl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "oat_quick_method_header.h"
@@ -39,6 +41,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
 #include "debugger.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -2391,4 +2394,114 @@
                                 reinterpret_cast<uintptr_t>(method));
 }
 
+// Returns shorty type so the caller can determine how to put |result|
+// into expected registers. The shorty type is static so the compiler
+// could call different flavors of this code path depending on the
+// shorty type though this would require different entry points for
+// each type.
+extern "C" uintptr_t artInvokePolymorphic(
+    JValue* result,
+    mirror::Object* raw_method_handle,
+    Thread* self,
+    ArtMethod** sp)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveRefsAndArgs));
+
+  // Start new JNI local reference state
+  JNIEnvExt* env = self->GetJniEnv();
+  ScopedObjectAccessUnchecked soa(env);
+  ScopedJniEnvLocalRefState env_state(env);
+  const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe.");
+
+  // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC.
+  ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
+  uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+  const DexFile::CodeItem* code = caller_method->GetCodeItem();
+  const Instruction* inst = Instruction::At(&code->insns_[dex_pc]);
+  DCHECK(inst->Opcode() == Instruction::INVOKE_POLYMORPHIC ||
+         inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+  const DexFile* dex_file = caller_method->GetDexFile();
+  const uint32_t proto_idx = inst->VRegH();
+  const char* shorty = dex_file->GetShorty(proto_idx);
+  const size_t shorty_length = strlen(shorty);
+  static const bool kMethodIsStatic = false;  // invoke() and invokeExact() are not static.
+  RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa);
+  gc_visitor.Visit();
+
+  // Wrap raw_method_handle in a Handle for safety.
+  StackHandleScope<5> hs(self);
+  Handle<mirror::MethodHandleImpl> method_handle(
+      hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle))));
+  raw_method_handle = nullptr;
+  self->EndAssertNoThreadSuspension(old_cause);
+
+  // Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact().
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::kForceICCECheck>(self,
+                                                                                   inst->VRegB(),
+                                                                                   caller_method,
+                                                                                   kVirtual);
+  DCHECK((resolved_method ==
+          jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) ||
+         (resolved_method ==
+          jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invoke)));
+  if (UNLIKELY(method_handle.IsNull())) {
+    ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual);
+    return static_cast<uintptr_t>('V');
+  }
+
+  Handle<mirror::Class> caller_class(hs.NewHandle(caller_method->GetDeclaringClass()));
+  Handle<mirror::MethodType> method_type(hs.NewHandle(linker->ResolveMethodType(
+      *dex_file, proto_idx,
+      hs.NewHandle<mirror::DexCache>(caller_class->GetDexCache()),
+      hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader()))));
+  // This implies we couldn't resolve one or more types in this method handle.
+  if (UNLIKELY(method_type.IsNull())) {
+    CHECK(self->IsExceptionPending());
+    return static_cast<uintptr_t>('V');
+  }
+
+  DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst->VRegA());
+  DCHECK_EQ(resolved_method->IsStatic(), kMethodIsStatic);
+
+  // Fix references before constructing the shadow frame.
+  gc_visitor.FixupReferences();
+
+  // Construct shadow frame placing arguments consecutively from |first_arg|.
+  const bool is_range = (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+  const size_t num_vregs = is_range ? inst->VRegA_4rcc() : inst->VRegA_45cc();
+  const size_t first_arg = 0;
+  ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+      CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, resolved_method, dex_pc);
+  ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
+  ScopedStackedShadowFramePusher
+      frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+  BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
+                                                    kMethodIsStatic,
+                                                    shorty,
+                                                    strlen(shorty),
+                                                    shadow_frame,
+                                                    first_arg);
+  shadow_frame_builder.VisitArguments();
+
+  // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in
+  // consecutive order.
+  uint32_t unused_args[Instruction::kMaxVarArgRegs] = {};
+  uint32_t first_callee_arg = first_arg + 1;
+  const bool do_assignability_check = false;
+  if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self,
+                                                                        resolved_method,
+                                                                        *shadow_frame,
+                                                                        method_handle,
+                                                                        method_type,
+                                                                        unused_args,
+                                                                        first_callee_arg,
+                                                                        result)) {
+    DCHECK(self->IsExceptionPending());
+  }
+
+  return static_cast<uintptr_t>(shorty[0]);
+}
+
 }  // namespace art