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