diff options
-rw-r--r-- | runtime/Android.bp | 7 | ||||
-rw-r--r-- | runtime/arch/context.h | 6 | ||||
-rw-r--r-- | runtime/art_method.cc | 5 | ||||
-rw-r--r-- | runtime/check_reference_map_visitor.h | 7 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils.cc | 4 | ||||
-rw-r--r-- | runtime/interpreter/interpreter.cc | 1 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_mterp_impl.h | 3 | ||||
-rw-r--r-- | runtime/interpreter/mterp/mterp.h | 6 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp_stub.cc | 45 | ||||
-rw-r--r-- | runtime/nterp_helpers.cc | 152 | ||||
-rw-r--r-- | runtime/nterp_helpers.h | 66 | ||||
-rw-r--r-- | runtime/oat_quick_method_header.cc | 22 | ||||
-rw-r--r-- | runtime/oat_quick_method_header.h | 8 | ||||
-rw-r--r-- | runtime/quick_exception_handler.cc | 7 | ||||
-rw-r--r-- | runtime/stack.cc | 21 | ||||
-rw-r--r-- | runtime/thread.cc | 29 |
16 files changed, 380 insertions, 9 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 01e2dc6260..baa921dc5b 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -210,6 +210,7 @@ libart_cc_defaults { "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc", "native/sun_misc_Unsafe.cc", "non_debuggable_classes.cc", + "nterp_helpers.cc", "oat.cc", "oat_file.cc", "oat_file_assistant.cc", @@ -290,6 +291,7 @@ libart_cc_defaults { arm: { srcs: [ "interpreter/mterp/mterp.cc", + "interpreter/mterp/nterp_stub.cc", ":libart_mterp.arm", "arch/arm/context_arm.cc", "arch/arm/entrypoints_init_arm.cc", @@ -305,6 +307,7 @@ libart_cc_defaults { arm64: { srcs: [ "interpreter/mterp/mterp.cc", + "interpreter/mterp/nterp_stub.cc", ":libart_mterp.arm64", "arch/arm64/context_arm64.cc", "arch/arm64/entrypoints_init_arm64.cc", @@ -319,6 +322,7 @@ libart_cc_defaults { x86: { srcs: [ "interpreter/mterp/mterp.cc", + "interpreter/mterp/nterp_stub.cc", ":libart_mterp.x86", "arch/x86/context_x86.cc", "arch/x86/entrypoints_init_x86.cc", @@ -340,6 +344,7 @@ libart_cc_defaults { // Note that the fault_handler_x86.cc is not a mistake. This file is // shared between the x86 and x86_64 architectures. "interpreter/mterp/mterp.cc", + "interpreter/mterp/nterp_stub.cc", ":libart_mterp.x86_64", "arch/x86_64/context_x86_64.cc", "arch/x86_64/entrypoints_init_x86_64.cc", @@ -360,6 +365,7 @@ libart_cc_defaults { mips: { srcs: [ "interpreter/mterp/mterp.cc", + "interpreter/mterp/nterp_stub.cc", ":libart_mterp.mips", "arch/mips/context_mips.cc", "arch/mips/entrypoints_init_mips.cc", @@ -373,6 +379,7 @@ libart_cc_defaults { mips64: { srcs: [ "interpreter/mterp/mterp.cc", + "interpreter/mterp/nterp_stub.cc", ":libart_mterp.mips64", "arch/mips64/context_mips64.cc", "arch/mips64/entrypoints_init_mips64.cc", diff --git a/runtime/arch/context.h b/runtime/arch/context.h index 5980b03190..be7adc7ca1 100644 --- a/runtime/arch/context.h +++ b/runtime/arch/context.h @@ -88,6 +88,12 @@ class Context { // Smashes the caller save registers. If we're throwing, we don't want to return bogus values. virtual void SmashCallerSaves() = 0; + // Set `new_value` to the physical register containing the dex PC pointer in + // an nterp frame. + virtual void SetNterpDexPC(uintptr_t new_value ATTRIBUTE_UNUSED) { + abort(); + } + // Switches execution of the executing context to this context NO_RETURN virtual void DoLongJump() = 0; diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 646f73d8cc..d975a3017e 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -612,6 +612,11 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } } + if (OatQuickMethodHeader::NterpMethodHeader != nullptr && + OatQuickMethodHeader::NterpMethodHeader->Contains(pc)) { + return OatQuickMethodHeader::NterpMethodHeader; + } + // Check whether the pc is in the JIT code cache. jit::Jit* jit = runtime->GetJit(); if (jit != nullptr) { diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index ebf9ea0b4d..f4bda90fc1 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -40,7 +40,12 @@ class CheckReferenceMapVisitor : public StackVisitor { CHECK_EQ(GetDexPc(), dex::kDexNoIndex); } - if (m == nullptr || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) { + // If the method is not compiled, continue the stack walk. + if (m == nullptr || + m->IsNative() || + m->IsRuntimeMethod() || + IsShadowFrame() || + !GetCurrentOatQuickMethodHeader()->IsOptimized()) { return true; } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 17be7ec687..849a9673d1 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -202,8 +202,8 @@ static inline ArtMethod* DoGetCalleeSaveMethodCaller(ArtMethod* outer_method, if (outer_method != nullptr) { const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc); DCHECK(current_code != nullptr); - DCHECK(current_code->IsOptimized()); - if (CodeInfo::HasInlineInfo(current_code->GetOptimizedCodeInfoPtr())) { + if (current_code->IsOptimized() && + CodeInfo::HasInlineInfo(current_code->GetOptimizedCodeInfoPtr())) { uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc); CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(current_code); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index b5b01fe331..b5e52388b8 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -703,6 +703,7 @@ void ArtInterpreterToInterpreterBridge(Thread* self, void CheckInterpreterAsmConstants() { CheckMterpAsmConstants(); + CheckNterpAsmConstants(); } void InitInterpreterTls(Thread* self) { diff --git a/runtime/interpreter/interpreter_mterp_impl.h b/runtime/interpreter/interpreter_mterp_impl.h index 177b0fd360..892790b118 100644 --- a/runtime/interpreter/interpreter_mterp_impl.h +++ b/runtime/interpreter/interpreter_mterp_impl.h @@ -36,6 +36,9 @@ extern "C" bool ExecuteMterpImpl(Thread* self, ShadowFrame* shadow_frame, JValue* result_register) REQUIRES_SHARED(Locks::mutator_lock_); +// The entrypoint for nterp, which ArtMethods can directly point to. +extern "C" void ExecuteNterpImpl() REQUIRES_SHARED(Locks::mutator_lock_); + } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/mterp/mterp.h b/runtime/interpreter/mterp/mterp.h index af52758bbc..dfbba29952 100644 --- a/runtime/interpreter/mterp/mterp.h +++ b/runtime/interpreter/mterp/mterp.h @@ -26,6 +26,9 @@ extern "C" void* artMterpAsmInstructionStart[]; extern "C" void* artMterpAsmInstructionEnd[]; +extern "C" void* artNterpAsmInstructionStart[]; +extern "C" void* artNterpAsmInstructionEnd[]; + namespace art { class Thread; @@ -34,7 +37,10 @@ namespace interpreter { void InitMterpTls(Thread* self); void CheckMterpAsmConstants(); +void CheckNterpAsmConstants(); bool CanUseMterp(); +bool IsNterpSupported(); +const void* GetNterpEntryPoint(); // Poison value for TestExportPC. If we segfault with this value, it means that a mterp // handler for a recent opcode failed to export the Dalvik PC prior to a possible exit from diff --git a/runtime/interpreter/mterp/nterp_stub.cc b/runtime/interpreter/mterp/nterp_stub.cc new file mode 100644 index 0000000000..e77f0e3389 --- /dev/null +++ b/runtime/interpreter/mterp/nterp_stub.cc @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 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 "base/locks.h" + +/* + * Stub definitions for targets without nterp implementations. + */ + +namespace art { +namespace interpreter { + +bool IsNterpSupported() { + return false; +} + +void CheckNterpAsmConstants() { +} + +extern "C" void ExecuteNterpImpl() REQUIRES_SHARED(Locks::mutator_lock_) { + UNIMPLEMENTED(FATAL); +} + +const void* GetNterpEntryPoint() { + return nullptr; +} + +extern "C" void* artNterpAsmInstructionStart[] = { nullptr }; +extern "C" void* artNterpAsmInstructionEnd[] = { nullptr }; + +} // namespace interpreter +} // namespace art diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc new file mode 100644 index 0000000000..df0eb7305a --- /dev/null +++ b/runtime/nterp_helpers.cc @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2019 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 "art_method-inl.h" +#include "dex/code_item_accessors.h" +#include "entrypoints/quick/callee_save_frame.h" +#include "interpreter/interpreter_mterp_impl.h" +#include "nterp_helpers.h" +#include "oat_quick_method_header.h" +#include "quick/quick_method_frame_info.h" + +namespace art { + +/** + * An nterp frame follows the optimizing compiler's ABI conventions, with + * int/long/reference parameters being passed in core registers / stack and + * float/double parameters being passed in floating point registers / stack. + * + * There are no ManagedStack transitions between compiler and nterp frames. + * + * On entry, nterp will copy its parameters to a dex register array allocated on + * the stack. There is a fast path when calling from nterp to nterp to not + * follow the ABI but just copy the parameters from the caller's dex registers + * to the callee's dex registers. + * + * The stack layout of an nterp frame is: + * ---------------- + * | | All callee save registers of the platform + * | callee-save | (core and floating point). + * | registers | On x86 and x64 this includes the return address, + * | | already spilled on entry. + * ---------------- + * | | Contains `registers_size` entries (of size 4) from + * | dex | the code item information of the method. + * | registers | + * | | + * ---------------- + * | | A copy of the dex registers above, but only + * | reference | containing references, used for GC. + * | registers | + * | | + * ---------------- + * | caller fp | Frame pointer of caller. Stored below the reference + * ---------------- registers array for easy access from nterp when returning. + * | dex_pc_ptr | Pointer to the dex instruction being executed. + * ---------------- Stored whenever nterp goes into the runtime. + * | alignment | Stack aligment of kStackAlignment. TODO: try to move + * ---------------- this below the callee-save registers. + * | | In case nterp calls compiled code, we reserve space + * | out | for out registers. This space will be used for + * | registers | arguments passed on stack. + * | | + * ---------------- + * | ArtMethod* | The method being currently executed. + * ---------------- + * + * Exception handling: + * Nterp follows the same convention than the compiler, + * with the addition of: + * - All catch handlers have the same landing pad. + * - Before doing the longjmp for exception delivery, the register containing the + * dex PC pointer must be updated. + * + * Stack walking: + * An nterp frame is walked like a compiled code frame. We add an + * OatQuickMethodHeader prefix to the nterp entry point, which contains: + * - vmap_table_offset=0 (nterp doesn't need one). + * - code_size=NterpEnd-NterpStart + */ + +static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize); + +static constexpr size_t NterpGetFrameEntrySize() { + uint32_t core_spills = + RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); + uint32_t fp_spills = + RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); + // Note: the return address is considered part of the callee saves. + return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) * kPointerSize; +} + +static size_t NterpGetFrameSize(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + CodeItemDataAccessor accessor(method->DexInstructionData()); + const uint16_t num_regs = accessor.RegistersSize(); + const uint16_t out_regs = accessor.OutsSize(); + + size_t frame_size = + NterpGetFrameEntrySize() + + (num_regs * kVRegSize) * 2 + // dex registers and reference registers + kPointerSize + // previous frame + kPointerSize + // saved dex pc + (out_regs * kVRegSize) + // out arguments + kPointerSize; // method + return RoundUp(frame_size, kStackAlignment); +} + +QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) { + uint32_t core_spills = + RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); + uint32_t fp_spills = + RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); + return QuickMethodFrameInfo(NterpGetFrameSize(*frame), core_spills, fp_spills); +} + +uintptr_t NterpGetRegistersArray(ArtMethod** frame) { + CodeItemDataAccessor accessor((*frame)->DexInstructionData()); + const uint16_t num_regs = accessor.RegistersSize(); + // The registers array is just below the frame entry. + return reinterpret_cast<uintptr_t>(frame) + NterpGetFrameSize(*frame) - + NterpGetFrameEntrySize() - + (num_regs * kVRegSize); +} + +uintptr_t NterpGetReferenceArray(ArtMethod** frame) { + CodeItemDataAccessor accessor((*frame)->DexInstructionData()); + const uint16_t num_regs = accessor.RegistersSize(); + // The references array is just below the registers array. + return NterpGetRegistersArray(frame) - (num_regs * kVRegSize); +} + +uint32_t NterpGetDexPC(ArtMethod** frame) { + uintptr_t dex_pc_ptr = NterpGetReferenceArray(frame) - + kPointerSize - // saved previous frame + kPointerSize; // saved dex pc + CodeItemInstructionAccessor accessor((*frame)->DexInstructions()); + return *reinterpret_cast<const uint16_t**>(dex_pc_ptr) - accessor.Insns(); +} + +uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) { + return reinterpret_cast<uint32_t*>(NterpGetRegistersArray(frame))[vreg]; +} + +uintptr_t NterpGetCatchHandler() { + // Nterp uses the same landing pad for all exceptions. The dex_pc_ptr set before + // longjmp will actually be used to jmp to the catch handler. + return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd); +} + +} // namespace art diff --git a/runtime/nterp_helpers.h b/runtime/nterp_helpers.h new file mode 100644 index 0000000000..758d1fdfc0 --- /dev/null +++ b/runtime/nterp_helpers.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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_NTERP_HELPERS_H_ +#define ART_RUNTIME_NTERP_HELPERS_H_ + +#include "quick/quick_method_frame_info.h" + +namespace art { + +class ArtMethod; + +/** + * Returns the QuickMethodFrameInfo of the given frame corresponding to the + * given method. + */ +QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) + REQUIRES_SHARED(Locks::mutator_lock_); + +/** + * Returns the dex PC at which the given nterp frame is executing. + */ +uint32_t NterpGetDexPC(ArtMethod** frame) + REQUIRES_SHARED(Locks::mutator_lock_); + +/** + * Returns the reference array to be used by the GC to visit references in an + * nterp frame. + */ +uintptr_t NterpGetReferenceArray(ArtMethod** frame) + REQUIRES_SHARED(Locks::mutator_lock_); + +/** + * Returns the dex register array to be used by the GC to update references in + * an nterp frame. + */ +uintptr_t NterpGetRegistersArray(ArtMethod** frame) + REQUIRES_SHARED(Locks::mutator_lock_); + +/** + * Returns the nterp landing pad for catching an exception. + */ +uintptr_t NterpGetCatchHandler(); + +/** + * Returns the value of dex register number `vreg` in the given frame. + */ +uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) + REQUIRES_SHARED(Locks::mutator_lock_); + +} // namespace art + +#endif // ART_RUNTIME_NTERP_HELPERS_H_ diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc index 7a6ebf8075..ebb868b11f 100644 --- a/runtime/oat_quick_method_header.cc +++ b/runtime/oat_quick_method_header.cc @@ -18,6 +18,9 @@ #include "art_method.h" #include "dex/dex_file_types.h" +#include "interpreter/interpreter_mterp_impl.h" +#include "interpreter/mterp/mterp.h" +#include "nterp_helpers.h" #include "scoped_thread_state_change-inl.h" #include "stack_map.h" #include "thread.h" @@ -32,6 +35,8 @@ uint32_t OatQuickMethodHeader::ToDexPc(ArtMethod** frame, uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point); if (method->IsNative()) { return dex::kDexNoIndex; + } else if (IsNterpMethodHeader()) { + return NterpGetDexPC(frame); } else { DCHECK(IsOptimized()); CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(this); @@ -41,7 +46,6 @@ uint32_t OatQuickMethodHeader::ToDexPc(ArtMethod** frame, } } if (abort_on_failure) { - ScopedObjectAccess soa(Thread::Current()); LOG(FATAL) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset) << "(PC " << reinterpret_cast<void*>(pc) << ", entry_point=" << entry_point @@ -57,6 +61,11 @@ uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method, bool abort_on_failure) const { const void* entry_point = GetEntryPoint(); DCHECK(!method->IsNative()); + if (IsNterpMethodHeader()) { + // This should only be called on an nterp frame for getting a catch handler. + CHECK(is_for_catch_handler); + return NterpGetCatchHandler(); + } DCHECK(IsOptimized()); // Search for the dex-to-pc mapping in stack maps. CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(this); @@ -79,4 +88,15 @@ uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method, return UINTPTR_MAX; } +OatQuickMethodHeader* OatQuickMethodHeader::NterpMethodHeader = + (interpreter::IsNterpSupported() + ? reinterpret_cast<OatQuickMethodHeader*>( + reinterpret_cast<uintptr_t>(interpreter::GetNterpEntryPoint()) - + sizeof(OatQuickMethodHeader)) + : nullptr); + +bool OatQuickMethodHeader::IsNterpMethodHeader() const { + return interpreter::IsNterpSupported() ? (this == NterpMethodHeader) : false; +} + } // namespace art diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 0d08149693..c8ee9b4e91 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_OAT_QUICK_METHOD_HEADER_H_ #include "arch/instruction_set.h" +#include "base/locks.h" #include "base/macros.h" #include "base/utils.h" #include "quick/quick_method_frame_info.h" @@ -37,6 +38,10 @@ class PACKED(4) OatQuickMethodHeader { code_size_(code_size) { } + static OatQuickMethodHeader* NterpMethodHeader; + + bool IsNterpMethodHeader() const; + static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) { uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr); uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_); @@ -150,7 +155,8 @@ class PACKED(4) OatQuickMethodHeader { uint32_t ToDexPc(ArtMethod** frame, const uintptr_t pc, - bool abort_on_failure = true) const; + bool abort_on_failure = true) const + REQUIRES_SHARED(Locks::mutator_lock_); void SetHasShouldDeoptimizeFlag() { DCHECK_EQ(code_size_ & kShouldDeoptimizeMask, 0u); diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 727bdf02e1..0e04b7b696 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -33,6 +33,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/throwable.h" +#include "nterp_helpers.h" #include "oat_quick_method_header.h" #include "stack.h" #include "stack_map.h" @@ -663,6 +664,12 @@ void QuickExceptionHandler::DoLongJump(bool smash_caller_saves) { if (smash_caller_saves) { context_->SmashCallerSaves(); } + if (handler_method_ != nullptr && + handler_method_header_ != nullptr && + handler_method_header_->IsNterpMethodHeader()) { + context_->SetNterpDexPC(reinterpret_cast<uintptr_t>( + handler_method_->DexInstructions().Insns() + handler_dex_pc_)); + } context_->DoLongJump(); UNREACHABLE(); } diff --git a/runtime/stack.cc b/runtime/stack.cc index e69bc1ea17..410e0fd144 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -38,6 +38,7 @@ #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" +#include "nterp_helpers.h" #include "oat_quick_method_header.h" #include "obj_ptr-inl.h" #include "quick/quick_method_frame_info.h" @@ -122,13 +123,16 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { return current_inline_frames_.back().GetDexPc(); } else if (cur_oat_quick_method_header_ == nullptr) { return dex::kDexNoIndex; - } else if (!(*GetCurrentQuickFrame())->IsNative()) { + } else if ((*GetCurrentQuickFrame())->IsNative()) { + return cur_oat_quick_method_header_->ToDexPc( + GetCurrentQuickFrame(), cur_quick_frame_pc_, abort_on_failure); + } else if (cur_oat_quick_method_header_->IsOptimized()) { StackMap* stack_map = GetCurrentStackMap(); DCHECK(stack_map->IsValid()); return stack_map->GetDexPc(); } else { - return cur_oat_quick_method_header_->ToDexPc( - GetCurrentQuickFrame(), cur_quick_frame_pc_, abort_on_failure); + DCHECK(cur_oat_quick_method_header_->IsNterpMethodHeader()); + return NterpGetDexPC(cur_quick_frame_); } } else { return 0; @@ -214,6 +218,10 @@ bool StackVisitor::GetVReg(ArtMethod* m, if (GetVRegFromDebuggerShadowFrame(vreg, kind, val)) { return true; } + if (cur_oat_quick_method_header_->IsNterpMethodHeader()) { + *val = NterpGetVReg(cur_quick_frame_, vreg); + return true; + } DCHECK(cur_oat_quick_method_header_->IsOptimized()); if (location.has_value() && kind != kReferenceVReg) { uint32_t val2 = *val; @@ -772,7 +780,12 @@ static uint32_t GetNumberOfReferenceArgsWithoutReceiver(ArtMethod* method) QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { if (cur_oat_quick_method_header_ != nullptr) { - return cur_oat_quick_method_header_->GetFrameInfo(); + if (cur_oat_quick_method_header_->IsOptimized()) { + return cur_oat_quick_method_header_->GetFrameInfo(); + } else { + DCHECK(cur_oat_quick_method_header_->IsNterpMethodHeader()); + return NterpFrameInfo(cur_quick_frame_); + } } ArtMethod* method = GetMethod(); diff --git a/runtime/thread.cc b/runtime/thread.cc index a4bc8ee5f8..6e57ec6695 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -89,6 +89,7 @@ #include "native_stack_dump.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" +#include "nterp_helpers.h" #include "nth_caller_visitor.h" #include "oat_quick_method_header.h" #include "obj_ptr-inl.h" @@ -3713,6 +3714,8 @@ class ReferenceMapVisitor : public StackVisitor { ShadowFrame* shadow_frame = GetCurrentShadowFrame(); if (shadow_frame != nullptr) { VisitShadowFrame(shadow_frame); + } else if (GetCurrentOatQuickMethodHeader()->IsNterpMethodHeader()) { + VisitNterpFrame(); } else { VisitQuickFrame(); } @@ -3781,6 +3784,32 @@ class ReferenceMapVisitor : public StackVisitor { } } + void VisitNterpFrame() REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod** cur_quick_frame = GetCurrentQuickFrame(); + StackReference<mirror::Object>* vreg_ref_base = + reinterpret_cast<StackReference<mirror::Object>*>(NterpGetReferenceArray(cur_quick_frame)); + StackReference<mirror::Object>* vreg_int_base = + reinterpret_cast<StackReference<mirror::Object>*>(NterpGetRegistersArray(cur_quick_frame)); + CodeItemDataAccessor accessor((*cur_quick_frame)->DexInstructionData()); + const uint16_t num_regs = accessor.RegistersSize(); + // An nterp frame has two arrays: a dex register array and a reference array + // that shadows the dex register array but only containing references + // (non-reference dex registers have nulls). See nterp_helpers.cc. + for (size_t reg = 0; reg < num_regs; ++reg) { + StackReference<mirror::Object>* ref_addr = vreg_ref_base + reg; + mirror::Object* ref = ref_addr->AsMirrorPtr(); + if (ref != nullptr) { + mirror::Object* new_ref = ref; + visitor_(&new_ref, reg, this); + if (new_ref != ref) { + ref_addr->Assign(new_ref); + StackReference<mirror::Object>* int_addr = vreg_int_base + reg; + int_addr->Assign(new_ref); + } + } + } + } + template <typename T> ALWAYS_INLINE inline void VisitQuickFrameWithVregCallback() REQUIRES_SHARED(Locks::mutator_lock_) { |