Remove Frame, merge shadow and quick representations.
Change-Id: I5ae03a5e52111792d2df7e83cbd89ab25777844b
diff --git a/src/stack.cc b/src/stack.cc
index d724a72..336f8ad 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -17,178 +17,13 @@
#include "stack.h"
#include "compiler.h"
+#include "oat/runtime/context.h"
#include "object.h"
#include "object_utils.h"
#include "thread_list.h"
namespace art {
-bool Frame::HasMethod() const {
- return GetMethod() != NULL && (!GetMethod()->IsCalleeSaveMethod());
-}
-
-void Frame::Next() {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
-#else
- size_t frame_size = GetMethod()->GetFrameSizeInBytes();
- DCHECK_NE(frame_size, 0u);
- DCHECK_LT(frame_size, 1024u);
- byte* next_sp = reinterpret_cast<byte*>(sp_) + frame_size;
- sp_ = reinterpret_cast<Method**>(next_sp);
- if (*sp_ != NULL) {
- DCHECK((*sp_)->GetClass() == Method::GetMethodClass() ||
- (*sp_)->GetClass() == Method::GetConstructorClass());
- }
-#endif
-}
-
-uintptr_t Frame::GetReturnPC() const {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
- return 0;
-#else
- byte* pc_addr = reinterpret_cast<byte*>(sp_) + GetMethod()->GetReturnPcOffsetInBytes();
- return *reinterpret_cast<uintptr_t*>(pc_addr);
-#endif
-}
-
-void Frame::SetReturnPC(uintptr_t pc) {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
-#else
- byte* pc_addr = reinterpret_cast<byte*>(sp_) + GetMethod()->GetReturnPcOffsetInBytes();
- *reinterpret_cast<uintptr_t*>(pc_addr) = pc;
-#endif
-}
-
-/*
- * Return sp-relative offset for a Dalvik virtual register, compiler
- * spill or Method* in bytes using Method*.
- * Note that (reg >= 0) refers to a Dalvik register, (reg == -2)
- * denotes Method* and (reg <= -3) denotes a compiler temp.
- *
- * +------------------------+
- * | IN[ins-1] | {Note: resides in caller's frame}
- * | . |
- * | IN[0] |
- * | caller's Method* |
- * +========================+ {Note: start of callee's frame}
- * | core callee-save spill | {variable sized}
- * +------------------------+
- * | fp callee-save spill |
- * +------------------------+
- * | filler word | {For compatibility, if V[locals-1] used as wide
- * +------------------------+
- * | V[locals-1] |
- * | V[locals-2] |
- * | . |
- * | . | ... (reg == 2)
- * | V[1] | ... (reg == 1)
- * | V[0] | ... (reg == 0) <---- "locals_start"
- * +------------------------+
- * | Compiler temps | ... (reg == -2)
- * | | ... (reg == -3)
- * | | ... (reg == -4)
- * +------------------------+
- * | stack alignment padding| {0 to (kStackAlignWords-1) of padding}
- * +------------------------+
- * | OUT[outs-1] |
- * | OUT[outs-2] |
- * | . |
- * | OUT[0] |
- * | curMethod* | ... (reg == -1) <<== sp, 16-byte aligned
- * +========================+
- */
-int Frame::GetVRegOffset(const DexFile::CodeItem* code_item,
- uint32_t core_spills, uint32_t fp_spills,
- size_t frame_size, int reg) {
- DCHECK_EQ(frame_size & (kStackAlignment - 1), 0U);
- int num_spills = __builtin_popcount(core_spills) + __builtin_popcount(fp_spills) + 1 /* filler */;
- int num_ins = code_item->ins_size_;
- int num_regs = code_item->registers_size_ - num_ins;
- int locals_start = frame_size - ((num_spills + num_regs) * sizeof(uint32_t));
- if (reg == -2) {
- return 0; // Method*
- } else if (reg <= -3) {
- return locals_start - ((reg + 1) * sizeof(uint32_t)); // Compiler temp
- } else if (reg < num_regs) {
- return locals_start + (reg * sizeof(uint32_t)); // Dalvik local reg
- } else {
- return frame_size + ((reg - num_regs) * sizeof(uint32_t)) + sizeof(uint32_t); // Dalvik in
- }
-}
-
-uint32_t Frame::GetVReg(const DexFile::CodeItem* code_item, uint32_t core_spills,
- uint32_t fp_spills, size_t frame_size, int vreg) const {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
- return 0;
-#else
- int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
- byte* vreg_addr = reinterpret_cast<byte*>(sp_) + offset;
- return *reinterpret_cast<uint32_t*>(vreg_addr);
-#endif
-}
-
-uint32_t Frame::GetVReg(Method* m, int vreg) const {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
- return 0;
-#else
- DCHECK(m == GetMethod());
- const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
- DCHECK(code_item != NULL); // can't be NULL or how would we compile its instructions?
- uint32_t core_spills = m->GetCoreSpillMask();
- uint32_t fp_spills = m->GetFpSpillMask();
- size_t frame_size = m->GetFrameSizeInBytes();
- return GetVReg(code_item, core_spills, fp_spills, frame_size, vreg);
-#endif
-}
-
-void Frame::SetVReg(Method* m, int vreg, uint32_t new_value) {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
-#else
- DCHECK(m == GetMethod());
- const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
- DCHECK(code_item != NULL); // can't be NULL or how would we compile its instructions?
- uint32_t core_spills = m->GetCoreSpillMask();
- uint32_t fp_spills = m->GetFpSpillMask();
- size_t frame_size = m->GetFrameSizeInBytes();
- int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
- byte* vreg_addr = reinterpret_cast<byte*>(sp_) + offset;
- *reinterpret_cast<uint32_t*>(vreg_addr) = new_value;
-#endif
-}
-
-uintptr_t Frame::LoadCalleeSave(int num) const {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
- return 0;
-#else
- // Callee saves are held at the top of the frame
- Method* method = GetMethod();
- DCHECK(method != NULL);
- size_t frame_size = method->GetFrameSizeInBytes();
- byte* save_addr = reinterpret_cast<byte*>(sp_) + frame_size - ((num + 1) * kPointerSize);
-#if defined(__i386__)
- save_addr -= kPointerSize; // account for return address
-#endif
- return *reinterpret_cast<uintptr_t*>(save_addr);
-#endif
-}
-
-Method* Frame::NextMethod() const {
-#if defined(ART_USE_LLVM_COMPILER)
- LOG(FATAL) << "LLVM compiler don't support this function";
- return NULL;
-#else
- byte* next_sp = reinterpret_cast<byte*>(sp_) + GetMethod()->GetFrameSizeInBytes();
- return *reinterpret_cast<Method**>(next_sp);
-#endif
-}
-
class StackGetter {
public:
StackGetter(JNIEnv* env, Thread* thread) : env_(env), thread_(thread), trace_(NULL) {
@@ -219,4 +54,220 @@
return stack_getter.GetTrace();
}
+void ManagedStack::PushManagedStackFragment(ManagedStack* fragment) {
+ // Copy this top fragment into given fragment.
+ memcpy(fragment, this, sizeof(ManagedStack));
+ // Clear this fragment, which has become the top.
+ memset(this, 0, sizeof(ManagedStack));
+ // Link our top fragment onto the given fragment.
+ link_ = fragment;
+}
+
+void ManagedStack::PopManagedStackFragment(const ManagedStack& fragment) {
+ DCHECK(&fragment == link_);
+ // Copy this given fragment back to the top.
+ memcpy(this, &fragment, sizeof(ManagedStack));
+}
+
+size_t ManagedStack::NumShadowFrameReferences() const {
+ size_t count = 0;
+ for (const ManagedStack* current_fragment = this; current_fragment != NULL;
+ current_fragment = current_fragment->GetLink()) {
+ for (ShadowFrame* current_frame = current_fragment->top_shadow_frame_; current_frame != NULL;
+ current_frame = current_frame->GetLink()) {
+ count += current_frame->NumberOfReferences();
+ }
+ }
+ return count;
+}
+
+bool ManagedStack::ShadowFramesContain(Object** shadow_frame_entry) const {
+ for (const ManagedStack* current_fragment = this; current_fragment != NULL;
+ current_fragment = current_fragment->GetLink()) {
+ for (ShadowFrame* current_frame = current_fragment->top_shadow_frame_; current_frame != NULL;
+ current_frame = current_frame->GetLink()) {
+ if (current_frame->Contains(shadow_frame_entry)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+uint32_t StackVisitor::GetDexPc() const {
+ if (cur_shadow_frame_ != NULL) {
+ return cur_shadow_frame_->GetDexPC();
+ } else if (cur_quick_frame_ != NULL) {
+ return GetMethod()->ToDexPC(AdjustQuickFramePcForDexPcComputation(cur_quick_frame_pc_));
+ } else {
+ return 0;
+ }
+}
+
+uint32_t StackVisitor::GetVReg(Method* m, int vreg) const {
+ DCHECK(m == GetMethod());
+ uint32_t core_spills = m->GetCoreSpillMask();
+ const VmapTable vmap_table(m->GetVmapTableRaw());
+ uint32_t vmap_offset;
+ // TODO: IsInContext stops before spotting floating point registers.
+ if (vmap_table.IsInContext(vreg, vmap_offset)) {
+ // Compute the register we need to load from the context.
+ uint32_t spill_mask = core_spills;
+ CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
+ uint32_t matches = 0;
+ uint32_t spill_shifts = 0;
+ while (matches != (vmap_offset + 1)) {
+ DCHECK_NE(spill_mask, 0u);
+ matches += spill_mask & 1; // Add 1 if the low bit is set.
+ spill_mask >>= 1;
+ spill_shifts++;
+ }
+ spill_shifts--; // Wind back one as we want the last match.
+ return GetGPR(spill_shifts);
+ } else {
+ const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
+ DCHECK(code_item != NULL); // can't be NULL or how would we compile its instructions?
+ uint32_t fp_spills = m->GetFpSpillMask();
+ size_t frame_size = m->GetFrameSizeInBytes();
+ return GetVReg(code_item, core_spills, fp_spills, frame_size, vreg);
+ }
+}
+
+void StackVisitor::SetVReg(Method* m, int vreg, uint32_t new_value) {
+ DCHECK(m == GetMethod());
+ const VmapTable vmap_table(m->GetVmapTableRaw());
+ uint32_t vmap_offset;
+ // TODO: IsInContext stops before spotting floating point registers.
+ if (vmap_table.IsInContext(vreg, vmap_offset)) {
+ UNIMPLEMENTED(FATAL);
+ }
+ const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
+ DCHECK(code_item != NULL); // can't be NULL or how would we compile its instructions?
+ uint32_t core_spills = m->GetCoreSpillMask();
+ uint32_t fp_spills = m->GetFpSpillMask();
+ size_t frame_size = m->GetFrameSizeInBytes();
+ int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
+ byte* vreg_addr = reinterpret_cast<byte*>(GetCurrentQuickFrame()) + offset;
+ *reinterpret_cast<uint32_t*>(vreg_addr) = new_value;
+}
+
+uintptr_t StackVisitor::GetGPR(uint32_t reg) const {
+ return context_->GetGPR(reg);
+}
+
+uintptr_t StackVisitor::GetReturnPc() const {
+ Method** sp = GetCurrentQuickFrame();
+ CHECK(sp != NULL);
+ byte* pc_addr = reinterpret_cast<byte*>(sp) + GetMethod()->GetReturnPcOffsetInBytes();
+ return *reinterpret_cast<uintptr_t*>(pc_addr);
+}
+
+void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) {
+ Method** sp = GetCurrentQuickFrame();
+ CHECK(sp != NULL);
+ byte* pc_addr = reinterpret_cast<byte*>(sp) + GetMethod()->GetReturnPcOffsetInBytes();
+ *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc;
+}
+
+size_t StackVisitor::ComputeNumFrames() const {
+ struct NumFramesVisitor : public StackVisitor {
+ explicit NumFramesVisitor(const ManagedStack* stack,
+ const std::vector<TraceStackFrame>* trace_stack) :
+ StackVisitor(stack, trace_stack), frames(0) {}
+
+ virtual bool VisitFrame() {
+ frames++;
+ return true;
+ }
+ size_t frames;
+ };
+
+ NumFramesVisitor visitor(stack_start_, trace_stack_);
+ visitor.WalkStack(true);
+ return visitor.frames;
+}
+
+void StackVisitor::SanityCheckFrame() {
+#ifndef NDEBUG
+ Method* method = GetMethod();
+ CHECK(method->GetClass() == Method::GetMethodClass() ||
+ method->GetClass() == Method::GetConstructorClass());
+ if (cur_quick_frame_ != NULL) {
+ method->AssertPcIsWithinCode(AdjustQuickFramePcForDexPcComputation(cur_quick_frame_pc_));
+ // Frame sanity.
+ size_t frame_size = method->GetFrameSizeInBytes();
+ CHECK_NE(frame_size, 0u);
+ CHECK_LT(frame_size, 1024u);
+ size_t return_pc_offset = method->GetReturnPcOffsetInBytes();
+ CHECK_LT(return_pc_offset, frame_size);
+ }
+#endif
+}
+
+void StackVisitor::WalkStack(bool include_transitions) {
+ bool method_tracing_active = Runtime::Current()->IsMethodTracingActive();
+ uint32_t trace_stack_depth = 0;
+ for (const ManagedStack* current_fragment = stack_start_; current_fragment != NULL;
+ current_fragment = current_fragment->GetLink()) {
+ cur_shadow_frame_ = current_fragment->GetTopShadowFrame();
+ cur_quick_frame_ = current_fragment->GetTopQuickFrame();
+ cur_quick_frame_pc_ = current_fragment->GetTopQuickFramePc();
+ if (cur_quick_frame_ != NULL) { // Handle quick stack frames.
+ // Can't be both a shadow and a quick fragment.
+ DCHECK(current_fragment->GetTopShadowFrame() == NULL);
+ Method* method = *cur_quick_frame_;
+ do {
+ SanityCheckFrame();
+ bool should_continue = VisitFrame();
+ if (UNLIKELY(!should_continue)) {
+ return;
+ }
+ if (context_ != NULL) {
+ context_->FillCalleeSaves(*this);
+ }
+ size_t frame_size = method->GetFrameSizeInBytes();
+ // Compute PC for next stack frame from return PC.
+ size_t return_pc_offset = method->GetReturnPcOffsetInBytes();
+ byte* return_pc_addr = reinterpret_cast<byte*>(cur_quick_frame_) + return_pc_offset;
+ uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
+ if (UNLIKELY(method_tracing_active)) {
+ // While profiling, the return pc is restored from the side stack, except when walking
+ // the stack for an exception where the side stack will be unwound in VisitFrame.
+ // TODO: stop using include_transitions as a proxy for is this the catch block visitor.
+ if (IsTraceExitPc(return_pc) && !include_transitions) {
+ // TODO: unify trace and managed stack.
+ TraceStackFrame trace_frame = GetTraceStackFrame(trace_stack_depth);
+ trace_stack_depth++;
+ CHECK(trace_frame.method_ == GetMethod()) << "Excepted: " << PrettyMethod(method)
+ << " Found: " << PrettyMethod(GetMethod());
+ return_pc = trace_frame.return_pc_;
+ }
+ }
+ cur_quick_frame_pc_ = return_pc;
+ byte* next_frame = reinterpret_cast<byte*>(cur_quick_frame_) + frame_size;
+ cur_quick_frame_ = reinterpret_cast<Method**>(next_frame);
+ cur_depth_++;
+ method = *cur_quick_frame_;
+ } while (method != NULL);
+ } else if (cur_shadow_frame_ != NULL) {
+ do {
+ SanityCheckFrame();
+ bool should_continue = VisitFrame();
+ if (UNLIKELY(!should_continue)) {
+ return;
+ }
+ cur_depth_++;
+ cur_shadow_frame_ = cur_shadow_frame_->GetLink();
+ } while(cur_shadow_frame_ != NULL);
+ }
+ cur_depth_++;
+ if (include_transitions) {
+ bool should_continue = VisitFrame();
+ if (!should_continue) {
+ return;
+ }
+ }
+ }
+}
+
} // namespace art