diff options
author | 2022-07-19 13:45:59 +0000 | |
---|---|---|
committer | 2022-07-19 15:34:45 +0000 | |
commit | 27aecbb0180a98a672a8ab96b763d0864105d266 (patch) | |
tree | b0101b055267dc35adf1f31890baf413bfc3ab94 | |
parent | 9be8b3fea4dc69b112bb9e269c1cb4bc66fde885 (diff) |
Revert "Use the thread local cache in interpreter / unresolved entrypoints"
This reverts commit 16032a421a08fb6396de8e8f168a62fe29a0f7ad.
Reason for revert: Need to handle non-read barrier GCs.
Change-Id: Ic3c1cbc97f4d62e701fdcecff9598295cf79e748
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils-inl.h | 249 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils.h | 11 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 12 | ||||
-rw-r--r-- | runtime/interpreter/interpreter.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 40 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.h | 105 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_intrinsics.cc | 678 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_intrinsics.h | 41 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl-inl.h | 2 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp.cc | 7 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime_test.cc | 6 |
12 files changed, 964 insertions, 190 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 172d857f4b..8e77b43906 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -173,6 +173,7 @@ libart_cc_defaults { "interpreter/interpreter.cc", "interpreter/interpreter_cache.cc", "interpreter/interpreter_common.cc", + "interpreter/interpreter_intrinsics.cc", "interpreter/interpreter_switch_impl0.cc", "interpreter/interpreter_switch_impl1.cc", "interpreter/interpreter_switch_impl2.cc", diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index b454d49119..4ee1013816 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -486,131 +486,6 @@ EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite); #undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL #undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL -static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx) - REQUIRES_SHARED(Locks::mutator_lock_) { - const dex::MethodId& method_id = dex_file->GetMethodId(method_idx); - const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_); - const char* method_name = dex_file->GetMethodName(method_id); - // Instead of calling ResolveMethod() which has suspend point and can trigger - // GC, look up the method symbolically. - // Compare method's class name and method name against string init. - // It's ok since it's not allowed to create your own java/lang/String. - // TODO: verify that assumption. - if ((strcmp(class_name, "Ljava/lang/String;") == 0) && - (strcmp(method_name, "<init>") == 0)) { - return true; - } - return false; -} - -static inline bool IsStringInit(const Instruction& instr, ArtMethod* caller) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (instr.Opcode() == Instruction::INVOKE_DIRECT || - instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) { - uint16_t callee_method_idx = (instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) ? - instr.VRegB_3rc() : instr.VRegB_35c(); - return IsStringInit(caller->GetDexFile(), callee_method_idx); - } - return false; -} - -extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr); - -template <InvokeType type> -ArtMethod* FindMethodToCall(Thread* self, - ArtMethod* caller, - ObjPtr<mirror::Object>* this_object, - const Instruction& inst, - /*out*/ bool* string_init) - REQUIRES_SHARED(Locks::mutator_lock_) { - PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - - // Try to find the method in thread-local cache. - size_t tls_value = 0u; - if (!self->GetInterpreterCache()->Get(self, &inst, &tls_value)) { - DCHECK(!self->IsExceptionPending()); - // NterpGetMethod can suspend, so save this_object. - StackHandleScope<1> hs(self); - HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object)); - tls_value = NterpGetMethod(self, caller, reinterpret_cast<const uint16_t*>(&inst)); - if (self->IsExceptionPending()) { - return nullptr; - } - } - - if (type != kStatic && UNLIKELY((*this_object) == nullptr)) { - if (UNLIKELY(IsStringInit(inst, caller))) { - // Hack for String init: - // - // We assume that the input of String.<init> in verified code is always - // an uninitialized reference. If it is a null constant, it must have been - // optimized out by the compiler and we arrive here after deoptimization. - // Do not throw NullPointerException. - } else { - // Maintain interpreter-like semantics where NullPointerException is thrown - // after potential NoSuchMethodError from class linker. - const uint32_t method_idx = inst.VRegB(); - ThrowNullPointerExceptionForMethodAccess(method_idx, type); - return nullptr; - } - } - - static constexpr size_t kStringInitMethodFlag = 0b1; - static constexpr size_t kInvokeInterfaceOnObjectMethodFlag = 0b1; - static constexpr size_t kDefaultMethodFlag = 0b10; - static constexpr size_t kMethodMask = ~0b11; - - ArtMethod* called_method = nullptr; - switch (type) { - case kDirect: - case kSuper: - case kStatic: - // Note: for the interpreter, the String.<init> special casing for invocation is handled - // in DoCallCommon. - *string_init = ((tls_value & kStringInitMethodFlag) != 0); - DCHECK_EQ(*string_init, IsStringInit(inst, caller)); - called_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask); - break; - case kInterface: - if ((tls_value & kInvokeInterfaceOnObjectMethodFlag) != 0) { - // invokeinterface on a j.l.Object method. - uint16_t method_index = tls_value >> 16; - called_method = (*this_object)->GetClass()->GetVTableEntry(method_index, pointer_size); - } else { - ArtMethod* interface_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask); - if ((tls_value & kDefaultMethodFlag) != 0) { - // Default non-abstract method. - called_method = (*this_object)->GetClass()->GetImt(pointer_size)->Get( - interface_method->GetMethodIndex(), pointer_size); - } else { - // Regular abstract interface method. - called_method = (*this_object)->GetClass()->GetImt(pointer_size)->Get( - interface_method->GetImtIndex(), pointer_size); - } - if (called_method->IsRuntimeMethod()) { - called_method = (*this_object)->GetClass()->FindVirtualMethodForInterface( - interface_method, pointer_size); - if (UNLIKELY(called_method == nullptr)) { - ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch( - interface_method, *this_object, caller); - return nullptr; - } - } - } - break; - case kVirtual: - called_method = (*this_object)->GetClass()->GetVTableEntry(tls_value, pointer_size); - break; - } - - if (UNLIKELY(!called_method->IsInvokable())) { - called_method->ThrowInvocationTimeError((type == kStatic) ? nullptr : *this_object); - return nullptr; - } - DCHECK(!called_method->IsRuntimeMethod()) << called_method->PrettyMethod(); - return called_method; -} - template<bool access_check> ALWAYS_INLINE ArtMethod* FindSuperMethodToCall(uint32_t method_idx, ArtMethod* resolved_method, @@ -671,6 +546,130 @@ ALWAYS_INLINE ArtMethod* FindSuperMethodToCall(uint32_t method_idx, return super_class->GetVTableEntry(vtable_index, linker->GetImagePointerSize()); } +// Follow virtual/interface indirections if applicable. +// Will throw null-pointer exception the if the object is null. +template<InvokeType type, bool access_check> +ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx, + ArtMethod* resolved_method, + ObjPtr<mirror::Object>* this_object, + ArtMethod* referrer, + Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + // Null pointer check. + if (UNLIKELY(*this_object == nullptr && type != kStatic)) { + if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() && + resolved_method->IsConstructor())) { + // Hack for String init: + // + // We assume that the input of String.<init> in verified code is always + // an unitialized reference. If it is a null constant, it must have been + // optimized out by the compiler. Do not throw NullPointerException. + } else { + // Maintain interpreter-like semantics where NullPointerException is thrown + // after potential NoSuchMethodError from class linker. + ThrowNullPointerExceptionForMethodAccess(method_idx, type); + return nullptr; // Failure. + } + } + switch (type) { + case kStatic: + case kDirect: + return resolved_method; + case kVirtual: { + ObjPtr<mirror::Class> klass = (*this_object)->GetClass(); + uint16_t vtable_index = resolved_method->GetMethodIndex(); + if (access_check && + (!klass->HasVTable() || + vtable_index >= static_cast<uint32_t>(klass->GetVTableLength()))) { + // Behavior to agree with that of the verifier. + ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), + resolved_method->GetName(), resolved_method->GetSignature()); + return nullptr; // Failure. + } + DCHECK(klass->HasVTable()) << klass->PrettyClass(); + return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize()); + } + case kSuper: { + return FindSuperMethodToCall<access_check>(method_idx, resolved_method, referrer, self); + } + case kInterface: { + size_t imt_index = resolved_method->GetImtIndex(); + PointerSize pointer_size = class_linker->GetImagePointerSize(); + ObjPtr<mirror::Class> klass = (*this_object)->GetClass(); + ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size); + if (!imt_method->IsRuntimeMethod()) { + if (kIsDebugBuild) { + ArtMethod* method = klass->FindVirtualMethodForInterface( + resolved_method, class_linker->GetImagePointerSize()); + CHECK_EQ(imt_method, method) << ArtMethod::PrettyMethod(resolved_method) << " / " + << imt_method->PrettyMethod() << " / " + << ArtMethod::PrettyMethod(method) << " / " + << klass->PrettyClass(); + } + return imt_method; + } else { + ArtMethod* interface_method = klass->FindVirtualMethodForInterface( + resolved_method, class_linker->GetImagePointerSize()); + if (UNLIKELY(interface_method == nullptr)) { + ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, + *this_object, referrer); + return nullptr; // Failure. + } + return interface_method; + } + } + default: + LOG(FATAL) << "Unknown invoke type " << type; + return nullptr; // Failure. + } +} + +template<InvokeType type, bool access_check> +inline ArtMethod* FindMethodFromCode(uint32_t method_idx, + ObjPtr<mirror::Object>* this_object, + ArtMethod* referrer, + Thread* self) { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + constexpr ClassLinker::ResolveMode resolve_mode = + access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE + : ClassLinker::ResolveMode::kNoChecks; + ArtMethod* resolved_method; + if (type == kStatic) { + resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type); + } else { + StackHandleScope<1> hs(self); + HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object)); + resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type); + } + if (UNLIKELY(resolved_method == nullptr)) { + DCHECK(self->IsExceptionPending()); // Throw exception and unwind. + return nullptr; // Failure. + } + return FindMethodToCall<type, access_check>( + method_idx, resolved_method, this_object, referrer, self); +} + +// Explicit template declarations of FindMethodFromCode for all invoke types. +#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \ + template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \ + ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \ + ObjPtr<mirror::Object>* this_object, \ + ArtMethod* referrer, \ + Thread* self) +#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \ + EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \ + EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true) + +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper); +EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface); + +#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL +#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL + inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx, ArtMethod* referrer, Thread* self, diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index ae5687506a..8b6fc69bea 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -143,12 +143,11 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -template<InvokeType type> -inline ArtMethod* FindMethodToCall(Thread* self, - ArtMethod* referrer, - ObjPtr<mirror::Object>* this_object, - const Instruction& inst, - /*out*/ bool* string_init) +template<InvokeType type, bool access_check> +inline ArtMethod* FindMethodFromCode(uint32_t method_idx, + ObjPtr<mirror::Object>* this_object, + ArtMethod* referrer, + Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index e7fa1484dc..fc7711027f 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2267,18 +2267,12 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, uint32_t shorty_len; const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len); { - // Remember the args in case a GC happens in FindMethodToCall. + // Remember the args in case a GC happens in FindMethodFromCode. ScopedObjectAccessUnchecked soa(self->GetJniEnv()); RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa); visitor.VisitArguments(); - - uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); - CodeItemInstructionAccessor accessor(caller_method->DexInstructions()); - CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits()); - const Instruction& instr = accessor.InstructionAt(dex_pc); - bool string_init = false; - method = FindMethodToCall<type>(self, caller_method, &this_object, instr, &string_init); - + method = FindMethodFromCode<type, /*access_check=*/true>( + method_idx, &this_object, caller_method, self); visitor.FixupReferences(); } diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 6f0a5c9212..0ffc38beeb 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -511,7 +511,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, new_dex_pc = dex_pc + instr->SizeInCodeUnits(); } else if (instr->IsInvoke()) { DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault); - if (IsStringInit(*instr, shadow_frame->GetMethod())) { + if (IsStringInit(instr, shadow_frame->GetMethod())) { uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr); // Move the StringFactory.newStringFromChars() result into the register representing // "this object" when invoking the string constructor in the original dex instruction. diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 69a9c0eb85..c6b2ddc964 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -242,8 +242,7 @@ static ALWAYS_INLINE bool DoCallCommon(ArtMethod* called_method, JValue* result, uint16_t number_of_inputs, uint32_t (&arg)[Instruction::kMaxVarArgRegs], - uint32_t vregC, - bool string_init) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_); template <bool is_range> ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame, @@ -1195,8 +1194,15 @@ static inline bool DoCallCommon(ArtMethod* called_method, JValue* result, uint16_t number_of_inputs, uint32_t (&arg)[Instruction::kMaxVarArgRegs], - uint32_t vregC, - bool string_init) { + uint32_t vregC) { + bool string_init = false; + // Replace calls to String.<init> with equivalent StringFactory call. + if (UNLIKELY(called_method->GetDeclaringClass()->IsStringClass() + && called_method->IsConstructor())) { + called_method = WellKnownClasses::StringInitToStringFactory(called_method); + string_init = true; + } + // Compute method information. CodeItemDataAccessor accessor(called_method->DexInstructionData()); // Number of registers for the callee's call frame. @@ -1387,13 +1393,8 @@ static inline bool DoCallCommon(ArtMethod* called_method, template<bool is_range, bool do_assignability_check> NO_STACK_PROTECTOR -bool DoCall(ArtMethod* called_method, - Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - bool is_string_init, - JValue* result) { +bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, JValue* result) { // Argument word count. const uint16_t number_of_inputs = (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data); @@ -1410,14 +1411,8 @@ bool DoCall(ArtMethod* called_method, } return DoCallCommon<is_range, do_assignability_check>( - called_method, - self, - shadow_frame, - result, - number_of_inputs, - arg, - vregC, - is_string_init); + called_method, self, shadow_frame, + result, number_of_inputs, arg, vregC); } template <bool is_range, bool do_access_check, bool transaction_active> @@ -1544,12 +1539,9 @@ void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count // Explicit DoCall template function declarations. #define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \ template REQUIRES_SHARED(Locks::mutator_lock_) \ - bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, \ - Thread* self, \ + bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Thread* self, \ ShadowFrame& shadow_frame, \ - const Instruction* inst, \ - uint16_t inst_data, \ - bool string_init, \ + const Instruction* inst, uint16_t inst_data, \ JValue* result) EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false); EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 49d7e649ea..fe9cf57ec5 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -20,6 +20,7 @@ #include "android-base/macros.h" #include "instrumentation.h" #include "interpreter.h" +#include "interpreter_intrinsics.h" #include "transaction.h" #include <math.h> @@ -125,13 +126,8 @@ void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count // DoFastInvoke and DoInvokeVirtualQuick functions. // Returns true on success, otherwise throws an exception and returns false. template<bool is_range, bool do_assignability_check> -bool DoCall(ArtMethod* called_method, - Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - bool string_init, - JValue* result); +bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, uint16_t inst_data, JValue* result); // Called by the switch interpreter to know if we can stay in it. bool ShouldStayInSwitchInterpreter(ArtMethod* method) @@ -224,7 +220,7 @@ static inline ALWAYS_INLINE void PerformNonStandardReturn( // Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range]. // Returns true on success, otherwise throws an exception and returns false. -template<InvokeType type, bool is_range, bool do_access_check> +template<InvokeType type, bool is_range, bool do_access_check, bool is_mterp> static ALWAYS_INLINE bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -235,19 +231,68 @@ static ALWAYS_INLINE bool DoInvoke(Thread* self, if (UNLIKELY(self->ObserveAsyncException())) { return false; } - const uint32_t vregC = is_range ? inst->VRegC_3rc() : inst->VRegC_35c(); - ObjPtr<mirror::Object> obj = type == kStatic ? nullptr : shadow_frame.GetVRegReference(vregC); + const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); ArtMethod* sf_method = shadow_frame.GetMethod(); - bool string_init = false; - ArtMethod* called_method = FindMethodToCall<type>(self, sf_method, &obj, *inst, &string_init); - if (called_method == nullptr) { - DCHECK(self->IsExceptionPending()); + + // Try to find the method in small thread-local cache first (only used when + // nterp is not used as mterp and nterp use the cache in an incompatible way). + InterpreterCache* tls_cache = self->GetInterpreterCache(); + size_t tls_value; + ArtMethod* resolved_method; + if (!IsNterpSupported() && LIKELY(tls_cache->Get(self, inst, &tls_value))) { + resolved_method = reinterpret_cast<ArtMethod*>(tls_value); + } else { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + constexpr ClassLinker::ResolveMode resolve_mode = + do_access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE + : ClassLinker::ResolveMode::kNoChecks; + resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, sf_method, type); + if (UNLIKELY(resolved_method == nullptr)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } + if (!IsNterpSupported()) { + tls_cache->Set(self, inst, reinterpret_cast<size_t>(resolved_method)); + } + } + + // Null pointer check and virtual method resolution. + ArtMethod* called_method = nullptr; + { + // `FindMethodToCall` might suspend, so don't keep `receiver` as a local + // variable after the call. + ObjPtr<mirror::Object> receiver = + (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC); + called_method = FindMethodToCall<type, do_access_check>( + method_idx, resolved_method, &receiver, sf_method, self); + if (UNLIKELY(called_method == nullptr)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return false; + } + } + if (UNLIKELY(!called_method->IsInvokable())) { + called_method->ThrowInvocationTimeError( + (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC)); result->SetJ(0); return false; } - return DoCall<is_range, do_access_check>( - called_method, self, shadow_frame, inst, inst_data, string_init, result); + jit::Jit* jit = Runtime::Current()->GetJit(); + if (is_mterp && !is_range && called_method->IsIntrinsic()) { + if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data, + shadow_frame.GetResultRegister())) { + if (jit != nullptr && sf_method != nullptr) { + jit->NotifyInterpreterToCompiledCodeTransition(self, sf_method); + } + return !self->IsExceptionPending(); + } + } + + return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data, + result); } static inline ObjPtr<mirror::MethodHandle> ResolveMethodHandle(Thread* self, @@ -715,6 +760,34 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, uint16_t arg_offset, JValue* result); +static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + const dex::MethodId& method_id = dex_file->GetMethodId(method_idx); + const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_); + const char* method_name = dex_file->GetMethodName(method_id); + // Instead of calling ResolveMethod() which has suspend point and can trigger + // GC, look up the method symbolically. + // Compare method's class name and method name against string init. + // It's ok since it's not allowed to create your own java/lang/String. + // TODO: verify that assumption. + if ((strcmp(class_name, "Ljava/lang/String;") == 0) && + (strcmp(method_name, "<init>") == 0)) { + return true; + } + return false; +} + +static inline bool IsStringInit(const Instruction* instr, ArtMethod* caller) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (instr->Opcode() == Instruction::INVOKE_DIRECT || + instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) { + uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ? + instr->VRegB_3rc() : instr->VRegB_35c(); + return IsStringInit(caller->GetDexFile(), callee_method_idx); + } + return false; +} + // Set string value created from StringFactory.newStringFromXXX() into all aliases of // StringFactory.newEmptyString(). void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc new file mode 100644 index 0000000000..c8344bc760 --- /dev/null +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2017 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 "interpreter/interpreter_intrinsics.h" + +#include "dex/dex_instruction.h" +#include "intrinsics_enum.h" +#include "interpreter/interpreter_common.h" + +namespace art { +namespace interpreter { + + +#define BINARY_INTRINSIC(name, op, get1, get2, set) \ +static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result_register) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \ + inst->GetVarArgs(arg, inst_data); \ + result_register->set(op(shadow_frame->get1, shadow_frame->get2)); \ + return true; \ +} + +#define BINARY_II_INTRINSIC(name, op, set) \ + BINARY_INTRINSIC(name, op, GetVReg(arg[0]), GetVReg(arg[1]), set) + +#define BINARY_JJ_INTRINSIC(name, op, set) \ + BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVRegLong(arg[2]), set) + +#define BINARY_JI_INTRINSIC(name, op, set) \ + BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVReg(arg[2]), set) + +#define UNARY_INTRINSIC(name, op, get, set) \ +static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result_register) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \ + inst->GetVarArgs(arg, inst_data); \ + result_register->set(op(shadow_frame->get(arg[0]))); \ + return true; \ +} + + +// java.lang.Integer.reverse(I)I +UNARY_INTRINSIC(MterpIntegerReverse, ReverseBits32, GetVReg, SetI); + +// java.lang.Integer.reverseBytes(I)I +UNARY_INTRINSIC(MterpIntegerReverseBytes, BSWAP, GetVReg, SetI); + +// java.lang.Integer.bitCount(I)I +UNARY_INTRINSIC(MterpIntegerBitCount, POPCOUNT, GetVReg, SetI); + +// java.lang.Integer.compare(II)I +BINARY_II_INTRINSIC(MterpIntegerCompare, Compare, SetI); + +// java.lang.Integer.highestOneBit(I)I +UNARY_INTRINSIC(MterpIntegerHighestOneBit, HighestOneBitValue, GetVReg, SetI); + +// java.lang.Integer.LowestOneBit(I)I +UNARY_INTRINSIC(MterpIntegerLowestOneBit, LowestOneBitValue, GetVReg, SetI); + +// java.lang.Integer.numberOfLeadingZeros(I)I +UNARY_INTRINSIC(MterpIntegerNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVReg, SetI); + +// java.lang.Integer.numberOfTrailingZeros(I)I +UNARY_INTRINSIC(MterpIntegerNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVReg, SetI); + +// java.lang.Integer.rotateRight(II)I +BINARY_II_INTRINSIC(MterpIntegerRotateRight, (Rot<int32_t, false>), SetI); + +// java.lang.Integer.rotateLeft(II)I +BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot<int32_t, true>), SetI); + +// java.lang.Integer.signum(I)I +UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI); + +// java.lang.Long.reverse(J)J +UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ); + +// java.lang.Long.reverseBytes(J)J +UNARY_INTRINSIC(MterpLongReverseBytes, BSWAP, GetVRegLong, SetJ); + +// java.lang.Long.bitCount(J)I +UNARY_INTRINSIC(MterpLongBitCount, POPCOUNT, GetVRegLong, SetI); + +// java.lang.Long.compare(JJ)I +BINARY_JJ_INTRINSIC(MterpLongCompare, Compare, SetI); + +// java.lang.Long.highestOneBit(J)J +UNARY_INTRINSIC(MterpLongHighestOneBit, HighestOneBitValue, GetVRegLong, SetJ); + +// java.lang.Long.lowestOneBit(J)J +UNARY_INTRINSIC(MterpLongLowestOneBit, LowestOneBitValue, GetVRegLong, SetJ); + +// java.lang.Long.numberOfLeadingZeros(J)I +UNARY_INTRINSIC(MterpLongNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVRegLong, SetJ); + +// java.lang.Long.numberOfTrailingZeros(J)I +UNARY_INTRINSIC(MterpLongNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVRegLong, SetJ); + +// java.lang.Long.rotateRight(JI)J +BINARY_JI_INTRINSIC(MterpLongRotateRight, (Rot<int64_t, false>), SetJ); + +// java.lang.Long.rotateLeft(JI)J +BINARY_JI_INTRINSIC(MterpLongRotateLeft, (Rot<int64_t, true>), SetJ); + +// java.lang.Long.signum(J)I +UNARY_INTRINSIC(MterpLongSignum, Signum, GetVRegLong, SetI); + +// java.lang.Short.reverseBytes(S)S +UNARY_INTRINSIC(MterpShortReverseBytes, BSWAP, GetVRegShort, SetS); + +// java.lang.Math.min(II)I +BINARY_II_INTRINSIC(MterpMathMinIntInt, std::min, SetI); + +// java.lang.Math.min(JJ)J +BINARY_JJ_INTRINSIC(MterpMathMinLongLong, std::min, SetJ); + +// java.lang.Math.max(II)I +BINARY_II_INTRINSIC(MterpMathMaxIntInt, std::max, SetI); + +// java.lang.Math.max(JJ)J +BINARY_JJ_INTRINSIC(MterpMathMaxLongLong, std::max, SetJ); + +// java.lang.Math.abs(I)I +UNARY_INTRINSIC(MterpMathAbsInt, std::abs, GetVReg, SetI); + +// java.lang.Math.abs(J)J +UNARY_INTRINSIC(MterpMathAbsLong, std::abs, GetVRegLong, SetJ); + +// java.lang.Math.abs(F)F +UNARY_INTRINSIC(MterpMathAbsFloat, 0x7fffffff&, GetVReg, SetI); + +// java.lang.Math.abs(D)D +UNARY_INTRINSIC(MterpMathAbsDouble, INT64_C(0x7fffffffffffffff)&, GetVRegLong, SetJ); + +// java.lang.Math.sqrt(D)D +UNARY_INTRINSIC(MterpMathSqrt, std::sqrt, GetVRegDouble, SetD); + +// java.lang.Math.ceil(D)D +UNARY_INTRINSIC(MterpMathCeil, std::ceil, GetVRegDouble, SetD); + +// java.lang.Math.floor(D)D +UNARY_INTRINSIC(MterpMathFloor, std::floor, GetVRegDouble, SetD); + +// java.lang.Math.sin(D)D +UNARY_INTRINSIC(MterpMathSin, std::sin, GetVRegDouble, SetD); + +// java.lang.Math.cos(D)D +UNARY_INTRINSIC(MterpMathCos, std::cos, GetVRegDouble, SetD); + +// java.lang.Math.tan(D)D +UNARY_INTRINSIC(MterpMathTan, std::tan, GetVRegDouble, SetD); + +// java.lang.Math.asin(D)D +UNARY_INTRINSIC(MterpMathAsin, std::asin, GetVRegDouble, SetD); + +// java.lang.Math.acos(D)D +UNARY_INTRINSIC(MterpMathAcos, std::acos, GetVRegDouble, SetD); + +// java.lang.Math.atan(D)D +UNARY_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD); + +// java.lang.String.charAt(I)C +static ALWAYS_INLINE bool MterpStringCharAt(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); + int length = str->GetLength(); + int index = shadow_frame->GetVReg(arg[1]); + uint16_t res; + if (UNLIKELY(index < 0) || (index >= length)) { + return false; // Punt and let non-intrinsic version deal with the throw. + } + if (str->IsCompressed()) { + res = str->GetValueCompressed()[index]; + } else { + res = str->GetValue()[index]; + } + result_register->SetC(res); + return true; +} + +// java.lang.String.compareTo(Ljava/lang/string)I +static ALWAYS_INLINE bool MterpStringCompareTo(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); + ObjPtr<mirror::Object> arg1 = shadow_frame->GetVRegReference(arg[1]); + if (arg1 == nullptr) { + return false; + } + result_register->SetI(str->CompareTo(arg1->AsString())); + return true; +} + +#define STRING_INDEXOF_INTRINSIC(name, starting_pos) \ +static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result_register) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \ + inst->GetVarArgs(arg, inst_data); \ + ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \ + int ch = shadow_frame->GetVReg(arg[1]); \ + if (ch >= 0x10000) { \ + /* Punt if supplementary char. */ \ + return false; \ + } \ + result_register->SetI(str->FastIndexOf(ch, starting_pos)); \ + return true; \ +} + +// java.lang.String.indexOf(I)I +STRING_INDEXOF_INTRINSIC(StringIndexOf, 0); + +// java.lang.String.indexOf(II)I +STRING_INDEXOF_INTRINSIC(StringIndexOfAfter, shadow_frame->GetVReg(arg[2])); + +#define SIMPLE_STRING_INTRINSIC(name, operation) \ +static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result_register) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \ + inst->GetVarArgs(arg, inst_data); \ + ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \ + result_register->operation; \ + return true; \ +} + +// java.lang.String.isEmpty()Z +SIMPLE_STRING_INTRINSIC(StringIsEmpty, SetZ(str->GetLength() == 0)) + +// java.lang.String.length()I +SIMPLE_STRING_INTRINSIC(StringLength, SetI(str->GetLength())) + +// java.lang.String.getCharsNoCheck(II[CI)V +static ALWAYS_INLINE bool MterpStringGetCharsNoCheck(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Start, end & index already checked by caller - won't throw. Destination is uncompressed. + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); + int32_t start = shadow_frame->GetVReg(arg[1]); + int32_t end = shadow_frame->GetVReg(arg[2]); + int32_t index = shadow_frame->GetVReg(arg[4]); + ObjPtr<mirror::CharArray> array = shadow_frame->GetVRegReference(arg[3])->AsCharArray(); + uint16_t* dst = array->GetData() + index; + int32_t len = (end - start); + if (str->IsCompressed()) { + const uint8_t* src_8 = str->GetValueCompressed() + start; + for (int i = 0; i < len; i++) { + dst[i] = src_8[i]; + } + } else { + uint16_t* src_16 = str->GetValue() + start; + memcpy(dst, src_16, len * sizeof(uint16_t)); + } + return true; +} + +// java.lang.String.equalsLjava/lang/Object;)Z +static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); + ObjPtr<mirror::Object> obj = shadow_frame->GetVRegReference(arg[1]); + bool res = false; // Assume not equal. + if ((obj != nullptr) && obj->IsString()) { + ObjPtr<mirror::String> str2 = obj->AsString(); + if (str->GetCount() == str2->GetCount()) { + // Length & compression status are same. Can use block compare. + void* bytes1; + void* bytes2; + int len = str->GetLength(); + if (str->IsCompressed()) { + bytes1 = str->GetValueCompressed(); + bytes2 = str2->GetValueCompressed(); + } else { + len *= sizeof(uint16_t); + bytes1 = str->GetValue(); + bytes2 = str2->GetValue(); + } + res = (memcmp(bytes1, bytes2, len) == 0); + } + } + result_register->SetZ(res); + return true; +} + +#define VARHANDLE_FENCE_INTRINSIC(name, std_memory_operation) \ +static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, \ + const Instruction* inst ATTRIBUTE_UNUSED, \ + uint16_t inst_data ATTRIBUTE_UNUSED, \ + JValue* result_register ATTRIBUTE_UNUSED) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + std::atomic_thread_fence(std_memory_operation); \ + return true; \ +} + +// The VarHandle fence methods are static (unlike jdk.internal.misc.Unsafe versions). +// The fences for the LoadLoadFence and StoreStoreFence are stronger +// than strictly required, but the impact should be marginal. +VARHANDLE_FENCE_INTRINSIC(MterpVarHandleFullFence, std::memory_order_seq_cst) +VARHANDLE_FENCE_INTRINSIC(MterpVarHandleAcquireFence, std::memory_order_acquire) +VARHANDLE_FENCE_INTRINSIC(MterpVarHandleReleaseFence, std::memory_order_release) +VARHANDLE_FENCE_INTRINSIC(MterpVarHandleLoadLoadFence, std::memory_order_acquire) +VARHANDLE_FENCE_INTRINSIC(MterpVarHandleStoreStoreFence, std::memory_order_release) + +#define METHOD_HANDLE_INVOKE_INTRINSIC(name) \ +static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) { \ + return DoInvokePolymorphic<false>(Thread::Current(), *shadow_frame, inst, inst_data, result); \ + } else { \ + return DoInvokePolymorphic<true>(Thread::Current(), *shadow_frame, inst, inst_data, result); \ + } \ +} + +METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvokeExact) +METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvoke) + +#define VAR_HANDLE_ACCESSOR_INTRINSIC(name) \ +static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + return Do##name(Thread::Current(), *shadow_frame, inst, inst_data, result); \ +} + +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchange) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndSet) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGet); +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAdd) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAnd) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOr) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXor) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSet) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetOpaque) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetVolatile) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSet) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetOpaque) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetRelease) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetVolatile) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSet) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetAcquire) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain) +VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease) + +static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, + const Instruction* inst ATTRIBUTE_UNUSED, + uint16_t inst_data ATTRIBUTE_UNUSED, + JValue* result_register ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Do nothing; Its only purpose is to keep the argument reference live + // at preceding suspend points. That's automatic in the interpreter. + return true; +} + +// Macro to help keep track of what's left to implement. +#define UNIMPLEMENTED_CASE(name) \ + case Intrinsics::k##name: \ + res = false; \ + break; + +#define INTRINSIC_CASE(name) \ + case Intrinsics::k##name: \ + res = Mterp##name(shadow_frame, inst, inst_data, result_register); \ + break; + +bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, + ArtMethod* const called_method, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + Intrinsics intrinsic = static_cast<Intrinsics>(called_method->GetIntrinsic()); + bool res = false; // Assume failure + switch (intrinsic) { + UNIMPLEMENTED_CASE(DoubleDoubleToRawLongBits /* (D)J */) + UNIMPLEMENTED_CASE(DoubleDoubleToLongBits /* (D)J */) + UNIMPLEMENTED_CASE(DoubleIsInfinite /* (D)Z */) + UNIMPLEMENTED_CASE(DoubleIsNaN /* (D)Z */) + UNIMPLEMENTED_CASE(DoubleLongBitsToDouble /* (J)D */) + UNIMPLEMENTED_CASE(FloatFloatToRawIntBits /* (F)I */) + UNIMPLEMENTED_CASE(FloatFloatToIntBits /* (F)I */) + UNIMPLEMENTED_CASE(FloatIsInfinite /* (F)Z */) + UNIMPLEMENTED_CASE(FloatIsNaN /* (F)Z */) + UNIMPLEMENTED_CASE(FloatIntBitsToFloat /* (I)F */) + UNIMPLEMENTED_CASE(IntegerDivideUnsigned /* (II)I */) + UNIMPLEMENTED_CASE(LongDivideUnsigned /* (JJ)J */) + INTRINSIC_CASE(IntegerReverse) + INTRINSIC_CASE(IntegerReverseBytes) + INTRINSIC_CASE(IntegerBitCount) + INTRINSIC_CASE(IntegerCompare) + INTRINSIC_CASE(IntegerHighestOneBit) + INTRINSIC_CASE(IntegerLowestOneBit) + INTRINSIC_CASE(IntegerNumberOfLeadingZeros) + INTRINSIC_CASE(IntegerNumberOfTrailingZeros) + INTRINSIC_CASE(IntegerRotateRight) + INTRINSIC_CASE(IntegerRotateLeft) + INTRINSIC_CASE(IntegerSignum) + INTRINSIC_CASE(LongReverse) + INTRINSIC_CASE(LongReverseBytes) + INTRINSIC_CASE(LongBitCount) + INTRINSIC_CASE(LongCompare) + INTRINSIC_CASE(LongHighestOneBit) + INTRINSIC_CASE(LongLowestOneBit) + INTRINSIC_CASE(LongNumberOfLeadingZeros) + INTRINSIC_CASE(LongNumberOfTrailingZeros) + INTRINSIC_CASE(LongRotateRight) + INTRINSIC_CASE(LongRotateLeft) + INTRINSIC_CASE(LongSignum) + INTRINSIC_CASE(ShortReverseBytes) + INTRINSIC_CASE(MathAbsDouble) + INTRINSIC_CASE(MathAbsFloat) + INTRINSIC_CASE(MathAbsLong) + INTRINSIC_CASE(MathAbsInt) + UNIMPLEMENTED_CASE(MathFmaDouble /* (DDD)D */) + UNIMPLEMENTED_CASE(MathFmaFloat /* (FFF)F */) + UNIMPLEMENTED_CASE(MathMinDoubleDouble /* (DD)D */) + UNIMPLEMENTED_CASE(MathMinFloatFloat /* (FF)F */) + INTRINSIC_CASE(MathMinLongLong) + INTRINSIC_CASE(MathMinIntInt) + UNIMPLEMENTED_CASE(MathMaxDoubleDouble /* (DD)D */) + UNIMPLEMENTED_CASE(MathMaxFloatFloat /* (FF)F */) + INTRINSIC_CASE(MathMaxLongLong) + INTRINSIC_CASE(MathMaxIntInt) + INTRINSIC_CASE(MathCos) + INTRINSIC_CASE(MathSin) + INTRINSIC_CASE(MathAcos) + INTRINSIC_CASE(MathAsin) + INTRINSIC_CASE(MathAtan) + UNIMPLEMENTED_CASE(MathAtan2 /* (DD)D */) + UNIMPLEMENTED_CASE(MathCbrt /* (D)D */) + UNIMPLEMENTED_CASE(MathCosh /* (D)D */) + UNIMPLEMENTED_CASE(MathExp /* (D)D */) + UNIMPLEMENTED_CASE(MathExpm1 /* (D)D */) + UNIMPLEMENTED_CASE(MathHypot /* (DD)D */) + UNIMPLEMENTED_CASE(MathLog /* (D)D */) + UNIMPLEMENTED_CASE(MathLog10 /* (D)D */) + UNIMPLEMENTED_CASE(MathNextAfter /* (DD)D */) + UNIMPLEMENTED_CASE(MathPow /* (DD)D */) + UNIMPLEMENTED_CASE(MathSinh /* (D)D */) + INTRINSIC_CASE(MathTan) + UNIMPLEMENTED_CASE(MathTanh /* (D)D */) + INTRINSIC_CASE(MathSqrt) + INTRINSIC_CASE(MathCeil) + INTRINSIC_CASE(MathFloor) + UNIMPLEMENTED_CASE(MathRint /* (D)D */) + UNIMPLEMENTED_CASE(MathRoundDouble /* (D)J */) + UNIMPLEMENTED_CASE(MathRoundFloat /* (F)I */) + UNIMPLEMENTED_CASE(MathMultiplyHigh /* (JJ)J */) + UNIMPLEMENTED_CASE(SystemArrayCopyByte /* ([BI[BII)V */) + UNIMPLEMENTED_CASE(SystemArrayCopyChar /* ([CI[CII)V */) + UNIMPLEMENTED_CASE(SystemArrayCopyInt /* ([II[III)V */) + UNIMPLEMENTED_CASE(SystemArrayCopy /* (Ljava/lang/Object;ILjava/lang/Object;II)V */) + UNIMPLEMENTED_CASE(ThreadCurrentThread /* ()Ljava/lang/Thread; */) + UNIMPLEMENTED_CASE(MemoryPeekByte /* (J)B */) + UNIMPLEMENTED_CASE(MemoryPeekIntNative /* (J)I */) + UNIMPLEMENTED_CASE(MemoryPeekLongNative /* (J)J */) + UNIMPLEMENTED_CASE(MemoryPeekShortNative /* (J)S */) + UNIMPLEMENTED_CASE(MemoryPokeByte /* (JB)V */) + UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */) + UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */) + UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */) + INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */) + INTRINSIC_CASE(StringCharAt) + INTRINSIC_CASE(StringCompareTo) + INTRINSIC_CASE(StringEquals) + INTRINSIC_CASE(StringGetCharsNoCheck) + INTRINSIC_CASE(StringIndexOf) + INTRINSIC_CASE(StringIndexOfAfter) + UNIMPLEMENTED_CASE(StringStringIndexOf /* (Ljava/lang/String;)I */) + UNIMPLEMENTED_CASE(StringStringIndexOfAfter /* (Ljava/lang/String;I)I */) + INTRINSIC_CASE(StringIsEmpty) + INTRINSIC_CASE(StringLength) + UNIMPLEMENTED_CASE(StringNewStringFromBytes /* ([BIII)Ljava/lang/String; */) + UNIMPLEMENTED_CASE(StringNewStringFromChars /* (II[C)Ljava/lang/String; */) + UNIMPLEMENTED_CASE(StringNewStringFromString /* (Ljava/lang/String;)Ljava/lang/String; */) + UNIMPLEMENTED_CASE(StringBufferAppend /* (Ljava/lang/String;)Ljava/lang/StringBuffer; */) + UNIMPLEMENTED_CASE(StringBufferLength /* ()I */) + UNIMPLEMENTED_CASE(StringBufferToString /* ()Ljava/lang/String; */) + UNIMPLEMENTED_CASE( + StringBuilderAppendObject /* (Ljava/lang/Object;)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE( + StringBuilderAppendString /* (Ljava/lang/String;)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE( + StringBuilderAppendCharSequence /* (Ljava/lang/CharSequence;)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendCharArray /* ([C)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendBoolean /* (Z)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendChar /* (C)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendInt /* (I)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendLong /* (J)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendFloat /* (F)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderAppendDouble /* (D)Ljava/lang/StringBuilder; */) + UNIMPLEMENTED_CASE(StringBuilderLength /* ()I */) + UNIMPLEMENTED_CASE(StringBuilderToString /* ()Ljava/lang/String; */) + UNIMPLEMENTED_CASE(UnsafeCASInt /* (Ljava/lang/Object;JII)Z */) + UNIMPLEMENTED_CASE(UnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */) + UNIMPLEMENTED_CASE(UnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */) + UNIMPLEMENTED_CASE(UnsafeGet /* (Ljava/lang/Object;J)I */) + UNIMPLEMENTED_CASE(UnsafeGetVolatile /* (Ljava/lang/Object;J)I */) + UNIMPLEMENTED_CASE(UnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(UnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(UnsafeGetLong /* (Ljava/lang/Object;J)J */) + UNIMPLEMENTED_CASE(UnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */) + UNIMPLEMENTED_CASE(UnsafePut /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(UnsafePutOrdered /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(UnsafePutVolatile /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(UnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(UnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(UnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(UnsafePutLong /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(UnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(UnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(UnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */) + UNIMPLEMENTED_CASE(UnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */) + UNIMPLEMENTED_CASE(UnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */) + UNIMPLEMENTED_CASE(UnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */) + UNIMPLEMENTED_CASE(UnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(UnsafeLoadFence /* ()V */) + UNIMPLEMENTED_CASE(UnsafeStoreFence /* ()V */) + UNIMPLEMENTED_CASE(UnsafeFullFence /* ()V */) + UNIMPLEMENTED_CASE(JdkUnsafeCASInt /* (Ljava/lang/Object;JII)Z */) + UNIMPLEMENTED_CASE(JdkUnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */) + UNIMPLEMENTED_CASE(JdkUnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */) + UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetInt /* (Ljava/lang/Object;JII)Z */) + UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetLong /* (Ljava/lang/Object;JJJ)Z */) + UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */) + UNIMPLEMENTED_CASE(JdkUnsafeGet /* (Ljava/lang/Object;J)I */) + UNIMPLEMENTED_CASE(JdkUnsafeGetVolatile /* (Ljava/lang/Object;J)I */) + UNIMPLEMENTED_CASE(JdkUnsafeGetAcquire /* (Ljava/lang/Object;J)I */) + UNIMPLEMENTED_CASE(JdkUnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(JdkUnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(JdkUnsafeGetObjectAcquire /* (Ljava/lang/Object;J)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(JdkUnsafeGetLong /* (Ljava/lang/Object;J)J */) + UNIMPLEMENTED_CASE(JdkUnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */) + UNIMPLEMENTED_CASE(JdkUnsafeGetLongAcquire /* (Ljava/lang/Object;J)J */) + UNIMPLEMENTED_CASE(JdkUnsafePut /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutOrdered /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutVolatile /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutRelease /* (Ljava/lang/Object;JI)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutObjectRelease /* (Ljava/lang/Object;JLjava/lang/Object;)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutLong /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(JdkUnsafePutLongRelease /* (Ljava/lang/Object;JJ)V */) + UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */) + UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */) + UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */) + UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */) + UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(JdkUnsafeLoadFence /* ()V */) + UNIMPLEMENTED_CASE(JdkUnsafeStoreFence /* ()V */) + UNIMPLEMENTED_CASE(JdkUnsafeFullFence /* ()V */) + UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */) + UNIMPLEMENTED_CASE(ReferenceRefersTo /* (Ljava/lang/Object;)Z */) + UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */) + UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */) + UNIMPLEMENTED_CASE(CRC32Update /* (II)I */) + UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */) + UNIMPLEMENTED_CASE(CRC32UpdateByteBuffer /* (IJII)I */) + UNIMPLEMENTED_CASE(FP16Compare /* (SS)I */) + UNIMPLEMENTED_CASE(FP16ToFloat /* (S)F */) + UNIMPLEMENTED_CASE(FP16ToHalf /* (F)S */) + UNIMPLEMENTED_CASE(FP16Floor /* (S)S */) + UNIMPLEMENTED_CASE(FP16Ceil /* (S)S */) + UNIMPLEMENTED_CASE(FP16Rint /* (S)S */) + UNIMPLEMENTED_CASE(FP16Greater /* (SS)Z */) + UNIMPLEMENTED_CASE(FP16GreaterEquals /* (SS)Z */) + UNIMPLEMENTED_CASE(FP16Less /* (SS)Z */) + UNIMPLEMENTED_CASE(FP16LessEquals /* (SS)Z */) + UNIMPLEMENTED_CASE(FP16Min /* (SS)S */) + UNIMPLEMENTED_CASE(FP16Max /* (SS)S */) + INTRINSIC_CASE(VarHandleFullFence) + INTRINSIC_CASE(VarHandleAcquireFence) + INTRINSIC_CASE(VarHandleReleaseFence) + INTRINSIC_CASE(VarHandleLoadLoadFence) + INTRINSIC_CASE(VarHandleStoreStoreFence) + INTRINSIC_CASE(MethodHandleInvokeExact) + INTRINSIC_CASE(MethodHandleInvoke) + INTRINSIC_CASE(VarHandleCompareAndExchange) + INTRINSIC_CASE(VarHandleCompareAndExchangeAcquire) + INTRINSIC_CASE(VarHandleCompareAndExchangeRelease) + INTRINSIC_CASE(VarHandleCompareAndSet) + INTRINSIC_CASE(VarHandleGet) + INTRINSIC_CASE(VarHandleGetAcquire) + INTRINSIC_CASE(VarHandleGetAndAdd) + INTRINSIC_CASE(VarHandleGetAndAddAcquire) + INTRINSIC_CASE(VarHandleGetAndAddRelease) + INTRINSIC_CASE(VarHandleGetAndBitwiseAnd) + INTRINSIC_CASE(VarHandleGetAndBitwiseAndAcquire) + INTRINSIC_CASE(VarHandleGetAndBitwiseAndRelease) + INTRINSIC_CASE(VarHandleGetAndBitwiseOr) + INTRINSIC_CASE(VarHandleGetAndBitwiseOrAcquire) + INTRINSIC_CASE(VarHandleGetAndBitwiseOrRelease) + INTRINSIC_CASE(VarHandleGetAndBitwiseXor) + INTRINSIC_CASE(VarHandleGetAndBitwiseXorAcquire) + INTRINSIC_CASE(VarHandleGetAndBitwiseXorRelease) + INTRINSIC_CASE(VarHandleGetAndSet) + INTRINSIC_CASE(VarHandleGetAndSetAcquire) + INTRINSIC_CASE(VarHandleGetAndSetRelease) + INTRINSIC_CASE(VarHandleGetOpaque) + INTRINSIC_CASE(VarHandleGetVolatile) + INTRINSIC_CASE(VarHandleSet) + INTRINSIC_CASE(VarHandleSetOpaque) + INTRINSIC_CASE(VarHandleSetRelease) + INTRINSIC_CASE(VarHandleSetVolatile) + INTRINSIC_CASE(VarHandleWeakCompareAndSet) + INTRINSIC_CASE(VarHandleWeakCompareAndSetAcquire) + INTRINSIC_CASE(VarHandleWeakCompareAndSetPlain) + INTRINSIC_CASE(VarHandleWeakCompareAndSetRelease) + case Intrinsics::kNone: + res = false; + break; + // Note: no default case to ensure we catch any newly added intrinsics. + } + return res; +} + +} // namespace interpreter +} // namespace art diff --git a/runtime/interpreter/interpreter_intrinsics.h b/runtime/interpreter/interpreter_intrinsics.h new file mode 100644 index 0000000000..2a23002d05 --- /dev/null +++ b/runtime/interpreter/interpreter_intrinsics.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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_INTERPRETER_INTERPRETER_INTRINSICS_H_ +#define ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_ + +#include "jvalue.h" + +namespace art { + +class ArtMethod; +class Instruction; +class ShadowFrame; + +namespace interpreter { + +// Invokes to methods identified as intrinics are routed here. If there is +// no interpreter implementation, return false and a normal invoke will proceed. +bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, + ArtMethod* const called_method, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register); + +} // namespace interpreter +} // namespace art + +#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_ diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h index 2858daf968..62bf7d8fa3 100644 --- a/runtime/interpreter/interpreter_switch_impl-inl.h +++ b/runtime/interpreter/interpreter_switch_impl-inl.h @@ -353,7 +353,7 @@ class InstructionHandler { template<InvokeType type, bool is_range> HANDLER_ATTRIBUTES bool HandleInvoke() { - bool success = DoInvoke<type, is_range, do_access_check>( + bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false>( Self(), shadow_frame_, inst_, inst_data_, ResultRegister()); return PossiblyHandlePendingExceptionOnInvoke(!success); } diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc index cc2ec4f427..17f17af68b 100644 --- a/runtime/interpreter/mterp/nterp.cc +++ b/runtime/interpreter/mterp/nterp.cc @@ -26,6 +26,7 @@ #include "entrypoints/entrypoint_utils-inl.h" #include "interpreter/interpreter_cache-inl.h" #include "interpreter/interpreter_common.h" +#include "interpreter/interpreter_intrinsics.h" #include "interpreter/shadow_frame-inl.h" #include "mirror/string-alloc-inl.h" #include "nterp_helpers.h" @@ -95,13 +96,13 @@ inline void UpdateHotness(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock } template<typename T> -inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T value) { +inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T value) { DCHECK(kUseReadBarrier) << "Nterp only works with read barriers"; self->GetInterpreterCache()->Set(self, dex_pc_ptr, value); } template<typename T> -inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T* value) { +inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T* value) { UpdateCache(self, dex_pc_ptr, reinterpret_cast<size_t>(value)); } @@ -251,7 +252,7 @@ extern "C" const char* NterpGetShortyFromInvokeCustom(ArtMethod* caller, uint16_ } FLATTEN -extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr) +extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr) REQUIRES_SHARED(Locks::mutator_lock_) { UpdateHotness(caller); const Instruction* inst = Instruction::At(dex_pc_ptr); diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 70948b8565..75a692e48d 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -420,13 +420,11 @@ TEST_F(UnstartedRuntimeTest, StringInit) { shadow_frame->SetVRegReference(0, reference_empty_string.Get()); shadow_frame->SetVRegReference(1, string_arg.Get()); - ArtMethod* factory = WellKnownClasses::StringInitToStringFactory(method); - interpreter::DoCall<false, false>(factory, + interpreter::DoCall<false, false>(method, self, *shadow_frame, Instruction::At(inst_data), inst_data[0], - /* string_init= */ true, &result); ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL()); EXPECT_EQ(string_arg->GetLength(), string_result->GetLength()); @@ -1026,7 +1024,6 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { *shadow_frame, Instruction::At(inst_data), inst_data[0], - /* string_init= */ false, &result); ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL()); ASSERT_TRUE(string_result != nullptr); @@ -1182,7 +1179,6 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { *shadow_frame, Instruction::At(inst_data), inst_data[0], - /* string_init= */ false, &result); CHECK(!self->IsExceptionPending()); } |