summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/Android.bp7
-rw-r--r--runtime/arch/context.h6
-rw-r--r--runtime/art_method.cc5
-rw-r--r--runtime/check_reference_map_visitor.h7
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc4
-rw-r--r--runtime/interpreter/interpreter.cc1
-rw-r--r--runtime/interpreter/interpreter_mterp_impl.h3
-rw-r--r--runtime/interpreter/mterp/mterp.h6
-rw-r--r--runtime/interpreter/mterp/nterp_stub.cc45
-rw-r--r--runtime/nterp_helpers.cc152
-rw-r--r--runtime/nterp_helpers.h66
-rw-r--r--runtime/oat_quick_method_header.cc22
-rw-r--r--runtime/oat_quick_method_header.h8
-rw-r--r--runtime/quick_exception_handler.cc7
-rw-r--r--runtime/stack.cc21
-rw-r--r--runtime/thread.cc29
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_) {