ART: Add INVOKE_TRAMPOLINE and imt_conflict stub to 64b architectures
"Generalize" the return type notion of the interface helpers.
Includes a simple test for imt_conflict. The other interface
trampolines are as of yet untested.
Change-Id: I30fc75f5103766d57628ff22bcbac7c7f81037e3
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 346b08c..ac922dd 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -508,26 +508,42 @@
ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode
/*
- * TODO arm64 specifics need to be fleshed out.
* All generated callsites for interface invokes and invocation slow paths will load arguments
- * as usual - except instead of loading x0 with the target Method*, x0 will contain
- * the method_idx. This wrapper will save x1-x3, load the caller's Method*, align the
+ * as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain
+ * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
* stack and call the appropriate C helper.
- * NOTE: "this" is first visible argument of the target, and so can be found in x1.
+ * NOTE: "this" is first visible argument of the target, and so can be found in arg1/x1.
*
- * The helper will attempt to locate the target and return a result in x0 consisting
+ * The helper will attempt to locate the target and return a 128-bit result in x0/x1 consisting
* of the target Method* in x0 and method->code_ in x1.
*
- * If unsuccessful, the helper will return NULL/NULL. There will be a pending exception in the
+ * If unsuccessful, the helper will return NULL/????. There will be a pending exception in the
* thread and we branch to another stub to deliver it.
*
* On success this wrapper will restore arguments and *jump* to the target, leaving the lr
* pointing back to the original caller.
+ *
+ * Adapted from ARM32 code.
+ *
+ * Clobbers x12.
*/
.macro INVOKE_TRAMPOLINE c_name, cxx_name
.extern \cxx_name
ENTRY \c_name
- brk 0
+ SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save callee saves in case allocation triggers GC
+ // Helper signature is always
+ // (method_idx, *this_object, *caller_method, *self, sp)
+
+ ldr x2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE] // pass caller Method*
+ mov x3, xSELF // pass Thread::Current
+ mov x4, sp
+ bl \cxx_name // (method_idx, this, caller, Thread*, SP)
+ mov x12, x1 // save Method*->code_
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+ cbz x0, 1f // did we find the target? if not go to exception delivery
+ br x12 // tail call to target
+1:
+ DELIVER_PENDING_EXCEPTION
END \c_name
.endm
@@ -1381,8 +1397,17 @@
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
-UNIMPLEMENTED art_quick_imt_conflict_trampoline
-
+ /*
+ * Called to resolve an imt conflict. x12 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+ENTRY art_quick_imt_conflict_trampoline
+ ldr x0, [sp, #0] // load caller Method*
+ ldr w0, [x0, #METHOD_DEX_CACHE_METHODS_OFFSET] // load dex_cache_resolved_methods
+ add x0, x0, #OBJECT_ARRAY_DATA_OFFSET // get starting address of data
+ ldr w0, [x0, x12, lsl 2] // load the target method
+ b art_quick_invoke_interface_trampoline
+END art_quick_imt_conflict_trampoline
ENTRY art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index b22ca82..9fa16db 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -16,6 +16,8 @@
#include "common_runtime_test.h"
#include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class-inl.h"
#include "mirror/string-inl.h"
#include <cstdio>
@@ -50,6 +52,7 @@
pair.first = "-Xmx4M"; // Smallest we can go.
}
}
+ options->push_back(std::make_pair("-Xint", nullptr));
}
// Helper function needed since TEST_F makes a new class.
@@ -283,6 +286,234 @@
return result;
}
+ // TODO: Set up a frame according to referrer's specs.
+ size_t Invoke3WithReferrerAndHidden(size_t arg0, size_t arg1, size_t arg2, uintptr_t code,
+ Thread* self, mirror::ArtMethod* referrer, size_t hidden) {
+ // Push a transition back into managed code onto the linked list in thread.
+ ManagedStack fragment;
+ self->PushManagedStackFragment(&fragment);
+
+ size_t result;
+ size_t fpr_result = 0;
+#if defined(__i386__)
+ // TODO: Set the thread?
+ __asm__ __volatile__(
+ "movd %[hidden], %%xmm0\n\t"
+ "pushl %[referrer]\n\t" // Store referrer
+ "call *%%edi\n\t" // Call the stub
+ "addl $4, %%esp" // Pop referrer
+ : "=a" (result)
+ // Use the result from eax
+ : "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"r"(referrer), [hidden]"r"(hidden)
+ // This places code into edi, arg0 into eax, arg1 into ecx, and arg2 into edx
+ : ); // clobber.
+ // TODO: Should we clobber the other registers? EBX gets clobbered by some of the stubs,
+ // but compilation fails when declaring that.
+#elif defined(__arm__)
+ __asm__ __volatile__(
+ "push {r1-r12, lr}\n\t" // Save state, 13*4B = 52B
+ ".cfi_adjust_cfa_offset 52\n\t"
+ "push {r9}\n\t"
+ ".cfi_adjust_cfa_offset 4\n\t"
+ "mov r9, %[referrer]\n\n"
+ "str r9, [sp, #-8]!\n\t" // Push referrer, +8B padding so 16B aligned
+ ".cfi_adjust_cfa_offset 8\n\t"
+ "ldr r9, [sp, #8]\n\t"
+
+ // Push everything on the stack, so we don't rely on the order. What a mess. :-(
+ "sub sp, sp, #24\n\t"
+ "str %[arg0], [sp]\n\t"
+ "str %[arg1], [sp, #4]\n\t"
+ "str %[arg2], [sp, #8]\n\t"
+ "str %[code], [sp, #12]\n\t"
+ "str %[self], [sp, #16]\n\t"
+ "str %[hidden], [sp, #20]\n\t"
+ "ldr r0, [sp]\n\t"
+ "ldr r1, [sp, #4]\n\t"
+ "ldr r2, [sp, #8]\n\t"
+ "ldr r3, [sp, #12]\n\t"
+ "ldr r9, [sp, #16]\n\t"
+ "ldr r12, [sp, #20]\n\t"
+ "add sp, sp, #24\n\t"
+
+ "blx r3\n\t" // Call the stub
+ "add sp, sp, #12\n\t" // Pop nullptr and padding
+ ".cfi_adjust_cfa_offset -12\n\t"
+ "pop {r1-r12, lr}\n\t" // Restore state
+ ".cfi_adjust_cfa_offset -52\n\t"
+ "mov %[result], r0\n\t" // Save the result
+ : [result] "=r" (result)
+ // Use the result from r0
+ : [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
+ [referrer] "r"(referrer), [hidden] "r"(hidden)
+ : ); // clobber.
+#elif defined(__aarch64__)
+ __asm__ __volatile__(
+ // Spill space for d8 - d15
+ "sub sp, sp, #64\n\t"
+ ".cfi_adjust_cfa_offset 64\n\t"
+ "stp d8, d9, [sp]\n\t"
+ "stp d10, d11, [sp, #16]\n\t"
+ "stp d12, d13, [sp, #32]\n\t"
+ "stp d14, d15, [sp, #48]\n\t"
+
+ "sub sp, sp, #48\n\t" // Reserve stack space, 16B aligned
+ ".cfi_adjust_cfa_offset 48\n\t"
+ "stp %[referrer], x1, [sp]\n\t"// referrer, x1
+ "stp x2, x3, [sp, #16]\n\t" // Save x2, x3
+ "stp x18, x30, [sp, #32]\n\t" // Save x18(xSELF), xLR
+
+ // Push everything on the stack, so we don't rely on the order. What a mess. :-(
+ "sub sp, sp, #48\n\t"
+ ".cfi_adjust_cfa_offset 48\n\t"
+ "str %[arg0], [sp]\n\t"
+ "str %[arg1], [sp, #8]\n\t"
+ "str %[arg2], [sp, #16]\n\t"
+ "str %[code], [sp, #24]\n\t"
+ "str %[self], [sp, #32]\n\t"
+ "str %[hidden], [sp, #40]\n\t"
+
+ // Now we definitely have x0-x3 free, use it to garble d8 - d15
+ "movk x0, #0xfad0\n\t"
+ "movk x0, #0xebad, lsl #16\n\t"
+ "movk x0, #0xfad0, lsl #32\n\t"
+ "movk x0, #0xebad, lsl #48\n\t"
+ "fmov d8, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d9, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d10, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d11, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d12, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d13, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d14, x0\n\t"
+ "add x0, x0, 1\n\t"
+ "fmov d15, x0\n\t"
+
+ // Load call params
+ "ldr x0, [sp]\n\t"
+ "ldr x1, [sp, #8]\n\t"
+ "ldr x2, [sp, #16]\n\t"
+ "ldr x3, [sp, #24]\n\t"
+ "ldr x18, [sp, #32]\n\t"
+ "ldr x12, [sp, #40]\n\t"
+ "add sp, sp, #48\n\t"
+ ".cfi_adjust_cfa_offset -48\n\t"
+
+
+ "blr x3\n\t" // Call the stub
+
+ // Test d8 - d15. We can use x1 and x2.
+ "movk x1, #0xfad0\n\t"
+ "movk x1, #0xebad, lsl #16\n\t"
+ "movk x1, #0xfad0, lsl #32\n\t"
+ "movk x1, #0xebad, lsl #48\n\t"
+ "fmov x2, d8\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d9\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d10\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d11\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d12\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d13\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d14\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+ "add x1, x1, 1\n\t"
+
+ "fmov x2, d15\n\t"
+ "cmp x1, x2\n\t"
+ "b.ne 1f\n\t"
+
+ "mov %[fpr_result], #0\n\t"
+
+ // Finish up.
+ "2:\n\t"
+ "ldp x1, x2, [sp, #8]\n\t" // Restore x1, x2
+ "ldp x3, x18, [sp, #24]\n\t" // Restore x3, xSELF
+ "ldr x30, [sp, #40]\n\t" // Restore xLR
+ "add sp, sp, #48\n\t" // Free stack space
+ ".cfi_adjust_cfa_offset -48\n\t"
+ "mov %[result], x0\n\t" // Save the result
+
+ "ldp d8, d9, [sp]\n\t" // Restore d8 - d15
+ "ldp d10, d11, [sp, #16]\n\t"
+ "ldp d12, d13, [sp, #32]\n\t"
+ "ldp d14, d15, [sp, #48]\n\t"
+ "add sp, sp, #64\n\t"
+ ".cfi_adjust_cfa_offset -64\n\t"
+
+ "b 3f\n\t" // Goto end
+
+ // Failed fpr verification.
+ "1:\n\t"
+ "mov %[fpr_result], #1\n\t"
+ "b 2b\n\t" // Goto finish-up
+
+ // End
+ "3:\n\t"
+ : [result] "=r" (result), [fpr_result] "=r" (fpr_result)
+ // Use the result from r0
+ : [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
+ [referrer] "r"(referrer), [hidden] "r"(hidden)
+ : "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17"); // clobber.
+#elif defined(__x86_64__)
+ // Note: Uses the native convention
+ // TODO: Set the thread?
+ __asm__ __volatile__(
+ "movq %[hidden], %%r9\n\t" // No need to save r9, listed as clobbered
+ "movd %%r9, %%xmm0\n\t"
+ "pushq %[referrer]\n\t" // Push referrer
+ "pushq (%%rsp)\n\t" // & 16B alignment padding
+ ".cfi_adjust_cfa_offset 16\n\t"
+ "call *%%rax\n\t" // Call the stub
+ "addq $16, %%rsp\n\t" // Pop nullptr and padding
+ ".cfi_adjust_cfa_offset -16\n\t"
+ : "=a" (result)
+ // Use the result from rax
+ : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "m"(referrer), [hidden] "m"(hidden)
+ // This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into rax
+ : "rbx", "rcx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); // clobber all
+ // TODO: Should we clobber the other registers?
+#else
+ LOG(WARNING) << "Was asked to invoke for an architecture I do not understand.";
+ result = 0;
+#endif
+ // Pop transition.
+ self->PopManagedStackFragment(fragment);
+
+ fp_result = fpr_result;
+ EXPECT_EQ(0U, fp_result);
+
+ return result;
+ }
+
// Method with 32b arg0, 64b arg1
size_t Invoke3UWithReferrer(size_t arg0, uint64_t arg1, uintptr_t code, Thread* self,
mirror::ArtMethod* referrer) {
@@ -1448,4 +1679,112 @@
TestFields(self, this, Primitive::Type::kPrimLong);
}
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__)
+extern "C" void art_quick_imt_conflict_trampoline(void);
+#endif
+
+TEST_F(StubTest, IMT) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__)
+ TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+
+ Thread* self = Thread::Current();
+
+ ScopedObjectAccess soa(self);
+ StackHandleScope<7> hs(self);
+
+ JNIEnv* env = Thread::Current()->GetJniEnv();
+
+ // ArrayList
+
+ // Load ArrayList and used methods (JNI).
+ jclass arraylist_jclass = env->FindClass("java/util/ArrayList");
+ ASSERT_NE(nullptr, arraylist_jclass);
+ jmethodID arraylist_constructor = env->GetMethodID(arraylist_jclass, "<init>", "()V");
+ ASSERT_NE(nullptr, arraylist_constructor);
+ jmethodID contains_jmethod = env->GetMethodID(arraylist_jclass, "contains", "(Ljava/lang/Object;)Z");
+ ASSERT_NE(nullptr, contains_jmethod);
+ jmethodID add_jmethod = env->GetMethodID(arraylist_jclass, "add", "(Ljava/lang/Object;)Z");
+ ASSERT_NE(nullptr, add_jmethod);
+
+ // Get mirror representation.
+ Handle<mirror::ArtMethod> contains_amethod(hs.NewHandle(soa.DecodeMethod(contains_jmethod)));
+
+ // Patch up ArrayList.contains.
+ if (contains_amethod.Get()->GetEntryPointFromQuickCompiledCode() == nullptr) {
+ contains_amethod.Get()->SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(
+ GetTlsPtr(self)->quick_entrypoints.pQuickToInterpreterBridge));
+ }
+
+ // List
+
+ // Load List and used methods (JNI).
+ jclass list_jclass = env->FindClass("java/util/List");
+ ASSERT_NE(nullptr, list_jclass);
+ jmethodID inf_contains_jmethod = env->GetMethodID(list_jclass, "contains", "(Ljava/lang/Object;)Z");
+ ASSERT_NE(nullptr, inf_contains_jmethod);
+
+ // Get mirror representation.
+ Handle<mirror::ArtMethod> inf_contains(hs.NewHandle(soa.DecodeMethod(inf_contains_jmethod)));
+
+ // Object
+
+ jclass obj_jclass = env->FindClass("java/lang/Object");
+ ASSERT_NE(nullptr, obj_jclass);
+ jmethodID obj_constructor = env->GetMethodID(obj_jclass, "<init>", "()V");
+ ASSERT_NE(nullptr, obj_constructor);
+
+ // Sanity check: check that there is a conflict for List.contains in ArrayList.
+
+ mirror::Class* arraylist_class = soa.Decode<mirror::Class*>(arraylist_jclass);
+ mirror::ArtMethod* m = arraylist_class->GetImTable()->Get(
+ inf_contains->GetDexMethodIndex() % ClassLinker::kImtSize);
+ ASSERT_TRUE(m->IsImtConflictMethod()) << "Test is meaningless, no IMT conflict in setup: " <<
+ PrettyMethod(m, true);
+
+
+ // Create instances.
+
+ jobject jarray_list = env->NewObject(arraylist_jclass, arraylist_constructor);
+ ASSERT_NE(nullptr, jarray_list);
+ Handle<mirror::Object> array_list(hs.NewHandle(soa.Decode<mirror::Object*>(jarray_list)));
+
+ jobject jobj = env->NewObject(obj_jclass, obj_constructor);
+ ASSERT_NE(nullptr, jobj);
+ Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(jobj)));
+
+ // Invoke.
+
+ size_t result =
+ Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
+ reinterpret_cast<size_t>(obj.Get()),
+ reinterpret_cast<uintptr_t>(&art_quick_imt_conflict_trampoline),
+ self, contains_amethod.Get(),
+ static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()));
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result);
+
+ // Add object.
+
+ env->CallBooleanMethod(jarray_list, add_jmethod, jobj);
+
+ ASSERT_FALSE(self->IsExceptionPending()) << PrettyTypeOf(self->GetException(nullptr));
+
+ // Invoke again.
+
+ result = Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
+ reinterpret_cast<size_t>(obj.Get()),
+ reinterpret_cast<uintptr_t>(&art_quick_imt_conflict_trampoline),
+ self, contains_amethod.Get(),
+ static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()));
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+#else
+ LOG(INFO) << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
} // namespace art
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 9c86c75..ed7f246 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -239,24 +239,45 @@
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
- * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
+ * as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain
* the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
* stack and call the appropriate C helper.
- * NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1.
+ * NOTE: "this" is first visible argument of the target, and so can be found in arg1/rsi.
*
- * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting
- * of the target Method* in r0 and method->code_ in r1.
+ * The helper will attempt to locate the target and return a 128-bit result in rax/rdx consisting
+ * of the target Method* in rax and method->code_ in rdx.
*
- * If unsuccessful, the helper will return NULL/NULL. There will bea pending exception in the
+ * If unsuccessful, the helper will return NULL/????. There will be a pending exception in the
* thread and we branch to another stub to deliver it.
*
- * On success this wrapper will restore arguments and *jump* to the target, leaving the lr
- * pointing back to the original caller.
+ * On success this wrapper will restore arguments and *jump* to the target, leaving the return
+ * location on the stack.
+ *
+ * Adapted from x86 code.
*/
MACRO2(INVOKE_TRAMPOLINE, c_name, cxx_name)
DEFINE_FUNCTION VAR(c_name, 0)
- int3
- int3
+ SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save callee saves in case allocation triggers GC
+ // Helper signature is always
+ // (method_idx, *this_object, *caller_method, *self, sp)
+
+ movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE(%rsp), %rdx // pass caller Method*
+ movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread
+ movq %rsp, %r8 // pass SP
+
+ call PLT_VAR(cxx_name, 1) // cxx_name(arg1, arg2, caller method*, Thread*, SP)
+ // save the code pointer
+ movq %rax, %rdi
+ movq %rdx, %rax
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+
+ testq %rdi, %rdi
+ jz 1f
+
+ // Tail call to intended method.
+ jmp *%rax
+1:
+ DELIVER_PENDING_EXCEPTION
END_FUNCTION VAR(c_name, 0)
END_MACRO
@@ -977,9 +998,18 @@
END_FUNCTION art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict.
+ * Called to resolve an imt conflict. Clobbers %rax (which will be clobbered later anyways).
+ *
+ * xmm0 is a hidden argument that holds the target method's dex method index.
+ * TODO: With proper hard-float support, this needs to be kept in sync with the quick compiler.
*/
-UNIMPLEMENTED art_quick_imt_conflict_trampoline
+DEFINE_FUNCTION art_quick_imt_conflict_trampoline
+ movq 16(%rsp), %rdi // load caller Method*
+ movl METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi // load dex_cache_resolved_methods
+ movd %xmm0, %rax // get target method index stored in xmm0
+ movl OBJECT_ARRAY_DATA_OFFSET(%rdi, %rax, 4), %edi // load the target method
+ jmp art_quick_invoke_interface_trampoline_local
+END_FUNCTION art_quick_imt_conflict_trampoline
DEFINE_FUNCTION art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 4bd86db..ee276c1 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1602,15 +1602,72 @@
}
}
-template<InvokeType type, bool access_check>
-static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self, mirror::ArtMethod** sp);
+// The following definitions create return types for two word-sized entities that will be passed
+// in registers so that memory operations for the interface trampolines can be avoided. The entities
+// are the resolved method and the pointer to the code to be invoked.
+//
+// On x86, ARM32 and MIPS, this is given for a *scalar* 64bit value. The definition thus *must* be
+// uint64_t or long long int. We use the upper 32b for code, and the lower 32b for the method.
+//
+// On x86_64 and ARM64, structs are decomposed for allocation, so we can create a structs of two
+// size_t-sized values.
+//
+// We need two operations:
+//
+// 1) A flag value that signals failure. The assembly stubs expect the method part to be "0".
+// GetFailureValue() will return a value that has method == 0.
+//
+// 2) A value that combines a code pointer and a method pointer.
+// GetSuccessValue() constructs this.
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+typedef uint64_t MethodAndCode;
+
+// Encodes method_ptr==nullptr and code_ptr==nullptr
+static constexpr MethodAndCode GetFailureValue() {
+ return 0;
+}
+
+// Use the lower 32b for the method pointer and the upper 32b for the code pointer.
+static MethodAndCode GetSuccessValue(const void* code, mirror::ArtMethod* method) {
+ uint32_t method_uint = reinterpret_cast<uint32_t>(method);
+ uint64_t code_uint = reinterpret_cast<uint32_t>(code);
+ return ((code_uint << 32) | method_uint);
+}
+
+#elif defined(__x86_64__) || defined(__aarch64__)
+struct MethodAndCode {
+ uintptr_t method;
+ uintptr_t code;
+};
+
+// Encodes method_ptr==nullptr. Leaves random value in code pointer.
+static MethodAndCode GetFailureValue() {
+ MethodAndCode ret;
+ ret.method = 0;
+ return ret;
+}
+
+// Write values into their respective members.
+static MethodAndCode GetSuccessValue(const void* code, mirror::ArtMethod* method) {
+ MethodAndCode ret;
+ ret.method = reinterpret_cast<uintptr_t>(method);
+ ret.code = reinterpret_cast<uintptr_t>(code);
+ return ret;
+}
+#else
+#error "Unsupported architecture"
+#endif
template<InvokeType type, bool access_check>
-static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self, mirror::ArtMethod** sp) {
+static MethodAndCode artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self, mirror::ArtMethod** sp);
+
+template<InvokeType type, bool access_check>
+static MethodAndCode artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self, mirror::ArtMethod** sp) {
mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
type);
if (UNLIKELY(method == nullptr)) {
@@ -1630,7 +1687,7 @@
if (UNLIKELY(method == NULL)) {
CHECK(self->IsExceptionPending());
- return 0; // failure
+ return GetFailureValue(); // Failure.
}
}
DCHECK(!self->IsExceptionPending());
@@ -1639,24 +1696,17 @@
// When we return, the caller will branch to this address, so it had better not be 0!
DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method) << " location: "
<< MethodHelper(method).GetDexFile().GetLocation();
-#ifdef __LP64__
- UNIMPLEMENTED(FATAL);
- return 0;
-#else
- uint32_t method_uint = reinterpret_cast<uint32_t>(method);
- uint64_t code_uint = reinterpret_cast<uint32_t>(code);
- uint64_t result = ((code_uint << 32) | method_uint);
- return result;
-#endif
+
+ return GetSuccessValue(code, method);
}
// Explicit artInvokeCommon template function declarations to please analysis tool.
#define EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(type, access_check) \
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
- uint64_t artInvokeCommon<type, access_check>(uint32_t method_idx, \
- mirror::Object* this_object, \
- mirror::ArtMethod* caller_method, \
- Thread* self, mirror::ArtMethod** sp) \
+ MethodAndCode artInvokeCommon<type, access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* caller_method, \
+ Thread* self, mirror::ArtMethod** sp) \
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, false);
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, true);
@@ -1672,57 +1722,57 @@
// See comments in runtime_support_asm.S
-extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- mirror::ArtMethod** sp)
+extern "C" MethodAndCode artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return artInvokeCommon<kInterface, true>(method_idx, this_object, caller_method, self, sp);
}
-extern "C" uint64_t artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- mirror::ArtMethod** sp)
+extern "C" MethodAndCode artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method, self, sp);
}
-extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- mirror::ArtMethod** sp)
+extern "C" MethodAndCode artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method, self, sp);
}
-extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- mirror::ArtMethod** sp)
+extern "C" MethodAndCode artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method, self, sp);
}
-extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- mirror::ArtMethod** sp)
+extern "C" MethodAndCode artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method, self, sp);
}
// Determine target of interface dispatch. This object is known non-null.
-extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_method,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self, mirror::ArtMethod** sp)
+extern "C" MethodAndCode artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_method,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self, mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* method;
if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
@@ -1731,7 +1781,7 @@
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(interface_method, this_object,
caller_method);
- return 0; // Failure.
+ return GetFailureValue(); // Failure.
}
} else {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
@@ -1828,7 +1878,7 @@
if (UNLIKELY(method == nullptr)) {
CHECK(self->IsExceptionPending());
- return 0; // Failure.
+ return GetFailureValue(); // Failure.
}
}
const void* code = method->GetEntryPointFromQuickCompiledCode();
@@ -1836,15 +1886,8 @@
// When we return, the caller will branch to this address, so it had better not be 0!
DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method) << " location: "
<< MethodHelper(method).GetDexFile().GetLocation();
-#ifdef __LP64__
- UNIMPLEMENTED(FATAL);
- return 0;
-#else
- uint32_t method_uint = reinterpret_cast<uint32_t>(method);
- uint64_t code_uint = reinterpret_cast<uint32_t>(code);
- uint64_t result = ((code_uint << 32) | method_uint);
- return result;
-#endif
+
+ return GetSuccessValue(code, method);
}
} // namespace art