blob: 163e653a5492c85e4b5697dc478ff0921202e2b0 [file] [log] [blame]
// 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() + (kPcMarkerSlotFromFp * kWordSize)));
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() + (kSavedContextSlotFromEntryFp * kWordSize)));
}
void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
// Visit objects between SP and (FP - callee_save_area).
ASSERT(visitor != NULL);
RawObject** first = reinterpret_cast<RawObject**>(sp());
RawObject** last = reinterpret_cast<RawObject**>(
fp() + (kExitLinkSlotFromEntryFp - 1) * kWordSize);
visitor->VisitPointers(first, last);
}
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** first = reinterpret_cast<RawObject**>(sp());
RawObject** last = reinterpret_cast<RawObject**>(
fp() + (kFirstLocalSlotFromFp * kWordSize));
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(last);
--last;
}
// 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(first);
++first;
}
// The last slot can be one slot (but not more) past the last slot
// in the case that all slots were covered by the stack map.
ASSERT((last + 1) >= first);
}
}
// Each slot between the first and last included are tagged objects.
visitor->VisitPointers(first, last);
}
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;
const uword pc_marker =
*(reinterpret_cast<uword*>(fp() + (kPcMarkerSlotFromFp * kWordSize)));
if (pc_marker != 0) {
const uword entry_point =
(pc_marker - Assembler::kEntryPointToPcMarkerOffset);
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());
}
void StackFrameIterator::SetupLastExitFrameData() {
Isolate* current = Isolate::Current();
uword exit_marker = current->top_exit_frame_info();
frames_.fp_ = exit_marker;
}
void StackFrameIterator::SetupNextExitFrameData() {
uword exit_address = entry_.fp() + (kExitLinkSlotFromEntryFp * kWordSize);
uword exit_marker = *reinterpret_cast<uword*>(exit_address);
frames_.fp_ = exit_marker;
frames_.sp_ = 0;
frames_.pc_ = 0;
}
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;
frames_.sp_ = 0;
frames_.pc_ = 0;
}
StackFrameIterator::StackFrameIterator(uword fp, uword sp, uword pc,
bool validate)
: validate_(validate), entry_(), exit_(), current_frame_(NULL) {
frames_.fp_ = fp;
frames_.sp_ = sp;
frames_.pc_ = pc;
}
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 (unless we start iterating in the simulator from a given
// triplet of fp, sp, and pc). 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;
}
if (frames_.pc_ == 0) {
// Iteration starts from an exit frame given by its fp.
current_frame_ = NextExitFrame();
} else if (*(reinterpret_cast<uword*>(
frames_.fp_ + (kSavedCallerFpSlotFromFp * kWordSize))) == 0) {
// Iteration starts from an entry frame given by its fp, sp, and pc.
current_frame_ = NextEntryFrame();
} else {
// Iteration starts from a Dart or stub frame given by its fp, sp, and pc.
current_frame_ = frames_.NextFrame(validate_);
}
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_;
frame->pc_ = pc_;
sp_ = frame->GetCallerSp();
fp_ = frame->GetCallerFp();
pc_ = frame->GetCallerPc();
ASSERT((validate == kDontValidateFrames) || frame->IsValid());
return frame;
}
ExitFrame* StackFrameIterator::NextExitFrame() {
exit_.sp_ = frames_.sp_;
exit_.fp_ = frames_.fp_;
exit_.pc_ = frames_.pc_;
frames_.sp_ = exit_.GetCallerSp();
frames_.fp_ = exit_.GetCallerFp();
frames_.pc_ = exit_.GetCallerPc();
ASSERT(exit_.IsValid());
return &exit_;
}
EntryFrame* StackFrameIterator::NextEntryFrame() {
ASSERT(!frames_.HasNext());
entry_.sp_ = frames_.sp_;
entry_.fp_ = frames_.fp_;
entry_.pc_ = frames_.pc_;
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 optimized 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