diff options
Diffstat (limited to 'runtime/entrypoints')
-rw-r--r-- | runtime/entrypoints/entrypoint_utils.h | 71 | ||||
-rw-r--r-- | runtime/entrypoints/interpreter/interpreter_entrypoints.cc | 43 | ||||
-rw-r--r-- | runtime/entrypoints/interpreter/interpreter_entrypoints.h | 47 | ||||
-rw-r--r-- | runtime/entrypoints/jni/jni_entrypoints.cc | 87 | ||||
-rw-r--r-- | runtime/entrypoints/jni/jni_entrypoints.h | 37 | ||||
-rw-r--r-- | runtime/entrypoints/portable/portable_entrypoints.h | 8 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_argument_visitor.h | 138 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_entrypoints.h | 74 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc | 2 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_interpreter_entrypoints.cc | 128 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_jni_entrypoints.cc | 74 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_proxy_entrypoints.cc | 126 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_stub_entrypoints.cc | 295 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 558 |
14 files changed, 845 insertions, 843 deletions
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 3f28b5e..b6781c0 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -30,24 +30,13 @@ #include "object_utils.h" #include "thread.h" -extern "C" void art_interpreter_invoke_handler(); -extern "C" void art_jni_dlsym_lookup_stub(); -extern "C" void art_portable_abstract_method_error_stub(); -extern "C" void art_portable_proxy_invoke_handler(); -extern "C" void art_quick_abstract_method_error_stub(); -extern "C" void art_quick_deoptimize(); -extern "C" void art_quick_instrumentation_entry_from_code(void*); -extern "C" void art_quick_instrumentation_exit_from_code(); -extern "C" void art_quick_interpreter_entry(void*); -extern "C" void art_quick_proxy_invoke_handler(); -extern "C" void art_work_around_app_jni_bugs(); - namespace art { + namespace mirror { -class Class; -class Field; -class Object; -} + class Class; + class Field; + class Object; +} // namespace mirror // Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it // cannot be resolved, throw an error. If it can, use it to create an instance. @@ -350,25 +339,43 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Entry point for deoptimization. -static inline uintptr_t GetDeoptimizationEntryPoint() { +extern "C" void art_quick_deoptimize(); +static inline uintptr_t GetQuickDeoptimizationEntryPoint() { return reinterpret_cast<uintptr_t>(art_quick_deoptimize); } // Return address of instrumentation stub. -static inline void* GetInstrumentationEntryPoint() { - return reinterpret_cast<void*>(art_quick_instrumentation_entry_from_code); +extern "C" void art_quick_instrumentation_entry(void*); +static inline void* GetQuickInstrumentationEntryPoint() { + return reinterpret_cast<void*>(art_quick_instrumentation_entry); } // The return_pc of instrumentation exit stub. -static inline uintptr_t GetInstrumentationExitPc() { - return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit_from_code); +extern "C" void art_quick_instrumentation_exit(); +static inline uintptr_t GetQuickInstrumentationExitPc() { + return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit); +} + +extern "C" void art_portable_to_interpreter_bridge(mirror::AbstractMethod*); +static inline const void* GetPortableToInterpreterBridge() { + return reinterpret_cast<void*>(art_portable_to_interpreter_bridge); +} + +extern "C" void art_quick_to_interpreter_bridge(mirror::AbstractMethod*); +static inline const void* GetQuickToInterpreterBridge() { + return reinterpret_cast<void*>(art_quick_to_interpreter_bridge); } // Return address of interpreter stub. -static inline void* GetInterpreterEntryPoint() { - return reinterpret_cast<void*>(art_quick_interpreter_entry); +static inline const void* GetCompiledCodeToInterpreterBridge() { +#if defined(ART_USE_PORTABLE_COMPILER) + return GetPortableToInterpreterBridge(); +#else + return GetQuickToInterpreterBridge(); +#endif } + static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) { return class_linker->GetPortableResolutionTrampoline(); } @@ -386,23 +393,25 @@ static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) { #endif } -static inline void* GetPortableAbstractMethodErrorStub() { - return reinterpret_cast<void*>(art_portable_abstract_method_error_stub); +extern "C" void art_portable_proxy_invoke_handler(); +static inline const void* GetPortableProxyInvokeHandler() { + return reinterpret_cast<void*>(art_portable_proxy_invoke_handler); } -static inline void* GetQuickAbstractMethodErrorStub() { - return reinterpret_cast<void*>(art_quick_abstract_method_error_stub); +extern "C" void art_quick_proxy_invoke_handler(); +static inline const void* GetQuickProxyInvokeHandler() { + return reinterpret_cast<void*>(art_quick_proxy_invoke_handler); } -// Return address of abstract method error stub for defined compiler. -static inline void* GetAbstractMethodErrorStub() { +static inline const void* GetProxyInvokeHandler() { #if defined(ART_USE_PORTABLE_COMPILER) - return GetPortableAbstractMethodErrorStub(); + return GetPortableProxyInvokeHandler(); #else - return GetQuickAbstractMethodErrorStub(); + return GetQuickProxyInvokeHandler(); #endif } +extern "C" void* art_jni_dlsym_lookup_stub(JNIEnv*, jobject); static inline void* GetJniDlsymLookupStub() { return reinterpret_cast<void*>(art_jni_dlsym_lookup_stub); } diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc new file mode 100644 index 0000000..d99c43e --- /dev/null +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "class_linker.h" +#include "interpreter/interpreter.h" +#include "invoke_arg_array_builder.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" +#include "runtime.h" +#include "stack.h" + +namespace art { + +extern "C" void artInterperterToCompiledCodeBridge(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::AbstractMethod* method = shadow_frame->GetMethod(); + // Ensure static methods are initialized. + if (method->IsStatic()) { + Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), true, true); + } + uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_; + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + arg_array.BuildArgArray(shadow_frame, arg_offset); + method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()[0]); +} + +} // namespace art diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.h b/runtime/entrypoints/interpreter/interpreter_entrypoints.h new file mode 100644 index 0000000..c7df4e6 --- /dev/null +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_ENTRYPOINTS_INTERPRETER_INTERPRETER_ENTRYPOINTS_H_ +#define ART_RUNTIME_ENTRYPOINTS_INTERPRETER_INTERPRETER_ENTRYPOINTS_H_ + +#include "base/macros.h" +#include "dex_file.h" +#include "offsets.h" + +#define INTERPRETER_ENTRYPOINT_OFFSET(x) \ + ThreadOffset(static_cast<uintptr_t>(OFFSETOF_MEMBER(Thread, interpreter_entrypoints_)) + \ + static_cast<uintptr_t>(OFFSETOF_MEMBER(InterpreterEntryPoints, x))) + +namespace art { + +union JValue; +class MethodHelper; +class ShadowFrame; +class Thread; + +// Pointers to functions that are called by interpreter trampolines via thread-local storage. +struct PACKED(4) InterpreterEntryPoints { + void (*pInterpreterToInterpreterBridge)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + void (*pInterpreterToCompiledCodeBridge)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +}; + +} // namespace art + +#endif // ART_RUNTIME_ENTRYPOINTS_INTERPRETER_INTERPRETER_ENTRYPOINTS_H_ diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 98f7b12..88b4936 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -15,23 +15,26 @@ */ #include "base/logging.h" -#include "mirror/abstract_method.h" +#include "entrypoints/entrypoint_utils.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/object-inl.h" +#include "object_utils.h" #include "scoped_thread_state_change.h" #include "thread.h" namespace art { // Used by the JNI dlsym stub to find the native method to invoke if none is registered. -extern "C" void* artFindNativeMethod(Thread* self) { +extern "C" void* artFindNativeMethod() { + Thread* self = Thread::Current(); Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native. - DCHECK(Thread::Current() == self); ScopedObjectAccess soa(self); mirror::AbstractMethod* method = self->GetCurrentMethod(NULL); DCHECK(method != NULL); - // Lookup symbol address for method, on failure we'll return NULL with an - // exception set, otherwise we return the address of the method we found. + // Lookup symbol address for method, on failure we'll return NULL with an exception set, + // otherwise we return the address of the method we found. void* native_code = soa.Vm()->FindCodeForNativeMethod(method); if (native_code == NULL) { DCHECK(self->IsExceptionPending()); @@ -43,4 +46,78 @@ extern "C" void* artFindNativeMethod(Thread* self) { } } +static void WorkAroundJniBugsForJobject(intptr_t* arg_ptr) { + intptr_t value = *arg_ptr; + mirror::Object** value_as_jni_rep = reinterpret_cast<mirror::Object**>(value); + mirror::Object* value_as_work_around_rep = value_as_jni_rep != NULL ? *value_as_jni_rep : NULL; + CHECK(Runtime::Current()->GetHeap()->IsHeapAddress(value_as_work_around_rep)) + << value_as_work_around_rep; + *arg_ptr = reinterpret_cast<intptr_t>(value_as_work_around_rep); +} + +extern "C" const void* artWorkAroundAppJniBugs(Thread* self, intptr_t* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(Thread::Current() == self); + // TODO: this code is specific to ARM + // On entry the stack pointed by sp is: + // | arg3 | <- Calling JNI method's frame (and extra bit for out args) + // | LR | + // | R3 | arg2 + // | R2 | arg1 + // | R1 | jclass/jobject + // | R0 | JNIEnv + // | unused | + // | unused | + // | unused | <- sp + mirror::AbstractMethod* jni_method = self->GetCurrentMethod(NULL); + DCHECK(jni_method->IsNative()) << PrettyMethod(jni_method); + intptr_t* arg_ptr = sp + 4; // pointer to r1 on stack + // Fix up this/jclass argument + WorkAroundJniBugsForJobject(arg_ptr); + arg_ptr++; + // Fix up jobject arguments + MethodHelper mh(jni_method); + int reg_num = 2; // Current register being processed, -1 for stack arguments. + for (uint32_t i = 1; i < mh.GetShortyLength(); i++) { + char shorty_char = mh.GetShorty()[i]; + if (shorty_char == 'L') { + WorkAroundJniBugsForJobject(arg_ptr); + } + if (shorty_char == 'J' || shorty_char == 'D') { + if (reg_num == 2) { + arg_ptr = sp + 8; // skip to out arguments + reg_num = -1; + } else if (reg_num == 3) { + arg_ptr = sp + 10; // skip to out arguments plus 2 slots as long must be aligned + reg_num = -1; + } else { + DCHECK_EQ(reg_num, -1); + if ((reinterpret_cast<intptr_t>(arg_ptr) & 7) == 4) { + arg_ptr += 3; // unaligned, pad and move through stack arguments + } else { + arg_ptr += 2; // aligned, move through stack arguments + } + } + } else { + if (reg_num == 2) { + arg_ptr++; // move through register arguments + reg_num++; + } else if (reg_num == 3) { + arg_ptr = sp + 8; // skip to outgoing stack arguments + reg_num = -1; + } else { + DCHECK_EQ(reg_num, -1); + arg_ptr++; // move through stack arguments + } + } + } + // Load expected destination, see Method::RegisterNative + const void* code = reinterpret_cast<const void*>(jni_method->GetNativeGcMap()); + if (UNLIKELY(code == NULL)) { + code = GetJniDlsymLookupStub(); + jni_method->RegisterNative(self, code); + } + return code; +} + } // namespace art diff --git a/runtime/entrypoints/jni/jni_entrypoints.h b/runtime/entrypoints/jni/jni_entrypoints.h new file mode 100644 index 0000000..0a53447 --- /dev/null +++ b/runtime/entrypoints/jni/jni_entrypoints.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_ENTRYPOINTS_JNI_JNI_ENTRYPOINTS_H_ +#define ART_RUNTIME_ENTRYPOINTS_JNI_JNI_ENTRYPOINTS_H_ + +#include "base/macros.h" +#include "offsets.h" + +#define JNI_ENTRYPOINT_OFFSET(x) \ + ThreadOffset(static_cast<uintptr_t>(OFFSETOF_MEMBER(Thread, jni_entrypoints_)) + \ + static_cast<uintptr_t>(OFFSETOF_MEMBER(JniEntryPoints, x))) + +namespace art { + +// Pointers to functions that are called by JNI trampolines via thread-local storage. +struct PACKED(4) JniEntryPoints { + // Called when the JNI method isn't registered. + void* (*pDlsymLookup)(JNIEnv* env, jobject); +}; + +} // namespace art + +#endif // ART_RUNTIME_ENTRYPOINTS_JNI_JNI_ENTRYPOINTS_H_ diff --git a/runtime/entrypoints/portable/portable_entrypoints.h b/runtime/entrypoints/portable/portable_entrypoints.h index a229c76..ec9e4f8 100644 --- a/runtime/entrypoints/portable/portable_entrypoints.h +++ b/runtime/entrypoints/portable/portable_entrypoints.h @@ -28,15 +28,15 @@ namespace mirror { class Thread; #define PORTABLE_ENTRYPOINT_OFFSET(x) \ - (static_cast<uintptr_t>(OFFSETOF_MEMBER(Thread, portable_entrypoints_)) + \ - static_cast<uintptr_t>(OFFSETOF_MEMBER(PortableEntryPoints, x))) + ThreadOffset(static_cast<uintptr_t>(OFFSETOF_MEMBER(Thread, portable_entrypoints_)) + \ + static_cast<uintptr_t>(OFFSETOF_MEMBER(PortableEntryPoints, x))) // Pointers to functions that are called by code generated by compiler's adhering to the portable // compiler ABI. struct PACKED(4) PortableEntryPoints { // Invocation - const void* (*pPortableResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*, - mirror::AbstractMethod**, Thread*); + void (*pPortableResolutionTrampoline)(mirror::AbstractMethod*); + void (*pPortableToInterpreterBridge)(mirror::AbstractMethod*); }; } // namespace art diff --git a/runtime/entrypoints/quick/quick_argument_visitor.h b/runtime/entrypoints/quick/quick_argument_visitor.h deleted file mode 100644 index 35fa972..0000000 --- a/runtime/entrypoints/quick/quick_argument_visitor.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ARGUMENT_VISITOR_H_ -#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ARGUMENT_VISITOR_H_ - -#include "object_utils.h" - -namespace art { - -// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame. -class QuickArgumentVisitor { - public: -// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame. -// Size of Runtime::kRefAndArgs callee save frame. -// Size of Method* and register parameters in out stack arguments. -#if defined(__arm__) -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48 -#define QUICK_STACK_ARG_SKIP 16 -#elif defined(__mips__) -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64 -#define QUICK_STACK_ARG_SKIP 16 -#elif defined(__i386__) -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32 -#define QUICK_STACK_ARG_SKIP 16 -#else -#error "Unsupported architecture" -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0 -#define QUICK_STACK_ARG_SKIP 0 -#endif - - QuickArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : - caller_mh_(caller_mh), - args_in_regs_(ComputeArgsInRegs(caller_mh)), - num_params_(caller_mh.NumArgs()), - reg_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET), - stack_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE - + QUICK_STACK_ARG_SKIP), - cur_args_(reg_args_), - cur_arg_index_(0), - param_index_(0), - is_split_long_or_double_(false) { - } - - virtual ~QuickArgumentVisitor() {} - - virtual void Visit() = 0; - - bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return caller_mh_.IsParamAReference(param_index_); - } - - bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return caller_mh_.IsParamALongOrDouble(param_index_); - } - - Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return caller_mh_.GetParamPrimitiveType(param_index_); - } - - byte* GetParamAddress() const { - return cur_args_ + (cur_arg_index_ * kPointerSize); - } - - bool IsSplitLongOrDouble() const { - return is_split_long_or_double_; - } - - uint64_t ReadSplitLongParam() const { - DCHECK(IsSplitLongOrDouble()); - uint64_t low_half = *reinterpret_cast<uint32_t*>(GetParamAddress()); - uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_); - return (low_half & 0xffffffffULL) | (high_half << 32); - } - - void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - for (cur_arg_index_ = 0; cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) { - is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble(); - Visit(); - cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1); - param_index_++; - } - cur_args_ = stack_args_; - cur_arg_index_ = is_split_long_or_double_ ? 1 : 0; - is_split_long_or_double_ = false; - while (param_index_ < num_params_) { - Visit(); - cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1); - param_index_++; - } - } - - private: - static size_t ComputeArgsInRegs(MethodHelper& mh) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - size_t args_in_regs = 0; - size_t num_params = mh.NumArgs(); - for (size_t i = 0; i < num_params; i++) { - args_in_regs = args_in_regs + (mh.IsParamALongOrDouble(i) ? 2 : 1); - if (args_in_regs > 3) { - args_in_regs = 3; - break; - } - } - return args_in_regs; - } - MethodHelper& caller_mh_; - const size_t args_in_regs_; - const size_t num_params_; - byte* const reg_args_; - byte* const stack_args_; - byte* cur_args_; - size_t cur_arg_index_; - size_t param_index_; - // Does a 64bit parameter straddle the register and stack arguments? - bool is_split_long_or_double_; -}; - -} // namespace art - -#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ARGUMENT_VISITOR_H_ diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h index 74b8cfd..e76679b 100644 --- a/runtime/entrypoints/quick/quick_entrypoints.h +++ b/runtime/entrypoints/quick/quick_entrypoints.h @@ -17,44 +17,45 @@ #ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_H_ #define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_H_ -#include "dex_file-inl.h" -#include "runtime.h" +#include <jni.h> + +#include "base/macros.h" +#include "offsets.h" #define QUICK_ENTRYPOINT_OFFSET(x) \ - (static_cast<uintptr_t>(OFFSETOF_MEMBER(Thread, quick_entrypoints_)) + \ - static_cast<uintptr_t>(OFFSETOF_MEMBER(QuickEntryPoints, x))) + ThreadOffset(static_cast<uintptr_t>(OFFSETOF_MEMBER(Thread, quick_entrypoints_)) + \ + static_cast<uintptr_t>(OFFSETOF_MEMBER(QuickEntryPoints, x))) namespace art { + namespace mirror { class AbstractMethod; class Class; class Object; } // namespace mirror -class DvmDex; -class MethodHelper; -class ShadowFrame; + class Thread; // Pointers to functions that are called by quick compiler generated code via thread-local storage. struct PACKED(4) QuickEntryPoints { // Alloc - void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t); - void* (*pAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t); - void* (*pAllocObjectFromCode)(uint32_t, void*); - void* (*pAllocObjectFromCodeWithAccessCheck)(uint32_t, void*); - void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t); - void* (*pCheckAndAllocArrayFromCodeWithAccessCheck)(uint32_t, void*, int32_t); + void* (*pAllocArray)(uint32_t, void*, int32_t); + void* (*pAllocArrayWithAccessCheck)(uint32_t, void*, int32_t); + void* (*pAllocObject)(uint32_t, void*); + void* (*pAllocObjectWithAccessCheck)(uint32_t, void*); + void* (*pCheckAndAllocArray)(uint32_t, void*, int32_t); + void* (*pCheckAndAllocArrayWithAccessCheck)(uint32_t, void*, int32_t); // Cast - uint32_t (*pInstanceofNonTrivialFromCode)(const mirror::Class*, const mirror::Class*); - void (*pCanPutArrayElementFromCode)(void*, void*); - void (*pCheckCastFromCode)(void*, void*); + uint32_t (*pInstanceofNonTrivial)(const mirror::Class*, const mirror::Class*); + void (*pCanPutArrayElement)(void*, void*); + void (*pCheckCast)(void*, void*); // DexCache void* (*pInitializeStaticStorage)(uint32_t, void*); - void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*); - void* (*pInitializeTypeFromCode)(uint32_t, void*); - void* (*pResolveStringFromCode)(void*, uint32_t); + void* (*pInitializeTypeAndVerifyAccess)(uint32_t, void*); + void* (*pInitializeType)(uint32_t, void*); + void* (*pResolveString)(void*, uint32_t); // Field int (*pSet32Instance)(uint32_t, void*, int32_t); // field_idx, obj, src @@ -71,7 +72,7 @@ struct PACKED(4) QuickEntryPoints { void* (*pGetObjStatic)(uint32_t); // FillArray - void (*pHandleFillArrayDataFromCode)(void*, void*); + void (*pHandleFillArrayData)(void*, void*); // JNI uint32_t (*pJniMethodStart)(Thread*); @@ -83,8 +84,8 @@ struct PACKED(4) QuickEntryPoints { jobject locked, Thread* self); // Locks - void (*pLockObjectFromCode)(void*); - void (*pUnlockObjectFromCode)(void*); + void (*pLockObject)(void*); + void (*pUnlockObject)(void*); // Math int32_t (*pCmpgDouble)(double, double); @@ -108,14 +109,6 @@ struct PACKED(4) QuickEntryPoints { uint64_t (*pShrLong)(uint64_t, uint32_t); uint64_t (*pUshrLong)(uint64_t, uint32_t); - // Interpreter - void (*pInterpreterToInterpreterEntry)(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - void (*pInterpreterToQuickEntry)(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); - // Intrinsics int32_t (*pIndexOf)(void*, uint32_t, uint32_t, uint32_t); int32_t (*pMemcmp16)(void*, void*, int32_t); @@ -123,8 +116,8 @@ struct PACKED(4) QuickEntryPoints { void* (*pMemcpy)(void*, const void*, size_t); // Invocation - const void* (*pQuickResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*, - mirror::AbstractMethod**, Thread*); + void (*pQuickResolutionTrampoline)(mirror::AbstractMethod*); + void (*pQuickToInterpreterBridge)(mirror::AbstractMethod*); void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*); void (*pInvokeInterfaceTrampoline)(uint32_t, void*); void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*); @@ -133,22 +126,21 @@ struct PACKED(4) QuickEntryPoints { void (*pInvokeVirtualTrampolineWithAccessCheck)(uint32_t, void*); // Thread - void (*pCheckSuspendFromCode)(Thread*); // Stub that is called when the suspend count is non-zero - void (*pTestSuspendFromCode)(); // Stub that is periodically called to test the suspend count + void (*pCheckSuspend)(Thread*); // Stub that is called when the suspend count is non-zero + void (*pTestSuspend)(); // Stub that is periodically called to test the suspend count // Throws void (*pDeliverException)(void*); - void (*pThrowArrayBoundsFromCode)(int32_t, int32_t); - void (*pThrowDivZeroFromCode)(); - void (*pThrowNoSuchMethodFromCode)(int32_t); - void (*pThrowNullPointerFromCode)(); - void (*pThrowStackOverflowFromCode)(void*); + void (*pThrowArrayBounds)(int32_t, int32_t); + void (*pThrowDivZero)(); + void (*pThrowNoSuchMethod)(int32_t); + void (*pThrowNullPointer)(); + void (*pThrowStackOverflow)(void*); }; // JNI entrypoints. -extern uint32_t JniMethodStart(Thread* self) - UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; +extern uint32_t JniMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR; extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc index 7ecd296..0e61942 100644 --- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc @@ -32,7 +32,7 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::AbstractMet FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); const void* result = instrumentation->GetQuickCodeFor(method); - bool interpreter_entry = (result == GetInterpreterEntryPoint()); + bool interpreter_entry = (result == GetQuickToInterpreterBridge()); instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? NULL : this_object, method, lr, interpreter_entry); CHECK(result != NULL) << PrettyMethod(method); diff --git a/runtime/entrypoints/quick/quick_interpreter_entrypoints.cc b/runtime/entrypoints/quick/quick_interpreter_entrypoints.cc deleted file mode 100644 index 656df8d..0000000 --- a/runtime/entrypoints/quick/quick_interpreter_entrypoints.cc +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "quick_argument_visitor.h" -#include "callee_save_frame.h" -#include "dex_file-inl.h" -#include "interpreter/interpreter.h" -#include "invoke_arg_array_builder.h" -#include "mirror/abstract_method-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object-inl.h" -#include "mirror/object_array-inl.h" -#include "object_utils.h" - -namespace art { - -// Visits arguments on the stack placing them into the shadow frame. -class BuildShadowFrameVisitor : public QuickArgumentVisitor { - public: - BuildShadowFrameVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp, - ShadowFrame& sf, size_t first_arg_reg) : - QuickArgumentVisitor(caller_mh, sp), sf_(sf), cur_reg_(first_arg_reg) {} - - virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Primitive::Type type = GetParamPrimitiveType(); - switch (type) { - case Primitive::kPrimLong: // Fall-through. - case Primitive::kPrimDouble: - if (IsSplitLongOrDouble()) { - sf_.SetVRegLong(cur_reg_, ReadSplitLongParam()); - } else { - sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress())); - } - ++cur_reg_; - break; - case Primitive::kPrimNot: - sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress())); - break; - case Primitive::kPrimBoolean: // Fall-through. - case Primitive::kPrimByte: // Fall-through. - case Primitive::kPrimChar: // Fall-through. - case Primitive::kPrimShort: // Fall-through. - case Primitive::kPrimInt: // Fall-through. - case Primitive::kPrimFloat: - sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress())); - break; - case Primitive::kPrimVoid: - LOG(FATAL) << "UNREACHABLE"; - break; - } - ++cur_reg_; - } - - private: - ShadowFrame& sf_; - size_t cur_reg_; - - DISALLOW_COPY_AND_ASSIGN(BuildShadowFrameVisitor); -}; - -extern "C" uint64_t artInterpreterEntry(mirror::AbstractMethod* method, Thread* self, - mirror::AbstractMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Ensure we don't get thread suspension until the object arguments are safely in the shadow - // frame. - const char* old_cause = self->StartAssertNoThreadSuspension("Building interpreter shadow frame"); - FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); - - MethodHelper mh(method); - const DexFile::CodeItem* code_item = mh.GetCodeItem(); - uint16_t num_regs = code_item->registers_size_; - void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); - ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, NULL, // No last shadow coming from quick. - method, 0, memory)); - size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_; - BuildShadowFrameVisitor shadow_frame_builder(mh, sp, *shadow_frame, first_arg_reg); - shadow_frame_builder.VisitArguments(); - // Push a transition back into managed code onto the linked list in thread. - ManagedStack fragment; - self->PushManagedStackFragment(&fragment); - self->PushShadowFrame(shadow_frame); - self->EndAssertNoThreadSuspension(old_cause); - - if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) { - // Ensure static method's class is initialized. - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), - true, true)) { - DCHECK(Thread::Current()->IsExceptionPending()); - self->PopManagedStackFragment(fragment); - return 0; - } - } - - JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame); - // Pop transition. - self->PopManagedStackFragment(fragment); - return result.GetJ(); -} - -extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::AbstractMethod* method = shadow_frame->GetMethod(); - // Ensure static methods are initialized. - if (method->IsStatic()) { - Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), true, true); - } - uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_; - ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); - arg_array.BuildArgArray(shadow_frame, arg_offset); - method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()[0]); -} - -} // namespace art diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 23a28f9..9907c04 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -94,78 +94,4 @@ extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, return o; } -static void WorkAroundJniBugsForJobject(intptr_t* arg_ptr) { - intptr_t value = *arg_ptr; - mirror::Object** value_as_jni_rep = reinterpret_cast<mirror::Object**>(value); - mirror::Object* value_as_work_around_rep = value_as_jni_rep != NULL ? *value_as_jni_rep : NULL; - CHECK(Runtime::Current()->GetHeap()->IsHeapAddress(value_as_work_around_rep)) - << value_as_work_around_rep; - *arg_ptr = reinterpret_cast<intptr_t>(value_as_work_around_rep); -} - -extern "C" const void* artWorkAroundAppJniBugs(Thread* self, intptr_t* sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(Thread::Current() == self); - // TODO: this code is specific to ARM - // On entry the stack pointed by sp is: - // | arg3 | <- Calling JNI method's frame (and extra bit for out args) - // | LR | - // | R3 | arg2 - // | R2 | arg1 - // | R1 | jclass/jobject - // | R0 | JNIEnv - // | unused | - // | unused | - // | unused | <- sp - mirror::AbstractMethod* jni_method = self->GetCurrentMethod(NULL); - DCHECK(jni_method->IsNative()) << PrettyMethod(jni_method); - intptr_t* arg_ptr = sp + 4; // pointer to r1 on stack - // Fix up this/jclass argument - WorkAroundJniBugsForJobject(arg_ptr); - arg_ptr++; - // Fix up jobject arguments - MethodHelper mh(jni_method); - int reg_num = 2; // Current register being processed, -1 for stack arguments. - for (uint32_t i = 1; i < mh.GetShortyLength(); i++) { - char shorty_char = mh.GetShorty()[i]; - if (shorty_char == 'L') { - WorkAroundJniBugsForJobject(arg_ptr); - } - if (shorty_char == 'J' || shorty_char == 'D') { - if (reg_num == 2) { - arg_ptr = sp + 8; // skip to out arguments - reg_num = -1; - } else if (reg_num == 3) { - arg_ptr = sp + 10; // skip to out arguments plus 2 slots as long must be aligned - reg_num = -1; - } else { - DCHECK_EQ(reg_num, -1); - if ((reinterpret_cast<intptr_t>(arg_ptr) & 7) == 4) { - arg_ptr += 3; // unaligned, pad and move through stack arguments - } else { - arg_ptr += 2; // aligned, move through stack arguments - } - } - } else { - if (reg_num == 2) { - arg_ptr++; // move through register arguments - reg_num++; - } else if (reg_num == 3) { - arg_ptr = sp + 8; // skip to outgoing stack arguments - reg_num = -1; - } else { - DCHECK_EQ(reg_num, -1); - arg_ptr++; // move through stack arguments - } - } - } - // Load expected destination, see Method::RegisterNative - const void* code = reinterpret_cast<const void*>(jni_method->GetNativeGcMap()); - if (UNLIKELY(code == NULL)) { - code = GetJniDlsymLookupStub(); - jni_method->RegisterNative(self, code); - } - return code; -} - } // namespace art diff --git a/runtime/entrypoints/quick/quick_proxy_entrypoints.cc b/runtime/entrypoints/quick/quick_proxy_entrypoints.cc deleted file mode 100644 index 4e3d749..0000000 --- a/runtime/entrypoints/quick/quick_proxy_entrypoints.cc +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "quick_argument_visitor.h" -#include "dex_file-inl.h" -#include "entrypoints/entrypoint_utils.h" -#include "mirror/abstract_method-inl.h" -#include "mirror/object_array-inl.h" -#include "mirror/object-inl.h" -#include "object_utils.h" -#include "reflection.h" -#include "scoped_thread_state_change.h" -#include "thread.h" -#include "well_known_classes.h" - -#include "ScopedLocalRef.h" - -namespace art { - -// Visits arguments on the stack placing them into the args vector, Object* arguments are converted -// to jobjects. -class BuildQuickArgumentVisitor : public QuickArgumentVisitor { - public: - BuildQuickArgumentVisitor(MethodHelper& caller_mh, mirror::AbstractMethod** sp, - ScopedObjectAccessUnchecked& soa, std::vector<jvalue>& args) : - QuickArgumentVisitor(caller_mh, sp), soa_(soa), args_(args) {} - - virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - jvalue val; - Primitive::Type type = GetParamPrimitiveType(); - switch (type) { - case Primitive::kPrimNot: { - mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress()); - val.l = soa_.AddLocalReference<jobject>(obj); - break; - } - case Primitive::kPrimLong: // Fall-through. - case Primitive::kPrimDouble: - if (IsSplitLongOrDouble()) { - val.j = ReadSplitLongParam(); - } else { - val.j = *reinterpret_cast<jlong*>(GetParamAddress()); - } - break; - case Primitive::kPrimBoolean: // Fall-through. - case Primitive::kPrimByte: // Fall-through. - case Primitive::kPrimChar: // Fall-through. - case Primitive::kPrimShort: // Fall-through. - case Primitive::kPrimInt: // Fall-through. - case Primitive::kPrimFloat: - val.i = *reinterpret_cast<jint*>(GetParamAddress()); - break; - case Primitive::kPrimVoid: - LOG(FATAL) << "UNREACHABLE"; - val.j = 0; - break; - } - args_.push_back(val); - } - - private: - ScopedObjectAccessUnchecked& soa_; - std::vector<jvalue>& args_; - - DISALLOW_COPY_AND_ASSIGN(BuildQuickArgumentVisitor); -}; - -// Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method -// which is responsible for recording callee save registers. We explicitly place into jobjects the -// incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a -// field within the proxy object, which will box the primitive arguments and deal with error cases. -extern "C" uint64_t artQuickProxyInvokeHandler(mirror::AbstractMethod* proxy_method, - mirror::Object* receiver, - Thread* self, mirror::AbstractMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Ensure we don't get thread suspension until the object arguments are safely in jobjects. - const char* old_cause = - self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments"); - // Register the top of the managed stack, making stack crawlable. - DCHECK_EQ(*sp, proxy_method); - self->SetTopOfStack(sp, 0); - DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), - Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); - self->VerifyStack(); - // Start new JNI local reference state. - JNIEnvExt* env = self->GetJniEnv(); - ScopedObjectAccessUnchecked soa(env); - ScopedJniEnvLocalRefState env_state(env); - // Create local ref. copies of proxy method and the receiver. - jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver); - - // Placing arguments into args vector and remove the receiver. - MethodHelper proxy_mh(proxy_method); - std::vector<jvalue> args; - BuildQuickArgumentVisitor local_ref_visitor(proxy_mh, sp, soa, args); - local_ref_visitor.VisitArguments(); - args.erase(args.begin()); - - // Convert proxy method into expected interface method. - mirror::AbstractMethod* interface_method = proxy_method->FindOverriddenMethod(); - DCHECK(interface_method != NULL); - DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method); - jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method); - - // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code - // that performs allocations. - self->EndAssertNoThreadSuspension(old_cause); - JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(), - rcvr_jobj, interface_method_jobj, args); - return result.GetJ(); -} - -} // namespace art diff --git a/runtime/entrypoints/quick/quick_stub_entrypoints.cc b/runtime/entrypoints/quick/quick_stub_entrypoints.cc deleted file mode 100644 index d78bbf3..0000000 --- a/runtime/entrypoints/quick/quick_stub_entrypoints.cc +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "callee_save_frame.h" -#include "class_linker-inl.h" -#include "dex_file-inl.h" -#include "dex_instruction-inl.h" -#include "mirror/class-inl.h" -#include "mirror/abstract_method-inl.h" -#include "mirror/object_array-inl.h" -#include "mirror/object-inl.h" -#include "object_utils.h" -#include "scoped_thread_state_change.h" - -// Architecture specific assembler helper to deliver exception. -extern "C" void art_quick_deliver_exception_from_code(void*); - -namespace art { - -// Lazily resolve a method for quick. Called by stub code. -extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, - mirror::Object* receiver, - mirror::AbstractMethod** sp, Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__arm__) - // On entry the stack pointed by sp is: - // | argN | | - // | ... | | - // | arg4 | | - // | arg3 spill | | Caller's frame - // | arg2 spill | | - // | arg1 spill | | - // | Method* | --- - // | LR | - // | ... | callee saves - // | R3 | arg3 - // | R2 | arg2 - // | R1 | arg1 - // | R0 | - // | Method* | <- sp - DCHECK_EQ(48U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); - mirror::AbstractMethod** caller_sp = reinterpret_cast<mirror::AbstractMethod**>(reinterpret_cast<byte*>(sp) + 48); - uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) + kPointerSize); - uint32_t pc_offset = 10; - uintptr_t caller_pc = regs[pc_offset]; -#elif defined(__i386__) - // On entry the stack pointed by sp is: - // | argN | | - // | ... | | - // | arg4 | | - // | arg3 spill | | Caller's frame - // | arg2 spill | | - // | arg1 spill | | - // | Method* | --- - // | Return | - // | EBP,ESI,EDI | callee saves - // | EBX | arg3 - // | EDX | arg2 - // | ECX | arg1 - // | EAX/Method* | <- sp - DCHECK_EQ(32U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); - mirror::AbstractMethod** caller_sp = reinterpret_cast<mirror::AbstractMethod**>(reinterpret_cast<byte*>(sp) + 32); - uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp)); - uintptr_t caller_pc = regs[7]; -#elif defined(__mips__) - // On entry the stack pointed by sp is: - // | argN | | - // | ... | | - // | arg4 | | - // | arg3 spill | | Caller's frame - // | arg2 spill | | - // | arg1 spill | | - // | Method* | --- - // | RA | - // | ... | callee saves - // | A3 | arg3 - // | A2 | arg2 - // | A1 | arg1 - // | A0/Method* | <- sp - DCHECK_EQ(64U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); - mirror::AbstractMethod** caller_sp = reinterpret_cast<mirror::AbstractMethod**>(reinterpret_cast<byte*>(sp) + 64); - uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp)); - uint32_t pc_offset = 15; - uintptr_t caller_pc = regs[pc_offset]; -#else - UNIMPLEMENTED(FATAL); - mirror::AbstractMethod** caller_sp = NULL; - uintptr_t* regs = NULL; - uintptr_t caller_pc = 0; -#endif - FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs); - // Start new JNI local reference state - JNIEnvExt* env = thread->GetJniEnv(); - ScopedObjectAccessUnchecked soa(env); - ScopedJniEnvLocalRefState env_state(env); - - // Compute details about the called method (avoid GCs) - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::AbstractMethod* caller = *caller_sp; - InvokeType invoke_type; - uint32_t dex_method_idx; -#if !defined(__i386__) - const char* shorty; - uint32_t shorty_len; -#endif - if (called->IsRuntimeMethod()) { - uint32_t dex_pc = caller->ToDexPc(caller_pc); - const DexFile::CodeItem* code = MethodHelper(caller).GetCodeItem(); - CHECK_LT(dex_pc, code->insns_size_in_code_units_); - const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); - Instruction::Code instr_code = instr->Opcode(); - bool is_range; - switch (instr_code) { - case Instruction::INVOKE_DIRECT: - invoke_type = kDirect; - is_range = false; - break; - case Instruction::INVOKE_DIRECT_RANGE: - invoke_type = kDirect; - is_range = true; - break; - case Instruction::INVOKE_STATIC: - invoke_type = kStatic; - is_range = false; - break; - case Instruction::INVOKE_STATIC_RANGE: - invoke_type = kStatic; - is_range = true; - break; - case Instruction::INVOKE_SUPER: - invoke_type = kSuper; - is_range = false; - break; - case Instruction::INVOKE_SUPER_RANGE: - invoke_type = kSuper; - is_range = true; - break; - case Instruction::INVOKE_VIRTUAL: - invoke_type = kVirtual; - is_range = false; - break; - case Instruction::INVOKE_VIRTUAL_RANGE: - invoke_type = kVirtual; - is_range = true; - break; - case Instruction::INVOKE_INTERFACE: - invoke_type = kInterface; - is_range = false; - break; - case Instruction::INVOKE_INTERFACE_RANGE: - invoke_type = kInterface; - is_range = true; - break; - default: - LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL); - // Avoid used uninitialized warnings. - invoke_type = kDirect; - is_range = false; - } - dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c(); -#if !defined(__i386__) - shorty = linker->MethodShorty(dex_method_idx, caller, &shorty_len); -#endif - } else { - invoke_type = kStatic; - dex_method_idx = called->GetDexMethodIndex(); -#if !defined(__i386__) - MethodHelper mh(called); - shorty = mh.GetShorty(); - shorty_len = mh.GetShortyLength(); -#endif - } -#if !defined(__i386__) - // Discover shorty (avoid GCs) - size_t args_in_regs = 0; - for (size_t i = 1; i < shorty_len; i++) { - char c = shorty[i]; - args_in_regs = args_in_regs + (c == 'J' || c == 'D' ? 2 : 1); - if (args_in_regs > 3) { - args_in_regs = 3; - break; - } - } - // Place into local references incoming arguments from the caller's register arguments - size_t cur_arg = 1; // skip method_idx in R0, first arg is in R1 - if (invoke_type != kStatic) { - mirror::Object* obj = reinterpret_cast<mirror::Object*>(regs[cur_arg]); - cur_arg++; - if (args_in_regs < 3) { - // If we thought we had fewer than 3 arguments in registers, account for the receiver - args_in_regs++; - } - soa.AddLocalReference<jobject>(obj); - } - size_t shorty_index = 1; // skip return value - // Iterate while arguments and arguments in registers (less 1 from cur_arg which is offset to skip - // R0) - while ((cur_arg - 1) < args_in_regs && shorty_index < shorty_len) { - char c = shorty[shorty_index]; - shorty_index++; - if (c == 'L') { - mirror::Object* obj = reinterpret_cast<mirror::Object*>(regs[cur_arg]); - soa.AddLocalReference<jobject>(obj); - } - cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1); - } - // Place into local references incoming arguments from the caller's stack arguments - cur_arg += pc_offset + 1; // skip LR/RA, Method* and spills for R1-R3/A1-A3 and callee saves - while (shorty_index < shorty_len) { - char c = shorty[shorty_index]; - shorty_index++; - if (c == 'L') { - mirror::Object* obj = reinterpret_cast<mirror::Object*>(regs[cur_arg]); - soa.AddLocalReference<jobject>(obj); - } - cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1); - } -#endif - // Resolve method filling in dex cache - if (called->IsRuntimeMethod()) { - called = linker->ResolveMethod(dex_method_idx, caller, invoke_type); - } - const void* code = NULL; - if (LIKELY(!thread->IsExceptionPending())) { - // Incompatible class change should have been handled in resolve method. - CHECK(!called->CheckIncompatibleClassChange(invoke_type)); - // Refine called method based on receiver. - if (invoke_type == kVirtual) { - called = receiver->GetClass()->FindVirtualMethodForVirtual(called); - } else if (invoke_type == kInterface) { - called = receiver->GetClass()->FindVirtualMethodForInterface(called); - } - // Ensure that the called method's class is initialized. - mirror::Class* called_class = called->GetDeclaringClass(); - linker->EnsureInitialized(called_class, true, true); - if (LIKELY(called_class->IsInitialized())) { - code = called->GetEntryPointFromCompiledCode(); - } else if (called_class->IsInitializing()) { - if (invoke_type == kStatic) { - // Class is still initializing, go to oat and grab code (trampoline must be left in place - // until class is initialized to stop races between threads). - code = linker->GetOatCodeFor(called); - } else { - // No trampoline for non-static methods. - code = called->GetEntryPointFromCompiledCode(); - } - } else { - DCHECK(called_class->IsErroneous()); - } - } - if (UNLIKELY(code == NULL)) { - // Something went wrong in ResolveMethod or EnsureInitialized, - // go into deliver exception with the pending exception in r0 - CHECK(thread->IsExceptionPending()); - code = reinterpret_cast<void*>(art_quick_deliver_exception_from_code); - regs[0] = reinterpret_cast<uintptr_t>(thread->GetException(NULL)); - thread->ClearException(); - } else { - // Expect class to at least be initializing. - DCHECK(called->GetDeclaringClass()->IsInitializing()); - // Don't want infinite recursion. - DCHECK(code != GetResolutionTrampoline(linker)); - // Set up entry into main method - regs[0] = reinterpret_cast<uintptr_t>(called); - } - return code; -} - -// Called by the abstract method error stub. -extern "C" void artThrowAbstractMethodErrorFromCode(mirror::AbstractMethod* method, Thread* self, - mirror::AbstractMethod** sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if !defined(ART_USE_PORTABLE_COMPILER) - FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); -#else - UNUSED(sp); -#endif - ThrowAbstractMethodError(method); - self->QuickDeliverException(); -} - -} // namespace art diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc new file mode 100644 index 0000000..9bf02e8 --- /dev/null +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "callee_save_frame.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "interpreter/interpreter.h" +#include "invoke_arg_array_builder.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "object_utils.h" +#include "runtime.h" + +namespace art { + +// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame. +class QuickArgumentVisitor { + public: +// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame. +// Size of Runtime::kRefAndArgs callee save frame. +// Size of Method* and register parameters in out stack arguments. +#if defined(__arm__) + // The callee save frame is pointed to by SP. + // | argN | | + // | ... | | + // | arg4 | | + // | arg3 spill | | Caller's frame + // | arg2 spill | | + // | arg1 spill | | + // | Method* | --- + // | LR | + // | ... | callee saves + // | R3 | arg3 + // | R2 | arg2 + // | R1 | arg1 + // | R0 | + // | Method* | <- sp +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 44 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48 +#define QUICK_STACK_ARG_SKIP 16 +#elif defined(__mips__) + // The callee save frame is pointed to by SP. + // | argN | | + // | ... | | + // | arg4 | | + // | arg3 spill | | Caller's frame + // | arg2 spill | | + // | arg1 spill | | + // | Method* | --- + // | RA | + // | ... | callee saves + // | A3 | arg3 + // | A2 | arg2 + // | A1 | arg1 + // | A0/Method* | <- sp +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 60 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64 +#define QUICK_STACK_ARG_SKIP 16 +#elif defined(__i386__) + // The callee save frame is pointed to by SP. + // | argN | | + // | ... | | + // | arg4 | | + // | arg3 spill | | Caller's frame + // | arg2 spill | | + // | arg1 spill | | + // | Method* | --- + // | Return | + // | EBP,ESI,EDI | callee saves + // | EBX | arg3 + // | EDX | arg2 + // | ECX | arg1 + // | EAX/Method* | <- sp +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 28 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32 +#define QUICK_STACK_ARG_SKIP 16 +#else +#error "Unsupported architecture" +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 0 +#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0 +#define QUICK_STACK_ARG_SKIP 0 +#endif + + static mirror::AbstractMethod* GetCallingMethod(mirror::AbstractMethod** sp) { + byte* previous_sp = reinterpret_cast<byte*>(sp) + + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE; + return *reinterpret_cast<mirror::AbstractMethod**>(previous_sp); + } + + static uintptr_t GetCallingPc(mirror::AbstractMethod** sp) { + byte* lr = reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET; + return *reinterpret_cast<uintptr_t*>(lr); + } + + QuickArgumentVisitor(mirror::AbstractMethod** sp, bool is_static, + const char* shorty, uint32_t shorty_len) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : + is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len), + args_in_regs_(ComputeArgsInRegs(is_static, shorty, shorty_len)), + num_params_((is_static ? 0 : 1) + shorty_len - 1), // +1 for this, -1 for return type + reg_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET), + stack_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE + + QUICK_STACK_ARG_SKIP), + cur_args_(reg_args_), + cur_arg_index_(0), + param_index_(0), + is_split_long_or_double_(false) { + DCHECK_EQ(static_cast<size_t>(QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE), + Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); + } + + virtual ~QuickArgumentVisitor() {} + + virtual void Visit() = 0; + + Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + size_t index = param_index_; + if (is_static_) { + index++; // 0th argument must skip return value at start of the shorty + } else if (index == 0) { + return Primitive::kPrimNot; + } + CHECK_LT(index, shorty_len_); + return Primitive::GetType(shorty_[index]); + } + + byte* GetParamAddress() const { + return cur_args_ + (cur_arg_index_ * kPointerSize); + } + + bool IsSplitLongOrDouble() const { + return is_split_long_or_double_; + } + + bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetParamPrimitiveType() == Primitive::kPrimNot; + } + + bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Primitive::Type type = GetParamPrimitiveType(); + return type == Primitive::kPrimLong || type == Primitive::kPrimDouble; + } + + uint64_t ReadSplitLongParam() const { + DCHECK(IsSplitLongOrDouble()); + uint64_t low_half = *reinterpret_cast<uint32_t*>(GetParamAddress()); + uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args_); + return (low_half & 0xffffffffULL) | (high_half << 32); + } + + void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + for (cur_arg_index_ = 0; cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) { + is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble(); + Visit(); + cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1); + param_index_++; + } + cur_args_ = stack_args_; + cur_arg_index_ = is_split_long_or_double_ ? 1 : 0; + is_split_long_or_double_ = false; + while (param_index_ < num_params_) { + Visit(); + cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1); + param_index_++; + } + } + + private: + static size_t ComputeArgsInRegs(bool is_static, const char* shorty, uint32_t shorty_len) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + size_t args_in_regs = (is_static ? 0 : 1); + for (size_t i = 0; i < shorty_len; i++) { + char s = shorty[i]; + if (s == 'J' || s == 'D') { + args_in_regs += 2; + } else { + args_in_regs++; + } + if (args_in_regs > 3) { + args_in_regs = 3; + break; + } + } + return args_in_regs; + } + + const bool is_static_; + const char* const shorty_; + const uint32_t shorty_len_; + const size_t args_in_regs_; + const size_t num_params_; + byte* const reg_args_; + byte* const stack_args_; + byte* cur_args_; + size_t cur_arg_index_; + size_t param_index_; + // Does a 64bit parameter straddle the register and stack arguments? + bool is_split_long_or_double_; +}; + +// Visits arguments on the stack placing them into the shadow frame. +class BuildShadowFrameVisitor : public QuickArgumentVisitor { + public: + BuildShadowFrameVisitor(mirror::AbstractMethod** sp, bool is_static, const char* shorty, + uint32_t shorty_len, ShadowFrame& sf, size_t first_arg_reg) : + QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sf_(sf), cur_reg_(first_arg_reg) {} + + virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Primitive::Type type = GetParamPrimitiveType(); + switch (type) { + case Primitive::kPrimLong: // Fall-through. + case Primitive::kPrimDouble: + if (IsSplitLongOrDouble()) { + sf_.SetVRegLong(cur_reg_, ReadSplitLongParam()); + } else { + sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress())); + } + ++cur_reg_; + break; + case Primitive::kPrimNot: + sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress())); + break; + case Primitive::kPrimBoolean: // Fall-through. + case Primitive::kPrimByte: // Fall-through. + case Primitive::kPrimChar: // Fall-through. + case Primitive::kPrimShort: // Fall-through. + case Primitive::kPrimInt: // Fall-through. + case Primitive::kPrimFloat: + sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress())); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "UNREACHABLE"; + break; + } + ++cur_reg_; + } + + private: + ShadowFrame& sf_; + size_t cur_reg_; + + DISALLOW_COPY_AND_ASSIGN(BuildShadowFrameVisitor); +}; + +extern "C" uint64_t artQuickToInterpreterBridge(mirror::AbstractMethod* method, Thread* self, + mirror::AbstractMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Ensure we don't get thread suspension until the object arguments are safely in the shadow + // frame. + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); + + if (method->IsAbstract()) { + ThrowAbstractMethodError(method); + return 0; + } else { + const char* old_cause = self->StartAssertNoThreadSuspension("Building interpreter shadow frame"); + MethodHelper mh(method); + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + uint16_t num_regs = code_item->registers_size_; + void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); + ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, NULL, // No last shadow coming from quick. + method, 0, memory)); + size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_; + BuildShadowFrameVisitor shadow_frame_builder(sp, mh.IsStatic(), mh.GetShorty(), + mh.GetShortyLength(), + *shadow_frame, first_arg_reg); + shadow_frame_builder.VisitArguments(); + // Push a transition back into managed code onto the linked list in thread. + ManagedStack fragment; + self->PushManagedStackFragment(&fragment); + self->PushShadowFrame(shadow_frame); + self->EndAssertNoThreadSuspension(old_cause); + + if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) { + // Ensure static method's class is initialized. + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), + true, true)) { + DCHECK(Thread::Current()->IsExceptionPending()); + self->PopManagedStackFragment(fragment); + return 0; + } + } + + JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame); + // Pop transition. + self->PopManagedStackFragment(fragment); + return result.GetJ(); + } +} + +// Visits arguments on the stack placing them into the args vector, Object* arguments are converted +// to jobjects. +class BuildQuickArgumentVisitor : public QuickArgumentVisitor { + public: + BuildQuickArgumentVisitor(mirror::AbstractMethod** sp, bool is_static, const char* shorty, + uint32_t shorty_len, ScopedObjectAccessUnchecked* soa, + std::vector<jvalue>* args) : + QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa), args_(args) {} + + virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + jvalue val; + Primitive::Type type = GetParamPrimitiveType(); + switch (type) { + case Primitive::kPrimNot: { + mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress()); + val.l = soa_->AddLocalReference<jobject>(obj); + break; + } + case Primitive::kPrimLong: // Fall-through. + case Primitive::kPrimDouble: + if (IsSplitLongOrDouble()) { + val.j = ReadSplitLongParam(); + } else { + val.j = *reinterpret_cast<jlong*>(GetParamAddress()); + } + break; + case Primitive::kPrimBoolean: // Fall-through. + case Primitive::kPrimByte: // Fall-through. + case Primitive::kPrimChar: // Fall-through. + case Primitive::kPrimShort: // Fall-through. + case Primitive::kPrimInt: // Fall-through. + case Primitive::kPrimFloat: + val.i = *reinterpret_cast<jint*>(GetParamAddress()); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "UNREACHABLE"; + val.j = 0; + break; + } + args_->push_back(val); + } + + private: + ScopedObjectAccessUnchecked* soa_; + std::vector<jvalue>* args_; + + DISALLOW_COPY_AND_ASSIGN(BuildQuickArgumentVisitor); +}; + +// Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method +// which is responsible for recording callee save registers. We explicitly place into jobjects the +// incoming reference arguments (so they survive GC). We invoke the invocation handler, which is a +// field within the proxy object, which will box the primitive arguments and deal with error cases. +extern "C" uint64_t artQuickProxyInvokeHandler(mirror::AbstractMethod* proxy_method, + mirror::Object* receiver, + Thread* self, mirror::AbstractMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Ensure we don't get thread suspension until the object arguments are safely in jobjects. + const char* old_cause = + self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments"); + // Register the top of the managed stack, making stack crawlable. + DCHECK_EQ(*sp, proxy_method); + self->SetTopOfStack(sp, 0); + DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), + Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); + self->VerifyStack(); + // Start new JNI local reference state. + JNIEnvExt* env = self->GetJniEnv(); + ScopedObjectAccessUnchecked soa(env); + ScopedJniEnvLocalRefState env_state(env); + // Create local ref. copies of proxy method and the receiver. + jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver); + + // Placing arguments into args vector and remove the receiver. + MethodHelper proxy_mh(proxy_method); + std::vector<jvalue> args; + BuildQuickArgumentVisitor local_ref_visitor(sp, proxy_mh.IsStatic(), proxy_mh.GetShorty(), + proxy_mh.GetShortyLength(), &soa, &args); + local_ref_visitor.VisitArguments(); + args.erase(args.begin()); + + // Convert proxy method into expected interface method. + mirror::AbstractMethod* interface_method = proxy_method->FindOverriddenMethod(); + DCHECK(interface_method != NULL); + DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method); + jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method); + + // All naked Object*s should now be in jobjects, so its safe to go into the main invoke code + // that performs allocations. + self->EndAssertNoThreadSuspension(old_cause); + JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(), + rcvr_jobj, interface_method_jobj, args); + return result.GetJ(); +} + +// Read object references held in arguments from quick frames and place in a JNI local references, +// so they don't get garbage collected. +class RememberFoGcArgumentVisitor : public QuickArgumentVisitor { + public: + RememberFoGcArgumentVisitor(mirror::AbstractMethod** sp, bool is_static, const char* shorty, + uint32_t shorty_len, ScopedObjectAccessUnchecked* soa) : + QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa) {} + + virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (IsParamAReference()) { + soa_->AddLocalReference<jobject>(*reinterpret_cast<mirror::Object**>(GetParamAddress())); + } + } + + private: + ScopedObjectAccessUnchecked* soa_; + + DISALLOW_COPY_AND_ASSIGN(RememberFoGcArgumentVisitor); +}; + +// Lazily resolve a method for quick. Called by stub code. +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + Thread* thread, mirror::AbstractMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs); + // Start new JNI local reference state + JNIEnvExt* env = thread->GetJniEnv(); + ScopedObjectAccessUnchecked soa(env); + ScopedJniEnvLocalRefState env_state(env); + const char* old_cause = thread->StartAssertNoThreadSuspension("Quick method resolution set up"); + + // Compute details about the called method (avoid GCs) + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + mirror::AbstractMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp); + InvokeType invoke_type; + const DexFile* dex_file; + uint32_t dex_method_idx; + if (called->IsRuntimeMethod()) { + uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp)); + const DexFile::CodeItem* code; + { + MethodHelper mh(caller); + dex_file = &mh.GetDexFile(); + code = mh.GetCodeItem(); + } + CHECK_LT(dex_pc, code->insns_size_in_code_units_); + const Instruction* instr = Instruction::At(&code->insns_[dex_pc]); + Instruction::Code instr_code = instr->Opcode(); + bool is_range; + switch (instr_code) { + case Instruction::INVOKE_DIRECT: + invoke_type = kDirect; + is_range = false; + break; + case Instruction::INVOKE_DIRECT_RANGE: + invoke_type = kDirect; + is_range = true; + break; + case Instruction::INVOKE_STATIC: + invoke_type = kStatic; + is_range = false; + break; + case Instruction::INVOKE_STATIC_RANGE: + invoke_type = kStatic; + is_range = true; + break; + case Instruction::INVOKE_SUPER: + invoke_type = kSuper; + is_range = false; + break; + case Instruction::INVOKE_SUPER_RANGE: + invoke_type = kSuper; + is_range = true; + break; + case Instruction::INVOKE_VIRTUAL: + invoke_type = kVirtual; + is_range = false; + break; + case Instruction::INVOKE_VIRTUAL_RANGE: + invoke_type = kVirtual; + is_range = true; + break; + case Instruction::INVOKE_INTERFACE: + invoke_type = kInterface; + is_range = false; + break; + case Instruction::INVOKE_INTERFACE_RANGE: + invoke_type = kInterface; + is_range = true; + break; + default: + LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(NULL); + // Avoid used uninitialized warnings. + invoke_type = kDirect; + is_range = false; + } + dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c(); + + } else { + invoke_type = kStatic; + dex_file = &MethodHelper(called).GetDexFile(); + dex_method_idx = called->GetDexMethodIndex(); + } + uint32_t shorty_len; + const char* shorty = + dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), &shorty_len); + RememberFoGcArgumentVisitor visitor(sp, invoke_type == kStatic, shorty, shorty_len, &soa); + visitor.VisitArguments(); + thread->EndAssertNoThreadSuspension(old_cause); + // Resolve method filling in dex cache. + if (called->IsRuntimeMethod()) { + called = linker->ResolveMethod(dex_method_idx, caller, invoke_type); + } + const void* code = NULL; + if (LIKELY(!thread->IsExceptionPending())) { + // Incompatible class change should have been handled in resolve method. + CHECK(!called->CheckIncompatibleClassChange(invoke_type)); + // Refine called method based on receiver. + if (invoke_type == kVirtual) { + called = receiver->GetClass()->FindVirtualMethodForVirtual(called); + } else if (invoke_type == kInterface) { + called = receiver->GetClass()->FindVirtualMethodForInterface(called); + } + // Ensure that the called method's class is initialized. + mirror::Class* called_class = called->GetDeclaringClass(); + linker->EnsureInitialized(called_class, true, true); + if (LIKELY(called_class->IsInitialized())) { + code = called->GetEntryPointFromCompiledCode(); + } else if (called_class->IsInitializing()) { + if (invoke_type == kStatic) { + // Class is still initializing, go to oat and grab code (trampoline must be left in place + // until class is initialized to stop races between threads). + code = linker->GetOatCodeFor(called); + } else { + // No trampoline for non-static methods. + code = called->GetEntryPointFromCompiledCode(); + } + } else { + DCHECK(called_class->IsErroneous()); + } + } + CHECK_EQ(code == NULL, thread->IsExceptionPending()); +#ifdef MOVING_GARBAGE_COLLECTOR + // TODO: locally saved objects may have moved during a GC during resolution. Need to update the + // registers so that the stale objects aren't passed to the method we've resolved. + UNIMPLEMENTED(WARNING); +#endif + // Place called method in callee-save frame to be placed as first argument to quick method. + *sp = called; + return code; +} + +} // namespace art |