diff options
author | 2019-12-04 16:18:15 +0000 | |
---|---|---|
committer | 2019-12-17 09:43:08 +0000 | |
commit | 013d1ee96b928f3bda9031e94d4a69f827133ce6 (patch) | |
tree | 4e374c043330f123dc3888922b554fd1291349b4 | |
parent | 9ca8b2bf46978e3a5698f8a27b48aa7eff3514df (diff) |
Introduce the notion of an nterp frame.
See comments in nterp_helpers.cc. An nterp frame follows the
calling conventions and exception handling of the compiler. There are
no ManagedStack transitions, and the compiler and interpreter can
just call each other directly.
For the stack walker, an nterp frame looks like a compiled frame.
This CL introduces an nterp frame, another CL will contain an
implementation for x64.
Bug: 119800099
Test: test.py
Change-Id: Ie9b691f58908b7f283b4cd63b84b651526155d27
-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_) { |