diff options
| -rw-r--r-- | runtime/arch/arm64/quick_entrypoints_arm64.S | 43 | ||||
| -rw-r--r-- | runtime/arch/stub_test.cc | 339 | ||||
| -rw-r--r-- | runtime/arch/x86_64/quick_entrypoints_x86_64.S | 52 | ||||
| -rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 163 |
4 files changed, 517 insertions, 80 deletions
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 346b08c741..ac922ddecd 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -508,26 +508,42 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr 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 @@ ENTRY art_quick_proxy_invoke_handler 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 b22ca82473..9fa16dbbcb 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 @@ class StubTest : public CommonRuntimeTest { 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 @@ class StubTest : public CommonRuntimeTest { 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 @@ TEST_F(StubTest, Fields64) { 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 9c86c75ddd..ed7f246466 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 @@ TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromC /* * 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 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler 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 4bd86db47f..ee276c1670 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1602,15 +1602,72 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, mirror::ArtMet } } +// 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 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) { mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type); if (UNLIKELY(method == nullptr)) { @@ -1630,7 +1687,7 @@ static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object if (UNLIKELY(method == NULL)) { CHECK(self->IsExceptionPending()); - return 0; // failure + return GetFailureValue(); // Failure. } } DCHECK(!self->IsExceptionPending()); @@ -1639,24 +1696,17 @@ static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object // 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 @@ EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper, true); // 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 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me 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 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me if (UNLIKELY(method == nullptr)) { CHECK(self->IsExceptionPending()); - return 0; // Failure. + return GetFailureValue(); // Failure. } } const void* code = method->GetEntryPointFromQuickCompiledCode(); @@ -1836,15 +1886,8 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me // 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 |