9f3783186f
R=kmillikin@google.com BUG= Review URL: https://codereview.chromium.org//12457034 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21351 260f80e4-7a28-3924-810f-c04153c831b5
346 lines
11 KiB
C++
346 lines
11 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/stack_frame.h"
|
|
|
|
#include "vm/assembler.h"
|
|
#include "vm/deopt_instructions.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/os.h"
|
|
#include "vm/parser.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/stub_code.h"
|
|
#include "vm/visitor.h"
|
|
|
|
namespace dart {
|
|
|
|
|
|
bool StackFrame::IsStubFrame() const {
|
|
ASSERT(!(IsEntryFrame() || IsExitFrame()));
|
|
uword saved_pc =
|
|
*(reinterpret_cast<uword*>(fp() + EntrypointMarkerOffsetFromFp()));
|
|
return (saved_pc == 0);
|
|
}
|
|
|
|
|
|
void StackFrame::Print() const {
|
|
OS::Print("[%-8s : sp(%#"Px") ]\n", GetName(), sp());
|
|
}
|
|
|
|
|
|
void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
// There are no objects to visit in this frame.
|
|
}
|
|
|
|
|
|
RawContext* EntryFrame::SavedContext() const {
|
|
return *(reinterpret_cast<RawContext**>(fp() + SavedContextOffset()));
|
|
}
|
|
|
|
|
|
void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
// Visit objects between SP and (FP - callee_save_area).
|
|
ASSERT(visitor != NULL);
|
|
RawObject** start = reinterpret_cast<RawObject**>(sp());
|
|
RawObject** end = reinterpret_cast<RawObject**>(
|
|
fp() - kWordSize + ExitLinkOffset());
|
|
visitor->VisitPointers(start, end);
|
|
}
|
|
|
|
|
|
void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
// NOTE: This code runs while GC is in progress and runs within
|
|
// a NoHandleScope block. Hence it is not ok to use regular Zone or
|
|
// Scope handles. We use direct stack handles, the raw pointers in
|
|
// these handles are not traversed. The use of handles is mainly to
|
|
// be able to reuse the handle based code and avoid having to add
|
|
// helper functions to the raw object interface.
|
|
ASSERT(visitor != NULL);
|
|
NoGCScope no_gc;
|
|
RawObject** start_addr = reinterpret_cast<RawObject**>(sp());
|
|
RawObject** end_addr =
|
|
reinterpret_cast<RawObject**>(fp()) + kFirstLocalSlotIndex;
|
|
Code code;
|
|
code = LookupDartCode();
|
|
if (!code.IsNull()) {
|
|
// Visit the code object.
|
|
RawObject* raw_code = code.raw();
|
|
visitor->VisitPointer(&raw_code);
|
|
// Visit stack based on stack maps.
|
|
Array maps;
|
|
maps = Array::null();
|
|
Stackmap map;
|
|
map = code.GetStackmap(pc(), &maps, &map);
|
|
if (!map.IsNull()) {
|
|
// A stack map is present in the code object, use the stack map to
|
|
// visit frame slots which are marked as having objects.
|
|
//
|
|
// The layout of the frame is (lower addresses to the right):
|
|
// | spill slots | outgoing arguments | saved registers |
|
|
// |XXXXXXXXXXXXX|--------------------|XXXXXXXXXXXXXXXXX|
|
|
//
|
|
// The splill slots and any saved registers are described in the stack
|
|
// map. The outgoing arguments are assumed to be tagged; the number
|
|
// of outgoing arguments is not explicitly tracked.
|
|
//
|
|
// TODO(kmillikin): This does not handle slow path calls with
|
|
// arguments, where the arguments are pushed after the live registers.
|
|
// Enable such calls.
|
|
intptr_t length = map.Length();
|
|
// Spill slots are at the 'bottom' of the frame.
|
|
intptr_t spill_slot_count = length - map.RegisterBitCount();
|
|
for (intptr_t bit = 0; bit < spill_slot_count; ++bit) {
|
|
if (map.IsObject(bit)) visitor->VisitPointer(end_addr);
|
|
--end_addr;
|
|
}
|
|
|
|
// The live registers at the 'top' of the frame comprise the rest of the
|
|
// stack map.
|
|
for (intptr_t bit = length - 1; bit >= spill_slot_count; --bit) {
|
|
if (map.IsObject(bit)) visitor->VisitPointer(start_addr);
|
|
++start_addr;
|
|
}
|
|
|
|
// The end address can be one slot (but not more) past the start
|
|
// address in the case that all slots were covered by the stack map.
|
|
ASSERT((end_addr + 1) >= start_addr);
|
|
}
|
|
}
|
|
// Each slot between the start and end address are tagged objects.
|
|
visitor->VisitPointers(start_addr, end_addr);
|
|
}
|
|
|
|
|
|
RawFunction* StackFrame::LookupDartFunction() const {
|
|
const Code& code = Code::Handle(LookupDartCode());
|
|
if (!code.IsNull()) {
|
|
return code.function();
|
|
}
|
|
return Function::null();
|
|
}
|
|
|
|
|
|
RawCode* StackFrame::LookupDartCode() const {
|
|
// We add a no gc scope to ensure that the code below does not trigger
|
|
// a GC as we are handling raw object references here. It is possible
|
|
// that the code is called while a GC is in progress, that is ok.
|
|
NoGCScope no_gc;
|
|
RawCode* code = GetCodeObject();
|
|
ASSERT(code == Code::null() || code->ptr()->function_ != Function::null());
|
|
return code;
|
|
}
|
|
|
|
|
|
RawCode* StackFrame::GetCodeObject() const {
|
|
// We add a no gc scope to ensure that the code below does not trigger
|
|
// a GC as we are handling raw object references here. It is possible
|
|
// that the code is called while a GC is in progress, that is ok.
|
|
NoGCScope no_gc;
|
|
uword saved_pc =
|
|
*(reinterpret_cast<uword*>(fp() + EntrypointMarkerOffsetFromFp()));
|
|
if (saved_pc != 0) {
|
|
uword entry_point =
|
|
(saved_pc - Assembler::kOffsetOfSavedPCfromEntrypoint);
|
|
RawInstructions* instr = Instructions::FromEntryPoint(entry_point);
|
|
if (instr != Instructions::null()) {
|
|
return instr->ptr()->code_;
|
|
}
|
|
}
|
|
return Code::null();
|
|
}
|
|
|
|
|
|
bool StackFrame::FindExceptionHandler(uword* handler_pc) const {
|
|
const Code& code = Code::Handle(LookupDartCode());
|
|
if (code.IsNull()) {
|
|
return false; // Stub frames do not have exception handlers.
|
|
}
|
|
|
|
// Find pc descriptor for the current pc.
|
|
const PcDescriptors& descriptors =
|
|
PcDescriptors::Handle(code.pc_descriptors());
|
|
for (intptr_t i = 0; i < descriptors.Length(); i++) {
|
|
if ((static_cast<uword>(descriptors.PC(i)) == pc()) &&
|
|
(descriptors.TryIndex(i) != -1)) {
|
|
const intptr_t try_index = descriptors.TryIndex(i);
|
|
const ExceptionHandlers& handlers =
|
|
ExceptionHandlers::Handle(code.exception_handlers());
|
|
*handler_pc = handlers.HandlerPC(try_index);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
intptr_t StackFrame::GetTokenPos() const {
|
|
const Code& code = Code::Handle(LookupDartCode());
|
|
if (code.IsNull()) {
|
|
return -1; // Stub frames do not have token_pos.
|
|
}
|
|
const PcDescriptors& descriptors =
|
|
PcDescriptors::Handle(code.pc_descriptors());
|
|
ASSERT(!descriptors.IsNull());
|
|
for (int i = 0; i < descriptors.Length(); i++) {
|
|
if (static_cast<uword>(descriptors.PC(i)) == pc()) {
|
|
return descriptors.TokenPos(i);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
bool StackFrame::IsValid() const {
|
|
if (IsEntryFrame() || IsExitFrame() || IsStubFrame()) {
|
|
return true;
|
|
}
|
|
return (LookupDartCode() != Code::null());
|
|
}
|
|
|
|
|
|
StackFrameIterator::StackFrameIterator(bool validate)
|
|
: validate_(validate), entry_(), exit_(), current_frame_(NULL) {
|
|
SetupLastExitFrameData(); // Setup data for last exit frame.
|
|
}
|
|
|
|
StackFrameIterator::StackFrameIterator(uword last_fp, bool validate)
|
|
: validate_(validate), entry_(), exit_(), current_frame_(NULL) {
|
|
frames_.fp_ = last_fp;
|
|
}
|
|
|
|
|
|
StackFrame* StackFrameIterator::NextFrame() {
|
|
// When we are at the start of iteration after having created an
|
|
// iterator object current_frame_ will be NULL as we haven't seen
|
|
// any frames yet. At this point if NextFrame is called it tries
|
|
// to set up the next exit frame by reading the top_exit_frame_info
|
|
// from the isolate. If we do not have any dart invocations yet
|
|
// top_exit_frame_info will be 0 and so we would return NULL.
|
|
|
|
// current_frame_ will also be NULL, when we are at the end of having
|
|
// iterated through all the frames. if NextFrame is called at this
|
|
// point we will try and set up the next exit frame but since we are
|
|
// at the end of the iteration fp_ will be 0 and we would return NULL.
|
|
if (current_frame_ == NULL) {
|
|
if (!HasNextFrame()) {
|
|
return NULL;
|
|
}
|
|
current_frame_ = NextExitFrame();
|
|
return current_frame_;
|
|
}
|
|
ASSERT((validate_ == kDontValidateFrames) || current_frame_->IsValid());
|
|
if (current_frame_->IsEntryFrame()) {
|
|
if (HasNextFrame()) { // We have another chained block.
|
|
current_frame_ = NextExitFrame();
|
|
return current_frame_;
|
|
}
|
|
current_frame_ = NULL; // No more frames.
|
|
return current_frame_;
|
|
}
|
|
ASSERT(current_frame_->IsExitFrame() ||
|
|
current_frame_->IsDartFrame() ||
|
|
current_frame_->IsStubFrame());
|
|
|
|
// Consume dart/stub frames using StackFrameIterator::FrameSetIterator
|
|
// until we are out of dart/stub frames at which point we return the
|
|
// corresponding entry frame for that set of dart/stub frames.
|
|
current_frame_ =
|
|
(frames_.HasNext()) ? frames_.NextFrame(validate_) : NextEntryFrame();
|
|
return current_frame_;
|
|
}
|
|
|
|
|
|
StackFrame* StackFrameIterator::FrameSetIterator::NextFrame(bool validate) {
|
|
StackFrame* frame;
|
|
ASSERT(HasNext());
|
|
frame = &stack_frame_;
|
|
frame->sp_ = sp_;
|
|
frame->fp_ = fp_;
|
|
sp_ = frame->GetCallerSp();
|
|
fp_ = frame->GetCallerFp();
|
|
ASSERT((validate == kDontValidateFrames) || frame->IsValid());
|
|
return frame;
|
|
}
|
|
|
|
|
|
ExitFrame* StackFrameIterator::NextExitFrame() {
|
|
exit_.sp_ = frames_.sp_;
|
|
exit_.fp_ = frames_.fp_;
|
|
frames_.sp_ = exit_.GetCallerSp();
|
|
frames_.fp_ = exit_.GetCallerFp();
|
|
ASSERT(exit_.IsValid());
|
|
return &exit_;
|
|
}
|
|
|
|
|
|
EntryFrame* StackFrameIterator::NextEntryFrame() {
|
|
ASSERT(!frames_.HasNext());
|
|
entry_.sp_ = frames_.sp_;
|
|
entry_.fp_ = frames_.fp_;
|
|
SetupNextExitFrameData(); // Setup data for next exit frame in chain.
|
|
ASSERT(entry_.IsValid());
|
|
return &entry_;
|
|
}
|
|
|
|
|
|
InlinedFunctionsIterator::InlinedFunctionsIterator(StackFrame* frame)
|
|
: index_(0),
|
|
code_(Code::Handle()),
|
|
deopt_info_(DeoptInfo::Handle()),
|
|
function_(Function::Handle()),
|
|
pc_(0),
|
|
deopt_instructions_(),
|
|
object_table_(Array::Handle()) {
|
|
ASSERT(frame != NULL);
|
|
code_ = frame->LookupDartCode();
|
|
ASSERT(code_.is_optimized());
|
|
intptr_t deopt_reason = kDeoptUnknown;
|
|
deopt_info_ = code_.GetDeoptInfoAtPc(frame->pc(), &deopt_reason);
|
|
if (deopt_info_.IsNull()) {
|
|
// This is the case when a call without deopt info in optimzed code
|
|
// throws an exception. (e.g. in the parameter copying prologue).
|
|
// In that case there won't be any inlined frames.
|
|
function_ = code_.function();
|
|
pc_ = frame->pc();
|
|
ASSERT(pc_ != 0);
|
|
} else {
|
|
// Unpack deopt info into instructions (translate away suffixes).
|
|
const Array& deopt_table = Array::Handle(code_.deopt_info_array());
|
|
ASSERT(!deopt_table.IsNull());
|
|
deopt_info_.ToInstructions(deopt_table, &deopt_instructions_);
|
|
object_table_ = code_.object_table();
|
|
Advance();
|
|
}
|
|
}
|
|
|
|
|
|
void InlinedFunctionsIterator::Advance() {
|
|
// Iterate over the deopt instructions and determine the inlined
|
|
// functions if any and iterate over them.
|
|
ASSERT(!Done());
|
|
|
|
if (deopt_info_.IsNull()) {
|
|
SetDone();
|
|
return;
|
|
}
|
|
|
|
Function& func = Function::Handle();
|
|
ASSERT(deopt_instructions_.length() != 0);
|
|
while (index_ < deopt_instructions_.length()) {
|
|
DeoptInstr* deopt_instr = deopt_instructions_[index_++];
|
|
if (deopt_instr->kind() == DeoptInstr::kRetAddress) {
|
|
pc_ = DeoptInstr::GetRetAddress(deopt_instr, object_table_, &func);
|
|
code_ = func.unoptimized_code();
|
|
function_ = func.raw();
|
|
return;
|
|
}
|
|
}
|
|
SetDone();
|
|
}
|
|
|
|
} // namespace dart
|