| Index: runtime/vm/deopt_instructions.cc
|
| ===================================================================
|
| --- runtime/vm/deopt_instructions.cc (revision 28466)
|
| +++ runtime/vm/deopt_instructions.cc (working copy)
|
| @@ -18,30 +18,97 @@
|
| DECLARE_FLAG(bool, trace_deoptimization);
|
| DECLARE_FLAG(bool, trace_deoptimization_verbose);
|
|
|
| -DeoptContext::DeoptContext(const Array& object_table,
|
| - intptr_t num_args,
|
| - DeoptReasonId deopt_reason)
|
| - : object_table_(object_table.raw()),
|
| +
|
| +DeoptContext::DeoptContext(const StackFrame* frame,
|
| + const Code& code,
|
| + DestFrameOptions dest_options,
|
| + fpu_register_t* fpu_registers,
|
| + intptr_t* cpu_registers)
|
| + : code_(code.raw()),
|
| + object_table_(Array::null()),
|
| + deopt_info_(DeoptInfo::null()),
|
| + dest_frame_is_allocated_(false),
|
| dest_frame_(NULL),
|
| dest_frame_size_(0),
|
| - source_frame_is_copy_(false),
|
| + source_frame_is_allocated_(false),
|
| source_frame_(NULL),
|
| source_frame_size_(0),
|
| - cpu_registers_(NULL),
|
| - fpu_registers_(NULL),
|
| - num_args_(num_args),
|
| - deopt_reason_(deopt_reason),
|
| + cpu_registers_(cpu_registers),
|
| + fpu_registers_(fpu_registers),
|
| + num_args_(0),
|
| + deopt_reason_(kDeoptUnknown),
|
| isolate_(Isolate::Current()),
|
| deferred_boxes_(NULL),
|
| deferred_object_refs_(NULL),
|
| deferred_objects_count_(0),
|
| deferred_objects_(NULL) {
|
| + object_table_ = code.object_table();
|
| +
|
| + intptr_t deopt_reason = kDeoptUnknown;
|
| + const DeoptInfo& deopt_info =
|
| + DeoptInfo::Handle(code.GetDeoptInfoAtPc(frame->pc(), &deopt_reason));
|
| + ASSERT(!deopt_info.IsNull());
|
| + deopt_info_ = deopt_info.raw();
|
| + deopt_reason_ = static_cast<DeoptReasonId>(deopt_reason);
|
| +
|
| + const Function& function = Function::Handle(code.function());
|
| +
|
| + // Do not include incoming arguments if there are optional arguments
|
| + // (they are copied into local space at method entry).
|
| + num_args_ =
|
| + function.HasOptionalParameters() ? 0 : function.num_fixed_parameters();
|
| +
|
| + // The fixed size section of the (fake) Dart frame called via a stub by the
|
| + // optimized function contains FP, PP (ARM and MIPS only), PC-marker and
|
| + // return-address. This section is copied as well, so that its contained
|
| + // values can be updated before returning to the deoptimized function.
|
| + source_frame_size_ =
|
| + + kDartFrameFixedSize // For saved values below sp.
|
| + + ((frame->fp() - frame->sp()) / kWordSize) // For frame size incl. sp.
|
| + + 1 // For fp.
|
| + + kParamEndSlotFromFp // For saved values above fp.
|
| + + num_args_; // For arguments.
|
| + source_frame_ = reinterpret_cast<intptr_t*>(
|
| + frame->sp() - (kDartFrameFixedSize * kWordSize));
|
| +
|
| + if (dest_options == kDestIsOriginalFrame) {
|
| + // Work from a copy of the source frame.
|
| + intptr_t* original_frame = source_frame_;
|
| + source_frame_ = new intptr_t[source_frame_size_];
|
| + ASSERT(source_frame_ != NULL);
|
| + for (intptr_t i = 0; i < source_frame_size_; i++) {
|
| + source_frame_[i] = original_frame[i];
|
| + }
|
| + source_frame_is_allocated_ = true;
|
| + }
|
| + caller_fp_ = GetSourceFp();
|
| +
|
| + dest_frame_size_ = deopt_info.FrameSize();
|
| +
|
| + if (dest_options == kDestIsAllocated) {
|
| + dest_frame_ = new intptr_t[dest_frame_size_];
|
| + ASSERT(source_frame_ != NULL);
|
| + for (intptr_t i = 0; i < dest_frame_size_; i++) {
|
| + dest_frame_[i] = 0;
|
| + }
|
| + dest_frame_is_allocated_ = true;
|
| + }
|
| +
|
| + if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) {
|
| + OS::PrintErr(
|
| + "Deoptimizing (reason %" Pd " '%s') at pc %#" Px " '%s' (count %d)\n",
|
| + deopt_reason,
|
| + DeoptReasonToText(deopt_reason_),
|
| + frame->pc(),
|
| + function.ToFullyQualifiedCString(),
|
| + function.deoptimization_counter());
|
| + }
|
| }
|
|
|
|
|
| DeoptContext::~DeoptContext() {
|
| // Delete memory for source frame and registers.
|
| - if (source_frame_is_copy_) {
|
| + if (source_frame_is_allocated_) {
|
| delete[] source_frame_;
|
| }
|
| source_frame_ = NULL;
|
| @@ -49,6 +116,10 @@
|
| delete[] cpu_registers_;
|
| fpu_registers_ = NULL;
|
| cpu_registers_ = NULL;
|
| + if (dest_frame_is_allocated_) {
|
| + delete[] dest_frame_;
|
| + }
|
| + dest_frame_ = NULL;
|
|
|
| // Delete all deferred objects.
|
| for (intptr_t i = 0; i < deferred_objects_count_; i++) {
|
| @@ -60,36 +131,27 @@
|
| }
|
|
|
|
|
| -void DeoptContext::SetSourceArgs(intptr_t* frame_start,
|
| - intptr_t frame_size,
|
| - fpu_register_t* fpu_registers,
|
| - intptr_t* cpu_registers,
|
| - bool source_frame_is_copy) {
|
| - ASSERT(frame_start != NULL);
|
| - ASSERT(frame_size >= 0);
|
| - ASSERT(source_frame_ == NULL);
|
| - ASSERT(cpu_registers_ == NULL && fpu_registers_ == NULL);
|
| - source_frame_ = frame_start;
|
| - source_frame_size_ = frame_size;
|
| - caller_fp_ = GetSourceFp();
|
| - cpu_registers_ = cpu_registers;
|
| - fpu_registers_ = fpu_registers;
|
| - source_frame_is_copy_ = source_frame_is_copy;
|
| -}
|
| +void DeoptContext::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
| + visitor->VisitPointer(reinterpret_cast<RawObject**>(&object_table_));
|
| + visitor->VisitPointer(reinterpret_cast<RawObject**>(&deopt_info_));
|
|
|
| -
|
| -void DeoptContext::SetDestArgs(intptr_t* frame_start,
|
| - intptr_t frame_size) {
|
| - ASSERT(frame_start != NULL);
|
| - ASSERT(frame_size >= 0);
|
| - ASSERT(dest_frame_ == NULL);
|
| - dest_frame_ = frame_start;
|
| - dest_frame_size_ = frame_size;
|
| + // Visit any object pointers on the destination stack.
|
| + if (dest_frame_is_allocated_) {
|
| + for (intptr_t i = 0; i < dest_frame_size_; i++) {
|
| + if (dest_frame_[i] != 0) {
|
| + visitor->VisitPointer(reinterpret_cast<RawObject**>(&dest_frame_[i]));
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
|
|
| -void DeoptContext::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
| - visitor->VisitPointer(reinterpret_cast<RawObject**>(&object_table_));
|
| +intptr_t DeoptContext::DestStackAdjustment() const {
|
| + return (dest_frame_size_
|
| + - kDartFrameFixedSize
|
| + - num_args_
|
| + - kParamEndSlotFromFp
|
| + - 1); // For fp.
|
| }
|
|
|
|
|
| @@ -121,6 +183,121 @@
|
| }
|
|
|
|
|
| +static bool IsObjectInstruction(DeoptInstr::Kind kind) {
|
| + switch (kind) {
|
| + case DeoptInstr::kConstant:
|
| + case DeoptInstr::kStackSlot:
|
| + case DeoptInstr::kDoubleStackSlot:
|
| + case DeoptInstr::kInt64StackSlot:
|
| + case DeoptInstr::kFloat32x4StackSlot:
|
| + case DeoptInstr::kUint32x4StackSlot:
|
| + case DeoptInstr::kPp:
|
| + case DeoptInstr::kCallerPp:
|
| + case DeoptInstr::kMaterializedObjectRef:
|
| + return true;
|
| +
|
| + case DeoptInstr::kRegister:
|
| + case DeoptInstr::kFpuRegister:
|
| + case DeoptInstr::kInt64FpuRegister:
|
| + case DeoptInstr::kFloat32x4FpuRegister:
|
| + case DeoptInstr::kUint32x4FpuRegister:
|
| + // TODO(turnidge): Sometimes we encounter a deopt instruction
|
| + // with a register source while deoptimizing frames during
|
| + // debugging but we haven't saved our register set. This
|
| + // happens specifically when using the VMService to inspect the
|
| + // stack. In that case, the register values will have been
|
| + // saved before the StackOverflow runtime call but we do not
|
| + // actually keep track of which registers were saved and
|
| + // restored.
|
| + //
|
| + // It is possible to save this information at the point of the
|
| + // StackOverflow runtime call but would require a bit of magic
|
| + // to either make sure that the registers are pushed on the
|
| + // stack in a predictable fashion or that we save enough
|
| + // information to recover them after the fact.
|
| + //
|
| + // For now, we punt on these instructions.
|
| + return false;
|
| +
|
| + case DeoptInstr::kRetAddress:
|
| + case DeoptInstr::kPcMarker:
|
| + case DeoptInstr::kCallerFp:
|
| + case DeoptInstr::kCallerPc:
|
| + return false;
|
| +
|
| + case DeoptInstr::kSuffix:
|
| + case DeoptInstr::kMaterializeObject:
|
| + // We should not encounter these instructions when filling stack slots.
|
| + UNIMPLEMENTED();
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +
|
| +void DeoptContext::FillDestFrame() {
|
| + const Code& code = Code::Handle(code_);
|
| + const DeoptInfo& deopt_info = DeoptInfo::Handle(deopt_info_);
|
| +
|
| + const intptr_t len = deopt_info.TranslationLength();
|
| + GrowableArray<DeoptInstr*> deopt_instructions(len);
|
| + const Array& deopt_table = Array::Handle(code.deopt_info_array());
|
| + ASSERT(!deopt_table.IsNull());
|
| + deopt_info.ToInstructions(deopt_table, &deopt_instructions);
|
| +
|
| + const intptr_t frame_size = deopt_info.FrameSize();
|
| +
|
| + // For now, we never place non-objects in the deoptimized frame if
|
| + // the destination frame is a copy. This allows us to copy the
|
| + // deoptimized frame into an Array.
|
| + const bool objects_only = dest_frame_is_allocated_;
|
| +
|
| + // All kMaterializeObject instructions are emitted before the instructions
|
| + // that describe stack frames. Skip them and defer materialization of
|
| + // objects until the frame is fully reconstructed and it is safe to perform
|
| + // GC.
|
| + // Arguments (class of the instance to allocate and field-value pairs) are
|
| + // described as part of the expression stack for the bottom-most deoptimized
|
| + // frame. They will be used during materialization and removed from the stack
|
| + // right before control switches to the unoptimized code.
|
| + const intptr_t num_materializations = len - frame_size;
|
| + PrepareForDeferredMaterialization(num_materializations);
|
| + for (intptr_t from_index = 0, to_index = kDartFrameFixedSize;
|
| + from_index < num_materializations;
|
| + from_index++) {
|
| + const intptr_t field_count =
|
| + DeoptInstr::GetFieldCount(deopt_instructions[from_index]);
|
| + intptr_t* args = GetDestFrameAddressAt(to_index);
|
| + DeferredObject* obj = new DeferredObject(field_count, args);
|
| + SetDeferredObjectAt(from_index, obj);
|
| + to_index += obj->ArgumentCount();
|
| + }
|
| +
|
| + // Populate stack frames.
|
| + for (intptr_t to_index = frame_size - 1, from_index = len - 1;
|
| + to_index >= 0;
|
| + to_index--, from_index--) {
|
| + intptr_t* to_addr = GetDestFrameAddressAt(to_index);
|
| + DeoptInstr* instr = deopt_instructions[from_index];
|
| + if (!objects_only || IsObjectInstruction(instr->kind())) {
|
| + instr->Execute(this, to_addr);
|
| + } else {
|
| + *reinterpret_cast<RawObject**>(to_addr) = Object::null();
|
| + }
|
| + }
|
| +
|
| + if (FLAG_trace_deoptimization_verbose) {
|
| + intptr_t* start = dest_frame_;
|
| + for (intptr_t i = 0; i < frame_size; i++) {
|
| + OS::PrintErr("*%" Pd ". [%" Px "] %#014" Px " [%s]\n",
|
| + i,
|
| + reinterpret_cast<uword>(&start[i]),
|
| + start[i],
|
| + deopt_instructions[i + (len - frame_size)]->ToCString());
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| static void FillDeferredSlots(DeferredSlot** slot_list) {
|
| DeferredSlot* slot = *slot_list;
|
| *slot_list = NULL;
|
| @@ -149,14 +326,46 @@
|
| FillDeferredSlots(&deferred_object_refs_);
|
|
|
| // Compute total number of artificial arguments used during deoptimization.
|
| - intptr_t deopt_arguments = 0;
|
| + intptr_t deopt_arg_count = 0;
|
| for (intptr_t i = 0; i < DeferredObjectsCount(); i++) {
|
| - deopt_arguments += GetDeferredObject(i)->ArgumentCount();
|
| + deopt_arg_count += GetDeferredObject(i)->ArgumentCount();
|
| }
|
| - return deopt_arguments;
|
| +
|
| + // Since this is the only step where GC can occur during deoptimization,
|
| + // use it to report the source line where deoptimization occured.
|
| + if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) {
|
| + DartFrameIterator iterator;
|
| + StackFrame* top_frame = iterator.NextFrame();
|
| + ASSERT(top_frame != NULL);
|
| + const Code& code = Code::Handle(top_frame->LookupDartCode());
|
| + const Function& top_function = Function::Handle(code.function());
|
| + const Script& script = Script::Handle(top_function.script());
|
| + const intptr_t token_pos = code.GetTokenIndexOfPC(top_frame->pc());
|
| + intptr_t line, column;
|
| + script.GetTokenLocation(token_pos, &line, &column);
|
| + String& line_string = String::Handle(script.GetLine(line));
|
| + OS::PrintErr(" Function: %s\n", top_function.ToFullyQualifiedCString());
|
| + OS::PrintErr(" Line %" Pd ": '%s'\n", line, line_string.ToCString());
|
| + OS::PrintErr(" Deopt args: %" Pd "\n", deopt_arg_count);
|
| + }
|
| +
|
| + return deopt_arg_count;
|
| }
|
|
|
|
|
| +RawArray* DeoptContext::DestFrameAsArray() {
|
| + ASSERT(dest_frame_ != NULL && dest_frame_is_allocated_);
|
| + const Array& dest_array =
|
| + Array::Handle(Array::New(dest_frame_size_));
|
| + Instance& obj = Instance::Handle();
|
| + for (intptr_t i = 0; i < dest_frame_size_; i++) {
|
| + obj ^= reinterpret_cast<RawObject*>(dest_frame_[i]);
|
| + dest_array.SetAt(i, obj);
|
| + }
|
| + return dest_array.raw();
|
| +}
|
| +
|
| +
|
| // Deoptimization instruction moving value from optimized frame at
|
| // 'source_index' to specified slots in the unoptimized frame.
|
| // 'source_index' represents the slot index of the frame (0 being
|
|
|